From 21dbfdff46560c1825c429895919da9d3a0ff861 Mon Sep 17 00:00:00 2001 From: Clif Date: Tue, 2 Dec 2025 20:12:04 -0500 Subject: [PATCH 01/18] MLProject.ipynb creation --- .../1st dataset_draft.....xlsx | Bin 24494 -> 13288 bytes 4_data_analysis/MLProject.ipynb | 63 ++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 4_data_analysis/MLProject.ipynb diff --git a/1_datasets/haiti_catalog_raw_data/1st dataset_draft.....xlsx b/1_datasets/haiti_catalog_raw_data/1st dataset_draft.....xlsx index 660d1c31fef262a0b6c4c7eb93f315d77e21b825..b55615a4a419f18993883f3d26b0d3f38441cc64 100644 GIT binary patch literal 13288 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 literal 24494 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

T!h6BP5eT5;@%bSU4};U+Ax{$Cm>mDoZT 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

rW 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 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mUN_tourism_inbound_Arrivals_caribbean_countries\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcsv\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mtourism_data\u001b[39;00m\n", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'UN_tourism_inbound_Arrivals_caribbean_countries'" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "import UN_tourism_inbound_Arrivals_caribbean_countries.csv as tourism_data\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "209e6b6f", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "3.10.3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From ace2ee02a06525aa6f7ccef3eb15c60ff70ddafc Mon Sep 17 00:00:00 2001 From: Clif Date: Thu, 4 Dec 2025 22:51:08 -0500 Subject: [PATCH 02/18] MLProjects directories --- 4_data_analysis/MLProject.ipynb | 333 +++++++++++++++++++++++++++++++- 1 file changed, 324 insertions(+), 9 deletions(-) diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index c31ad8b..9da3321 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -2,38 +2,353 @@ "cells": [ { "cell_type": "markdown", - "id": "c732b6a4", + "id": "ad521be8", "metadata": {}, "source": [] }, { "cell_type": "code", - "execution_count": 5, - "id": "10fb3608", + "execution_count": 47, + "id": "9beb769c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'/home/clif_lastrophysicien/ELO2_LAPERLE_HT/4_data_analysis'" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import os\n", + "import pandas as pd\n", + "import numpy as np \n", + "\n", + "\n", + "os.getcwd()\n" + ] + }, + { + "cell_type": "markdown", + "id": "a48c7dbf", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "01fa3f99", + "metadata": {}, + "outputs": [], + "source": [ + "ROOT_DIR = os.path.abspath(\"..\")\n", + "DATA_DIR = os.path.join(ROOT_DIR, \"1_datasets\")\n", + "\n", + "sys.path.append(DATA_DIR)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "bfa8c14f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ROOT_DIR = /home/clif_lastrophysicien/ELO2_LAPERLE_HT\n", + "CSV_PATH = /home/clif_lastrophysicien/ELO2_LAPERLE_HT/1_datasets/raw_data/UN_tourism_caribbean_countries_raw/UN_tourism_inbound_Arrivals_caribbean_countries.csv\n", + "Existe ? True\n" + ] + } + ], + "source": [ + "import os\n", + "import pandas as pd\n", + "\n", + "ROOT_DIR = os.path.abspath(\"..\")\n", + "print(\"ROOT_DIR =\", ROOT_DIR)\n", + "\n", + "CSV_PATH = os.path.join(\n", + " ROOT_DIR,\n", + " \"1_datasets\",\n", + " \"raw_data\",\n", + " \"UN_tourism_caribbean_countries_raw\",\n", + " \"UN_tourism_inbound_Arrivals_caribbean_countries.csv\",\n", + ")\n", + "\n", + "print(\"CSV_PATH =\", CSV_PATH)\n", + "print(\"Existe ?\", os.path.exists(CSV_PATH))\n", + "\n", + "df = pd.read_csv(CSV_PATH)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "6d84e890", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
indicator_codeindicator_labelindicator_previous_codereporter_area_codereporter_area_labelpartner_area_codepartner_area_labelyearvalueflagflag_labelunitnotesdrop
0INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1995227.0NaNNaNthousand tripsNaNNaN
1INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1996270.0NaNNaNthousand tripsNaNNaN
2INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1997286.0NaNNaNthousand tripsNaNNaN
3INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1998336.0NaNNaNthousand tripsNaNNaN
4INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1999328.0NaNNaNthousand tripsNaNNaN
\n", + "
" + ], + "text/plain": [ + " indicator_code \\\n", + "0 INBD_TRIP_TOTL_CRUS_EXCR \n", + "1 INBD_TRIP_TOTL_CRUS_EXCR \n", + "2 INBD_TRIP_TOTL_CRUS_EXCR \n", + "3 INBD_TRIP_TOTL_CRUS_EXCR \n", + "4 INBD_TRIP_TOTL_CRUS_EXCR \n", + "\n", + " indicator_label indicator_previous_code \\\n", + "0 inbound - trips - total - cruise ships - same-... CP_1_4 \n", + "1 inbound - trips - total - cruise ships - same-... CP_1_4 \n", + "2 inbound - trips - total - cruise ships - same-... CP_1_4 \n", + "3 inbound - trips - total - cruise ships - same-... CP_1_4 \n", + "4 inbound - trips - total - cruise ships - same-... CP_1_4 \n", + "\n", + " reporter_area_code reporter_area_label partner_area_code \\\n", + "0 28 Antigua and Barbuda 999 \n", + "1 28 Antigua and Barbuda 999 \n", + "2 28 Antigua and Barbuda 999 \n", + "3 28 Antigua and Barbuda 999 \n", + "4 28 Antigua and Barbuda 999 \n", + "\n", + " partner_area_label year value flag flag_label unit notes drop \n", + "0 World 1995 227.0 NaN NaN thousand trips NaN NaN \n", + "1 World 1996 270.0 NaN NaN thousand trips NaN NaN \n", + "2 World 1997 286.0 NaN NaN thousand trips NaN NaN \n", + "3 World 1998 336.0 NaN NaN thousand trips NaN NaN \n", + "4 World 1999 328.0 NaN NaN thousand trips NaN NaN " + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "165677d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "RangeIndex: 3025 entries, 0 to 3024\n", + "Data columns (total 14 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 indicator_code 3025 non-null object \n", + " 1 indicator_label 3025 non-null object \n", + " 2 indicator_previous_code 3025 non-null object \n", + " 3 reporter_area_code 3025 non-null int64 \n", + " 4 reporter_area_label 3025 non-null object \n", + " 5 partner_area_code 3025 non-null int64 \n", + " 6 partner_area_label 3025 non-null object \n", + " 7 year 3025 non-null int64 \n", + " 8 value 3025 non-null float64\n", + " 9 flag 2 non-null object \n", + " 10 flag_label 2 non-null object \n", + " 11 unit 3025 non-null object \n", + " 12 notes 2 non-null object \n", + " 13 drop 0 non-null float64\n", + "dtypes: float64(2), int64(3), object(9)\n", + "memory usage: 331.0+ KB\n" + ] + } + ], + "source": [ + "df.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "b7213152", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Series([], Name: count, dtype: int64)" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.value_counts() " + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "ec66ab0d", "metadata": {}, "outputs": [ { "ename": "ModuleNotFoundError", - "evalue": "No module named 'UN_tourism_inbound_Arrivals_caribbean_countries'", + "evalue": "No module named 'matplotlib'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[5], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mnumpy\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mnp\u001b[39;00m\n\u001b[0;32m----> 3\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mUN_tourism_inbound_Arrivals_caribbean_countries\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcsv\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mtourism_data\u001b[39;00m\n", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'UN_tourism_inbound_Arrivals_caribbean_countries'" + "Cell \u001b[0;32mIn[44], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mmatplotlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpyplot\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mplt\u001b[39;00m\n\u001b[1;32m 3\u001b[0m plt\u001b[38;5;241m.\u001b[39mscatter(df[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mYear\u001b[39m\u001b[38;5;124m'\u001b[39m], df[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mArrivals\u001b[39m\u001b[38;5;124m'\u001b[39m])\n\u001b[1;32m 4\u001b[0m plt\u001b[38;5;241m.\u001b[39mxlabel(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mYear\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", + "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'matplotlib'" ] } ], "source": [ - "import numpy as np\n", + "import matplotlib.pyplot as plt\n", "\n", - "import UN_tourism_inbound_Arrivals_caribbean_countries.csv as tourism_data\n" + "plt.scatter(df['Year'], df['Arrivals'])\n", + "plt.xlabel('Year')\n", + "plt.ylabel('Arrivals') " ] }, { "cell_type": "code", "execution_count": null, - "id": "209e6b6f", + "id": "3eb6a166", "metadata": {}, "outputs": [], "source": [] From 88011671f650f8e3ce844fc578540d3727832a18 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 09:35:49 -0500 Subject: [PATCH 03/18] cached .venv files suppression --- .gitignore | 466 ++ .venv/bin/get_gprof | 75 + .venv/bin/get_objgraph | 54 + .venv/bin/isort | 7 + .venv/bin/isort-identify-imports | 7 + .venv/bin/pylint | 7 + .venv/bin/pylint-config | 7 + .venv/bin/pyreverse | 7 + .venv/bin/symilar | 7 + .venv/bin/undill | 22 + .../astroid-4.0.2.dist-info/INSTALLER | 1 + .../astroid-4.0.2.dist-info/METADATA | 122 + .../astroid-4.0.2.dist-info/RECORD | 197 + .../astroid-4.0.2.dist-info/WHEEL | 5 + .../licenses/CONTRIBUTORS.txt | 226 + .../astroid-4.0.2.dist-info/licenses/LICENSE | 508 ++ .../astroid-4.0.2.dist-info/top_level.txt | 1 + .../site-packages/astroid/__init__.py | 242 + .../site-packages/astroid/__pkginfo__.py | 6 + .../python3.10/site-packages/astroid/_ast.py | 102 + .../site-packages/astroid/arguments.py | 309 + .../site-packages/astroid/astroid_manager.py | 20 + .../python3.10/site-packages/astroid/bases.py | 778 +++ .../site-packages/astroid/brain/__init__.py | 0 .../astroid/brain/brain_argparse.py | 50 + .../astroid/brain/brain_attrs.py | 110 + .../astroid/brain/brain_boto3.py | 32 + .../astroid/brain/brain_builtin_inference.py | 1106 ++++ .../astroid/brain/brain_collections.py | 138 + .../astroid/brain/brain_crypt.py | 27 + .../astroid/brain/brain_ctypes.py | 86 + .../astroid/brain/brain_curses.py | 185 + .../astroid/brain/brain_dataclasses.py | 635 ++ .../astroid/brain/brain_datetime.py | 20 + .../astroid/brain/brain_dateutil.py | 28 + .../astroid/brain/brain_functools.py | 174 + .../site-packages/astroid/brain/brain_gi.py | 252 + .../astroid/brain/brain_hashlib.py | 96 + .../site-packages/astroid/brain/brain_http.py | 238 + .../astroid/brain/brain_hypothesis.py | 56 + .../site-packages/astroid/brain/brain_io.py | 44 + .../astroid/brain/brain_mechanize.py | 125 + .../astroid/brain/brain_multiprocessing.py | 106 + .../astroid/brain/brain_namedtuple_enum.py | 681 ++ .../brain/brain_numpy_core_einsumfunc.py | 28 + .../brain/brain_numpy_core_fromnumeric.py | 24 + .../brain/brain_numpy_core_function_base.py | 35 + .../brain/brain_numpy_core_multiarray.py | 106 + .../astroid/brain/brain_numpy_core_numeric.py | 50 + .../brain/brain_numpy_core_numerictypes.py | 265 + .../astroid/brain/brain_numpy_core_umath.py | 154 + .../astroid/brain/brain_numpy_ma.py | 33 + .../astroid/brain/brain_numpy_ndarray.py | 163 + .../brain/brain_numpy_random_mtrand.py | 73 + .../astroid/brain/brain_numpy_utils.py | 94 + .../astroid/brain/brain_pathlib.py | 55 + .../astroid/brain/brain_pkg_resources.py | 72 + .../astroid/brain/brain_pytest.py | 85 + .../site-packages/astroid/brain/brain_qt.py | 89 + .../astroid/brain/brain_random.py | 94 + .../site-packages/astroid/brain/brain_re.py | 97 + .../astroid/brain/brain_regex.py | 95 + .../astroid/brain/brain_responses.py | 80 + .../astroid/brain/brain_scipy_signal.py | 90 + .../astroid/brain/brain_signal.py | 120 + .../site-packages/astroid/brain/brain_six.py | 244 + .../astroid/brain/brain_sqlalchemy.py | 41 + .../site-packages/astroid/brain/brain_ssl.py | 163 + .../astroid/brain/brain_statistics.py | 73 + .../astroid/brain/brain_subprocess.py | 100 + .../astroid/brain/brain_threading.py | 33 + .../site-packages/astroid/brain/brain_type.py | 70 + .../astroid/brain/brain_typing.py | 504 ++ .../astroid/brain/brain_unittest.py | 31 + .../site-packages/astroid/brain/brain_uuid.py | 18 + .../site-packages/astroid/brain/helpers.py | 146 + .../site-packages/astroid/builder.py | 505 ++ .../python3.10/site-packages/astroid/const.py | 26 + .../site-packages/astroid/constraint.py | 186 + .../site-packages/astroid/context.py | 204 + .../site-packages/astroid/decorators.py | 232 + .../site-packages/astroid/exceptions.py | 419 ++ .../astroid/filter_statements.py | 240 + .../site-packages/astroid/helpers.py | 335 + .../site-packages/astroid/inference_tip.py | 130 + .../astroid/interpreter/__init__.py | 0 .../astroid/interpreter/_import/__init__.py | 0 .../astroid/interpreter/_import/spec.py | 496 ++ .../astroid/interpreter/_import/util.py | 111 + .../astroid/interpreter/dunder_lookup.py | 75 + .../astroid/interpreter/objectmodel.py | 1013 +++ .../site-packages/astroid/manager.py | 478 ++ .../site-packages/astroid/modutils.py | 703 ++ .../site-packages/astroid/nodes/__init__.py | 303 + .../astroid/nodes/_base_nodes.py | 672 ++ .../site-packages/astroid/nodes/as_string.py | 740 +++ .../site-packages/astroid/nodes/const.py | 27 + .../astroid/nodes/node_classes.py | 5701 +++++++++++++++++ .../site-packages/astroid/nodes/node_ng.py | 771 +++ .../astroid/nodes/scoped_nodes/__init__.py | 47 + .../astroid/nodes/scoped_nodes/mixin.py | 202 + .../nodes/scoped_nodes/scoped_nodes.py | 2900 +++++++++ .../astroid/nodes/scoped_nodes/utils.py | 35 + .../site-packages/astroid/nodes/utils.py | 14 + .../site-packages/astroid/objects.py | 360 ++ .../site-packages/astroid/protocols.py | 958 +++ .../site-packages/astroid/raw_building.py | 735 +++ .../site-packages/astroid/rebuilder.py | 1996 ++++++ .../site-packages/astroid/test_utils.py | 78 + .../site-packages/astroid/transforms.py | 163 + .../site-packages/astroid/typing.py | 98 + .../python3.10/site-packages/astroid/util.py | 159 + .../dill-0.4.0.dist-info/INSTALLER | 1 + .../dill-0.4.0.dist-info/LICENSE | 35 + .../dill-0.4.0.dist-info/METADATA | 281 + .../site-packages/dill-0.4.0.dist-info/RECORD | 101 + .../site-packages/dill-0.4.0.dist-info/WHEEL | 5 + .../dill-0.4.0.dist-info/top_level.txt | 1 + .../python3.10/site-packages/dill/__diff.py | 234 + .../python3.10/site-packages/dill/__info__.py | 291 + .../python3.10/site-packages/dill/__init__.py | 119 + .../python3.10/site-packages/dill/_dill.py | 2255 +++++++ .../python3.10/site-packages/dill/_objects.py | 541 ++ .../python3.10/site-packages/dill/_shims.py | 193 + .../python3.10/site-packages/dill/detect.py | 287 + .../python3.10/site-packages/dill/logger.py | 285 + .../python3.10/site-packages/dill/objtypes.py | 24 + .../python3.10/site-packages/dill/pointers.py | 122 + .../python3.10/site-packages/dill/session.py | 612 ++ .../python3.10/site-packages/dill/settings.py | 25 + .../python3.10/site-packages/dill/source.py | 1023 +++ .../lib/python3.10/site-packages/dill/temp.py | 252 + .../site-packages/dill/tests/__init__.py | 22 + .../site-packages/dill/tests/__main__.py | 35 + .../site-packages/dill/tests/test_abc.py | 169 + .../site-packages/dill/tests/test_check.py | 62 + .../site-packages/dill/tests/test_classdef.py | 340 + .../dill/tests/test_dataclasses.py | 35 + .../site-packages/dill/tests/test_detect.py | 160 + .../dill/tests/test_dictviews.py | 39 + .../site-packages/dill/tests/test_diff.py | 107 + .../dill/tests/test_extendpickle.py | 53 + .../site-packages/dill/tests/test_fglobals.py | 55 + .../site-packages/dill/tests/test_file.py | 500 ++ .../dill/tests/test_functions.py | 141 + .../site-packages/dill/tests/test_functors.py | 39 + .../site-packages/dill/tests/test_logger.py | 70 + .../site-packages/dill/tests/test_mixins.py | 121 + .../site-packages/dill/tests/test_module.py | 84 + .../dill/tests/test_moduledict.py | 54 + .../site-packages/dill/tests/test_nested.py | 135 + .../site-packages/dill/tests/test_objects.py | 63 + .../dill/tests/test_properties.py | 62 + .../dill/tests/test_pycapsule.py | 45 + .../dill/tests/test_recursive.py | 177 + .../dill/tests/test_registered.py | 64 + .../dill/tests/test_restricted.py | 27 + .../site-packages/dill/tests/test_selected.py | 126 + .../site-packages/dill/tests/test_session.py | 280 + .../site-packages/dill/tests/test_source.py | 173 + .../site-packages/dill/tests/test_sources.py | 190 + .../site-packages/dill/tests/test_temp.py | 103 + .../site-packages/dill/tests/test_threads.py | 46 + .../site-packages/dill/tests/test_weakref.py | 72 + .../isort-7.0.0.dist-info/INSTALLER | 1 + .../isort-7.0.0.dist-info/METADATA | 375 ++ .../isort-7.0.0.dist-info/RECORD | 101 + .../site-packages/isort-7.0.0.dist-info/WHEEL | 4 + .../isort-7.0.0.dist-info/entry_points.txt | 6 + .../isort-7.0.0.dist-info/licenses/LICENSE | 21 + .../site-packages/isort/__init__.py | 39 + .../site-packages/isort/__main__.py | 3 + .../isort/_vendored/tomli/LICENSE | 21 + .../isort/_vendored/tomli/__init__.py | 6 + .../isort/_vendored/tomli/_parser.py | 650 ++ .../isort/_vendored/tomli/_re.py | 100 + .../isort/_vendored/tomli/py.typed | 1 + .../site-packages/isort/_version.py | 3 + .../lib/python3.10/site-packages/isort/api.py | 660 ++ .../site-packages/isort/comments.py | 29 + .../python3.10/site-packages/isort/core.py | 513 ++ .../isort/deprecated/__init__.py | 0 .../site-packages/isort/deprecated/finders.py | 392 ++ .../site-packages/isort/exceptions.py | 197 + .../python3.10/site-packages/isort/files.py | 41 + .../python3.10/site-packages/isort/format.py | 157 + .../python3.10/site-packages/isort/hooks.py | 93 + .../site-packages/isort/identify.py | 208 + .../lib/python3.10/site-packages/isort/io.py | 73 + .../python3.10/site-packages/isort/literal.py | 115 + .../python3.10/site-packages/isort/logo.py | 19 + .../python3.10/site-packages/isort/main.py | 1308 ++++ .../python3.10/site-packages/isort/output.py | 686 ++ .../python3.10/site-packages/isort/parse.py | 601 ++ .../python3.10/site-packages/isort/place.py | 146 + .../site-packages/isort/profiles.py | 96 + .../python3.10/site-packages/isort/py.typed | 0 .../site-packages/isort/sections.py | 8 + .../site-packages/isort/settings.py | 933 +++ .../isort/setuptools_commands.py | 63 + .../python3.10/site-packages/isort/sorting.py | 131 + .../site-packages/isort/stdlibs/__init__.py | 18 + .../site-packages/isort/stdlibs/all.py | 3 + .../site-packages/isort/stdlibs/py2.py | 3 + .../site-packages/isort/stdlibs/py27.py | 301 + .../site-packages/isort/stdlibs/py3.py | 13 + .../site-packages/isort/stdlibs/py310.py | 232 + .../site-packages/isort/stdlibs/py311.py | 232 + .../site-packages/isort/stdlibs/py312.py | 227 + .../site-packages/isort/stdlibs/py313.py | 207 + .../site-packages/isort/stdlibs/py314.py | 208 + .../site-packages/isort/stdlibs/py36.py | 224 + .../site-packages/isort/stdlibs/py37.py | 225 + .../site-packages/isort/stdlibs/py38.py | 233 + .../site-packages/isort/stdlibs/py39.py | 234 + .../python3.10/site-packages/isort/utils.py | 74 + .../python3.10/site-packages/isort/wrap.py | 147 + .../site-packages/isort/wrap_modes.py | 375 ++ .../mccabe-0.7.0.dist-info/INSTALLER | 1 + .../mccabe-0.7.0.dist-info/LICENSE | 25 + .../mccabe-0.7.0.dist-info/METADATA | 199 + .../mccabe-0.7.0.dist-info/RECORD | 9 + .../mccabe-0.7.0.dist-info/WHEEL | 6 + .../mccabe-0.7.0.dist-info/entry_points.txt | 3 + .../mccabe-0.7.0.dist-info/top_level.txt | 1 + .venv/lib/python3.10/site-packages/mccabe.py | 346 + .../platformdirs-4.5.0.dist-info/INSTALLER | 1 + .../platformdirs-4.5.0.dist-info/METADATA | 350 + .../platformdirs-4.5.0.dist-info/RECORD | 22 + .../platformdirs-4.5.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 21 + .../site-packages/platformdirs/__init__.py | 631 ++ .../site-packages/platformdirs/__main__.py | 55 + .../site-packages/platformdirs/android.py | 249 + .../site-packages/platformdirs/api.py | 299 + .../site-packages/platformdirs/macos.py | 146 + .../site-packages/platformdirs/py.typed | 0 .../site-packages/platformdirs/unix.py | 272 + .../site-packages/platformdirs/version.py | 34 + .../site-packages/platformdirs/windows.py | 272 + .../pylint-4.0.4.dist-info/INSTALLER | 1 + .../pylint-4.0.4.dist-info/METADATA | 277 + .../pylint-4.0.4.dist-info/RECORD | 371 ++ .../pylint-4.0.4.dist-info/REQUESTED | 0 .../pylint-4.0.4.dist-info/WHEEL | 5 + .../pylint-4.0.4.dist-info/entry_points.txt | 5 + .../licenses/CONTRIBUTORS.txt | 699 ++ .../pylint-4.0.4.dist-info/licenses/LICENSE | 340 + .../pylint-4.0.4.dist-info/top_level.txt | 1 + .../site-packages/pylint/__init__.py | 119 + .../site-packages/pylint/__main__.py | 10 + .../site-packages/pylint/__pkginfo__.py | 43 + .../site-packages/pylint/checkers/__init__.py | 140 + .../pylint/checkers/async_checker.py | 97 + .../pylint/checkers/bad_chained_comparison.py | 60 + .../pylint/checkers/base/__init__.py | 50 + .../pylint/checkers/base/basic_checker.py | 962 +++ .../checkers/base/basic_error_checker.py | 647 ++ .../checkers/base/comparison_checker.py | 352 + .../pylint/checkers/base/docstring_checker.py | 203 + .../pylint/checkers/base/function_checker.py | 149 + .../checkers/base/name_checker/__init__.py | 25 + .../checkers/base/name_checker/checker.py | 804 +++ .../base/name_checker/naming_style.py | 187 + .../pylint/checkers/base/pass_checker.py | 29 + .../pylint/checkers/base_checker.py | 248 + .../pylint/checkers/classes/__init__.py | 18 + .../pylint/checkers/classes/class_checker.py | 2408 +++++++ .../classes/special_methods_checker.py | 403 ++ .../pylint/checkers/clear_lru_cache.py | 37 + .../pylint/checkers/dataclass_checker.py | 129 + .../pylint/checkers/deprecated.py | 294 + .../pylint/checkers/design_analysis.py | 705 ++ .../pylint/checkers/dunder_methods.py | 102 + .../pylint/checkers/ellipsis_checker.py | 58 + .../pylint/checkers/exceptions.py | 658 ++ .../site-packages/pylint/checkers/format.py | 733 +++ .../site-packages/pylint/checkers/imports.py | 1274 ++++ .../pylint/checkers/lambda_expressions.py | 94 + .../site-packages/pylint/checkers/logging.py | 417 ++ .../checkers/match_statements_checker.py | 230 + .../pylint/checkers/method_args.py | 129 + .../site-packages/pylint/checkers/misc.py | 192 + .../checkers/modified_iterating_checker.py | 198 + .../pylint/checkers/nested_min_max.py | 176 + .../site-packages/pylint/checkers/newstyle.py | 113 + .../pylint/checkers/non_ascii_names.py | 174 + .../pylint/checkers/raw_metrics.py | 109 + .../pylint/checkers/refactoring/__init__.py | 33 + .../implicit_booleaness_checker.py | 420 ++ .../checkers/refactoring/not_checker.py | 84 + .../refactoring/recommendation_checker.py | 452 ++ .../refactoring/refactoring_checker.py | 2454 +++++++ .../site-packages/pylint/checkers/spelling.py | 473 ++ .../site-packages/pylint/checkers/stdlib.py | 1003 +++ .../site-packages/pylint/checkers/strings.py | 1101 ++++ .../site-packages/pylint/checkers/symilar.py | 932 +++ .../pylint/checkers/threading_checker.py | 59 + .../pylint/checkers/typecheck.py | 2355 +++++++ .../site-packages/pylint/checkers/unicode.py | 537 ++ .../pylint/checkers/unsupported_version.py | 196 + .../site-packages/pylint/checkers/utils.py | 2336 +++++++ .../pylint/checkers/variables.py | 3534 ++++++++++ .../site-packages/pylint/config/__init__.py | 9 + .../config/_breaking_changes/__init__.py | 178 + .../pylint/config/_pylint_config/__init__.py | 13 + .../config/_pylint_config/generate_command.py | 49 + .../config/_pylint_config/help_message.py | 59 + .../pylint/config/_pylint_config/main.py | 25 + .../pylint/config/_pylint_config/setup.py | 49 + .../pylint/config/_pylint_config/utils.py | 110 + .../site-packages/pylint/config/argument.py | 503 ++ .../pylint/config/arguments_manager.py | 402 ++ .../pylint/config/arguments_provider.py | 65 + .../pylint/config/callback_actions.py | 468 ++ .../pylint/config/config_file_parser.py | 129 + .../pylint/config/config_initialization.py | 206 + .../pylint/config/deprecation_actions.py | 108 + .../site-packages/pylint/config/exceptions.py | 25 + .../config/find_default_config_files.py | 150 + .../pylint/config/help_formatter.py | 64 + .../site-packages/pylint/config/utils.py | 259 + .../site-packages/pylint/constants.py | 284 + .../site-packages/pylint/exceptions.py | 53 + .../pylint/extensions/__init__.py | 20 + .../pylint/extensions/_check_docs_utils.py | 941 +++ .../pylint/extensions/bad_builtin.py | 65 + .../pylint/extensions/broad_try_clause.py | 73 + .../pylint/extensions/check_elif.py | 64 + .../pylint/extensions/code_style.py | 361 ++ .../pylint/extensions/comparison_placement.py | 69 + .../pylint/extensions/confusing_elif.py | 55 + ...nsider_refactoring_into_while_condition.py | 93 + .../extensions/consider_ternary_expression.py | 52 + .../pylint/extensions/dict_init_mutate.py | 54 + .../pylint/extensions/docparams.py | 697 ++ .../pylint/extensions/docstyle.py | 89 + .../site-packages/pylint/extensions/dunder.py | 75 + .../pylint/extensions/empty_comment.py | 63 + .../pylint/extensions/eq_without_hash.py | 39 + .../pylint/extensions/for_any_all.py | 163 + .../pylint/extensions/magic_value.py | 119 + .../site-packages/pylint/extensions/mccabe.py | 226 + .../pylint/extensions/no_self_use.py | 111 + .../extensions/overlapping_exceptions.py | 88 + .../pylint/extensions/private_import.py | 266 + .../pylint/extensions/redefined_loop_name.py | 88 + .../extensions/redefined_variable_type.py | 108 + .../pylint/extensions/set_membership.py | 52 + .../site-packages/pylint/extensions/typing.py | 558 ++ .../pylint/extensions/while_used.py | 37 + .../python3.10/site-packages/pylint/graph.py | 211 + .../site-packages/pylint/interfaces.py | 38 + .../site-packages/pylint/lint/__init__.py | 48 + .../site-packages/pylint/lint/base_options.py | 595 ++ .../site-packages/pylint/lint/caching.py | 71 + .../pylint/lint/expand_modules.py | 185 + .../pylint/lint/message_state_handler.py | 444 ++ .../site-packages/pylint/lint/parallel.py | 173 + .../site-packages/pylint/lint/pylinter.py | 1357 ++++ .../pylint/lint/report_functions.py | 85 + .../site-packages/pylint/lint/run.py | 270 + .../site-packages/pylint/lint/utils.py | 135 + .../site-packages/pylint/message/__init__.py | 17 + .../pylint/message/_deleted_message_ids.py | 179 + .../site-packages/pylint/message/message.py | 75 + .../pylint/message/message_definition.py | 131 + .../message/message_definition_store.py | 118 + .../pylint/message/message_id_store.py | 160 + .../python3.10/site-packages/pylint/py.typed | 0 .../pylint/pyreverse/__init__.py | 7 + .../pylint/pyreverse/diadefslib.py | 295 + .../pylint/pyreverse/diagrams.py | 368 ++ .../pylint/pyreverse/dot_printer.py | 190 + .../pylint/pyreverse/inspector.py | 569 ++ .../site-packages/pylint/pyreverse/main.py | 367 ++ .../pylint/pyreverse/mermaidjs_printer.py | 131 + .../pylint/pyreverse/plantuml_printer.py | 100 + .../site-packages/pylint/pyreverse/printer.py | 133 + .../pylint/pyreverse/printer_factory.py | 22 + .../site-packages/pylint/pyreverse/utils.py | 269 + .../site-packages/pylint/pyreverse/writer.py | 214 + .../pylint/reporters/__init__.py | 34 + .../pylint/reporters/base_reporter.py | 89 + .../pylint/reporters/collecting_reporter.py | 28 + .../pylint/reporters/json_reporter.py | 201 + .../pylint/reporters/multi_reporter.py | 111 + .../pylint/reporters/progress_reporters.py | 31 + .../reporters/reports_handler_mix_in.py | 83 + .../site-packages/pylint/reporters/text.py | 298 + .../pylint/reporters/ureports/__init__.py | 7 + .../pylint/reporters/ureports/base_writer.py | 107 + .../pylint/reporters/ureports/nodes.py | 194 + .../pylint/reporters/ureports/text_writer.py | 108 + .../pylint/testutils/__init__.py | 35 + .../pylint/testutils/_primer/__init__.py | 10 + .../testutils/_primer/package_to_lint.py | 137 + .../pylint/testutils/_primer/primer.py | 129 + .../testutils/_primer/primer_command.py | 39 + .../_primer/primer_compare_command.py | 174 + .../_primer/primer_prepare_command.py | 48 + .../testutils/_primer/primer_run_command.py | 109 + .../site-packages/pylint/testutils/_run.py | 41 + .../pylint/testutils/checker_test_case.py | 85 + .../pylint/testutils/configuration_test.py | 148 + .../pylint/testutils/constants.py | 31 + .../pylint/testutils/decorator.py | 37 + .../pylint/testutils/functional/__init__.py | 23 + .../functional/find_functional_tests.py | 144 + .../functional/lint_module_output_update.py | 43 + .../pylint/testutils/functional/test_file.py | 129 + .../pylint/testutils/get_test_info.py | 50 + .../pylint/testutils/global_test_linter.py | 20 + .../pylint/testutils/lint_module_test.py | 342 + .../pylint/testutils/output_line.py | 121 + .../pylint/testutils/pyreverse.py | 131 + .../pylint/testutils/reporter_for_tests.py | 79 + .../pylint/testutils/testing_pylintrc | 13 + .../pylint/testutils/tokenize_str.py | 13 + .../pylint/testutils/unittest_linter.py | 84 + .../site-packages/pylint/testutils/utils.py | 107 + .../python3.10/site-packages/pylint/typing.py | 138 + .../site-packages/pylint/utils/__init__.py | 49 + .../site-packages/pylint/utils/ast_walker.py | 102 + .../site-packages/pylint/utils/docs.py | 96 + .../site-packages/pylint/utils/file_state.py | 254 + .../site-packages/pylint/utils/linterstats.py | 408 ++ .../pylint/utils/pragma_parser.py | 135 + .../site-packages/pylint/utils/utils.py | 335 + .../tomli-2.3.0.dist-info/INSTALLER | 1 + .../tomli-2.3.0.dist-info/METADATA | 269 + .../tomli-2.3.0.dist-info/RECORD | 14 + .../site-packages/tomli-2.3.0.dist-info/WHEEL | 4 + .../tomli-2.3.0.dist-info/licenses/LICENSE | 21 + .../site-packages/tomli/__init__.py | 8 + .../python3.10/site-packages/tomli/_parser.py | 777 +++ .../lib/python3.10/site-packages/tomli/_re.py | 115 + .../python3.10/site-packages/tomli/_types.py | 10 + .../python3.10/site-packages/tomli/py.typed | 1 + .../tomlkit-0.13.3.dist-info/INSTALLER | 1 + .../tomlkit-0.13.3.dist-info/LICENSE | 20 + .../tomlkit-0.13.3.dist-info/METADATA | 76 + .../tomlkit-0.13.3.dist-info/RECORD | 32 + .../tomlkit-0.13.3.dist-info/WHEEL | 4 + .../site-packages/tomlkit/__init__.py | 59 + .../site-packages/tomlkit/_compat.py | 22 + .../site-packages/tomlkit/_types.py | 82 + .../site-packages/tomlkit/_utils.py | 158 + .../python3.10/site-packages/tomlkit/api.py | 312 + .../site-packages/tomlkit/container.py | 946 +++ .../site-packages/tomlkit/exceptions.py | 234 + .../python3.10/site-packages/tomlkit/items.py | 2013 ++++++ .../site-packages/tomlkit/parser.py | 1140 ++++ .../python3.10/site-packages/tomlkit/py.typed | 0 .../site-packages/tomlkit/source.py | 180 + .../site-packages/tomlkit/toml_char.py | 52 + .../site-packages/tomlkit/toml_document.py | 7 + .../site-packages/tomlkit/toml_file.py | 59 + .../INSTALLER | 1 + .../METADATA | 72 + .../typing_extensions-4.15.0.dist-info/RECORD | 7 + .../typing_extensions-4.15.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 279 + .../site-packages/typing_extensions.py | 4317 +++++++++++++ 4_data_analysis/MLProject.ipynb | 359 +- ...UN_tourism_caribbean_countries_cleaned.csv | 3026 +++++++++ 466 files changed, 120618 insertions(+), 269 deletions(-) create mode 100755 .venv/bin/get_gprof create mode 100755 .venv/bin/get_objgraph create mode 100755 .venv/bin/isort create mode 100755 .venv/bin/isort-identify-imports create mode 100755 .venv/bin/pylint create mode 100755 .venv/bin/pylint-config create mode 100755 .venv/bin/pyreverse create mode 100755 .venv/bin/symilar create mode 100755 .venv/bin/undill create mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER create mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA create mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD create mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL create mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt create mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt create mode 100644 .venv/lib/python3.10/site-packages/astroid/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/__pkginfo__.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/_ast.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/arguments.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/astroid_manager.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/bases.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_http.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_io.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_random.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_re.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_six.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_type.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/helpers.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/builder.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/const.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/constraint.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/context.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/decorators.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/exceptions.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/filter_statements.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/helpers.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/inference_tip.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/manager.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/modutils.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/as_string.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/const.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/utils.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/objects.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/protocols.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/raw_building.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/rebuilder.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/test_utils.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/transforms.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/typing.py create mode 100644 .venv/lib/python3.10/site-packages/astroid/util.py create mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA create mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD create mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.10/site-packages/dill/__diff.py create mode 100644 .venv/lib/python3.10/site-packages/dill/__info__.py create mode 100644 .venv/lib/python3.10/site-packages/dill/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/dill/_dill.py create mode 100644 .venv/lib/python3.10/site-packages/dill/_objects.py create mode 100644 .venv/lib/python3.10/site-packages/dill/_shims.py create mode 100644 .venv/lib/python3.10/site-packages/dill/detect.py create mode 100644 .venv/lib/python3.10/site-packages/dill/logger.py create mode 100644 .venv/lib/python3.10/site-packages/dill/objtypes.py create mode 100644 .venv/lib/python3.10/site-packages/dill/pointers.py create mode 100644 .venv/lib/python3.10/site-packages/dill/session.py create mode 100644 .venv/lib/python3.10/site-packages/dill/settings.py create mode 100644 .venv/lib/python3.10/site-packages/dill/source.py create mode 100644 .venv/lib/python3.10/site-packages/dill/temp.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/__main__.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_abc.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_check.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_classdef.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_detect.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_diff.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_file.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_functions.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_functors.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_logger.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_mixins.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_module.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_nested.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_objects.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_properties.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_recursive.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_registered.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_restricted.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_selected.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_session.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_source.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_sources.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_temp.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_threads.py create mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_weakref.py create mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA create mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD create mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/isort/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/isort/__main__.py create mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py create mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py create mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed create mode 100644 .venv/lib/python3.10/site-packages/isort/_version.py create mode 100644 .venv/lib/python3.10/site-packages/isort/api.py create mode 100644 .venv/lib/python3.10/site-packages/isort/comments.py create mode 100644 .venv/lib/python3.10/site-packages/isort/core.py create mode 100644 .venv/lib/python3.10/site-packages/isort/deprecated/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/isort/deprecated/finders.py create mode 100644 .venv/lib/python3.10/site-packages/isort/exceptions.py create mode 100644 .venv/lib/python3.10/site-packages/isort/files.py create mode 100644 .venv/lib/python3.10/site-packages/isort/format.py create mode 100644 .venv/lib/python3.10/site-packages/isort/hooks.py create mode 100644 .venv/lib/python3.10/site-packages/isort/identify.py create mode 100644 .venv/lib/python3.10/site-packages/isort/io.py create mode 100644 .venv/lib/python3.10/site-packages/isort/literal.py create mode 100644 .venv/lib/python3.10/site-packages/isort/logo.py create mode 100644 .venv/lib/python3.10/site-packages/isort/main.py create mode 100644 .venv/lib/python3.10/site-packages/isort/output.py create mode 100644 .venv/lib/python3.10/site-packages/isort/parse.py create mode 100644 .venv/lib/python3.10/site-packages/isort/place.py create mode 100644 .venv/lib/python3.10/site-packages/isort/profiles.py create mode 100644 .venv/lib/python3.10/site-packages/isort/py.typed create mode 100644 .venv/lib/python3.10/site-packages/isort/sections.py create mode 100644 .venv/lib/python3.10/site-packages/isort/settings.py create mode 100644 .venv/lib/python3.10/site-packages/isort/setuptools_commands.py create mode 100644 .venv/lib/python3.10/site-packages/isort/sorting.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/all.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py2.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py27.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py3.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py310.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py311.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py312.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py313.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py314.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py36.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py37.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py38.py create mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py39.py create mode 100644 .venv/lib/python3.10/site-packages/isort/utils.py create mode 100644 .venv/lib/python3.10/site-packages/isort/wrap.py create mode 100644 .venv/lib/python3.10/site-packages/isort/wrap_modes.py create mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA create mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD create mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.10/site-packages/mccabe.py create mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA create mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD create mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/platformdirs/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/platformdirs/__main__.py create mode 100644 .venv/lib/python3.10/site-packages/platformdirs/android.py create mode 100644 .venv/lib/python3.10/site-packages/platformdirs/api.py create mode 100644 .venv/lib/python3.10/site-packages/platformdirs/macos.py create mode 100644 .venv/lib/python3.10/site-packages/platformdirs/py.typed create mode 100644 .venv/lib/python3.10/site-packages/platformdirs/unix.py create mode 100644 .venv/lib/python3.10/site-packages/platformdirs/version.py create mode 100644 .venv/lib/python3.10/site-packages/platformdirs/windows.py create mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER create mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA create mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD create mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/REQUESTED create mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL create mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt create mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt create mode 100644 .venv/lib/python3.10/site-packages/pylint/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/__main__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/__pkginfo__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/pass_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/classes/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/classes/class_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/classes/special_methods_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/clear_lru_cache.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/dataclass_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/deprecated.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/design_analysis.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/dunder_methods.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/ellipsis_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/exceptions.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/format.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/imports.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/lambda_expressions.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/logging.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/match_statements_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/method_args.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/misc.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/modified_iterating_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/nested_min_max.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/newstyle.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/non_ascii_names.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/raw_metrics.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/implicit_booleaness_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/not_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/recommendation_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/refactoring_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/spelling.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/stdlib.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/strings.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/symilar.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/threading_checker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/typecheck.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/unicode.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/unsupported_version.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/utils.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/variables.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_breaking_changes/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/generate_command.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/help_message.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/main.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/setup.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/utils.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/argument.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/arguments_manager.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/arguments_provider.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/callback_actions.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/config_file_parser.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/config_initialization.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/deprecation_actions.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/exceptions.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/find_default_config_files.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/help_formatter.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/config/utils.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/constants.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/exceptions.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/_check_docs_utils.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/bad_builtin.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/broad_try_clause.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/check_elif.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/code_style.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/comparison_placement.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/confusing_elif.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/consider_refactoring_into_while_condition.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/consider_ternary_expression.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/dict_init_mutate.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/docparams.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/docstyle.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/dunder.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/empty_comment.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/eq_without_hash.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/for_any_all.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/magic_value.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/mccabe.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/no_self_use.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/overlapping_exceptions.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/private_import.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/redefined_loop_name.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/redefined_variable_type.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/set_membership.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/typing.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/while_used.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/graph.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/interfaces.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/base_options.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/caching.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/expand_modules.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/message_state_handler.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/parallel.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/pylinter.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/report_functions.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/run.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/utils.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/message/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/message/_deleted_message_ids.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/message/message.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/message/message_definition.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/message/message_definition_store.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/message/message_id_store.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/py.typed create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/diadefslib.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/diagrams.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/dot_printer.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/inspector.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/main.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/mermaidjs_printer.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/plantuml_printer.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/printer.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/printer_factory.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/utils.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/writer.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/base_reporter.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/collecting_reporter.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/json_reporter.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/multi_reporter.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/progress_reporters.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/reports_handler_mix_in.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/text.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/ureports/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/ureports/base_writer.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/ureports/nodes.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/ureports/text_writer.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/package_to_lint.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_command.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_compare_command.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_prepare_command.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_run_command.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_run.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/checker_test_case.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/configuration_test.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/constants.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/decorator.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/functional/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/functional/find_functional_tests.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/functional/lint_module_output_update.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/functional/test_file.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/get_test_info.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/global_test_linter.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/lint_module_test.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/output_line.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/pyreverse.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/reporter_for_tests.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/testing_pylintrc create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/tokenize_str.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/unittest_linter.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/utils.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/typing.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/ast_walker.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/docs.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/file_state.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/linterstats.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/pragma_parser.py create mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/utils.py create mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/METADATA create mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/RECORD create mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/tomli/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/tomli/_parser.py create mode 100644 .venv/lib/python3.10/site-packages/tomli/_re.py create mode 100644 .venv/lib/python3.10/site-packages/tomli/_types.py create mode 100644 .venv/lib/python3.10/site-packages/tomli/py.typed create mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/METADATA create mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/RECORD create mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/__init__.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/_compat.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/_types.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/_utils.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/api.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/container.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/exceptions.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/items.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/parser.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/py.typed create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/source.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/toml_char.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/toml_document.py create mode 100644 .venv/lib/python3.10/site-packages/tomlkit/toml_file.py create mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/METADATA create mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/RECORD create mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.10/site-packages/typing_extensions.py create mode 100644 4_data_analysis/UN_tourism_caribbean_countries_cleaned.csv diff --git a/.gitignore b/.gitignore index b1c6cec..0d0fa46 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,469 @@ venv/ *.db *.idea *.ruff_cache + + +"git restore --staged ..." to unstage) + new file: .venv/bin/get_gprof + new file: .venv/bin/get_objgraph + new file: .venv/bin/isort + new file: .venv/bin/isort-identify-imports + new file: .venv/bin/pylint + new file: .venv/bin/pylint-config + new file: .venv/bin/pyreverse + new file: .venv/bin/symilar + new file: .venv/bin/undill + new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER + new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA + new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD + new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL + new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt + new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE + new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt + new file: .venv/lib/python3.10/site-packages/astroid/__init__.py + new file: .venv/lib/python3.10/site-packages/astroid/__pkginfo__.py + new file: .venv/lib/python3.10/site-packages/astroid/_ast.py + new file: .venv/lib/python3.10/site-packages/astroid/arguments.py + new file: .venv/lib/python3.10/site-packages/astroid/astroid_manager.py + new file: .venv/lib/python3.10/site-packages/astroid/bases.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/__init__.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_http.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_io.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_random.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_re.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_six.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_type.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py + new file: .venv/lib/python3.10/site-packages/astroid/brain/helpers.py + new file: .venv/lib/python3.10/site-packages/astroid/builder.py + new file: .venv/lib/python3.10/site-packages/astroid/const.py + new file: .venv/lib/python3.10/site-packages/astroid/constraint.py + new file: .venv/lib/python3.10/site-packages/astroid/context.py + new file: .venv/lib/python3.10/site-packages/astroid/decorators.py + new file: .venv/lib/python3.10/site-packages/astroid/exceptions.py + new file: .venv/lib/python3.10/site-packages/astroid/filter_statements.py + new file: .venv/lib/python3.10/site-packages/astroid/helpers.py + new file: .venv/lib/python3.10/site-packages/astroid/inference_tip.py + new file: .venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py + new file: .venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py + new file: .venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py + new file: .venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py + new file: .venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py + new file: .venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py + new file: .venv/lib/python3.10/site-packages/astroid/manager.py + new file: .venv/lib/python3.10/site-packages/astroid/modutils.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/__init__.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/as_string.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/const.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py + new file: .venv/lib/python3.10/site-packages/astroid/nodes/utils.py + new file: .venv/lib/python3.10/site-packages/astroid/objects.py + new file: .venv/lib/python3.10/site-packages/astroid/protocols.py + new file: .venv/lib/python3.10/site-packages/astroid/raw_building.py + new file: .venv/lib/python3.10/site-packages/astroid/rebuilder.py + new file: .venv/lib/python3.10/site-packages/astroid/test_utils.py + new file: .venv/lib/python3.10/site-packages/astroid/transforms.py + new file: .venv/lib/python3.10/site-packages/astroid/typing.py + new file: .venv/lib/python3.10/site-packages/astroid/util.py + new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER + new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE + new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA + new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD + new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL + new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt + new file: .venv/lib/python3.10/site-packages/dill/__diff.py + new file: .venv/lib/python3.10/site-packages/dill/__info__.py + new file: .venv/lib/python3.10/site-packages/dill/__init__.py + new file: .venv/lib/python3.10/site-packages/dill/_dill.py + new file: .venv/lib/python3.10/site-packages/dill/_objects.py + new file: .venv/lib/python3.10/site-packages/dill/_shims.py + new file: .venv/lib/python3.10/site-packages/dill/detect.py + new file: .venv/lib/python3.10/site-packages/dill/logger.py + new file: .venv/lib/python3.10/site-packages/dill/objtypes.py + new file: .venv/lib/python3.10/site-packages/dill/pointers.py + new file: .venv/lib/python3.10/site-packages/dill/session.py + new file: .venv/lib/python3.10/site-packages/dill/settings.py + new file: .venv/lib/python3.10/site-packages/dill/source.py + new file: .venv/lib/python3.10/site-packages/dill/temp.py + new file: .venv/lib/python3.10/site-packages/dill/tests/__init__.py + new file: .venv/lib/python3.10/site-packages/dill/tests/__main__.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_abc.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_check.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_classdef.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_detect.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_diff.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_file.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_functions.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_functors.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_logger.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_mixins.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_module.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_nested.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_objects.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_properties.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_recursive.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_registered.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_restricted.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_selected.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_session.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_source.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_sources.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_temp.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_threads.py + new file: .venv/lib/python3.10/site-packages/dill/tests/test_weakref.py + new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER + new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA + new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD + new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL + new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt + new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE + new file: .venv/lib/python3.10/site-packages/isort/__init__.py + new file: .venv/lib/python3.10/site-packages/isort/__main__.py + new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE + new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py + new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py + new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py + new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed + new file: .venv/lib/python3.10/site-packages/isort/_version.py + new file: .venv/lib/python3.10/site-packages/isort/api.py + new file: .venv/lib/python3.10/site-packages/isort/comments.py + new file: .venv/lib/python3.10/site-packages/isort/core.py + new file: .venv/lib/python3.10/site-packages/isort/deprecated/__init__.py + new file: .venv/lib/python3.10/site-packages/isort/deprecated/finders.py + new file: .venv/lib/python3.10/site-packages/isort/exceptions.py + new file: .venv/lib/python3.10/site-packages/isort/files.py + new file: .venv/lib/python3.10/site-packages/isort/format.py + new file: .venv/lib/python3.10/site-packages/isort/hooks.py + new file: .venv/lib/python3.10/site-packages/isort/identify.py + new file: .venv/lib/python3.10/site-packages/isort/io.py + new file: .venv/lib/python3.10/site-packages/isort/literal.py + new file: .venv/lib/python3.10/site-packages/isort/logo.py + new file: .venv/lib/python3.10/site-packages/isort/main.py + new file: .venv/lib/python3.10/site-packages/isort/output.py + new file: .venv/lib/python3.10/site-packages/isort/parse.py + new file: .venv/lib/python3.10/site-packages/isort/place.py + new file: .venv/lib/python3.10/site-packages/isort/profiles.py + new file: .venv/lib/python3.10/site-packages/isort/py.typed + new file: .venv/lib/python3.10/site-packages/isort/sections.py + new file: .venv/lib/python3.10/site-packages/isort/settings.py + new file: .venv/lib/python3.10/site-packages/isort/setuptools_commands.py + new file: .venv/lib/python3.10/site-packages/isort/sorting.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/all.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py2.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py27.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py3.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py310.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py311.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py312.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py313.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py314.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py36.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py37.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py38.py + new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py39.py + new file: .venv/lib/python3.10/site-packages/isort/utils.py + new file: .venv/lib/python3.10/site-packages/isort/wrap.py + new file: .venv/lib/python3.10/site-packages/isort/wrap_modes.py + new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER + new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE + new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA + new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD + new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL + new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt + new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt + new file: .venv/lib/python3.10/site-packages/mccabe.py + new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER + new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA + new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD + new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL + new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE + new file: .venv/lib/python3.10/site-packages/platformdirs/__init__.py + new file: .venv/lib/python3.10/site-packages/platformdirs/__main__.py + new file: .venv/lib/python3.10/site-packages/platformdirs/android.py + new file: .venv/lib/python3.10/site-packages/platformdirs/api.py + new file: .venv/lib/python3.10/site-packages/platformdirs/macos.py + new file: .venv/lib/python3.10/site-packages/platformdirs/py.typed + new file: .venv/lib/python3.10/site-packages/platformdirs/unix.py + new file: .venv/lib/python3.10/site-packages/platformdirs/version.py + new file: .venv/lib/python3.10/site-packages/platformdirs/windows.py + new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER + new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA + new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD + new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/REQUESTED + new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL + new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt + new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt + new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE + new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt + new file: .venv/lib/python3.10/site-packages/pylint/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/__main__.py + new file: .venv/lib/python3.10/site-packages/pylint/__pkginfo__.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/pass_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/base_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/classes/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/classes/class_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/classes/special_methods_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/clear_lru_cache.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/dataclass_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/deprecated.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/design_analysis.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/dunder_methods.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/ellipsis_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/exceptions.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/format.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/imports.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/lambda_expressions.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/logging.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/match_statements_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/method_args.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/misc.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/modified_iterating_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/nested_min_max.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/newstyle.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/non_ascii_names.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/raw_metrics.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/implicit_booleaness_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/not_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/recommendation_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/refactoring_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/spelling.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/stdlib.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/strings.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/symilar.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/threading_checker.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/typecheck.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/unicode.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/unsupported_version.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/utils.py + new file: .venv/lib/python3.10/site-packages/pylint/checkers/variables.py + new file: .venv/lib/python3.10/site-packages/pylint/config/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/config/_breaking_changes/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/generate_command.py + new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/help_message.py + new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/main.py + new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/setup.py + new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/utils.py + new file: .venv/lib/python3.10/site-packages/pylint/config/argument.py + new file: .venv/lib/python3.10/site-packages/pylint/config/arguments_manager.py + new file: .venv/lib/python3.10/site-packages/pylint/config/arguments_provider.py + new file: .venv/lib/python3.10/site-packages/pylint/config/callback_actions.py + new file: .venv/lib/python3.10/site-packages/pylint/config/config_file_parser.py + new file: .venv/lib/python3.10/site-packages/pylint/config/config_initialization.py + new file: .venv/lib/python3.10/site-packages/pylint/config/deprecation_actions.py + new file: .venv/lib/python3.10/site-packages/pylint/config/exceptions.py + new file: .venv/lib/python3.10/site-packages/pylint/config/find_default_config_files.py + new file: .venv/lib/python3.10/site-packages/pylint/config/help_formatter.py + new file: .venv/lib/python3.10/site-packages/pylint/config/utils.py + new file: .venv/lib/python3.10/site-packages/pylint/constants.py + new file: .venv/lib/python3.10/site-packages/pylint/exceptions.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/_check_docs_utils.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/bad_builtin.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/broad_try_clause.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/check_elif.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/code_style.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/comparison_placement.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/confusing_elif.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/consider_refactoring_into_while_condition.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/consider_ternary_expression.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/dict_init_mutate.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/docparams.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/docstyle.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/dunder.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/empty_comment.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/eq_without_hash.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/for_any_all.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/magic_value.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/mccabe.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/no_self_use.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/overlapping_exceptions.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/private_import.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/redefined_loop_name.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/redefined_variable_type.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/set_membership.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/typing.py + new file: .venv/lib/python3.10/site-packages/pylint/extensions/while_used.py + new file: .venv/lib/python3.10/site-packages/pylint/graph.py + new file: .venv/lib/python3.10/site-packages/pylint/interfaces.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/base_options.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/caching.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/expand_modules.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/message_state_handler.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/parallel.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/pylinter.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/report_functions.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/run.py + new file: .venv/lib/python3.10/site-packages/pylint/lint/utils.py + new file: .venv/lib/python3.10/site-packages/pylint/message/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/message/_deleted_message_ids.py + new file: .venv/lib/python3.10/site-packages/pylint/message/message.py + new file: .venv/lib/python3.10/site-packages/pylint/message/message_definition.py + new file: .venv/lib/python3.10/site-packages/pylint/message/message_definition_store.py + new file: .venv/lib/python3.10/site-packages/pylint/message/message_id_store.py + new file: .venv/lib/python3.10/site-packages/pylint/py.typed + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/diadefslib.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/diagrams.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/dot_printer.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/inspector.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/main.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/mermaidjs_printer.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/plantuml_printer.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/printer.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/printer_factory.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/utils.py + new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/writer.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/base_reporter.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/collecting_reporter.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/json_reporter.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/multi_reporter.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/progress_reporters.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/reports_handler_mix_in.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/text.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/ureports/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/ureports/base_writer.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/ureports/nodes.py + new file: .venv/lib/python3.10/site-packages/pylint/reporters/ureports/text_writer.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/package_to_lint.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_command.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_compare_command.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_prepare_command.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_run_command.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/_run.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/checker_test_case.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/configuration_test.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/constants.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/decorator.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/functional/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/functional/find_functional_tests.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/functional/lint_module_output_update.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/functional/test_file.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/get_test_info.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/global_test_linter.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/lint_module_test.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/output_line.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/pyreverse.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/reporter_for_tests.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/testing_pylintrc + new file: .venv/lib/python3.10/site-packages/pylint/testutils/tokenize_str.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/unittest_linter.py + new file: .venv/lib/python3.10/site-packages/pylint/testutils/utils.py + new file: .venv/lib/python3.10/site-packages/pylint/typing.py + new file: .venv/lib/python3.10/site-packages/pylint/utils/__init__.py + new file: .venv/lib/python3.10/site-packages/pylint/utils/ast_walker.py + new file: .venv/lib/python3.10/site-packages/pylint/utils/docs.py + new file: .venv/lib/python3.10/site-packages/pylint/utils/file_state.py + new file: .venv/lib/python3.10/site-packages/pylint/utils/linterstats.py + new file: .venv/lib/python3.10/site-packages/pylint/utils/pragma_parser.py + new file: .venv/lib/python3.10/site-packages/pylint/utils/utils.py + new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/INSTALLER + new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/METADATA + new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/RECORD + new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/WHEEL + new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/licenses/LICENSE + new file: .venv/lib/python3.10/site-packages/tomli/__init__.py + new file: .venv/lib/python3.10/site-packages/tomli/_parser.py + new file: .venv/lib/python3.10/site-packages/tomli/_re.py + new file: .venv/lib/python3.10/site-packages/tomli/_types.py + new file: .venv/lib/python3.10/site-packages/tomli/py.typed + new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/INSTALLER + new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/LICENSE + new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/METADATA + new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/RECORD + new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/WHEEL + new file: .venv/lib/python3.10/site-packages/tomlkit/__init__.py + new file: .venv/lib/python3.10/site-packages/tomlkit/_compat.py + new file: .venv/lib/python3.10/site-packages/tomlkit/_types.py + new file: .venv/lib/python3.10/site-packages/tomlkit/_utils.py + new file: .venv/lib/python3.10/site-packages/tomlkit/api.py + new file: .venv/lib/python3.10/site-packages/tomlkit/container.py + new file: .venv/lib/python3.10/site-packages/tomlkit/exceptions.py + new file: .venv/lib/python3.10/site-packages/tomlkit/items.py + new file: .venv/lib/python3.10/site-packages/tomlkit/parser.py + new file: .venv/lib/python3.10/site-packages/tomlkit/py.typed + new file: .venv/lib/python3.10/site-packages/tomlkit/source.py + new file: .venv/lib/python3.10/site-packages/tomlkit/toml_char.py + new file: .venv/lib/python3.10/site-packages/tomlkit/toml_document.py + new file: .venv/lib/python3.10/site-packages/tomlkit/toml_file.py + new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER + new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/METADATA + new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/RECORD + new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/WHEEL + new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE + new file: .venv/lib/python3.10/site-packages/typing_extensions.py diff --git a/.venv/bin/get_gprof b/.venv/bin/get_gprof new file mode 100755 index 0000000..749ade0 --- /dev/null +++ b/.venv/bin/get_gprof @@ -0,0 +1,75 @@ +#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +''' +build profile graph for the given instance + +running: + $ get_gprof + +executes: + gprof2dot -f pstats .prof | dot -Tpng -o .call.png + +where: + are arguments for gprof2dot, such as "-n 5 -e 5" + is code to create the instance to profile + is the class of the instance (i.e. type(instance)) + +For example: + $ get_gprof -n 5 -e 1 "import numpy; numpy.array([1,2])" + +will create 'ndarray.call.png' with the profile graph for numpy.array([1,2]), +where '-n 5' eliminates nodes below 5% threshold, similarly '-e 1' eliminates +edges below 1% threshold +''' + +if __name__ == "__main__": + import sys + if len(sys.argv) < 2: + print ("Please provide an object instance (e.g. 'import math; math.pi')") + sys.exit() + # grab args for gprof2dot + args = sys.argv[1:-1] + args = ' '.join(args) + # last arg builds the object + obj = sys.argv[-1] + obj = obj.split(';') + # multi-line prep for generating an instance + for line in obj[:-1]: + exec(line) + # one-line generation of an instance + try: + obj = eval(obj[-1]) + except Exception: + print ("Error processing object instance") + sys.exit() + + # get object 'name' + objtype = type(obj) + name = getattr(objtype, '__name__', getattr(objtype, '__class__', objtype)) + + # profile dumping an object + import dill + import os + import cProfile + #name = os.path.splitext(os.path.basename(__file__))[0] + cProfile.run("dill.dumps(obj)", filename="%s.prof" % name) + msg = "gprof2dot -f pstats %s %s.prof | dot -Tpng -o %s.call.png" % (args, name, name) + try: + res = os.system(msg) + except Exception: + print ("Please verify install of 'gprof2dot' to view profile graphs") + if res: + print ("Please verify install of 'gprof2dot' to view profile graphs") + + # get stats + f_prof = "%s.prof" % name + import pstats + stats = pstats.Stats(f_prof, stream=sys.stdout) + stats.strip_dirs().sort_stats('cumtime') + stats.print_stats(20) #XXX: save to file instead of print top 20? + os.remove(f_prof) diff --git a/.venv/bin/get_objgraph b/.venv/bin/get_objgraph new file mode 100755 index 0000000..a9944aa --- /dev/null +++ b/.venv/bin/get_objgraph @@ -0,0 +1,54 @@ +#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +display the reference paths for objects in ``dill.types`` or a .pkl file + +Notes: + the generated image is useful in showing the pointer references in + objects that are or can be pickled. Any object in ``dill.objects`` + listed in ``dill.load_types(picklable=True, unpicklable=True)`` works. + +Examples:: + + $ get_objgraph ArrayType + Image generated as ArrayType.png +""" + +import dill as pickle +#pickle.debug.trace(True) +#import pickle + +# get all objects for testing +from dill import load_types +load_types(pickleable=True,unpickleable=True) +from dill import objects + +if __name__ == "__main__": + import sys + if len(sys.argv) != 2: + print ("Please provide exactly one file or type name (e.g. 'IntType')") + msg = "\n" + for objtype in list(objects.keys())[:40]: + msg += objtype + ', ' + print (msg + "...") + else: + objtype = str(sys.argv[-1]) + try: + obj = objects[objtype] + except KeyError: + obj = pickle.load(open(objtype,'rb')) + import os + objtype = os.path.splitext(objtype)[0] + try: + import objgraph + objgraph.show_refs(obj, filename=objtype+'.png') + except ImportError: + print ("Please install 'objgraph' to view object graphs") + + +# EOF diff --git a/.venv/bin/isort b/.venv/bin/isort new file mode 100755 index 0000000..a91b31e --- /dev/null +++ b/.venv/bin/isort @@ -0,0 +1,7 @@ +#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 +import sys +from isort.main import main +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/isort-identify-imports b/.venv/bin/isort-identify-imports new file mode 100755 index 0000000..6c012b8 --- /dev/null +++ b/.venv/bin/isort-identify-imports @@ -0,0 +1,7 @@ +#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 +import sys +from isort.main import identify_imports_main +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(identify_imports_main()) diff --git a/.venv/bin/pylint b/.venv/bin/pylint new file mode 100755 index 0000000..949f9c5 --- /dev/null +++ b/.venv/bin/pylint @@ -0,0 +1,7 @@ +#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 +import sys +from pylint import run_pylint +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(run_pylint()) diff --git a/.venv/bin/pylint-config b/.venv/bin/pylint-config new file mode 100755 index 0000000..1778e49 --- /dev/null +++ b/.venv/bin/pylint-config @@ -0,0 +1,7 @@ +#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 +import sys +from pylint import _run_pylint_config +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(_run_pylint_config()) diff --git a/.venv/bin/pyreverse b/.venv/bin/pyreverse new file mode 100755 index 0000000..d291842 --- /dev/null +++ b/.venv/bin/pyreverse @@ -0,0 +1,7 @@ +#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 +import sys +from pylint import run_pyreverse +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(run_pyreverse()) diff --git a/.venv/bin/symilar b/.venv/bin/symilar new file mode 100755 index 0000000..c4f2f0a --- /dev/null +++ b/.venv/bin/symilar @@ -0,0 +1,7 @@ +#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 +import sys +from pylint import run_symilar +if __name__ == '__main__': + if sys.argv[0].endswith('.exe'): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(run_symilar()) diff --git a/.venv/bin/undill b/.venv/bin/undill new file mode 100755 index 0000000..32e3531 --- /dev/null +++ b/.venv/bin/undill @@ -0,0 +1,22 @@ +#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +unpickle the contents of a pickled object file + +Examples:: + + $ undill hello.pkl + ['hello', 'world'] +""" + +if __name__ == '__main__': + import sys + import dill + for file in sys.argv[1:]: + print (dill.load(open(file,'rb'))) + diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA new file mode 100644 index 0000000..33d9aa9 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA @@ -0,0 +1,122 @@ +Metadata-Version: 2.4 +Name: astroid +Version: 4.0.2 +Summary: An abstract syntax tree for Python with inference support. +License-Expression: LGPL-2.1-or-later +Project-URL: Bug tracker, https://github.com/pylint-dev/astroid/issues +Project-URL: Discord server, https://discord.gg/Egy6P8AMB5 +Project-URL: Docs, https://pylint.readthedocs.io/projects/astroid/en/latest/ +Project-URL: Source Code, https://github.com/pylint-dev/astroid +Keywords: abstract syntax tree,python,static code analysis +Classifier: Development Status :: 6 - Mature +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Topic :: Software Development :: Testing +Requires-Python: >=3.10.0 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: CONTRIBUTORS.txt +Requires-Dist: typing-extensions>=4; python_version < "3.11" +Dynamic: license-file + +Astroid +======= + +.. image:: https://codecov.io/gh/pylint-dev/astroid/branch/main/graph/badge.svg?token=Buxy4WptLb + :target: https://codecov.io/gh/pylint-dev/astroid + :alt: Coverage badge from codecov + +.. image:: https://readthedocs.org/projects/astroid/badge/?version=latest + :target: http://astroid.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + +.. image:: https://results.pre-commit.ci/badge/github/pylint-dev/astroid/main.svg + :target: https://results.pre-commit.ci/latest/github/pylint-dev/astroid/main + :alt: pre-commit.ci status + +.. |tidelift_logo| image:: https://raw.githubusercontent.com/pylint-dev/astroid/main/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png + :width: 200 + :alt: Tidelift + +.. list-table:: + :widths: 10 100 + + * - |tidelift_logo| + - Professional support for astroid is available as part of the + `Tidelift Subscription`_. Tidelift gives software development teams a single source for + purchasing and maintaining their software, with professional grade assurances + from the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-astroid?utm_source=pypi-astroid&utm_medium=referral&utm_campaign=readme + + + +What's this? +------------ + +The aim of this module is to provide a common base representation of +python source code. It is currently the library powering pylint's capabilities. + +It provides a compatible representation which comes from the `_ast` +module. It rebuilds the tree generated by the builtin _ast module by +recursively walking down the AST and building an extended ast. The new +node classes have additional methods and attributes for different +usages. They include some support for static inference and local name +scopes. Furthermore, astroid can also build partial trees by inspecting living +objects. + + +Installation +------------ + +Extract the tarball, jump into the created directory and run:: + + pip install . + + +If you want to do an editable installation, you can run:: + + pip install -e . + + +If you have any questions, please mail the code-quality@python.org +mailing list for support. See +http://mail.python.org/mailman/listinfo/code-quality for subscription +information and archives. + +Documentation +------------- +http://astroid.readthedocs.io/en/latest/ + + +Python Versions +--------------- + +astroid 2.0 is currently available for Python 3 only. If you want Python 2 +support, use an older version of astroid (though note that these versions +are no longer supported). + +Test +---- + +Tests are in the 'test' subdirectory. To launch the whole tests suite, you can use +either `tox` or `pytest`:: + + tox + pytest diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD new file mode 100644 index 0000000..9c8ada9 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD @@ -0,0 +1,197 @@ +astroid-4.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +astroid-4.0.2.dist-info/METADATA,sha256=agB87FwwkVcjxaOosr7YNv7hQ1ANIYy4sJncPUwX98s,4382 +astroid-4.0.2.dist-info/RECORD,, +astroid-4.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt,sha256=c0v8W544hmspwqUGRfzPIcVh6vzof-I35MHcoIl0JEs,9339 +astroid-4.0.2.dist-info/licenses/LICENSE,sha256=_qFr2p5zTeoNnI2fW5CYeO9BcWJjVDKWCf_889tCyyQ,26516 +astroid-4.0.2.dist-info/top_level.txt,sha256=HsdW4O2x7ZXRj6k-agi3RaQybGLobI3VSE-jt4vQUXM,8 +astroid/__init__.py,sha256=FA-4lFE-nkNgJ9nLhh6BDYb2_rIgpFxaS0UX_LFUqC4,7718 +astroid/__pkginfo__.py,sha256=SRnk6MFqpPf0RFnriaY_nHFCzBor39Im0vy9HyYvook,283 +astroid/__pycache__/__init__.cpython-310.pyc,, +astroid/__pycache__/__pkginfo__.cpython-310.pyc,, +astroid/__pycache__/_ast.cpython-310.pyc,, +astroid/__pycache__/arguments.cpython-310.pyc,, +astroid/__pycache__/astroid_manager.cpython-310.pyc,, +astroid/__pycache__/bases.cpython-310.pyc,, +astroid/__pycache__/builder.cpython-310.pyc,, +astroid/__pycache__/const.cpython-310.pyc,, +astroid/__pycache__/constraint.cpython-310.pyc,, +astroid/__pycache__/context.cpython-310.pyc,, +astroid/__pycache__/decorators.cpython-310.pyc,, +astroid/__pycache__/exceptions.cpython-310.pyc,, +astroid/__pycache__/filter_statements.cpython-310.pyc,, +astroid/__pycache__/helpers.cpython-310.pyc,, +astroid/__pycache__/inference_tip.cpython-310.pyc,, +astroid/__pycache__/manager.cpython-310.pyc,, +astroid/__pycache__/modutils.cpython-310.pyc,, +astroid/__pycache__/objects.cpython-310.pyc,, +astroid/__pycache__/protocols.cpython-310.pyc,, +astroid/__pycache__/raw_building.cpython-310.pyc,, +astroid/__pycache__/rebuilder.cpython-310.pyc,, +astroid/__pycache__/test_utils.cpython-310.pyc,, +astroid/__pycache__/transforms.cpython-310.pyc,, +astroid/__pycache__/typing.cpython-310.pyc,, +astroid/__pycache__/util.cpython-310.pyc,, +astroid/_ast.py,sha256=LLKPdNWj6Qk4u3yj9wid9zIOsKf_YV4Yfr8L4EA285U,3003 +astroid/arguments.py,sha256=UwoLKjjgYrmtTHWRTEXuqz4g4elodx_DJIR5vBkCfzo,13094 +astroid/astroid_manager.py,sha256=uGIFUKDTjCdy757OF60r3apRqVybugBZh1rudtOSGMA,729 +astroid/bases.py,sha256=IJyVeO6ssO3QmBZ_2C4_yJO2YvxhtolJPlNisqM2k6A,28007 +astroid/brain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/brain/__pycache__/__init__.cpython-310.pyc,, +astroid/brain/__pycache__/brain_argparse.cpython-310.pyc,, +astroid/brain/__pycache__/brain_attrs.cpython-310.pyc,, +astroid/brain/__pycache__/brain_boto3.cpython-310.pyc,, +astroid/brain/__pycache__/brain_builtin_inference.cpython-310.pyc,, +astroid/brain/__pycache__/brain_collections.cpython-310.pyc,, +astroid/brain/__pycache__/brain_crypt.cpython-310.pyc,, +astroid/brain/__pycache__/brain_ctypes.cpython-310.pyc,, +astroid/brain/__pycache__/brain_curses.cpython-310.pyc,, +astroid/brain/__pycache__/brain_dataclasses.cpython-310.pyc,, +astroid/brain/__pycache__/brain_datetime.cpython-310.pyc,, +astroid/brain/__pycache__/brain_dateutil.cpython-310.pyc,, +astroid/brain/__pycache__/brain_functools.cpython-310.pyc,, +astroid/brain/__pycache__/brain_gi.cpython-310.pyc,, +astroid/brain/__pycache__/brain_hashlib.cpython-310.pyc,, +astroid/brain/__pycache__/brain_http.cpython-310.pyc,, +astroid/brain/__pycache__/brain_hypothesis.cpython-310.pyc,, +astroid/brain/__pycache__/brain_io.cpython-310.pyc,, +astroid/brain/__pycache__/brain_mechanize.cpython-310.pyc,, +astroid/brain/__pycache__/brain_multiprocessing.cpython-310.pyc,, +astroid/brain/__pycache__/brain_namedtuple_enum.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_einsumfunc.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_umath.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_ma.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_ndarray.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_utils.cpython-310.pyc,, +astroid/brain/__pycache__/brain_pathlib.cpython-310.pyc,, +astroid/brain/__pycache__/brain_pkg_resources.cpython-310.pyc,, +astroid/brain/__pycache__/brain_pytest.cpython-310.pyc,, +astroid/brain/__pycache__/brain_qt.cpython-310.pyc,, +astroid/brain/__pycache__/brain_random.cpython-310.pyc,, +astroid/brain/__pycache__/brain_re.cpython-310.pyc,, +astroid/brain/__pycache__/brain_regex.cpython-310.pyc,, +astroid/brain/__pycache__/brain_responses.cpython-310.pyc,, +astroid/brain/__pycache__/brain_scipy_signal.cpython-310.pyc,, +astroid/brain/__pycache__/brain_signal.cpython-310.pyc,, +astroid/brain/__pycache__/brain_six.cpython-310.pyc,, +astroid/brain/__pycache__/brain_sqlalchemy.cpython-310.pyc,, +astroid/brain/__pycache__/brain_ssl.cpython-310.pyc,, +astroid/brain/__pycache__/brain_statistics.cpython-310.pyc,, +astroid/brain/__pycache__/brain_subprocess.cpython-310.pyc,, +astroid/brain/__pycache__/brain_threading.cpython-310.pyc,, +astroid/brain/__pycache__/brain_type.cpython-310.pyc,, +astroid/brain/__pycache__/brain_typing.cpython-310.pyc,, +astroid/brain/__pycache__/brain_unittest.cpython-310.pyc,, +astroid/brain/__pycache__/brain_uuid.cpython-310.pyc,, +astroid/brain/__pycache__/helpers.cpython-310.pyc,, +astroid/brain/brain_argparse.py,sha256=SxYW42PcxTv0YM4VGAS_K8iYnYDxZ5LQZeptooyIMo0,1725 +astroid/brain/brain_attrs.py,sha256=y6xnsBRIhXiJucZPjtBsGM-hNVMMOttBnxIHNE65YRU,3567 +astroid/brain/brain_boto3.py,sha256=efZTk72fPOe6wnEVCH9bIBFWxcQDlS5xrzuNakpR06Y,1112 +astroid/brain/brain_builtin_inference.py,sha256=WApCZIQF5FkFOO83PRdGt2I3vNCMEg8h5gGPwC3E6-U,37758 +astroid/brain/brain_collections.py,sha256=VetyHBQO9nWQmaDeujEOfZ92JANUpgIAWLH5-jT6uZ4,4787 +astroid/brain/brain_crypt.py,sha256=UKa9GpbK7b_N2fAx4-KNqr4Pvsg3EI1Mu1T3uuV3CcE,957 +astroid/brain/brain_ctypes.py,sha256=ZlOQHSIyvslgdYC_a0hOop3_I5uBh3afbQ8ZuaZxke0,2762 +astroid/brain/brain_curses.py,sha256=Ne_30je70tZpUyobl3b0Y5ATZhxolYCxfP-oLB4eciM,3571 +astroid/brain/brain_dataclasses.py,sha256=gzBGXp0cDeq-LV1spl-2kZtfKDltJdGni0c5SQg6suE,22119 +astroid/brain/brain_datetime.py,sha256=q9kjCi-Fz5S-u4Hyv88mJvCgXYvk2X8eEUTaw637lK4,813 +astroid/brain/brain_dateutil.py,sha256=y_GyQYVf_UR6zTOcgmVm3uQhkxVCVVzRD0Ow4Gg1jFI,861 +astroid/brain/brain_functools.py,sha256=I8nxIKGTcQKq5ZeGAiFbm3ErlZfLnpdUYy31pLXxwxc,6401 +astroid/brain/brain_gi.py,sha256=krGyADdWRjm7w-ZVTHbx77hylNgs8ziwZ1--RU6QhxM,7662 +astroid/brain/brain_hashlib.py,sha256=c76QAbD-LYkKMVrdAe6nRboJdG3l1LkK0UAZOlxfSFM,2799 +astroid/brain/brain_http.py,sha256=Vj7mN4uU8qROy-p9BjLOhF9_HTZ0wFbiNuDgi2uXKY4,11668 +astroid/brain/brain_hypothesis.py,sha256=8fwM59eqVgKEL3Dkz2Uva62v0-AZhBPyFxXyOs65E0g,1885 +astroid/brain/brain_io.py,sha256=YV_hxg1EGjV3Wprvp5M_YF8pWoC2MK1oyyH3io6hZpk,1589 +astroid/brain/brain_mechanize.py,sha256=-80L1FfddRUM-qENpAZ5sro9P9Kgz2uchwjjM5ErJJk,2740 +astroid/brain/brain_multiprocessing.py,sha256=ATm2DwP-SXt4LH6hhwN-7ova5Bt483efnuKzKkulmfQ,3260 +astroid/brain/brain_namedtuple_enum.py,sha256=KDWuKv3mAHE1IYXu7CMCb9bMM7vmQnxY8ON96jgUuVk,24278 +astroid/brain/brain_numpy_core_einsumfunc.py,sha256=YfPheziU5UKb80hXS0VZcWVHmw_lQ1t0-Q-w13dwQyM,885 +astroid/brain/brain_numpy_core_fromnumeric.py,sha256=Aum9F0w1An28elWtvBcTHzohV0YCkIOqg8nIqkm179c,834 +astroid/brain/brain_numpy_core_function_base.py,sha256=qSusSR1lilumpc6T8mrELunZS3zgUuuouh-qzNBiBcc,1356 +astroid/brain/brain_numpy_core_multiarray.py,sha256=s9adpaJ8SAk3WTgSEuNgCUwx8HOGt_vyZdXl3QJ1OmE,4408 +astroid/brain/brain_numpy_core_numeric.py,sha256=B95vP691ynDRXFqhg_VI1TKyWkxGbdCZji3M8qsyTmc,1692 +astroid/brain/brain_numpy_core_numerictypes.py,sha256=Bgc4_qguu3ypTIr74STkeUu79OhtLwrD0jVUFSBZ1wA,8648 +astroid/brain/brain_numpy_core_umath.py,sha256=M2_oE2HGcD5DEcU3W2_CvENjxjuOxfLPvnSgFjtmpU4,4981 +astroid/brain/brain_numpy_ma.py,sha256=3UnzHTkN8H6yxI8km6bBZoWyV80U3vLIzfFyD8gEOLo,990 +astroid/brain/brain_numpy_ndarray.py,sha256=qoNm2LSOkaerQus6wOj9Lxbjgh7SAg4bEbfPQbZKO6c,9034 +astroid/brain/brain_numpy_random_mtrand.py,sha256=d3g_v9X1e4Wr8I9EE8g7E0d-hqGQk_gYvByj4-1HC5w,3538 +astroid/brain/brain_numpy_utils.py,sha256=03ztDMdAFnQLFmTOiEeABcSBv_gL46Mpi9rO2rbw0M0,2893 +astroid/brain/brain_pathlib.py,sha256=jX_TTTpf4wp1lePKTSGFFjbQVSa13fXafAfdIqow9JM,1728 +astroid/brain/brain_pkg_resources.py,sha256=rfjLwAejZVqM_s721UdAPbuEMyM0fx3sb1yYkPDeDqE,2302 +astroid/brain/brain_pytest.py,sha256=tqisdV1Cw2vdYdjTNnF_jBMO2jO7lw86Fjh6XpZoJ1g,2312 +astroid/brain/brain_qt.py,sha256=yNle65lxFYmRJksiaqksF_FINvae732yvcOl6PVdIvM,2874 +astroid/brain/brain_random.py,sha256=VMVXkVHEhdQ8S5VaW-tHS3hQYmQj4XI66Y9tV1qDjTE,3110 +astroid/brain/brain_re.py,sha256=-Dh05FNgXrWxOS-mrajf7LFUgozA7qFQzfjs8SwNoxU,2999 +astroid/brain/brain_regex.py,sha256=GI-TCq6gGAE9OYwyl2Bi9auJINJn0uF8ADBYekyKZyU,3466 +astroid/brain/brain_responses.py,sha256=vC9JUtdjtN2W5ux8_6G7QG_nhbZOBV3lMpy1fG56nzc,1962 +astroid/brain/brain_scipy_signal.py,sha256=HPGStodLs9L9APFnc77RZBf-PIlaKm-COdyTSk2KVnM,2370 +astroid/brain/brain_signal.py,sha256=Qt8cFsEwYmwaI_PU5aBBfYV4TLkF0lBPdME-oeGWMYk,3932 +astroid/brain/brain_six.py,sha256=ps49tRfn6NRtTpjmcBOfrF8shznz0T1SB7Pn1teWTIs,7772 +astroid/brain/brain_sqlalchemy.py,sha256=8JlRq_TCEheDtbc2BIupibHRtEZ4WasqLEPkkZ5sziY,1103 +astroid/brain/brain_ssl.py,sha256=MOU0L1wh-ysKzACAKVTnpCca6awwwsF09OcJi5-NKfk,6712 +astroid/brain/brain_statistics.py,sha256=howkDIgRSFh6Zy20ZS2OCN4HQxfEStiIwsICMOZ5bi4,2786 +astroid/brain/brain_subprocess.py,sha256=dj-Y3S_QbWaIsDA6j8a74xEwPd5Fe5J1uj1YDFDrcrQ,2962 +astroid/brain/brain_threading.py,sha256=3XS9DNglTezqvBUlWTKbhjWLgkjXmQR9w-p5sNPJLO4,964 +astroid/brain/brain_type.py,sha256=sThdsmoQwUxIe04m5Wb6WnouGk1ZmZWtBo6AfXqWLsA,2497 +astroid/brain/brain_typing.py,sha256=b5Lvle7K9vhxzvFqH3BOwB8aYLPjiex-6FQCdupDvZk,17237 +astroid/brain/brain_unittest.py,sha256=ejWKg85j26dfKWHwycKztGs4_RSPTtqs9mTF3XKfsyA,1178 +astroid/brain/brain_uuid.py,sha256=K1so-CZChY4lqI-qWQbdrrJu_z0gJPPG4h4J3Iz9FoU,678 +astroid/brain/helpers.py,sha256=VIf9Yy6SVaXBQ0Lh1oLEa0szhJRXuyTR4qANXhAeJBs,4743 +astroid/builder.py,sha256=pk_U8QDLwQmwZMnDRK7Z8EnJjeCLHV_7msGIf1AgVGE,19169 +astroid/const.py,sha256=dcdtuOYk6xd_5K9kLZcijEp93kXYzpb_GrmHBGccs4Q,694 +astroid/constraint.py,sha256=BzI-DMG1Vp3eapvmVjahYEL9VYSRoluGkTscokNUipc,6483 +astroid/context.py,sha256=xseGQ6wscT5eOPiLl7nd6nS7Mfwi3KrQGSScPlBU6h4,6293 +astroid/decorators.py,sha256=aUEm2t4UM31X8vpGi5BJb3jOcK44LtZQRUl9H1Dlqh4,8531 +astroid/exceptions.py,sha256=9OrH8LliIRyv85CQEmm0br0R0XWuRJ74tmIDAv2QR_s,12935 +astroid/filter_statements.py,sha256=CJRqKTLsspIfHlLjHQsEpfLjscjPoOtLWdLuVhb7OoM,9451 +astroid/helpers.py,sha256=7r3xj2r2DriL9J8IbuIlmim-T7aC1Jog7GbJ8R6TM7U,11819 +astroid/inference_tip.py,sha256=5Dsfn-9qie1Wuzc2XaWzhlPQQxD3fGt64RR3nyptvHc,4586 +astroid/interpreter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/interpreter/__pycache__/__init__.cpython-310.pyc,, +astroid/interpreter/__pycache__/dunder_lookup.cpython-310.pyc,, +astroid/interpreter/__pycache__/objectmodel.cpython-310.pyc,, +astroid/interpreter/_import/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/interpreter/_import/__pycache__/__init__.cpython-310.pyc,, +astroid/interpreter/_import/__pycache__/spec.cpython-310.pyc,, +astroid/interpreter/_import/__pycache__/util.cpython-310.pyc,, +astroid/interpreter/_import/spec.py,sha256=uVQdyJ7OTJzTM50EVmyXmN2bMUR_D-EXI36CC-NTPBU,17613 +astroid/interpreter/_import/util.py,sha256=XCvSEZ_iTHUfvIX9YrPjp4LVgA0IuJSsOnLwlpuGN6A,4675 +astroid/interpreter/dunder_lookup.py,sha256=miAYo7UTVOhfMYpo44qdV3WJEk1P__JVTGWGPtT1wnk,2487 +astroid/interpreter/objectmodel.py,sha256=gP8LsWsw3uzXWh4QhecNom27Q8N7MKN4ipGMg_abSuo,34180 +astroid/manager.py,sha256=adI6Dc8b0HqScUoGet-FzV90CHguHpJ-s2Ne-fwgdOM,18331 +astroid/modutils.py,sha256=KAyhpORelvLrHnAVoiX5HDhDnMCuAHhoqaox2E6rSdw,23458 +astroid/nodes/__init__.py,sha256=X6V4GWkgUL8AuqfzK_Ns7jpCM_RK14UMyY8KMhEqZ8o,4862 +astroid/nodes/__pycache__/__init__.cpython-310.pyc,, +astroid/nodes/__pycache__/_base_nodes.cpython-310.pyc,, +astroid/nodes/__pycache__/as_string.cpython-310.pyc,, +astroid/nodes/__pycache__/const.cpython-310.pyc,, +astroid/nodes/__pycache__/node_classes.cpython-310.pyc,, +astroid/nodes/__pycache__/node_ng.cpython-310.pyc,, +astroid/nodes/__pycache__/utils.cpython-310.pyc,, +astroid/nodes/_base_nodes.py,sha256=IY3vZ4vEvsAepGTvrPf7zm11BPTlRQUndzyT9CmEsZ8,23927 +astroid/nodes/as_string.py,sha256=ap_dHTQTE9xyxb6GndRrd4TS4xdfhDadCW0NcGmktqQ,29106 +astroid/nodes/const.py,sha256=aD7rKF5kPM2UFJAR5pzZDAM-Zm7p9LG57E-9FjB1gTc,807 +astroid/nodes/node_classes.py,sha256=G4zKRdUNdp-u-wJWPdE72J5mBN5_bXkqKo6g5txIDwo,177422 +astroid/nodes/node_ng.py,sha256=SzcLmkZiuv2Pk4q6iPSfokPRdGaM9VqPLQqrJCaCKOE,26435 +astroid/nodes/scoped_nodes/__init__.py,sha256=YXf0Sc8m3HlzbG9IGtX2aHTSGbG4nHfxl9_kY_QLs1g,1271 +astroid/nodes/scoped_nodes/__pycache__/__init__.cpython-310.pyc,, +astroid/nodes/scoped_nodes/__pycache__/mixin.cpython-310.pyc,, +astroid/nodes/scoped_nodes/__pycache__/scoped_nodes.cpython-310.pyc,, +astroid/nodes/scoped_nodes/__pycache__/utils.cpython-310.pyc,, +astroid/nodes/scoped_nodes/mixin.py,sha256=kDdvUEZRSPmQ88sLD2edjalmU88-UZbUIaBERzCy2Ew,7151 +astroid/nodes/scoped_nodes/scoped_nodes.py,sha256=32D6acbmS94Ojdf5kljqOOCQ_roMKaV5x6Ff5ctnSOY,100704 +astroid/nodes/scoped_nodes/utils.py,sha256=rBKL_c6byvOWq7yzRSMNWsZQIe5smST8Dnpz40Nni7Q,1181 +astroid/nodes/utils.py,sha256=5strAqVk0zh9cOD4nH8EZiIsaDn5-gLIT2U1hAoY9wU,433 +astroid/objects.py,sha256=qEuJQ7WfBxxs2p1vpNGqJ2aunfXl5kUwVgZhdNu-n7c,12676 +astroid/protocols.py,sha256=tMR8XAboNiDOqlbdfoxjU66TNl0AD3OJ0Yll2zqCnsg,32562 +astroid/raw_building.py,sha256=hohV5ukMBC2xYJ9ehc6_QV3sop3i0YRkUY-hb6ckumg,25307 +astroid/rebuilder.py,sha256=Jv3xq3M2jdrJS1Y9n0wvYJbHB2ly9-UbXE2pelBbQsw,71520 +astroid/test_utils.py,sha256=gyLSvyMM7QfsfqZhmVW6nM1whn1ArMWTsiHi8_Jy060,2474 +astroid/transforms.py,sha256=AARfSAiYVQ6Ale33UOEkn0n-QjAgJ9xI3HZenReFisA,5946 +astroid/typing.py,sha256=fYk-NAnlC7_prv9jvfnXcL32278yJ65x-qimK32tW_o,2807 +astroid/util.py,sha256=H0hAr2TTxsK97137v9gp59-JmuCMWABmmI_0UOOM9N0,4846 diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL new file mode 100644 index 0000000..e7fa31b --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt new file mode 100644 index 0000000..67068c0 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt @@ -0,0 +1,226 @@ +# This file is autocompleted by 'contributors-txt', +# using the configuration in 'script/.contributors_aliases.json'. +# Do not add new persons manually and only add information without +# using '-' as the line first character. +# Please verify that your change are stable if you modify manually. + +Ex-maintainers +-------------- +- Claudiu Popa +- Sylvain Thénault +- Torsten Marek + + +Maintainers +----------- +- Pierre Sassoulas +- Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> +- Jacob Walls +- Marc Mueller <30130371+cdce8p@users.noreply.github.com> +- Hippo91 +- Bryce Guinta +- Ceridwen +- Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> +- Łukasz Rogalski +- Florian Bruhin +- Ashley Whetter +- Dimitri Prybysh +- Areveny + + +Contributors +------------ +- Emile Anclin +- Nick Drozd +- correctmost <134317971+correctmost@users.noreply.github.com> +- Andrew Haigh +- Julien Cristau +- Artem Yurchenko <44875844+temyurchenko@users.noreply.github.com> +- David Liu +- Alexandre Fayolle +- Eevee (Alex Munroe) +- David Gilman +- Tushar Sadhwani +- Matus Valo +- Julien Jehannet +- Hugo van Kemenade +- Calen Pennington +- Antonio +- Akhil Kamat +- Zen Lee <53538590+zenlyj@users.noreply.github.com> +- Tim Martin +- Phil Schaf +- Alex Hall +- Raphael Gaschignard +- Radosław Ganczarek +- Paligot Gérard +- Ioana Tagirta +- Eric Vergnaud +- Derek Gustafson +- David Shea +- Daniel Harding +- Christian Clauss +- Ville Skyttä +- Synrom <30272537+Synrom@users.noreply.github.com> +- Rene Zhang +- Philip Lorenz +- Nicolas Chauvat +- Michael K +- Mario Corchero +- Marien Zwart +- Laura Médioni +- James Addison <55152140+jayaddison@users.noreply.github.com> +- FELD Boris +- Enji Cooper +- Dani Alcala <112832187+clavedeluna@users.noreply.github.com> +- Adrien Di Mascio +- tristanlatr <19967168+tristanlatr@users.noreply.github.com> +- grayjk +- emile@crater.logilab.fr +- doranid +- brendanator +- Tomas Gavenciak +- Tim Paine +- Thomas Hisch +- Stefan Scherfke +- Sergei Lebedev <185856+superbobry@users.noreply.github.com> +- Saugat Pachhai (सौगात) +- Robert Hofer <1058012+hofrob@users.noreply.github.com> +- Ram Rachum +- Pierre-Yves David +- Peter Pentchev +- Peter Kolbus +- Omer Katz +- Moises Lopez +- Mitch Harding +- Michal Vasilek +- Keichi Takahashi +- Kavins Singh +- Karthikeyan Singaravelan +- Joshua Cannon +- John Vandenberg +- Jacob Bogdanov +- Google, Inc. +- Emmanuel Ferdman +- David Euresti +- David Douard +- David Cain +- Anthony Truchet +- Anthony Sottile +- Alexander Shadchin +- wgehalo +- tejaschauhan36912 <59693377+tejaschauhan36912@users.noreply.github.com> +- rr- +- raylu +- plucury +- pavan-msys <149513767+pavan-msys@users.noreply.github.com> +- ostr00000 +- noah-weingarden <33741795+noah-weingarden@users.noreply.github.com> +- nathannaveen <42319948+nathannaveen@users.noreply.github.com> +- mathieui +- markmcclain +- ioanatia +- alm +- adam-grant-hendry <59346180+adam-grant-hendry@users.noreply.github.com> +- aatle <168398276+aatle@users.noreply.github.com> +- Zbigniew Jędrzejewski-Szmek +- Zac Hatfield-Dodds +- Vilnis Termanis +- Valentin Valls +- Uilian Ries +- Tomas Novak +- Thirumal Venkat +- SupImDos <62866982+SupImDos@users.noreply.github.com> +- Stéphane Brunner +- Stanislav Levin +- Simon Hewitt +- Serhiy Storchaka +- Roy Wright +- Robin Jarry +- René Fritze <47802+renefritze@users.noreply.github.com> +- Redoubts +- Philipp Hörist +- Peter de Blanc +- Peter Talley +- Ovidiu Sabou +- Oleh Prypin +- Nicolas Noirbent +- Neil Girdhar +- Miro Hrončok +- Michał Masłowski +- Mateusz Bysiek +- Matej Aleksandrov +- Marcelo Trylesinski +- Leandro T. C. Melo +- Konrad Weihmann +- Kian Meng, Ang +- Kai Mueller <15907922+kasium@users.noreply.github.com> +- Jörg Thalheim +- Jérome Perrin +- JulianJvn <128477611+JulianJvn@users.noreply.github.com> +- Josef Kemetmüller +- Jonathan Striebel +- John Belmonte +- Jeff Widman +- Jeff Quast +- Jarrad Hope +- Jared Garst +- Jamie Scott +- Jakub Wilk +- Iva Miholic +- Ionel Maries Cristian +- HoverHell +- Hashem Nasarat +- HQupgradeHQ <18361586+HQupgradeHQ@users.noreply.github.com> +- Gwyn Ciesla +- Grygorii Iermolenko +- Gregory P. Smith +- Giuseppe Scrivano +- Frédéric Chapoton +- Francis Charette Migneault +- Felix Mölder +- Federico Bond +- DudeNr33 <3929834+DudeNr33@users.noreply.github.com> +- Dmitry Shachnev +- Denis Laxalde +- Deepyaman Datta +- David Poirier +- Dave Hirschfeld +- Dave Baum +- Daniel Martin +- Daniel Colascione +- Damien Baty +- Craig Franklin +- Colin Kennedy +- Cole Robinson +- Christoph Reiter +- Chris Philip +- Charlie Ringström <34444482+Chasarr@users.noreply.github.com> +- BioGeek +- Bianca Power <30207144+biancapower@users.noreply.github.com> +- Benjamin Elven <25181435+S3ntinelX@users.noreply.github.com> +- Ben Elliston +- Becker Awqatty +- Batuhan Taskaya +- BasPH +- Azeem Bande-Ali +- Avram Lubkin +- Aru Sahni +- Artsiom Kaval +- Anubhav <35621759+anubh-v@users.noreply.github.com> +- Antoine Boellinger +- Alphadelta14 +- Alexander Scheel +- Alexander Presnyakov +- Ahmed Azzaoui + +Co-Author +--------- +The following persons were credited manually but did not commit themselves +under this name, or we did not manage to find their commits in the history. + +- François Mockers +- platings +- carl +- alain lefroy +- Mark Gius diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE new file mode 100644 index 0000000..182e0fb --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE @@ -0,0 +1,508 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt new file mode 100644 index 0000000..450d4fe --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +astroid diff --git a/.venv/lib/python3.10/site-packages/astroid/__init__.py b/.venv/lib/python3.10/site-packages/astroid/__init__.py new file mode 100644 index 0000000..abb45cf --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/__init__.py @@ -0,0 +1,242 @@ +# 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 +""" + +# 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 Context +from astroid.exceptions import ( + AstroidBuildingError, + 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 as _DEPRECATED_AnnAssign, + Arguments as _DEPRECATED_Arguments, + Assert as _DEPRECATED_Assert, + Assign as _DEPRECATED_Assign, + AssignAttr as _DEPRECATED_AssignAttr, + AssignName as _DEPRECATED_AssignName, + AsyncFor as _DEPRECATED_AsyncFor, + AsyncFunctionDef as _DEPRECATED_AsyncFunctionDef, + AsyncWith as _DEPRECATED_AsyncWith, + Attribute as _DEPRECATED_Attribute, + AugAssign as _DEPRECATED_AugAssign, + Await as _DEPRECATED_Await, + BinOp as _DEPRECATED_BinOp, + BoolOp as _DEPRECATED_BoolOp, + Break as _DEPRECATED_Break, + Call as _DEPRECATED_Call, + ClassDef as _DEPRECATED_ClassDef, + Compare as _DEPRECATED_Compare, + Comprehension as _DEPRECATED_Comprehension, + ComprehensionScope as _DEPRECATED_ComprehensionScope, + Const as _DEPRECATED_Const, + Continue as _DEPRECATED_Continue, + Decorators as _DEPRECATED_Decorators, + DelAttr as _DEPRECATED_DelAttr, + Delete as _DEPRECATED_Delete, + DelName as _DEPRECATED_DelName, + Dict as _DEPRECATED_Dict, + DictComp as _DEPRECATED_DictComp, + DictUnpack as _DEPRECATED_DictUnpack, + EmptyNode as _DEPRECATED_EmptyNode, + EvaluatedObject as _DEPRECATED_EvaluatedObject, + ExceptHandler as _DEPRECATED_ExceptHandler, + Expr as _DEPRECATED_Expr, + For as _DEPRECATED_For, + FormattedValue as _DEPRECATED_FormattedValue, + FunctionDef as _DEPRECATED_FunctionDef, + GeneratorExp as _DEPRECATED_GeneratorExp, + Global as _DEPRECATED_Global, + If as _DEPRECATED_If, + IfExp as _DEPRECATED_IfExp, + Import as _DEPRECATED_Import, + ImportFrom as _DEPRECATED_ImportFrom, + Interpolation as _DEPRECATED_Interpolation, + JoinedStr as _DEPRECATED_JoinedStr, + Keyword as _DEPRECATED_Keyword, + Lambda as _DEPRECATED_Lambda, + List as _DEPRECATED_List, + ListComp as _DEPRECATED_ListComp, + Match as _DEPRECATED_Match, + MatchAs as _DEPRECATED_MatchAs, + MatchCase as _DEPRECATED_MatchCase, + MatchClass as _DEPRECATED_MatchClass, + MatchMapping as _DEPRECATED_MatchMapping, + MatchOr as _DEPRECATED_MatchOr, + MatchSequence as _DEPRECATED_MatchSequence, + MatchSingleton as _DEPRECATED_MatchSingleton, + MatchStar as _DEPRECATED_MatchStar, + MatchValue as _DEPRECATED_MatchValue, + Module as _DEPRECATED_Module, + Name as _DEPRECATED_Name, + NamedExpr as _DEPRECATED_NamedExpr, + NodeNG as _DEPRECATED_NodeNG, + Nonlocal as _DEPRECATED_Nonlocal, + ParamSpec as _DEPRECATED_ParamSpec, + Pass as _DEPRECATED_Pass, + Raise as _DEPRECATED_Raise, + Return as _DEPRECATED_Return, + Set as _DEPRECATED_Set, + SetComp as _DEPRECATED_SetComp, + Slice as _DEPRECATED_Slice, + Starred as _DEPRECATED_Starred, + Subscript as _DEPRECATED_Subscript, + TemplateStr as _DEPRECATED_TemplateStr, + Try as _DEPRECATED_Try, + TryStar as _DEPRECATED_TryStar, + Tuple as _DEPRECATED_Tuple, + TypeAlias as _DEPRECATED_TypeAlias, + TypeVar as _DEPRECATED_TypeVar, + TypeVarTuple as _DEPRECATED_TypeVarTuple, + UnaryOp as _DEPRECATED_UnaryOp, + Unknown as _DEPRECATED_Unknown, + While as _DEPRECATED_While, + With as _DEPRECATED_With, + Yield as _DEPRECATED_Yield, + YieldFrom as _DEPRECATED_YieldFrom, + are_exclusive, + builtin_lookup, + unpack_infer, + function_to_method, +) + +# isort: on + +from astroid.util import Uninferable + +__all__ = [ + "CONST_CLS", + "MANAGER", + "AstroidBuildingError", + "AstroidError", + "AstroidImportError", + "AstroidIndexError", + "AstroidSyntaxError", + "AstroidTypeError", + "AstroidValueError", + "AttributeInferenceError", + "BaseInstance", + "BoundMethod", + "Context", + "DuplicateBasesError", + "ExceptionInstance", + "InconsistentMroError", + "InferenceError", + "InferenceOverwriteError", + "Instance", + "MroError", + "NameInferenceError", + "NoDefault", + "NotFoundError", + "ParentMissingError", + "ResolveError", + "StatementMissing", + "SuperArgumentTypeError", + "SuperError", + "TooManyLevelsError", + "UnboundMethod", + "Uninferable", + "UnresolvableName", + "UseInferenceDefault", + "__version__", + "_inference_tip_cached", + "are_exclusive", + "builtin_lookup", + "extract_node", + "function_to_method", + "inference_tip", + "node_classes", + "parse", + "raw_building", + "register_module_extender", + "scoped_nodes", + "unpack_infer", + "version", +] + + +def __getattr__(name: str): + if (val := globals().get(f"_DEPRECATED_{name}")) is None: + msg = f"module '{__name__}' has no attribute '{name}" + raise AttributeError(msg) + + # pylint: disable-next=import-outside-toplevel + import warnings + + msg = ( + f"importing '{name}' from 'astroid' is deprecated and will be removed in v5, " + "import it from 'astroid.nodes' instead" + ) + warnings.warn(msg, DeprecationWarning, stacklevel=2) + return val diff --git a/.venv/lib/python3.10/site-packages/astroid/__pkginfo__.py b/.venv/lib/python3.10/site-packages/astroid/__pkginfo__.py new file mode 100644 index 0000000..8ef2395 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/__pkginfo__.py @@ -0,0 +1,6 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt + +__version__ = "4.0.2" +version = __version__ diff --git a/.venv/lib/python3.10/site-packages/astroid/_ast.py b/.venv/lib/python3.10/site-packages/astroid/_ast.py new file mode 100644 index 0000000..e3ad97d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/_ast.py @@ -0,0 +1,102 @@ +# 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, filename: str | None = None + ) -> ast.Module: + if filename: + return ast.parse(string, filename=filename, type_comments=type_comments) + 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, "", "func_type") + 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, + } diff --git a/.venv/lib/python3.10/site-packages/astroid/arguments.py b/.venv/lib/python3.10/site-packages/astroid/arguments.py new file mode 100644 index 0000000..3781889 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/arguments.py @@ -0,0 +1,309 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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: nodes.Call, 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) -> bool: + """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, + ) -> dict[str | None, InferenceResult]: + 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.""" + # pylint: disable = too-many-branches + + 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, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/astroid_manager.py b/.venv/lib/python3.10/site-packages/astroid/astroid_manager.py new file mode 100644 index 0000000..3031057 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/astroid_manager.py @@ -0,0 +1,20 @@ +""" +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) diff --git a/.venv/lib/python3.10/site-packages/astroid/bases.py b/.venv/lib/python3.10/site-packages/astroid/bases.py new file mode 100644 index 0000000..a029da6 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/bases.py @@ -0,0 +1,778 @@ +# 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 PY311_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", "functools.cached_property"} +# enum.property was added in Python 3.11 +if PY311_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 + + if not meth.decorators: + return False + # Lookup for subclasses of *property* + 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): + # Check for a class which inherits from a standard property type + if any(inferred.is_subtype_of(pclass) for pclass in PROPERTIES): + return True + for base_class in inferred.bases: + # Check for a class which inherits from functools.cached_property + # and includes a subscripted type annotation + if isinstance(base_class, nodes.Subscript): + value = safe_infer(base_class.value, context=context) + if not isinstance(value, nodes.ClassDef): + continue + if value.name != "cached_property": + continue + module, _ = value.lookup(value.name) + if isinstance(module, nodes.Module) and module.name == "functools": + return True + continue + + 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]: + """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 + if isinstance(node, BaseInstance) and node._proxied is self._proxied: + inferred = True + yield node + # Prevent recursion. + 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]: + return method.infer_call_result(self, context) + + def __repr__(self) -> str: + return "".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]: + 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.GeneratorBaseModel + + 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"" + + def __str__(self) -> str: + return f"Generator({self._proxied.name})" + + +class AsyncGenerator(Generator): + """Special node representing an async generator.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + AsyncGenerator.special_attributes = objectmodel.AsyncGeneratorModel() + + def pytype(self) -> Literal["builtins.async_generator"]: + return "builtins.async_generator" + + def display_type(self) -> str: + return "AsyncGenerator" + + def __repr__(self) -> str: + return f"" + + 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"" + + def __str__(self) -> str: + return f"UnionType({self._proxied.name})" diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/__init__.py b/.venv/lib/python3.10/site-packages/astroid/brain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py new file mode 100644 index 0000000..6bde22f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py @@ -0,0 +1,50 @@ +# 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, nodes +from astroid.context import InferenceContext +from astroid.exceptions import UseInferenceDefault +from astroid.inference_tip import inference_tip +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.SYNTHETIC_ROOT, # this class is not real + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + ) + 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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py new file mode 100644 index 0000000..b619bb3 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py @@ -0,0 +1,110 @@ +# 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 import nodes +from astroid.brain.helpers import is_class_var +from astroid.manager import AstroidManager +from astroid.util import safe_infer + +ATTRIB_NAMES = frozenset( + ( + "attr.Factory", + "attr.ib", + "attrib", + "attr.attrib", + "attr.field", + "attrs.field", + "field", + ) +) +NEW_ATTRS_NAMES = frozenset( + ( + "attrs.define", + "attrs.mutable", + "attrs.frozen", + ) +) +ATTRS_NAMES = frozenset( + ( + "attr.s", + "attrs", + "attr.attrs", + "attr.attributes", + "attr.define", + "attr.mutable", + "attr.frozen", + *NEW_ATTRS_NAMES, + ) +) + + +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, nodes.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: nodes.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__"] = [nodes.Unknown(parent=node)] + + use_bare_annotations = is_decorated_with_attrs(node, NEW_ATTRS_NAMES) + for cdef_body_node in node.body: + if not isinstance(cdef_body_node, (nodes.Assign, nodes.AnnAssign)): + continue + if isinstance(cdef_body_node.value, nodes.Call): + if cdef_body_node.value.func.as_string() not in ATTRIB_NAMES: + continue + elif not use_bare_annotations: + continue + + # Skip attributes that are explicitly annotated as class variables + if isinstance(cdef_body_node, nodes.AnnAssign) and is_class_var( + cdef_body_node.annotation + ): + continue + + targets = ( + cdef_body_node.targets + if hasattr(cdef_body_node, "targets") + else [cdef_body_node.target] + ) + for target in targets: + rhs_node = nodes.Unknown( + lineno=cdef_body_node.lineno, + col_offset=cdef_body_node.col_offset, + parent=cdef_body_node, + ) + if isinstance(target, nodes.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( + nodes.ClassDef, attr_attributes_transform, is_decorated_with_attrs + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py new file mode 100644 index 0000000..3a95feb --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py @@ -0,0 +1,32 @@ +# 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.builder 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: ClassDef) -> ClassDef: + """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: ClassDef) -> 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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py new file mode 100644 index 0000000..e21d361 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py @@ -0,0 +1,1106 @@ +# 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, Iterator +from functools import partial +from typing import TYPE_CHECKING, Any, NoReturn, cast + +from astroid import arguments, helpers, nodes, objects, util +from astroid.builder import AstroidBuilder +from astroid.context import InferenceContext +from astroid.exceptions import ( + AstroidTypeError, + AttributeInferenceError, + InferenceError, + MroError, + UseInferenceDefault, +) +from astroid.inference_tip import inference_tip +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 = ( + objects.FrozenSet | objects.DictItems | objects.DictKeys | objects.DictValues +) + +BuiltContainers = type[tuple] | type[list] | type[set] | type[frozenset] + +CopyResult = 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: + # pylint: disable = too-many-boolean-expressions + 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): + return node.func.name == builtin_name + 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((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) -> bool: + 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="", + lineno=node.lineno, + col_offset=node.col_offset, + # ↓ semantically, the definition of the class of property isn't within + # node.frame. It's somewhere in the builtins module, but we are special + # casing it for each "property()" call, so we are making up the + # definition on the spot, ad-hoc. + parent=scoped_nodes.SYNTHETIC_ROOT, + ) + 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( + f"TypeError: arg 1 must be class, not {type(obj_type)!r}" + ) + + # 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) -> nodes.Const: + """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) -> nodes.Const: + """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: list) -> nodes.Dict: + 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) and 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)) + and 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, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py new file mode 100644 index 0000000..94944e6 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py @@ -0,0 +1,138 @@ +# 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 + +from astroid.brain.helpers import register_module_extender +from astroid.builder import AstroidBuilder, extract_node, parse +from astroid.const import PY313_PLUS +from astroid.context import InferenceContext +from astroid.exceptions import AttributeInferenceError +from astroid.manager import AstroidManager +from astroid.nodes.scoped_nodes import ClassDef + +if TYPE_CHECKING: + from astroid import nodes + + +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 _collections_abc_313_transform() -> nodes.Module: + """See https://github.com/python/cpython/pull/124735""" + return AstroidBuilder(AstroidManager()).string_build( + "from _collections_abc import *" + ) + + +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 + @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 + @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) + + # 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 + ) + + if PY313_PLUS: + register_module_extender( + manager, "collections.abc", _collections_abc_313_transform + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py new file mode 100644 index 0000000..71f9dfc --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py @@ -0,0 +1,27 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt + +from astroid import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def _re_transform() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py new file mode 100644 index 0000000..8ae10bc --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py @@ -0,0 +1,86 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def enrich_ctypes_redefined_types() -> nodes.Module: + """ + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py new file mode 100644 index 0000000..5824fd7 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py @@ -0,0 +1,185 @@ +# 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 nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def _curses_transform() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py new file mode 100644 index 0000000..bafd5f1 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py @@ -0,0 +1,635 @@ +# 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 + +from astroid import bases, context, nodes +from astroid.brain.helpers import is_class_var +from astroid.builder import parse +from astroid.const import PY313_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 = ( + 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) and 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 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() is True + + 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) + and 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 keyword.value.bool_value() is False # 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( + node: nodes.ClassDef, assigns: list[nodes.AnnAssign], kw_only_decorated: bool +) -> str: + """Return an init method for a dataclass given the targets.""" + # pylint: disable = too-many-locals, too-many-branches, too-many-statements + + 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 (keyword.value.bool_value() is False) + 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() is True: + 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 _looks_like_dataclasses(node: nodes.Module) -> bool: + return node.qname() == "dataclasses" + + +def _resolve_private_replace_to_public(node: nodes.Module) -> None: + """In python/cpython@6f3c138, a _replace() method was extracted from + replace(), and this indirection made replace() uninferable.""" + if "_replace" in node.locals: + node.locals["replace"] = node.locals["_replace"] + + +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_keyword_only_sentinel(node: nodes.NodeNG) -> bool: + """Return True if node is the KW_ONLY sentinel.""" + 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: + if PY313_PLUS: + manager.register_transform( + nodes.Module, + _resolve_private_replace_to_public, + _looks_like_dataclasses, + ) + + 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, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py new file mode 100644 index 0000000..f4cb667 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py @@ -0,0 +1,20 @@ +# 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 nodes +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() -> nodes.Module: + """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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py new file mode 100644 index 0000000..c27343f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py @@ -0,0 +1,28 @@ +# 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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import AstroidBuilder +from astroid.manager import AstroidManager + + +def dateutil_transform() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py new file mode 100644 index 0000000..1cb8442 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py @@ -0,0 +1,174 @@ +# 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, nodes, objects +from astroid.builder import extract_node +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.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, nodes.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, nodes.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, (nodes.Attribute, nodes.Call)): + continue + if _looks_like_functools_member(decorator, "lru_cache"): + return True + return False + + +def _looks_like_functools_member( + node: nodes.Attribute | nodes.Call, member: str +) -> bool: + """Check if the given Call node is the wanted member of functools.""" + if isinstance(node, nodes.Attribute): + return node.attrname == member + if isinstance(node.func, nodes.Name): + return node.func.name == member + if isinstance(node.func, nodes.Attribute): + return ( + node.func.attrname == member + and isinstance(node.func.expr, nodes.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( + nodes.FunctionDef, _transform_lru_cache, _looks_like_lru_cache + ) + + manager.register_transform( + nodes.Call, + inference_tip(_functools_partial_inference), + _looks_like_partial, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py new file mode 100644 index 0000000..fa60077 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py @@ -0,0 +1,252 @@ +# 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. + """ + # pylint: disable = too-many-branches, too-many-statements + + 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(" bool: + # Return whether this looks like a call to gi.require_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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py new file mode 100644 index 0000000..a17645a --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py @@ -0,0 +1,96 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt + +from astroid import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def _hashlib_transform() -> nodes.Module: + init_signature = "value='', usedforsecurity=True" + 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, " + "node_depth=0, inner_size=0, last_node=False, usedforsecurity=True" + ) + + blake2s_signature = ( + "data=b'', *, digest_size=32, key=b'', salt=b'', " + "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, " + "node_depth=0, inner_size=0, last_node=False, usedforsecurity=True" + ) + + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_http.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_http.py new file mode 100644 index 0000000..9802c0f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_http.py @@ -0,0 +1,238 @@ +# 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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import AstroidBuilder +from astroid.manager import AstroidManager + + +def _http_transform() -> nodes.Module: + code = textwrap.dedent( + """ + from enum import IntEnum, StrEnum + from collections import namedtuple + _HTTPStatus = namedtuple('_HTTPStatus', 'value phrase description') + + class HTTPMethod(StrEnum): + GET = "GET" + POST = "POST" + PUT = "PUT" + DELETE = "DELETE" + HEAD = "HEAD" + OPTIONS = "OPTIONS" + PATCH = "PATCH" + TRACE = "TRACE" + CONNECT = "CONNECT" + + 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', '') + EARLY_HINTS = _HTTPStatus(103, 'Early Hints') + 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') + CONTENT_TOO_LARGE = _HTTPStatus(413, 'Content Too Large', + 'Content is too large') + REQUEST_ENTITY_TOO_LARGE = CONTENT_TOO_LARGE + URI_TOO_LONG = _HTTPStatus(414, 'URI Too Long', 'URI is too long') + REQUEST_URI_TOO_LONG = URI_TOO_LONG + UNSUPPORTED_MEDIA_TYPE = _HTTPStatus(415, 'Unsupported Media Type', + 'Entity body in unsupported format') + RANGE_NOT_SATISFIABLE = (416, 'Range Not Satisfiable', + 'Cannot satisfy request range') + REQUESTED_RANGE_NOT_SATISFIABLE = RANGE_NOT_SATISFIABLE + EXPECTATION_FAILED = _HTTPStatus(417, 'Expectation Failed', + 'Expect condition could not be satisfied') + IM_A_TEAPOT = _HTTPStatus(418, 'I\\\'m a Teapot', + 'Server refuses to brew coffee because it is a teapot.') + MISDIRECTED_REQUEST = _HTTPStatus(421, 'Misdirected Request', + 'Server is not able to produce a response') + UNPROCESSABLE_CONTENT = _HTTPStatus(422, 'Unprocessable Content') + UNPROCESSABLE_ENTITY = UNPROCESSABLE_CONTENT + LOCKED = _HTTPStatus(423, 'Locked') + FAILED_DEPENDENCY = _HTTPStatus(424, 'Failed Dependency') + TOO_EARLY = _HTTPStatus(425, 'Too Early') + 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() -> nodes.Module: + return AstroidBuilder(AstroidManager()).string_build( + textwrap.dedent( + """ + from http import HTTPStatus + + CONTINUE = HTTPStatus.CONTINUE + SWITCHING_PROTOCOLS = HTTPStatus.SWITCHING_PROTOCOLS + PROCESSING = HTTPStatus.PROCESSING + EARLY_HINTS = HTTPStatus.EARLY_HINTS + 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 + CONTENT_TOO_LARGE = HTTPStatus.CONTENT_TOO_LARGE + REQUEST_ENTITY_TOO_LARGE = HTTPStatus.CONTENT_TOO_LARGE + URI_TOO_LONG = HTTPStatus.URI_TOO_LONG + REQUEST_URI_TOO_LONG = HTTPStatus.URI_TOO_LONG + UNSUPPORTED_MEDIA_TYPE = HTTPStatus.UNSUPPORTED_MEDIA_TYPE + RANGE_NOT_SATISFIABLE = HTTPStatus.RANGE_NOT_SATISFIABLE + REQUESTED_RANGE_NOT_SATISFIABLE = HTTPStatus.RANGE_NOT_SATISFIABLE + EXPECTATION_FAILED = HTTPStatus.EXPECTATION_FAILED + IM_A_TEAPOT = HTTPStatus.IM_A_TEAPOT + UNPROCESSABLE_CONTENT = HTTPStatus.UNPROCESSABLE_CONTENT + UNPROCESSABLE_ENTITY = HTTPStatus.UNPROCESSABLE_CONTENT + LOCKED = HTTPStatus.LOCKED + FAILED_DEPENDENCY = HTTPStatus.FAILED_DEPENDENCY + TOO_EARLY = HTTPStatus.TOO_EARLY + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py new file mode 100644 index 0000000..ba20f06 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py @@ -0,0 +1,56 @@ +# 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: FunctionDef) -> 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: FunctionDef) -> FunctionDef: + """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. + """ + assert isinstance(node.args.args, list) + 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, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_io.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_io.py new file mode 100644 index 0000000..ab6e607 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_io.py @@ -0,0 +1,44 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py new file mode 100644 index 0000000..62cc2d0 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py @@ -0,0 +1,125 @@ +# 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 nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import AstroidBuilder +from astroid.manager import AstroidManager + + +def mechanize_transform() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py new file mode 100644 index 0000000..e6413b0 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py @@ -0,0 +1,106 @@ +# 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py new file mode 100644 index 0000000..ff5b715 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py @@ -0,0 +1,681 @@ +# 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 + +from astroid import arguments, bases, 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.inference_tip import inference_tip +from astroid.manager import AstroidManager +from astroid.nodes.scoped_nodes.scoped_nodes import SYNTHETIC_ROOT + +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: nodes.NodeNG, + *, + parent: 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=parent, + ) + class_node.postinit( + 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: nodes.Name = _extract_single_node("tuple") + class_node, name, attributes = infer_func_form( + node, tuple_base, parent=SYNTHETIC_ROOT, context=context + ) + + call_site = arguments.CallSite.from_call(node, context=context) + func = util.safe_infer( + _extract_single_node("import collections; collections.namedtuple") + ) + assert isinstance(func, nodes.NodeNG) + try: + rename_arg_bool_value = next( + call_site.infer_argument(func, "rename", context or InferenceContext()) + ).bool_value() + rename = rename_arg_bool_value is True + 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): + # pylint: disable = too-many-boolean-expressions + 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 + # + for name in (typename, *attributes): + if not isinstance(name, str): + raise AstroidTypeError( + f"Type names and field names must be strings, not {type(name)!r}" + ) + 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) + # + + 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__ = [''] + """ + ) + + # FIXME arguably, the base here shouldn't be the EnumMeta class definition + # itself, but a reference (Name) to it. Otherwise, the invariant that all + # children of a node have that node as their parent is broken. + class_node = infer_func_form( + node, + enum_meta, + parent=SYNTHETIC_ROOT, + 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): + """The name of the Enum member. + + This is a reconstruction by astroid: enums are too dynamic to understand, but we at least + know 'name' should be a string, so this is astroid's best guess. + """ + 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: nodes.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, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py new file mode 100644 index 0000000..b72369c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py @@ -0,0 +1,28 @@ +# 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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py new file mode 100644 index 0000000..ce4173c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py @@ -0,0 +1,24 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def numpy_core_fromnumeric_transform() -> nodes.Module: + 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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py new file mode 100644 index 0000000..b66ba5f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py @@ -0,0 +1,35 @@ +# 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 import nodes +from astroid.brain.brain_numpy_utils import ( + attribute_name_looks_like_numpy_member, + infer_numpy_attribute, +) +from astroid.inference_tip import inference_tip +from astroid.manager import AstroidManager + +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: + manager.register_transform( + nodes.Attribute, + inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)), + functools.partial( + attribute_name_looks_like_numpy_member, + frozenset(METHODS_TO_BE_INFERRED.keys()), + ), + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py new file mode 100644 index 0000000..19850d3 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py @@ -0,0 +1,106 @@ +# 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 import nodes +from astroid.brain.brain_numpy_utils import ( + attribute_name_looks_like_numpy_member, + infer_numpy_attribute, + infer_numpy_name, + 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 + + +def numpy_core_multiarray_transform() -> nodes.Module: + 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 + ) + + method_names = frozenset(METHODS_TO_BE_INFERRED.keys()) + + manager.register_transform( + nodes.Attribute, + inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)), + functools.partial(attribute_name_looks_like_numpy_member, method_names), + ) + manager.register_transform( + nodes.Name, + inference_tip(functools.partial(infer_numpy_name, METHODS_TO_BE_INFERRED)), + functools.partial(member_name_looks_like_numpy_member, method_names), + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py new file mode 100644 index 0000000..ee08e02 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py @@ -0,0 +1,50 @@ +# 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 import nodes +from astroid.brain.brain_numpy_utils import ( + attribute_name_looks_like_numpy_member, + infer_numpy_attribute, +) +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 + + +def numpy_core_numeric_transform() -> nodes.Module: + 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 + ) + + manager.register_transform( + nodes.Attribute, + inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)), + functools.partial( + attribute_name_looks_like_numpy_member, + frozenset(METHODS_TO_BE_INFERRED.keys()), + ), + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py new file mode 100644 index 0000000..7111c83 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py @@ -0,0 +1,265 @@ +# 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 import nodes +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() -> nodes.Module: + # 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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py new file mode 100644 index 0000000..a048a1c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py @@ -0,0 +1,154 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def numpy_core_umath_transform() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py new file mode 100644 index 0000000..e61acb5 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py @@ -0,0 +1,33 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def numpy_ma_transform() -> nodes.Module: + """ + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py new file mode 100644 index 0000000..c98adb1 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py @@ -0,0 +1,163 @@ +# 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 import nodes +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 + + +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: nodes.Attribute) -> bool: + return node.attrname == "ndarray" + + +def register(manager: AstroidManager) -> None: + manager.register_transform( + nodes.Attribute, + inference_tip(infer_numpy_ndarray), + _looks_like_numpy_ndarray, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py new file mode 100644 index 0000000..be1c957 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py @@ -0,0 +1,73 @@ +# 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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def numpy_random_mtrand_transform() -> nodes.Module: + 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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py new file mode 100644 index 0000000..1a8f665 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py @@ -0,0 +1,94 @@ +# 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 import nodes +from astroid.builder import extract_node +from astroid.context import InferenceContext + +# 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_name( + sources: dict[str, str], node: nodes.Name, context: InferenceContext | None = None +): + extracted_node = extract_node(sources[node.name]) + return extracted_node.infer(context=context) + + +def infer_numpy_attribute( + sources: dict[str, str], + node: nodes.Attribute, + context: InferenceContext | None = None, +): + extracted_node = extract_node(sources[node.attrname]) + return extracted_node.infer(context=context) + + +def _is_a_numpy_module(node: nodes.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 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, nodes.Import) + ] + return any( + ("numpy", module_nickname) in target.names or ("numpy", None) in target.names + for target in potential_import_target + ) + + +def member_name_looks_like_numpy_member( + member_names: frozenset[str], node: nodes.Name +) -> bool: + """ + Returns True if the Name node's name matches a member name from numpy + """ + return node.name in member_names and node.root().name.startswith("numpy") + + +def attribute_name_looks_like_numpy_member( + member_names: frozenset[str], node: nodes.Attribute +) -> bool: + """ + Returns True if the Attribute node's name matches a member name from numpy + """ + return ( + node.attrname in member_names + and isinstance(node.expr, nodes.Name) + and _is_a_numpy_module(node.expr) + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py new file mode 100644 index 0000000..d1d1bda --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py @@ -0,0 +1,55 @@ +# 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, nodes +from astroid.builder import _extract_single_node +from astroid.const import PY313 +from astroid.exceptions import InferenceError, UseInferenceDefault +from astroid.inference_tip import inference_tip +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 + parents = "builtins.tuple" if PY313 else "pathlib._PathParents" + return ( + isinstance(value, bases.Instance) + and isinstance(value._proxied, nodes.ClassDef) + and value.qname() == parents + ) + + +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, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py new file mode 100644 index 0000000..e2bd669 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py @@ -0,0 +1,72 @@ +# 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 nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def pkg_resources_transform() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py new file mode 100644 index 0000000..6d06267 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py @@ -0,0 +1,85 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt + +"""Astroid hooks for pytest.""" +from astroid import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import AstroidBuilder +from astroid.manager import AstroidManager + + +def pytest_transform() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py new file mode 100644 index 0000000..30581e0 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py @@ -0,0 +1,89 @@ +# 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 +from astroid.brain.helpers import register_module_extender +from astroid.builder import AstroidBuilder, parse +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"}, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_random.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_random.py new file mode 100644 index 0000000..84b4f4e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_random.py @@ -0,0 +1,94 @@ +# 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 import nodes +from astroid.context import InferenceContext +from astroid.exceptions import UseInferenceDefault +from astroid.inference_tip import inference_tip +from astroid.manager import AstroidManager +from astroid.util import safe_infer + +ACCEPTED_ITERABLES_FOR_SAMPLE = (nodes.List, nodes.Set, nodes.Tuple) + + +def _clone_node_with_lineno(node, parent, lineno): + if isinstance(node, nodes.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, nodes.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 = nodes.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, nodes.Attribute): + return func.attrname == "sample" + if isinstance(func, nodes.Name): + return func.name == "sample" + return False + + +def register(manager: AstroidManager) -> None: + manager.register_transform( + nodes.Call, inference_tip(infer_random_sample), _looks_like_random_sample + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_re.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_re.py new file mode 100644 index 0000000..6464645 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_re.py @@ -0,0 +1,97 @@ +# 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, nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import _extract_single_node, parse +from astroid.const import PY311_PLUS +from astroid.inference_tip import inference_tip +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, + ) + 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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py new file mode 100644 index 0000000..70fb946 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py @@ -0,0 +1,95 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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, nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import _extract_single_node, parse +from astroid.inference_tip import inference_tip +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, + ) + 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 + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py new file mode 100644 index 0000000..f2e6069 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py @@ -0,0 +1,80 @@ +# 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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def responses_funcs() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py new file mode 100644 index 0000000..a7a2576 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py @@ -0,0 +1,90 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def scipy_signal() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py new file mode 100644 index 0000000..649e974 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py @@ -0,0 +1,120 @@ +# 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_six.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_six.py new file mode 100644 index 0000000..1218009 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_six.py @@ -0,0 +1,244 @@ +# 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 ( + isinstance(func, (nodes.FunctionDef, nodes.ClassDef)) + and 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, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py new file mode 100644 index 0000000..8410d9e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py @@ -0,0 +1,41 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt + +from astroid import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def _session_transform() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py new file mode 100644 index 0000000..6b4fc5c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py @@ -0,0 +1,163 @@ +# 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 nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.const import PY312_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 + 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 + """ + if PY312_PLUS: + enum += "OP_LEGACY_SERVER_CONNECT = 15" + return enum + + +def ssl_transform() -> nodes.Module: + return parse( + f""" + # 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 OP_LEGACY_SERVER_CONNECT" if PY312_PLUS else ""} + + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py new file mode 100644 index 0000000..5420ef9 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py @@ -0,0 +1,73 @@ +# 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 statistics library module. + +Provides inference improvements for statistics module functions that have +complex runtime behavior difficult to analyze statically. +""" + +from __future__ import annotations + +from collections.abc import Iterator +from typing import TYPE_CHECKING + +from astroid import nodes +from astroid.context import InferenceContext +from astroid.inference_tip import inference_tip +from astroid.manager import AstroidManager +from astroid.util import Uninferable + +if TYPE_CHECKING: + from astroid.typing import InferenceResult + + +def _looks_like_statistics_quantiles(node: nodes.Call) -> bool: + """Check if this is a call to statistics.quantiles.""" + match node.func: + case nodes.Attribute(expr=nodes.Name(name="statistics"), attrname="quantiles"): + # Case 1: statistics.quantiles(...) + return True + case nodes.Name(name="quantiles"): + # Case 2: from statistics import quantiles; quantiles(...) + # Check if quantiles was imported from statistics + try: + frame = node.frame() + if "quantiles" in frame.locals: + # Look for import from statistics + for stmt in frame.body: + if ( + isinstance(stmt, nodes.ImportFrom) + and stmt.modname == "statistics" + and any(name[0] == "quantiles" for name in stmt.names or []) + ): + return True + except (AttributeError, TypeError): + # If we can't determine the import context, be conservative + pass + return False + + +def infer_statistics_quantiles( + node: nodes.Call, context: InferenceContext | None = None +) -> Iterator[InferenceResult]: + """Infer the result of statistics.quantiles() calls. + + Returns Uninferable because quantiles() has complex runtime behavior + that cannot be statically analyzed, preventing false positives in + pylint's unbalanced-tuple-unpacking checker. + + statistics.quantiles() returns a list with (n-1) elements, but static + analysis sees only the empty list initializations in the function body. + """ + yield Uninferable + + +def register(manager: AstroidManager) -> None: + """Register statistics-specific inference improvements.""" + manager.register_transform( + nodes.Call, + inference_tip(infer_statistics_quantiles), + _looks_like_statistics_quantiles, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py new file mode 100644 index 0000000..3a99802 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py @@ -0,0 +1,100 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt + +import textwrap + +from astroid import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.const import PY311_PLUS +from astroid.manager import AstroidManager + + +def _subprocess_transform() -> nodes.Module: + 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, + user=None, group=None, extra_groups=None, umask=-1, 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} + @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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py new file mode 100644 index 0000000..95af2db --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py @@ -0,0 +1,33 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt + +from astroid import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def _thread_transform() -> nodes.Module: + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_type.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_type.py new file mode 100644 index 0000000..8391e59 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_type.py @@ -0,0 +1,70 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 nodes +from astroid.builder import extract_node +from astroid.context import InferenceContext +from astroid.exceptions import UseInferenceDefault +from astroid.inference_tip import inference_tip +from astroid.manager import AstroidManager + + +def _looks_like_type_subscript(node: nodes.Name) -> 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.NodeNG + :return: whether the node is a Name node inside a type related subscript + """ + if 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.NodeNG + :return: the inferred node + :rtype: nodes.NodeNG + """ + node_scope, _ = node.scope().lookup("type") + if not (isinstance(node_scope, nodes.Module) and 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: + manager.register_transform( + nodes.Name, inference_tip(infer_type_sub), _looks_like_type_subscript + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py new file mode 100644 index 0000000..217a803 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py @@ -0,0 +1,504 @@ +# 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, nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import AstroidBuilder, _extract_single_node, extract_node +from astroid.const import PY312_PLUS, PY313_PLUS, PY314_PLUS +from astroid.exceptions import ( + AstroidSyntaxError, + AttributeInferenceError, + InferenceError, + UseInferenceDefault, +) +from astroid.inference_tip import inference_tip +from astroid.manager import AstroidManager + +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", # scheduled for removal in 3.17 + "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, nodes.Attribute): + return func.attrname in TYPING_TYPEVARS + if isinstance(func, nodes.Name): + return func.name in TYPING_TYPEVARS + return False + + +def infer_typing_typevar_or_newtype( + node: nodes.Call, context_itton: context.InferenceContext | None = None +) -> Iterator[nodes.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], nodes.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, nodes.Name): + return node.name in TYPING_MEMBERS + if isinstance(node, nodes.Attribute): + return node.attrname in TYPING_MEMBERS + if isinstance(node, nodes.Subscript): + return _looks_like_typing_subscript(node.value) + return False + + +def infer_typing_attr( + node: nodes.Subscript, ctx: context.InferenceContext | None = None +) -> Iterator[nodes.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 ( + PY313_PLUS + and isinstance(value, nodes.FunctionDef) + and value.qname() == "typing.Annotated" + ): + # typing.Annotated is a FunctionDef on 3.13+ + node._explicit_inference = lambda node, context: iter([value]) + return iter([value]) + + if isinstance(value, nodes.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, nodes.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] + # Avoid re-instantiating this class every time it's seen + node._explicit_inference = lambda node, context: iter([value]) + return iter([value]) + + node = extract_node(TYPING_TYPE_TEMPLATE.format(value.qname().split(".")[-1])) + return node.infer(context=ctx) + + +def _looks_like_generic_class_pep695(node: nodes.ClassDef) -> bool: + """Check if class is using type parameter. Python 3.12+.""" + return len(node.type_params) > 0 + + +def infer_typing_generic_class_pep695( + node: nodes.ClassDef, ctx: context.InferenceContext | None = None +) -> Iterator[nodes.ClassDef]: + """Add __class_getitem__ for generic classes. Python 3.12+.""" + func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE) + node.locals["__class_getitem__"] = [func_to_add] + return iter([node]) + + +def _looks_like_typedDict( # pylint: disable=invalid-name + node: nodes.FunctionDef | nodes.ClassDef, +) -> bool: + """Check if node is TypedDict FunctionDef.""" + return node.qname() in TYPING_TYPEDDICT_QUALIFIED + + +def infer_typedDict( # pylint: disable=invalid-name + node: nodes.FunctionDef, ctx: context.InferenceContext | None = None +) -> Iterator[nodes.ClassDef]: + """Replace TypedDict FunctionDef with ClassDef.""" + class_def = nodes.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: nodes.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, nodes.Name) + # TODO: remove _DeprecatedGenericAlias when Py3.14 min + and node.func.name in {"_alias", "_DeprecatedGenericAlias"} + and len(node.args) == 2 + and ( + # _alias function works also for builtins object such as list and dict + isinstance(node.args[0], (nodes.Attribute, nodes.Name)) + ) + ) + + +def _forbid_class_getitem_access(node: nodes.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: nodes.Call, ctx: context.InferenceContext | None = None +) -> Iterator[nodes.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, nodes.Assign) + and len(node.parent.targets) == 1 + and isinstance(node.parent.targets[0], nodes.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 = nodes.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, nodes.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 isinstance(maybe_type_var, nodes.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: nodes.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, nodes.Name) + and node.args + and ( + ( + node.func.name == "_TupleType" + and isinstance(node.args[0], nodes.Name) + and node.args[0].name == "tuple" + ) + or ( + node.func.name == "_CallableType" + and isinstance(node.args[0], nodes.Attribute) + and node.args[0].as_string() == "collections.abc.Callable" + ) + ) + ) + + +def infer_special_alias( + node: nodes.Call, ctx: context.InferenceContext | None = None +) -> Iterator[nodes.ClassDef]: + """Infer call to tuple alias as new subscriptable class typing.Tuple.""" + if not ( + isinstance(node.parent, nodes.Assign) + and len(node.parent.targets) == 1 + and isinstance(node.parent.targets[0], nodes.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 = nodes.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] + # 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_typing_cast(node: nodes.Call) -> bool: + return (isinstance(node.func, nodes.Name) and node.func.name == "cast") or ( + isinstance(node.func, nodes.Attribute) and node.func.attrname == "cast" + ) + + +def infer_typing_cast( + node: nodes.Call, ctx: context.InferenceContext | None = None +) -> Iterator[nodes.NodeNG]: + """Infer call to cast() returning same type as casted-from var.""" + if not isinstance(node.func, (nodes.Name, nodes.Attribute)): + raise UseInferenceDefault + + try: + func = next(node.func.infer(context=ctx)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + if not ( + isinstance(func, nodes.FunctionDef) + and func.qname() == "typing.cast" + and len(node.args) == 2 + ): + raise UseInferenceDefault + + return node.args[1].infer(context=ctx) + + +def _typing_transform(): + code = textwrap.dedent( + """ + class Generic: + @classmethod + def __class_getitem__(cls, item): return cls + class ParamSpec: + @property + def args(self): + return ParamSpecArgs(self) + @property + def kwargs(self): + return ParamSpecKwargs(self) + 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: ... + class ContextManager: + @classmethod + def __class_getitem__(cls, item): return cls + class AsyncContextManager: + @classmethod + def __class_getitem__(cls, item): return cls + class Pattern: + @classmethod + def __class_getitem__(cls, item): return cls + class Match: + @classmethod + def __class_getitem__(cls, item): return cls + """ + ) + if PY314_PLUS: + code += textwrap.dedent( + """ + from annotationlib import ForwardRef + class Union: + @classmethod + def __class_getitem__(cls, item): return cls + """ + ) + return AstroidBuilder(AstroidManager()).string_build(code) + + +def register(manager: AstroidManager) -> None: + manager.register_transform( + nodes.Call, + inference_tip(infer_typing_typevar_or_newtype), + looks_like_typing_typevar_or_newtype, + ) + manager.register_transform( + nodes.Subscript, inference_tip(infer_typing_attr), _looks_like_typing_subscript + ) + manager.register_transform( + nodes.Call, inference_tip(infer_typing_cast), _looks_like_typing_cast + ) + + manager.register_transform( + nodes.FunctionDef, inference_tip(infer_typedDict), _looks_like_typedDict + ) + + manager.register_transform( + nodes.Call, inference_tip(infer_typing_alias), _looks_like_typing_alias + ) + manager.register_transform( + nodes.Call, inference_tip(infer_special_alias), _looks_like_special_alias + ) + + if PY312_PLUS: + register_module_extender(manager, "typing", _typing_transform) + manager.register_transform( + nodes.ClassDef, + inference_tip(infer_typing_generic_class_pep695), + _looks_like_generic_class_pep695, + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py new file mode 100644 index 0000000..4103ce0 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py @@ -0,0 +1,31 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 import nodes +from astroid.brain.helpers import register_module_extender +from astroid.builder import parse +from astroid.manager import AstroidManager + + +def IsolatedAsyncioTestCaseImport() -> nodes.Module: + """ + 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py new file mode 100644 index 0000000..4405a62 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py @@ -0,0 +1,18 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 import nodes +from astroid.manager import AstroidManager + + +def _patch_uuid_class(node: nodes.ClassDef) -> None: + # The .int member is patched using __dict__ + node.locals["int"] = [nodes.Const(0, parent=node)] + + +def register(manager: AstroidManager) -> None: + manager.register_transform( + nodes.ClassDef, _patch_uuid_class, lambda node: node.qname() == "uuid.UUID" + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/helpers.py b/.venv/lib/python3.10/site-packages/astroid/brain/helpers.py new file mode 100644 index 0000000..0064a1f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/brain/helpers.py @@ -0,0 +1,146 @@ +# 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 typing import TYPE_CHECKING + +from astroid.exceptions import InferenceError +from astroid.manager import AstroidManager +from astroid.nodes.scoped_nodes import Module + +if TYPE_CHECKING: + from astroid.nodes.node_ng import NodeNG + + +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_functools, + brain_gi, + brain_hashlib, + brain_http, + brain_hypothesis, + brain_io, + brain_mechanize, + brain_multiprocessing, + brain_namedtuple_enum, + 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_statistics, + 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_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_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_statistics.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) + + +def is_class_var(node: NodeNG) -> bool: + """Return True if node is a ClassVar, with or without subscripting.""" + try: + inferred = next(node.infer()) + except (InferenceError, StopIteration): + return False + + return getattr(inferred, "name", "") == "ClassVar" diff --git a/.venv/lib/python3.10/site-packages/astroid/builder.py b/.venv/lib/python3.10/site-packages/astroid/builder.py new file mode 100644 index 0000000..f166ab4 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/builder.py @@ -0,0 +1,505 @@ +# 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 re +import textwrap +import types +import warnings +from collections.abc import Collection, Iterator, Sequence +from io import TextIOWrapper +from tokenize import detect_encoding +from typing import TYPE_CHECKING, cast + +from astroid import bases, modutils, nodes, raw_building, rebuilder, util +from astroid._ast import ParserModule, get_parser_module +from astroid.const import PY312_PLUS, PY314_PLUS +from astroid.exceptions import AstroidBuildingError, AstroidSyntaxError, InferenceError + +if TYPE_CHECKING: + 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 = "#@" + +if PY312_PLUS: + warnings.filterwarnings("ignore", ".*invalid escape sequence", SyntaxWarning) +if PY314_PLUS: + warnings.filterwarnings( + "ignore", "'(return|continue|break)' in a 'finally'", SyntaxWarning + ) + + +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. 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, apply_transforms: bool = True) -> None: + super().__init__(manager) + self._apply_transforms = apply_transforms + if not raw_building.InspectBuilder.bootstrapped: + manager.bootstrap() + + 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, global_names 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, global_names) + # 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, modname=modname + ) + except (TypeError, ValueError, SyntaxError, MemoryError) 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, global_name: Collection[str] + ) -> None: + """Store imported names to the locals. + + Resort the locals if coming from a delayed node + """ + + def add_local(parent_or_root: nodes.NodeNG, name: str) -> None: + parent_or_root.set_local(name, node) + my_list = parent_or_root.scope().locals[name] + if TYPE_CHECKING: + my_list = cast(list[nodes.NodeNG], my_list) + my_list.sort(key=lambda n: n.fromlineno or 0) + + assert node.parent # It should always default to the module + module = node.root() + for name, asname in node.names: + if name == "*": + try: + imported = node.do_import_module() + except AstroidBuildingError: + continue + for name in imported.public_names(): + if name in global_name: + add_local(module, name) + else: + add_local(node.parent, name) + else: + name = asname or name + if name in global_name: + add_local(module, name) + else: + add_local(node.parent, name) + + def delayed_assattr(self, node: nodes.AssignAttr) -> None: + """Visit an AssignAttr 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: + # We want a narrow check on the parent type, not all of its subclasses + if type(inferred) in {bases.Instance, 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. + """ + # pylint: disable-next=import-outside-toplevel + from astroid.manager import AstroidManager + + code = textwrap.dedent(code) + builder = AstroidBuilder(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 + and node.args + ): + 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, modname: str | None = None +) -> tuple[ast.Module, ParserModule]: + parser_module = get_parser_module(type_comments=type_comments) + try: + parsed = parser_module.parse( + data + "\n", type_comments=type_comments, filename=modname + ) + 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. We use a heuristic for + # determining if the error is due to type annotations. + type_annot_related = re.search(r"#\s+type:", exc.text or "") + if not (type_annot_related and type_comments): + raise + + parser_module = get_parser_module(type_comments=False) + parsed = parser_module.parse(data + "\n", type_comments=False) + return parsed, parser_module diff --git a/.venv/lib/python3.10/site-packages/astroid/const.py b/.venv/lib/python3.10/site-packages/astroid/const.py new file mode 100644 index 0000000..dcce074 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/const.py @@ -0,0 +1,26 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt + +import enum +import sys + +PY311_PLUS = sys.version_info >= (3, 11) +PY312_PLUS = sys.version_info >= (3, 12) +PY313 = sys.version_info[:2] == (3, 13) +PY313_PLUS = sys.version_info >= (3, 13) +PY314_PLUS = sys.version_info >= (3, 14) + +WIN32 = sys.platform == "win32" + +IS_PYPY = sys.implementation.name == "pypy" +IS_JYTHON = sys.implementation.name == "jython" + + +class Context(enum.Enum): + Load = 1 + Store = 2 + Del = 3 + + +_EMPTY_OBJECT_MARKER = object() diff --git a/.venv/lib/python3.10/site-packages/astroid/constraint.py b/.venv/lib/python3.10/site-packages/astroid/constraint.py new file mode 100644 index 0000000..692d22d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/constraint.py @@ -0,0 +1,186 @@ +# 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 + +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 = 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) + + +class BooleanConstraint(Constraint): + """Represents an "x" or "not x" constraint.""" + + @classmethod + def match( + cls, node: _NameNodes, expr: nodes.NodeNG, negate: bool = False + ) -> Self | None: + """Return a new constraint for node if expr matches one of these patterns: + + - direct match (expr == node): use given negate value + - negated match (expr == `not node`): flip negate value + + Return None if no pattern matches. + """ + if _matches(expr, node): + return cls(node=node, negate=negate) + + if ( + isinstance(expr, nodes.UnaryOp) + and expr.op == "not" + and _matches(expr.operand, node) + ): + return cls(node=node, negate=not negate) + + return None + + def satisfied_by(self, inferred: InferenceResult) -> bool: + """Return True for uninferable results, or depending on negate flag: + + - negate=False: satisfied if boolean value is True + - negate=True: satisfied if boolean value is False + """ + inferred_booleaness = inferred.bool_value() + if isinstance(inferred, util.UninferableBase) or isinstance( + inferred_booleaness, util.UninferableBase + ): + return True + + return self.negate ^ inferred_booleaness + + +def get_constraints( + expr: _NameNodes, frame: nodes.LocalsDictNodeNG +) -> dict[nodes.If | nodes.IfExp, 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 | nodes.IfExp, set[Constraint]] = {} + while current_node is not None and current_node is not frame: + parent = current_node.parent + if isinstance(parent, (nodes.If, nodes.IfExp)): + 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, + BooleanConstraint, + ) +) +"""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 diff --git a/.venv/lib/python3.10/site-packages/astroid/context.py b/.venv/lib/python3.10/site-packages/astroid/context.py new file mode 100644 index 0000000..fa9ed22 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/context.py @@ -0,0 +1,204 @@ +# 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, Sequence +from typing import TYPE_CHECKING + +from astroid.typing import InferenceResult, SuccessfulInferenceResult + +if TYPE_CHECKING: + from astroid import constraint, nodes + +_InferenceCache = dict[ + tuple["nodes.NodeNG", str | None, str | None, str | None], Sequence["nodes.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__ = ( + "_nodes_inferred", + "boundnode", + "callcontext", + "constraints", + "extra_context", + "lookupname", + "path", + ) + + 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 | nodes.IfExp, 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", "callee", "keywords") + + def __init__( + self, + args: list[nodes.NodeNG], + keywords: list[nodes.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 diff --git a/.venv/lib/python3.10/site-packages/astroid/decorators.py b/.venv/lib/python3.10/site-packages/astroid/decorators.py new file mode 100644 index 0000000..05d2dd3 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/decorators.py @@ -0,0 +1,232 @@ +# 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 ParamSpec, TypeVar + +from astroid import util +from astroid.context import InferenceContext +from astroid.exceptions import InferenceError +from astroid.typing import InferenceResult + +_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]], +) -> Callable[_P, Generator[InferenceResult]]: + def inner(*args: _P.args, **kwargs: _P.kwargs) -> Generator[InferenceResult]: + 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]], +) -> Callable[_P, Generator[InferenceResult]]: + def inner(*args: _P.args, **kwargs: _P.kwargs) -> Generator[InferenceResult]: + 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 + # pylint: disable = too-many-boolean-expressions + 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/exceptions.py b/.venv/lib/python3.10/site-packages/astroid/exceptions.py new file mode 100644 index 0000000..e523b70 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/exceptions.py @@ -0,0 +1,419 @@ +# 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", + "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: + try: + return self.message.format(**vars(self)) + except ValueError: + return self.message # Return raw message if formatting fails + + +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, too-many-positional-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 diff --git a/.venv/lib/python3.10/site-packages/astroid/filter_statements.py b/.venv/lib/python3.10/site-packages/astroid/filter_statements.py new file mode 100644 index 0000000..a48b6e7 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/filter_statements.py @@ -0,0 +1,240 @@ +# 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, _base_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. + """ + # pylint: disable = too-many-branches, too-many-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: _base_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 diff --git a/.venv/lib/python3.10/site-packages/astroid/helpers.py b/.venv/lib/python3.10/site-packages/astroid/helpers.py new file mode 100644 index 0000000..9c370aa --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/helpers.py @@ -0,0 +1,335 @@ +# 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, 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]: + 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): + 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: + 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( + f"arg 2 must be a type or tuple of types, not {type(klass)!r}" + ) + + 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, not {type(node)!r}") + 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 + + 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/inference_tip.py b/.venv/lib/python3.10/site-packages/astroid/inference_tip.py new file mode 100644 index 0000000..c3187c0 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/inference_tip.py @@ -0,0 +1,130 @@ +# 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]: + 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py new file mode 100644 index 0000000..af7c55b --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py @@ -0,0 +1,496 @@ +# 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 Iterable, Iterator, Sequence +from functools import lru_cache +from pathlib import Path +from typing import Literal, NamedTuple, Protocol + +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 + + @staticmethod + @abc.abstractmethod + def find_module( + modname: str, + module_parts: tuple[str, ...], + processed: tuple[str, ...], + submodule_path: tuple[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 tuple 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 tuple 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] + ) + + @staticmethod + @lru_cache(maxsize=1024) + def find_module( + modname: str, + module_parts: tuple[str, ...], + processed: tuple[str, ...], + submodule_path: tuple[str, ...] | None, + ) -> ModuleSpec | None: + # pylint: disable-next=import-outside-toplevel + from astroid.modutils import cached_os_path_isfile + + # Although we should be able to use `find_spec` this doesn't work on PyPy for builtins. + # Therefore, we use the `builtin_module_nams` heuristic for these. + if submodule_path is None and modname in sys.builtin_module_names: + return ModuleSpec( + name=modname, + location=None, + type=ModuleType.C_BUILTIN, + ) + + if submodule_path is not None: + search_paths = list(submodule_path) + else: + search_paths = sys.path + + suffixes = (".py", ".pyi", importlib.machinery.BYTECODE_SUFFIXES[0]) + for entry in search_paths: + package_directory = os.path.join(entry, modname) + for suffix in suffixes: + package_file_name = "__init__" + suffix + file_path = os.path.join(package_directory, package_file_name) + if cached_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 cached_os_path_isfile(file_path): + return ModuleSpec(name=modname, location=file_path, type=type_) + + # If the module name matches a stdlib module name, check whether this is a frozen + # module. Note that `find_spec` actually imports parent modules, so we want to make + # sure we only run this code for stuff that can be expected to be frozen. For now + # this is only stdlib. + if (modname in sys.stdlib_module_names and not processed) or ( + processed and processed[0] in sys.stdlib_module_names + ): + try: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=Warning) + spec = importlib.util.find_spec(".".join((*processed, modname))) + except ValueError: + spec = None + + if ( + spec + and spec.loader # type: ignore[comparison-overlap] # noqa: E501 + is importlib.machinery.FrozenImporter + ): + return ModuleSpec( + name=modname, + location=getattr(spec.loader_state, "filename", None), + type=ModuleType.PY_FROZEN, + ) + + return None + + def contribute_to_path( + self, spec: ModuleSpec, processed: list[str] + ) -> Sequence[str] | None: + if spec.location is None: + # Builtin. + return None + # pylint: disable-next=import-outside-toplevel + from astroid.modutils import EXT_LIB_DIRS + + 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.""" + + @staticmethod + @lru_cache(maxsize=1024) + def find_module( + modname: str, + module_parts: tuple[str, ...], + processed: tuple[str, ...], + submodule_path: tuple[str, ...] | None, + ) -> ModuleSpec | None: + if processed: + modname = ".".join([*processed, modname]) + if util.is_namespace(modname) and modname in sys.modules: + return ModuleSpec( + name=modname, + location="", + origin="namespace", + type=ModuleType.PY_NAMESPACE, + submodule_search_locations=sys.modules[modname].__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( + entry_path + ) + except zipimport.ZipImportError: + continue + + @staticmethod + @lru_cache(maxsize=1024) + def find_module( + modname: str, + module_parts: tuple[str, ...], + processed: tuple[str, ...], + submodule_path: tuple[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, + ) + + def contribute_to_path( + self, spec: ModuleSpec, processed: list[str] + ) -> Sequence[str] | None: + return spec.submodule_search_locations + + +class PathSpecFinder(Finder): + """Finder based on importlib.machinery.PathFinder.""" + + @staticmethod + @lru_cache(maxsize=1024) + def find_module( + modname: str, + module_parts: tuple[str, ...], + processed: tuple[str, ...], + submodule_path: tuple[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, +) + + +@lru_cache(maxsize=1024) +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 importer is not None and isinstance(importer, zipimport.zipimporter): + yield filepath, importer + + +def _search_zip( + modpath: tuple[str, ...], +) -> tuple[Literal[ModuleType.PY_ZIPMODULE], str, str]: + for filepath, importer in _get_zipimporters(): + found = importer.find_spec(modpath[0]) + if found: + if not importer.find_spec(os.path.sep.join(modpath)): + raise ImportError( + "No module named {} in {}/{}".format( + ".".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: tuple[str, ...], + processed: tuple[str, ...], + submodule_path: tuple[str, ...] | None, +) -> tuple[Finder | _MetaPathFinder, ModuleSpec]: + for finder in _SPEC_FINDERS: + finder_instance = finder(search_path) + mod_spec = finder.find_module(modname, module_parts, processed, submodule_path) + if mod_spec is None: + continue + return finder_instance, mod_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: Iterable[str], path: Iterable[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. + """ + return _find_spec(tuple(modpath), tuple(path) if path else None) + + +@lru_cache(maxsize=1024) +def _find_spec( + module_path: tuple[str, ...], path: tuple[str, ...] | None +) -> ModuleSpec: + _path = path or sys.path + + # Need a copy for not mutating the argument. + modpath = list(module_path) + + search_paths = None + processed: list[str] = [] + + while modpath: + modname = modpath.pop(0) + + submodule_path = search_paths or path + if submodule_path is not None: + submodule_path = tuple(submodule_path) + + finder, spec = _find_spec_with_path( + _path, modname, module_path, tuple(processed), submodule_path + ) + processed.append(modname) + if modpath: + if isinstance(finder, Finder): + search_paths = finder.contribute_to_path(spec, processed) + # If modname is a package from an editable install, update search_paths + # 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] + search_paths = spec.submodule_search_locations + + if spec.type == ModuleType.PKG_DIRECTORY: + spec = spec._replace(submodule_search_locations=search_paths) + + return spec diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py new file mode 100644 index 0000000..8b8725f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py @@ -0,0 +1,111 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 # type: ignore[attr-defined] +from importlib.util import _find_spec_from_path # type: ignore[attr-defined] + +from astroid.const import IS_PYPY + +if sys.version_info >= (3, 11): + from importlib.machinery import NamespaceLoader +else: + from importlib._bootstrap_external import _NamespaceLoader as NamespaceLoader + + +@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: + 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 + and ( + found_spec.loader is None or isinstance(found_spec.loader, NamespaceLoader) + ) + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py new file mode 100644 index 0000000..8eab35c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py @@ -0,0 +1,75 @@ +# 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 import nodes +from astroid.exceptions import AttributeInferenceError + +if TYPE_CHECKING: + 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, (nodes.List, nodes.Tuple, nodes.Const, nodes.Dict, nodes.Set)): + return _builtin_lookup(node, name) + if isinstance(node, astroid.Instance): + return _lookup_in_mro(node, name) + if isinstance(node, nodes.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 diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py new file mode 100644 index 0000000..eac9e43 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py @@ -0,0 +1,1013 @@ +# 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(parent=self._instance) + + @property + def attr___loader__(self): + # No handling for now. + return node_classes.Unknown(parent=self._instance) + + @property + def attr___cached__(self): + # No handling for now. + return node_classes.Unknown(parent=self._instance) + + +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 = nodes.Arguments( + parent=func.args.parent, vararg=None, kwarg=None + ) + + positional_or_keyword_params = func.args.args.copy() + positional_or_keyword_params.append( + nodes.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(parent=self._instance) + + 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___annotations__(self) -> node_classes.Unknown: + return node_classes.Unknown(parent=self._instance) + + @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): + mro = self._instance.mro() + obj = node_classes.Tuple(parent=self._instance) + obj.postinit(mro) + return obj + + @property + def attr_mro(self): + 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. + """ + + 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 GeneratorBaseModel(FunctionModel, ContextManagerModel): + def __init__(self, gen_module: nodes.Module): + super().__init__() + for name, values in gen_module.locals.items(): + method = values[0] + if isinstance(method, nodes.FunctionDef): + method = bases.BoundMethod(method, _get_bound_node(self)) + + def patched(cls, meth=method): + return meth + + setattr(type(self), IMPL_PREFIX + name, property(patched)) + + @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 GeneratorModel(GeneratorBaseModel): + def __init__(self): + super().__init__(AstroidManager().builtins_module["generator"]) + + +class AsyncGeneratorModel(GeneratorBaseModel): + def __init__(self): + super().__init__(AstroidManager().builtins_module["async_generator"]) + + +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 GroupExceptionInstanceModel(ExceptionInstanceModel): + @property + def attr_exceptions(self) -> nodes.Tuple: + return node_classes.Tuple(parent=self._instance) + + +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(b"") + + +BUILTIN_EXCEPTIONS = { + "builtins.SyntaxError": SyntaxErrorInstanceModel, + "builtins.ExceptionGroup": GroupExceptionInstanceModel, + "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) -> nodes.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 diff --git a/.venv/lib/python3.10/site-packages/astroid/manager.py b/.venv/lib/python3.10/site-packages/astroid/manager.py new file mode 100644 index 0000000..e232886 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/manager.py @@ -0,0 +1,478 @@ +# 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.builder import AstroidBuilder, build_namespace_package_module +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_, + _has_init, + cached_os_path_isfile, + 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(), + "module_denylist": set(), + "_transform": TransformVisitor(), + "prefer_stubs": False, + } + + 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.module_denylist = AstroidManager.brain["module_denylist"] + self._transform = AstroidManager.brain["_transform"] + self.prefer_stubs = AstroidManager.brain["prefer_stubs"] + + @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"] + + @property + def prefer_stubs(self) -> bool: + return AstroidManager.brain["prefer_stubs"] + + @prefer_stubs.setter + def prefer_stubs(self, value: bool) -> None: + AstroidManager.brain["prefer_stubs"] = value + + 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, prefer_stubs=self.prefer_stubs + ) + 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: + 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. + """ + return AstroidBuilder(self).string_build(data, modname, filepath) + + def _build_stub_module(self, modname: str) -> nodes.Module: + return AstroidBuilder(self).string_build("", modname) + + def _build_namespace_module( + self, modname: str, path: Sequence[str] + ) -> nodes.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.module_denylist: + raise AstroidImportError(f"Skipping ignored module {modname!r}") + 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 + + 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: + 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 + + 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: + yield from modastroid.igetattr(name, context) + 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 `nodes.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._import.spec import ( + _find_spec, + _is_setuptools_namespace, + ) + 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() + self._mod_file_cache.clear() + + # NB: not a new TransformVisitor() + AstroidManager.brain["_transform"].transforms = collections.defaultdict(list) + + for lru_cache in ( + LookupMixIn.lookup, + _cache_normalize_path_, + _has_init, + cached_os_path_isfile, + util.is_namespace, + ObjectModel.attributes, + ClassDef._metaclass_lookup_attribute, + _find_spec, + _is_setuptools_namespace, + ): + lru_cache.cache_clear() # type: ignore[attr-defined] + + for finder in spec._SPEC_FINDERS: + finder.find_module.cache_clear() + + self.bootstrap() + + # Reload brain plugins. During initialisation this is done in astroid.manager.py + register_all_brains(self) diff --git a/.venv/lib/python3.10/site-packages/astroid/modutils.py b/.venv/lib/python3.10/site-packages/astroid/modutils.py new file mode 100644 index 0000000..0868c60 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/modutils.py @@ -0,0 +1,703 @@ +# 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 sys import stdlib_module_names + +from astroid.const import IS_JYTHON +from astroid.interpreter._import import spec, util + +logger = logging.getLogger(__name__) + + +if sys.platform.startswith("win"): + PY_SOURCE_EXTS = ("py", "pyw", "pyi") + PY_SOURCE_EXTS_STUBS_FIRST = ("pyi", "pyw", "py") + PY_COMPILED_EXTS = ("dll", "pyd") +else: + PY_SOURCE_EXTS = ("py", "pyi") + PY_SOURCE_EXTS_STUBS_FIRST = ("pyi", "py") + 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 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 _is_subpath(path: str, base: str) -> bool: + path = os.path.normcase(os.path.normpath(path)) + base = os.path.normcase(os.path.normpath(base)) + if not path.startswith(base): + return False + return (len(path) == len(base)) or (path[len(base)] == os.path.sep) + + +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 + """ + path_to_check = os.path.normcase(os.path.normpath(path_to_check)) + + abs_filename = os.path.abspath(filename) + if _is_subpath(abs_filename, path_to_check): + base_path = os.path.splitext(abs_filename)[0] + relative_base_path = base_path[len(path_to_check) :].lstrip(os.path.sep) + return [pkg for pkg in relative_base_path.split(os.sep) if pkg] + + real_filename = os.path.realpath(filename) + if _is_subpath(real_filename, path_to_check): + base_path = os.path.splitext(real_filename)[0] + relative_base_path = base_path[len(path_to_check) :].lstrip(os.path.sep) + return [pkg for pkg in relative_base_path.split(os.sep) if pkg] + + return None + + +def modpath_from_file_with_callback( + filename: str, + path: list[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 + paths_to_check + 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(paths_to_check) + ) + ) + + +def modpath_from_file(filename: str, path: list[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 paths where the module or package should be + searched, additionally to sys.path + + :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, prefer_stubs: 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) + orig_ext = orig_ext.lstrip(".") + if orig_ext not in PY_SOURCE_EXTS and os.path.exists(f"{base}.{orig_ext}"): + return f"{base}.{orig_ext}" + for ext in PY_SOURCE_EXTS_STUBS_FIRST if prefer_stubs else 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] + ) + ) + + +@lru_cache(maxsize=1024) +def cached_os_path_isfile(path: str | os.PathLike[str]) -> bool: + """A cached version of os.path.isfile that helps avoid repetitive I/O""" + return os.path.isfile(path) + + +# 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")) + + +@lru_cache(maxsize=1024) +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) + ) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/__init__.py b/.venv/lib/python3.10/site-packages/astroid/nodes/__init__.py new file mode 100644 index 0000000..6a67516 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/__init__.py @@ -0,0 +1,303 @@ +# 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 ` + +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, + Interpolation, + 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, + TemplateStr, + Try, + TryStar, + Tuple, + TypeAlias, + TypeVar, + TypeVarTuple, + UnaryOp, + Unknown, + While, + With, + Yield, + YieldFrom, + are_exclusive, + const_factory, + unpack_infer, +) +from astroid.nodes.scoped_nodes import ( + SYNTHETIC_ROOT, + 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__ = ( + "CONST_CLS", + "SYNTHETIC_ROOT", + "AnnAssign", + "Arguments", + "Assert", + "Assign", + "AssignAttr", + "AssignName", + "AsyncFor", + "AsyncFunctionDef", + "AsyncWith", + "Attribute", + "AugAssign", + "Await", + "BaseContainer", + "BinOp", + "BoolOp", + "Break", + "Call", + "ClassDef", + "Compare", + "Comprehension", + "ComprehensionScope", + "Const", + "Continue", + "Decorators", + "DelAttr", + "DelName", + "Delete", + "Dict", + "DictComp", + "DictUnpack", + "EmptyNode", + "EvaluatedObject", + "ExceptHandler", + "Expr", + "For", + "FormattedValue", + "FunctionDef", + "GeneratorExp", + "Global", + "If", + "IfExp", + "Import", + "ImportFrom", + "Interpolation", + "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", + "TemplateStr", + "Try", + "TryStar", + "Tuple", + "TypeAlias", + "TypeVar", + "TypeVarTuple", + "UnaryOp", + "Unknown", + "While", + "With", + "Yield", + "YieldFrom", + "are_exclusive", + "builtin_lookup", + "const_factory", + "function_to_method", + "get_wrapping_class", + "unpack_infer", +) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py b/.venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py new file mode 100644 index 0000000..df452cb --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py @@ -0,0 +1,672 @@ +# 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 Callable, Generator, Iterator +from functools import cached_property, lru_cache, partial +from typing import TYPE_CHECKING, Any, ClassVar + +from astroid import bases, nodes, util +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, + InferenceResult | None, + nodes.AugAssign | nodes.BinOp, + InferenceResult, + InferenceResult | None, + InferenceContext, + InferenceContext, + ], + list[partial[Generator[InferenceResult]]], + ] + + +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 imported by .""" + 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], + ], + context: InferenceContext | None, + error: type[util.BadOperationMessage], + ) -> Generator[InferenceResult]: + 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]: + """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]]: + """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]]: + """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]: + """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]]]: + """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]]]: + """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 + ), + ] + + # pylint: disable = too-many-boolean-expressions + if ( + 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]: + """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) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/as_string.py b/.venv/lib/python3.10/site-packages/astroid/nodes/as_string.py new file mode 100644 index 0000000..01007b9 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/as_string.py @@ -0,0 +1,740 @@ +# 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 + +import warnings +from collections.abc import Iterator +from typing import TYPE_CHECKING + +from astroid import nodes + +if TYPE_CHECKING: + from astroid import objects + +# 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: nodes.NodeNG) -> str: + """Makes this visitor behave as a simple function""" + return node.accept(self).replace(DOC_NEWLINE, "\n") + + def _docs_dedent(self, doc_node: nodes.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: nodes.NodeNG, child: nodes.NodeNG, 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: nodes.NodeNG, child: nodes.NodeNG, 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_ methods ########################################### + + def visit_await(self, node: nodes.Await) -> str: + return f"await {node.value.accept(self)}" + + def visit_asyncwith(self, node: nodes.AsyncWith) -> str: + return f"async {self.visit_with(node)}" + + def visit_asyncfor(self, node: nodes.AsyncFor) -> str: + return f"async {self.visit_for(node)}" + + def visit_arguments(self, node: nodes.Arguments) -> str: + """return an nodes.Arguments node as string""" + return node.format_args() + + def visit_assignattr(self, node: nodes.AssignAttr) -> str: + """return an nodes.AssignAttr node as string""" + return self.visit_attribute(node) + + def visit_assert(self, node: nodes.Assert) -> str: + """return an nodes.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: nodes.AssignName) -> str: + """return an nodes.AssignName node as string""" + return node.name + + def visit_assign(self, node: nodes.Assign) -> str: + """return an nodes.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: nodes.AugAssign) -> str: + """return an nodes.AugAssign node as string""" + return f"{node.target.accept(self)} {node.op} {node.value.accept(self)}" + + def visit_annassign(self, node: nodes.AnnAssign) -> str: + """Return an nodes.AnnAssign 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: nodes.BinOp) -> str: + """return an nodes.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: nodes.BoolOp) -> str: + """return an nodes.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: nodes.Break) -> str: + """return an nodes.Break node as string""" + return "break" + + def visit_call(self, node: nodes.Call) -> str: + """return an nodes.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 _handle_type_params( + self, type_params: list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] + ) -> str: + return ( + f"[{', '.join(tp.accept(self) for tp in type_params)}]" + if type_params + else "" + ) + + def visit_classdef(self, node: nodes.ClassDef) -> str: + """return an nodes.ClassDef node as string""" + decorate = node.decorators.accept(self) if node.decorators else "" + type_params = self._handle_type_params(node.type_params) + 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) + return "\n\n{}class {}{}{}:{}\n{}\n".format( + decorate, node.name, type_params, args_str, docs, self._stmt_list(node.body) + ) + + def visit_compare(self, node: nodes.Compare) -> str: + """return an nodes.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: nodes.Comprehension) -> str: + """return an nodes.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: nodes.Const) -> str: + """return an nodes.Const node as string""" + if node.value is Ellipsis: + return "..." + return repr(node.value) + + def visit_continue(self, node: nodes.Continue) -> str: + """return an nodes.Continue node as string""" + return "continue" + + def visit_delete(self, node: nodes.Delete) -> str: + """return an nodes.Delete node as string""" + return f"del {', '.join(child.accept(self) for child in node.targets)}" + + def visit_delattr(self, node: nodes.DelAttr) -> str: + """return an nodes.DelAttr node as string""" + return self.visit_attribute(node) + + def visit_delname(self, node: nodes.DelName) -> str: + """return an nodes.DelName node as string""" + return node.name + + def visit_decorators(self, node: nodes.Decorators) -> str: + """return an nodes.Decorators node as string""" + return "@%s\n" % "\n@".join(item.accept(self) for item in node.nodes) + + def visit_dict(self, node: nodes.Dict) -> str: + """return an nodes.Dict node as string""" + return "{%s}" % ", ".join(self._visit_dict(node)) + + def _visit_dict(self, node: nodes.Dict) -> 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: nodes.DictUnpack) -> str: + return "**" + + def visit_dictcomp(self, node: nodes.DictComp) -> str: + """return an nodes.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: nodes.Expr) -> str: + """return an nodes.Expr node as string""" + return node.value.accept(self) + + def visit_emptynode(self, node: nodes.EmptyNode) -> str: + """dummy method for visiting an EmptyNode""" + return "" + + def visit_excepthandler(self, node: nodes.ExceptHandler) -> 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: nodes.EmptyNode) -> str: + """return an EmptyNode as string""" + return "" + + def visit_for(self, node: nodes.For) -> str: + """return an nodes.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: nodes.ImportFrom) -> str: + """return an nodes.ImportFrom node as string""" + return "from {} import {}".format( + "." * (node.level or 0) + node.modname, _import_string(node.names) + ) + + def visit_joinedstr(self, node: nodes.JoinedStr) -> 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: nodes.FormattedValue) -> 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 "" + type_params = self._handle_type_params(node.type_params) + docs = self._docs_dedent(node.doc_node) + trailer = ":" + if node.returns: + return_annotation = " -> " + node.returns.as_string() + trailer = return_annotation + ":" + def_format = "\n%s%s %s%s(%s)%s%s\n%s" + return def_format % ( + decorate, + keyword, + node.name, + type_params, + node.args.accept(self), + trailer, + docs, + self._stmt_list(node.body), + ) + + def visit_functiondef(self, node: nodes.FunctionDef) -> str: + """return an nodes.FunctionDef node as string""" + return self.handle_functiondef(node, "def") + + def visit_asyncfunctiondef(self, node: nodes.AsyncFunctionDef) -> str: + """return an nodes.AsyncFunction node as string""" + return self.handle_functiondef(node, "async def") + + def visit_generatorexp(self, node: nodes.GeneratorExp) -> str: + """return an nodes.GeneratorExp node as string""" + return "({} {})".format( + node.elt.accept(self), " ".join(n.accept(self) for n in node.generators) + ) + + def visit_attribute( + self, node: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr + ) -> str: + """return an nodes.Attribute node as string""" + try: + left = self._precedence_parens(node, node.expr) + except RecursionError: + warnings.warn( + "Recursion limit exhausted; defaulting to adding parentheses.", + UserWarning, + stacklevel=2, + ) + left = f"({node.expr.accept(self)})" + if left.isdigit(): + left = f"({left})" + return f"{left}.{node.attrname}" + + def visit_global(self, node: nodes.Global) -> str: + """return an nodes.Global node as string""" + return f"global {', '.join(node.names)}" + + def visit_if(self, node: nodes.If) -> str: + """return an nodes.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: nodes.IfExp) -> str: + """return an nodes.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: nodes.Import) -> str: + """return an nodes.Import node as string""" + return f"import {_import_string(node.names)}" + + def visit_keyword(self, node: nodes.Keyword) -> str: + """return an nodes.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: nodes.Lambda) -> str: + """return an nodes.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: nodes.List) -> str: + """return an nodes.List node as string""" + return f"[{', '.join(child.accept(self) for child in node.elts)}]" + + def visit_listcomp(self, node: nodes.ListComp) -> str: + """return an nodes.ListComp node as string""" + return "[{} {}]".format( + node.elt.accept(self), " ".join(n.accept(self) for n in node.generators) + ) + + def visit_module(self, node: nodes.Module) -> str: + """return an nodes.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: nodes.Name) -> str: + """return an nodes.Name node as string""" + return node.name + + def visit_namedexpr(self, node: nodes.NamedExpr) -> 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: nodes.Nonlocal) -> str: + """return an nodes.Nonlocal node as string""" + return f"nonlocal {', '.join(node.names)}" + + def visit_paramspec(self, node: nodes.ParamSpec) -> str: + """return an nodes.ParamSpec node as string""" + default_value_str = ( + f" = {node.default_value.accept(self)}" if node.default_value else "" + ) + return f"**{node.name.accept(self)}{default_value_str}" + + def visit_pass(self, node: nodes.Pass) -> str: + """return an nodes.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: nodes.Raise) -> str: + """return an nodes.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: nodes.Return) -> str: + """return an nodes.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: nodes.Set) -> str: + """return an nodes.Set node as string""" + return "{%s}" % ", ".join(child.accept(self) for child in node.elts) + + def visit_setcomp(self, node: nodes.SetComp) -> str: + """return an nodes.SetComp node as string""" + return "{{{} {}}}".format( + node.elt.accept(self), " ".join(n.accept(self) for n in node.generators) + ) + + def visit_slice(self, node: nodes.Slice) -> str: + """return an nodes.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: nodes.Subscript) -> str: + """return an nodes.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: nodes.Try) -> str: + """return an nodes.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: nodes.TryStar) -> str: + """return an nodes.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: nodes.Tuple) -> str: + """return an nodes.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 nodes.TypeAlias node as string""" + type_params = self._handle_type_params(node.type_params) + return f"type {node.name.accept(self)}{type_params} = {node.value.accept(self)}" + + def visit_typevar(self, node: nodes.TypeVar) -> str: + """return an nodes.TypeVar node as string""" + bound_str = f": {node.bound.accept(self)}" if node.bound else "" + default_value_str = ( + f" = {node.default_value.accept(self)}" if node.default_value else "" + ) + return f"{node.name.accept(self)}{bound_str}{default_value_str}" + + def visit_typevartuple(self, node: nodes.TypeVarTuple) -> str: + """return an nodes.TypeVarTuple node as string""" + default_value_str = ( + f" = {node.default_value.accept(self)}" if node.default_value else "" + ) + return f"*{node.name.accept(self)}{default_value_str}" + + def visit_unaryop(self, node: nodes.UnaryOp) -> str: + """return an nodes.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: nodes.While) -> str: + """return an nodes.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: nodes.With) -> str: # 'with' without 'as' is possible + """return an nodes.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: nodes.Yield) -> 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: nodes.YieldFrom) -> str: + """Return an nodes.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: nodes.Starred) -> str: + """return Starred node as string""" + return "*" + node.value.accept(self) + + def visit_match(self, node: nodes.Match) -> str: + """Return an nodes.Match node as string.""" + return f"match {node.subject.accept(self)}:\n{self._stmt_list(node.cases)}" + + def visit_matchcase(self, node: nodes.MatchCase) -> str: + """Return an nodes.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: nodes.MatchValue) -> str: + """Return an nodes.MatchValue node as string.""" + return node.value.accept(self) + + @staticmethod + def visit_matchsingleton(node: nodes.MatchSingleton) -> str: + """Return an nodes.MatchSingleton node as string.""" + return str(node.value) + + def visit_matchsequence(self, node: nodes.MatchSequence) -> str: + """Return an nodes.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: nodes.MatchMapping) -> str: + """Return an nodes..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: nodes.MatchClass) -> str: + """Return an nodes..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: nodes.MatchStar) -> str: + """Return an nodes..MatchStar node as string.""" + return f"*{node.name.accept(self) if node.name else '_'}" + + def visit_matchas(self, node: nodes.MatchAs) -> str: + """Return an nodes..MatchAs node as string.""" + if isinstance( + node.parent, (nodes.MatchSequence, nodes.MatchMapping, nodes.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: nodes.MatchOr) -> str: + """Return an nodes.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) + + def visit_templatestr(self, node: nodes.TemplateStr) -> str: + """Return an nodes.TemplateStr node as string.""" + string = "" + for value in node.values: + match value: + case nodes.Interpolation(): + string += "{" + value.accept(self) + "}" + case _: + string += value.accept(self)[1:-1] + for quote in ("'", '"', '"""', "'''"): + if quote not in string: + break + return "t" + quote + string + quote + + def visit_interpolation(self, node: nodes.Interpolation) -> str: + """Return an nodes.Interpolation node as string.""" + result = f"{node.str}" + 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 result + + # These aren't for real AST nodes, but for inference objects. + + def visit_frozenset(self, node: objects.FrozenSet) -> str: + return node.parent.accept(self) + + def visit_super(self, node: objects.Super) -> str: + return node.parent.accept(self) + + def visit_uninferable(self, node) -> str: + return str(node) + + def visit_property(self, node: objects.Property) -> str: + return node.function.accept(self) + + def visit_evaluatedobject(self, node: nodes.EvaluatedObject) -> str: + return node.original.accept(self) + + def visit_unknown(self, node: nodes.Unknown) -> str: + return str(node) + + +def _import_string(names: list[tuple[str, str | None]]) -> 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(" ") diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/const.py b/.venv/lib/python3.10/site-packages/astroid/nodes/const.py new file mode 100644 index 0000000..f66b633 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/const.py @@ -0,0 +1,27 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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 +} diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py b/.venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py new file mode 100644 index 0000000..0d3a425 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py @@ -0,0 +1,5701 @@ +# 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 Callable, Generator, Iterable, Iterator, Mapping +from functools import cached_property +from typing import TYPE_CHECKING, Any, ClassVar, Literal, Union + +from astroid import decorators, protocols, util +from astroid.bases import Instance, _infer_stmts +from astroid.const import _EMPTY_OBJECT_MARKER, PY314_PLUS, 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.nodes.scoped_nodes import SYNTHETIC_ROOT +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) + +# pylint: disable-next=consider-alternative-union-syntax +AssignedStmtsPossibleNode = Union["List", "Tuple", "AssignName", "AssignAttr", None] +AssignedStmtsCall = Callable[ + [ + _NodesT, + AssignedStmtsPossibleNode, + InferenceContext | None, + list[int] | None, + ], + Any, +] +InferBinaryOperation = Callable[ + [_NodesT, InferenceContext | None], + Generator[InferenceResult | _BadOpMessageT], +] +InferLHS = Callable[ + [_NodesT, InferenceContext | None], + Generator[InferenceResult, None, InferenceErrorInfo | None], +] +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 + + >>> list(node.get_children()) + [, ] + >>> 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()) + [] + >>> 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 + + >>> list(node.get_children()) + [, ] + >>> 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 + + >>> node.args + + """ + + # 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, too-many-positional-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]: + # 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) + else: + default_offset = None + 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 default_offset 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 + + >>> list(node.get_children()) + [, ] + >>> 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 + + """ + + _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 + + """ + + 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 + + """ + + _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 + + """ + + _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 + ) -> list[util.BadBinaryOperationMessage]: + """Get a list of type errors which can occur during inference. + + Each TypeError is represented by a :class:`BadBinaryOperationMessage` , + which holds the original exception. + + If any inferred result is uninferable, an empty list is returned. + """ + bad = [] + try: + for result in self._infer_augassign(context=context): + if result is util.Uninferable: + raise InferenceError + if isinstance(result, util.BadBinaryOperationMessage): + bad.append(result) + except InferenceError: + return [] + return bad + + 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]: + """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]: + 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 + + """ + + _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 + ) -> list[util.BadBinaryOperationMessage]: + """Get a list of type errors which can occur during inference. + + Each TypeError is represented by a :class:`BadBinaryOperationMessage`, + which holds the original exception. + + If any inferred result is uninferable, an empty list is returned. + """ + bad = [] + try: + for result in self._infer_binop(context=context): + if result is util.Uninferable: + raise InferenceError + if isinstance(result, util.BadBinaryOperationMessage): + bad.append(result) + except InferenceError: + return [] + return bad + + def get_children(self): + yield self.left + yield self.right + + def op_precedence(self) -> int: + 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]: + """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]: + 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 + + """ + + _astroid_fields = ("values",) + _other_fields = ("op",) + + def __init__( + self, + op: 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 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 = 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) -> int: + 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 + + """ + + +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 + + """ + + _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 + + >>> node.ops + [('<=', ), ('<=', )] + """ + + _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, ValueError, or TypeError 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, TypeError): + 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]: + """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()) + [, ] + >>> 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 + + >>> list(node.get_children()) + [, + , + , + , + ] + """ + + _other_fields = ("value", "kind") + + def __init__( + self, + value: Any, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG = SYNTHETIC_ROOT, + 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. + """ + if getattr(value, "__name__", None) == "__doc__": + warnings.warn( # pragma: no cover + "You have most likely called a __doc__ field of some object " + "and it didn't return a string. " + "That happens to some symbols from the standard library. " + "Check for isinstance(.__doc__, str).", + RuntimeWarning, + stacklevel=0, + ) + self.value = 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 or Uninferable + """ + # bool(NotImplemented) is deprecated; it raises TypeError starting from Python 3.14 + # and returns True for versions under 3.14 + if self.value is NotImplemented: + return util.Uninferable if PY314_PLUS else True + 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 + + """ + + +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 + + >>> list(node.get_children())[0] + + """ + + _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 + + >>> list(node.get_children())[0] + + """ + + _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 + + """ + + _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 + + """ + + _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 + + >>> node.parent + + """ + + _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: NodeNG = SYNTHETIC_ROOT, + *, + 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]: + 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 + + >>> node.handlers + [] + """ + + _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 + + """ + + _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 + + >>> node.body[0] + + """ + + +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 + + >>> node.body[0] + + >>> list(node.body[0].get_children())[0] + + """ + + _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 + + """ + + _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]: + """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 + + """ + + _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]: + 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 + + """ + + _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) -> bool: + 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 + + """ + + _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]: + """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: + test_bool_value = test.bool_value() + if not isinstance(test, util.UninferableBase) and not isinstance( + test_bool_value, 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 + + """ + + _other_fields = ("names",) + + def __init__( + self, + names: list[tuple[str, str | 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 + """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]: + """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 + + >>> node.keywords + [] + """ + + _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 + + """ + + _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 + + >>> node.body[0] + + """ + + _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] + + """ + + _astroid_fields = ("name", "default_value") + name: AssignName + default_value: 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, default_value: NodeNG | None) -> None: + self.name = name + self.default_value = default_value + + def _infer( + self, context: InferenceContext | None = None, **kwargs: Any + ) -> Iterator[ParamSpec]: + yield self + + assigned_stmts = protocols.generic_type_assigned_stmts + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + +class Pass(_base_nodes.NoChildrenNode, _base_nodes.Statement): + """Class representing an :class:`ast.Pass` node. + + >>> import astroid + >>> node = astroid.extract_node('pass') + >>> node + + """ + + +class Raise(_base_nodes.Statement): + """Class representing an :class:`ast.Raise` node. + + >>> import astroid + >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")') + >>> node + + """ + + _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 + + """ + + _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) -> bool: + 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 + + """ + + 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 + + >>> node.slice + + """ + + _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 display_type(self) -> Literal["Slice"]: + """A human readable type of this node. + + :returns: The type of this node. + """ + return "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 + + """ + + _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_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 + + """ + + _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 + + """ + + _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 + + """ + + _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], + ] + ] = 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] + + """ + + _astroid_fields = ("name", "bound", "default_value") + name: AssignName + bound: NodeNG | None + default_value: 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, + default_value: NodeNG | None = None, + ) -> None: + self.name = name + self.bound = bound + self.default_value = default_value + + def _infer( + self, context: InferenceContext | None = None, **kwargs: Any + ) -> Iterator[TypeVar]: + yield self + + assigned_stmts = protocols.generic_type_assigned_stmts + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + +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] + + """ + + _astroid_fields = ("name", "default_value") + name: AssignName + default_value: 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, default_value: NodeNG | None = None + ) -> None: + self.name = name + self.default_value = default_value + + def _infer( + self, context: InferenceContext | None = None, **kwargs: Any + ) -> Iterator[TypeVarTuple]: + yield self + + assigned_stmts = protocols.generic_type_assigned_stmts + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + +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 + + """ + + _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 + ) -> list[util.BadUnaryOperationMessage]: + """Get a list of type errors which can occur during inference. + + Each TypeError is represented by a :class:`BadUnaryOperationMessage`, + which holds the original exception. + + If any inferred result is uninferable, an empty list is returned. + """ + bad = [] + try: + for result in self._infer_unaryop(context=context): + if result is util.Uninferable: + raise InferenceError + if isinstance(result, util.BadUnaryOperationMessage): + bad.append(result) + except InferenceError: + return [] + return bad + + def get_children(self): + yield self.operand + + def op_precedence(self) -> int: + 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 + + """ + + _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 + + """ + + _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 + + """ + + _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 + + >>> node.values + [, ] + """ + + _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 + + def _infer( + self, context: InferenceContext | None = None, **kwargs: Any + ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: + format_specs = Const("") if self.format_spec is None else self.format_spec + uninferable_already_generated = False + for format_spec in format_specs.infer(context, **kwargs): + if not isinstance(format_spec, Const): + if not uninferable_already_generated: + yield util.Uninferable + uninferable_already_generated = True + continue + for value in self.value.infer(context, **kwargs): + value_to_format = value + if isinstance(value, Const): + value_to_format = value.value + try: + formatted = format(value_to_format, format_spec.value) + yield Const( + formatted, + lineno=self.lineno, + col_offset=self.col_offset, + end_lineno=self.end_lineno, + end_col_offset=self.end_col_offset, + ) + continue + except (ValueError, TypeError): + # happens when format_spec.value is invalid + yield util.Uninferable + uninferable_already_generated = True + continue + + +UNINFERABLE_VALUE = "{Uninferable}" + + +class JoinedStr(NodeNG): + """Represents a list of string expressions to be joined. + + >>> import astroid + >>> node = astroid.extract_node('f"Format {type_}"') + >>> node + + """ + + _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 + + def _infer( + self, context: InferenceContext | None = None, **kwargs: Any + ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: + if self.values: + yield from self._infer_with_values(context) + else: + yield Const("") + + def _infer_with_values( + self, context: InferenceContext | None = None, **kwargs: Any + ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: + uninferable_already_generated = False + for inferred in self._infer_from_values(self.values, context): + failed = inferred is util.Uninferable or ( + isinstance(inferred, Const) and UNINFERABLE_VALUE in inferred.value + ) + if failed: + if not uninferable_already_generated: + uninferable_already_generated = True + yield util.Uninferable + continue + yield inferred + + @classmethod + def _infer_from_values( + cls, nodes: list[NodeNG], context: InferenceContext | None = None, **kwargs: Any + ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: + if not nodes: + return + if len(nodes) == 1: + for node in cls._safe_infer_from_node(nodes[0], context, **kwargs): + if isinstance(node, Const): + yield node + continue + yield Const(UNINFERABLE_VALUE) + return + for prefix in cls._safe_infer_from_node(nodes[0], context, **kwargs): + for suffix in cls._infer_from_values(nodes[1:], context, **kwargs): + result = "" + for node in (prefix, suffix): + if isinstance(node, Const): + result += str(node.value) + continue + result += UNINFERABLE_VALUE + yield Const(result) + + @classmethod + def _safe_infer_from_node( + cls, node: NodeNG, context: InferenceContext | None = None, **kwargs: Any + ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: + try: + yield from node._infer(context, **kwargs) + except InferenceError: + yield util.Uninferable + + +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 + + """ + + _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) -> 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 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, + parent: NodeNG, + lineno: None = None, + col_offset: 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 + + +UNATTACHED_UNKNOWN = Unknown(parent=SYNTHETIC_ROOT) + + +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]: + 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 + + """ + + _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] + + """ + + _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 + + """ + + _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 + + >>> node.cases[1].pattern + + >>> node.cases[2].pattern + + """ + + _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 + + >>> node.cases[1].pattern + + """ + + _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 + + """ + + _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 + + >>> node.cases[1].pattern + + """ + + _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] + + """ + + _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] + + >>> node.cases[1].pattern.patterns[0] + + >>> node.cases[2].pattern + + >>> node.cases[3].pattern + + """ + + _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 + + """ + + _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 TemplateStr(NodeNG): + """Class representing an :class:`ast.TemplateStr` node. + + >>> import astroid + >>> node = astroid.extract_node('t"{name} finished {place!s}"') + >>> node + + """ + + _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: + self.values: 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, *, values: list[NodeNG]) -> None: + self.values = values + + def get_children(self) -> Iterator[NodeNG]: + yield from self.values + + +class Interpolation(NodeNG): + """Class representing an :class:`ast.Interpolation` node. + + >>> import astroid + >>> node = astroid.extract_node('t"{name} finished {place!s}"') + >>> node + + >>> node.values[0] + + >>> node.values[2] + + """ + + _astroid_fields = ("value", "format_spec") + _other_fields = ("str", "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: + self.value: NodeNG + """Any expression node.""" + + self.str: str + """Text of the interpolation expression.""" + + self.conversion: int + """The type of formatting to be applied to the value. + + .. seealso:: + :class:`ast.Interpolation` + """ + + self.format_spec: JoinedStr | None = None + """The formatting to be applied to the value. + + .. seealso:: + :class:`ast.Interpolation` + """ + + 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, + str: str, # pylint: disable=redefined-builtin + conversion: int = -1, + format_spec: JoinedStr | None = None, + ) -> None: + self.value = value + self.str = str + self.conversion = conversion + self.format_spec = format_spec + + def get_children(self) -> Iterator[NodeNG]: + yield self.value + if self.format_spec: + yield self.format_spec + + +# 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: + # NOTE: avoid accessing any attributes of element in the loop. + 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(): + # NOTE: avoid accessing any attributes of both key and value in the loop. + 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.""" + # NOTE: avoid accessing any attributes of value until it is known that value + # is of a const type, to avoid possibly triggering code for a live object. + # Accesses include value.__class__ and isinstance(value, ...), but not type(value). + # See: https://github.com/pylint-dev/astroid/issues/2686 + value_type = type(value) + assert not issubclass(value_type, 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_type not in CONST_CLS: + node = EmptyNode() + node.object = value + return node + + instance: List | Set | Tuple | Dict + initializer_cls = CONST_CLS[value_type] + if issubclass(initializer_cls, (List, Set, Tuple)): + instance = initializer_cls( + lineno=None, + col_offset=None, + parent=SYNTHETIC_ROOT, + 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=SYNTHETIC_ROOT, + end_lineno=None, + end_col_offset=None, + ) + instance.postinit(_create_dict_items(value, instance)) + return instance + return Const(value) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py b/.venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py new file mode 100644 index 0000000..1af39c2 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py @@ -0,0 +1,771 @@ +# 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 +from collections.abc import Generator, Iterator +from functools import cached_property +from functools import singledispatch as _singledispatch +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + TypeVar, + cast, + overload, +) + +from astroid import nodes, 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.nodes import _base_nodes + + +# Types for 'NodeNG.nodes_of_class()' +_NodesT = TypeVar("_NodesT", bound="NodeNG") +_NodesT2 = TypeVar("_NodesT2", bound="NodeNG") +_NodesT3 = TypeVar("_NodesT3", bound="NodeNG") +SkipKlassT = 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]: + """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) -> _base_nodes.Statement: + """The first parent node, including self, marked as statement node. + + :raises StatementMissing: If self has no parent attribute. + """ + if self.is_statement: + return cast("_base_nodes.Statement", self) + if not self.parent: + raise StatementMissing(target=self) + return self.parent.statement() + + def frame(self) -> 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 self.parent is None: + raise ParentMissingError(target=self) + return self.parent.frame() + + 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): + assert isinstance(self, nodes.Module) + return self + + while parent.parent: + parent = parent.parent + assert isinstance(parent, nodes.Module) + return parent + + 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 + """ + + # pylint: disable = too-many-statements + + @_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" 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) -> int: + # 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py new file mode 100644 index 0000000..01f99fa --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py @@ -0,0 +1,47 @@ +# 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 ( + SYNTHETIC_ROOT, + 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__ = ( + "SYNTHETIC_ROOT", + "AsyncFunctionDef", + "ClassDef", + "ComprehensionScope", + "DictComp", + "FunctionDef", + "GeneratorExp", + "Lambda", + "ListComp", + "LocalsDictNodeNG", + "Module", + "SetComp", + "_is_metaclass", + "builtin_lookup", + "function_to_method", + "get_wrapping_class", +) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py new file mode 100644 index 0000000..8874c06 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py @@ -0,0 +1,202 @@ +# 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.""" diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py new file mode 100644 index 0000000..f232db3 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -0,0 +1,2900 @@ +# 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 +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.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 _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 import Arguments, Const, NodeNG + 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 + + +def _infer_last( + arg: SuccessfulInferenceResult, context: InferenceContext +) -> InferenceResult: + res = util.Uninferable + for b in arg.infer(context=context.clone()): + res = b + return res + + +class Module(LocalsDictNodeNG): + """Class representing an :class:`ast.Module` node. + + >>> import astroid + >>> node = astroid.extract_node('import astroid') + >>> node + + >>> node.parent + + """ + + _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)] + if name == "__name__": + main_const = node_classes.const_factory("__main__") + main_const.parent = AstroidManager().builtins_module + result.append(main_const) + 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) -> NoReturn: + """The first parent node, including self, marked as statement node. + + When called on a :class:`Module` this raises a StatementMissing. + """ + 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]: + yield self + + +class __SyntheticRoot(Module): + def __init__(self): + super().__init__("__astroid_synthetic", pure_python=False) + + +SYNTHETIC_ROOT = __SyntheticRoot() + + +class GeneratorExp(ComprehensionScope): + """Class representing an :class:`ast.GeneratorExp` node. + + >>> import astroid + >>> node = astroid.extract_node('(thing for thing in things if thing)') + >>> node + + """ + + _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 + + """ + + _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 + + """ + + _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 + + """ + + _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 + l.1 at 0x7f23b2e41518> + """ + + _astroid_fields: ClassVar[tuple[str, ...]] = ("args", "body") + _other_other_fields: ClassVar[tuple[str, ...]] = ("locals",) + name = "" + 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]: + 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 + + """ + + _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 = "" + + 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, + ) + + 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 and 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 + """ + if self.returns: + return self.returns.tolineno + 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 ' 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 and bases._is_property(self)): + yield self + return InferenceErrorInfo(node=self, context=context) + + if not self.parent: + raise ParentMissingError(target=self) + prop_func = objects.Property( + function=self, + name=self.name, + lineno=self.lineno, + parent=self.parent, + col_offset=self.col_offset, + ) + 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 + """ + for yield_ in self.nodes_of_class(node_classes.Yield): + if yield_.value is None: + yield node_classes.Const(None, parent=yield_, lineno=yield_.lineno) + 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 context is None: + context = InferenceContext() + 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, node_classes.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): + class_bases = [_infer_last(x, context) for x in caller.args[1:]] + new_class = ClassDef( + name="temporary_class", + lineno=0, + col_offset=0, + end_lineno=0, + end_col_offset=0, + parent=SYNTHETIC_ROOT, + ) + 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.type_params + + 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 + + >>> node.body[0] + + """ + + +def _is_metaclass( + klass: ClassDef, + seen: set[str] | None = 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: ClassDef, + ancestors: set[str] | None = None, + context: InferenceContext | None = None, +) -> Literal["class", "exception", "metaclass"]: + """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 klass._type != "metaclass": + # 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( + _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 + + """ + + # 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: Literal["class", "exception", "metaclass"] | None = 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" + + 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, + ) + 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__), + ("__annotations__", self.special_attributes.attr___annotations__), + ) + 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._metaclass = metaclass + self.position = position + self.doc_node = doc_node + self.type_params = type_params or [] + + @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"]: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + return "builtins.type" + + 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=caller.parent, + ) + + # 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] + + 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) and not self.type_params) + 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]: + """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)] + return result + + if class_context: + values += self._metaclass_lookup_attribute(name, context) + + result: list[InferenceResult] = [] + for value in values: + if isinstance(value, node_classes.AssignName): + stmt = value.statement() + # Ignore AnnAssigns without value, which are not attributes in the purest sense. + if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None: + continue + result.append(value) + + if not result: + raise AttributeInferenceError(target=self, attribute=name, context=context) + + return result + + @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(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.parent.scope() + attributes = [first_attr] + [ + attr + for attr in attributes + if attr.parent and attr.parent.scope() == first_scope + ] + functions = [attr for attr in attributes if isinstance(attr, FunctionDef)] + setter = None + for function in functions: + dec_names = function.decoratornames(context=context) + for dec_name in dec_names: + if dec_name is util.Uninferable: + continue + if dec_name.split(".")[-1] == "setter": + setter = function + if setter: + break + if functions: + # Prefer only the last function, unless a property is involved. + last_function = functions[-1] + attributes = [ + a + for a in attributes + if a not in functions or a is last_function or bases._is_property(a) + ] + + 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: + if not context.callcontext and not setter: + context.callcontext = CallContext( + args=function.args.arguments, callee=function + ) + # 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: + 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" + ): + 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. + + This will return an instance of builtins.type. + + :returns: The metaclass. + :rtype: builtins.type + """ + return builtin_lookup("type")[1][0] + + 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) -> bool: + 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) + and isinstance(inferred.value, str) + ): + continue + if not inferred.value: + continue + yield inferred + except InferenceError: + continue + + return None + + def _slots(self): + + 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 + + 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: + baseobj = _infer_last(stmt, context) + except InferenceError: + 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 + + mro = base._compute_mro(context=context) + bases_mro.append(mro) + + 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.type_params + + 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]: + yield self diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py new file mode 100644 index 0000000..8892008 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py @@ -0,0 +1,35 @@ +# 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/utils.py b/.venv/lib/python3.10/site-packages/astroid/nodes/utils.py new file mode 100644 index 0000000..6dc4828 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/nodes/utils.py @@ -0,0 +1,14 @@ +# 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/objects.py b/.venv/lib/python3.10/site-packages/astroid/objects.py new file mode 100644 index 0000000..73670ec --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/objects.py @@ -0,0 +1,360 @@ +# 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, 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, + ) + + 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.""" + + def __init__(self, call, name=None, lineno=None, col_offset=None, parent=None): + # TODO: Pass end_lineno, end_col_offset as well + super().__init__( + name, + lineno=lineno, + col_offset=col_offset, + end_col_offset=0, + end_lineno=0, + 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.""" + + def __init__(self, function, name=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, + ) + + 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]: + yield self diff --git a/.venv/lib/python3.10/site-packages/astroid/protocols.py b/.venv/lib/python3.10/site-packages/astroid/protocols.py new file mode 100644 index 0000000..50e5cfa --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/protocols.py @@ -0,0 +1,958 @@ +# 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.builder import extract_node +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]: + 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 not (value > 0 and self.elts): + node.elts = [] + return node + if len(self.elts) * 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 node_classes.UNATTACHED_UNKNOWN + else: + for inferred in elt.infer(context): + if not isinstance(inferred, util.UninferableBase): + yield inferred + else: + yield node_classes.UNATTACHED_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]: + """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]: + 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]: + # 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 + + def _generate_assigned(): + for assigned in node_classes.unpack_infer(self.type): + if isinstance(assigned, nodes.ClassDef): + assigned = objects.ExceptionInstance(assigned) + + yield assigned + + if isinstance(self.parent, node_classes.TryStar): + # except * handler has assigned ExceptionGroup with caught + # exceptions under exceptions attribute + # pylint: disable-next=stop-iteration-return + eg = next( + node_classes.unpack_infer( + extract_node( + """ +from builtins import ExceptionGroup +ExceptionGroup +""" + ) + ) + ) + assigned = objects.ExceptionInstance(eg) + assigned.instance_attrs["exceptions"] = [ + nodes.List.from_elements(_generate_assigned()) + ] + yield assigned + else: + yield from _generate_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, too-many-branches + + 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( + f"Could not make sense of this, the target must be a tuple, not {type(target)!r}", + 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]: + """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]: + """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]: + """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 + + +@decorators.yes_if_nothing_inferred +def generic_type_assigned_stmts( + self: nodes.TypeVar | nodes.TypeVarTuple | nodes.ParamSpec, + node: nodes.AssignName, + context: InferenceContext | None = None, + assign_path: None = None, +) -> Generator[nodes.NodeNG]: + """Hack. Return any Node so inference doesn't fail + when evaluating __class_getitem__. Revert if it's causing issues. + """ + yield nodes.Const(None) diff --git a/.venv/lib/python3.10/site-packages/astroid/raw_building.py b/.venv/lib/python3.10/site-packages/astroid/raw_building.py new file mode 100644 index 0000000..d1bbbd5 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/raw_building.py @@ -0,0 +1,735 @@ +# 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 TYPE_CHECKING, Any + +from astroid import bases, nodes +from astroid.const import _EMPTY_OBJECT_MARKER, IS_PYPY +from astroid.nodes import node_classes + +if TYPE_CHECKING: + from astroid.manager import AstroidManager + +logger = logging.getLogger(__name__) + + +_FunctionTypes = ( + types.FunctionType + | types.MethodType + | types.BuiltinFunctionType + | types.WrapperDescriptorType + | types.MethodDescriptorType + | types.ClassMethodDescriptorType +) + +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, parent: nodes.NodeNG, 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__] + doc = python_cls.__doc__ if isinstance(python_cls.__doc__, str) else None + ast_klass = build_class(cls_name, parent, cls_bases, doc) + func.instance_attrs["__class__"] = [ast_klass] + + +def build_dummy(runtime_object) -> nodes.EmptyNode: + enode = nodes.EmptyNode() + enode.object = runtime_object + return enode + + +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 + """ + _attach_local_node(node, build_dummy(runtime_object), 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, + parent: nodes.NodeNG, + 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=parent, + ) + 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, + parent: nodes.NodeNG, + 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=parent, + 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 +) -> 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) + + +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 +) -> nodes.FunctionDef: + """create astroid for a living function object""" + ( + args, + posonlyargs, + defaults, + kwonlyargs, + kwonly_defaults, + ) = _get_args_info_from_callable(member) + + return build_function( + getattr(member, "__name__", ""), + node, + args, + posonlyargs, + defaults, + member.__doc__ if isinstance(member.__doc__, str) else None, + kwonlyargs=kwonlyargs, + kwonlydefaults=kwonly_defaults, + ) + + +def object_build_datadescriptor( + node: nodes.Module | nodes.ClassDef, member: type +) -> nodes.ClassDef: + """create astroid for a living data descriptor object""" + return _base_class_object_build(node, member, []) + + +def object_build_methoddescriptor( + node: nodes.Module | nodes.ClassDef, + member: _FunctionTypes, +) -> nodes.FunctionDef: + """create astroid for a living method descriptor object""" + # FIXME get arguments ? + name = getattr(member, "__name__", "") + func = build_function(name, node, doc=member.__doc__) + _add_dunder_class(func, node, member) + return func + + +def _base_class_object_build( + node: nodes.Module | nodes.ClassDef, + member: type, + basenames: list[str], +) -> nodes.ClassDef: + """create astroid for a living class object, with a given set of base names + (e.g. ancestors) + """ + name = getattr(member, "__name__", "") + doc = member.__doc__ if isinstance(member.__doc__, str) else None + klass = build_class(name, node, basenames, doc) + klass._newstyle = isinstance(member, type) + 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): + member_object = member() + if hasattr(member_object, "__dict__"): + instdict = member_object.__dict__ + else: + raise TypeError + 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, + member: _FunctionTypes, + module: types.ModuleType, +) -> nodes.FunctionDef | nodes.EmptyNode: + # 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: + return object_build_methoddescriptor(node, member) + if filename == getattr(module, "__file__", None): + return object_build_function(node, member) + return build_dummy(member) + + +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: + self._manager = manager_instance + 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 alias 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 alias == "__class_getitem__" + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + member = getattr(obj, alias) + except AttributeError: + # damned ExtensionClass.Base, I know you're there ! + attach_dummy_node(node, alias) + continue + if inspect.ismethod(member) and not pypy__class_getitem__: + member = member.__func__ + if inspect.isfunction(member): + child = _build_from_function(node, member, self._module) + elif inspect.isbuiltin(member) or pypy__class_getitem__: + if self.imported_member(node, member, alias): + continue + child = object_build_methoddescriptor(node, member) + elif inspect.isclass(member): + if self.imported_member(node, member, alias): + continue + if member in self._done: + child = self._done[member] + assert isinstance(child, nodes.ClassDef) + else: + child = object_build_class(node, member) + # recursion + self.object_build(child, member) + elif inspect.ismethoddescriptor(member): + child: nodes.NodeNG = object_build_methoddescriptor(node, member) + elif inspect.isdatadescriptor(member): + child = object_build_datadescriptor(node, member) + elif isinstance(member, tuple(node_classes.CONST_CLS)): + if alias in node.special_attributes: + continue + child = nodes.const_factory(member) + elif inspect.isroutine(member): + # This should be called for Jython, where some builtin + # methods aren't caught by isbuiltin branch. + child = _build_from_function(node, member, self._module) + elif _safe_has_attribute(member, "__all__"): + child: nodes.NodeNG = build_module(alias) + # recursion + self.object_build(child, member) + else: + # create an empty node so that the name is actually defined + child: nodes.NodeNG = build_dummy(member) + if child not in node.locals.get(alias, ()): + node.add_local_node(child, alias) + 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 + # pylint: disable-next=import-outside-toplevel + from astroid.manager import AstroidManager + + builder = InspectBuilder(AstroidManager()) + 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", astroid_builtin) + elif cls is TYPE_NOTIMPLEMENTED: + proxy = build_class("NotImplementedType", astroid_builtin) + elif cls is TYPE_ELLIPSIS: + proxy = build_class("Ellipsis", 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=astroid_builtin, + ) + astroid_builtin.set_local(_GeneratorType.name, _GeneratorType) + 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=astroid_builtin, + ) + astroid_builtin.set_local(_AsyncGeneratorType.name, _AsyncGeneratorType) + 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=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=astroid_builtin, + ) + doc = _type.__doc__ if isinstance(_type.__doc__, str) else None + klass.postinit( + bases=[], + body=[], + decorators=None, + doc_node=nodes.Const(doc) if 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() diff --git a/.venv/lib/python3.10/site-packages/astroid/rebuilder.py b/.venv/lib/python3.10/site-packages/astroid/rebuilder.py new file mode 100644 index 0000000..97f3a39 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/rebuilder.py @@ -0,0 +1,1996 @@ +# 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, Collection, Generator +from io import StringIO +from tokenize import TokenInfo, generate_tokens +from typing import TYPE_CHECKING, Final, TypeVar, cast, overload + +from astroid import nodes +from astroid._ast import ParserModule, get_parser_module, parse_function_type_comment +from astroid.const import PY312_PLUS, PY313_PLUS, Context +from astroid.nodes.utils import Position +from astroid.typing import InferenceResult + +if TYPE_CHECKING: + from astroid.manager import AstroidManager + + T_Doc = TypeVar( + "T_Doc", + ast.Module, + ast.ClassDef, + 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 = nodes.Module | nodes.ClassDef | nodes.FunctionDef + + +REDIRECT: Final[dict[str, str]] = { + "arguments": "Arguments", + "comprehension": "Comprehension", + "ListCompFor": "Comprehension", + "GenExprFor": "Comprehension", + "excepthandler": "ExceptHandler", + "keyword": "Keyword", + "match_case": "MatchCase", +} + + +# 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[tuple[nodes.ImportFrom, Collection[str]]] = [] + self._delayed_assattr: list[nodes.AssignAttr] = [] + self._visit_meths: dict[ + type[ast.AST], Callable[[ast.AST, nodes.NodeNG], nodes.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 | 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:] + 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 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), + ) + return newnode + + if TYPE_CHECKING: # noqa: C901 + + @overload + def visit(self, node: ast.arg, parent: nodes.NodeNG) -> nodes.AssignName: ... + + @overload + def visit( + self, node: ast.arguments, parent: nodes.NodeNG + ) -> nodes.Arguments: ... + + @overload + def visit(self, node: ast.Assert, parent: nodes.NodeNG) -> nodes.Assert: ... + + @overload + def visit( + self, node: ast.AsyncFunctionDef, parent: nodes.NodeNG + ) -> nodes.AsyncFunctionDef: ... + + @overload + def visit(self, node: ast.AsyncFor, parent: nodes.NodeNG) -> nodes.AsyncFor: ... + + @overload + def visit(self, node: ast.Await, parent: nodes.NodeNG) -> nodes.Await: ... + + @overload + def visit( + self, node: ast.AsyncWith, parent: nodes.NodeNG + ) -> nodes.AsyncWith: ... + + @overload + def visit(self, node: ast.Assign, parent: nodes.NodeNG) -> nodes.Assign: ... + + @overload + def visit( + self, node: ast.AnnAssign, parent: nodes.NodeNG + ) -> nodes.AnnAssign: ... + + @overload + def visit( + self, node: ast.AugAssign, parent: nodes.NodeNG + ) -> nodes.AugAssign: ... + + @overload + def visit(self, node: ast.BinOp, parent: nodes.NodeNG) -> nodes.BinOp: ... + + @overload + def visit(self, node: ast.BoolOp, parent: nodes.NodeNG) -> nodes.BoolOp: ... + + @overload + def visit(self, node: ast.Break, parent: nodes.NodeNG) -> nodes.Break: ... + + @overload + def visit(self, node: ast.Call, parent: nodes.NodeNG) -> nodes.Call: ... + + @overload + def visit(self, node: ast.ClassDef, parent: nodes.NodeNG) -> nodes.ClassDef: ... + + @overload + def visit(self, node: ast.Continue, parent: nodes.NodeNG) -> nodes.Continue: ... + + @overload + def visit(self, node: ast.Compare, parent: nodes.NodeNG) -> nodes.Compare: ... + + @overload + def visit( + self, node: ast.comprehension, parent: nodes.NodeNG + ) -> nodes.Comprehension: ... + + @overload + def visit(self, node: ast.Delete, parent: nodes.NodeNG) -> nodes.Delete: ... + + @overload + def visit(self, node: ast.Dict, parent: nodes.NodeNG) -> nodes.Dict: ... + + @overload + def visit(self, node: ast.DictComp, parent: nodes.NodeNG) -> nodes.DictComp: ... + + @overload + def visit(self, node: ast.Expr, parent: nodes.NodeNG) -> nodes.Expr: ... + + @overload + def visit( + self, node: ast.ExceptHandler, parent: nodes.NodeNG + ) -> nodes.ExceptHandler: ... + + @overload + def visit(self, node: ast.For, parent: nodes.NodeNG) -> nodes.For: ... + + @overload + def visit( + self, node: ast.ImportFrom, parent: nodes.NodeNG + ) -> nodes.ImportFrom: ... + + @overload + def visit( + self, node: ast.FunctionDef, parent: nodes.NodeNG + ) -> nodes.FunctionDef: ... + + @overload + def visit( + self, node: ast.GeneratorExp, parent: nodes.NodeNG + ) -> nodes.GeneratorExp: ... + + @overload + def visit( + self, node: ast.Attribute, parent: nodes.NodeNG + ) -> nodes.Attribute: ... + + @overload + def visit(self, node: ast.Global, parent: nodes.NodeNG) -> nodes.Global: ... + + @overload + def visit(self, node: ast.If, parent: nodes.NodeNG) -> nodes.If: ... + + @overload + def visit(self, node: ast.IfExp, parent: nodes.NodeNG) -> nodes.IfExp: ... + + @overload + def visit(self, node: ast.Import, parent: nodes.NodeNG) -> nodes.Import: ... + + @overload + def visit( + self, node: ast.JoinedStr, parent: nodes.NodeNG + ) -> nodes.JoinedStr: ... + + @overload + def visit( + self, node: ast.FormattedValue, parent: nodes.NodeNG + ) -> nodes.FormattedValue: ... + + @overload + def visit( + self, node: ast.NamedExpr, parent: nodes.NodeNG + ) -> nodes.NamedExpr: ... + + @overload + def visit(self, node: ast.keyword, parent: nodes.NodeNG) -> nodes.Keyword: ... + + @overload + def visit(self, node: ast.Lambda, parent: nodes.NodeNG) -> nodes.Lambda: ... + + @overload + def visit(self, node: ast.List, parent: nodes.NodeNG) -> nodes.List: ... + + @overload + def visit(self, node: ast.ListComp, parent: nodes.NodeNG) -> nodes.ListComp: ... + + @overload + def visit( + self, node: ast.Name, parent: nodes.NodeNG + ) -> nodes.Name | nodes.Const | nodes.AssignName | nodes.DelName: ... + + @overload + def visit(self, node: ast.Nonlocal, parent: nodes.NodeNG) -> nodes.Nonlocal: ... + + @overload + def visit(self, node: ast.Constant, parent: nodes.NodeNG) -> nodes.Const: ... + + if sys.version_info >= (3, 12): + + @overload + def visit( + self, node: ast.ParamSpec, parent: nodes.NodeNG + ) -> nodes.ParamSpec: ... + + @overload + def visit(self, node: ast.Pass, parent: nodes.NodeNG) -> nodes.Pass: ... + + @overload + def visit(self, node: ast.Raise, parent: nodes.NodeNG) -> nodes.Raise: ... + + @overload + def visit(self, node: ast.Return, parent: nodes.NodeNG) -> nodes.Return: ... + + @overload + def visit(self, node: ast.Set, parent: nodes.NodeNG) -> nodes.Set: ... + + @overload + def visit(self, node: ast.SetComp, parent: nodes.NodeNG) -> nodes.SetComp: ... + + @overload + def visit(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice: ... + + @overload + def visit( + self, node: ast.Subscript, parent: nodes.NodeNG + ) -> nodes.Subscript: ... + + @overload + def visit(self, node: ast.Starred, parent: nodes.NodeNG) -> nodes.Starred: ... + + @overload + def visit(self, node: ast.Try, parent: nodes.NodeNG) -> nodes.Try: ... + + if sys.version_info >= (3, 11): + + @overload + def visit( + self, node: ast.TryStar, parent: nodes.NodeNG + ) -> nodes.TryStar: ... + + @overload + def visit(self, node: ast.Tuple, parent: nodes.NodeNG) -> nodes.Tuple: ... + + if sys.version_info >= (3, 12): + + @overload + def visit( + self, node: ast.TypeAlias, parent: nodes.NodeNG + ) -> nodes.TypeAlias: ... + + @overload + def visit( + self, node: ast.TypeVar, parent: nodes.NodeNG + ) -> nodes.TypeVar: ... + + @overload + def visit( + self, node: ast.TypeVarTuple, parent: nodes.NodeNG + ) -> nodes.TypeVarTuple: ... + + @overload + def visit(self, node: ast.UnaryOp, parent: nodes.NodeNG) -> nodes.UnaryOp: ... + + @overload + def visit(self, node: ast.While, parent: nodes.NodeNG) -> nodes.While: ... + + @overload + def visit(self, node: ast.With, parent: nodes.NodeNG) -> nodes.With: ... + + @overload + def visit(self, node: ast.Yield, parent: nodes.NodeNG) -> nodes.Yield: ... + + @overload + def visit( + self, node: ast.YieldFrom, parent: nodes.NodeNG + ) -> nodes.YieldFrom: ... + + @overload + def visit(self, node: ast.Match, parent: nodes.NodeNG) -> nodes.Match: ... + + @overload + def visit( + self, node: ast.match_case, parent: nodes.NodeNG + ) -> nodes.MatchCase: ... + + @overload + def visit( + self, node: ast.MatchValue, parent: nodes.NodeNG + ) -> nodes.MatchValue: ... + + @overload + def visit( + self, node: ast.MatchSingleton, parent: nodes.NodeNG + ) -> nodes.MatchSingleton: ... + + @overload + def visit( + self, node: ast.MatchSequence, parent: nodes.NodeNG + ) -> nodes.MatchSequence: ... + + @overload + def visit( + self, node: ast.MatchMapping, parent: nodes.NodeNG + ) -> nodes.MatchMapping: ... + + @overload + def visit( + self, node: ast.MatchClass, parent: nodes.NodeNG + ) -> nodes.MatchClass: ... + + @overload + def visit( + self, node: ast.MatchStar, parent: nodes.NodeNG + ) -> nodes.MatchStar: ... + + @overload + def visit(self, node: ast.MatchAs, parent: nodes.NodeNG) -> nodes.MatchAs: ... + + @overload + def visit(self, node: ast.MatchOr, parent: nodes.NodeNG) -> nodes.MatchOr: ... + + @overload + def visit(self, node: ast.pattern, parent: nodes.NodeNG) -> nodes.Pattern: ... + + if sys.version_info >= (3, 14): + + @overload + def visit( + self, node: ast.TemplateStr, parent: nodes.NodeNG + ) -> nodes.TemplateStr: ... + + @overload + def visit( + self, node: ast.Interpolation, parent: nodes.NodeNG + ) -> nodes.Interpolation: ... + + @overload + def visit(self, node: ast.AST, parent: nodes.NodeNG) -> nodes.NodeNG: ... + + @overload + def visit(self, node: None, parent: nodes.NodeNG) -> None: ... + + def visit(self, node: ast.AST | None, parent: nodes.NodeNG) -> nodes.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: nodes.NodeNG) -> nodes.AssignName: + """Visit an arg node by returning a fresh AssignName instance.""" + return self.visit_assignname(node, parent, node.arg) + + def visit_arguments( + self, node: ast.arguments, parent: nodes.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, + ( + nodes.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 + ), + ( + nodes.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: nodes.NodeNG | None = None + kwargannotation: nodes.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) + + 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: nodes.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: nodes.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 + ), + ) -> nodes.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: nodes.NodeNG + ) -> tuple[nodes.NodeNG | None, list[nodes.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: nodes.NodeNG | None = None + argtypes: list[nodes.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: nodes.NodeNG + ) -> nodes.AsyncFunctionDef: + return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent) + + def visit_asyncfor( + self, node: ast.AsyncFor, parent: nodes.NodeNG + ) -> nodes.AsyncFor: + return self._visit_for(nodes.AsyncFor, node, parent) + + def visit_await(self, node: ast.Await, parent: nodes.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: nodes.NodeNG + ) -> nodes.AsyncWith: + return self._visit_with(nodes.AsyncWith, node, parent) + + def visit_assign(self, node: ast.Assign, parent: nodes.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: nodes.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: nodes.NodeNG, node_name: str + ) -> nodes.AssignName: ... + + @overload + def visit_assignname( + self, node: ast.AST, parent: nodes.NodeNG, node_name: None + ) -> None: ... + + def visit_assignname( + self, node: ast.AST, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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 [] + ), + ) + parent.set_local(newnode.name, newnode) + return newnode + + def visit_continue( + self, node: ast.Continue, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.NodeNG, newnode: nodes.Dict + ) -> Generator[tuple[nodes.NodeNG, nodes.NodeNG]]: + for key, value in zip(node.keys, node.values): + rebuilt_key: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.NodeNG + ) -> nodes.For: ... + + @overload + def _visit_for( + self, cls: type[nodes.AsyncFor], node: ast.AsyncFor, parent: nodes.NodeNG + ) -> nodes.AsyncFor: ... + + def _visit_for( + self, cls: type[_ForT], node: ast.For | ast.AsyncFor, parent: nodes.NodeNG + ) -> _ForT: + """Visit a For node by returning a fresh instance of it.""" + newnode = cls( + 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( + 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: nodes.NodeNG) -> nodes.For: + return self._visit_for(nodes.For, node, parent) + + def visit_importfrom( + self, node: ast.ImportFrom, parent: nodes.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, self._global_names[-1].keys() if self._global_names else ()) + ) + return newnode + + @overload + def _visit_functiondef( + self, cls: type[nodes.FunctionDef], node: ast.FunctionDef, parent: nodes.NodeNG + ) -> nodes.FunctionDef: ... + + @overload + def _visit_functiondef( + self, + cls: type[nodes.AsyncFunctionDef], + node: ast.AsyncFunctionDef, + parent: nodes.NodeNG, + ) -> nodes.AsyncFunctionDef: ... + + def _visit_functiondef( + self, + cls: type[_FunctionT], + node: ast.FunctionDef | ast.AsyncFunctionDef, + parent: nodes.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: nodes.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() + parent.set_local(newnode.name, newnode) + return newnode + + def visit_functiondef( + self, node: ast.FunctionDef, parent: nodes.NodeNG + ) -> nodes.FunctionDef: + return self._visit_functiondef(nodes.FunctionDef, node, parent) + + def visit_generatorexp( + self, node: ast.GeneratorExp, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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).split(".")[0] + if self._global_names and name in self._global_names[-1]: + parent.root().set_local(name, newnode) + else: + parent.set_local(name, newnode) + return newnode + + def visit_joinedstr( + self, node: ast.JoinedStr, parent: nodes.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: nodes.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: nodes.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 + + def visit_keyword(self, node: ast.keyword, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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((nodes.AssignName | nodes.DelName), newnode) + self._save_assignment(newnode) + return newnode + + def visit_nonlocal( + self, node: ast.Nonlocal, parent: nodes.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: nodes.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: nodes.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), + default_value=( + self.visit(node.default_value, newnode) if PY313_PLUS else None + ), + ) + return newnode + + def visit_pass(self, node: ast.Pass, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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), + default_value=( + self.visit(node.default_value, newnode) if PY313_PLUS else None + ), + ) + return newnode + + def visit_typevartuple( + self, node: ast.TypeVarTuple, parent: nodes.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), + default_value=( + self.visit(node.default_value, newnode) if PY313_PLUS else None + ), + ) + return newnode + + def visit_unaryop(self, node: ast.UnaryOp, parent: nodes.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: nodes.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: nodes.NodeNG + ) -> nodes.With: ... + + @overload + def _visit_with( + self, cls: type[nodes.AsyncWith], node: ast.AsyncWith, parent: nodes.NodeNG + ) -> nodes.AsyncWith: ... + + def _visit_with( + self, + cls: type[_WithT], + node: ast.With | ast.AsyncWith, + parent: nodes.NodeNG, + ) -> _WithT: + newnode = cls( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + + def visit_child( + child: ast.withitem, + ) -> tuple[nodes.NodeNG, nodes.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: nodes.NodeNG) -> nodes.NodeNG: + return self._visit_with(nodes.With, node, parent) + + def visit_yield(self, node: ast.Yield, parent: nodes.NodeNG) -> nodes.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: nodes.NodeNG + ) -> nodes.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 + + def visit_match(self, node: ast.Match, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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 + + if sys.version_info >= (3, 14): + + def visit_templatestr( + self, node: ast.TemplateStr, parent: nodes.NodeNG + ) -> nodes.TemplateStr: + newnode = nodes.TemplateStr( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + newnode.postinit( + values=[self.visit(value, newnode) for value in node.values] + ) + return newnode + + def visit_interpolation( + self, node: ast.Interpolation, parent: nodes.NodeNG + ) -> nodes.Interpolation: + newnode = nodes.Interpolation( + 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, parent), + str=node.str, + conversion=node.conversion, + format_spec=self.visit(node.format_spec, parent), + ) + return newnode diff --git a/.venv/lib/python3.10/site-packages/astroid/test_utils.py b/.venv/lib/python3.10/site-packages/astroid/test_utils.py new file mode 100644 index 0000000..afddb1a --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/test_utils.py @@ -0,0 +1,78 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/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() + m.module_denylist = set() + return m diff --git a/.venv/lib/python3.10/site-packages/astroid/transforms.py b/.venv/lib/python3.10/site-packages/astroid/transforms.py new file mode 100644 index 0000000..d44ec3d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/transforms.py @@ -0,0 +1,163 @@ +# 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 collections import defaultdict +from collections.abc import Callable +from typing import TYPE_CHECKING, 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 = Callable[[_SuccessfulInferenceResultT], bool] | None + +# pylint: disable-next=consider-alternative-union-syntax +_Vistables = Union[ + "nodes.NodeNG", list["nodes.NodeNG"], tuple["nodes.NodeNG", ...], str, None +] +_VisitReturns = ( + 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) + if TYPE_CHECKING: + 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 not node: + return node + 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 isinstance(node, str): + return node + + try: + return self._visit(node) + except RecursionError: + # Returning the node untransformed is better than giving up. + warnings.warn( + f"Astroid was unable to transform {node}.\n" + "Some functionality will be missing unless the system recursion limit is lifted.\n" + "From pylint, try: --init-hook='import sys; sys.setrecursionlimit(2000)' or higher.", + UserWarning, + stacklevel=0, + ) + return 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/typing.py b/.venv/lib/python3.10/site-packages/astroid/typing.py new file mode 100644 index 0000000..37ea434 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/typing.py @@ -0,0 +1,98 @@ +# 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, Generator +from typing import ( + TYPE_CHECKING, + Any, + 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 + + +# pylint: disable=consider-alternative-union-syntax +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], +] + + +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 diff --git a/.venv/lib/python3.10/site-packages/astroid/util.py b/.venv/lib/python3.10/site-packages/astroid/util.py new file mode 100644 index 0000000..510b81c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/astroid/util.py @@ -0,0 +1,159 @@ +# 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 diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE new file mode 100644 index 0000000..648ba98 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE @@ -0,0 +1,35 @@ +Copyright (c) 2004-2016 California Institute of Technology. +Copyright (c) 2016-2025 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. + diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA new file mode 100644 index 0000000..6c3e255 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA @@ -0,0 +1,281 @@ +Metadata-Version: 2.1 +Name: dill +Version: 0.4.0 +Summary: serialize all of Python +Home-page: https://github.com/uqfoundation/dill +Download-URL: https://pypi.org/project/dill/#files +Author: Mike McKerns +Author-email: mmckerns@uqfoundation.org +Maintainer: Mike McKerns +Maintainer-email: mmckerns@uqfoundation.org +License: BSD-3-Clause +Project-URL: Documentation, http://dill.rtfd.io +Project-URL: Source Code, https://github.com/uqfoundation/dill +Project-URL: Bug Tracker, https://github.com/uqfoundation/dill/issues +Platform: Linux +Platform: Windows +Platform: Mac +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: BSD License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Software Development +Requires-Python: >=3.8 +License-File: LICENSE +Provides-Extra: graph +Requires-Dist: objgraph >=1.7.2 ; extra == 'graph' +Provides-Extra: profile +Requires-Dist: gprof2dot >=2022.7.29 ; extra == 'profile' +Provides-Extra: readline + +----------------------------- +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] + +To optionally include the ``gprof2dot`` diagnostic tool in the install:: + + $ pip install dill[profile] + +For windows users, to optionally install session history tools:: + + $ pip install dill[readline] + + +Requirements +============ + +``dill`` requires: + + - ``python`` (or ``pypy``), **>=3.8** + - ``setuptools``, **>=42** + +Optional requirements: + + - ``objgraph``, **>=1.7.2** + - ``gprof2dot``, **>=2022.7.29** + - ``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: at 0x7fe074f8c280> + ├┬ F2: + │└ # F2 [34 B] + ├┬ Co: at 0x7fe07501eb30, file "", line 1> + │├┬ F2: + ││└ # F2 [19 B] + │└ # Co [87 B] + ├┬ D1: + │└ # D1 [22 B] + ├┬ D2: + │└ # D2 [2 B] + ├┬ D2: + │├┬ D2: + ││└ # 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. diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD new file mode 100644 index 0000000..6a03692 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD @@ -0,0 +1,101 @@ +../../../bin/get_gprof,sha256=u3jtVyBFM-MOV_hjjswKy-BCbTL8FHzFEyPG-R3uJ3M,2501 +../../../bin/get_objgraph,sha256=r5IbXHDLImzWN2a4SQd3IHQMl_gwnwYvSZdUoeLDPhM,1695 +../../../bin/undill,sha256=N92W9cF0G6aYodwZtn70tgEdYpX0YUd2D0GGBxfOLRQ,631 +dill-0.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +dill-0.4.0.dist-info/LICENSE,sha256=mYpIzbuubSrNbyOVUajkpFDpZ-udj9jiQsNOnmTkDiY,1790 +dill-0.4.0.dist-info/METADATA,sha256=Zzr1eKtuTvrriL-H_4DmN7fxjB9tENzGRz0DwAI7u5Q,10174 +dill-0.4.0.dist-info/RECORD,, +dill-0.4.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92 +dill-0.4.0.dist-info/top_level.txt,sha256=HLSIyYIjQzJiBvs3_-16ntezE3j6mWGTW0DT1xDd7X0,5 +dill/__diff.py,sha256=yLCKcd0GkDxR86wdE0SG2qw5Yxhon6qHeOeI1li8Vrc,7146 +dill/__info__.py,sha256=sD2tcjIAuRmMAIqBtF4-JWK7VTCIoQx3axUNBQb-qPw,10756 +dill/__init__.py,sha256=u5X9cMJo3zAtpGN4MUATU71s5wNvnIbXsZXi29DyvXE,3798 +dill/__pycache__/__diff.cpython-310.pyc,, +dill/__pycache__/__info__.cpython-310.pyc,, +dill/__pycache__/__init__.cpython-310.pyc,, +dill/__pycache__/_dill.cpython-310.pyc,, +dill/__pycache__/_objects.cpython-310.pyc,, +dill/__pycache__/_shims.cpython-310.pyc,, +dill/__pycache__/detect.cpython-310.pyc,, +dill/__pycache__/logger.cpython-310.pyc,, +dill/__pycache__/objtypes.cpython-310.pyc,, +dill/__pycache__/pointers.cpython-310.pyc,, +dill/__pycache__/session.cpython-310.pyc,, +dill/__pycache__/settings.cpython-310.pyc,, +dill/__pycache__/source.cpython-310.pyc,, +dill/__pycache__/temp.cpython-310.pyc,, +dill/_dill.py,sha256=DX51DqW1DXL_5VdZ-zF5w9avxiVOsNh6cNwMkd39Fwo,91313 +dill/_objects.py,sha256=wPETr0_Gtj__cFAIEO6FRSC_vpBYexmlDiHl_d8geJU,19740 +dill/_shims.py,sha256=mGN22u7TeK-keDvhGvWZVvCm-GW0TY04MXW-Hncbh7c,6635 +dill/detect.py,sha256=L0eWBEVzqMV6nf8gd5R4G-cytzJEjBNTKAlsVVkJRlY,11207 +dill/logger.py,sha256=WNX59lxDHuZnSMKZ294i0uCDTmbneWPLu79Be7mOa-Q,11143 +dill/objtypes.py,sha256=NvSqfHX6AY2-QOVUv0hEda_GEcx_uiuGPQvILJu8TyY,736 +dill/pointers.py,sha256=Q8AYqhdcvdq9X4HZ46lihM2bnZqGZG2vVoGpRxTHRNE,4467 +dill/session.py,sha256=qc41kfzXT9MIx1KGSEIVbUsDKBHErhptpeQzwcGspkU,23541 +dill/settings.py,sha256=hmtpAbplWE-HHxRHLPEkOxpEqWoCMTMn_FRa-1seao4,630 +dill/source.py,sha256=aaK9d3J7yNQuVw15ESRXxVqE8DxYHf1TfSOBEQYcZmU,45507 +dill/temp.py,sha256=QhkBKO5eNsEwSHdhiw5nCzALchAsl1yWQ1apZlVXY7w,8027 +dill/tests/__init__.py,sha256=5z3npvfFh6Xw5xa8ML-AQk3PDyTc3MIngJlUVVf35vk,479 +dill/tests/__main__.py,sha256=i9A86Q2NGoEhO-080sgnv2-TXxTRoH-gN2RTaPvpGvA,899 +dill/tests/__pycache__/__init__.cpython-310.pyc,, +dill/tests/__pycache__/__main__.cpython-310.pyc,, +dill/tests/__pycache__/test_abc.cpython-310.pyc,, +dill/tests/__pycache__/test_check.cpython-310.pyc,, +dill/tests/__pycache__/test_classdef.cpython-310.pyc,, +dill/tests/__pycache__/test_dataclasses.cpython-310.pyc,, +dill/tests/__pycache__/test_detect.cpython-310.pyc,, +dill/tests/__pycache__/test_dictviews.cpython-310.pyc,, +dill/tests/__pycache__/test_diff.cpython-310.pyc,, +dill/tests/__pycache__/test_extendpickle.cpython-310.pyc,, +dill/tests/__pycache__/test_fglobals.cpython-310.pyc,, +dill/tests/__pycache__/test_file.cpython-310.pyc,, +dill/tests/__pycache__/test_functions.cpython-310.pyc,, +dill/tests/__pycache__/test_functors.cpython-310.pyc,, +dill/tests/__pycache__/test_logger.cpython-310.pyc,, +dill/tests/__pycache__/test_mixins.cpython-310.pyc,, +dill/tests/__pycache__/test_module.cpython-310.pyc,, +dill/tests/__pycache__/test_moduledict.cpython-310.pyc,, +dill/tests/__pycache__/test_nested.cpython-310.pyc,, +dill/tests/__pycache__/test_objects.cpython-310.pyc,, +dill/tests/__pycache__/test_properties.cpython-310.pyc,, +dill/tests/__pycache__/test_pycapsule.cpython-310.pyc,, +dill/tests/__pycache__/test_recursive.cpython-310.pyc,, +dill/tests/__pycache__/test_registered.cpython-310.pyc,, +dill/tests/__pycache__/test_restricted.cpython-310.pyc,, +dill/tests/__pycache__/test_selected.cpython-310.pyc,, +dill/tests/__pycache__/test_session.cpython-310.pyc,, +dill/tests/__pycache__/test_source.cpython-310.pyc,, +dill/tests/__pycache__/test_sources.cpython-310.pyc,, +dill/tests/__pycache__/test_temp.cpython-310.pyc,, +dill/tests/__pycache__/test_threads.cpython-310.pyc,, +dill/tests/__pycache__/test_weakref.cpython-310.pyc,, +dill/tests/test_abc.py,sha256=OT50obWxTiqk2eZLrbfHGPU9_Wr30wFTQ1Z8CfsdClU,4227 +dill/tests/test_check.py,sha256=OT6iu8R1BzZFQdiR3YgPoKKV7KJ2AYLJP62zTgsThdo,1396 +dill/tests/test_classdef.py,sha256=M3zzqKruuqIFTZDUqKYbjMtJmVkI59N0DxWZe019-qM,8600 +dill/tests/test_dataclasses.py,sha256=MPkDH5pfvYlYbwlGm-uFMN7svhPUImqKUYpROWO_Wak,890 +dill/tests/test_detect.py,sha256=wjgVFO8UMDLaM7A1cu1bQNXgYQCOpLGYqMYxID5CNsk,4144 +dill/tests/test_dictviews.py,sha256=qPAuqmp1yApPsARB6EKjmIWIAigK6P0L2Q68my02D-I,1337 +dill/tests/test_diff.py,sha256=TXCpiSPVTwEVR9-cs_sr_MYP9T7R2Pw2N2PAvnOvWFI,2667 +dill/tests/test_extendpickle.py,sha256=mcnPB0_YIqx6Ct678LUzWER1FRA1kVSRxE6PKhaN2oQ,1315 +dill/tests/test_fglobals.py,sha256=tbdBdLzW_7rx5mR-BzeThzEhH0KbtDZfAazLBYC_h94,1676 +dill/tests/test_file.py,sha256=XjW_EFSPsH7udEijchzef-OunBiJGJ-ieLRm8Vcai4Q,13578 +dill/tests/test_functions.py,sha256=AVBbHCDffw4Jca3Kijjae4rQfX0ZFTgurUjPeP2qqoM,4267 +dill/tests/test_functors.py,sha256=GSHR6UGBuiq0iUk4mk04Ly9ohSVdt9fBCrEYwByzSxk,930 +dill/tests/test_logger.py,sha256=PiZHr3bGIh6rW_Rx3uAQlVl5oFl-n2BQjXkpPzbmjng,2385 +dill/tests/test_mixins.py,sha256=GHO-GXeYvWzZeysJnsIDR8sTj_RADBs0-z23r_SfoNA,4007 +dill/tests/test_module.py,sha256=BOJ1mU4qaK680gEERfFXAIElTTuOg1nY4Ne26UhcpvU,1943 +dill/tests/test_moduledict.py,sha256=SlosX5tkmdfGLuhYweASu-WKIsrgphXrEm20veZmFJc,1182 +dill/tests/test_nested.py,sha256=dLgI3ZKWWAyZxXfTF131EHtph8yTS-e9Wqb8Mj3HA6A,3146 +dill/tests/test_objects.py,sha256=QyYSMiBZDyjhsGwDkdUeJH65rHODQMrLFajy-fUqzLI,1931 +dill/tests/test_properties.py,sha256=PinOM_C2Fzfj_3FlQm8OdjAmJ8WQ38nHWKkfHI4rCeA,1346 +dill/tests/test_pycapsule.py,sha256=XgT8VQZAAY3q7oV3N8726PnqU4GjuxSoT_FuFsoNsso,1417 +dill/tests/test_recursive.py,sha256=Bq2Q--Ro3Mb4CppYm5K5v0__5mGcFvSEjzmZOC81C64,4182 +dill/tests/test_registered.py,sha256=jgUBl_msBiqQTgvCC1bwKTGK-jmAz2FSE4pcJgEDRBw,1573 +dill/tests/test_restricted.py,sha256=mk93WLtNAEQr03h4N-NZO7Wr9xPWvzgMPOPEdi4Aku4,783 +dill/tests/test_selected.py,sha256=LjhZNntQ9KDJf9lhEYTHqzbFpsoeEFK2wobO1JT0FRg,3258 +dill/tests/test_session.py,sha256=-xmj46QxC60MaRIljv2eVrqIQYZrIcL54PsPaTLpoXc,10161 +dill/tests/test_source.py,sha256=EOTG_Fh0cWlICTRq04KH0Y5_G9BgGigkAOhr3CD1-dk,7059 +dill/tests/test_sources.py,sha256=sQXVRsv_u9i1kTyJYRtX_Ykb3TgaPrQ3sI2az8bNxQQ,8672 +dill/tests/test_temp.py,sha256=8NvuImVkYM1dw-j_nhaeDAb8Oan9whCOoehXktqurtw,2619 +dill/tests/test_threads.py,sha256=PD8RVdtu_kCkM7AVV8a81ywVWRi1g3GyDRocx04ZRFg,1257 +dill/tests/test_weakref.py,sha256=TfQHVMqgzeBywBiTjHj6ep8E6GF90y5SFzLgWbRbc2Q,1602 diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL new file mode 100644 index 0000000..bab98d6 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.43.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt new file mode 100644 index 0000000..85eea70 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt @@ -0,0 +1 @@ +dill diff --git a/.venv/lib/python3.10/site-packages/dill/__diff.py b/.venv/lib/python3.10/site-packages/dill/__diff.py new file mode 100644 index 0000000..63e8182 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/__diff.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +""" +Module to show if an object has changed since it was memorised +""" + +import builtins +import os +import sys +import types +try: + import numpy.ma + HAS_NUMPY = True +except ImportError: + HAS_NUMPY = False + +# pypy doesn't use reference counting +getrefcount = getattr(sys, 'getrefcount', lambda x:0) + +# memo of objects indexed by id to a tuple (attributes, sequence items) +# attributes is a dict indexed by attribute name to attribute id +# sequence items is either a list of ids, of a dictionary of keys to ids +memo = {} +id_to_obj = {} +# types that cannot have changing attributes +builtins_types = set((str, list, dict, set, frozenset, int)) +dont_memo = set(id(i) for i in (memo, sys.modules, sys.path_importer_cache, + os.environ, id_to_obj)) + + +def get_attrs(obj): + """ + Gets all the attributes of an object though its __dict__ or return None + """ + if type(obj) in builtins_types \ + or type(obj) is type and obj in builtins_types: + return + return getattr(obj, '__dict__', None) + + +def get_seq(obj, cache={str: False, frozenset: False, list: True, set: True, + dict: True, tuple: True, type: False, + types.ModuleType: False, types.FunctionType: False, + types.BuiltinFunctionType: False}): + """ + Gets all the items in a sequence or return None + """ + try: + o_type = obj.__class__ + except AttributeError: + o_type = type(obj) + hsattr = hasattr + if o_type in cache: + if cache[o_type]: + if hsattr(obj, "copy"): + return obj.copy() + return obj + elif HAS_NUMPY and o_type in (numpy.ndarray, numpy.ma.core.MaskedConstant): + if obj.shape and obj.size: + return obj + else: + return [] + elif hsattr(obj, "__contains__") and hsattr(obj, "__iter__") \ + and hsattr(obj, "__len__") and hsattr(o_type, "__contains__") \ + and hsattr(o_type, "__iter__") and hsattr(o_type, "__len__"): + cache[o_type] = True + if hsattr(obj, "copy"): + return obj.copy() + return obj + else: + cache[o_type] = False + return None + + +def memorise(obj, force=False): + """ + Adds an object to the memo, and recursively adds all the objects + attributes, and if it is a container, its items. Use force=True to update + an object already in the memo. Updating is not recursively done. + """ + obj_id = id(obj) + if obj_id in memo and not force or obj_id in dont_memo: + return + id_ = id + g = get_attrs(obj) + if g is None: + attrs_id = None + else: + attrs_id = dict((key,id_(value)) for key, value in g.items()) + + s = get_seq(obj) + if s is None: + seq_id = None + elif hasattr(s, "items"): + seq_id = dict((id_(key),id_(value)) for key, value in s.items()) + elif not hasattr(s, "__len__"): #XXX: avoid TypeError from unexpected case + seq_id = None + else: + seq_id = [id_(i) for i in s] + + memo[obj_id] = attrs_id, seq_id + id_to_obj[obj_id] = obj + mem = memorise + if g is not None: + [mem(value) for key, value in g.items()] + + if s is not None: + if hasattr(s, "items"): + [(mem(key), mem(item)) + for key, item in s.items()] + else: + if hasattr(s, '__len__'): + [mem(item) for item in s] + else: mem(s) + + +def release_gone(): + itop, mp, src = id_to_obj.pop, memo.pop, getrefcount + [(itop(id_), mp(id_)) for id_, obj in list(id_to_obj.items()) + if src(obj) < 4] #XXX: correct for pypy? + + +def whats_changed(obj, seen=None, simple=False, first=True): + """ + Check an object against the memo. Returns a list in the form + (attribute changes, container changed). Attribute changes is a dict of + attribute name to attribute value. container changed is a boolean. + If simple is true, just returns a boolean. None for either item means + that it has not been checked yet + """ + # Special cases + if first: + # ignore the _ variable, which only appears in interactive sessions + if "_" in builtins.__dict__: + del builtins._ + if seen is None: + seen = {} + + obj_id = id(obj) + + if obj_id in seen: + if simple: + return any(seen[obj_id]) + return seen[obj_id] + + # Safety checks + if obj_id in dont_memo: + seen[obj_id] = [{}, False] + if simple: + return False + return seen[obj_id] + elif obj_id not in memo: + if simple: + return True + else: + raise RuntimeError("Object not memorised " + str(obj)) + + seen[obj_id] = ({}, False) + + chngd = whats_changed + id_ = id + + # compare attributes + attrs = get_attrs(obj) + if attrs is None: + changed = {} + else: + obj_attrs = memo[obj_id][0] + obj_get = obj_attrs.get + changed = dict((key,None) for key in obj_attrs if key not in attrs) + for key, o in attrs.items(): + if id_(o) != obj_get(key, None) or chngd(o, seen, True, False): + changed[key] = o + + # compare sequence + items = get_seq(obj) + seq_diff = False + if (items is not None) and (hasattr(items, '__len__')): + obj_seq = memo[obj_id][1] + if (len(items) != len(obj_seq)): + seq_diff = True + elif hasattr(obj, "items"): # dict type obj + obj_get = obj_seq.get + for key, item in items.items(): + if id_(item) != obj_get(id_(key)) \ + or chngd(key, seen, True, False) \ + or chngd(item, seen, True, False): + seq_diff = True + break + else: + for i, j in zip(items, obj_seq): # list type obj + if id_(i) != j or chngd(i, seen, True, False): + seq_diff = True + break + seen[obj_id] = changed, seq_diff + if simple: + return changed or seq_diff + return changed, seq_diff + + +def has_changed(*args, **kwds): + kwds['simple'] = True # ignore simple if passed in + return whats_changed(*args, **kwds) + +__import__ = __import__ + + +def _imp(*args, **kwds): + """ + Replaces the default __import__, to allow a module to be memorised + before the user can change it + """ + before = set(sys.modules.keys()) + mod = __import__(*args, **kwds) + after = set(sys.modules.keys()).difference(before) + for m in after: + memorise(sys.modules[m]) + return mod + +builtins.__import__ = _imp +if hasattr(builtins, "_"): + del builtins._ + +# memorise all already imported modules. This implies that this must be +# imported first for any changes to be recorded +for mod in list(sys.modules.values()): + memorise(mod) +release_gone() diff --git a/.venv/lib/python3.10/site-packages/dill/__info__.py b/.venv/lib/python3.10/site-packages/dill/__info__.py new file mode 100644 index 0000000..e71bdfa --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/__info__.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2025 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] + +To optionally include the ``gprof2dot`` diagnostic tool in the install:: + + $ pip install dill[profile] + +For windows users, to optionally install session history tools:: + + $ pip install dill[readline] + + +Requirements +============ + +``dill`` requires: + + - ``python`` (or ``pypy``), **>=3.8** + - ``setuptools``, **>=42** + +Optional requirements: + + - ``objgraph``, **>=1.7.2** + - ``gprof2dot``, **>=2022.7.29** + - ``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: at 0x7fe074f8c280> + ├┬ F2: + │└ # F2 [34 B] + ├┬ Co: at 0x7fe07501eb30, file "", line 1> + │├┬ F2: + ││└ # F2 [19 B] + │└ # Co [87 B] + ├┬ D1: + │└ # D1 [22 B] + ├┬ D2: + │└ # D2 [2 B] + ├┬ D2: + │├┬ D2: + ││└ # 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.4.0' +__author__ = 'Mike McKerns' + +__license__ = ''' +Copyright (c) 2004-2016 California Institute of Technology. +Copyright (c) 2016-2025 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. + +''' diff --git a/.venv/lib/python3.10/site-packages/dill/__init__.py b/.venv/lib/python3.10/site-packages/dill/__init__.py new file mode 100644 index 0000000..7fc930c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/__init__.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +# author, version, license, and long description +try: # the package is installed + from .__info__ import __version__, __author__, __doc__, __license__ +except: # pragma: no cover + import os + import sys + parent = os.path.dirname(os.path.abspath(os.path.dirname(__file__))) + sys.path.append(parent) + # get distribution meta info + from version import (__version__, __author__, + get_license_text, get_readme_as_rst) + __license__ = get_license_text(os.path.join(parent, 'LICENSE')) + __license__ = "\n%s" % __license__ + __doc__ = get_readme_as_rst(os.path.join(parent, 'README.md')) + del os, sys, parent, get_license_text, get_readme_as_rst + + +from ._dill import ( + 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, +) +from .session import ( + dump_module, load_module, load_module_asdict, + dump_session, load_session # backward compatibility +) +from . import detect, logger, session, source, temp + +# get global settings +from .settings import settings + +# make sure "trace" is turned off +logger.trace(False) + +objects = {} +# local import of dill._objects +#from . import _objects +#objects.update(_objects.succeeds) +#del _objects + +# local import of dill.objtypes +from . import objtypes as types + +def load_types(pickleable=True, unpickleable=True): + """load pickleable and/or unpickleable types to ``dill.types`` + + ``dill.types`` is meant to mimic the ``types`` module, providing a + registry of object types. By default, the module is empty (for import + speed purposes). Use the ``load_types`` function to load selected object + types to the ``dill.types`` module. + + Args: + pickleable (bool, default=True): if True, load pickleable types. + unpickleable (bool, default=True): if True, load unpickleable types. + + Returns: + None + """ + from importlib import reload + # local import of dill.objects + from . import _objects + if pickleable: + objects.update(_objects.succeeds) + else: + [objects.pop(obj,None) for obj in _objects.succeeds] + if unpickleable: + objects.update(_objects.failures) + else: + [objects.pop(obj,None) for obj in _objects.failures] + objects.update(_objects.registered) + del _objects + # reset contents of types to 'empty' + [types.__dict__.pop(obj) for obj in list(types.__dict__.keys()) \ + if obj.find('Type') != -1] + # add corresponding types from objects to types + reload(types) + +def extend(use_dill=True): + '''add (or remove) dill types to/from the pickle registry + + by default, ``dill`` populates its types to ``pickle.Pickler.dispatch``. + Thus, all ``dill`` types are available upon calling ``'import pickle'``. + To drop all ``dill`` types from the ``pickle`` dispatch, *use_dill=False*. + + Args: + use_dill (bool, default=True): if True, extend the dispatch table. + + Returns: + None + ''' + from ._dill import _revert_extension, _extend + if use_dill: _extend() + else: _revert_extension() + return + +extend() + + +def license(): + """print license""" + print (__license__) + return + +def citation(): + """print citation""" + print (__doc__[-491:-118]) + return + +# end of file diff --git a/.venv/lib/python3.10/site-packages/dill/_dill.py b/.venv/lib/python3.10/site-packages/dill/_dill.py new file mode 100644 index 0000000..aec297c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/_dill.py @@ -0,0 +1,2255 @@ +# -*- coding: utf-8 -*- +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2015 California Institute of Technology. +# Copyright (c) 2016-2025 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 . +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 +try: + from _thread import _ExceptHookArgs as ExceptHookArgsType +except ImportError: + ExceptHookArgsType = None +try: + from _thread import _ThreadHandle as ThreadHandleType +except ImportError: + ThreadHandleType = None +#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 + +IS_PYODIDE = sys.platform == 'emscripten' + +FileType = get_file_type('rb', buffering=0) +TextWrapperType = get_file_type('r', buffering=-1) +BufferedRandomType = None if IS_PYODIDE else 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 = None if IS_PYODIDE else 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 and not IS_PYPY: + 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,11,'p'), 'co_qualname', 'argcount posonlyargcount kwonlyargcount nlocals stacksize flags code consts names varnames filename name qualname firstlineno linetable 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 + # PyPy 3.11 7.3.19+ (17 members) + elif m.case(( + 'argcount', 'posonlyargcount', 'kwonlyargcount', 'nlocals', 'stacksize', 'flags', # args[0:6] + 'code', 'consts', 'names', 'varnames', 'filename', 'name', 'qualname', # args[6:13] + 'firstlineno', 'linetable', 'freevars', 'cellvars' # args[13:] + )): + if CODE_VERSION == (3,11,'p'): + 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], + args[16], + ) + 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] + +if ThreadHandleType: + def _create_thread_handle(ident, done, *args): #XXX: ignores 'blocking' + from threading import _make_thread_handle + handle = _make_thread_handle(ident) + if done: + handle._set_done() + return handle + +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 = {'':sys.__stdin__, '':sys.__stdout__, + '':sys.__stderr__} #XXX: better fileno=(0,1,2) ? + if name in list(names.keys()): + f = names[name] #XXX: safer "f=sys.stdin" + elif name == '': + f = os.tmpfile() + elif name == '': + 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 = '' # 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 == '': # 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] + submodule = getattr(__import__(module, None, None, [obj]), obj) + if isinstance(submodule, (ModuleType, type)): + return submodule + return __import__(import_name, None, None, [obj]) + else: + return __import__(import_name) + 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 == '': + 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])) + if sys.hexversion < 0x30e00a1: + pickler._batch_setitems(iter(source.items())) + else: + pickler._batch_setitems(iter(source.items()), obj=obj) + 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. `_. +# 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_qualname"): # pypy 3.11 7.3.19+ (17 args) + args = ( + obj.co_lnotab, 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_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. `_. + # 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 + +if ThreadHandleType: + @register(ThreadHandleType) + def save_thread_handle(pickler, obj): + logger.trace(pickler, "Th: %s", obj) + pickler.save_reduce(_create_thread_handle, (obj.ident, obj.is_done()), obj=obj) + logger.trace(pickler, "# Th") + return + +@register(LockType) #XXX: copied Thread will have new Event (due to new Lock) +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(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 BufferedRandomType: + @register(BufferedRandomType) + 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(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 + + if PyBufferedRandomType: + @register(PyBufferedRandomType) + 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. `_. +# 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 and hasattr(value, '__qualname__'): + 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 caes: NoneType, NotImplementedType, EllipsisType, EnumMeta, etc + 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") + elif obj is ExceptHookArgsType: #NOTE: must be after NoneType for pypy + logger.trace(pickler, "T7: %s", obj) + pickler.write(GLOBAL + b'threading\nExceptHookArgs\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) + #from _socket import CAPI as _testcapsule + _testcapsule_name = b'dill._dill._testcapsule' + _testcapsule = _PyCapsule_New( + ctypes.cast(_PyCapsule_New, ctypes.c_void_p), + ctypes.c_char_p(_testcapsule_name), + 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 diff --git a/.venv/lib/python3.10/site-packages/dill/_objects.py b/.venv/lib/python3.10/site-packages/dill/_objects.py new file mode 100644 index 0000000..a37cd79 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/_objects.py @@ -0,0 +1,541 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +all Python Standard Library objects (currently: CH 1-15 @ 2.7) +and some other common objects (i.e. numpy.ndarray) +""" + +__all__ = ['registered','failures','succeeds'] + +# helper imports +import warnings; warnings.filterwarnings("ignore", category=DeprecationWarning) +import sys +import queue as Queue +#import dbm as anydbm #XXX: delete foo +from io import BytesIO as StringIO +import re +import array +import collections +import codecs +import struct +import dataclasses +import datetime +import calendar +import weakref +import pprint +import decimal +import numbers +import functools +import itertools +import operator +import tempfile +import shelve +import zlib +import gzip +import zipfile +import tarfile +import csv +import hashlib +import hmac +import os +import logging +import logging.handlers +import optparse +#import __hello__ +import threading +import socket +import contextlib +try: + import bz2 + import sqlite3 + import dbm.ndbm as dbm + HAS_ALL = True +except ImportError: # Ubuntu + HAS_ALL = False +try: + #import curses + #from curses import textpad, panel + HAS_CURSES = True +except ImportError: # Windows + HAS_CURSES = False +try: + import ctypes + HAS_CTYPES = True + # if using `pypy`, pythonapi is not found + IS_PYPY = not hasattr(ctypes, 'pythonapi') +except ImportError: # MacPorts + HAS_CTYPES = False + IS_PYPY = False + +IS_PYODIDE = sys.platform == 'emscripten' + +# helper objects +class _class: + def _method(self): + pass +# @classmethod +# def _clsmethod(cls): #XXX: test me +# pass +# @staticmethod +# def _static(self): #XXX: test me +# pass +class _class2: + def __call__(self): + pass +_instance2 = _class2() +class _newclass(object): + def _method(self): + pass +# @classmethod +# def _clsmethod(cls): #XXX: test me +# pass +# @staticmethod +# def _static(self): #XXX: test me +# pass +class _newclass2(object): + __slots__ = ['descriptor'] +def _function(x): yield x +def _function2(): + try: raise + except Exception: + from sys import exc_info + e, er, tb = exc_info() + return er, tb +if HAS_CTYPES: + class _Struct(ctypes.Structure): + pass + _Struct._fields_ = [("_field", ctypes.c_int),("next", ctypes.POINTER(_Struct))] +_filedescrip, _tempfile = tempfile.mkstemp('r') # deleted in cleanup +if sys.hexversion < 0x30d00a1: + _tmpf = tempfile.TemporaryFile('w') # emits OSError 9 in python 3.13 +else: + _tmpf = tempfile.NamedTemporaryFile('w').file # for > python 3.9 + +# objects used by dill for type declaration +registered = d = {} +# objects dill fails to pickle +failures = x = {} +# all other type objects +succeeds = a = {} + +# types module (part of CH 8) +a['BooleanType'] = bool(1) +a['BuiltinFunctionType'] = len +a['BuiltinMethodType'] = a['BuiltinFunctionType'] +a['BytesType'] = _bytes = codecs.latin_1_encode('\x00')[0] # bytes(1) +a['ClassType'] = _class +a['ComplexType'] = complex(1) +a['DictType'] = _dict = {} +a['DictionaryType'] = a['DictType'] +a['FloatType'] = float(1) +a['FunctionType'] = _function +a['InstanceType'] = _instance = _class() +a['IntType'] = _int = int(1) +a['ListType'] = _list = [] +a['NoneType'] = None +a['ObjectType'] = object() +a['StringType'] = _str = str(1) +a['TupleType'] = _tuple = () +a['TypeType'] = type +a['LongType'] = _int +a['UnicodeType'] = _str +# built-in constants (CH 4) +a['CopyrightType'] = copyright +# built-in types (CH 5) +a['ClassObjectType'] = _newclass # +a['ClassInstanceType'] = _newclass() # +a['SetType'] = _set = set() +a['FrozenSetType'] = frozenset() +# built-in exceptions (CH 6) +a['ExceptionType'] = _exception = _function2()[0] +# string services (CH 7) +a['SREPatternType'] = _srepattern = re.compile('') +# data types (CH 8) +a['ArrayType'] = array.array("f") +a['DequeType'] = collections.deque([0]) +a['DefaultDictType'] = collections.defaultdict(_function, _dict) +a['TZInfoType'] = datetime.tzinfo() +a['DateTimeType'] = datetime.datetime.today() +a['CalendarType'] = calendar.Calendar() +# numeric and mathematical types (CH 9) +a['DecimalType'] = decimal.Decimal(1) +# data compression and archiving (CH 12) +a['TarInfoType'] = tarfile.TarInfo() +# generic operating system services (CH 15) +a['LoggerType'] = _logger = logging.getLogger() +a['FormatterType'] = logging.Formatter() # pickle ok +a['FilterType'] = logging.Filter() # pickle ok +a['LogRecordType'] = logging.makeLogRecord(_dict) # pickle ok +a['OptionParserType'] = _oparser = optparse.OptionParser() # pickle ok +a['OptionGroupType'] = optparse.OptionGroup(_oparser,"foo") # pickle ok +a['OptionType'] = optparse.Option('--foo') # pickle ok +if HAS_CTYPES: + z = x if IS_PYPY else a + z['CCharType'] = _cchar = ctypes.c_char() + z['CWCharType'] = ctypes.c_wchar() # fail == 2.6 + z['CByteType'] = ctypes.c_byte() + z['CUByteType'] = ctypes.c_ubyte() + z['CShortType'] = ctypes.c_short() + z['CUShortType'] = ctypes.c_ushort() + z['CIntType'] = ctypes.c_int() + z['CUIntType'] = ctypes.c_uint() + z['CLongType'] = ctypes.c_long() + z['CULongType'] = ctypes.c_ulong() + z['CLongLongType'] = ctypes.c_longlong() + z['CULongLongType'] = ctypes.c_ulonglong() + z['CFloatType'] = ctypes.c_float() + z['CDoubleType'] = ctypes.c_double() + z['CSizeTType'] = ctypes.c_size_t() + del z + a['CLibraryLoaderType'] = ctypes.cdll + a['StructureType'] = _Struct + # if not IS_PYPY: + # a['BigEndianStructureType'] = ctypes.BigEndianStructure() +#NOTE: also LittleEndianStructureType and UnionType... abstract classes +#NOTE: remember for ctypesobj.contents creates a new python object +#NOTE: ctypes.c_int._objects is memberdescriptor for object's __dict__ +#NOTE: base class of all ctypes data types is non-public _CData + +import fractions +import io +from io import StringIO as TextIO +# built-in functions (CH 2) +a['ByteArrayType'] = bytearray([1]) +# numeric and mathematical types (CH 9) +a['FractionType'] = fractions.Fraction() +a['NumberType'] = numbers.Number() +# generic operating system services (CH 15) +a['IOBaseType'] = io.IOBase() +a['RawIOBaseType'] = io.RawIOBase() +a['TextIOBaseType'] = io.TextIOBase() +a['BufferedIOBaseType'] = io.BufferedIOBase() +a['UnicodeIOType'] = TextIO() # the new StringIO +a['LoggerAdapterType'] = logging.LoggerAdapter(_logger,_dict) # pickle ok +if HAS_CTYPES: + z = x if IS_PYPY else a + z['CBoolType'] = ctypes.c_bool(1) + z['CLongDoubleType'] = ctypes.c_longdouble() + del z +import argparse +# data types (CH 8) +a['OrderedDictType'] = collections.OrderedDict(_dict) +a['CounterType'] = collections.Counter(_dict) +if HAS_CTYPES: + z = x if IS_PYPY else a + z['CSSizeTType'] = ctypes.c_ssize_t() + del z +# generic operating system services (CH 15) +a['NullHandlerType'] = logging.NullHandler() # pickle ok # new 2.7 +a['ArgParseFileType'] = argparse.FileType() # pickle ok + +# -- pickle fails on all below here ----------------------------------------- +# types module (part of CH 8) +a['CodeType'] = compile('','','exec') +a['DictProxyType'] = type.__dict__ +a['DictProxyType2'] = _newclass.__dict__ +a['EllipsisType'] = Ellipsis +a['ClosedFileType'] = open(os.devnull, 'wb', buffering=0).close() +a['GetSetDescriptorType'] = array.array.typecode +a['LambdaType'] = _lambda = lambda x: lambda y: x #XXX: works when not imported! +a['MemberDescriptorType'] = _newclass2.descriptor +if not IS_PYPY: + a['MemberDescriptorType2'] = datetime.timedelta.days +a['MethodType'] = _method = _class()._method #XXX: works when not imported! +a['ModuleType'] = datetime +a['NotImplementedType'] = NotImplemented +a['SliceType'] = slice(1) +a['UnboundMethodType'] = _class._method #XXX: works when not imported! +d['TextWrapperType'] = open(os.devnull, 'r') # same as mode='w','w+','r+' +if not IS_PYODIDE: + d['BufferedRandomType'] = open(os.devnull, 'r+b') # same as mode='w+b' +d['BufferedReaderType'] = open(os.devnull, 'rb') # (default: buffering=-1) +d['BufferedWriterType'] = open(os.devnull, 'wb') +try: # oddities: deprecated + from _pyio import open as _open + d['PyTextWrapperType'] = _open(os.devnull, 'r', buffering=-1) + if not IS_PYODIDE: + d['PyBufferedRandomType'] = _open(os.devnull, 'r+b', buffering=-1) + d['PyBufferedReaderType'] = _open(os.devnull, 'rb', buffering=-1) + d['PyBufferedWriterType'] = _open(os.devnull, 'wb', buffering=-1) +except ImportError: + pass +# other (concrete) object types +z = d if sys.hexversion < 0x30800a2 else a +z['CellType'] = (_lambda)(0).__closure__[0] +del z +a['XRangeType'] = _xrange = range(1) +a['MethodDescriptorType'] = type.__dict__['mro'] +a['WrapperDescriptorType'] = type.__repr__ +#a['WrapperDescriptorType2'] = type.__dict__['__module__']#XXX: GetSetDescriptor +a['ClassMethodDescriptorType'] = type.__dict__['__prepare__'] +# built-in functions (CH 2) +_methodwrap = (1).__lt__ +a['MethodWrapperType'] = _methodwrap +a['StaticMethodType'] = staticmethod(_method) +a['ClassMethodType'] = classmethod(_method) +a['PropertyType'] = property() +d['SuperType'] = super(Exception, _exception) +# string services (CH 7) +_in = _bytes +a['InputType'] = _cstrI = StringIO(_in) +a['OutputType'] = _cstrO = StringIO() +# data types (CH 8) +a['WeakKeyDictionaryType'] = weakref.WeakKeyDictionary() +a['WeakValueDictionaryType'] = weakref.WeakValueDictionary() +a['ReferenceType'] = weakref.ref(_instance) +a['DeadReferenceType'] = weakref.ref(_class()) +a['ProxyType'] = weakref.proxy(_instance) +a['DeadProxyType'] = weakref.proxy(_class()) +a['CallableProxyType'] = weakref.proxy(_instance2) +a['DeadCallableProxyType'] = weakref.proxy(_class2()) +a['QueueType'] = Queue.Queue() +# numeric and mathematical types (CH 9) +d['PartialType'] = functools.partial(int,base=2) +a['IzipType'] = zip('0','1') +d['ItemGetterType'] = operator.itemgetter(0) +d['AttrGetterType'] = operator.attrgetter('__repr__') +# file and directory access (CH 10) +_fileW = _cstrO +# data persistence (CH 11) +if HAS_ALL: + x['ConnectionType'] = _conn = sqlite3.connect(':memory:') + x['CursorType'] = _conn.cursor() +a['ShelveType'] = shelve.Shelf({}) +# data compression and archiving (CH 12) +if HAS_ALL: + x['BZ2FileType'] = bz2.BZ2File(os.devnull) + x['BZ2CompressorType'] = bz2.BZ2Compressor() + x['BZ2DecompressorType'] = bz2.BZ2Decompressor() +#x['ZipFileType'] = _zip = zipfile.ZipFile(os.devnull,'w') +#_zip.write(_tempfile,'x') [causes annoying warning/error printed on import] +#a['ZipInfoType'] = _zip.getinfo('x') +a['TarFileType'] = tarfile.open(fileobj=_fileW,mode='w') +# file formats (CH 13) +x['DialectType'] = csv.get_dialect('excel') +if sys.hexversion < 0x30d00a1: + import xdrlib + a['PackerType'] = xdrlib.Packer() +# optional operating system services (CH 16) +a['LockType'] = threading.Lock() +a['RLockType'] = threading.RLock() +# generic operating system services (CH 15) # also closed/open and r/w/etc... +a['NamedLoggerType'] = _logger = logging.getLogger(__name__) +#a['FrozenModuleType'] = __hello__ #FIXME: prints "Hello world..." +# interprocess communication (CH 17) +x['SocketType'] = _socket = socket.socket() +x['SocketPairType'] = socket.socketpair()[0] +# python runtime services (CH 27) +a['GeneratorContextManagerType'] = contextlib.contextmanager(max)([1]) + +try: # ipython + __IPYTHON__ is True # is ipython +except NameError: + # built-in constants (CH 4) + a['QuitterType'] = quit + d['ExitType'] = a['QuitterType'] +try: # numpy #FIXME: slow... 0.05 to 0.1 sec to import numpy + from numpy import ufunc as _numpy_ufunc + from numpy import array as _numpy_array + from numpy import int32 as _numpy_int32 + a['NumpyUfuncType'] = _numpy_ufunc + a['NumpyArrayType'] = _numpy_array + a['NumpyInt32Type'] = _numpy_int32 +except ImportError: + pass +# generic operating system services (CH 15) +a['FileHandlerType'] = logging.FileHandler(os.devnull) +a['RotatingFileHandlerType'] = logging.handlers.RotatingFileHandler(os.devnull) +a['SocketHandlerType'] = logging.handlers.SocketHandler('localhost',514) +a['MemoryHandlerType'] = logging.handlers.MemoryHandler(1) +# data types (CH 8) +a['WeakSetType'] = weakref.WeakSet() # 2.7 +# generic operating system services (CH 15) [errors when dill is imported] +#a['ArgumentParserType'] = _parser = argparse.ArgumentParser('PROG') +#a['NamespaceType'] = _parser.parse_args() # pickle ok +#a['SubParsersActionType'] = _parser.add_subparsers() +#a['MutuallyExclusiveGroupType'] = _parser.add_mutually_exclusive_group() +#a['ArgumentGroupType'] = _parser.add_argument_group() + +# -- dill fails in some versions below here --------------------------------- +# types module (part of CH 8) +d['FileType'] = open(os.devnull, 'rb', buffering=0) # same 'wb','wb+','rb+' +# built-in functions (CH 2) +# Iterators: +a['ListIteratorType'] = iter(_list) # empty vs non-empty +a['SetIteratorType'] = iter(_set) #XXX: empty vs non-empty #FIXME: list_iterator +a['TupleIteratorType']= iter(_tuple) # empty vs non-empty +a['XRangeIteratorType'] = iter(_xrange) # empty vs non-empty +a["BytesIteratorType"] = iter(b'') +a["BytearrayIteratorType"] = iter(bytearray(b'')) +z = x if IS_PYPY else a +z["CallableIteratorType"] = iter(iter, None) +del z +x["MemoryIteratorType"] = iter(memoryview(b'')) +a["ListReverseiteratorType"] = reversed([]) +X = a['OrderedDictType'] +d["OdictKeysType"] = X.keys() +d["OdictValuesType"] = X.values() +d["OdictItemsType"] = X.items() +a["OdictIteratorType"] = iter(X.keys()) #FIXME: list_iterator +del X +#FIXME: list_iterator +a['DictionaryItemIteratorType'] = iter(type.__dict__.items()) +a['DictionaryKeyIteratorType'] = iter(type.__dict__.keys()) +a['DictionaryValueIteratorType'] = iter(type.__dict__.values()) +if sys.hexversion >= 0x30800a0: + a["DictReversekeyiteratorType"] = reversed({}.keys()) + a["DictReversevalueiteratorType"] = reversed({}.values()) + a["DictReverseitemiteratorType"] = reversed({}.items()) + +try: + import symtable + #FIXME: fails to pickle + x["SymtableEntryType"] = symtable.symtable("", "string", "exec")._table +except ImportError: + pass + +if sys.hexversion >= 0x30a00a0 and not IS_PYPY: + x['LineIteratorType'] = compile('3', '', 'eval').co_lines() + +if sys.hexversion >= 0x30b00b0 and not IS_PYPY: + from types import GenericAlias + d["GenericAliasIteratorType"] = iter(GenericAlias(list, (int,))) + x['PositionsIteratorType'] = compile('3', '', 'eval').co_positions() + +# data types (CH 8) +a['PrettyPrinterType'] = pprint.PrettyPrinter() +# file and directory access (CH 10) +a['TemporaryFileType'] = _tmpf +# data compression and archiving (CH 12) +x['GzipFileType'] = gzip.GzipFile(fileobj=_fileW) +# generic operating system services (CH 15) +a['StreamHandlerType'] = logging.StreamHandler() +# numeric and mathematical types (CH 9) +z = a if sys.hexversion < 0x30e00a1 else x +z['CountType'] = itertools.count(0) #FIXME: __reduce__ removed in 3.14.0a1 +z['ChainType'] = itertools.chain('0','1') +z['ProductType'] = itertools.product('0','1') +z['CycleType'] = itertools.cycle('0') +z['PermutationsType'] = itertools.permutations('0') +z['CombinationsType'] = itertools.combinations('0',1) +z['RepeatType'] = itertools.repeat(0) +z['CompressType'] = itertools.compress('0',[1]) +del z +#XXX: ...and etc + +# -- dill fails on all below here ------------------------------------------- +# types module (part of CH 8) +x['GeneratorType'] = _generator = _function(1) #XXX: priority +x['FrameType'] = _generator.gi_frame #XXX: inspect.currentframe() +x['TracebackType'] = _function2()[1] #(see: inspect.getouterframes,getframeinfo) +# other (concrete) object types +# (also: Capsule / CObject ?) +# built-in functions (CH 2) +# built-in types (CH 5) +# string services (CH 7) +x['StructType'] = struct.Struct('c') +x['CallableIteratorType'] = _srepattern.finditer('') +x['SREMatchType'] = _srepattern.match('') +x['SREScannerType'] = _srepattern.scanner('') +x['StreamReader'] = codecs.StreamReader(_cstrI) #XXX: ... and etc +# python object persistence (CH 11) +# x['DbShelveType'] = shelve.open('foo','n')#,protocol=2) #XXX: delete foo +if HAS_ALL: + z = a if IS_PYPY else x + z['DbmType'] = dbm.open(_tempfile,'n') + del z +# x['DbCursorType'] = _dbcursor = anydbm.open('foo','n') #XXX: delete foo +# x['DbType'] = _dbcursor.db +# data compression and archiving (CH 12) +x['ZlibCompressType'] = zlib.compressobj() +x['ZlibDecompressType'] = zlib.decompressobj() +# file formats (CH 13) +x['CSVReaderType'] = csv.reader(_cstrI) +x['CSVWriterType'] = csv.writer(_cstrO) +x['CSVDictReaderType'] = csv.DictReader(_cstrI) +x['CSVDictWriterType'] = csv.DictWriter(_cstrO,{}) +# cryptographic services (CH 14) +x['HashType'] = hashlib.md5() +if (sys.hexversion < 0x30800a1): + x['HMACType'] = hmac.new(_in) +else: + x['HMACType'] = hmac.new(_in, digestmod='md5') +# generic operating system services (CH 15) +if HAS_CURSES: pass + #x['CursesWindowType'] = _curwin = curses.initscr() #FIXME: messes up tty + #x['CursesTextPadType'] = textpad.Textbox(_curwin) + #x['CursesPanelType'] = panel.new_panel(_curwin) +if HAS_CTYPES: + x['CCharPType'] = ctypes.c_char_p() + x['CWCharPType'] = ctypes.c_wchar_p() + x['CVoidPType'] = ctypes.c_void_p() + if sys.platform[:3] == 'win': + x['CDLLType'] = _cdll = ctypes.cdll.msvcrt + else: + x['CDLLType'] = _cdll = ctypes.CDLL(None) + if not IS_PYPY: + x['PyDLLType'] = _pydll = ctypes.pythonapi + x['FuncPtrType'] = _cdll._FuncPtr() + x['CCharArrayType'] = ctypes.create_string_buffer(1) + x['CWCharArrayType'] = ctypes.create_unicode_buffer(1) + x['CParamType'] = ctypes.byref(_cchar) + x['LPCCharType'] = ctypes.pointer(_cchar) + x['LPCCharObjType'] = _lpchar = ctypes.POINTER(ctypes.c_char) + x['NullPtrType'] = _lpchar() + x['NullPyObjectType'] = ctypes.py_object() + x['PyObjectType'] = ctypes.py_object(lambda :None) + z = a if IS_PYPY else x + z['FieldType'] = _field = _Struct._field + z['CFUNCTYPEType'] = _cfunc = ctypes.CFUNCTYPE(ctypes.c_char) + if sys.hexversion < 0x30c00b3: + x['CFunctionType'] = _cfunc(str) + del z +# numeric and mathematical types (CH 9) +a['MethodCallerType'] = operator.methodcaller('mro') # 2.6 +# built-in types (CH 5) +x['MemoryType'] = memoryview(_in) # 2.7 +x['MemoryType2'] = memoryview(bytearray(_in)) # 2.7 +d['DictItemsType'] = _dict.items() # 2.7 +d['DictKeysType'] = _dict.keys() # 2.7 +d['DictValuesType'] = _dict.values() # 2.7 +# generic operating system services (CH 15) +a['RawTextHelpFormatterType'] = argparse.RawTextHelpFormatter('PROG') +a['RawDescriptionHelpFormatterType'] = argparse.RawDescriptionHelpFormatter('PROG') +a['ArgDefaultsHelpFormatterType'] = argparse.ArgumentDefaultsHelpFormatter('PROG') +z = a if IS_PYPY else x +z['CmpKeyType'] = _cmpkey = functools.cmp_to_key(_methodwrap) # 2.7, >=3.2 +z['CmpKeyObjType'] = _cmpkey('0') #2.7, >=3.2 +del z +# oddities: removed, etc +x['BufferType'] = x['MemoryType'] + +from dill._dill import _testcapsule +if _testcapsule is not None: + d['PyCapsuleType'] = _testcapsule +del _testcapsule + +if hasattr(dataclasses, '_HAS_DEFAULT_FACTORY'): + a['DataclassesHasDefaultFactoryType'] = dataclasses._HAS_DEFAULT_FACTORY + +if hasattr(dataclasses, 'MISSING'): + a['DataclassesMissingType'] = dataclasses.MISSING + +if hasattr(dataclasses, 'KW_ONLY'): + a['DataclassesKWOnlyType'] = dataclasses.KW_ONLY + +if hasattr(dataclasses, '_FIELD_BASE'): + a['DataclassesFieldBaseType'] = dataclasses._FIELD + +# -- cleanup ---------------------------------------------------------------- +a.update(d) # registered also succeed +if sys.platform[:3] == 'win': + os.close(_filedescrip) # required on win32 +os.remove(_tempfile) + + +# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/_shims.py b/.venv/lib/python3.10/site-packages/dill/_shims.py new file mode 100644 index 0000000..bb0a9dc --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/_shims.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) +# Copyright (c) 2021-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +Provides shims for compatibility between versions of dill and Python. + +Compatibility shims should be provided in this file. Here are two simple example +use cases. + +Deprecation of constructor function: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Assume that we were transitioning _import_module in _dill.py to +the builtin function importlib.import_module when present. + +@move_to(_dill) +def _import_module(import_name): + ... # code already in _dill.py + +_import_module = Getattr(importlib, 'import_module', Getattr(_dill, '_import_module', None)) + +The code will attempt to find import_module in the importlib module. If not +present, it will use the _import_module function in _dill. + +Emulate new Python behavior in older Python versions: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +CellType.cell_contents behaves differently in Python 3.6 and 3.7. It is +read-only in Python 3.6 and writable and deletable in 3.7. + +if _dill.OLD37 and _dill.HAS_CTYPES and ...: + @move_to(_dill) + def _setattr(object, name, value): + if type(object) is _dill.CellType and name == 'cell_contents': + _PyCell_Set.argtypes = (ctypes.py_object, ctypes.py_object) + _PyCell_Set(object, value) + else: + setattr(object, name, value) +... # more cases below + +_setattr = Getattr(_dill, '_setattr', setattr) + +_dill._setattr will be used when present to emulate Python 3.7 functionality in +older versions of Python while defaulting to the standard setattr in 3.7+. + +See this PR for the discussion that lead to this system: +https://github.com/uqfoundation/dill/pull/443 +""" + +import inspect +import sys + +_dill = sys.modules['dill._dill'] + + +class Reduce(object): + """ + Reduce objects are wrappers used for compatibility enforcement during + unpickle-time. They should only be used in calls to pickler.save and + other Reduce objects. They are only evaluated within unpickler.load. + + Pickling a Reduce object makes the two implementations equivalent: + + pickler.save(Reduce(*reduction)) + + pickler.save_reduce(*reduction, obj=reduction) + """ + __slots__ = ['reduction'] + def __new__(cls, *reduction, **kwargs): + """ + Args: + *reduction: a tuple that matches the format given here: + https://docs.python.org/3/library/pickle.html#object.__reduce__ + is_callable: a bool to indicate that the object created by + unpickling `reduction` is callable. If true, the current Reduce + is allowed to be used as the function in further save_reduce calls + or Reduce objects. + """ + is_callable = kwargs.get('is_callable', False) # Pleases Py2. Can be removed later + if is_callable: + self = object.__new__(_CallableReduce) + else: + self = object.__new__(Reduce) + self.reduction = reduction + return self + def __repr__(self): + return 'Reduce%s' % (self.reduction,) + def __copy__(self): + return self # pragma: no cover + def __deepcopy__(self, memo): + return self # pragma: no cover + def __reduce__(self): + return self.reduction + def __reduce_ex__(self, protocol): + return self.__reduce__() + +class _CallableReduce(Reduce): + # A version of Reduce for functions. Used to trick pickler.save_reduce into + # thinking that Reduce objects of functions are themselves meaningful functions. + def __call__(self, *args, **kwargs): + reduction = self.__reduce__() + func = reduction[0] + f_args = reduction[1] + obj = func(*f_args) + return obj(*args, **kwargs) + +__NO_DEFAULT = _dill.Sentinel('Getattr.NO_DEFAULT') + +def Getattr(object, name, default=__NO_DEFAULT): + """ + A Reduce object that represents the getattr operation. When unpickled, the + Getattr will access an attribute 'name' of 'object' and return the value + stored there. If the attribute doesn't exist, the default value will be + returned if present. + + The following statements are equivalent: + + Getattr(collections, 'OrderedDict') + Getattr(collections, 'spam', None) + Getattr(*args) + + Reduce(getattr, (collections, 'OrderedDict')) + Reduce(getattr, (collections, 'spam', None)) + Reduce(getattr, args) + + During unpickling, the first two will result in collections.OrderedDict and + None respectively because the first attribute exists and the second one does + not, forcing it to use the default value given in the third argument. + """ + + if default is Getattr.NO_DEFAULT: + reduction = (getattr, (object, name)) + else: + reduction = (getattr, (object, name, default)) + + return Reduce(*reduction, is_callable=callable(default)) + +Getattr.NO_DEFAULT = __NO_DEFAULT +del __NO_DEFAULT + +def move_to(module, name=None): + def decorator(func): + if name is None: + fname = func.__name__ + else: + fname = name + module.__dict__[fname] = func + func.__module__ = module.__name__ + return func + return decorator + +def register_shim(name, default): + """ + A easier to understand and more compact way of "softly" defining a function. + These two pieces of code are equivalent: + + if _dill.OLD3X: + def _create_class(): + ... + _create_class = register_shim('_create_class', types.new_class) + + if _dill.OLD3X: + @move_to(_dill) + def _create_class(): + ... + _create_class = Getattr(_dill, '_create_class', types.new_class) + + Intuitively, it creates a function or object in the versions of dill/python + that require special reimplementations, and use a core library or default + implementation if that function or object does not exist. + """ + func = globals().get(name) + if func is not None: + _dill.__dict__[name] = func + func.__module__ = _dill.__name__ + + if default is Getattr.NO_DEFAULT: + reduction = (getattr, (_dill, name)) + else: + reduction = (getattr, (_dill, name, default)) + + return Reduce(*reduction, is_callable=callable(default)) + +###################### +## Compatibility Shims are defined below +###################### + +_CELL_EMPTY = register_shim('_CELL_EMPTY', None) + +_setattr = register_shim('_setattr', setattr) +_delattr = register_shim('_delattr', delattr) diff --git a/.venv/lib/python3.10/site-packages/dill/detect.py b/.venv/lib/python3.10/site-packages/dill/detect.py new file mode 100644 index 0000000..2f0bea1 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/detect.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +Methods for detecting objects leading to pickling failures. +""" + +import dis +from inspect import ismethod, isfunction, istraceback, isframe, iscode + +from .pointers import parent, reference, at, parents, children +from .logger import trace + +__all__ = ['baditems','badobjects','badtypes','code','errors','freevars', + 'getmodule','globalvars','nestedcode','nestedglobals','outermost', + 'referredglobals','referrednested','trace','varnames'] + +def getmodule(object, _filename=None, force=False): + """get the module of the object""" + from inspect import getmodule as getmod + module = getmod(object, _filename) + if module or not force: return module + import builtins + from .source import getname + name = getname(object, force=True) + return builtins if name in vars(builtins).keys() else None + +def outermost(func): # is analogous to getsource(func,enclosing=True) + """get outermost enclosing object (i.e. the outer function in a closure) + + NOTE: this is the object-equivalent of getsource(func, enclosing=True) + """ + if ismethod(func): + _globals = func.__func__.__globals__ or {} + elif isfunction(func): + _globals = func.__globals__ or {} + else: + return #XXX: or raise? no matches + _globals = _globals.items() + # get the enclosing source + from .source import getsourcelines + try: lines,lnum = getsourcelines(func, enclosing=True) + except Exception: #TypeError, IOError + lines,lnum = [],None + code = ''.join(lines) + # get all possible names,objects that are named in the enclosing source + _locals = ((name,obj) for (name,obj) in _globals if name in code) + # now only save the objects that generate the enclosing block + for name,obj in _locals: #XXX: don't really need 'name' + try: + if getsourcelines(obj) == (lines,lnum): return obj + except Exception: #TypeError, IOError + pass + return #XXX: or raise? no matches + +def nestedcode(func, recurse=True): #XXX: or return dict of {co_name: co} ? + """get the code objects for any nested functions (e.g. in a closure)""" + func = code(func) + if not iscode(func): return [] #XXX: or raise? no matches + nested = set() + for co in func.co_consts: + if co is None: continue + co = code(co) + if co: + nested.add(co) + if recurse: nested |= set(nestedcode(co, recurse=True)) + return list(nested) + +def code(func): + """get the code object for the given function or method + + NOTE: use dill.source.getsource(CODEOBJ) to get the source code + """ + if ismethod(func): func = func.__func__ + if isfunction(func): func = func.__code__ + if istraceback(func): func = func.tb_frame + if isframe(func): func = func.f_code + if iscode(func): return func + return + +#XXX: ugly: parse dis.dis for name after " len(referrednested(func)), try calling func(). + If possible, python builds code objects, but delays building functions + until func() is called. + """ + import gc + funcs = set() + # get the code objects, and try to track down by referrence + for co in nestedcode(func, recurse): + # look for function objects that refer to the code object + for obj in gc.get_referrers(co): + # get methods + _ = getattr(obj, '__func__', None) # ismethod + if getattr(_, '__code__', None) is co: funcs.add(obj) + # get functions + elif getattr(obj, '__code__', None) is co: funcs.add(obj) + # get frame objects + elif getattr(obj, 'f_code', None) is co: funcs.add(obj) + # get code objects + elif hasattr(obj, 'co_code') and obj is co: funcs.add(obj) +# frameobjs => func.__code__.co_varnames not in func.__code__.co_cellvars +# funcobjs => func.__code__.co_cellvars not in func.__code__.co_varnames +# frameobjs are not found, however funcobjs are... +# (see: test_mixins.quad ... and test_mixins.wtf) +# after execution, code objects get compiled, and then may be found by gc + return list(funcs) + + +def freevars(func): + """get objects defined in enclosing code that are referred to by func + + returns a dict of {name:object}""" + if ismethod(func): func = func.__func__ + if isfunction(func): + closures = func.__closure__ or () + func = func.__code__.co_freevars # get freevars + else: + return {} + + def get_cell_contents(): + for name, c in zip(func, closures): + try: + cell_contents = c.cell_contents + except ValueError: # cell is empty + continue + yield name, c.cell_contents + + return dict(get_cell_contents()) + +# thanks to Davies Liu for recursion of globals +def nestedglobals(func, recurse=True): + """get the names of any globals found within func""" + func = code(func) + if func is None: return list() + import sys + from .temp import capture + CAN_NULL = sys.hexversion >= 0x30b00a7 # NULL may be prepended >= 3.11a7 + names = set() + with capture('stdout') as out: + try: + dis.dis(func) #XXX: dis.dis(None) disassembles last traceback + except IndexError: + pass #FIXME: HACK for IS_PYPY (3.11) + for line in out.getvalue().splitlines(): + if '_GLOBAL' in line: + name = line.split('(')[-1].split(')')[0] + if CAN_NULL: + names.add(name.replace('NULL + ', '').replace(' + NULL', '')) + else: + names.add(name) + for co in getattr(func, 'co_consts', tuple()): + if co and recurse and iscode(co): + names.update(nestedglobals(co, recurse=True)) + return list(names) + +def referredglobals(func, recurse=True, builtin=False): + """get the names of objects in the global scope referred to by func""" + return globalvars(func, recurse, builtin).keys() + +def globalvars(func, recurse=True, builtin=False): + """get objects defined in global scope that are referred to by func + + return a dict of {name:object}""" + if ismethod(func): func = func.__func__ + if isfunction(func): + globs = vars(getmodule(sum)).copy() if builtin else {} + # get references from within closure + orig_func, func = func, set() + for obj in orig_func.__closure__ or {}: + try: + cell_contents = obj.cell_contents + except ValueError: # cell is empty + pass + else: + _vars = globalvars(cell_contents, recurse, builtin) or {} + func.update(_vars) #XXX: (above) be wary of infinte recursion? + globs.update(_vars) + # get globals + globs.update(orig_func.__globals__ or {}) + # get names of references + if not recurse: + func.update(orig_func.__code__.co_names) + else: + func.update(nestedglobals(orig_func.__code__)) + # find globals for all entries of func + for key in func.copy(): #XXX: unnecessary...? + nested_func = globs.get(key) + if nested_func is orig_func: + #func.remove(key) if key in func else None + continue #XXX: globalvars(func, False)? + func.update(globalvars(nested_func, True, builtin)) + elif iscode(func): + globs = vars(getmodule(sum)).copy() if builtin else {} + #globs.update(globals()) + if not recurse: + func = func.co_names # get names + else: + orig_func = func.co_name # to stop infinite recursion + func = set(nestedglobals(func)) + # find globals for all entries of func + for key in func.copy(): #XXX: unnecessary...? + if key is orig_func: + #func.remove(key) if key in func else None + continue #XXX: globalvars(func, False)? + nested_func = globs.get(key) + func.update(globalvars(nested_func, True, builtin)) + else: + return {} + #NOTE: if name not in __globals__, then we skip it... + return dict((name,globs[name]) for name in func if name in globs) + + +def varnames(func): + """get names of variables defined by func + + returns a tuple (local vars, local vars referrenced by nested functions)""" + func = code(func) + if not iscode(func): + return () #XXX: better ((),())? or None? + return func.co_varnames, func.co_cellvars + + +def baditems(obj, exact=False, safe=False): #XXX: obj=globals() ? + """get items in object that fail to pickle""" + if not hasattr(obj,'__iter__'): # is not iterable + return [j for j in (badobjects(obj,0,exact,safe),) if j is not None] + obj = obj.values() if getattr(obj,'values',None) else obj + _obj = [] # can't use a set, as items may be unhashable + [_obj.append(badobjects(i,0,exact,safe)) for i in obj if i not in _obj] + return [j for j in _obj if j is not None] + + +def badobjects(obj, depth=0, exact=False, safe=False): + """get objects that fail to pickle""" + from dill import pickles + if not depth: + if pickles(obj,exact,safe): return None + return obj + return dict(((attr, badobjects(getattr(obj,attr),depth-1,exact,safe)) \ + for attr in dir(obj) if not pickles(getattr(obj,attr),exact,safe))) + +def badtypes(obj, depth=0, exact=False, safe=False): + """get types for objects that fail to pickle""" + from dill import pickles + if not depth: + if pickles(obj,exact,safe): return None + return type(obj) + return dict(((attr, badtypes(getattr(obj,attr),depth-1,exact,safe)) \ + for attr in dir(obj) if not pickles(getattr(obj,attr),exact,safe))) + +def errors(obj, depth=0, exact=False, safe=False): + """get errors for objects that fail to pickle""" + from dill import pickles, copy + if not depth: + try: + pik = copy(obj) + if exact: + assert pik == obj, \ + "Unpickling produces %s instead of %s" % (pik,obj) + assert type(pik) == type(obj), \ + "Unpickling produces %s instead of %s" % (type(pik),type(obj)) + return None + except Exception: + import sys + return sys.exc_info()[1] + _dict = {} + for attr in dir(obj): + try: + _attr = getattr(obj,attr) + except Exception: + import sys + _dict[attr] = sys.exc_info()[1] + continue + if not pickles(_attr,exact,safe): + _dict[attr] = errors(_attr,depth-1,exact,safe) + return _dict + + +# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/logger.py b/.venv/lib/python3.10/site-packages/dill/logger.py new file mode 100644 index 0000000..435f82b --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/logger.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Author: Leonardo Gama (@leogama) +# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +Logging utilities for dill. + +The 'logger' object is dill's top-level logger. + +The 'adapter' object wraps the logger and implements a 'trace()' method that +generates a detailed tree-style trace for the pickling call at log level INFO. + +The 'trace()' function sets and resets dill's logger log level, enabling and +disabling the pickling trace. + +The trace shows a tree structure depicting the depth of each object serialized +*with dill save functions*, but not the ones that use save functions from +'pickle._Pickler.dispatch'. If the information is available, it also displays +the size in bytes that the object contributed to the pickle stream (including +its child objects). Sample trace output: + + >>> import dill, dill.tests + >>> dill.detect.trace(True) + >>> dill.dump_session(main=dill.tests) + ┬ M1: + ├┬ F2: + │└ # F2 [32 B] + ├┬ D2: + │├┬ T4: + ││└ # T4 [35 B] + │├┬ D2: + ││├┬ T4: + │││└ # T4 [50 B] + ││├┬ D2: + │││└ # D2 [84 B] + ││└ # D2 [413 B] + │└ # D2 [763 B] + └ # M1 [813 B] +""" + +__all__ = ['adapter', 'logger', 'trace'] + +import codecs +import contextlib +import locale +import logging +import math +import os +from functools import partial +from typing import TextIO, Union + +import dill + +# Tree drawing characters: Unicode to ASCII map. +ASCII_MAP = str.maketrans({"│": "|", "├": "|", "┬": "+", "└": "`"}) + +## Notes about the design choices ## + +# Here is some domumentation of the Standard Library's logging internals that +# can't be found completely in the official documentation. dill's logger is +# obtained by calling logging.getLogger('dill') and therefore is an instance of +# logging.getLoggerClass() at the call time. As this is controlled by the user, +# in order to add some functionality to it it's necessary to use a LoggerAdapter +# to wrap it, overriding some of the adapter's methods and creating new ones. +# +# Basic calling sequence +# ====================== +# +# Python's logging functionality can be conceptually divided into five steps: +# 0. Check logging level -> abort if call level is greater than logger level +# 1. Gather information -> construct a LogRecord from passed arguments and context +# 2. Filter (optional) -> discard message if the record matches a filter +# 3. Format -> format message with args, then format output string with message plus record +# 4. Handle -> write the formatted string to output as defined in the handler +# +# dill.logging.logger.log -> # or logger.info, etc. +# Logger.log -> \ +# Logger._log -> }- accept 'extra' parameter for custom record entries +# Logger.makeRecord -> / +# LogRecord.__init__ +# Logger.handle -> +# Logger.callHandlers -> +# Handler.handle -> +# Filterer.filter -> +# Filter.filter +# StreamHandler.emit -> +# Handler.format -> +# Formatter.format -> +# LogRecord.getMessage # does: record.message = msg % args +# Formatter.formatMessage -> +# PercentStyle.format # does: self._fmt % vars(record) +# +# NOTE: All methods from the second line on are from logging.__init__.py + +class TraceAdapter(logging.LoggerAdapter): + """ + Tracks object tree depth and calculates pickled object size. + + A single instance of this wraps the module's logger, as the logging API + doesn't allow setting it directly with a custom Logger subclass. The added + 'trace()' method receives a pickle instance as the first argument and + creates extra values to be added in the LogRecord from it, then calls + 'info()'. + + Usage of logger with 'trace()' method: + + >>> from dill.logger import adapter as logger #NOTE: not dill.logger.logger + >>> ... + >>> def save_atype(pickler, obj): + >>> logger.trace(pickler, "Message with %s and %r etc. placeholders", 'text', obj) + >>> ... + """ + def __init__(self, logger): + self.logger = logger + def addHandler(self, handler): + formatter = TraceFormatter("%(prefix)s%(message)s%(suffix)s", handler=handler) + handler.setFormatter(formatter) + self.logger.addHandler(handler) + def removeHandler(self, handler): + self.logger.removeHandler(handler) + def process(self, msg, kwargs): + # A no-op override, as we don't have self.extra. + return msg, kwargs + def trace_setup(self, pickler): + # Called by Pickler.dump(). + if not dill._dill.is_dill(pickler, child=False): + return + if self.isEnabledFor(logging.INFO): + pickler._trace_depth = 1 + pickler._size_stack = [] + else: + pickler._trace_depth = None + def trace(self, pickler, msg, *args, **kwargs): + if not hasattr(pickler, '_trace_depth'): + logger.info(msg, *args, **kwargs) + return + if pickler._trace_depth is None: + return + extra = kwargs.get('extra', {}) + pushed_obj = msg.startswith('#') + size = None + try: + # Streams are not required to be tellable. + size = pickler._file.tell() + frame = pickler.framer.current_frame + try: + size += frame.tell() + except AttributeError: + # PyPy may use a BytesBuilder as frame + size += len(frame) + except (AttributeError, TypeError): + pass + if size is not None: + if not pushed_obj: + pickler._size_stack.append(size) + else: + size -= pickler._size_stack.pop() + extra['size'] = size + if pushed_obj: + pickler._trace_depth -= 1 + extra['depth'] = pickler._trace_depth + kwargs['extra'] = extra + self.info(msg, *args, **kwargs) + if not pushed_obj: + pickler._trace_depth += 1 + +class TraceFormatter(logging.Formatter): + """ + Generates message prefix and suffix from record. + + This Formatter adds prefix and suffix strings to the log message in trace + mode (an also provides empty string defaults for normal logs). + """ + def __init__(self, *args, handler=None, **kwargs): + super().__init__(*args, **kwargs) + try: + encoding = handler.stream.encoding + if encoding is None: + raise AttributeError + except AttributeError: + encoding = locale.getpreferredencoding() + try: + encoding = codecs.lookup(encoding).name + except LookupError: + self.is_utf8 = False + else: + self.is_utf8 = (encoding == codecs.lookup('utf-8').name) + def format(self, record): + fields = {'prefix': "", 'suffix': ""} + if getattr(record, 'depth', 0) > 0: + if record.msg.startswith("#"): + prefix = (record.depth - 1)*"│" + "└" + elif record.depth == 1: + prefix = "┬" + else: + prefix = (record.depth - 2)*"│" + "├┬" + if not self.is_utf8: + prefix = prefix.translate(ASCII_MAP) + "-" + fields['prefix'] = prefix + " " + if hasattr(record, 'size') and record.size is not None and record.size >= 1: + # Show object size in human-readable form. + power = int(math.log(record.size, 2)) // 10 + size = record.size >> power*10 + fields['suffix'] = " [%d %sB]" % (size, "KMGTP"[power] + "i" if power else "") + vars(record).update(fields) + return super().format(record) + +logger = logging.getLogger('dill') +logger.propagate = False +adapter = TraceAdapter(logger) +stderr_handler = logging._StderrHandler() +adapter.addHandler(stderr_handler) + +def trace(arg: Union[bool, TextIO, str, os.PathLike] = None, *, mode: str = 'a') -> None: + """print a trace through the stack when pickling; useful for debugging + + With a single boolean argument, enable or disable the tracing. + + Example usage: + + >>> import dill + >>> dill.detect.trace(True) + >>> dill.dump_session() + + Alternatively, ``trace()`` can be used as a context manager. With no + arguments, it just takes care of restoring the tracing state on exit. + Either a file handle, or a file name and (optionally) a file mode may be + specitfied to redirect the tracing output in the ``with`` block context. A + log function is yielded by the manager so the user can write extra + information to the file. + + Example usage: + + >>> from dill import detect + >>> D = {'a': 42, 'b': {'x': None}} + >>> with detect.trace(): + >>> dumps(D) + ┬ D2: + ├┬ D2: + │└ # D2 [8 B] + └ # D2 [22 B] + >>> squared = lambda x: x**2 + >>> with detect.trace('output.txt', mode='w') as log: + >>> log("> D = %r", D) + >>> dumps(D) + >>> log("> squared = %r", squared) + >>> dumps(squared) + + Arguments: + arg: a boolean value, or an optional file-like or path-like object for the context manager + mode: mode string for ``open()`` if a file name is passed as the first argument + """ + if repr(arg) not in ('False', 'True'): + return TraceManager(file=arg, mode=mode) + logger.setLevel(logging.INFO if arg else logging.WARNING) + +class TraceManager(contextlib.AbstractContextManager): + """context manager version of trace(); can redirect the trace to a file""" + def __init__(self, file, mode): + self.file = file + self.mode = mode + self.redirect = file is not None + self.file_is_stream = hasattr(file, 'write') + def __enter__(self): + if self.redirect: + stderr_handler.flush() + if self.file_is_stream: + self.handler = logging.StreamHandler(self.file) + else: + self.handler = logging.FileHandler(self.file, self.mode) + adapter.removeHandler(stderr_handler) + adapter.addHandler(self.handler) + self.old_level = adapter.getEffectiveLevel() + adapter.setLevel(logging.INFO) + return adapter.info + def __exit__(self, *exc_info): + adapter.setLevel(self.old_level) + if self.redirect: + adapter.removeHandler(self.handler) + adapter.addHandler(stderr_handler) + if not self.file_is_stream: + self.handler.close() diff --git a/.venv/lib/python3.10/site-packages/dill/objtypes.py b/.venv/lib/python3.10/site-packages/dill/objtypes.py new file mode 100644 index 0000000..0453fe3 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/objtypes.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +all Python Standard Library object types (currently: CH 1-15 @ 2.7) +and some other common object types (i.e. numpy.ndarray) + +to load more objects and types, use dill.load_types() +""" + +# non-local import of dill.objects +from dill import objects +for _type in objects.keys(): + exec("%s = type(objects['%s'])" % (_type,_type)) + +del objects +try: + del _type +except NameError: + pass diff --git a/.venv/lib/python3.10/site-packages/dill/pointers.py b/.venv/lib/python3.10/site-packages/dill/pointers.py new file mode 100644 index 0000000..d3e2e31 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/pointers.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +__all__ = ['parent', 'reference', 'at', 'parents', 'children'] + +import gc +import sys + +from ._dill import _proxy_helper as reference +from ._dill import _locate_object as at + +def parent(obj, objtype, ignore=()): + """ +>>> listiter = iter([4,5,6,7]) +>>> obj = parent(listiter, list) +>>> obj == [4,5,6,7] # actually 'is', but don't have handle any longer +True + +NOTE: objtype can be a single type (e.g. int or list) or a tuple of types. + +WARNING: if obj is a sequence (e.g. list), may produce unexpected results. +Parent finds *one* parent (e.g. the last member of the sequence). + """ + depth = 1 #XXX: always looking for the parent (only, right?) + chain = parents(obj, objtype, depth, ignore) + parent = chain.pop() + if parent is obj: + return None + return parent + + +def parents(obj, objtype, depth=1, ignore=()): #XXX: objtype=object ? + """Find the chain of referents for obj. Chain will end with obj. + + objtype: an object type or tuple of types to search for + depth: search depth (e.g. depth=2 is 'grandparents') + ignore: an object or tuple of objects to ignore in the search + """ + edge_func = gc.get_referents # looking for refs, not back_refs + predicate = lambda x: isinstance(x, objtype) # looking for parent type + #if objtype is None: predicate = lambda x: True #XXX: in obj.mro() ? + ignore = (ignore,) if not hasattr(ignore, '__len__') else ignore + ignore = (id(obj) for obj in ignore) + chain = find_chain(obj, predicate, edge_func, depth)[::-1] + #XXX: should pop off obj... ? + return chain + + +def children(obj, objtype, depth=1, ignore=()): #XXX: objtype=object ? + """Find the chain of referrers for obj. Chain will start with obj. + + objtype: an object type or tuple of types to search for + depth: search depth (e.g. depth=2 is 'grandchildren') + ignore: an object or tuple of objects to ignore in the search + + NOTE: a common thing to ignore is all globals, 'ignore=(globals(),)' + + NOTE: repeated calls may yield different results, as python stores + the last value in the special variable '_'; thus, it is often good + to execute something to replace '_' (e.g. >>> 1+1). + """ + edge_func = gc.get_referrers # looking for back_refs, not refs + predicate = lambda x: isinstance(x, objtype) # looking for child type + #if objtype is None: predicate = lambda x: True #XXX: in obj.mro() ? + ignore = (ignore,) if not hasattr(ignore, '__len__') else ignore + ignore = (id(obj) for obj in ignore) + chain = find_chain(obj, predicate, edge_func, depth, ignore) + #XXX: should pop off obj... ? + return chain + + +# more generic helper function (cut-n-paste from objgraph) +# Source at http://mg.pov.lt/objgraph/ +# Copyright (c) 2008-2010 Marius Gedminas +# Copyright (c) 2010 Stefano Rivera +# Released under the MIT licence (see objgraph/objgrah.py) + +def find_chain(obj, predicate, edge_func, max_depth=20, extra_ignore=()): + queue = [obj] + depth = {id(obj): 0} + parent = {id(obj): None} + ignore = set(extra_ignore) + ignore.add(id(extra_ignore)) + ignore.add(id(queue)) + ignore.add(id(depth)) + ignore.add(id(parent)) + ignore.add(id(ignore)) + ignore.add(id(sys._getframe())) # this function + ignore.add(id(sys._getframe(1))) # find_chain/find_backref_chain, likely + gc.collect() + while queue: + target = queue.pop(0) + if predicate(target): + chain = [target] + while parent[id(target)] is not None: + target = parent[id(target)] + chain.append(target) + return chain + tdepth = depth[id(target)] + if tdepth < max_depth: + referrers = edge_func(target) + ignore.add(id(referrers)) + for source in referrers: + if id(source) in ignore: + continue + if id(source) not in depth: + depth[id(source)] = tdepth + 1 + parent[id(source)] = target + queue.append(source) + return [obj] # not found + + +# backward compatibility +refobject = at + + +# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/session.py b/.venv/lib/python3.10/site-packages/dill/session.py new file mode 100644 index 0000000..8278ccd --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/session.py @@ -0,0 +1,612 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Author: Leonardo Gama (@leogama) +# Copyright (c) 2008-2015 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +Pickle and restore the intepreter session. +""" + +__all__ = [ + 'dump_module', 'load_module', 'load_module_asdict', + 'dump_session', 'load_session' # backward compatibility +] + +import re +import os +import sys +import warnings +import pathlib +import tempfile + +TEMPDIR = pathlib.PurePath(tempfile.gettempdir()) + +# Type hints. +from typing import Optional, Union + +from dill import _dill, Pickler, Unpickler +from ._dill import ( + BuiltinMethodType, FunctionType, MethodType, ModuleType, TypeType, + _import_module, _is_builtin_module, _is_imported_module, _main_module, + _reverse_typemap, __builtin__, UnpicklingError, +) + +def _module_map(): + """get map of imported modules""" + from collections import defaultdict + from types import SimpleNamespace + modmap = SimpleNamespace( + by_name=defaultdict(list), + by_id=defaultdict(list), + top_level={}, + ) + for modname, module in sys.modules.items(): + if modname in ('__main__', '__mp_main__') or not isinstance(module, ModuleType): + continue + if '.' not in modname: + modmap.top_level[id(module)] = modname + for objname, modobj in module.__dict__.items(): + modmap.by_name[objname].append((modobj, modname)) + modmap.by_id[id(modobj)].append((modobj, objname, modname)) + return modmap + +IMPORTED_AS_TYPES = (ModuleType, TypeType, FunctionType, MethodType, BuiltinMethodType) +if 'PyCapsuleType' in _reverse_typemap: + IMPORTED_AS_TYPES += (_reverse_typemap['PyCapsuleType'],) +IMPORTED_AS_MODULES = ('ctypes', 'typing', 'subprocess', 'threading', + r'concurrent\.futures(\.\w+)?', r'multiprocessing(\.\w+)?') +IMPORTED_AS_MODULES = tuple(re.compile(x) for x in IMPORTED_AS_MODULES) + +def _lookup_module(modmap, name, obj, main_module): + """lookup name or id of obj if module is imported""" + for modobj, modname in modmap.by_name[name]: + if modobj is obj and sys.modules[modname] is not main_module: + return modname, name + __module__ = getattr(obj, '__module__', None) + if isinstance(obj, IMPORTED_AS_TYPES) or (__module__ is not None + and any(regex.fullmatch(__module__) for regex in IMPORTED_AS_MODULES)): + for modobj, objname, modname in modmap.by_id[id(obj)]: + if sys.modules[modname] is not main_module: + return modname, objname + return None, None + +def _stash_modules(main_module): + modmap = _module_map() + newmod = ModuleType(main_module.__name__) + + imported = [] + imported_as = [] + imported_top_level = [] # keep separated for backward compatibility + original = {} + for name, obj in main_module.__dict__.items(): + if obj is main_module: + original[name] = newmod # self-reference + elif obj is main_module.__dict__: + original[name] = newmod.__dict__ + # Avoid incorrectly matching a singleton value in another package (ex.: __doc__). + elif any(obj is singleton for singleton in (None, False, True)) \ + or isinstance(obj, ModuleType) and _is_builtin_module(obj): # always saved by ref + original[name] = obj + else: + source_module, objname = _lookup_module(modmap, name, obj, main_module) + if source_module is not None: + if objname == name: + imported.append((source_module, name)) + else: + imported_as.append((source_module, objname, name)) + else: + try: + imported_top_level.append((modmap.top_level[id(obj)], name)) + except KeyError: + original[name] = obj + + if len(original) < len(main_module.__dict__): + newmod.__dict__.update(original) + newmod.__dill_imported = imported + newmod.__dill_imported_as = imported_as + newmod.__dill_imported_top_level = imported_top_level + if getattr(newmod, '__loader__', None) is None and _is_imported_module(main_module): + # Trick _is_imported_module() to force saving as an imported module. + newmod.__loader__ = True # will be discarded by save_module() + return newmod + else: + return main_module + +def _restore_modules(unpickler, main_module): + try: + for modname, name in main_module.__dict__.pop('__dill_imported'): + main_module.__dict__[name] = unpickler.find_class(modname, name) + for modname, objname, name in main_module.__dict__.pop('__dill_imported_as'): + main_module.__dict__[name] = unpickler.find_class(modname, objname) + for modname, name in main_module.__dict__.pop('__dill_imported_top_level'): + main_module.__dict__[name] = __import__(modname) + except KeyError: + pass + +#NOTE: 06/03/15 renamed main_module to main +def dump_module( + filename: Union[str, os.PathLike] = None, + module: Optional[Union[ModuleType, str]] = None, + refimported: bool = False, + **kwds +) -> None: + """Pickle the current state of :py:mod:`__main__` or another module to a file. + + Save the contents of :py:mod:`__main__` (e.g. from an interactive + interpreter session), an imported module, or a module-type object (e.g. + built with :py:class:`~types.ModuleType`), to a file. The pickled + module can then be restored with the function :py:func:`load_module`. + + Args: + filename: a path-like object or a writable stream. If `None` + (the default), write to a named file in a temporary directory. + module: a module object or the name of an importable module. If `None` + (the default), :py:mod:`__main__` is saved. + refimported: if `True`, all objects identified as having been imported + into the module's namespace are saved by reference. *Note:* this is + similar but independent from ``dill.settings[`byref`]``, as + ``refimported`` refers to virtually all imported objects, while + ``byref`` only affects select objects. + **kwds: extra keyword arguments passed to :py:class:`Pickler()`. + + Raises: + :py:exc:`PicklingError`: if pickling fails. + + Examples: + + - Save current interpreter session state: + + >>> import dill + >>> squared = lambda x: x*x + >>> dill.dump_module() # save state of __main__ to /tmp/session.pkl + + - Save the state of an imported/importable module: + + >>> import dill + >>> import pox + >>> pox.plus_one = lambda x: x+1 + >>> dill.dump_module('pox_session.pkl', module=pox) + + - Save the state of a non-importable, module-type object: + + >>> import dill + >>> from types import ModuleType + >>> foo = ModuleType('foo') + >>> foo.values = [1,2,3] + >>> import math + >>> foo.sin = math.sin + >>> dill.dump_module('foo_session.pkl', module=foo, refimported=True) + + - Restore the state of the saved modules: + + >>> import dill + >>> dill.load_module() + >>> squared(2) + 4 + >>> pox = dill.load_module('pox_session.pkl') + >>> pox.plus_one(1) + 2 + >>> foo = dill.load_module('foo_session.pkl') + >>> [foo.sin(x) for x in foo.values] + [0.8414709848078965, 0.9092974268256817, 0.1411200080598672] + + - Use `refimported` to save imported objects by reference: + + >>> import dill + >>> from html.entities import html5 + >>> type(html5), len(html5) + (dict, 2231) + >>> import io + >>> buf = io.BytesIO() + >>> dill.dump_module(buf) # saves __main__, with html5 saved by value + >>> len(buf.getvalue()) # pickle size in bytes + 71665 + >>> buf = io.BytesIO() + >>> dill.dump_module(buf, refimported=True) # html5 saved by reference + >>> len(buf.getvalue()) + 438 + + *Changed in version 0.3.6:* Function ``dump_session()`` was renamed to + ``dump_module()``. Parameters ``main`` and ``byref`` were renamed to + ``module`` and ``refimported``, respectively. + + Note: + Currently, ``dill.settings['byref']`` and ``dill.settings['recurse']`` + don't apply to this function. + """ + for old_par, par in [('main', 'module'), ('byref', 'refimported')]: + if old_par in kwds: + message = "The argument %r has been renamed %r" % (old_par, par) + if old_par == 'byref': + message += " to distinguish it from dill.settings['byref']" + warnings.warn(message + ".", PendingDeprecationWarning) + if locals()[par]: # the defaults are None and False + raise TypeError("both %r and %r arguments were used" % (par, old_par)) + refimported = kwds.pop('byref', refimported) + module = kwds.pop('main', module) + + from .settings import settings + protocol = settings['protocol'] + main = module + if main is None: + main = _main_module + elif isinstance(main, str): + main = _import_module(main) + if not isinstance(main, ModuleType): + raise TypeError("%r is not a module" % main) + if hasattr(filename, 'write'): + file = filename + else: + if filename is None: + filename = str(TEMPDIR/'session.pkl') + file = open(filename, 'wb') + try: + pickler = Pickler(file, protocol, **kwds) + pickler._original_main = main + if refimported: + main = _stash_modules(main) + pickler._main = main #FIXME: dill.settings are disabled + pickler._byref = False # disable pickling by name reference + pickler._recurse = False # disable pickling recursion for globals + pickler._session = True # is best indicator of when pickling a session + pickler._first_pass = True + pickler._main_modified = main is not pickler._original_main + pickler.dump(main) + finally: + if file is not filename: # if newly opened file + file.close() + return + +# Backward compatibility. +def dump_session(filename=None, main=None, byref=False, **kwds): + warnings.warn("dump_session() has been renamed dump_module()", PendingDeprecationWarning) + dump_module(filename, module=main, refimported=byref, **kwds) +dump_session.__doc__ = dump_module.__doc__ + +class _PeekableReader: + """lightweight stream wrapper that implements peek()""" + def __init__(self, stream): + self.stream = stream + def read(self, n): + return self.stream.read(n) + def readline(self): + return self.stream.readline() + def tell(self): + return self.stream.tell() + def close(self): + return self.stream.close() + def peek(self, n): + stream = self.stream + try: + if hasattr(stream, 'flush'): stream.flush() + position = stream.tell() + stream.seek(position) # assert seek() works before reading + chunk = stream.read(n) + stream.seek(position) + return chunk + except (AttributeError, OSError): + raise NotImplementedError("stream is not peekable: %r", stream) from None + +def _make_peekable(stream): + """return stream as an object with a peek() method""" + import io + if hasattr(stream, 'peek'): + return stream + if not (hasattr(stream, 'tell') and hasattr(stream, 'seek')): + try: + return io.BufferedReader(stream) + except Exception: + pass + return _PeekableReader(stream) + +def _identify_module(file, main=None): + """identify the name of the module stored in the given file-type object""" + from pickletools import genops + UNICODE = {'UNICODE', 'BINUNICODE', 'SHORT_BINUNICODE'} + found_import = False + try: + for opcode, arg, pos in genops(file.peek(256)): + if not found_import: + if opcode.name in ('GLOBAL', 'SHORT_BINUNICODE') and \ + arg.endswith('_import_module'): + found_import = True + else: + if opcode.name in UNICODE: + return arg + else: + raise UnpicklingError("reached STOP without finding main module") + except (NotImplementedError, ValueError) as error: + # ValueError occours when the end of the chunk is reached (without a STOP). + if isinstance(error, NotImplementedError) and main is not None: + # file is not peekable, but we have main. + return None + raise UnpicklingError("unable to identify main module") from error + +def load_module( + filename: Union[str, os.PathLike] = None, + module: Optional[Union[ModuleType, str]] = None, + **kwds +) -> Optional[ModuleType]: + """Update the selected module (default is :py:mod:`__main__`) with + the state saved at ``filename``. + + Restore a module to the state saved with :py:func:`dump_module`. The + saved module can be :py:mod:`__main__` (e.g. an interpreter session), + an imported module, or a module-type object (e.g. created with + :py:class:`~types.ModuleType`). + + When restoring the state of a non-importable module-type object, the + current instance of this module may be passed as the argument ``main``. + Otherwise, a new instance is created with :py:class:`~types.ModuleType` + and returned. + + Args: + filename: a path-like object or a readable stream. If `None` + (the default), read from a named file in a temporary directory. + module: a module object or the name of an importable module; + the module name and kind (i.e. imported or non-imported) must + match the name and kind of the module stored at ``filename``. + **kwds: extra keyword arguments passed to :py:class:`Unpickler()`. + + Raises: + :py:exc:`UnpicklingError`: if unpickling fails. + :py:exc:`ValueError`: if the argument ``main`` and module saved + at ``filename`` are incompatible. + + Returns: + A module object, if the saved module is not :py:mod:`__main__` or + a module instance wasn't provided with the argument ``main``. + + Examples: + + - Save the state of some modules: + + >>> import dill + >>> squared = lambda x: x*x + >>> dill.dump_module() # save state of __main__ to /tmp/session.pkl + >>> + >>> import pox # an imported module + >>> pox.plus_one = lambda x: x+1 + >>> dill.dump_module('pox_session.pkl', module=pox) + >>> + >>> from types import ModuleType + >>> foo = ModuleType('foo') # a module-type object + >>> foo.values = [1,2,3] + >>> import math + >>> foo.sin = math.sin + >>> dill.dump_module('foo_session.pkl', module=foo, refimported=True) + + - Restore the state of the interpreter: + + >>> import dill + >>> dill.load_module() # updates __main__ from /tmp/session.pkl + >>> squared(2) + 4 + + - Load the saved state of an importable module: + + >>> import dill + >>> pox = dill.load_module('pox_session.pkl') + >>> pox.plus_one(1) + 2 + >>> import sys + >>> pox in sys.modules.values() + True + + - Load the saved state of a non-importable module-type object: + + >>> import dill + >>> foo = dill.load_module('foo_session.pkl') + >>> [foo.sin(x) for x in foo.values] + [0.8414709848078965, 0.9092974268256817, 0.1411200080598672] + >>> import math + >>> foo.sin is math.sin # foo.sin was saved by reference + True + >>> import sys + >>> foo in sys.modules.values() + False + + - Update the state of a non-importable module-type object: + + >>> import dill + >>> from types import ModuleType + >>> foo = ModuleType('foo') + >>> foo.values = ['a','b'] + >>> foo.sin = lambda x: x*x + >>> dill.load_module('foo_session.pkl', module=foo) + >>> [foo.sin(x) for x in foo.values] + [0.8414709848078965, 0.9092974268256817, 0.1411200080598672] + + *Changed in version 0.3.6:* Function ``load_session()`` was renamed to + ``load_module()``. Parameter ``main`` was renamed to ``module``. + + See also: + :py:func:`load_module_asdict` to load the contents of module saved + with :py:func:`dump_module` into a dictionary. + """ + if 'main' in kwds: + warnings.warn( + "The argument 'main' has been renamed 'module'.", + PendingDeprecationWarning + ) + if module is not None: + raise TypeError("both 'module' and 'main' arguments were used") + module = kwds.pop('main') + main = module + if hasattr(filename, 'read'): + file = filename + else: + if filename is None: + filename = str(TEMPDIR/'session.pkl') + file = open(filename, 'rb') + try: + file = _make_peekable(file) + #FIXME: dill.settings are disabled + unpickler = Unpickler(file, **kwds) + unpickler._session = True + + # Resolve unpickler._main + pickle_main = _identify_module(file, main) + if main is None and pickle_main is not None: + main = pickle_main + if isinstance(main, str): + if main.startswith('__runtime__.'): + # Create runtime module to load the session into. + main = ModuleType(main.partition('.')[-1]) + else: + main = _import_module(main) + if main is not None: + if not isinstance(main, ModuleType): + raise TypeError("%r is not a module" % main) + unpickler._main = main + else: + main = unpickler._main + + # Check against the pickle's main. + is_main_imported = _is_imported_module(main) + if pickle_main is not None: + is_runtime_mod = pickle_main.startswith('__runtime__.') + if is_runtime_mod: + pickle_main = pickle_main.partition('.')[-1] + error_msg = "can't update{} module{} %r with the saved state of{} module{} %r" + if is_runtime_mod and is_main_imported: + raise ValueError( + error_msg.format(" imported", "", "", "-type object") + % (main.__name__, pickle_main) + ) + if not is_runtime_mod and not is_main_imported: + raise ValueError( + error_msg.format("", "-type object", " imported", "") + % (pickle_main, main.__name__) + ) + if main.__name__ != pickle_main: + raise ValueError(error_msg.format("", "", "", "") % (main.__name__, pickle_main)) + + # This is for find_class() to be able to locate it. + if not is_main_imported: + runtime_main = '__runtime__.%s' % main.__name__ + sys.modules[runtime_main] = main + + loaded = unpickler.load() + finally: + if not hasattr(filename, 'read'): # if newly opened file + file.close() + try: + del sys.modules[runtime_main] + except (KeyError, NameError): + pass + assert loaded is main + _restore_modules(unpickler, main) + if main is _main_module or main is module: + return None + else: + return main + +# Backward compatibility. +def load_session(filename=None, main=None, **kwds): + warnings.warn("load_session() has been renamed load_module().", PendingDeprecationWarning) + load_module(filename, module=main, **kwds) +load_session.__doc__ = load_module.__doc__ + +def load_module_asdict( + filename: Union[str, os.PathLike] = None, + update: bool = False, + **kwds +) -> dict: + """ + Load the contents of a saved module into a dictionary. + + ``load_module_asdict()`` is the near-equivalent of:: + + lambda filename: vars(dill.load_module(filename)).copy() + + however, does not alter the original module. Also, the path of + the loaded module is stored in the ``__session__`` attribute. + + Args: + filename: a path-like object or a readable stream. If `None` + (the default), read from a named file in a temporary directory. + update: if `True`, initialize the dictionary with the current state + of the module prior to loading the state stored at filename. + **kwds: extra keyword arguments passed to :py:class:`Unpickler()` + + Raises: + :py:exc:`UnpicklingError`: if unpickling fails + + Returns: + A copy of the restored module's dictionary. + + Note: + If ``update`` is True, the corresponding module may first be imported + into the current namespace before the saved state is loaded from + filename to the dictionary. Note that any module that is imported into + the current namespace as a side-effect of using ``update`` will not be + modified by loading the saved module in filename to a dictionary. + + Example: + >>> import dill + >>> alist = [1, 2, 3] + >>> anum = 42 + >>> dill.dump_module() + >>> anum = 0 + >>> new_var = 'spam' + >>> main = dill.load_module_asdict() + >>> main['__name__'], main['__session__'] + ('__main__', '/tmp/session.pkl') + >>> main is globals() # loaded objects don't reference globals + False + >>> main['alist'] == alist + True + >>> main['alist'] is alist # was saved by value + False + >>> main['anum'] == anum # changed after the session was saved + False + >>> new_var in main # would be True if the option 'update' was set + False + """ + if 'module' in kwds: + raise TypeError("'module' is an invalid keyword argument for load_module_asdict()") + if hasattr(filename, 'read'): + file = filename + else: + if filename is None: + filename = str(TEMPDIR/'session.pkl') + file = open(filename, 'rb') + try: + file = _make_peekable(file) + main_name = _identify_module(file) + old_main = sys.modules.get(main_name) + main = ModuleType(main_name) + if update: + if old_main is None: + old_main = _import_module(main_name) + main.__dict__.update(old_main.__dict__) + else: + main.__builtins__ = __builtin__ + sys.modules[main_name] = main + load_module(file, **kwds) + finally: + if not hasattr(filename, 'read'): # if newly opened file + file.close() + try: + if old_main is None: + del sys.modules[main_name] + else: + sys.modules[main_name] = old_main + except NameError: # failed before setting old_main + pass + main.__session__ = str(filename) + return main.__dict__ + + +# Internal exports for backward compatibility with dill v0.3.5.1 +# Can't be placed in dill._dill because of circular import problems. +for name in ( + '_lookup_module', '_module_map', '_restore_modules', '_stash_modules', + 'dump_session', 'load_session' # backward compatibility functions +): + setattr(_dill, name, globals()[name]) +del name diff --git a/.venv/lib/python3.10/site-packages/dill/settings.py b/.venv/lib/python3.10/site-packages/dill/settings.py new file mode 100644 index 0000000..bba1ab9 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/settings.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +global settings for Pickler +""" + +from pickle import DEFAULT_PROTOCOL + +settings = { + #'main' : None, + 'protocol' : DEFAULT_PROTOCOL, + 'byref' : False, + #'strictio' : False, + 'fmode' : 0, #HANDLE_FMODE + 'recurse' : False, + 'ignore' : False, +} + +del DEFAULT_PROTOCOL + diff --git a/.venv/lib/python3.10/site-packages/dill/source.py b/.venv/lib/python3.10/site-packages/dill/source.py new file mode 100644 index 0000000..4b538fa --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/source.py @@ -0,0 +1,1023 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +# +# inspired by inspect.py from Python-2.7.6 +# inspect.py author: 'Ka-Ping Yee ' +# inspect.py merged into original dill.source by Mike McKerns 4/13/14 +""" +Extensions to python's 'inspect' module, which can be used +to retrieve information from live python objects. The methods +defined in this module are augmented to facilitate access to +source code of interactively defined functions and classes, +as well as provide access to source code for objects defined +in a file. +""" + +__all__ = ['findsource', 'getsourcelines', 'getsource', 'indent', 'outdent', \ + '_wrap', 'dumpsource', 'getname', '_namespace', 'getimport', \ + '_importable', 'importable','isdynamic', 'isfrommain'] + +import linecache +import re +from inspect import (getblock, getfile, getmodule, getsourcefile, indentsize, + isbuiltin, isclass, iscode, isframe, isfunction, ismethod, + ismodule, istraceback) +from tokenize import TokenError + +from ._dill import IS_IPYTHON + + +def isfrommain(obj): + "check if object was built in __main__" + module = getmodule(obj) + if module and module.__name__ == '__main__': + return True + return False + + +def isdynamic(obj): + "check if object was built in the interpreter" + try: file = getfile(obj) + except TypeError: file = None + if file == '' and isfrommain(obj): + return True + return False + + +def _matchlambda(func, line): + """check if lambda object 'func' matches raw line of code 'line'""" + from .detect import code as getcode + from .detect import freevars, globalvars, varnames + dummy = lambda : '__this_is_a_big_dummy_function__' + # process the line (removing leading whitespace, etc) + lhs,rhs = line.split('lambda ',1)[-1].split(":", 1) #FIXME: if !1 inputs + try: #FIXME: unsafe + _ = eval("lambda %s : %s" % (lhs,rhs), globals(),locals()) + except Exception: _ = dummy + # get code objects, for comparison + _, code = getcode(_).co_code, getcode(func).co_code + # check if func is in closure + _f = [line.count(i) for i in freevars(func).keys()] + if not _f: # not in closure + # check if code matches + if _ == code: return True + return False + # weak check on freevars + if not all(_f): return False #XXX: VERY WEAK + # weak check on varnames and globalvars + _f = varnames(func) + _f = [line.count(i) for i in _f[0]+_f[1]] + if _f and not all(_f): return False #XXX: VERY WEAK + _f = [line.count(i) for i in globalvars(func).keys()] + if _f and not all(_f): return False #XXX: VERY WEAK + # check if func is a double lambda + if (line.count('lambda ') > 1) and (lhs in freevars(func).keys()): + _lhs,_rhs = rhs.split('lambda ',1)[-1].split(":",1) #FIXME: if !1 inputs + try: #FIXME: unsafe + _f = eval("lambda %s : %s" % (_lhs,_rhs), globals(),locals()) + except Exception: _f = dummy + # get code objects, for comparison + _, code = getcode(_f).co_code, getcode(func).co_code + if len(_) != len(code): return False + #NOTE: should be same code same order, but except for 't' and '\x88' + _ = set((i,j) for (i,j) in zip(_,code) if i != j) + if len(_) != 1: return False #('t','\x88') + return True + # check indentsize + if not indentsize(line): return False #FIXME: is this a good check??? + # check if code 'pattern' matches + #XXX: or pattern match against dis.dis(code)? (or use uncompyle2?) + _ = _.split(_[0]) # 't' #XXX: remove matching values if starts the same? + _f = code.split(code[0]) # '\x88' + #NOTE: should be same code different order, with different first element + _ = dict(re.match(r'([\W\D\S])(.*)', _[i]).groups() for i in range(1,len(_))) + _f = dict(re.match(r'([\W\D\S])(.*)', _f[i]).groups() for i in range(1,len(_f))) + if (_.keys() == _f.keys()) and (sorted(_.values()) == sorted(_f.values())): + return True + return False + + +def findsource(object): + """Return the entire source file and starting line number for an object. + For interactively-defined objects, the 'file' is the interpreter's history. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a list of all the lines + in the file and the line number indexes a line in that list. An IOError + is raised if the source code cannot be retrieved, while a TypeError is + raised for objects where the source code is unavailable (e.g. builtins).""" + + module = getmodule(object) + try: file = getfile(module) + except TypeError: file = None + is_module_main = (module and module.__name__ == '__main__' and not file) + if IS_IPYTHON and is_module_main: + #FIXME: quick fix for functions and classes in IPython interpreter + try: + file = getfile(object) + sourcefile = getsourcefile(object) + except TypeError: + if isclass(object): + for object_method in filter(isfunction, object.__dict__.values()): + # look for a method of the class + file_candidate = getfile(object_method) + if not file_candidate.startswith('': pat1 = r'(.*(?': + pat1 = r'(.*(?' + if stdin: + lnum = len(lines) - 1 # can't get lnum easily, so leverage pat + if not pat1: pat1 = r'^(\s*def\s)|(.*(? 0: #XXX: won't find decorators in ? + line = lines[lnum] + if pat1.match(line): + if not stdin: break # co_firstlineno does the job + if name == '': # hackery needed to confirm a match + if _matchlambda(obj, line): break + else: # not a lambda, just look for the name + if name in line: # need to check for decorator... + hats = 0 + for _lnum in range(lnum-1,-1,-1): + if pat2.match(lines[_lnum]): hats += 1 + else: break + lnum = lnum - hats + break + lnum = lnum - 1 + return lines, lnum + + try: # turn instances into classes + if not isclass(object) and isclass(type(object)): # __class__ + object = object.__class__ #XXX: sometimes type(class) is better? + #XXX: we don't find how the instance was built + except AttributeError: pass + if isclass(object): + name = object.__name__ + pat = re.compile(r'^(\s*)class\s*' + name + r'\b') + # make some effort to find the best matching class definition: + # use the one with the least indentation, which is the one + # that's most probably not inside a function definition. + candidates = [] + for i in range(len(lines)-1,-1,-1): + match = pat.match(lines[i]) + if match: + # if it's at toplevel, it's already the best one + if lines[i][0] == 'c': + return lines, i + # else add whitespace to candidate list + candidates.append((match.group(1), i)) + if candidates: + # this will sort by whitespace, and by line number, + # less whitespace first #XXX: should sort high lnum before low + candidates.sort() + return lines, candidates[0][1] + else: + raise IOError('could not find class definition') + raise IOError('could not find code object') + + +def getblocks(object, lstrip=False, enclosing=False, locate=False): + """Return a list of source lines and starting line number for an object. + Interactively-defined objects refer to lines in the interpreter's history. + + If enclosing=True, then also return any enclosing code. + If lstrip=True, ensure there is no indentation in the first line of code. + If locate=True, then also return the line number for the block of code. + + DEPRECATED: use 'getsourcelines' instead + """ + lines, lnum = findsource(object) + + if ismodule(object): + if lstrip: lines = _outdent(lines) + return ([lines], [0]) if locate is True else [lines] + + #XXX: 'enclosing' means: closures only? or classes and files? + indent = indentsize(lines[lnum]) + block = getblock(lines[lnum:]) #XXX: catch any TokenError here? + + if not enclosing or not indent: + if lstrip: block = _outdent(block) + return ([block], [lnum]) if locate is True else [block] + + pat1 = r'^(\s*def\s)|(.*(? indent: #XXX: should be >= ? + line += len(code) - skip + elif target in ''.join(code): + blocks.append(code) # save code block as the potential winner + _lnum.append(line - skip) # save the line number for the match + line += len(code) - skip + else: + line += 1 + skip = 0 + # find skip: the number of consecutive decorators + elif pat2.match(lines[line]): + try: code = getblock(lines[line:]) + except TokenError: code = [lines[line]] + skip = 1 + for _line in code[1:]: # skip lines that are decorators + if not pat2.match(_line): break + skip += 1 + line += skip + # no match: reset skip and go to the next line + else: + line +=1 + skip = 0 + + if not blocks: + blocks = [block] + _lnum = [lnum] + if lstrip: blocks = [_outdent(block) for block in blocks] + # return last match + return (blocks, _lnum) if locate is True else blocks + + +def getsourcelines(object, lstrip=False, enclosing=False): + """Return a list of source lines and starting line number for an object. + Interactively-defined objects refer to lines in the interpreter's history. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a list of the lines + corresponding to the object and the line number indicates where in the + original source file the first line of code was found. An IOError is + raised if the source code cannot be retrieved, while a TypeError is + raised for objects where the source code is unavailable (e.g. builtins). + + If lstrip=True, ensure there is no indentation in the first line of code. + If enclosing=True, then also return any enclosing code.""" + code, n = getblocks(object, lstrip=lstrip, enclosing=enclosing, locate=True) + return code[-1], n[-1] + + +#NOTE: broke backward compatibility 4/16/14 (was lstrip=True, force=True) +def getsource(object, alias='', lstrip=False, enclosing=False, \ + force=False, builtin=False): + """Return the text of the source code for an object. The source code for + interactively-defined objects are extracted from the interpreter's history. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a single string. An + IOError is raised if the source code cannot be retrieved, while a + TypeError is raised for objects where the source code is unavailable + (e.g. builtins). + + If alias is provided, then add a line of code that renames the object. + If lstrip=True, ensure there is no indentation in the first line of code. + If enclosing=True, then also return any enclosing code. + If force=True, catch (TypeError,IOError) and try to use import hooks. + If builtin=True, force an import for any builtins + """ + # hascode denotes a callable + hascode = _hascode(object) + # is a class instance type (and not in builtins) + instance = _isinstance(object) + + # get source lines; if fail, try to 'force' an import + try: # fails for builtins, and other assorted object types + lines, lnum = getsourcelines(object, enclosing=enclosing) + except (TypeError, IOError): # failed to get source, resort to import hooks + if not force: # don't try to get types that findsource can't get + raise + if not getmodule(object): # get things like 'None' and '1' + if not instance: return getimport(object, alias, builtin=builtin) + # special handling (numpy arrays, ...) + _import = getimport(object, builtin=builtin) + name = getname(object, force=True) + _alias = "%s = " % alias if alias else "" + if alias == name: _alias = "" + return _import+_alias+"%s\n" % name + else: #FIXME: could use a good bit of cleanup, since using getimport... + if not instance: return getimport(object, alias, builtin=builtin) + # now we are dealing with an instance... + name = object.__class__.__name__ + module = object.__module__ + if module in ['builtins','__builtin__']: + return getimport(object, alias, builtin=builtin) + else: #FIXME: leverage getimport? use 'from module import name'? + lines, lnum = ["%s = __import__('%s', fromlist=['%s']).%s\n" % (name,module,name,name)], 0 + obj = eval(lines[0].lstrip(name + ' = ')) + lines, lnum = getsourcelines(obj, enclosing=enclosing) + + # strip leading indent (helps ensure can be imported) + if lstrip or alias: + lines = _outdent(lines) + + # instantiate, if there's a nice repr #XXX: BAD IDEA??? + if instance: #and force: #XXX: move into findsource or getsourcelines ? + if '(' in repr(object): lines.append('%r\n' % object) + #else: #XXX: better to somehow to leverage __reduce__ ? + # reconstructor,args = object.__reduce__() + # _ = reconstructor(*args) + else: # fall back to serialization #XXX: bad idea? + #XXX: better not duplicate work? #XXX: better new/enclose=True? + lines = dumpsource(object, alias='', new=force, enclose=False) + lines, lnum = [line+'\n' for line in lines.split('\n')][:-1], 0 + #else: object.__code__ # raise AttributeError + + # add an alias to the source code + if alias: + if hascode: + skip = 0 + for line in lines: # skip lines that are decorators + if not line.startswith('@'): break + skip += 1 + #XXX: use regex from findsource / getsourcelines ? + if lines[skip].lstrip().startswith('def '): # we have a function + if alias != object.__name__: + lines.append('\n%s = %s\n' % (alias, object.__name__)) + elif 'lambda ' in lines[skip]: # we have a lambda + if alias != lines[skip].split('=')[0].strip(): + lines[skip] = '%s = %s' % (alias, lines[skip]) + else: # ...try to use the object's name + if alias != object.__name__: + lines.append('\n%s = %s\n' % (alias, object.__name__)) + else: # class or class instance + if instance: + if alias != lines[-1].split('=')[0].strip(): + lines[-1] = ('%s = ' % alias) + lines[-1] + else: + name = getname(object, force=True) or object.__name__ + if alias != name: + lines.append('\n%s = %s\n' % (alias, name)) + return ''.join(lines) + + +def _hascode(object): + '''True if object has an attribute that stores it's __code__''' + return getattr(object,'__code__',None) or getattr(object,'func_code',None) + +def _isinstance(object): + '''True if object is a class instance type (and is not a builtin)''' + if _hascode(object) or isclass(object) or ismodule(object): + return False + if istraceback(object) or isframe(object) or iscode(object): + return False + # special handling (numpy arrays, ...) + if not getmodule(object) and getmodule(type(object)).__name__ in ['numpy']: + return True +# # check if is instance of a builtin +# if not getmodule(object) and getmodule(type(object)).__name__ in ['__builtin__','builtins']: +# return False + _types = ('") + if not repr(type(object)).startswith(_types): #FIXME: weak hack + return False + if not getmodule(object) or object.__module__ in ['builtins','__builtin__'] or getname(object, force=True) in ['array']: + return False + return True # by process of elimination... it's what we want + + +def _intypes(object): + '''check if object is in the 'types' module''' + import types + # allow user to pass in object or object.__name__ + if type(object) is not type(''): + object = getname(object, force=True) + if object == 'ellipsis': object = 'EllipsisType' + return True if hasattr(types, object) else False + + +def _isstring(object): #XXX: isstringlike better? + '''check if object is a string-like type''' + return isinstance(object, (str, bytes)) + + +def indent(code, spaces=4): + '''indent a block of code with whitespace (default is 4 spaces)''' + indent = indentsize(code) + from numbers import Integral + if isinstance(spaces, Integral): spaces = ' '*spaces + # if '\t' is provided, will indent with a tab + nspaces = indentsize(spaces) + # blank lines (etc) need to be ignored + lines = code.split('\n') +## stq = "'''"; dtq = '"""' +## in_stq = in_dtq = False + for i in range(len(lines)): + #FIXME: works... but shouldn't indent 2nd+ lines of multiline doc + _indent = indentsize(lines[i]) + if indent > _indent: continue + lines[i] = spaces+lines[i] +## #FIXME: may fail when stq and dtq in same line (depends on ordering) +## nstq, ndtq = lines[i].count(stq), lines[i].count(dtq) +## if not in_dtq and not in_stq: +## lines[i] = spaces+lines[i] # we indent +## # entering a comment block +## if nstq%2: in_stq = not in_stq +## if ndtq%2: in_dtq = not in_dtq +## # leaving a comment block +## elif in_dtq and ndtq%2: in_dtq = not in_dtq +## elif in_stq and nstq%2: in_stq = not in_stq +## else: pass + if lines[-1].strip() == '': lines[-1] = '' + return '\n'.join(lines) + + +def _outdent(lines, spaces=None, all=True): + '''outdent lines of code, accounting for docs and line continuations''' + indent = indentsize(lines[0]) + if spaces is None or spaces > indent or spaces < 0: spaces = indent + for i in range(len(lines) if all else 1): + #FIXME: works... but shouldn't outdent 2nd+ lines of multiline doc + _indent = indentsize(lines[i]) + if spaces > _indent: _spaces = _indent + else: _spaces = spaces + lines[i] = lines[i][_spaces:] + return lines + +def outdent(code, spaces=None, all=True): + '''outdent a block of code (default is to strip all leading whitespace)''' + indent = indentsize(code) + if spaces is None or spaces > indent or spaces < 0: spaces = indent + #XXX: will this delete '\n' in some cases? + if not all: return code[spaces:] + return '\n'.join(_outdent(code.split('\n'), spaces=spaces, all=all)) + + +# _wrap provides an wrapper to correctly exec and load into locals +__globals__ = globals() +__locals__ = locals() +def _wrap(f): + """ encapsulate a function and it's __import__ """ + def func(*args, **kwds): + try: + # _ = eval(getsource(f, force=True)) #XXX: safer but less robust + exec(getimportable(f, alias='_'), __globals__, __locals__) + except Exception: + raise ImportError('cannot import name ' + f.__name__) + return _(*args, **kwds) + func.__name__ = f.__name__ + func.__doc__ = f.__doc__ + return func + + +def _enclose(object, alias=''): #FIXME: needs alias to hold returned object + """create a function enclosure around the source of some object""" + #XXX: dummy and stub should append a random string + dummy = '__this_is_a_big_dummy_enclosing_function__' + stub = '__this_is_a_stub_variable__' + code = 'def %s():\n' % dummy + code += indent(getsource(object, alias=stub, lstrip=True, force=True)) + code += indent('return %s\n' % stub) + if alias: code += '%s = ' % alias + code += '%s(); del %s\n' % (dummy, dummy) + #code += "globals().pop('%s',lambda :None)()\n" % dummy + return code + + +def dumpsource(object, alias='', new=False, enclose=True): + """'dump to source', where the code includes a pickled object. + + If new=True and object is a class instance, then create a new + instance using the unpacked class source code. If enclose, then + create the object inside a function enclosure (thus minimizing + any global namespace pollution). + """ + from dill import dumps + pik = repr(dumps(object)) + code = 'import dill\n' + if enclose: + stub = '__this_is_a_stub_variable__' #XXX: *must* be same _enclose.stub + pre = '%s = ' % stub + new = False #FIXME: new=True doesn't work with enclose=True + else: + stub = alias + pre = '%s = ' % stub if alias else alias + + # if a 'new' instance is not needed, then just dump and load + if not new or not _isinstance(object): + code += pre + 'dill.loads(%s)\n' % pik + else: #XXX: other cases where source code is needed??? + code += getsource(object.__class__, alias='', lstrip=True, force=True) + mod = repr(object.__module__) # should have a module (no builtins here) + code += pre + 'dill.loads(%s.replace(b%s,bytes(__name__,"UTF-8")))\n' % (pik,mod) + #code += 'del %s' % object.__class__.__name__ #NOTE: kills any existing! + + if enclose: + # generation of the 'enclosure' + dummy = '__this_is_a_big_dummy_object__' + dummy = _enclose(dummy, alias=alias) + # hack to replace the 'dummy' with the 'real' code + dummy = dummy.split('\n') + code = dummy[0]+'\n' + indent(code) + '\n'.join(dummy[-3:]) + + return code #XXX: better 'dumpsourcelines', returning list of lines? + + +def getname(obj, force=False, fqn=False): #XXX: throw(?) to raise error on fail? + """get the name of the object. for lambdas, get the name of the pointer """ + if fqn: return '.'.join(_namespace(obj)) #NOTE: returns 'type' + module = getmodule(obj) + if not module: # things like "None" and "1" + if not force: return None #NOTE: returns 'instance' NOT 'type' #FIXME? + # handle some special cases + if hasattr(obj, 'dtype') and not obj.shape: + return getname(obj.__class__) + "(" + repr(obj.tolist()) + ")" + return repr(obj) + try: + #XXX: 'wrong' for decorators and curried functions ? + # if obj.func_closure: ...use logic from getimportable, etc ? + name = obj.__name__ + if name == '': + return getsource(obj).split('=',1)[0].strip() + # handle some special cases + if module.__name__ in ['builtins','__builtin__']: + if name == 'ellipsis': name = 'EllipsisType' + return name + except AttributeError: #XXX: better to just throw AttributeError ? + if not force: return None + name = repr(obj) + if name.startswith('<'): # or name.split('('): + return None + return name + + +def _namespace(obj): + """_namespace(obj); return namespace hierarchy (as a list of names) + for the given object. For an instance, find the class hierarchy. + + For example: + + >>> from functools import partial + >>> p = partial(int, base=2) + >>> _namespace(p) + [\'functools\', \'partial\'] + """ + # mostly for functions and modules and such + #FIXME: 'wrong' for decorators and curried functions + try: #XXX: needs some work and testing on different types + module = qual = str(getmodule(obj)).split()[1].strip('>').strip('"').strip("'") + qual = qual.split('.') + if ismodule(obj): + return qual + # get name of a lambda, function, etc + name = getname(obj) or obj.__name__ # failing, raise AttributeError + # check special cases (NoneType, ...) + if module in ['builtins','__builtin__']: # BuiltinFunctionType + if _intypes(name): return ['types'] + [name] + return qual + [name] #XXX: can be wrong for some aliased objects + except Exception: pass + # special case: numpy.inf and numpy.nan (we don't want them as floats) + if str(obj) in ['inf','nan','Inf','NaN']: # is more, but are they needed? + return ['numpy'] + [str(obj)] + # mostly for classes and class instances and such + module = getattr(obj.__class__, '__module__', None) + qual = str(obj.__class__) + try: qual = qual[qual.index("'")+1:-2] + except ValueError: pass # str(obj.__class__) made the 'try' unnecessary + qual = qual.split(".") + if module in ['builtins','__builtin__']: + # check special cases (NoneType, Ellipsis, ...) + if qual[-1] == 'ellipsis': qual[-1] = 'EllipsisType' + if _intypes(qual[-1]): module = 'types' #XXX: BuiltinFunctionType + qual = [module] + qual + return qual + + +#NOTE: 05/25/14 broke backward compatibility: added 'alias' as 3rd argument +def _getimport(head, tail, alias='', verify=True, builtin=False): + """helper to build a likely import string from head and tail of namespace. + ('head','tail') are used in the following context: "from head import tail" + + If verify=True, then test the import string before returning it. + If builtin=True, then force an import for builtins where possible. + If alias is provided, then rename the object on import. + """ + # special handling for a few common types + if tail in ['Ellipsis', 'NotImplemented'] and head in ['types']: + head = len.__module__ + elif tail in ['None'] and head in ['types']: + _alias = '%s = ' % alias if alias else '' + if alias == tail: _alias = '' + return _alias+'%s\n' % tail + # we don't need to import from builtins, so return '' +# elif tail in ['NoneType','int','float','long','complex']: return '' #XXX: ? + if head in ['builtins','__builtin__']: + # special cases (NoneType, Ellipsis, ...) #XXX: BuiltinFunctionType + if tail == 'ellipsis': tail = 'EllipsisType' + if _intypes(tail): head = 'types' + elif not builtin: + _alias = '%s = ' % alias if alias else '' + if alias == tail: _alias = '' + return _alias+'%s\n' % tail + else: pass # handle builtins below + # get likely import string + if not head: _str = "import %s" % tail + else: _str = "from %s import %s" % (head, tail) + _alias = " as %s\n" % alias if alias else "\n" + if alias == tail: _alias = "\n" + _str += _alias + # FIXME: fails on most decorators, currying, and such... + # (could look for magic __wrapped__ or __func__ attr) + # (could fix in 'namespace' to check obj for closure) + if verify and not head.startswith('dill.'):# weird behavior for dill + #print(_str) + try: exec(_str) #XXX: check if == obj? (name collision) + except ImportError: #XXX: better top-down or bottom-up recursion? + _head = head.rsplit(".",1)[0] #(or get all, then compare == obj?) + if not _head: raise + if _head != head: + _str = _getimport(_head, tail, alias, verify) + return _str + + +#XXX: rename builtin to force? vice versa? verify to force? (as in getsource) +#NOTE: 05/25/14 broke backward compatibility: added 'alias' as 2nd argument +def getimport(obj, alias='', verify=True, builtin=False, enclosing=False): + """get the likely import string for the given object + + obj is the object to inspect + If verify=True, then test the import string before returning it. + If builtin=True, then force an import for builtins where possible. + If enclosing=True, get the import for the outermost enclosing callable. + If alias is provided, then rename the object on import. + """ + if enclosing: + from .detect import outermost + _obj = outermost(obj) + obj = _obj if _obj else obj + # get the namespace + qual = _namespace(obj) + head = '.'.join(qual[:-1]) + tail = qual[-1] + # for named things... with a nice repr #XXX: move into _namespace? + try: # look for '<...>' and be mindful it might be in lists, dicts, etc... + name = repr(obj).split('<',1)[1].split('>',1)[1] + name = None # we have a 'object'-style repr + except Exception: # it's probably something 'importable' + if head in ['builtins','__builtin__']: + name = repr(obj) #XXX: catch [1,2], (1,2), set([1,2])... others? + elif _isinstance(obj): + name = getname(obj, force=True).split('(')[0] + else: + name = repr(obj).split('(')[0] + #if not repr(obj).startswith('<'): name = repr(obj).split('(')[0] + #else: name = None + if name: # try using name instead of tail + try: return _getimport(head, name, alias, verify, builtin) + except ImportError: pass + except SyntaxError: + if head in ['builtins','__builtin__']: + _alias = '%s = ' % alias if alias else '' + if alias == name: _alias = '' + return _alias+'%s\n' % name + else: pass + try: + #if type(obj) is type(abs): _builtin = builtin # BuiltinFunctionType + #else: _builtin = False + return _getimport(head, tail, alias, verify, builtin) + except ImportError: + raise # could do some checking against obj + except SyntaxError: + if head in ['builtins','__builtin__']: + _alias = '%s = ' % alias if alias else '' + if alias == tail: _alias = '' + return _alias+'%s\n' % tail + raise # could do some checking against obj + + +def _importable(obj, alias='', source=None, enclosing=False, force=True, \ + builtin=True, lstrip=True): + """get an import string (or the source code) for the given object + + This function will attempt to discover the name of the object, or the repr + of the object, or the source code for the object. To attempt to force + discovery of the source code, use source=True, to attempt to force the + use of an import, use source=False; otherwise an import will be sought + for objects not defined in __main__. The intent is to build a string + that can be imported from a python file. obj is the object to inspect. + If alias is provided, then rename the object with the given alias. + + If source=True, use these options: + If enclosing=True, then also return any enclosing code. + If force=True, catch (TypeError,IOError) and try to use import hooks. + If lstrip=True, ensure there is no indentation in the first line of code. + + If source=False, use these options: + If enclosing=True, get the import for the outermost enclosing callable. + If force=True, then don't test the import string before returning it. + If builtin=True, then force an import for builtins where possible. + """ + if source is None: + source = True if isfrommain(obj) else False + if source: # first try to get the source + try: + return getsource(obj, alias, enclosing=enclosing, \ + force=force, lstrip=lstrip, builtin=builtin) + except Exception: pass + try: + if not _isinstance(obj): + return getimport(obj, alias, enclosing=enclosing, \ + verify=(not force), builtin=builtin) + # first 'get the import', then 'get the instance' + _import = getimport(obj, enclosing=enclosing, \ + verify=(not force), builtin=builtin) + name = getname(obj, force=True) + if not name: + raise AttributeError("object has no atribute '__name__'") + _alias = "%s = " % alias if alias else "" + if alias == name: _alias = "" + return _import+_alias+"%s\n" % name + + except Exception: pass + if not source: # try getsource, only if it hasn't been tried yet + try: + return getsource(obj, alias, enclosing=enclosing, \ + force=force, lstrip=lstrip, builtin=builtin) + except Exception: pass + # get the name (of functions, lambdas, and classes) + # or hope that obj can be built from the __repr__ + #XXX: what to do about class instances and such? + obj = getname(obj, force=force) + # we either have __repr__ or __name__ (or None) + if not obj or obj.startswith('<'): + raise AttributeError("object has no atribute '__name__'") + _alias = '%s = ' % alias if alias else '' + if alias == obj: _alias = '' + return _alias+'%s\n' % obj + #XXX: possible failsafe... (for example, for instances when source=False) + # "import dill; result = dill.loads(); # repr()" + +def _closuredimport(func, alias='', builtin=False): + """get import for closured objects; return a dict of 'name' and 'import'""" + import re + from .detect import freevars, outermost + free_vars = freevars(func) + func_vars = {} + # split into 'funcs' and 'non-funcs' + for name,obj in list(free_vars.items()): + if not isfunction(obj): continue + # get import for 'funcs' + fobj = free_vars.pop(name) + src = getsource(fobj) + if src.lstrip().startswith('@'): # we have a decorator + src = getimport(fobj, alias=alias, builtin=builtin) + else: # we have to "hack" a bit... and maybe be lucky + encl = outermost(func) + # pattern: 'func = enclosing(fobj' + pat = r'.*[\w\s]=\s*'+getname(encl)+r'\('+getname(fobj) + mod = getname(getmodule(encl)) + #HACK: get file containing 'outer' function; is func there? + lines,_ = findsource(encl) + candidate = [line for line in lines if getname(encl) in line and \ + re.match(pat, line)] + if not candidate: + mod = getname(getmodule(fobj)) + #HACK: get file containing 'inner' function; is func there? + lines,_ = findsource(fobj) + candidate = [line for line in lines \ + if getname(fobj) in line and re.match(pat, line)] + if not len(candidate): raise TypeError('import could not be found') + candidate = candidate[-1] + name = candidate.split('=',1)[0].split()[-1].strip() + src = _getimport(mod, name, alias=alias, builtin=builtin) + func_vars[name] = src + if not func_vars: + name = outermost(func) + mod = getname(getmodule(name)) + if not mod or name is func: # then it can be handled by getimport + name = getname(func, force=True) #XXX: better key? + src = getimport(func, alias=alias, builtin=builtin) + else: + lines,_ = findsource(name) + # pattern: 'func = enclosing(' + candidate = [line for line in lines if getname(name) in line and \ + re.match(r'.*[\w\s]=\s*'+getname(name)+r'\(', line)] + if not len(candidate): raise TypeError('import could not be found') + candidate = candidate[-1] + name = candidate.split('=',1)[0].split()[-1].strip() + src = _getimport(mod, name, alias=alias, builtin=builtin) + func_vars[name] = src + return func_vars + +#XXX: should be able to use __qualname__ +def _closuredsource(func, alias=''): + """get source code for closured objects; return a dict of 'name' + and 'code blocks'""" + #FIXME: this entire function is a messy messy HACK + # - pollutes global namespace + # - fails if name of freevars are reused + # - can unnecessarily duplicate function code + from .detect import freevars + free_vars = freevars(func) + func_vars = {} + # split into 'funcs' and 'non-funcs' + for name,obj in list(free_vars.items()): + if not isfunction(obj): + # get source for 'non-funcs' + free_vars[name] = getsource(obj, force=True, alias=name) + continue + # get source for 'funcs' + fobj = free_vars.pop(name) + src = getsource(fobj, alias) # DO NOT include dependencies + # if source doesn't start with '@', use name as the alias + if not src.lstrip().startswith('@'): #FIXME: 'enclose' in dummy; + src = importable(fobj,alias=name)# wrong ref 'name' + org = getsource(func, alias, enclosing=False, lstrip=True) + src = (src, org) # undecorated first, then target + else: #NOTE: reproduces the code! + org = getsource(func, enclosing=True, lstrip=False) + src = importable(fobj, alias, source=True) # include dependencies + src = (org, src) # target first, then decorated + func_vars[name] = src + src = ''.join(free_vars.values()) + if not func_vars: #FIXME: 'enclose' in dummy; wrong ref 'name' + org = getsource(func, alias, force=True, enclosing=False, lstrip=True) + src = (src, org) # variables first, then target + else: + src = (src, None) # just variables (better '' instead of None?) + func_vars[None] = src + # FIXME: remove duplicates (however, order is important...) + return func_vars + +def importable(obj, alias='', source=None, builtin=True): + """get an importable string (i.e. source code or the import string) + for the given object, including any required objects from the enclosing + and global scope + + This function will attempt to discover the name of the object, or the repr + of the object, or the source code for the object. To attempt to force + discovery of the source code, use source=True, to attempt to force the + use of an import, use source=False; otherwise an import will be sought + for objects not defined in __main__. The intent is to build a string + that can be imported from a python file. + + obj is the object to inspect. If alias is provided, then rename the + object with the given alias. If builtin=True, then force an import for + builtins where possible. + """ + #NOTE: we always 'force', and 'lstrip' as necessary + #NOTE: for 'enclosing', use importable(outermost(obj)) + if source is None: + source = True if isfrommain(obj) else False + elif builtin and isbuiltin(obj): + source = False + tried_source = tried_import = False + while True: + if not source: # we want an import + try: + if _isinstance(obj): # for instances, punt to _importable + return _importable(obj, alias, source=False, builtin=builtin) + src = _closuredimport(obj, alias=alias, builtin=builtin) + if len(src) == 0: + raise NotImplementedError('not implemented') + if len(src) > 1: + raise NotImplementedError('not implemented') + return list(src.values())[0] + except Exception: + if tried_source: raise + tried_import = True + # we want the source + try: + src = _closuredsource(obj, alias=alias) + if len(src) == 0: + raise NotImplementedError('not implemented') + # groan... an inline code stitcher + def _code_stitcher(block): + "stitch together the strings in tuple 'block'" + if block[0] and block[-1]: block = '\n'.join(block) + elif block[0]: block = block[0] + elif block[-1]: block = block[-1] + else: block = '' + return block + # get free_vars first + _src = _code_stitcher(src.pop(None)) + _src = [_src] if _src else [] + # get func_vars + for xxx in src.values(): + xxx = _code_stitcher(xxx) + if xxx: _src.append(xxx) + # make a single source string + if not len(_src): + src = '' + elif len(_src) == 1: + src = _src[0] + else: + src = '\n'.join(_src) + # get source code of objects referred to by obj in global scope + from .detect import globalvars + obj = globalvars(obj) #XXX: don't worry about alias? recurse? etc? + obj = list(getsource(_obj,name,force=True) for (name,_obj) in obj.items() if not isbuiltin(_obj)) + obj = '\n'.join(obj) if obj else '' + # combine all referred-to source (global then enclosing) + if not obj: return src + if not src: return obj + return obj + src + except Exception: + if tried_import: raise + tried_source = True + source = not source + # should never get here + return + + +# backward compatibility +def getimportable(obj, alias='', byname=True, explicit=False): + return importable(obj,alias,source=(not byname),builtin=explicit) + #return outdent(_importable(obj,alias,source=(not byname),builtin=explicit)) +def likely_import(obj, passive=False, explicit=False): + return getimport(obj, verify=(not passive), builtin=explicit) +def _likely_import(first, last, passive=False, explicit=True): + return _getimport(first, last, verify=(not passive), builtin=explicit) +_get_name = getname +getblocks_from_history = getblocks + + + +# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/temp.py b/.venv/lib/python3.10/site-packages/dill/temp.py new file mode 100644 index 0000000..c272877 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/temp.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +Methods for serialized objects (or source code) stored in temporary files +and file-like objects. +""" +#XXX: better instead to have functions write to any given file-like object ? +#XXX: currently, all file-like objects are created by the function... + +__all__ = ['dump_source', 'dump', 'dumpIO_source', 'dumpIO',\ + 'load_source', 'load', 'loadIO_source', 'loadIO',\ + 'capture'] + +import contextlib + + +@contextlib.contextmanager +def capture(stream='stdout'): + """builds a context that temporarily replaces the given stream name + + >>> with capture('stdout') as out: + ... print ("foo!") + ... + >>> print (out.getvalue()) + foo! + + """ + import sys + from io import StringIO + orig = getattr(sys, stream) + setattr(sys, stream, StringIO()) + try: + yield getattr(sys, stream) + finally: + setattr(sys, stream, orig) + + +def b(x): # deal with b'foo' versus 'foo' + import codecs + return codecs.latin_1_encode(x)[0] + +def load_source(file, **kwds): + """load an object that was stored with dill.temp.dump_source + + file: filehandle + alias: string name of stored object + mode: mode to open the file, one of: {'r', 'rb'} + + >>> f = lambda x: x**2 + >>> pyfile = dill.temp.dump_source(f, alias='_f') + >>> _f = dill.temp.load_source(pyfile) + >>> _f(4) + 16 + """ + alias = kwds.pop('alias', None) + mode = kwds.pop('mode', 'r') + fname = getattr(file, 'name', file) # fname=file.name or fname=file (if str) + source = open(fname, mode=mode, **kwds).read() + if not alias: + tag = source.strip().splitlines()[-1].split() + if tag[0] != '#NAME:': + stub = source.splitlines()[0] + raise IOError("unknown name for code: %s" % stub) + alias = tag[-1] + local = {} + exec(source, local) + _ = eval("%s" % alias, local) + return _ + +def dump_source(object, **kwds): + """write object source to a NamedTemporaryFile (instead of dill.dump) +Loads with "import" or "dill.temp.load_source". Returns the filehandle. + + >>> f = lambda x: x**2 + >>> pyfile = dill.temp.dump_source(f, alias='_f') + >>> _f = dill.temp.load_source(pyfile) + >>> _f(4) + 16 + + >>> f = lambda x: x**2 + >>> pyfile = dill.temp.dump_source(f, dir='.') + >>> modulename = os.path.basename(pyfile.name).split('.py')[0] + >>> exec('from %s import f as _f' % modulename) + >>> _f(4) + 16 + +Optional kwds: + If 'alias' is specified, the object will be renamed to the given string. + + If 'prefix' is specified, the file name will begin with that prefix, + otherwise a default prefix is used. + + If 'dir' is specified, the file will be created in that directory, + otherwise a default directory is used. + + If 'text' is specified and true, the file is opened in text + mode. Else (the default) the file is opened in binary mode. On + some operating systems, this makes no difference. + +NOTE: Keep the return value for as long as you want your file to exist ! + """ #XXX: write a "load_source"? + from .source import importable, getname + import tempfile + kwds.setdefault('delete', True) + kwds.pop('suffix', '') # this is *always* '.py' + alias = kwds.pop('alias', '') #XXX: include an alias so a name is known + name = str(alias) or getname(object) + name = "\n#NAME: %s\n" % name + #XXX: assumes kwds['dir'] is writable and on $PYTHONPATH + file = tempfile.NamedTemporaryFile(suffix='.py', **kwds) + file.write(b(''.join([importable(object, alias=alias),name]))) + file.flush() + return file + +def load(file, **kwds): + """load an object that was stored with dill.temp.dump + + file: filehandle + mode: mode to open the file, one of: {'r', 'rb'} + + >>> dumpfile = dill.temp.dump([1, 2, 3, 4, 5]) + >>> dill.temp.load(dumpfile) + [1, 2, 3, 4, 5] + """ + import dill as pickle + mode = kwds.pop('mode', 'rb') + name = getattr(file, 'name', file) # name=file.name or name=file (if str) + return pickle.load(open(name, mode=mode, **kwds)) + +def dump(object, **kwds): + """dill.dump of object to a NamedTemporaryFile. +Loads with "dill.temp.load". Returns the filehandle. + + >>> dumpfile = dill.temp.dump([1, 2, 3, 4, 5]) + >>> dill.temp.load(dumpfile) + [1, 2, 3, 4, 5] + +Optional kwds: + If 'suffix' is specified, the file name will end with that suffix, + otherwise there will be no suffix. + + If 'prefix' is specified, the file name will begin with that prefix, + otherwise a default prefix is used. + + If 'dir' is specified, the file will be created in that directory, + otherwise a default directory is used. + + If 'text' is specified and true, the file is opened in text + mode. Else (the default) the file is opened in binary mode. On + some operating systems, this makes no difference. + +NOTE: Keep the return value for as long as you want your file to exist ! + """ + import dill as pickle + import tempfile + kwds.setdefault('delete', True) + file = tempfile.NamedTemporaryFile(**kwds) + pickle.dump(object, file) + file.flush() + return file + +def loadIO(buffer, **kwds): + """load an object that was stored with dill.temp.dumpIO + + buffer: buffer object + + >>> dumpfile = dill.temp.dumpIO([1, 2, 3, 4, 5]) + >>> dill.temp.loadIO(dumpfile) + [1, 2, 3, 4, 5] + """ + import dill as pickle + from io import BytesIO as StringIO + value = getattr(buffer, 'getvalue', buffer) # value or buffer.getvalue + if value != buffer: value = value() # buffer.getvalue() + return pickle.load(StringIO(value)) + +def dumpIO(object, **kwds): + """dill.dump of object to a buffer. +Loads with "dill.temp.loadIO". Returns the buffer object. + + >>> dumpfile = dill.temp.dumpIO([1, 2, 3, 4, 5]) + >>> dill.temp.loadIO(dumpfile) + [1, 2, 3, 4, 5] + """ + import dill as pickle + from io import BytesIO as StringIO + file = StringIO() + pickle.dump(object, file) + file.flush() + return file + +def loadIO_source(buffer, **kwds): + """load an object that was stored with dill.temp.dumpIO_source + + buffer: buffer object + alias: string name of stored object + + >>> f = lambda x:x**2 + >>> pyfile = dill.temp.dumpIO_source(f, alias='_f') + >>> _f = dill.temp.loadIO_source(pyfile) + >>> _f(4) + 16 + """ + alias = kwds.pop('alias', None) + source = getattr(buffer, 'getvalue', buffer) # source or buffer.getvalue + if source != buffer: source = source() # buffer.getvalue() + source = source.decode() # buffer to string + if not alias: + tag = source.strip().splitlines()[-1].split() + if tag[0] != '#NAME:': + stub = source.splitlines()[0] + raise IOError("unknown name for code: %s" % stub) + alias = tag[-1] + local = {} + exec(source, local) + _ = eval("%s" % alias, local) + return _ + +def dumpIO_source(object, **kwds): + """write object source to a buffer (instead of dill.dump) +Loads by with dill.temp.loadIO_source. Returns the buffer object. + + >>> f = lambda x:x**2 + >>> pyfile = dill.temp.dumpIO_source(f, alias='_f') + >>> _f = dill.temp.loadIO_source(pyfile) + >>> _f(4) + 16 + +Optional kwds: + If 'alias' is specified, the object will be renamed to the given string. + """ + from .source import importable, getname + from io import BytesIO as StringIO + alias = kwds.pop('alias', '') #XXX: include an alias so a name is known + name = str(alias) or getname(object) + name = "\n#NAME: %s\n" % name + #XXX: assumes kwds['dir'] is writable and on $PYTHONPATH + file = StringIO() + file.write(b(''.join([importable(object, alias=alias),name]))) + file.flush() + return file + + +del contextlib + + +# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/tests/__init__.py b/.venv/lib/python3.10/site-packages/dill/tests/__init__.py new file mode 100644 index 0000000..809bc6d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/__init__.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2018-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +to run this test suite, first build and install `dill`. + + $ python -m pip install ../.. + + +then run the tests with: + + $ python -m dill.tests + + +or, if `nose` is installed: + + $ nosetests + +""" diff --git a/.venv/lib/python3.10/site-packages/dill/tests/__main__.py b/.venv/lib/python3.10/site-packages/dill/tests/__main__.py new file mode 100644 index 0000000..17b3fab --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/__main__.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2018-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import glob +import os +import sys +import subprocess as sp +python = sys.executable +try: + import pox + python = pox.which_python(version=True) or python +except ImportError: + pass +shell = sys.platform[:3] == 'win' + +suite = os.path.dirname(__file__) or os.path.curdir +tests = glob.glob(suite + os.path.sep + 'test_*.py') + + +if __name__ == '__main__': + + failed = 0 + for test in tests: + p = sp.Popen([python, test], shell=shell).wait() + if p: + print('F', end='', flush=True) + failed = 1 + else: + print('.', end='', flush=True) + print('') + exit(failed) diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_abc.py b/.venv/lib/python3.10/site-packages/dill/tests/test_abc.py new file mode 100644 index 0000000..37d2acc --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_abc.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2023-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +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 type(instance) #NOTE: issue #612, test_abc_local + #NOTE: dill.copy of local (or non-local) classes should (not) be the same? + 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) + #NOTE: dill.copy of local (or non-local) classes should (not) be the same? + # + # .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() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_check.py b/.venv/lib/python3.10/site-packages/dill/tests/test_check.py new file mode 100644 index 0000000..9daef72 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_check.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +from dill import check +import sys + +from dill.temp import capture + + +#FIXME: this doesn't catch output... it's from the internal call +def raise_check(func, **kwds): + try: + with capture('stdout') as out: + check(func, **kwds) + except Exception: + e = sys.exc_info()[1] + raise AssertionError(str(e)) + else: + assert 'Traceback' not in out.getvalue() + finally: + out.close() + + +f = lambda x:x**2 + + +def test_simple(verbose=None): + raise_check(f, verbose=verbose) + + +def test_recurse(verbose=None): + raise_check(f, recurse=True, verbose=verbose) + + +def test_byref(verbose=None): + raise_check(f, byref=True, verbose=verbose) + + +def test_protocol(verbose=None): + raise_check(f, protocol=True, verbose=verbose) + + +def test_python(verbose=None): + raise_check(f, python=None, verbose=verbose) + + +#TODO: test incompatible versions +#TODO: test dump failure +#TODO: test load failure + + +if __name__ == '__main__': + test_simple() + test_recurse() + test_byref() + test_protocol() + test_python() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_classdef.py b/.venv/lib/python3.10/site-packages/dill/tests/test_classdef.py new file mode 100644 index 0000000..e3110cd --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_classdef.py @@ -0,0 +1,340 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 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('..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 + +def test_inherit(): #NOTE: see issue #612 + class Foo: + w = 0 + x = 1 + y = 1.1 + a = () + b = (1,) + n = None + + class Bar(Foo): + w = 2 + x = 1 + y = 1.1 + z = 0.2 + a = () + b = (1,) + c = (2,) + n = None + + Baz = dill.copy(Bar) + + import platform + is_pypy = platform.python_implementation() == 'PyPy' + assert Bar.__dict__ == Baz.__dict__ + # ints + assert 'w' in Bar.__dict__ and 'w' in Baz.__dict__ + assert Bar.__dict__['w'] is Baz.__dict__['w'] + assert 'x' in Bar.__dict__ and 'x' in Baz.__dict__ + assert Bar.__dict__['x'] is Baz.__dict__['x'] + # floats + assert 'y' in Bar.__dict__ and 'y' in Baz.__dict__ + same = Bar.__dict__['y'] is Baz.__dict__['y'] + assert same if is_pypy else not same + assert 'z' in Bar.__dict__ and 'z' in Baz.__dict__ + same = Bar.__dict__['z'] is Baz.__dict__['z'] + assert same if is_pypy else not same + # tuples + assert 'a' in Bar.__dict__ and 'a' in Baz.__dict__ + assert Bar.__dict__['a'] is Baz.__dict__['a'] + assert 'b' in Bar.__dict__ and 'b' in Baz.__dict__ + assert Bar.__dict__['b'] is not Baz.__dict__['b'] + assert 'c' in Bar.__dict__ and 'c' in Baz.__dict__ + assert Bar.__dict__['c'] is not Baz.__dict__['c'] + # None + assert 'n' in Bar.__dict__ and 'n' in Baz.__dict__ + assert Bar.__dict__['n'] is Baz.__dict__['n'] + + +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() + test_inherit() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py b/.venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py new file mode 100644 index 0000000..0da145c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) +# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +test pickling a dataclass +""" + +import dill +import dataclasses + +def test_dataclasses(): + # Issue #500 + @dataclasses.dataclass + class A: + x: int + y: str + + @dataclasses.dataclass + class B: + a: A + + a = A(1, "test") + before = B(a) + save = dill.dumps(before) + after = dill.loads(save) + assert before != after # classes don't match + assert before == B(A(**dataclasses.asdict(after.a))) + assert dataclasses.asdict(before) == dataclasses.asdict(after) + +if __name__ == '__main__': + test_dataclasses() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_detect.py b/.venv/lib/python3.10/site-packages/dill/tests/test_detect.py new file mode 100644 index 0000000..b0a71c2 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_detect.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +from dill.detect import baditems, badobjects, badtypes, errors, parent, at, globalvars +from dill import settings +from dill._dill import IS_PYPY +from pickle import PicklingError + +import inspect +import sys +import os + +def test_bad_things(): + f = inspect.currentframe() + assert baditems(f) == [f] + #assert baditems(globals()) == [f] #XXX + assert badobjects(f) is f + assert badtypes(f) == type(f) + assert type(errors(f)) is TypeError + d = badtypes(f, 1) + assert isinstance(d, dict) + assert list(badobjects(f, 1).keys()) == list(d.keys()) + assert list(errors(f, 1).keys()) == list(d.keys()) + s = set([(err.__class__.__name__,err.args[0]) for err in list(errors(f, 1).values())]) + a = dict(s) + if not os.environ.get('COVERAGE'): #XXX: travis-ci + proxy = 0 if type(f.f_locals) is dict else 1 + assert len(s) == len(a) + proxy # TypeError (and possibly PicklingError) + n = 2 + assert len(a) is n if 'PicklingError' in a.keys() else n-1 + +def test_parent(): + x = [4,5,6,7] + listiter = iter(x) + obj = parent(listiter, list) + assert obj is x + + if IS_PYPY: assert parent(obj, int) is None + else: assert parent(obj, int) is x[-1] # python oddly? finds last int + assert at(id(at)) is at + +a, b, c = 1, 2, 3 + +def squared(x): + return a+x**2 + +def foo(x): + def bar(y): + return squared(x)+y + return bar + +class _class: + def _method(self): + pass + def ok(self): + return True + +def test_globals(): + def f(): + a + def g(): + b + def h(): + c + assert globalvars(f) == dict(a=1, b=2, c=3) + + res = globalvars(foo, recurse=True) + assert set(res) == set(['squared', 'a']) + res = globalvars(foo, recurse=False) + assert res == {} + zap = foo(2) + res = globalvars(zap, recurse=True) + assert set(res) == set(['squared', 'a']) + res = globalvars(zap, recurse=False) + assert set(res) == set(['squared']) + del zap + res = globalvars(squared) + assert set(res) == set(['a']) + # FIXME: should find referenced __builtins__ + #res = globalvars(_class, recurse=True) + #assert set(res) == set(['True']) + #res = globalvars(_class, recurse=False) + #assert res == {} + #res = globalvars(_class.ok, recurse=True) + #assert set(res) == set(['True']) + #res = globalvars(_class.ok, recurse=False) + #assert set(res) == set(['True']) + + +#98 dill ignores __getstate__ in interactive lambdas +bar = [0] + +class Foo(object): + def __init__(self): + pass + def __getstate__(self): + bar[0] = bar[0]+1 + return {} + def __setstate__(self, data): + pass + +f = Foo() + +def test_getstate(): + from dill import dumps, loads + dumps(f) + b = bar[0] + dumps(lambda: f, recurse=False) # doesn't call __getstate__ + assert bar[0] == b + dumps(lambda: f, recurse=True) # calls __getstate__ + assert bar[0] == b + 1 + +#97 serialize lambdas in test files +def test_deleted(): + global sin + from dill import dumps, loads + from math import sin, pi + + def sinc(x): + return sin(x)/x + + settings['recurse'] = True + _sinc = dumps(sinc) + sin = globals().pop('sin') + sin = 1 + del sin + sinc_ = loads(_sinc) # no NameError... pickling preserves 'sin' + res = sinc_(1) + from math import sin + assert sinc(1) == res + + +def test_lambdify(): + try: + from sympy import symbols, lambdify + except ImportError: + return + settings['recurse'] = True + x = symbols("x") + y = x**2 + f = lambdify([x], y) + z = min + d = globals() + globalvars(f, recurse=True, builtin=True) + assert z is min + assert d is globals() + + +if __name__ == '__main__': + test_bad_things() + test_parent() + test_globals() + test_getstate() + test_deleted() + test_lambdify() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py b/.venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py new file mode 100644 index 0000000..bb904b1 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) +# Copyright (c) 2021-2025 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 dill._dill import OLD310, MAPPING_PROXY_TRICK, DictProxyType + +def test_dictproxy(): + assert dill.copy(DictProxyType({'a': 2})) + +def test_dictviews(): + x = {'a': 1} + assert dill.copy(x.keys()) + assert dill.copy(x.values()) + assert dill.copy(x.items()) + +def test_dictproxy_trick(): + if not OLD310 and MAPPING_PROXY_TRICK: + x = {'a': 1} + all_views = (x.values(), x.items(), x.keys(), x) + seperate_views = dill.copy(all_views) + new_x = seperate_views[-1] + new_x['b'] = 2 + new_x['c'] = 1 + assert len(new_x) == 3 and len(x) == 1 + assert len(seperate_views[0]) == 3 and len(all_views[0]) == 1 + assert len(seperate_views[1]) == 3 and len(all_views[1]) == 1 + assert len(seperate_views[2]) == 3 and len(all_views[2]) == 1 + assert dict(all_views[1]) == x + assert dict(seperate_views[1]) == new_x + +if __name__ == '__main__': + test_dictproxy() + test_dictviews() + test_dictproxy_trick() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_diff.py b/.venv/lib/python3.10/site-packages/dill/tests/test_diff.py new file mode 100644 index 0000000..16d821d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_diff.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +from dill import __diff as diff + +import sys +IS_PYPY = not hasattr(sys, 'getrefcount') + +class A: + pass + +def test_diff(): + a = A() + b = A() + c = A() + a.a = b + b.a = c + diff.memorise(a) + assert not diff.has_changed(a) + c.a = 1 + assert diff.has_changed(a) + diff.memorise(c, force=True) + assert not diff.has_changed(a) + c.a = 2 + assert diff.has_changed(a) + changed = diff.whats_changed(a) + assert list(changed[0].keys()) == ["a"] + assert not changed[1] + + a2 = [] + b2 = [a2] + c2 = [b2] + diff.memorise(c2) + assert not diff.has_changed(c2) + a2.append(1) + assert diff.has_changed(c2) + changed = diff.whats_changed(c2) + assert changed[0] == {} + assert changed[1] + + a3 = {} + b3 = {1: a3} + c3 = {1: b3} + diff.memorise(c3) + assert not diff.has_changed(c3) + a3[1] = 1 + assert diff.has_changed(c3) + changed = diff.whats_changed(c3) + assert changed[0] == {} + assert changed[1] + + if not IS_PYPY: + import abc + # make sure the "_abc_invaldation_counter" doesn't make test fail + diff.memorise(abc.ABCMeta, force=True) + assert not diff.has_changed(abc) + abc.ABCMeta.zzz = 1 + assert diff.has_changed(abc) + changed = diff.whats_changed(abc) + assert list(changed[0].keys()) == ["ABCMeta"] + assert not changed[1] + + ''' + import Queue + diff.memorise(Queue, force=True) + assert not diff.has_changed(Queue) + Queue.Queue.zzz = 1 + assert diff.has_changed(Queue) + changed = diff.whats_changed(Queue) + assert list(changed[0].keys()) == ["Queue"] + assert not changed[1] + + import math + diff.memorise(math, force=True) + assert not diff.has_changed(math) + math.zzz = 1 + assert diff.has_changed(math) + changed = diff.whats_changed(math) + assert list(changed[0].keys()) == ["zzz"] + assert not changed[1] + ''' + + a = A() + b = A() + c = A() + a.a = b + b.a = c + diff.memorise(a) + assert not diff.has_changed(a) + c.a = 1 + assert diff.has_changed(a) + diff.memorise(c, force=True) + assert not diff.has_changed(a) + del c.a + assert diff.has_changed(a) + changed = diff.whats_changed(a) + assert list(changed[0].keys()) == ["a"] + assert not changed[1] + + +if __name__ == '__main__': + test_diff() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py b/.venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py new file mode 100644 index 0000000..2d66e54 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 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 as pickle +from io import BytesIO as StringIO + + +def my_fn(x): + return x * 17 + + +def test_extend(): + obj = lambda : my_fn(34) + assert obj() == 578 + + obj_io = StringIO() + pickler = pickle.Pickler(obj_io) + pickler.dump(obj) + + obj_str = obj_io.getvalue() + + obj2_io = StringIO(obj_str) + unpickler = pickle.Unpickler(obj2_io) + obj2 = unpickler.load() + + assert obj2() == 578 + + +def test_isdill(): + obj_io = StringIO() + pickler = pickle.Pickler(obj_io) + assert pickle._dill.is_dill(pickler) is True + + pickler = pickle._dill.StockPickler(obj_io) + assert pickle._dill.is_dill(pickler) is False + + try: + import multiprocess as mp + pickler = mp.reduction.ForkingPickler(obj_io) + assert pickle._dill.is_dill(pickler, child=True) is True + assert pickle._dill.is_dill(pickler, child=False) is False + except Exception: + pass + + +if __name__ == '__main__': + test_extend() + test_isdill() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py b/.venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py new file mode 100644 index 0000000..45e4166 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2021-2025 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 +dill.settings['recurse'] = True + +def get_fun_with_strftime(): + def fun_with_strftime(): + import datetime + return datetime.datetime.strptime("04-01-1943", "%d-%m-%Y").strftime( + "%Y-%m-%d %H:%M:%S" + ) + return fun_with_strftime + + +def get_fun_with_strftime2(): + import datetime + return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + +def test_doc_dill_issue_219(): + back_fn = dill.loads(dill.dumps(get_fun_with_strftime())) + assert back_fn() == "1943-01-04 00:00:00" + dupl = dill.loads(dill.dumps(get_fun_with_strftime2)) + assert dupl() == get_fun_with_strftime2() + + +def get_fun_with_internal_import(): + def fun_with_import(): + import re + return re.compile("$") + return fun_with_import + + +def test_method_with_internal_import_should_work(): + import re + back_fn = dill.loads(dill.dumps(get_fun_with_internal_import())) + import inspect + if hasattr(inspect, 'getclosurevars'): + vars = inspect.getclosurevars(back_fn) + assert vars.globals == {} + assert vars.nonlocals == {} + assert back_fn() == re.compile("$") + assert "__builtins__" in back_fn.__globals__ + + +if __name__ == "__main__": + import sys + if (sys.version_info[:3] != (3,10,0) or sys.version_info[3] != 'alpha'): + test_doc_dill_issue_219() + test_method_with_internal_import_should_work() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_file.py b/.venv/lib/python3.10/site-packages/dill/tests/test_file.py new file mode 100644 index 0000000..4a9bac3 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_file.py @@ -0,0 +1,500 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import os +import sys +import string +import random + +import dill + + +dill.settings['recurse'] = True + +fname = "_test_file.txt" +rand_chars = list(string.ascii_letters) + ["\n"] * 40 # bias newline + +buffer_error = ValueError("invalid buffer size") +dne_error = FileNotFoundError("[Errno 2] No such file or directory: '%s'" % fname) + + +def write_randomness(number=200): + f = open(fname, "w") + for i in range(number): + f.write(random.choice(rand_chars)) + f.close() + f = open(fname, "r") + contents = f.read() + f.close() + return contents + + +def trunc_file(): + open(fname, "w").close() + + +def throws(op, args, exc): + try: + op(*args) + except type(exc): + return sys.exc_info()[1].args == exc.args + else: + return False + + +def teardown_module(): + if os.path.exists(fname): + os.remove(fname) + + +def bench(strictio, fmode, skippypy): + import platform + if skippypy and platform.python_implementation() == 'PyPy': + # Skip for PyPy... + return + + # file exists, with same contents + # read + + write_randomness() + + f = open(fname, "r") + _f = dill.loads(dill.dumps(f, fmode=fmode))#, strictio=strictio)) + assert _f.mode == f.mode + assert _f.tell() == f.tell() + assert _f.read() == f.read() + f.close() + _f.close() + + # write + + f = open(fname, "w") + f.write("hello") + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + f1mode = f.mode + ftell = f.tell() + f.close() + f2 = dill.loads(f_dumped) #FIXME: fails due to pypy/issues/1233 + # TypeError: expected py_object instance instead of str + f2mode = f2.mode + f2tell = f2.tell() + f2name = f2.name + f2.write(" world!") + f2.close() + + if fmode == dill.HANDLE_FMODE: + assert open(fname).read() == " world!" + assert f2mode == f1mode + assert f2tell == 0 + elif fmode == dill.CONTENTS_FMODE: + assert open(fname).read() == "hello world!" + assert f2mode == f1mode + assert f2tell == ftell + assert f2name == fname + elif fmode == dill.FILE_FMODE: + assert open(fname).read() == "hello world!" + assert f2mode == f1mode + assert f2tell == ftell + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + + # append + + trunc_file() + + f = open(fname, "a") + f.write("hello") + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + f1mode = f.mode + ftell = f.tell() + f.close() + f2 = dill.loads(f_dumped) + f2mode = f2.mode + f2tell = f2.tell() + f2.write(" world!") + f2.close() + + assert f2mode == f1mode + if fmode == dill.CONTENTS_FMODE: + assert open(fname).read() == "hello world!" + assert f2tell == ftell + elif fmode == dill.HANDLE_FMODE: + assert open(fname).read() == "hello world!" + assert f2tell == ftell + elif fmode == dill.FILE_FMODE: + assert open(fname).read() == "hello world!" + assert f2tell == ftell + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + + # file exists, with different contents (smaller size) + # read + + write_randomness() + + f = open(fname, "r") + fstr = f.read() + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + f1mode = f.mode + ftell = f.tell() + f.close() + _flen = 150 + _fstr = write_randomness(number=_flen) + + if strictio: # throw error if ftell > EOF + assert throws(dill.loads, (f_dumped,), buffer_error) + else: + f2 = dill.loads(f_dumped) + assert f2.mode == f1mode + if fmode == dill.CONTENTS_FMODE: + assert f2.tell() == _flen + assert f2.read() == "" + f2.seek(0) + assert f2.read() == _fstr + assert f2.tell() == _flen # 150 + elif fmode == dill.HANDLE_FMODE: + assert f2.tell() == 0 + assert f2.read() == _fstr + assert f2.tell() == _flen # 150 + elif fmode == dill.FILE_FMODE: + assert f2.tell() == ftell # 200 + assert f2.read() == "" + f2.seek(0) + assert f2.read() == fstr + assert f2.tell() == ftell # 200 + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + f2.close() + + # write + + write_randomness() + + f = open(fname, "w") + f.write("hello") + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + f1mode = f.mode + ftell = f.tell() + f.close() + fstr = open(fname).read() + + f = open(fname, "w") + f.write("h") + _ftell = f.tell() + f.close() + + if strictio: # throw error if ftell > EOF + assert throws(dill.loads, (f_dumped,), buffer_error) + else: + f2 = dill.loads(f_dumped) + f2mode = f2.mode + f2tell = f2.tell() + f2.write(" world!") + f2.close() + if fmode == dill.CONTENTS_FMODE: + assert open(fname).read() == "h world!" + assert f2mode == f1mode + assert f2tell == _ftell + elif fmode == dill.HANDLE_FMODE: + assert open(fname).read() == " world!" + assert f2mode == f1mode + assert f2tell == 0 + elif fmode == dill.FILE_FMODE: + assert open(fname).read() == "hello world!" + assert f2mode == f1mode + assert f2tell == ftell + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + f2.close() + + # append + + trunc_file() + + f = open(fname, "a") + f.write("hello") + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + f1mode = f.mode + ftell = f.tell() + f.close() + fstr = open(fname).read() + + f = open(fname, "w") + f.write("h") + _ftell = f.tell() + f.close() + + if strictio: # throw error if ftell > EOF + assert throws(dill.loads, (f_dumped,), buffer_error) + else: + f2 = dill.loads(f_dumped) + f2mode = f2.mode + f2tell = f2.tell() + f2.write(" world!") + f2.close() + assert f2mode == f1mode + if fmode == dill.CONTENTS_FMODE: + # position of writes cannot be changed on some OSs + assert open(fname).read() == "h world!" + assert f2tell == _ftell + elif fmode == dill.HANDLE_FMODE: + assert open(fname).read() == "h world!" + assert f2tell == _ftell + elif fmode == dill.FILE_FMODE: + assert open(fname).read() == "hello world!" + assert f2tell == ftell + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + f2.close() + + # file does not exist + # read + + write_randomness() + + f = open(fname, "r") + fstr = f.read() + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + f1mode = f.mode + ftell = f.tell() + f.close() + + os.remove(fname) + + if strictio: # throw error if file DNE + assert throws(dill.loads, (f_dumped,), dne_error) + else: + f2 = dill.loads(f_dumped) + assert f2.mode == f1mode + if fmode == dill.CONTENTS_FMODE: + # FIXME: this fails on systems where f2.tell() always returns 0 + # assert f2.tell() == ftell # 200 + assert f2.read() == "" + f2.seek(0) + assert f2.read() == "" + assert f2.tell() == 0 + elif fmode == dill.FILE_FMODE: + assert f2.tell() == ftell # 200 + assert f2.read() == "" + f2.seek(0) + assert f2.read() == fstr + assert f2.tell() == ftell # 200 + elif fmode == dill.HANDLE_FMODE: + assert f2.tell() == 0 + assert f2.read() == "" + assert f2.tell() == 0 + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + f2.close() + + # write + + write_randomness() + + f = open(fname, "w+") + f.write("hello") + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + ftell = f.tell() + f1mode = f.mode + f.close() + + os.remove(fname) + + if strictio: # throw error if file DNE + assert throws(dill.loads, (f_dumped,), dne_error) + else: + f2 = dill.loads(f_dumped) + f2mode = f2.mode + f2tell = f2.tell() + f2.write(" world!") + f2.close() + if fmode == dill.CONTENTS_FMODE: + assert open(fname).read() == " world!" + assert f2mode == 'w+' + assert f2tell == 0 + elif fmode == dill.HANDLE_FMODE: + assert open(fname).read() == " world!" + assert f2mode == f1mode + assert f2tell == 0 + elif fmode == dill.FILE_FMODE: + assert open(fname).read() == "hello world!" + assert f2mode == f1mode + assert f2tell == ftell + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + + # append + + trunc_file() + + f = open(fname, "a") + f.write("hello") + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + ftell = f.tell() + f1mode = f.mode + f.close() + + os.remove(fname) + + if strictio: # throw error if file DNE + assert throws(dill.loads, (f_dumped,), dne_error) + else: + f2 = dill.loads(f_dumped) + f2mode = f2.mode + f2tell = f2.tell() + f2.write(" world!") + f2.close() + assert f2mode == f1mode + if fmode == dill.CONTENTS_FMODE: + assert open(fname).read() == " world!" + assert f2tell == 0 + elif fmode == dill.HANDLE_FMODE: + assert open(fname).read() == " world!" + assert f2tell == 0 + elif fmode == dill.FILE_FMODE: + assert open(fname).read() == "hello world!" + assert f2tell == ftell + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + + # file exists, with different contents (larger size) + # read + + write_randomness() + + f = open(fname, "r") + fstr = f.read() + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + f1mode = f.mode + ftell = f.tell() + f.close() + _flen = 250 + _fstr = write_randomness(number=_flen) + + # XXX: no safe_file: no way to be 'safe'? + + f2 = dill.loads(f_dumped) + assert f2.mode == f1mode + if fmode == dill.CONTENTS_FMODE: + assert f2.tell() == ftell # 200 + assert f2.read() == _fstr[ftell:] + f2.seek(0) + assert f2.read() == _fstr + assert f2.tell() == _flen # 250 + elif fmode == dill.HANDLE_FMODE: + assert f2.tell() == 0 + assert f2.read() == _fstr + assert f2.tell() == _flen # 250 + elif fmode == dill.FILE_FMODE: + assert f2.tell() == ftell # 200 + assert f2.read() == "" + f2.seek(0) + assert f2.read() == fstr + assert f2.tell() == ftell # 200 + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + f2.close() # XXX: other alternatives? + + # write + + f = open(fname, "w") + f.write("hello") + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + f1mode = f.mode + ftell = f.tell() + + fstr = open(fname).read() + + f.write(" and goodbye!") + _ftell = f.tell() + f.close() + + # XXX: no safe_file: no way to be 'safe'? + + f2 = dill.loads(f_dumped) + f2mode = f2.mode + f2tell = f2.tell() + f2.write(" world!") + f2.close() + if fmode == dill.CONTENTS_FMODE: + assert open(fname).read() == "hello world!odbye!" + assert f2mode == f1mode + assert f2tell == ftell + elif fmode == dill.HANDLE_FMODE: + assert open(fname).read() == " world!" + assert f2mode == f1mode + assert f2tell == 0 + elif fmode == dill.FILE_FMODE: + assert open(fname).read() == "hello world!" + assert f2mode == f1mode + assert f2tell == ftell + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + f2.close() + + # append + + trunc_file() + + f = open(fname, "a") + f.write("hello") + f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) + f1mode = f.mode + ftell = f.tell() + fstr = open(fname).read() + + f.write(" and goodbye!") + _ftell = f.tell() + f.close() + + # XXX: no safe_file: no way to be 'safe'? + + f2 = dill.loads(f_dumped) + f2mode = f2.mode + f2tell = f2.tell() + f2.write(" world!") + f2.close() + assert f2mode == f1mode + if fmode == dill.CONTENTS_FMODE: + assert open(fname).read() == "hello and goodbye! world!" + assert f2tell == ftell + elif fmode == dill.HANDLE_FMODE: + assert open(fname).read() == "hello and goodbye! world!" + assert f2tell == _ftell + elif fmode == dill.FILE_FMODE: + assert open(fname).read() == "hello world!" + assert f2tell == ftell + else: + raise RuntimeError("Unknown file mode '%s'" % fmode) + f2.close() + + +def test_nostrictio_handlefmode(): + bench(False, dill.HANDLE_FMODE, False) + teardown_module() + + +def test_nostrictio_filefmode(): + bench(False, dill.FILE_FMODE, False) + teardown_module() + + +def test_nostrictio_contentsfmode(): + bench(False, dill.CONTENTS_FMODE, True) + teardown_module() + + +#bench(True, dill.HANDLE_FMODE, False) +#bench(True, dill.FILE_FMODE, False) +#bench(True, dill.CONTENTS_FMODE, True) + + +if __name__ == '__main__': + test_nostrictio_handlefmode() + test_nostrictio_filefmode() + test_nostrictio_contentsfmode() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_functions.py b/.venv/lib/python3.10/site-packages/dill/tests/test_functions.py new file mode 100644 index 0000000..463b135 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_functions.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2019-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import functools +import dill +import sys +dill.settings['recurse'] = True + + +def function_a(a): + return a + + +def function_b(b, b1): + return b + b1 + + +def function_c(c, c1=1): + return c + c1 + + +def function_d(d, d1, d2=1): + """doc string""" + return d + d1 + d2 + +function_d.__module__ = 'a module' + + +exec(''' +def function_e(e, *e1, e2=1, e3=2): + return e + sum(e1) + e2 + e3''') + +globalvar = 0 + +@functools.lru_cache(None) +def function_with_cache(x): + global globalvar + globalvar += x + return globalvar + + +def function_with_unassigned_variable(): + if False: + value = None + return (lambda: value) + + +def test_issue_510(): + # A very bizzare use of functions and methods that pickle doesn't get + # correctly for odd reasons. + class Foo: + def __init__(self): + def f2(self): + return self + self.f2 = f2.__get__(self) + + import dill, pickletools + f = Foo() + f1 = dill.copy(f) + assert f1.f2() is f1 + + +def test_functions(): + dumped_func_a = dill.dumps(function_a) + assert dill.loads(dumped_func_a)(0) == 0 + + dumped_func_b = dill.dumps(function_b) + assert dill.loads(dumped_func_b)(1,2) == 3 + + dumped_func_c = dill.dumps(function_c) + assert dill.loads(dumped_func_c)(1) == 2 + assert dill.loads(dumped_func_c)(1, 2) == 3 + + dumped_func_d = dill.dumps(function_d) + assert dill.loads(dumped_func_d).__doc__ == function_d.__doc__ + assert dill.loads(dumped_func_d).__module__ == function_d.__module__ + assert dill.loads(dumped_func_d)(1, 2) == 4 + assert dill.loads(dumped_func_d)(1, 2, 3) == 6 + assert dill.loads(dumped_func_d)(1, 2, d2=3) == 6 + + function_with_cache(1) + globalvar = 0 + dumped_func_cache = dill.dumps(function_with_cache) + assert function_with_cache(2) == 3 + assert function_with_cache(1) == 1 + assert function_with_cache(3) == 6 + assert function_with_cache(2) == 3 + + empty_cell = function_with_unassigned_variable() + cell_copy = dill.loads(dill.dumps(empty_cell)) + assert 'empty' in str(cell_copy.__closure__[0]) + try: + cell_copy() + except Exception: + # this is good + pass + else: + raise AssertionError('cell_copy() did not read an empty cell') + + exec(''' +dumped_func_e = dill.dumps(function_e) +assert dill.loads(dumped_func_e)(1, 2) == 6 +assert dill.loads(dumped_func_e)(1, 2, 3) == 9 +assert dill.loads(dumped_func_e)(1, 2, e2=3) == 8 +assert dill.loads(dumped_func_e)(1, 2, e2=3, e3=4) == 10 +assert dill.loads(dumped_func_e)(1, 2, 3, e2=4) == 12 +assert dill.loads(dumped_func_e)(1, 2, 3, e2=4, e3=5) == 15''') + +def test_code_object(): + import warnings + from dill._dill import ALL_CODE_PARAMS, CODE_PARAMS, CODE_VERSION, _create_code + code = function_c.__code__ + warnings.filterwarnings('ignore', category=DeprecationWarning) # issue 597 + LNOTAB = getattr(code, 'co_lnotab', b'') + if warnings.filters: del warnings.filters[0] + fields = {f: getattr(code, 'co_'+f) for f in CODE_PARAMS} + fields.setdefault('posonlyargcount', 0) # python >= 3.8 + fields.setdefault('lnotab', LNOTAB) # python <= 3.9 + fields.setdefault('linetable', b'') # python >= 3.10 + fields.setdefault('qualname', fields['name']) # python >= 3.11 + fields.setdefault('exceptiontable', b'') # python >= 3.11 + fields.setdefault('endlinetable', None) # python == 3.11a + fields.setdefault('columntable', None) # python == 3.11a + + for version, _, params in ALL_CODE_PARAMS: + args = tuple(fields[p] for p in params.split()) + try: + _create_code(*args) + if version >= (3,10): + _create_code(fields['lnotab'], *args) + except Exception as error: + raise Exception("failed to construct code object with format version {}".format(version)) from error + +if __name__ == '__main__': + test_functions() + test_issue_510() + test_code_object() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_functors.py b/.venv/lib/python3.10/site-packages/dill/tests/test_functors.py new file mode 100644 index 0000000..8bc4124 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_functors.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import functools +import dill +dill.settings['recurse'] = True + + +def f(a, b, c): # without keywords + pass + + +def g(a, b, c=2): # with keywords + pass + + +def h(a=1, b=2, c=3): # without args + pass + + +def test_functools(): + fp = functools.partial(f, 1, 2) + gp = functools.partial(g, 1, c=2) + hp = functools.partial(h, 1, c=2) + bp = functools.partial(int, base=2) + + assert dill.pickles(fp, safe=True) + assert dill.pickles(gp, safe=True) + assert dill.pickles(hp, safe=True) + assert dill.pickles(bp, safe=True) + + +if __name__ == '__main__': + test_functools() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_logger.py b/.venv/lib/python3.10/site-packages/dill/tests/test_logger.py new file mode 100644 index 0000000..b46a96a --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_logger.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +# Author: Leonardo Gama (@leogama) +# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import logging +import re +import tempfile + +import dill +from dill import detect +from dill.logger import stderr_handler, adapter as logger + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +test_obj = {'a': (1, 2), 'b': object(), 'f': lambda x: x**2, 'big': list(range(10))} + +def test_logging(should_trace): + buffer = StringIO() + handler = logging.StreamHandler(buffer) + logger.addHandler(handler) + try: + dill.dumps(test_obj) + if should_trace: + regex = re.compile(r'(\S*┬ \w.*[^)]' # begin pickling object + r'|│*└ # \w.* \[\d+ (\wi)?B])' # object written (with size) + ) + for line in buffer.getvalue().splitlines(): + assert regex.fullmatch(line) + return buffer.getvalue() + else: + assert buffer.getvalue() == "" + finally: + logger.removeHandler(handler) + buffer.close() + +def test_trace_to_file(stream_trace): + file = tempfile.NamedTemporaryFile(mode='r') + with detect.trace(file.name, mode='w'): + dill.dumps(test_obj) + file_trace = file.read() + file.close() + # Apparently, objects can change location in memory... + reghex = re.compile(r'0x[0-9A-Za-z]+') + file_trace, stream_trace = reghex.sub('0x', file_trace), reghex.sub('0x', stream_trace) + # PyPy prints dictionary contents with repr(dict)... + regdict = re.compile(r'(dict\.__repr__ of ).*') + file_trace, stream_trace = regdict.sub(r'\1{}>', file_trace), regdict.sub(r'\1{}>', stream_trace) + assert file_trace == stream_trace + +if __name__ == '__main__': + logger.removeHandler(stderr_handler) + test_logging(should_trace=False) + detect.trace(True) + test_logging(should_trace=True) + detect.trace(False) + test_logging(should_trace=False) + + loglevel = logging.ERROR + logger.setLevel(loglevel) + with detect.trace(): + stream_trace = test_logging(should_trace=True) + test_logging(should_trace=False) + assert logger.getEffectiveLevel() == loglevel + test_trace_to_file(stream_trace) diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_mixins.py b/.venv/lib/python3.10/site-packages/dill/tests/test_mixins.py new file mode 100644 index 0000000..9a54bad --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_mixins.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 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 +dill.settings['recurse'] = True + + +def wtf(x,y,z): + def zzz(): + return x + def yyy(): + return y + def xxx(): + return z + return zzz,yyy + + +def quad(a=1, b=1, c=0): + inverted = [False] + def invert(): + inverted[0] = not inverted[0] + def dec(f): + def func(*args, **kwds): + x = f(*args, **kwds) + if inverted[0]: x = -x + return a*x**2 + b*x + c + func.__wrapped__ = f + func.invert = invert + func.inverted = inverted + return func + return dec + + +@quad(a=0,b=2) +def double_add(*args): + return sum(args) + + +fx = sum([1,2,3]) + + +### to make it interesting... +def quad_factory(a=1,b=1,c=0): + def dec(f): + def func(*args,**kwds): + fx = f(*args,**kwds) + return a*fx**2 + b*fx + c + return func + return dec + + +@quad_factory(a=0,b=4,c=0) +def quadish(x): + return x+1 + + +quadratic = quad_factory() + + +def doubler(f): + def inner(*args, **kwds): + fx = f(*args, **kwds) + return 2*fx + return inner + + +@doubler +def quadruple(x): + return 2*x + + +def test_mixins(): + # test mixins + assert double_add(1,2,3) == 2*fx + double_add.invert() + assert double_add(1,2,3) == -2*fx + + _d = dill.copy(double_add) + assert _d(1,2,3) == -2*fx + #_d.invert() #FIXME: fails seemingly randomly + #assert _d(1,2,3) == 2*fx + + assert _d.__wrapped__(1,2,3) == fx + + # XXX: issue or feature? in python3.4, inverted is linked through copy + if not double_add.inverted[0]: + double_add.invert() + + # test some stuff from source and pointers + ds = dill.source + dd = dill.detect + assert ds.getsource(dd.freevars(quadish)['f']) == '@quad_factory(a=0,b=4,c=0)\ndef quadish(x):\n return x+1\n' + assert ds.getsource(dd.freevars(quadruple)['f']) == '@doubler\ndef quadruple(x):\n return 2*x\n' + assert ds.importable(quadish, source=False) == 'from %s import quadish\n' % __name__ + assert ds.importable(quadruple, source=False) == 'from %s import quadruple\n' % __name__ + assert ds.importable(quadratic, source=False) == 'from %s import quadratic\n' % __name__ + assert ds.importable(double_add, source=False) == 'from %s import double_add\n' % __name__ + assert ds.importable(quadruple, source=True) == 'def doubler(f):\n def inner(*args, **kwds):\n fx = f(*args, **kwds)\n return 2*fx\n return inner\n\n@doubler\ndef quadruple(x):\n return 2*x\n' + #***** #FIXME: this needs work + result = ds.importable(quadish, source=True) + a,b,c,_,result = result.split('\n',4) + assert result == 'def quad_factory(a=1,b=1,c=0):\n def dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n return dec\n\n@quad_factory(a=0,b=4,c=0)\ndef quadish(x):\n return x+1\n' + assert set([a,b,c]) == set(['a = 0', 'c = 0', 'b = 4']) + result = ds.importable(quadratic, source=True) + a,b,c,result = result.split('\n',3) + assert result == '\ndef dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n' + assert set([a,b,c]) == set(['a = 1', 'c = 0', 'b = 1']) + result = ds.importable(double_add, source=True) + a,b,c,d,_,result = result.split('\n',5) + assert result == 'def quad(a=1, b=1, c=0):\n inverted = [False]\n def invert():\n inverted[0] = not inverted[0]\n def dec(f):\n def func(*args, **kwds):\n x = f(*args, **kwds)\n if inverted[0]: x = -x\n return a*x**2 + b*x + c\n func.__wrapped__ = f\n func.invert = invert\n func.inverted = inverted\n return func\n return dec\n\n@quad(a=0,b=2)\ndef double_add(*args):\n return sum(args)\n' + assert set([a,b,c,d]) == set(['a = 0', 'c = 0', 'b = 2', 'inverted = [True]']) + #***** + + +if __name__ == '__main__': + test_mixins() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_module.py b/.venv/lib/python3.10/site-packages/dill/tests/test_module.py new file mode 100644 index 0000000..beec0c6 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_module.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import sys +import dill +import test_mixins as module +from importlib import reload +dill.settings['recurse'] = True + +cached = (module.__cached__ if hasattr(module, "__cached__") + else module.__file__.split(".", 1)[0] + ".pyc") + +module.a = 1234 + +pik_mod = dill.dumps(module) + +module.a = 0 + +# remove module +del sys.modules[module.__name__] +del module + +module = dill.loads(pik_mod) +def test_attributes(): + #assert hasattr(module, "a") and module.a == 1234 #FIXME: -m dill.tests + assert module.double_add(1, 2, 3) == 2 * module.fx + +# Restart, and test use_diff + +reload(module) + +try: + dill.use_diff() + + module.a = 1234 + + pik_mod = dill.dumps(module) + + module.a = 0 + + # remove module + del sys.modules[module.__name__] + del module + + module = dill.loads(pik_mod) + def test_diff_attributes(): + assert hasattr(module, "a") and module.a == 1234 + assert module.double_add(1, 2, 3) == 2 * module.fx + +except AttributeError: + def test_diff_attributes(): + pass + +# clean up +import os +if os.path.exists(cached): + os.remove(cached) +pycache = os.path.join(os.path.dirname(module.__file__), "__pycache__") +if os.path.exists(pycache) and not os.listdir(pycache): + os.removedirs(pycache) + + +# test when module is None +import math + +def get_lambda(str, **kwarg): + return eval(str, kwarg, None) + +obj = get_lambda('lambda x: math.exp(x)', math=math) + +def test_module_is_none(): + assert obj.__module__ is None + assert dill.copy(obj)(3) == obj(3) + + +if __name__ == '__main__': + test_attributes() + test_diff_attributes() + test_module_is_none() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py b/.venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py new file mode 100644 index 0000000..477ba1b --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 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 +dill.settings['recurse'] = True + +def f(func): + def w(*args): + return f(*args) + return w + +@f +def f2(): pass + +# check when __main__ and on import +def test_decorated(): + assert dill.pickles(f2) + + +import doctest +import logging +logging.basicConfig(level=logging.DEBUG) + +class SomeUnreferencedUnpicklableClass(object): + def __reduce__(self): + raise Exception + +unpicklable = SomeUnreferencedUnpicklableClass() + +# This works fine outside of Doctest: +def test_normal(): + serialized = dill.dumps(lambda x: x) + +# should not try to pickle unpicklable object in __globals__ +def tests(): + """ + >>> serialized = dill.dumps(lambda x: x) + """ + return + +#print("\n\nRunning Doctest:") +def test_doctest(): + doctest.testmod() + + +if __name__ == '__main__': + test_decorated() + test_normal() + test_doctest() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_nested.py b/.venv/lib/python3.10/site-packages/dill/tests/test_nested.py new file mode 100644 index 0000000..6fd435c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_nested.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +test dill's ability to handle nested functions +""" + +import os +import math + +import dill as pickle +pickle.settings['recurse'] = True + + +# the nested function: pickle should fail here, but dill is ok. +def adder(augend): + zero = [0] + + def inner(addend): + return addend + augend + zero[0] + return inner + + +# rewrite the nested function using a class: standard pickle should work here. +class cadder(object): + def __init__(self, augend): + self.augend = augend + self.zero = [0] + + def __call__(self, addend): + return addend + self.augend + self.zero[0] + + +# rewrite again, but as an old-style class +class c2adder: + def __init__(self, augend): + self.augend = augend + self.zero = [0] + + def __call__(self, addend): + return addend + self.augend + self.zero[0] + + +# some basic class stuff +class basic(object): + pass + + +class basic2: + pass + + +x = 5 +y = 1 + + +def test_basic(): + a = [0, 1, 2] + pa = pickle.dumps(a) + pmath = pickle.dumps(math) #XXX: FAILS in pickle + pmap = pickle.dumps(map) + # ... + la = pickle.loads(pa) + lmath = pickle.loads(pmath) + lmap = pickle.loads(pmap) + assert list(map(math.sin, a)) == list(lmap(lmath.sin, la)) + + +def test_basic_class(): + pbasic2 = pickle.dumps(basic2) + _pbasic2 = pickle.loads(pbasic2)() + pbasic = pickle.dumps(basic) + _pbasic = pickle.loads(pbasic)() + + +def test_c2adder(): + pc2adder = pickle.dumps(c2adder) + pc2add5 = pickle.loads(pc2adder)(x) + assert pc2add5(y) == x+y + + +def test_pickled_cadder(): + pcadder = pickle.dumps(cadder) + pcadd5 = pickle.loads(pcadder)(x) + assert pcadd5(y) == x+y + + +def test_raw_adder_and_inner(): + add5 = adder(x) + assert add5(y) == x+y + + +def test_pickled_adder(): + padder = pickle.dumps(adder) + padd5 = pickle.loads(padder)(x) + assert padd5(y) == x+y + + +def test_pickled_inner(): + add5 = adder(x) + pinner = pickle.dumps(add5) #XXX: FAILS in pickle + p5add = pickle.loads(pinner) + assert p5add(y) == x+y + + +def test_moduledict_where_not_main(): + try: + from . import test_moduledict + except ImportError: + import test_moduledict + name = 'test_moduledict.py' + if os.path.exists(name) and os.path.exists(name+'c'): + os.remove(name+'c') + + if os.path.exists(name) and hasattr(test_moduledict, "__cached__") \ + and os.path.exists(test_moduledict.__cached__): + os.remove(getattr(test_moduledict, "__cached__")) + + if os.path.exists("__pycache__") and not os.listdir("__pycache__"): + os.removedirs("__pycache__") + + +if __name__ == '__main__': + test_basic() + test_basic_class() + test_c2adder() + test_pickled_cadder() + test_raw_adder_and_inner() + test_pickled_adder() + test_pickled_inner() + test_moduledict_where_not_main() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_objects.py b/.venv/lib/python3.10/site-packages/dill/tests/test_objects.py new file mode 100644 index 0000000..d26a7e8 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_objects.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +demonstrate dill's ability to pickle different python types +test pickling of all Python Standard Library objects (currently: CH 1-14 @ 2.7) +""" + +import dill as pickle +pickle.settings['recurse'] = True +#pickle.detect.trace(True) +#import pickle + +# get all objects for testing +from dill import load_types, objects, extend +load_types(pickleable=True,unpickleable=False) + +# uncomment the next two lines to test cloudpickle +#extend(False) +#import cloudpickle as pickle + +# helper objects +class _class: + def _method(self): + pass + +# objects that *fail* if imported +special = {} +special['LambdaType'] = _lambda = lambda x: lambda y: x +special['MethodType'] = _method = _class()._method +special['UnboundMethodType'] = _class._method +objects.update(special) + +def pickles(name, exact=False, verbose=True): + """quick check if object pickles with dill""" + obj = objects[name] + try: + pik = pickle.loads(pickle.dumps(obj)) + if exact: + try: + assert pik == obj + except AssertionError: + assert type(obj) == type(pik) + if verbose: print ("weak: %s %s" % (name, type(obj))) + else: + assert type(obj) == type(pik) + except Exception: + if verbose: print ("fails: %s %s" % (name, type(obj))) + + +def test_objects(verbose=True): + for member in objects.keys(): + #pickles(member, exact=True, verbose=verbose) + pickles(member, exact=False, verbose=verbose) + +if __name__ == '__main__': + import warnings + warnings.simplefilter('ignore') + test_objects(verbose=False) diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_properties.py b/.venv/lib/python3.10/site-packages/dill/tests/test_properties.py new file mode 100644 index 0000000..b19bce5 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_properties.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import sys + +import dill +dill.settings['recurse'] = True + + +class Foo(object): + def __init__(self): + self._data = 1 + + def _get_data(self): + return self._data + + def _set_data(self, x): + self._data = x + + data = property(_get_data, _set_data) + + +def test_data_not_none(): + FooS = dill.copy(Foo) + assert FooS.data.fget is not None + assert FooS.data.fset is not None + assert FooS.data.fdel is None + + +def test_data_unchanged(): + FooS = dill.copy(Foo) + try: + res = FooS().data + except Exception: + e = sys.exc_info()[1] + raise AssertionError(str(e)) + else: + assert res == 1 + + +def test_data_changed(): + FooS = dill.copy(Foo) + try: + f = FooS() + f.data = 1024 + res = f.data + except Exception: + e = sys.exc_info()[1] + raise AssertionError(str(e)) + else: + assert res == 1024 + + +if __name__ == '__main__': + test_data_not_none() + test_data_unchanged() + test_data_changed() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py b/.venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py new file mode 100644 index 0000000..aa315ba --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) +# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +test pickling a PyCapsule object +""" + +import dill +import warnings + +test_pycapsule = None + +if dill._dill._testcapsule is not None: + import ctypes + def test_pycapsule(): + name = ctypes.create_string_buffer(b'dill._testcapsule') + capsule = dill._dill._PyCapsule_New( + ctypes.cast(dill._dill._PyCapsule_New, ctypes.c_void_p), + name, + None + ) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + dill.copy(capsule) + dill._testcapsule = capsule + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + dill.copy(capsule) + dill._testcapsule = None + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", dill.PicklingWarning) + dill.copy(capsule) + except dill.UnpicklingError: + pass + else: + raise AssertionError("Expected a different error") + +if __name__ == '__main__': + if test_pycapsule is not None: + test_pycapsule() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_recursive.py b/.venv/lib/python3.10/site-packages/dill/tests/test_recursive.py new file mode 100644 index 0000000..d7542ff --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_recursive.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2019-2025 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 functools import partial +import warnings + + +def copy(obj, byref=False, recurse=False): + if byref: + try: + return dill.copy(obj, byref=byref, recurse=recurse) + except Exception: + pass + else: + raise AssertionError('Copy of %s with byref=True should have given a warning!' % (obj,)) + + warnings.simplefilter('ignore') + val = dill.copy(obj, byref=byref, recurse=recurse) + warnings.simplefilter('error') + return val + else: + return dill.copy(obj, byref=byref, recurse=recurse) + + +class obj1(object): + def __init__(self): + super(obj1, self).__init__() + +class obj2(object): + def __init__(self): + super(obj2, self).__init__() + +class obj3(object): + super_ = super + def __init__(self): + obj3.super_(obj3, self).__init__() + + +def test_super(): + assert copy(obj1(), byref=True) + assert copy(obj1(), byref=True, recurse=True) + assert copy(obj1(), recurse=True) + assert copy(obj1()) + + assert copy(obj2(), byref=True) + assert copy(obj2(), byref=True, recurse=True) + assert copy(obj2(), recurse=True) + assert copy(obj2()) + + assert copy(obj3(), byref=True) + assert copy(obj3(), byref=True, recurse=True) + assert copy(obj3(), recurse=True) + assert copy(obj3()) + + +def get_trigger(model): + pass + +class Machine(object): + def __init__(self): + self.child = Model() + self.trigger = partial(get_trigger, self) + self.child.trigger = partial(get_trigger, self.child) + +class Model(object): + pass + + + +def test_partial(): + assert copy(Machine(), byref=True) + assert copy(Machine(), byref=True, recurse=True) + assert copy(Machine(), recurse=True) + assert copy(Machine()) + + +class Machine2(object): + def __init__(self): + self.go = partial(self.member, self) + def member(self, model): + pass + + +class SubMachine(Machine2): + def __init__(self): + super(SubMachine, self).__init__() + + +def test_partials(): + assert copy(SubMachine(), byref=True) + assert copy(SubMachine(), byref=True, recurse=True) + assert copy(SubMachine(), recurse=True) + assert copy(SubMachine()) + + +class obj4(object): + def __init__(self): + super(obj4, self).__init__() + a = self + class obj5(object): + def __init__(self): + super(obj5, self).__init__() + self.a = a + self.b = obj5() + + +def test_circular_reference(): + assert copy(obj4()) + obj4_copy = dill.loads(dill.dumps(obj4())) + assert type(obj4_copy) is type(obj4_copy).__init__.__closure__[0].cell_contents + assert type(obj4_copy.b) is type(obj4_copy.b).__init__.__closure__[0].cell_contents + + +def f(): + def g(): + return g + return g + + +def test_function_cells(): + assert copy(f()) + + +def fib(n): + assert n >= 0 + if n <= 1: + return n + else: + return fib(n-1) + fib(n-2) + + +def test_recursive_function(): + global fib + fib2 = copy(fib, recurse=True) + fib3 = copy(fib) + fib4 = fib + del fib + assert fib2(5) == 5 + for _fib in (fib3, fib4): + try: + _fib(5) + except Exception: + # This is expected to fail because fib no longer exists + pass + else: + raise AssertionError("Function fib shouldn't have been found") + fib = fib4 + + +def collection_function_recursion(): + d = {} + def g(): + return d + d['g'] = g + return g + + +def test_collection_function_recursion(): + g = copy(collection_function_recursion()) + assert g()['g'] is g + + +if __name__ == '__main__': + with warnings.catch_warnings(): + warnings.simplefilter('error') + test_super() + test_partial() + test_partials() + test_circular_reference() + test_function_cells() + test_recursive_function() + test_collection_function_recursion() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_registered.py b/.venv/lib/python3.10/site-packages/dill/tests/test_registered.py new file mode 100644 index 0000000..92c3703 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_registered.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +test pickling registered objects +""" + +import dill +from dill._objects import failures, registered, succeeds +import warnings +warnings.filterwarnings('ignore') + +def check(d, ok=True): + res = [] + for k,v in d.items(): + try: + z = dill.copy(v) + if ok: res.append(k) + except: + if not ok: res.append(k) + return res + +fails = check(failures) +try: + assert not bool(fails) +except AssertionError as e: + print("FAILS: %s" % fails) + raise e from None + +register = check(registered, ok=False) +try: + assert not bool(register) +except AssertionError as e: + print("REGISTER: %s" % register) + raise e from None + +success = check(succeeds, ok=False) +try: + assert not bool(success) +except AssertionError as e: + print("SUCCESS: %s" % success) + raise e from None + +import builtins +import types +q = dill._dill._reverse_typemap +p = {k:v for k,v in q.items() if k not in vars(builtins) and k not in vars(types)} + +diff = set(p.keys()).difference(registered.keys()) +try: + assert not bool(diff) +except AssertionError as e: + print("DIFF: %s" % diff) + raise e from None + +miss = set(registered.keys()).difference(p.keys()) +try: + assert not bool(miss) +except AssertionError as e: + print("MISS: %s" % miss) + raise e from None diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_restricted.py b/.venv/lib/python3.10/site-packages/dill/tests/test_restricted.py new file mode 100644 index 0000000..ea4857b --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_restricted.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Author: Kirill Makhonin (@kirillmakhonin) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 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 + +class RestrictedType: + def __bool__(*args, **kwargs): + raise Exception('Restricted function') + + __eq__ = __lt__ = __le__ = __ne__ = __gt__ = __ge__ = __hash__ = __bool__ + +glob_obj = RestrictedType() + +def restricted_func(): + a = glob_obj + +def test_function_with_restricted_object(): + deserialized = dill.loads(dill.dumps(restricted_func, recurse=True)) + + +if __name__ == '__main__': + test_function_with_restricted_object() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_selected.py b/.venv/lib/python3.10/site-packages/dill/tests/test_selected.py new file mode 100644 index 0000000..ea0fafa --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_selected.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +testing some selected object types +""" + +import dill +dill.settings['recurse'] = True + +verbose = False + +def test_dict_contents(): + c = type.__dict__ + for i,j in c.items(): + #try: + ok = dill.pickles(j) + #except Exception: + # print ("FAIL: %s with %s" % (i, dill.detect.errors(j))) + if verbose: print ("%s: %s, %s" % (ok, type(j), j)) + assert ok + if verbose: print ("") + +def _g(x): yield x; + +def _f(): + try: raise + except Exception: + from sys import exc_info + e, er, tb = exc_info() + return er, tb + +class _d(object): + def _method(self): + pass + +from dill import objects +from dill import load_types +load_types(pickleable=True,unpickleable=False) +_newclass = objects['ClassObjectType'] +# some clean-up #FIXME: should happen internal to dill +objects['TemporaryFileType'].close() +objects['TextWrapperType'].close() +if 'BufferedRandomType' in objects: + objects['BufferedRandomType'].close() +objects['BufferedReaderType'].close() +objects['BufferedWriterType'].close() +objects['FileType'].close() +del objects + +# getset_descriptor for new-style classes (fails on '_method', if not __main__) +def test_class_descriptors(): + d = _d.__dict__ + for i in d.values(): + ok = dill.pickles(i) + if verbose: print ("%s: %s, %s" % (ok, type(i), i)) + assert ok + if verbose: print ("") + od = _newclass.__dict__ + for i in od.values(): + ok = dill.pickles(i) + if verbose: print ("%s: %s, %s" % (ok, type(i), i)) + assert ok + if verbose: print ("") + +# (__main__) class instance for new-style classes +def test_class(): + o = _d() + oo = _newclass() + ok = dill.pickles(o) + if verbose: print ("%s: %s, %s" % (ok, type(o), o)) + assert ok + ok = dill.pickles(oo) + if verbose: print ("%s: %s, %s" % (ok, type(oo), oo)) + assert ok + if verbose: print ("") + +# frames, generators, and tracebacks (all depend on frame) +def test_frame_related(): + g = _g(1) + f = g.gi_frame + e,t = _f() + _is = lambda ok: ok + ok = dill.pickles(f) + if verbose: print ("%s: %s, %s" % (ok, type(f), f)) + assert not ok + ok = dill.pickles(g) + if verbose: print ("%s: %s, %s" % (ok, type(g), g)) + assert _is(not ok) #XXX: dill fails + ok = dill.pickles(t) + if verbose: print ("%s: %s, %s" % (ok, type(t), t)) + assert not ok #XXX: dill fails + ok = dill.pickles(e) + if verbose: print ("%s: %s, %s" % (ok, type(e), e)) + assert ok + if verbose: print ("") + +def test_typing(): + import typing + x = typing.Any + assert x == dill.copy(x) + x = typing.Dict[int, str] + assert x == dill.copy(x) + x = typing.List[int] + assert x == dill.copy(x) + x = typing.Tuple[int, str] + assert x == dill.copy(x) + x = typing.Tuple[int] + assert x == dill.copy(x) + x = typing.Tuple[()] + assert x == dill.copy(x) + x = typing.Tuple[()].copy_with(()) + assert x == dill.copy(x) + return + + +if __name__ == '__main__': + test_frame_related() + test_dict_contents() + test_class() + test_class_descriptors() + test_typing() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_session.py b/.venv/lib/python3.10/site-packages/dill/tests/test_session.py new file mode 100644 index 0000000..891eaf8 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_session.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python + +# Author: Leonardo Gama (@leogama) +# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import atexit +import os +import sys +import __main__ +from contextlib import suppress +from io import BytesIO + +import dill + +session_file = os.path.join(os.path.dirname(__file__), 'session-refimported-%s.pkl') + +################### +# Child process # +################### + +def _error_line(error, obj, refimported): + import traceback + line = traceback.format_exc().splitlines()[-2].replace('[obj]', '['+repr(obj)+']') + return "while testing (with refimported=%s): %s" % (refimported, line.lstrip()) + +if __name__ == '__main__' and len(sys.argv) >= 3 and sys.argv[1] == '--child': + # Test session loading in a fresh interpreter session. + refimported = (sys.argv[2] == 'True') + dill.load_module(session_file % refimported, module='__main__') + + def test_modules(refimported): + # FIXME: In this test setting with CPython 3.7, 'calendar' is not included + # in sys.modules, independent of the value of refimported. Tried to + # run garbage collection just before loading the session with no luck. It + # fails even when preceding them with 'import calendar'. Needed to run + # these kinds of tests in a supbrocess. Failing test sample: + # assert globals()['day_name'] is sys.modules['calendar'].__dict__['day_name'] + try: + for obj in ('json', 'url', 'local_mod', 'sax', 'dom'): + assert globals()[obj].__name__ in sys.modules + assert 'calendar' in sys.modules and 'cmath' in sys.modules + import calendar, cmath + + for obj in ('Calendar', 'isleap'): + assert globals()[obj] is sys.modules['calendar'].__dict__[obj] + assert __main__.day_name.__module__ == 'calendar' + if refimported: + assert __main__.day_name is calendar.day_name + + assert __main__.complex_log is cmath.log + + except AssertionError as error: + error.args = (_error_line(error, obj, refimported),) + raise + + test_modules(refimported) + sys.exit() + +#################### +# Parent process # +#################### + +# Create various kinds of objects to test different internal logics. + +## Modules. +import json # top-level module +import urllib as url # top-level module under alias +from xml import sax # submodule +import xml.dom.minidom as dom # submodule under alias +import test_dictviews as local_mod # non-builtin top-level module + +## Imported objects. +from calendar import Calendar, isleap, day_name # class, function, other object +from cmath import log as complex_log # imported with alias + +## Local objects. +x = 17 +empty = None +names = ['Alice', 'Bob', 'Carol'] +def squared(x): return x**2 +cubed = lambda x: x**3 +class Person: + def __init__(self, name, age): + self.name = name + self.age = age +person = Person(names[0], x) +class CalendarSubclass(Calendar): + def weekdays(self): + return [day_name[i] for i in self.iterweekdays()] +cal = CalendarSubclass() +selfref = __main__ + +# Setup global namespace for session saving tests. +class TestNamespace: + test_globals = globals().copy() + def __init__(self, **extra): + self.extra = extra + def __enter__(self): + self.backup = globals().copy() + globals().clear() + globals().update(self.test_globals) + globals().update(self.extra) + return self + def __exit__(self, *exc_info): + globals().clear() + globals().update(self.backup) + +def _clean_up_cache(module): + cached = module.__file__.split('.', 1)[0] + '.pyc' + cached = module.__cached__ if hasattr(module, '__cached__') else cached + pycache = os.path.join(os.path.dirname(module.__file__), '__pycache__') + for remove, file in [(os.remove, cached), (os.removedirs, pycache)]: + with suppress(OSError): + remove(file) + +atexit.register(_clean_up_cache, local_mod) + +def _test_objects(main, globals_copy, refimported): + try: + main_dict = __main__.__dict__ + global Person, person, Calendar, CalendarSubclass, cal, selfref + + for obj in ('json', 'url', 'local_mod', 'sax', 'dom'): + assert globals()[obj].__name__ == globals_copy[obj].__name__ + + for obj in ('x', 'empty', 'names'): + assert main_dict[obj] == globals_copy[obj] + + for obj in ['squared', 'cubed']: + assert main_dict[obj].__globals__ is main_dict + assert main_dict[obj](3) == globals_copy[obj](3) + + assert Person.__module__ == __main__.__name__ + assert isinstance(person, Person) + assert person.age == globals_copy['person'].age + + assert issubclass(CalendarSubclass, Calendar) + assert isinstance(cal, CalendarSubclass) + assert cal.weekdays() == globals_copy['cal'].weekdays() + + assert selfref is __main__ + + except AssertionError as error: + error.args = (_error_line(error, obj, refimported),) + raise + +def test_session_main(refimported): + """test dump/load_module() for __main__, both in this process and in a subprocess""" + extra_objects = {} + if refimported: + # Test unpickleable imported object in main. + from sys import flags + extra_objects['flags'] = flags + + with TestNamespace(**extra_objects) as ns: + try: + # Test session loading in a new session. + dill.dump_module(session_file % refimported, refimported=refimported) + from dill.tests.__main__ import python, shell, sp + error = sp.call([python, __file__, '--child', str(refimported)], shell=shell) + if error: sys.exit(error) + finally: + with suppress(OSError): + os.remove(session_file % refimported) + + # Test session loading in the same session. + session_buffer = BytesIO() + dill.dump_module(session_buffer, refimported=refimported) + session_buffer.seek(0) + dill.load_module(session_buffer, module='__main__') + ns.backup['_test_objects'](__main__, ns.backup, refimported) + +def test_session_other(): + """test dump/load_module() for a module other than __main__""" + import test_classdef as module + atexit.register(_clean_up_cache, module) + module.selfref = module + dict_objects = [obj for obj in module.__dict__.keys() if not obj.startswith('__')] + + session_buffer = BytesIO() + dill.dump_module(session_buffer, module) + + for obj in dict_objects: + del module.__dict__[obj] + + session_buffer.seek(0) + dill.load_module(session_buffer, module) + + assert all(obj in module.__dict__ for obj in dict_objects) + assert module.selfref is module + +def test_runtime_module(): + from types import ModuleType + modname = '__runtime__' + runtime = ModuleType(modname) + runtime.x = 42 + + mod = dill.session._stash_modules(runtime) + if mod is not runtime: + print("There are objects to save by referenece that shouldn't be:", + mod.__dill_imported, mod.__dill_imported_as, mod.__dill_imported_top_level, + file=sys.stderr) + + # This is also for code coverage, tests the use case of dump_module(refimported=True) + # without imported objects in the namespace. It's a contrived example because + # even dill can't be in it. This should work after fixing #462. + session_buffer = BytesIO() + dill.dump_module(session_buffer, module=runtime, refimported=True) + session_dump = session_buffer.getvalue() + + # Pass a new runtime created module with the same name. + runtime = ModuleType(modname) # empty + return_val = dill.load_module(BytesIO(session_dump), module=runtime) + assert return_val is None + assert runtime.__name__ == modname + assert runtime.x == 42 + assert runtime not in sys.modules.values() + + # Pass nothing as main. load_module() must create it. + session_buffer.seek(0) + runtime = dill.load_module(BytesIO(session_dump)) + assert runtime.__name__ == modname + assert runtime.x == 42 + assert runtime not in sys.modules.values() + +def test_refimported_imported_as(): + import collections + import concurrent.futures + import types + import typing + mod = sys.modules['__test__'] = types.ModuleType('__test__') + dill.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) + mod.Dict = collections.UserDict # select by type + mod.AsyncCM = typing.AsyncContextManager # select by __module__ + mod.thread_exec = dill.executor # select by __module__ with regex + + session_buffer = BytesIO() + dill.dump_module(session_buffer, mod, refimported=True) + session_buffer.seek(0) + mod = dill.load(session_buffer) + del sys.modules['__test__'] + + assert set(mod.__dill_imported_as) == { + ('collections', 'UserDict', 'Dict'), + ('typing', 'AsyncContextManager', 'AsyncCM'), + ('dill', 'executor', 'thread_exec'), + } + +def test_load_module_asdict(): + with TestNamespace(): + session_buffer = BytesIO() + dill.dump_module(session_buffer) + + global empty, names, x, y + x = y = 0 # change x and create y + del empty + globals_state = globals().copy() + + session_buffer.seek(0) + main_vars = dill.load_module_asdict(session_buffer) + + assert main_vars is not globals() + assert globals() == globals_state + + assert main_vars['__name__'] == '__main__' + assert main_vars['names'] == names + assert main_vars['names'] is not names + assert main_vars['x'] != x + assert 'y' not in main_vars + assert 'empty' in main_vars + +if __name__ == '__main__': + test_session_main(refimported=False) + test_session_main(refimported=True) + test_session_other() + test_runtime_module() + test_refimported_imported_as() + test_load_module_asdict() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_source.py b/.venv/lib/python3.10/site-packages/dill/tests/test_source.py new file mode 100644 index 0000000..12b4519 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_source.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +from dill.source import getsource, getname, _wrap, getimport +from dill.source import importable +from dill._dill import IS_PYPY + +import sys +PY310b = 0x30a00b1 + +f = lambda x: x**2 +def g(x): return f(x) - x + +def h(x): + def g(x): return x + return g(x) - x + +class Foo(object): + def bar(self, x): + return x*x+x +_foo = Foo() + +def add(x,y): + return x+y + +# yes, same as 'f', but things are tricky when it comes to pointers +squared = lambda x:x**2 + +class Bar: + pass +_bar = Bar() + + # inspect.getsourcelines # dill.source.getblocks +def test_getsource(): + assert getsource(f) == 'f = lambda x: x**2\n' + assert getsource(g) == 'def g(x): return f(x) - x\n' + assert getsource(h) == 'def h(x):\n def g(x): return x\n return g(x) - x\n' + assert getname(f) == 'f' + assert getname(g) == 'g' + assert getname(h) == 'h' + assert _wrap(f)(4) == 16 + assert _wrap(g)(4) == 12 + assert _wrap(h)(4) == 0 + + assert getname(Foo) == 'Foo' + assert getname(Bar) == 'Bar' + assert getsource(Bar) == 'class Bar:\n pass\n' + assert getsource(Foo) == 'class Foo(object):\n def bar(self, x):\n return x*x+x\n' + #XXX: add getsource for _foo, _bar + +# test itself +def test_itself(): + assert getimport(getimport)=='from dill.source import getimport\n' + +# builtin functions and objects +def test_builtin(): + assert getimport(pow) == 'pow\n' + assert getimport(100) == '100\n' + assert getimport(True) == 'True\n' + assert getimport(pow, builtin=True) == 'from builtins import pow\n' + assert getimport(100, builtin=True) == '100\n' + assert getimport(True, builtin=True) == 'True\n' + # this is kinda BS... you can't import a None + assert getimport(None) == 'None\n' + assert getimport(None, builtin=True) == 'None\n' + + +# other imported functions +def test_imported(): + from math import sin + assert getimport(sin) == 'from math import sin\n' + +# interactively defined functions +def test_dynamic(): + assert getimport(add) == 'from %s import add\n' % __name__ + # interactive lambdas + assert getimport(squared) == 'from %s import squared\n' % __name__ + +# classes and class instances +def test_classes(): + from io import BytesIO as StringIO + y = "from _io import BytesIO\n" + x = y if (IS_PYPY or sys.hexversion >= PY310b) else "from io import BytesIO\n" + s = StringIO() + + assert getimport(StringIO) == x + assert getimport(s) == y + # interactively defined classes and class instances + assert getimport(Foo) == 'from %s import Foo\n' % __name__ + assert getimport(_foo) == 'from %s import Foo\n' % __name__ + + +# test importable +def test_importable(): + assert importable(add, source=False) == 'from %s import add\n' % __name__ + assert importable(squared, source=False) == 'from %s import squared\n' % __name__ + assert importable(Foo, source=False) == 'from %s import Foo\n' % __name__ + assert importable(Foo.bar, source=False) == 'from %s import bar\n' % __name__ + assert importable(_foo.bar, source=False) == 'from %s import bar\n' % __name__ + assert importable(None, source=False) == 'None\n' + assert importable(100, source=False) == '100\n' + + assert importable(add, source=True) == 'def add(x,y):\n return x+y\n' + assert importable(squared, source=True) == 'squared = lambda x:x**2\n' + assert importable(None, source=True) == 'None\n' + assert importable(Bar, source=True) == 'class Bar:\n pass\n' + assert importable(Foo, source=True) == 'class Foo(object):\n def bar(self, x):\n return x*x+x\n' + assert importable(Foo.bar, source=True) == 'def bar(self, x):\n return x*x+x\n' + assert importable(Foo.bar, source=False) == 'from %s import bar\n' % __name__ + assert importable(Foo.bar, alias='memo', source=False) == 'from %s import bar as memo\n' % __name__ + assert importable(Foo, alias='memo', source=False) == 'from %s import Foo as memo\n' % __name__ + assert importable(squared, alias='memo', source=False) == 'from %s import squared as memo\n' % __name__ + assert importable(squared, alias='memo', source=True) == 'memo = squared = lambda x:x**2\n' + assert importable(add, alias='memo', source=True) == 'def add(x,y):\n return x+y\n\nmemo = add\n' + assert importable(None, alias='memo', source=True) == 'memo = None\n' + assert importable(100, alias='memo', source=True) == 'memo = 100\n' + assert importable(add, builtin=True, source=False) == 'from %s import add\n' % __name__ + assert importable(squared, builtin=True, source=False) == 'from %s import squared\n' % __name__ + assert importable(Foo, builtin=True, source=False) == 'from %s import Foo\n' % __name__ + assert importable(Foo.bar, builtin=True, source=False) == 'from %s import bar\n' % __name__ + assert importable(_foo.bar, builtin=True, source=False) == 'from %s import bar\n' % __name__ + assert importable(None, builtin=True, source=False) == 'None\n' + assert importable(100, builtin=True, source=False) == '100\n' + + +def test_numpy(): + try: + import numpy as np + y = np.array + x = y([1,2,3]) + assert importable(x, source=False) == 'from numpy import array\narray([1, 2, 3])\n' + assert importable(y, source=False) == 'from %s import array\n' % y.__module__ + assert importable(x, source=True) == 'from numpy import array\narray([1, 2, 3])\n' + assert importable(y, source=True) == 'from %s import array\n' % y.__module__ + y = np.int64 + x = y(0) + assert importable(x, source=False) == 'from numpy import int64\nint64(0)\n' + assert importable(y, source=False) == 'from %s import int64\n' % y.__module__ + assert importable(x, source=True) == 'from numpy import int64\nint64(0)\n' + assert importable(y, source=True) == 'from %s import int64\n' % y.__module__ + y = np.bool_ + x = y(0) + import warnings + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', category=FutureWarning) + warnings.filterwarnings('ignore', category=DeprecationWarning) + if hasattr(np, 'bool'): b = 'bool_' if np.bool is bool else 'bool' + else: b = 'bool_' + assert importable(x, source=False) == 'from numpy import %s\n%s(False)\n' % (b,b) + assert importable(y, source=False) == 'from %s import %s\n' % (y.__module__,b) + assert importable(x, source=True) == 'from numpy import %s\n%s(False)\n' % (b,b) + assert importable(y, source=True) == 'from %s import %s\n' % (y.__module__,b) + except ImportError: pass + +#NOTE: if before getimport(pow), will cause pow to throw AssertionError +def test_foo(): + assert importable(_foo, source=True).startswith("import dill\nclass Foo(object):\n def bar(self, x):\n return x*x+x\ndill.loads(") + +if __name__ == '__main__': + test_getsource() + test_itself() + test_builtin() + test_imported() + test_dynamic() + test_classes() + test_importable() + test_numpy() + test_foo() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_sources.py b/.venv/lib/python3.10/site-packages/dill/tests/test_sources.py new file mode 100644 index 0000000..478b967 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_sources.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @uqfoundation) +# Copyright (c) 2024-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +check that dill.source performs as expected with changes to locals in 3.13.0b1 +see: https://github.com/python/cpython/issues/118888 +""" +# repeat functions from test_source.py +f = lambda x: x**2 +def g(x): return f(x) - x + +def h(x): + def g(x): return x + return g(x) - x + +class Foo(object): + def bar(self, x): + return x*x+x +_foo = Foo() + +def add(x,y): + return x+y + +squared = lambda x:x**2 + +class Bar: + pass +_bar = Bar() + +# repeat, but from test_source.py +import test_source as ts + +# test objects created in other test modules +import test_mixins as tm + +import dill.source as ds + + +def test_isfrommain(): + assert ds.isfrommain(add) == True + assert ds.isfrommain(squared) == True + assert ds.isfrommain(Bar) == True + assert ds.isfrommain(_bar) == True + assert ds.isfrommain(ts.add) == False + assert ds.isfrommain(ts.squared) == False + assert ds.isfrommain(ts.Bar) == False + assert ds.isfrommain(ts._bar) == False + assert ds.isfrommain(tm.quad) == False + assert ds.isfrommain(tm.double_add) == False + assert ds.isfrommain(tm.quadratic) == False + assert ds.isdynamic(add) == False + assert ds.isdynamic(squared) == False + assert ds.isdynamic(ts.add) == False + assert ds.isdynamic(ts.squared) == False + assert ds.isdynamic(tm.double_add) == False + assert ds.isdynamic(tm.quadratic) == False + + +def test_matchlambda(): + assert ds._matchlambda(f, 'f = lambda x: x**2\n') + assert ds._matchlambda(squared, 'squared = lambda x:x**2\n') + assert ds._matchlambda(ts.f, 'f = lambda x: x**2\n') + assert ds._matchlambda(ts.squared, 'squared = lambda x:x**2\n') + + +def test_findsource(): + lines, lineno = ds.findsource(add) + assert lines[lineno] == 'def add(x,y):\n' + lines, lineno = ds.findsource(ts.add) + assert lines[lineno] == 'def add(x,y):\n' + lines, lineno = ds.findsource(squared) + assert lines[lineno] == 'squared = lambda x:x**2\n' + lines, lineno = ds.findsource(ts.squared) + assert lines[lineno] == 'squared = lambda x:x**2\n' + lines, lineno = ds.findsource(Bar) + assert lines[lineno] == 'class Bar:\n' + lines, lineno = ds.findsource(ts.Bar) + assert lines[lineno] == 'class Bar:\n' + lines, lineno = ds.findsource(_bar) + assert lines[lineno] == 'class Bar:\n' + lines, lineno = ds.findsource(ts._bar) + assert lines[lineno] == 'class Bar:\n' + lines, lineno = ds.findsource(tm.quad) + assert lines[lineno] == 'def quad(a=1, b=1, c=0):\n' + lines, lineno = ds.findsource(tm.double_add) + assert lines[lineno] == ' def func(*args, **kwds):\n' + lines, lineno = ds.findsource(tm.quadratic) + assert lines[lineno] == ' def dec(f):\n' + + +def test_getsourcelines(): + assert ''.join(ds.getsourcelines(add)[0]) == 'def add(x,y):\n return x+y\n' + assert ''.join(ds.getsourcelines(ts.add)[0]) == 'def add(x,y):\n return x+y\n' + assert ''.join(ds.getsourcelines(squared)[0]) == 'squared = lambda x:x**2\n' + assert ''.join(ds.getsourcelines(ts.squared)[0]) == 'squared = lambda x:x**2\n' + assert ''.join(ds.getsourcelines(Bar)[0]) == 'class Bar:\n pass\n' + assert ''.join(ds.getsourcelines(ts.Bar)[0]) == 'class Bar:\n pass\n' + assert ''.join(ds.getsourcelines(_bar)[0]) == 'class Bar:\n pass\n' #XXX: ? + assert ''.join(ds.getsourcelines(ts._bar)[0]) == 'class Bar:\n pass\n' #XXX: ? + assert ''.join(ds.getsourcelines(tm.quad)[0]) == 'def quad(a=1, b=1, c=0):\n inverted = [False]\n def invert():\n inverted[0] = not inverted[0]\n def dec(f):\n def func(*args, **kwds):\n x = f(*args, **kwds)\n if inverted[0]: x = -x\n return a*x**2 + b*x + c\n func.__wrapped__ = f\n func.invert = invert\n func.inverted = inverted\n return func\n return dec\n' + assert ''.join(ds.getsourcelines(tm.quadratic)[0]) == ' def dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n' + assert ''.join(ds.getsourcelines(tm.quadratic, lstrip=True)[0]) == 'def dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n' + assert ''.join(ds.getsourcelines(tm.quadratic, enclosing=True)[0]) == 'def quad_factory(a=1,b=1,c=0):\n def dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n return dec\n' + assert ''.join(ds.getsourcelines(tm.double_add)[0]) == ' def func(*args, **kwds):\n x = f(*args, **kwds)\n if inverted[0]: x = -x\n return a*x**2 + b*x + c\n' + assert ''.join(ds.getsourcelines(tm.double_add, enclosing=True)[0]) == 'def quad(a=1, b=1, c=0):\n inverted = [False]\n def invert():\n inverted[0] = not inverted[0]\n def dec(f):\n def func(*args, **kwds):\n x = f(*args, **kwds)\n if inverted[0]: x = -x\n return a*x**2 + b*x + c\n func.__wrapped__ = f\n func.invert = invert\n func.inverted = inverted\n return func\n return dec\n' + + +def test_indent(): + assert ds.outdent(''.join(ds.getsourcelines(tm.quadratic)[0])) == ''.join(ds.getsourcelines(tm.quadratic, lstrip=True)[0]) + assert ds.indent(''.join(ds.getsourcelines(tm.quadratic, lstrip=True)[0]), 2) == ''.join(ds.getsourcelines(tm.quadratic)[0]) + + +def test_dumpsource(): + local = {} + exec(ds.dumpsource(add, alias='raw'), {}, local) + exec(ds.dumpsource(ts.add, alias='mod'), {}, local) + assert local['raw'](1,2) == local['mod'](1,2) + exec(ds.dumpsource(squared, alias='raw'), {}, local) + exec(ds.dumpsource(ts.squared, alias='mod'), {}, local) + assert local['raw'](3) == local['mod'](3) + assert ds._wrap(add)(1,2) == ds._wrap(ts.add)(1,2) + assert ds._wrap(squared)(3) == ds._wrap(ts.squared)(3) + + +def test_name(): + assert ds._namespace(add) == ds.getname(add, fqn=True).split('.') + assert ds._namespace(ts.add) == ds.getname(ts.add, fqn=True).split('.') + assert ds._namespace(squared) == ds.getname(squared, fqn=True).split('.') + assert ds._namespace(ts.squared) == ds.getname(ts.squared, fqn=True).split('.') + assert ds._namespace(Bar) == ds.getname(Bar, fqn=True).split('.') + assert ds._namespace(ts.Bar) == ds.getname(ts.Bar, fqn=True).split('.') + assert ds._namespace(tm.quad) == ds.getname(tm.quad, fqn=True).split('.') + #XXX: the following also works, however behavior may be wrong for nested functions + #assert ds._namespace(tm.double_add) == ds.getname(tm.double_add, fqn=True).split('.') + #assert ds._namespace(tm.quadratic) == ds.getname(tm.quadratic, fqn=True).split('.') + assert ds.getname(add) == 'add' + assert ds.getname(ts.add) == 'add' + assert ds.getname(squared) == 'squared' + assert ds.getname(ts.squared) == 'squared' + assert ds.getname(Bar) == 'Bar' + assert ds.getname(ts.Bar) == 'Bar' + assert ds.getname(tm.quad) == 'quad' + assert ds.getname(tm.double_add) == 'func' #XXX: ? + assert ds.getname(tm.quadratic) == 'dec' #XXX: ? + + +def test_getimport(): + local = {} + exec(ds.getimport(add, alias='raw'), {}, local) + exec(ds.getimport(ts.add, alias='mod'), {}, local) + assert local['raw'](1,2) == local['mod'](1,2) + exec(ds.getimport(squared, alias='raw'), {}, local) + exec(ds.getimport(ts.squared, alias='mod'), {}, local) + assert local['raw'](3) == local['mod'](3) + exec(ds.getimport(Bar, alias='raw'), {}, local) + exec(ds.getimport(ts.Bar, alias='mod'), {}, local) + assert ds.getname(local['raw']) == ds.getname(local['mod']) + exec(ds.getimport(tm.quad, alias='mod'), {}, local) + assert local['mod']()(sum)([1,2,3]) == tm.quad()(sum)([1,2,3]) + #FIXME: wrong results for nested functions (e.g. tm.double_add, tm.quadratic) + + +def test_importable(): + assert ds.importable(add, source=False) == ds.getimport(add) + assert ds.importable(add) == ds.getsource(add) + assert ds.importable(squared, source=False) == ds.getimport(squared) + assert ds.importable(squared) == ds.getsource(squared) + assert ds.importable(Bar, source=False) == ds.getimport(Bar) + assert ds.importable(Bar) == ds.getsource(Bar) + assert ds.importable(ts.add) == ds.getimport(ts.add) + assert ds.importable(ts.add, source=True) == ds.getsource(ts.add) + assert ds.importable(ts.squared) == ds.getimport(ts.squared) + assert ds.importable(ts.squared, source=True) == ds.getsource(ts.squared) + assert ds.importable(ts.Bar) == ds.getimport(ts.Bar) + assert ds.importable(ts.Bar, source=True) == ds.getsource(ts.Bar) + + +if __name__ == '__main__': + test_isfrommain() + test_matchlambda() + test_findsource() + test_getsourcelines() + test_indent() + test_dumpsource() + test_name() + test_getimport() + test_importable() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_temp.py b/.venv/lib/python3.10/site-packages/dill/tests/test_temp.py new file mode 100644 index 0000000..e9201f4 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_temp.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import sys +from dill.temp import dump, dump_source, dumpIO, dumpIO_source +from dill.temp import load, load_source, loadIO, loadIO_source +WINDOWS = sys.platform[:3] == 'win' + + +f = lambda x: x**2 +x = [1,2,3,4,5] + +# source code to tempfile +def test_code_to_tempfile(): + if not WINDOWS: #see: https://bugs.python.org/issue14243 + pyfile = dump_source(f, alias='_f') + _f = load_source(pyfile) + assert _f(4) == f(4) + +# source code to stream +def test_code_to_stream(): + pyfile = dumpIO_source(f, alias='_f') + _f = loadIO_source(pyfile) + assert _f(4) == f(4) + +# pickle to tempfile +def test_pickle_to_tempfile(): + if not WINDOWS: #see: https://bugs.python.org/issue14243 + dumpfile = dump(x) + _x = load(dumpfile) + assert _x == x + +# pickle to stream +def test_pickle_to_stream(): + dumpfile = dumpIO(x) + _x = loadIO(dumpfile) + assert _x == x + +### now testing the objects ### +f = lambda x: x**2 +def g(x): return f(x) - x + +def h(x): + def g(x): return x + return g(x) - x + +class Foo(object): + def bar(self, x): + return x*x+x +_foo = Foo() + +def add(x,y): + return x+y + +# yes, same as 'f', but things are tricky when it comes to pointers +squared = lambda x:x**2 + +class Bar: + pass +_bar = Bar() + + +# test function-type objects that take 2 args +def test_two_arg_functions(): + for obj in [add]: + pyfile = dumpIO_source(obj, alias='_obj') + _obj = loadIO_source(pyfile) + assert _obj(4,2) == obj(4,2) + +# test function-type objects that take 1 arg +def test_one_arg_functions(): + for obj in [g, h, squared]: + pyfile = dumpIO_source(obj, alias='_obj') + _obj = loadIO_source(pyfile) + assert _obj(4) == obj(4) + +# test instance-type objects +#for obj in [_bar, _foo]: +# pyfile = dumpIO_source(obj, alias='_obj') +# _obj = loadIO_source(pyfile) +# assert type(_obj) == type(obj) + +# test the rest of the objects +def test_the_rest(): + for obj in [Bar, Foo, Foo.bar, _foo.bar]: + pyfile = dumpIO_source(obj, alias='_obj') + _obj = loadIO_source(pyfile) + assert _obj.__name__ == obj.__name__ + + +if __name__ == '__main__': + test_code_to_tempfile() + test_code_to_stream() + test_pickle_to_tempfile() + test_pickle_to_stream() + test_two_arg_functions() + test_one_arg_functions() + test_the_rest() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_threads.py b/.venv/lib/python3.10/site-packages/dill/tests/test_threads.py new file mode 100644 index 0000000..debc5e1 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_threads.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2024-2025 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 +dill.settings['recurse'] = True + + +def test_new_thread(): + import threading + t = threading.Thread() + t_ = dill.copy(t) + assert t.is_alive() == t_.is_alive() + for i in ['daemon','name','ident','native_id']: + if hasattr(t, i): + assert getattr(t, i) == getattr(t_, i) + +def test_run_thread(): + import threading + t = threading.Thread() + t.start() + t_ = dill.copy(t) + assert t.is_alive() == t_.is_alive() + for i in ['daemon','name','ident','native_id']: + if hasattr(t, i): + assert getattr(t, i) == getattr(t_, i) + +def test_join_thread(): + import threading + t = threading.Thread() + t.start() + t.join() + t_ = dill.copy(t) + assert t.is_alive() == t_.is_alive() + for i in ['daemon','name','ident','native_id']: + if hasattr(t, i): + assert getattr(t, i) == getattr(t_, i) + + +if __name__ == '__main__': + test_new_thread() + test_run_thread() + test_join_thread() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_weakref.py b/.venv/lib/python3.10/site-packages/dill/tests/test_weakref.py new file mode 100644 index 0000000..378a21a --- /dev/null +++ b/.venv/lib/python3.10/site-packages/dill/tests/test_weakref.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2025 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 +dill.settings['recurse'] = True +import weakref + +class _class: + def _method(self): + pass + +class _callable_class: + def __call__(self): + pass + +def _function(): + pass + + +def test_weakref(): + o = _class() + oc = _callable_class() + f = _function + x = _class + + # ReferenceType + r = weakref.ref(o) + d_r = weakref.ref(_class()) + fr = weakref.ref(f) + xr = weakref.ref(x) + + # ProxyType + p = weakref.proxy(o) + d_p = weakref.proxy(_class()) + + # CallableProxyType + cp = weakref.proxy(oc) + d_cp = weakref.proxy(_callable_class()) + fp = weakref.proxy(f) + xp = weakref.proxy(x) + + objlist = [r,d_r,fr,xr, p,d_p, cp,d_cp,fp,xp] + #dill.detect.trace(True) + + for obj in objlist: + res = dill.detect.errors(obj) + if res: + print ("%r:\n %s" % (obj, res)) + # else: + # print ("PASS: %s" % obj) + assert not res + +def test_dictproxy(): + from dill._dill import DictProxyType + try: + m = DictProxyType({"foo": "bar"}) + except Exception: + m = type.__dict__ + mp = dill.copy(m) + assert mp.items() == m.items() + + +if __name__ == '__main__': + test_weakref() + from dill._dill import IS_PYPY + if not IS_PYPY: + test_dictproxy() diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA new file mode 100644 index 0000000..1785ba1 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA @@ -0,0 +1,375 @@ +Metadata-Version: 2.4 +Name: isort +Version: 7.0.0 +Summary: A Python utility / library to sort Python imports. +Project-URL: Homepage, https://pycqa.github.io/isort/index.html +Project-URL: Documentation, https://pycqa.github.io/isort/index.html +Project-URL: Repository, https://github.com/PyCQA/isort +Project-URL: Changelog, https://github.com/PyCQA/isort/releases +Author-email: Timothy Crosley , staticdev +License-Expression: MIT +License-File: LICENSE +Keywords: Clean,Imports,Lint,Refactor,Sort +Classifier: Development Status :: 6 - Mature +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Requires-Python: >=3.10.0 +Provides-Extra: colors +Requires-Dist: colorama; extra == 'colors' +Provides-Extra: plugins +Requires-Dist: setuptools; extra == 'plugins' +Description-Content-Type: text/markdown + +[![isort - isort your imports, so you don't have to.](https://raw.githubusercontent.com/pycqa/isort/main/art/logo_large.png)](https://pycqa.github.io/isort/) + +------------------------------------------------------------------------ + +[![PyPI version](https://badge.fury.io/py/isort.svg)](https://badge.fury.io/py/isort) +[![Python Version](https://img.shields.io/pypi/pyversions/isort)][pypi status] +[![Test](https://github.com/PyCQA/isort/actions/workflows/test.yml/badge.svg)](https://github.com/PyCQA/isort/actions/workflows/test.yml) +[![Lint](https://github.com/PyCQA/isort/actions/workflows/lint.yml/badge.svg)](https://github.com/PyCQA/isort/actions/workflows/lint.yml) +[![Code coverage Status](https://codecov.io/gh/pycqa/isort/branch/main/graph/badge.svg)](https://codecov.io/gh/pycqa/isort) +[![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.org/project/isort/) +[![Downloads](https://pepy.tech/badge/isort)](https://pepy.tech/project/isort) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) +[![DeepSource](https://static.deepsource.io/deepsource-badge-light-mini.svg)](https://deepsource.io/gh/pycqa/isort/?ref=repository-badge) + +[pypi status]: https://pypi.org/project/isort/ +_________________ + +[Read Latest Documentation](https://pycqa.github.io/isort/) - [Browse GitHub Code Repository](https://github.com/pycqa/isort/) +_________________ + +isort your imports, so you don't have to. + +isort is a Python utility / library to sort imports alphabetically and +automatically separate into sections and by type. It provides a command line +utility, Python library and [plugins for various +editors](https://github.com/pycqa/isort/wiki/isort-Plugins) to +quickly sort all your imports. It requires Python 3.9+ to run but +supports formatting Python 2 code too. + +- [Try isort now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try.html) +- [Using black? See the isort and black compatibility guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility.html) +- [isort has official support for pre-commit!](https://pycqa.github.io/isort/docs/configuration/pre-commit.html) + +![Example Usage](https://raw.github.com/pycqa/isort/main/example.gif) + +Before isort: + +```python +from my_lib import Object + +import os + +from my_lib import Object3 + +from my_lib import Object2 + +import sys + +from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14 + +import sys + +from __future__ import absolute_import + +from third_party import lib3 + +print("Hey") +print("yo") +``` + +After isort: + +```python +from __future__ import absolute_import + +import os +import sys + +from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, + lib9, lib10, lib11, lib12, lib13, lib14, lib15) + +from my_lib import Object, Object2, Object3 + +print("Hey") +print("yo") +``` + +## Installing isort + +Installing isort is as simple as: + +```bash +pip install isort +``` + +## Using isort + +**From the command line**: + +To run on specific files: + +```bash +isort mypythonfile.py mypythonfile2.py +``` + +To apply recursively: + +```bash +isort . +``` + +If [globstar](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) +is enabled, `isort .` is equivalent to: + +```bash +isort **/*.py +``` + +To view proposed changes without applying them: + +```bash +isort mypythonfile.py --diff +``` + +Finally, to atomically run isort against a project, only applying +changes if they don't introduce syntax errors: + +```bash +isort --atomic . +``` + +(Note: this is disabled by default, as it prevents isort from +running against code written using a different version of Python.) + +**From within Python**: + +```python +import isort + +isort.file("pythonfile.py") +``` + +or: + +```python +import isort + +sorted_code = isort.code("import b\nimport a\n") +``` + +## Installing isort's for your preferred text editor + +Several plugins have been written that enable to use isort from within a +variety of text-editors. You can find a full list of them [on the isort +wiki](https://github.com/pycqa/isort/wiki/isort-Plugins). +Additionally, I will enthusiastically accept pull requests that include +plugins for other text editors and add documentation for them as I am +notified. + +## Multi line output modes + +You will notice above the \"multi\_line\_output\" setting. This setting +defines how from imports wrap when they extend past the line\_length +limit and has [12 possible settings](https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes.html). + +## Indentation + +To change the how constant indents appear - simply change the +indent property with the following accepted formats: + +- Number of spaces you would like. For example: 4 would cause standard + 4 space indentation. +- Tab +- A verbatim string with quotes around it. + +For example: + +```python +" " +``` + +is equivalent to 4. + +For the import styles that use parentheses, you can control whether or +not to include a trailing comma after the last import with the +`include_trailing_comma` option (defaults to `False`). + +## Intelligently Balanced Multi-line Imports + +As of isort 3.1.0 support for balanced multi-line imports has been +added. With this enabled isort will dynamically change the import length +to the one that produces the most balanced grid, while staying below the +maximum import length defined. + +Example: + +```python +from __future__ import (absolute_import, division, + print_function, unicode_literals) +``` + +Will be produced instead of: + +```python +from __future__ import (absolute_import, division, print_function, + unicode_literals) +``` + +To enable this set `balanced_wrapping` to `True` in your config or pass +the `-e` option into the command line utility. + +## Custom Sections and Ordering + +isort provides configuration options to change almost every aspect of how +imports are organized, ordered, or grouped together in sections. + +[Click here](https://pycqa.github.io/isort/docs/configuration/custom_sections_and_ordering.html) for an overview of all these options. + +## Skip processing of imports (outside of configuration) + +To make isort ignore a single import simply add a comment at the end of +the import line containing the text `isort:skip`: + +```python +import module # isort:skip +``` + +or: + +```python +from xyz import (abc, # isort:skip + yo, + hey) +``` + +To make isort skip an entire file simply add `isort:skip_file` to the +module's doc string: + +```python +""" my_module.py + Best module ever + + isort:skip_file +""" + +import b +import a +``` + +## Adding or removing an import from multiple files + +isort can be ran or configured to add / remove imports automatically. + +[See a complete guide here.](https://pycqa.github.io/isort/docs/configuration/add_or_remove_imports.html) + +## Using isort to verify code + +The `--check-only` option +------------------------- + +isort can also be used to verify that code is correctly formatted +by running it with `-c`. Any files that contain incorrectly sorted +and/or formatted imports will be outputted to `stderr`. + +```bash +isort **/*.py -c -v + +SUCCESS: /home/timothy/Projects/Open_Source/isort/isort_kate_plugin.py Everything Looks Good! +ERROR: /home/timothy/Projects/Open_Source/isort/isort/isort.py Imports are incorrectly sorted. +``` + +One great place this can be used is with a pre-commit git hook, such as +this one by \@acdha: + + + +This can help to ensure a certain level of code quality throughout a +project. + +## Git hook + +isort provides a hook function that can be integrated into your Git +pre-commit script to check Python code before committing. + +[More info here.](https://pycqa.github.io/isort/docs/configuration/git_hook.html) + +## Setuptools integration + +Upon installation, isort enables a `setuptools` command that checks +Python files declared by your project. + +[More info here.](https://pycqa.github.io/isort/docs/configuration/setuptools_integration.html) + +## Spread the word + +[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) + +Place this badge at the top of your repository to let others know your project uses isort. + +For README.md: + +```markdown +[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) +``` + +Or README.rst: + +```rst +.. image:: https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336 + :target: https://pycqa.github.io/isort/ +``` + +## Security contact information + +To report a security vulnerability, please use the [Tidelift security +contact](https://tidelift.com/security). Tidelift will coordinate the +fix and disclosure. + +## Why isort? + +isort simply stands for import sort. It was originally called +"sortImports" however I got tired of typing the extra characters and +came to the realization camelCase is not pythonic. + +I wrote isort because in an organization I used to work in the manager +came in one day and decided all code must have alphabetically sorted +imports. The code base was huge - and he meant for us to do it by hand. +However, being a programmer - I\'m too lazy to spend 8 hours mindlessly +performing a function, but not too lazy to spend 16 hours automating it. +I was given permission to open source sortImports and here we are :) + +------------------------------------------------------------------------ + +[Get professionally supported isort with the Tidelift +Subscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme) + +Professional support for isort is available as part of the [Tidelift +Subscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme). +Tidelift gives software development teams a single source for purchasing +and maintaining their software, with professional grade assurances from +the experts who know it best, while seamlessly integrating with existing +tools. + +------------------------------------------------------------------------ + +Thanks and I hope you find isort useful! + +~Timothy Crosley diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD new file mode 100644 index 0000000..22b6e9d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD @@ -0,0 +1,101 @@ +../../../bin/isort,sha256=wt-xKEp2aF5_QNZjWj3hn4izHD2Cu-kLJ_5kHN8LDmY,226 +../../../bin/isort-identify-imports,sha256=EZhp_yvp4eekQ76zoD9_yhWd8VUVpWA8Qx1XZoiX0lU,260 +isort-7.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +isort-7.0.0.dist-info/METADATA,sha256=nm4sPm5TimI06E1lXwKPjPWDHNTzJ1LVFn93VYM6NbQ,11986 +isort-7.0.0.dist-info/RECORD,, +isort-7.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +isort-7.0.0.dist-info/entry_points.txt,sha256=3EUwrc2fZP9efifU_6TBNChjhWtdYNaSBga6R5VfiSs,169 +isort-7.0.0.dist-info/licenses/LICENSE,sha256=BjKUABw9Uj26y6ud1UrCKZgnVsyvWSylMkCysM3YIGU,1089 +isort/__init__.py,sha256=izMCmePBol7NDXEMXZvMEXCvZ_Rfzli-kt6dOilU1N0,872 +isort/__main__.py,sha256=iK0trzN9CCXpQX-XPZDZ9JVkm2Lc0q0oiAgsa6FkJb4,36 +isort/__pycache__/__init__.cpython-310.pyc,, +isort/__pycache__/__main__.cpython-310.pyc,, +isort/__pycache__/_version.cpython-310.pyc,, +isort/__pycache__/api.cpython-310.pyc,, +isort/__pycache__/comments.cpython-310.pyc,, +isort/__pycache__/core.cpython-310.pyc,, +isort/__pycache__/exceptions.cpython-310.pyc,, +isort/__pycache__/files.cpython-310.pyc,, +isort/__pycache__/format.cpython-310.pyc,, +isort/__pycache__/hooks.cpython-310.pyc,, +isort/__pycache__/identify.cpython-310.pyc,, +isort/__pycache__/io.cpython-310.pyc,, +isort/__pycache__/literal.cpython-310.pyc,, +isort/__pycache__/logo.cpython-310.pyc,, +isort/__pycache__/main.cpython-310.pyc,, +isort/__pycache__/output.cpython-310.pyc,, +isort/__pycache__/parse.cpython-310.pyc,, +isort/__pycache__/place.cpython-310.pyc,, +isort/__pycache__/profiles.cpython-310.pyc,, +isort/__pycache__/sections.cpython-310.pyc,, +isort/__pycache__/settings.cpython-310.pyc,, +isort/__pycache__/setuptools_commands.cpython-310.pyc,, +isort/__pycache__/sorting.cpython-310.pyc,, +isort/__pycache__/utils.cpython-310.pyc,, +isort/__pycache__/wrap.cpython-310.pyc,, +isort/__pycache__/wrap_modes.cpython-310.pyc,, +isort/_vendored/tomli/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072 +isort/_vendored/tomli/__init__.py,sha256=Y3N65pvphV_EF4k2qKiq_vYcohIUHhT05GzdRc0TOy8,213 +isort/_vendored/tomli/__pycache__/__init__.cpython-310.pyc,, +isort/_vendored/tomli/__pycache__/_parser.cpython-310.pyc,, +isort/_vendored/tomli/__pycache__/_re.cpython-310.pyc,, +isort/_vendored/tomli/_parser.py,sha256=ZbnCfybF5Assq8i1hPmIhP0ey1_u9BT5UloqDdCTyew,21397 +isort/_vendored/tomli/_re.py,sha256=3r6TD3gNGFjgOsfpy8aLpxgvasL__pvaN2m1R5DTxeQ,2833 +isort/_vendored/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26 +isort/_version.py,sha256=pXTtYi-S-p8e00o2Ad-PNREL9wAQaPgQzk_c_jndLOw,72 +isort/api.py,sha256=RkB3sLfVhSEiBEsFkztUTuwOdaegqlgHT83Ips17vFY,26280 +isort/comments.py,sha256=cFvf-vofTEz3neRY5mQa9jwVb3bY_QRlU8G8f9vTvw4,887 +isort/core.py,sha256=mcHPy9ds8ijyL1sX02CktPJ1-PnPaiCgumrCoMrwDRI,22684 +isort/deprecated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +isort/deprecated/__pycache__/__init__.cpython-310.pyc,, +isort/deprecated/__pycache__/finders.cpython-310.pyc,, +isort/deprecated/finders.py,sha256=tbDwxTz-xbUS5C1lEi6KB7lif9IkXfNUf8VEY7dlRHQ,14176 +isort/exceptions.py,sha256=v3S4LVEQ0QOQK054fEnrUcJyJBTRPmkrrjrWMh1sHBY,7008 +isort/files.py,sha256=EJm_fmPxOqX8-ZFvvXR7Vas6BQ3Tzd-u1LnXuKlhrKg,1611 +isort/format.py,sha256=pY8zyf2Eh_o9h26_re-f8NxoSd00sKjbrjyNaO5yWsg,5455 +isort/hooks.py,sha256=9KJoPNALP1MIRrvj-oxRsksl04Ezam93Yec0FAJP5QM,3268 +isort/identify.py,sha256=6_SAcXcWVcOueuC1Yr0wV5OeKlbyg-GAlqpLLpG0bBE,8342 +isort/io.py,sha256=oG_-VRTBJn54kmlmtxBE_ix6wloWDnN5rnlLoj4WnpE,2219 +isort/literal.py,sha256=B48IZqL5gZWtkuN6gEb__kw_mP3-GUrC2auqSOiOoIg,3699 +isort/logo.py,sha256=cL3al79O7O0G2viqRMRfBPp0qtRZmJw2nHSCZw8XWdQ,388 +isort/main.py,sha256=zqAFDpJ7RlFVNjS9d9QgQRFiEEE7sID724F0dO4uJZQ,47137 +isort/output.py,sha256=IrxpyGnzQYYsFh1cMBAb6-2dMpfb7Q-EOQkh_MsFWvg,28631 +isort/parse.py,sha256=rKLrGg4D1UKoS1svk3ARWhKnqbAnq-qGuM2ZeFLLo-E,25519 +isort/place.py,sha256=7sQ2k12AI3MXebW1cDDOghC0QEh1BZXmXGWNxjcFoIg,5137 +isort/profiles.py,sha256=7vCdA-KVBLL2dr0o2ZMMV-yZbJYhMlwv9QKfCpLoFiw,2292 +isort/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +isort/sections.py,sha256=e3fmTIPb5xzeOoiaHEoKNcSEx1qNja_Z2beuJ_0cjVE,272 +isort/settings.py,sha256=Uq38k0caWCKzJaaSr3Jof7KB97oJ8nU3D_igD7al-8Y,35637 +isort/setuptools_commands.py,sha256=Haxx62_HNqZONZyc4pS5VixILKaME4mhioBh--1CPqs,2383 +isort/sorting.py,sha256=2TiXU3uHhPCIeXlDf9LslN9AaUnQFOQwsY4s9nlmE-U,4495 +isort/stdlibs/__init__.py,sha256=qOZAOAgHwqSKmUe87NzCZc9xI9GWPhRUE_Ot1RidpVw,288 +isort/stdlibs/__pycache__/__init__.cpython-310.pyc,, +isort/stdlibs/__pycache__/all.cpython-310.pyc,, +isort/stdlibs/__pycache__/py2.cpython-310.pyc,, +isort/stdlibs/__pycache__/py27.cpython-310.pyc,, +isort/stdlibs/__pycache__/py3.cpython-310.pyc,, +isort/stdlibs/__pycache__/py310.cpython-310.pyc,, +isort/stdlibs/__pycache__/py311.cpython-310.pyc,, +isort/stdlibs/__pycache__/py312.cpython-310.pyc,, +isort/stdlibs/__pycache__/py313.cpython-310.pyc,, +isort/stdlibs/__pycache__/py314.cpython-310.pyc,, +isort/stdlibs/__pycache__/py36.cpython-310.pyc,, +isort/stdlibs/__pycache__/py37.cpython-310.pyc,, +isort/stdlibs/__pycache__/py38.cpython-310.pyc,, +isort/stdlibs/__pycache__/py39.cpython-310.pyc,, +isort/stdlibs/all.py,sha256=n8Es1WK6UlupYyVvf1PDjGbionqix-afC3LkY8nzTcw,57 +isort/stdlibs/py2.py,sha256=dTgWTa7ggz1cwN8fuI9eIs9-5nTmkRxG_uO61CGwfXI,41 +isort/stdlibs/py27.py,sha256=QriKfttNSHsjaRtDfR5WXytjzf7Xi7p9lxiOOcmA2JM,4504 +isort/stdlibs/py3.py,sha256=e1Y2e7UjCe8NVJWSN75hr3KsG2DhFI2bvVGrL-Hqqm0,251 +isort/stdlibs/py310.py,sha256=eSmafU9DNrMhXpzgnJQs9DHqxjXU6bKWCSodw4H7GXM,3440 +isort/stdlibs/py311.py,sha256=tOI3W9oHIaelXuXhHHYmPP7Put83R0s4FDFyq-_Y4vU,3441 +isort/stdlibs/py312.py,sha256=gTInIvuBpNzWXsrXAuOwzib0BKumEPP6AsF9ed9AYdM,3368 +isort/stdlibs/py313.py,sha256=qCQF8fqOVwemGdssKq5dZ3P_SLqKjBrlF4VvRCcKUgo,3095 +isort/stdlibs/py314.py,sha256=XLtbO3Egu-DqJXXo47TAUWeD7LRihxula_nfVV1gVeE,3116 +isort/stdlibs/py36.py,sha256=iuXIDLcFrSviMMSOP4PoKWCG5BveMnZbFravpduSUss,3310 +isort/stdlibs/py37.py,sha256=dLxxRerCvb4O9vrifTg5KWgO0L3a6AQB13haK_tSBRw,3334 +isort/stdlibs/py38.py,sha256=kGTxrw7fgCwgnaSdQNcuUVgOQL3A0EOiNpjPvm6QCvI,3455 +isort/stdlibs/py39.py,sha256=z5gwSoKVw6i9G5Pib8SRN0XSZjyPsecdhhKpTUtGXxU,3464 +isort/utils.py,sha256=_gc-gWRk4TIAfrlHT0XoL8TP7BJ8mKofwkbVBLfCTnE,2441 +isort/wrap.py,sha256=W7WUga954p9gao4Je2lgrkHALTyKek1nGlx4RRiawnU,6381 +isort/wrap_modes.py,sha256=KFwm6Xo48iy88Tzufu6HdzDtsZjSi9_wvi7oRN7iKHY,13462 diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt new file mode 100644 index 0000000..fb42967 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt @@ -0,0 +1,6 @@ +[console_scripts] +isort = isort.main:main +isort-identify-imports = isort.main:identify_imports_main + +[distutils.commands] +isort = isort.setuptools_commands:ISortCommand diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..b5083a5 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Timothy Edmund Crosley + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/.venv/lib/python3.10/site-packages/isort/__init__.py b/.venv/lib/python3.10/site-packages/isort/__init__.py new file mode 100644 index 0000000..ba2bef8 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/__init__.py @@ -0,0 +1,39 @@ +"""Defines the public isort interface""" + +__all__ = ( + "Config", + "ImportKey", + "__version__", + "check_code", + "check_file", + "check_stream", + "code", + "file", + "find_imports_in_code", + "find_imports_in_file", + "find_imports_in_paths", + "find_imports_in_stream", + "place_module", + "place_module_with_reason", + "settings", + "stream", +) + +from . import settings +from ._version import __version__ +from .api import ImportKey +from .api import check_code_string as check_code +from .api import ( + check_file, + check_stream, + find_imports_in_code, + find_imports_in_file, + find_imports_in_paths, + find_imports_in_stream, + place_module, + place_module_with_reason, +) +from .api import sort_code_string as code +from .api import sort_file as file +from .api import sort_stream as stream +from .settings import Config diff --git a/.venv/lib/python3.10/site-packages/isort/__main__.py b/.venv/lib/python3.10/site-packages/isort/__main__.py new file mode 100644 index 0000000..94b1d05 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/__main__.py @@ -0,0 +1,3 @@ +from isort.main import main + +main() diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE new file mode 100644 index 0000000..e859590 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Taneli Hukkinen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py new file mode 100644 index 0000000..5b9f247 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py @@ -0,0 +1,6 @@ +"""A lil' TOML parser.""" + +__all__ = ("loads", "load", "TOMLDecodeError") +__version__ = "1.2.0" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT + +from ._parser import TOMLDecodeError, load, loads diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py new file mode 100644 index 0000000..ab36adc --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py @@ -0,0 +1,650 @@ +import string +import warnings +from types import MappingProxyType +from typing import IO, Any, Callable, Dict, FrozenSet, Iterable, NamedTuple, Optional, Tuple + +from ._re import ( + RE_DATETIME, + RE_LOCALTIME, + RE_NUMBER, + match_to_datetime, + match_to_localtime, + match_to_number, +) + +ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) + +# Neither of these sets include quotation mark or backslash. They are +# currently handled as separate cases in the parser functions. +ILLEGAL_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t") +ILLEGAL_MULTILINE_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t\n\r") + +ILLEGAL_LITERAL_STR_CHARS = ILLEGAL_BASIC_STR_CHARS +ILLEGAL_MULTILINE_LITERAL_STR_CHARS = ASCII_CTRL - frozenset("\t\n") + +ILLEGAL_COMMENT_CHARS = ILLEGAL_BASIC_STR_CHARS + +TOML_WS = frozenset(" \t") +TOML_WS_AND_NEWLINE = TOML_WS | frozenset("\n") +BARE_KEY_CHARS = frozenset(string.ascii_letters + string.digits + "-_") +KEY_INITIAL_CHARS = BARE_KEY_CHARS | frozenset("\"'") +HEXDIGIT_CHARS = frozenset(string.hexdigits) + +BASIC_STR_ESCAPE_REPLACEMENTS = MappingProxyType( + { + "\\b": "\u0008", # backspace + "\\t": "\u0009", # tab + "\\n": "\u000a", # linefeed + "\\f": "\u000c", # form feed + "\\r": "\u000d", # carriage return + '\\"': "\u0022", # quote + "\\\\": "\u005c", # backslash + } +) + +# Type annotations +ParseFloat = Callable[[str], Any] +Key = Tuple[str, ...] +Pos = int + + +class TOMLDecodeError(ValueError): + """An error raised if a document is not valid TOML.""" + + +def load(fp: IO, *, parse_float: ParseFloat = float) -> Dict[str, Any]: + """Parse TOML from a file object.""" + s = fp.read() + if isinstance(s, bytes): + s = s.decode() + else: + warnings.warn( + "Text file object support is deprecated in favor of binary file objects." + ' Use `open("foo.toml", "rb")` to open the file in binary mode.', + DeprecationWarning, + ) + return loads(s, parse_float=parse_float) + + +def loads(s: str, *, parse_float: ParseFloat = float) -> Dict[str, Any]: # noqa: C901 + """Parse TOML from a string.""" + + # The spec allows converting "\r\n" to "\n", even in string + # literals. Let's do so to simplify parsing. + src = s.replace("\r\n", "\n") + pos = 0 + out = Output(NestedDict(), Flags()) + header: Key = () + + # Parse one statement at a time + # (typically means one line in TOML source) + while True: + # 1. Skip line leading whitespace + pos = skip_chars(src, pos, TOML_WS) + + # 2. Parse rules. Expect one of the following: + # - end of file + # - end of line + # - comment + # - key/value pair + # - append dict to list (and move to its namespace) + # - create dict (and move to its namespace) + # Skip trailing whitespace when applicable. + try: + char = src[pos] + except IndexError: + break + if char == "\n": + pos += 1 + continue + if char in KEY_INITIAL_CHARS: + pos = key_value_rule(src, pos, out, header, parse_float) + pos = skip_chars(src, pos, TOML_WS) + elif char == "[": + try: + second_char: Optional[str] = src[pos + 1] + except IndexError: + second_char = None + if second_char == "[": + pos, header = create_list_rule(src, pos, out) + else: + pos, header = create_dict_rule(src, pos, out) + pos = skip_chars(src, pos, TOML_WS) + elif char != "#": + raise suffixed_err(src, pos, "Invalid statement") + + # 3. Skip comment + pos = skip_comment(src, pos) + + # 4. Expect end of line or end of file + try: + char = src[pos] + except IndexError: + break + if char != "\n": + raise suffixed_err(src, pos, "Expected newline or end of document after a statement") + pos += 1 + + return out.data.dict + + +class Flags: + """Flags that map to parsed keys/namespaces.""" + + # Marks an immutable namespace (inline array or inline table). + FROZEN = 0 + # Marks a nest that has been explicitly created and can no longer + # be opened using the "[table]" syntax. + EXPLICIT_NEST = 1 + + def __init__(self) -> None: + self._flags: Dict[str, dict] = {} + + def unset_all(self, key: Key) -> None: + cont = self._flags + for k in key[:-1]: + if k not in cont: + return + cont = cont[k]["nested"] + cont.pop(key[-1], None) + + def set_for_relative_key(self, head_key: Key, rel_key: Key, flag: int) -> None: + cont = self._flags + for k in head_key: + if k not in cont: + cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}} + cont = cont[k]["nested"] + for k in rel_key: + if k in cont: + cont[k]["flags"].add(flag) + else: + cont[k] = {"flags": {flag}, "recursive_flags": set(), "nested": {}} + cont = cont[k]["nested"] + + def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003 + cont = self._flags + key_parent, key_stem = key[:-1], key[-1] + for k in key_parent: + if k not in cont: + cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}} + cont = cont[k]["nested"] + if key_stem not in cont: + cont[key_stem] = {"flags": set(), "recursive_flags": set(), "nested": {}} + cont[key_stem]["recursive_flags" if recursive else "flags"].add(flag) + + def is_(self, key: Key, flag: int) -> bool: + if not key: + return False # document root has no flags + cont = self._flags + for k in key[:-1]: + if k not in cont: + return False + inner_cont = cont[k] + if flag in inner_cont["recursive_flags"]: + return True + cont = inner_cont["nested"] + key_stem = key[-1] + if key_stem in cont: + cont = cont[key_stem] + return flag in cont["flags"] or flag in cont["recursive_flags"] + return False + + +class NestedDict: + def __init__(self) -> None: + # The parsed content of the TOML document + self.dict: Dict[str, Any] = {} + + def get_or_create_nest( + self, + key: Key, + *, + access_lists: bool = True, + ) -> dict: + cont: Any = self.dict + for k in key: + if k not in cont: + cont[k] = {} + cont = cont[k] + if access_lists and isinstance(cont, list): + cont = cont[-1] + if not isinstance(cont, dict): + raise KeyError("There is no nest behind this key") + return cont + + def append_nest_to_list(self, key: Key) -> None: + cont = self.get_or_create_nest(key[:-1]) + last_key = key[-1] + if last_key in cont: + list_ = cont[last_key] + if not isinstance(list_, list): + raise KeyError("An object other than list found behind this key") + list_.append({}) + else: + cont[last_key] = [{}] + + +class Output(NamedTuple): + data: NestedDict + flags: Flags + + +def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos: + try: + while src[pos] in chars: + pos += 1 + except IndexError: + pass + return pos + + +def skip_until( + src: str, + pos: Pos, + expect: str, + *, + error_on: FrozenSet[str], + error_on_eof: bool, +) -> Pos: + try: + new_pos = src.index(expect, pos) + except ValueError: + new_pos = len(src) + if error_on_eof: + raise suffixed_err(src, new_pos, f'Expected "{expect!r}"') + + if not error_on.isdisjoint(src[pos:new_pos]): + while src[pos] not in error_on: + pos += 1 + raise suffixed_err(src, pos, f'Found invalid character "{src[pos]!r}"') + return new_pos + + +def skip_comment(src: str, pos: Pos) -> Pos: + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + if char == "#": + return skip_until(src, pos + 1, "\n", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False) + return pos + + +def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos: + while True: + pos_before_skip = pos + pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) + pos = skip_comment(src, pos) + if pos == pos_before_skip: + return pos + + +def create_dict_rule(src: str, pos: Pos, out: Output) -> Tuple[Pos, Key]: + pos += 1 # Skip "[" + pos = skip_chars(src, pos, TOML_WS) + pos, key = parse_key(src, pos) + + if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN): + raise suffixed_err(src, pos, f"Can not declare {key} twice") + out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) + try: + out.data.get_or_create_nest(key) + except KeyError: + raise suffixed_err(src, pos, "Can not overwrite a value") + + if not src.startswith("]", pos): + raise suffixed_err(src, pos, 'Expected "]" at the end of a table declaration') + return pos + 1, key + + +def create_list_rule(src: str, pos: Pos, out: Output) -> Tuple[Pos, Key]: + pos += 2 # Skip "[[" + pos = skip_chars(src, pos, TOML_WS) + pos, key = parse_key(src, pos) + + if out.flags.is_(key, Flags.FROZEN): + raise suffixed_err(src, pos, f"Can not mutate immutable namespace {key}") + # Free the namespace now that it points to another empty list item... + out.flags.unset_all(key) + # ...but this key precisely is still prohibited from table declaration + out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) + try: + out.data.append_nest_to_list(key) + except KeyError: + raise suffixed_err(src, pos, "Can not overwrite a value") + + if not src.startswith("]]", pos): + raise suffixed_err(src, pos, 'Expected "]]" at the end of an array declaration') + return pos + 2, key + + +def key_value_rule(src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat) -> Pos: + pos, key, value = parse_key_value_pair(src, pos, parse_float) + key_parent, key_stem = key[:-1], key[-1] + abs_key_parent = header + key_parent + + if out.flags.is_(abs_key_parent, Flags.FROZEN): + raise suffixed_err(src, pos, f"Can not mutate immutable namespace {abs_key_parent}") + # Containers in the relative path can't be opened with the table syntax after this + out.flags.set_for_relative_key(header, key, Flags.EXPLICIT_NEST) + try: + nest = out.data.get_or_create_nest(abs_key_parent) + except KeyError: + raise suffixed_err(src, pos, "Can not overwrite a value") + if key_stem in nest: + raise suffixed_err(src, pos, "Can not overwrite a value") + # Mark inline table and array namespaces recursively immutable + if isinstance(value, (dict, list)): + out.flags.set(header + key, Flags.FROZEN, recursive=True) + nest[key_stem] = value + return pos + + +def parse_key_value_pair(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, Key, Any]: + pos, key = parse_key(src, pos) + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + if char != "=": + raise suffixed_err(src, pos, 'Expected "=" after a key in a key/value pair') + pos += 1 + pos = skip_chars(src, pos, TOML_WS) + pos, value = parse_value(src, pos, parse_float) + return pos, key, value + + +def parse_key(src: str, pos: Pos) -> Tuple[Pos, Key]: + pos, key_part = parse_key_part(src, pos) + key: Key = (key_part,) + pos = skip_chars(src, pos, TOML_WS) + while True: + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + if char != ".": + return pos, key + pos += 1 + pos = skip_chars(src, pos, TOML_WS) + pos, key_part = parse_key_part(src, pos) + key += (key_part,) + pos = skip_chars(src, pos, TOML_WS) + + +def parse_key_part(src: str, pos: Pos) -> Tuple[Pos, str]: + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + if char in BARE_KEY_CHARS: + start_pos = pos + pos = skip_chars(src, pos, BARE_KEY_CHARS) + return pos, src[start_pos:pos] + if char == "'": + return parse_literal_str(src, pos) + if char == '"': + return parse_one_line_basic_str(src, pos) + raise suffixed_err(src, pos, "Invalid initial character for a key part") + + +def parse_one_line_basic_str(src: str, pos: Pos) -> Tuple[Pos, str]: + pos += 1 + return parse_basic_str(src, pos, multiline=False) + + +def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, list]: + pos += 1 + array: list = [] + + pos = skip_comments_and_array_ws(src, pos) + if src.startswith("]", pos): + return pos + 1, array + while True: + pos, val = parse_value(src, pos, parse_float) + array.append(val) + pos = skip_comments_and_array_ws(src, pos) + + c = src[pos : pos + 1] + if c == "]": + return pos + 1, array + if c != ",": + raise suffixed_err(src, pos, "Unclosed array") + pos += 1 + + pos = skip_comments_and_array_ws(src, pos) + if src.startswith("]", pos): + return pos + 1, array + + +def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, dict]: + pos += 1 + nested_dict = NestedDict() + flags = Flags() + + pos = skip_chars(src, pos, TOML_WS) + if src.startswith("}", pos): + return pos + 1, nested_dict.dict + while True: + pos, key, value = parse_key_value_pair(src, pos, parse_float) + key_parent, key_stem = key[:-1], key[-1] + if flags.is_(key, Flags.FROZEN): + raise suffixed_err(src, pos, f"Can not mutate immutable namespace {key}") + try: + nest = nested_dict.get_or_create_nest(key_parent, access_lists=False) + except KeyError: + raise suffixed_err(src, pos, "Can not overwrite a value") + if key_stem in nest: + raise suffixed_err(src, pos, f'Duplicate inline table key "{key_stem}"') + nest[key_stem] = value + pos = skip_chars(src, pos, TOML_WS) + c = src[pos : pos + 1] + if c == "}": + return pos + 1, nested_dict.dict + if c != ",": + raise suffixed_err(src, pos, "Unclosed inline table") + if isinstance(value, (dict, list)): + flags.set(key, Flags.FROZEN, recursive=True) + pos += 1 + pos = skip_chars(src, pos, TOML_WS) + + +def parse_basic_str_escape( # noqa: C901 + src: str, pos: Pos, *, multiline: bool = False +) -> Tuple[Pos, str]: + escape_id = src[pos : pos + 2] + pos += 2 + if multiline and escape_id in {"\\ ", "\\\t", "\\\n"}: + # Skip whitespace until next non-whitespace character or end of + # the doc. Error if non-whitespace is found before newline. + if escape_id != "\\\n": + pos = skip_chars(src, pos, TOML_WS) + try: + char = src[pos] + except IndexError: + return pos, "" + if char != "\n": + raise suffixed_err(src, pos, 'Unescaped "\\" in a string') + pos += 1 + pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) + return pos, "" + if escape_id == "\\u": + return parse_hex_char(src, pos, 4) + if escape_id == "\\U": + return parse_hex_char(src, pos, 8) + try: + return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id] + except KeyError: + if len(escape_id) != 2: + raise suffixed_err(src, pos, "Unterminated string") + raise suffixed_err(src, pos, 'Unescaped "\\" in a string') + + +def parse_basic_str_escape_multiline(src: str, pos: Pos) -> Tuple[Pos, str]: + return parse_basic_str_escape(src, pos, multiline=True) + + +def parse_hex_char(src: str, pos: Pos, hex_len: int) -> Tuple[Pos, str]: + hex_str = src[pos : pos + hex_len] + if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str): + raise suffixed_err(src, pos, "Invalid hex value") + pos += hex_len + hex_int = int(hex_str, 16) + if not is_unicode_scalar_value(hex_int): + raise suffixed_err(src, pos, "Escaped character is not a Unicode scalar value") + return pos, chr(hex_int) + + +def parse_literal_str(src: str, pos: Pos) -> Tuple[Pos, str]: + pos += 1 # Skip starting apostrophe + start_pos = pos + pos = skip_until(src, pos, "'", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True) + return pos + 1, src[start_pos:pos] # Skip ending apostrophe + + +def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> Tuple[Pos, str]: + pos += 3 + if src.startswith("\n", pos): + pos += 1 + + if literal: + delim = "'" + end_pos = skip_until( + src, + pos, + "'''", + error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS, + error_on_eof=True, + ) + result = src[pos:end_pos] + pos = end_pos + 3 + else: + delim = '"' + pos, result = parse_basic_str(src, pos, multiline=True) + + # Add at maximum two extra apostrophes/quotes if the end sequence + # is 4 or 5 chars long instead of just 3. + if not src.startswith(delim, pos): + return pos, result + pos += 1 + if not src.startswith(delim, pos): + return pos, result + delim + pos += 1 + return pos, result + (delim * 2) + + +def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> Tuple[Pos, str]: + if multiline: + error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS + parse_escapes = parse_basic_str_escape_multiline + else: + error_on = ILLEGAL_BASIC_STR_CHARS + parse_escapes = parse_basic_str_escape + result = "" + start_pos = pos + while True: + try: + char = src[pos] + except IndexError: + raise suffixed_err(src, pos, "Unterminated string") + if char == '"': + if not multiline: + return pos + 1, result + src[start_pos:pos] + if src.startswith('"""', pos): + return pos + 3, result + src[start_pos:pos] + pos += 1 + continue + if char == "\\": + result += src[start_pos:pos] + pos, parsed_escape = parse_escapes(src, pos) + result += parsed_escape + start_pos = pos + continue + if char in error_on: + raise suffixed_err(src, pos, f'Illegal character "{char!r}"') + pos += 1 + + +def parse_value(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, Any]: # noqa: C901 + try: + char: Optional[str] = src[pos] + except IndexError: + char = None + + # Basic strings + if char == '"': + if src.startswith('"""', pos): + return parse_multiline_str(src, pos, literal=False) + return parse_one_line_basic_str(src, pos) + + # Literal strings + if char == "'": + if src.startswith("'''", pos): + return parse_multiline_str(src, pos, literal=True) + return parse_literal_str(src, pos) + + # Booleans + if char == "t": + if src.startswith("true", pos): + return pos + 4, True + if char == "f": + if src.startswith("false", pos): + return pos + 5, False + + # Dates and times + datetime_match = RE_DATETIME.match(src, pos) + if datetime_match: + try: + datetime_obj = match_to_datetime(datetime_match) + except ValueError: + raise suffixed_err(src, pos, "Invalid date or datetime") + return datetime_match.end(), datetime_obj + localtime_match = RE_LOCALTIME.match(src, pos) + if localtime_match: + return localtime_match.end(), match_to_localtime(localtime_match) + + # Integers and "normal" floats. + # The regex will greedily match any type starting with a decimal + # char, so needs to be located after handling of dates and times. + number_match = RE_NUMBER.match(src, pos) + if number_match: + return number_match.end(), match_to_number(number_match, parse_float) + + # Arrays + if char == "[": + return parse_array(src, pos, parse_float) + + # Inline tables + if char == "{": + return parse_inline_table(src, pos, parse_float) + + # Special floats + first_three = src[pos : pos + 3] + if first_three in {"inf", "nan"}: + return pos + 3, parse_float(first_three) + first_four = src[pos : pos + 4] + if first_four in {"-inf", "+inf", "-nan", "+nan"}: + return pos + 4, parse_float(first_four) + + raise suffixed_err(src, pos, "Invalid value") + + +def suffixed_err(src: str, pos: Pos, msg: str) -> TOMLDecodeError: + """Return a `TOMLDecodeError` where error message is suffixed with + coordinates in source.""" + + def coord_repr(src: str, pos: Pos) -> str: + if pos >= len(src): + return "end of document" + line = src.count("\n", 0, pos) + 1 + if line == 1: + column = pos + 1 + else: + column = pos - src.rindex("\n", 0, pos) + return f"line {line}, column {column}" + + return TOMLDecodeError(f"{msg} (at {coord_repr(src, pos)})") + + +def is_unicode_scalar_value(codepoint: int) -> bool: + return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111) diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py new file mode 100644 index 0000000..8238aa1 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py @@ -0,0 +1,100 @@ +import re +from datetime import date, datetime, time, timedelta, timezone, tzinfo +from functools import lru_cache +from typing import TYPE_CHECKING, Any, Optional, Union + +if TYPE_CHECKING: + from tomli._parser import ParseFloat + +# E.g. +# - 00:32:00.999999 +# - 00:32:00 +_TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?" + +RE_NUMBER = re.compile( + r""" +0 +(?: + x[0-9A-Fa-f](?:_?[0-9A-Fa-f])* # hex + | + b[01](?:_?[01])* # bin + | + o[0-7](?:_?[0-7])* # oct +) +| +[+-]?(?:0|[1-9](?:_?[0-9])*) # dec, integer part +(?P + (?:\.[0-9](?:_?[0-9])*)? # optional fractional part + (?:[eE][+-]?[0-9](?:_?[0-9])*)? # optional exponent part +) +""", + flags=re.VERBOSE, +) +RE_LOCALTIME = re.compile(_TIME_RE_STR) +RE_DATETIME = re.compile( + rf""" +([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27 +(?: + [T ] + {_TIME_RE_STR} + (?:(Z)|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))? # optional time offset +)? +""", + flags=re.VERBOSE, +) + + +def match_to_datetime(match: "re.Match") -> Union[datetime, date]: + """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`. + + Raises ValueError if the match does not correspond to a valid date + or datetime. + """ + ( + year_str, + month_str, + day_str, + hour_str, + minute_str, + sec_str, + micros_str, + zulu_time, + offset_sign_str, + offset_hour_str, + offset_minute_str, + ) = match.groups() + year, month, day = int(year_str), int(month_str), int(day_str) + if hour_str is None: + return date(year, month, day) + hour, minute, sec = int(hour_str), int(minute_str), int(sec_str) + micros = int(micros_str.ljust(6, "0")) if micros_str else 0 + if offset_sign_str: + tz: Optional[tzinfo] = cached_tz(offset_hour_str, offset_minute_str, offset_sign_str) + elif zulu_time: + tz = timezone.utc + else: # local date-time + tz = None + return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz) + + +@lru_cache(maxsize=None) +def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone: + sign = 1 if sign_str == "+" else -1 + return timezone( + timedelta( + hours=sign * int(hour_str), + minutes=sign * int(minute_str), + ) + ) + + +def match_to_localtime(match: "re.Match") -> time: + hour_str, minute_str, sec_str, micros_str = match.groups() + micros = int(micros_str.ljust(6, "0")) if micros_str else 0 + return time(int(hour_str), int(minute_str), int(sec_str), micros) + + +def match_to_number(match: "re.Match", parse_float: "ParseFloat") -> Any: + if match.group("floatpart"): + return parse_float(match.group()) + return int(match.group(), 0) diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed new file mode 100644 index 0000000..7632ecf --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed @@ -0,0 +1 @@ +# Marker file for PEP 561 diff --git a/.venv/lib/python3.10/site-packages/isort/_version.py b/.venv/lib/python3.10/site-packages/isort/_version.py new file mode 100644 index 0000000..4a8ec67 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/_version.py @@ -0,0 +1,3 @@ +from importlib import metadata + +__version__ = metadata.version("isort") diff --git a/.venv/lib/python3.10/site-packages/isort/api.py b/.venv/lib/python3.10/site-packages/isort/api.py new file mode 100644 index 0000000..706ecd8 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/api.py @@ -0,0 +1,660 @@ +__all__ = ( + "ImportKey", + "check_code_string", + "check_file", + "check_stream", + "find_imports_in_code", + "find_imports_in_file", + "find_imports_in_paths", + "find_imports_in_stream", + "place_module", + "place_module_with_reason", + "sort_code_string", + "sort_file", + "sort_stream", +) + +import contextlib +import shutil +import sys +from collections.abc import Iterator +from enum import Enum +from io import StringIO +from itertools import chain +from pathlib import Path +from typing import Any, TextIO, cast +from warnings import warn + +from isort import core + +from . import files, identify, io +from .exceptions import ( + ExistingSyntaxErrors, + FileSkipComment, + FileSkipSetting, + IntroducedSyntaxErrors, +) +from .format import ask_whether_to_apply_changes_to_file, create_terminal_printer, show_unified_diff +from .io import Empty, File +from .place import module as place_module # noqa: F401 +from .place import module_with_reason as place_module_with_reason # noqa: F401 +from .settings import CYTHON_EXTENSIONS, DEFAULT_CONFIG, Config + + +class ImportKey(Enum): + """Defines how to key an individual import, generally for deduping. + + Import keys are defined from less to more specific: + + from x.y import z as a + ______| | | | + | | | | + PACKAGE | | | + ________| | | + | | | + MODULE | | + _________________| | + | | + ATTRIBUTE | + ______________________| + | + ALIAS + """ + + PACKAGE = 1 + MODULE = 2 + ATTRIBUTE = 3 + ALIAS = 4 + + +def sort_code_string( + code: str, + extension: str | None = None, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + disregard_skip: bool = False, + show_diff: bool | TextIO = False, + **config_kwargs: Any, +) -> str: + """Sorts any imports within the provided code string, returning a new string with them sorted. + + - **code**: The string of code with imports that need to be sorted. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. + - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a + TextIO stream is provided results will be written to it, otherwise no diff will be computed. + - ****config_kwargs**: Any config modifications. + """ + input_stream = StringIO(code) + output_stream = StringIO() + config = _config(path=file_path, config=config, **config_kwargs) + sort_stream( + input_stream, + output_stream, + extension=extension, + config=config, + file_path=file_path, + disregard_skip=disregard_skip, + show_diff=show_diff, + ) + output_stream.seek(0) + return output_stream.read() + + +def check_code_string( + code: str, + show_diff: bool | TextIO = False, + extension: str | None = None, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + disregard_skip: bool = False, + **config_kwargs: Any, +) -> bool: + """Checks the order, format, and categorization of imports within the provided code string. + Returns `True` if everything is correct, otherwise `False`. + + - **code**: The string of code with imports that need to be sorted. + - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a + TextIO stream is provided results will be written to it, otherwise no diff will be computed. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. + - ****config_kwargs**: Any config modifications. + """ + config = _config(path=file_path, config=config, **config_kwargs) + return check_stream( + StringIO(code), + show_diff=show_diff, + extension=extension, + config=config, + file_path=file_path, + disregard_skip=disregard_skip, + ) + + +def sort_stream( + input_stream: TextIO, + output_stream: TextIO, + extension: str | None = None, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + disregard_skip: bool = False, + show_diff: bool | TextIO = False, + raise_on_skip: bool = True, + **config_kwargs: Any, +) -> bool: + """Sorts any imports within the provided code stream, outputs to the provided output stream. + Returns `True` if anything is modified from the original input stream, otherwise `False`. + + - **input_stream**: The stream of code with imports that need to be sorted. + - **output_stream**: The stream where sorted imports should be written to. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. + - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a + TextIO stream is provided results will be written to it, otherwise no diff will be computed. + - ****config_kwargs**: Any config modifications. + """ + extension = extension or (file_path and file_path.suffix.lstrip(".")) or "py" + if show_diff: + _output_stream = StringIO() + _input_stream = StringIO(input_stream.read()) + changed = sort_stream( + input_stream=_input_stream, + output_stream=_output_stream, + extension=extension, + config=config, + file_path=file_path, + disregard_skip=disregard_skip, + raise_on_skip=raise_on_skip, + **config_kwargs, + ) + _output_stream.seek(0) + _input_stream.seek(0) + show_unified_diff( + file_input=_input_stream.read(), + file_output=_output_stream.read(), + file_path=file_path, + output=output_stream if show_diff is True else show_diff, + color_output=config.color_output, + ) + return changed + + config = _config(path=file_path, config=config, **config_kwargs) + content_source = str(file_path or "Passed in content") + if not disregard_skip and file_path and config.is_skipped(file_path): + raise FileSkipSetting(content_source) + + _internal_output = output_stream + + if config.atomic: + try: + file_content = input_stream.read() + compile(file_content, content_source, "exec", flags=0, dont_inherit=True) + except SyntaxError: + if extension not in CYTHON_EXTENSIONS: + raise ExistingSyntaxErrors(content_source) + if config.verbose: + warn( + f"{content_source} Python AST errors found but ignored due to Cython extension", + stacklevel=2, + ) + input_stream = StringIO(file_content) + + if not output_stream.readable(): + _internal_output = StringIO() + + try: + changed = core.process( + input_stream, + _internal_output, + extension=extension, + config=config, + raise_on_skip=raise_on_skip, + ) + except FileSkipComment: + raise FileSkipComment(content_source) + + if config.atomic: + _internal_output.seek(0) + try: + compile(_internal_output.read(), content_source, "exec", flags=0, dont_inherit=True) + _internal_output.seek(0) + except SyntaxError: # pragma: no cover + if extension not in CYTHON_EXTENSIONS: + raise IntroducedSyntaxErrors(content_source) + if config.verbose: + warn( + f"{content_source} Python AST errors found but ignored due to Cython extension", + stacklevel=2, + ) + if _internal_output != output_stream: + output_stream.write(_internal_output.read()) + + return changed + + +def check_stream( + input_stream: TextIO, + show_diff: bool | TextIO = False, + extension: str | None = None, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + disregard_skip: bool = False, + **config_kwargs: Any, +) -> bool: + """Checks any imports within the provided code stream, returning `False` if any unsorted or + incorrectly imports are found or `True` if no problems are identified. + + - **input_stream**: The stream of code with imports that need to be sorted. + - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a + TextIO stream is provided results will be written to it, otherwise no diff will be computed. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. + - ****config_kwargs**: Any config modifications. + """ + config = _config(path=file_path, config=config, **config_kwargs) + + if show_diff: + input_stream = StringIO(input_stream.read()) + + changed: bool = sort_stream( + input_stream=input_stream, + output_stream=Empty, + extension=extension, + config=config, + file_path=file_path, + disregard_skip=disregard_skip, + ) + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) + if not changed: + if config.verbose and not config.only_modified: + printer.success(f"{file_path or ''} Everything Looks Good!") + return True + + printer.error(f"{file_path or ''} Imports are incorrectly sorted and/or formatted.") + if show_diff: + output_stream = StringIO() + input_stream.seek(0) + file_contents = input_stream.read() + sort_stream( + input_stream=StringIO(file_contents), + output_stream=output_stream, + extension=extension, + config=config, + file_path=file_path, + disregard_skip=disregard_skip, + ) + output_stream.seek(0) + + show_unified_diff( + file_input=file_contents, + file_output=output_stream.read(), + file_path=file_path, + output=None if show_diff is True else show_diff, + color_output=config.color_output, + ) + return False + + +def check_file( + filename: str | Path, + show_diff: bool | TextIO = False, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + disregard_skip: bool = True, + extension: str | None = None, + **config_kwargs: Any, +) -> bool: + """Checks any imports within the provided file, returning `False` if any unsorted or + incorrectly imports are found or `True` if no problems are identified. + + - **filename**: The name or Path of the file to check. + - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a + TextIO stream is provided results will be written to it, otherwise no diff will be computed. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - ****config_kwargs**: Any config modifications. + """ + file_config: Config = config + + if "config_trie" in config_kwargs: + config_trie = config_kwargs.pop("config_trie", None) + if config_trie: + config_info = config_trie.search(filename) + if config.verbose: + print(f"{config_info[0]} used for file {filename}") + + file_config = Config(**config_info[1]) + + with io.File.read(filename) as source_file: + return check_stream( + source_file.stream, + show_diff=show_diff, + extension=extension, + config=file_config, + file_path=file_path or source_file.path, + disregard_skip=disregard_skip, + **config_kwargs, + ) + + +def _tmp_file(source_file: File) -> Path: + return source_file.path.with_suffix(source_file.path.suffix + ".isorted") + + +@contextlib.contextmanager +def _in_memory_output_stream_context() -> Iterator[TextIO]: + yield StringIO(newline=None) + + +@contextlib.contextmanager +def _file_output_stream_context(filename: str | Path, source_file: File) -> Iterator[TextIO]: + tmp_file = _tmp_file(source_file) + with tmp_file.open("w+", encoding=source_file.encoding, newline="") as output_stream: + shutil.copymode(filename, tmp_file) + yield output_stream + + +# Ignore DeepSource cyclomatic complexity check for this function. It is one +# the main entrypoints so sort of expected to be complex. +# skipcq: PY-R1000 +def sort_file( + filename: str | Path, + extension: str | None = None, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + disregard_skip: bool = True, + ask_to_apply: bool = False, + show_diff: bool | TextIO = False, + write_to_stdout: bool = False, + output: TextIO | None = None, + **config_kwargs: Any, +) -> bool: + """Sorts and formats any groups of imports within the provided file or Path. + Returns `True` if the file has been changed, otherwise `False`. + + - **filename**: The name or Path of the file to format. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. + - **ask_to_apply**: If `True`, prompt before applying any changes. + - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a + TextIO stream is provided results will be written to it, otherwise no diff will be computed. + - **write_to_stdout**: If `True`, write to stdout instead of the input file. + - **output**: If a TextIO is provided, results will be written there rather than replacing + the original file content. + - ****config_kwargs**: Any config modifications. + """ + file_config: Config = config + + if "config_trie" in config_kwargs: + config_trie = config_kwargs.pop("config_trie", None) + if config_trie: + config_info = config_trie.search(filename) + if config.verbose: + print(f"{config_info[0]} used for file {filename}") + + file_config = Config(**config_info[1]) + + with io.File.read(filename) as source_file: + actual_file_path = file_path or source_file.path + config = _config(path=actual_file_path, config=file_config, **config_kwargs) + changed: bool = False + try: + if write_to_stdout: + changed = sort_stream( + input_stream=source_file.stream, + output_stream=sys.stdout, + config=config, + file_path=actual_file_path, + disregard_skip=disregard_skip, + extension=extension, + ) + else: + if output is None: + try: + if config.overwrite_in_place: + output_stream_context = _in_memory_output_stream_context() + else: + output_stream_context = _file_output_stream_context( + filename, source_file + ) + with output_stream_context as output_stream: + changed = sort_stream( + input_stream=source_file.stream, + output_stream=output_stream, + config=config, + file_path=actual_file_path, + disregard_skip=disregard_skip, + extension=extension, + ) + output_stream.seek(0) + if changed: + if show_diff or ask_to_apply: + source_file.stream.seek(0) + show_unified_diff( + file_input=source_file.stream.read(), + file_output=output_stream.read(), + file_path=actual_file_path, + output=( + None if show_diff is True else cast(TextIO, show_diff) + ), + color_output=config.color_output, + ) + if show_diff or ( + ask_to_apply + and not ask_whether_to_apply_changes_to_file( + str(source_file.path) + ) + ): + return False + source_file.stream.close() + if config.overwrite_in_place: + output_stream.seek(0) + with source_file.path.open("w") as fs: + shutil.copyfileobj(output_stream, fs) + if changed: + if not config.overwrite_in_place: + tmp_file = _tmp_file(source_file) + tmp_file.replace(source_file.path) + if not config.quiet: + print(f"Fixing {source_file.path}") + finally: + if not config.overwrite_in_place: # pragma: no branch + tmp_file = _tmp_file(source_file) + tmp_file.unlink(missing_ok=True) + else: + changed = sort_stream( + input_stream=source_file.stream, + output_stream=output, + config=config, + file_path=actual_file_path, + disregard_skip=disregard_skip, + extension=extension, + ) + if changed and show_diff: + source_file.stream.seek(0) + output.seek(0) + show_unified_diff( + file_input=source_file.stream.read(), + file_output=output.read(), + file_path=actual_file_path, + output=None if show_diff is True else show_diff, + color_output=config.color_output, + ) + source_file.stream.close() + + except ExistingSyntaxErrors: + warn(f"{actual_file_path} unable to sort due to existing syntax errors", stacklevel=2) + except IntroducedSyntaxErrors: # pragma: no cover + warn( + f"{actual_file_path} unable to sort as isort introduces new syntax errors", + stacklevel=2, + ) + + return changed + + +def find_imports_in_code( + code: str, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + unique: bool | ImportKey = False, + top_only: bool = False, + **config_kwargs: Any, +) -> Iterator[identify.Import]: + """Finds and returns all imports within the provided code string. + + - **code**: The string of code with imports that need to be sorted. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **unique**: If True, only the first instance of an import is returned. + - **top_only**: If True, only return imports that occur before the first function or class. + - ****config_kwargs**: Any config modifications. + """ + yield from find_imports_in_stream( + input_stream=StringIO(code), + config=config, + file_path=file_path, + unique=unique, + top_only=top_only, + **config_kwargs, + ) + + +def find_imports_in_stream( + input_stream: TextIO, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + unique: bool | ImportKey = False, + top_only: bool = False, + _seen: set[str] | None = None, + **config_kwargs: Any, +) -> Iterator[identify.Import]: + """Finds and returns all imports within the provided code stream. + + - **input_stream**: The stream of code with imports that need to be sorted. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **unique**: If True, only the first instance of an import is returned. + - **top_only**: If True, only return imports that occur before the first function or class. + - **_seen**: An optional set of imports already seen. Generally meant only for internal use. + - ****config_kwargs**: Any config modifications. + """ + config = _config(config=config, **config_kwargs) + identified_imports = identify.imports( + input_stream, config=config, file_path=file_path, top_only=top_only + ) + if not unique: + yield from identified_imports + + seen: set[str] = set() if _seen is None else _seen + for identified_import in identified_imports: + if unique in (True, ImportKey.ALIAS): + key = identified_import.statement() + elif unique == ImportKey.ATTRIBUTE: + key = f"{identified_import.module}.{identified_import.attribute}" + elif unique == ImportKey.MODULE: + key = identified_import.module + elif unique == ImportKey.PACKAGE: # pragma: no branch # type checking ensures this + key = identified_import.module.split(".")[0] + + if key and key not in seen: + seen.add(key) + yield identified_import + + +def find_imports_in_file( + filename: str | Path, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + unique: bool | ImportKey = False, + top_only: bool = False, + **config_kwargs: Any, +) -> Iterator[identify.Import]: + """Finds and returns all imports within the provided source file. + + - **filename**: The name or Path of the file to look for imports in. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **unique**: If True, only the first instance of an import is returned. + - **top_only**: If True, only return imports that occur before the first function or class. + - ****config_kwargs**: Any config modifications. + """ + try: + with io.File.read(filename) as source_file: + yield from find_imports_in_stream( + input_stream=source_file.stream, + config=config, + file_path=file_path or source_file.path, + unique=unique, + top_only=top_only, + **config_kwargs, + ) + except OSError as error: + warn(f"Unable to parse file {filename} due to {error}", stacklevel=2) + + +def find_imports_in_paths( + paths: Iterator[str | Path], + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + unique: bool | ImportKey = False, + top_only: bool = False, + **config_kwargs: Any, +) -> Iterator[identify.Import]: + """Finds and returns all imports within the provided source paths. + + - **paths**: A collection of paths to recursively look for imports within. + - **extension**: The file extension that contains imports. Defaults to filename extension or py. + - **config**: The config object to use when sorting imports. + - **file_path**: The disk location where the code string was pulled from. + - **unique**: If True, only the first instance of an import is returned. + - **top_only**: If True, only return imports that occur before the first function or class. + - ****config_kwargs**: Any config modifications. + """ + config = _config(config=config, **config_kwargs) + seen: set[str] | None = set() if unique else None + yield from chain( + *( + find_imports_in_file( + file_name, unique=unique, config=config, top_only=top_only, _seen=seen + ) + for file_name in files.find(map(str, paths), config, [], []) + ) + ) + + +def _config( + path: Path | None = None, config: Config = DEFAULT_CONFIG, **config_kwargs: Any +) -> Config: + if path and ( + config is DEFAULT_CONFIG + and "settings_path" not in config_kwargs + and "settings_file" not in config_kwargs + ): + config_kwargs["settings_path"] = path + + if config_kwargs: + if config is not DEFAULT_CONFIG: + raise ValueError( + "You can either specify custom configuration options using kwargs or " + "passing in a Config object. Not Both!" + ) + + config = Config(**config_kwargs) + + return config diff --git a/.venv/lib/python3.10/site-packages/isort/comments.py b/.venv/lib/python3.10/site-packages/isort/comments.py new file mode 100644 index 0000000..0fdab29 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/comments.py @@ -0,0 +1,29 @@ +def parse(line: str) -> tuple[str, str]: + """Parses import lines for comments and returns back the + import statement and the associated comment. + """ + comment_start = line.find("#") + if comment_start != -1: + return (line[:comment_start], line[comment_start + 1 :].strip()) + + return (line, "") + + +def add_to_line( + comments: list[str] | None, + original_string: str = "", + removed: bool = False, + comment_prefix: str = "", +) -> str: + """Returns a string with comments added if removed is not set.""" + if removed: + return parse(original_string)[0] + + if not comments: + return original_string + + unique_comments: list[str] = [] + for comment in comments: + if comment not in unique_comments: + unique_comments.append(comment) + return f"{parse(original_string)[0]}{comment_prefix} {'; '.join(unique_comments)}" diff --git a/.venv/lib/python3.10/site-packages/isort/core.py b/.venv/lib/python3.10/site-packages/isort/core.py new file mode 100644 index 0000000..f3c2e52 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/core.py @@ -0,0 +1,513 @@ +import textwrap +from io import StringIO +from itertools import chain +from typing import TextIO + +import isort.literal +from isort.settings import DEFAULT_CONFIG, Config + +from . import output, parse +from .exceptions import ExistingSyntaxErrors, FileSkipComment +from .format import format_natural, remove_whitespace +from .settings import FILE_SKIP_COMMENTS + +CIMPORT_IDENTIFIERS = ("cimport ", "cimport*", "from.cimport") +IMPORT_START_IDENTIFIERS = ("from ", "from.import", "import ", "import*", *CIMPORT_IDENTIFIERS) +DOCSTRING_INDICATORS = ('"""', "'''") +COMMENT_INDICATORS = (*DOCSTRING_INDICATORS, "'", '"', "#") +CODE_SORT_COMMENTS = ( + "# isort: list", + "# isort: dict", + "# isort: set", + "# isort: unique-list", + "# isort: tuple", + "# isort: unique-tuple", + "# isort: assignments", +) +LITERAL_TYPE_MAPPING = {"(": "tuple", "[": "list", "{": "set"} + + +# Ignore DeepSource cyclomatic complexity check for this function. +# skipcq: PY-R1000 +def process( + input_stream: TextIO, + output_stream: TextIO, + extension: str = "py", + raise_on_skip: bool = True, + config: Config = DEFAULT_CONFIG, +) -> bool: + """Parses stream identifying sections of contiguous imports and sorting them + + Code with unsorted imports is read from the provided `input_stream`, sorted and then + outputted to the specified `output_stream`. + + - `input_stream`: Text stream with unsorted import sections. + - `output_stream`: Text stream to output sorted inputs into. + - `config`: Config settings to use when sorting imports. Defaults settings. + - *Default*: `isort.settings.DEFAULT_CONFIG`. + - `extension`: The file extension or file extension rules that should be used. + - *Default*: `"py"`. + - *Choices*: `["py", "pyi", "pyx"]`. + + Returns `True` if there were changes that needed to be made (errors present) from what + was provided in the input_stream, otherwise `False`. + """ + line_separator: str = config.line_ending + add_imports: list[str] = [format_natural(addition) for addition in config.add_imports] + import_section: str = "" + next_import_section: str = "" + next_cimports: bool = False + in_quote: str = "" + was_in_quote: bool = False + first_comment_index_start: int = -1 + first_comment_index_end: int = -1 + contains_imports: bool = False + in_top_comment: bool = False + first_import_section: bool = True + indent: str = "" + isort_off: bool = False + skip_file: bool = False + code_sorting: bool | str = False + code_sorting_section: str = "" + code_sorting_indent: str = "" + cimports: bool = False + made_changes: bool = False + stripped_line: str = "" + end_of_file: bool = False + verbose_output: list[str] = [] + lines_before: list[str] = [] + is_reexport: bool = False + reexport_rollback: int = 0 + + if config.float_to_top: + new_input = "" + current = "" + isort_off = False + for line in chain(input_stream, (None,)): + if isort_off and line is not None: + if line == "# isort: on\n": + isort_off = False + new_input += line + elif line in ("# isort: split\n", "# isort: off\n", None) or str(line).endswith( + "# isort: split\n" + ): + if line == "# isort: off\n": + isort_off = True + if current: + if add_imports: + add_line_separator = line_separator or "\n" + current += add_line_separator + add_line_separator.join(add_imports) + add_imports = [] + parsed = parse.file_contents(current, config=config) + verbose_output += parsed.verbose_output + extra_space = "" + while current and current[-1] == "\n": + extra_space += "\n" + current = current[:-1] + extra_space = extra_space.replace("\n", "", 1) + sorted_output = output.sorted_imports( + parsed, config, extension, import_type="import" + ) + made_changes = made_changes or _has_changed( + before=current, + after=sorted_output, + line_separator=parsed.line_separator, + ignore_whitespace=config.ignore_whitespace, + ) + new_input += sorted_output + new_input += extra_space + current = "" + new_input += line or "" + else: + current += line or "" + + input_stream = StringIO(new_input) + + for index, line in enumerate(chain(input_stream, (None,))): + if line is None: + if index == 0 and not config.force_adds: + return False + + not_imports = True + end_of_file = True + line = "" + if not line_separator: + line_separator = "\n" + + if code_sorting and code_sorting_section: + if is_reexport: + output_stream.seek(output_stream.tell() - reexport_rollback) + reexport_rollback = 0 + sorted_code = textwrap.indent( + isort.literal.assignment( + code_sorting_section, + str(code_sorting), + extension, + config=_indented_config(config, indent), + ), + code_sorting_indent, + ) + made_changes = made_changes or _has_changed( + before=code_sorting_section, + after=sorted_code, + line_separator=line_separator, + ignore_whitespace=config.ignore_whitespace, + ) + output_stream.write(sorted_code) + if is_reexport: + output_stream.truncate() + else: + stripped_line = line.strip() + if stripped_line and not line_separator: + line_separator = line[len(line.rstrip()) :].replace(" ", "").replace("\t", "") + + for file_skip_comment in FILE_SKIP_COMMENTS: + if file_skip_comment in line: + if raise_on_skip: + raise FileSkipComment("Passed in content") + isort_off = True + skip_file = True + + if not in_quote: + if stripped_line == "# isort: off": + isort_off = True + elif stripped_line.startswith("# isort: dont-add-imports"): + add_imports = [] + elif stripped_line.startswith("# isort: dont-add-import:"): + import_not_to_add = stripped_line.split("# isort: dont-add-import:", 1)[ + 1 + ].strip() + add_imports = [ + import_to_add + for import_to_add in add_imports + if import_to_add != import_not_to_add + ] + + if ( + (index == 0 or (index in {1, 2} and not contains_imports)) + and stripped_line.startswith("#") + and stripped_line not in config.section_comments + and stripped_line not in CODE_SORT_COMMENTS + ): + in_top_comment = True + elif in_top_comment and ( + not line.startswith("#") + or stripped_line in config.section_comments + or stripped_line in CODE_SORT_COMMENTS + ): + in_top_comment = False + first_comment_index_end = index - 1 + + was_in_quote = bool(in_quote) + if ((not stripped_line.startswith("#") or in_quote) and '"' in line) or "'" in line: + char_index = 0 + if first_comment_index_start == -1 and line.startswith(('"', "'")): + first_comment_index_start = index + while char_index < len(line): + if line[char_index] == "\\": + char_index += 1 + elif in_quote: + if line[char_index : char_index + len(in_quote)] == in_quote: + in_quote = "" + if first_comment_index_end < first_comment_index_start: + first_comment_index_end = index + elif line[char_index] in ("'", '"'): + long_quote = line[char_index : char_index + 3] + if long_quote in ('"""', "'''"): + in_quote = long_quote + char_index += 2 + else: + in_quote = line[char_index] + elif line[char_index] == "#": + break + char_index += 1 + + not_imports = bool(in_quote) or was_in_quote or in_top_comment or isort_off + if not (in_quote or was_in_quote or in_top_comment): + if isort_off: + if not skip_file and stripped_line == "# isort: on": + isort_off = False + elif stripped_line.endswith("# isort: split"): + not_imports = True + elif stripped_line in CODE_SORT_COMMENTS: + code_sorting = stripped_line.split("isort: ")[1].strip() + code_sorting_indent = line[: -len(line.lstrip())] + not_imports = True + elif config.sort_reexports and stripped_line.startswith("__all__"): + _, rhs = stripped_line.split("=") + code_sorting = LITERAL_TYPE_MAPPING.get(rhs.lstrip()[0], "tuple") + code_sorting_indent = line[: -len(line.lstrip())] + not_imports = True + code_sorting_section += line + reexport_rollback = len(line) + is_reexport = True + elif code_sorting: + if not stripped_line: + sorted_code = textwrap.indent( + isort.literal.assignment( + code_sorting_section, + str(code_sorting), + extension, + config=_indented_config(config, indent), + ), + code_sorting_indent, + ) + made_changes = made_changes or _has_changed( + before=code_sorting_section, + after=sorted_code, + line_separator=line_separator, + ignore_whitespace=config.ignore_whitespace, + ) + if is_reexport: + output_stream.seek(output_stream.tell() - reexport_rollback) + reexport_rollback = 0 + output_stream.write(sorted_code) + if is_reexport: + output_stream.truncate() + not_imports = True + code_sorting = False + code_sorting_section = "" + code_sorting_indent = "" + is_reexport = False + else: + code_sorting_section += line + line = "" + elif ( + stripped_line in config.section_comments + or stripped_line in config.section_comments_end + ): + if import_section and not contains_imports: + output_stream.write(import_section) + import_section = line + not_imports = False + else: + import_section += line + indent = line[: -len(line.lstrip())] + elif not (stripped_line or contains_imports): + not_imports = True + elif not stripped_line or ( + stripped_line.startswith("#") + and (not indent or indent + line.lstrip() == line) + and not config.treat_all_comments_as_code + and stripped_line not in config.treat_comments_as_code + ): + import_section += line + elif stripped_line.startswith(IMPORT_START_IDENTIFIERS): + new_indent = line[: -len(line.lstrip())] + import_statement = line + stripped_line = line.strip().split("#")[0] + while stripped_line.endswith("\\") or ( + "(" in stripped_line and ")" not in stripped_line + ): + if stripped_line.endswith("\\"): + while stripped_line and stripped_line.endswith("\\"): + line = input_stream.readline() + stripped_line = line.strip().split("#")[0] + import_statement += line + else: + while ")" not in stripped_line: + line = input_stream.readline() + + if not line: # end of file without closing parenthesis + raise ExistingSyntaxErrors("Parenthesis is not closed") + + stripped_line = line.strip().split("#")[0] + import_statement += line + + if ( + import_statement.lstrip().startswith("from") + and "import" not in import_statement + ): + line = import_statement + not_imports = True + else: + did_contain_imports = contains_imports + contains_imports = True + + cimport_statement: bool = False + if ( + import_statement.lstrip().startswith(CIMPORT_IDENTIFIERS) + or " cimport " in import_statement + or " cimport*" in import_statement + or " cimport(" in import_statement + or ( + ".cimport" in import_statement + and "cython.cimports" not in import_statement + ) # Allow pure python imports. See #2062 + ): + cimport_statement = True + + if cimport_statement != cimports or ( + new_indent != indent + and import_section + and (not did_contain_imports or len(new_indent) < len(indent)) + ): + indent = new_indent + if import_section: + next_cimports = cimport_statement + next_import_section = import_statement + import_statement = "" + not_imports = True + line = "" + else: + cimports = cimport_statement + else: + if new_indent != indent: + if import_section and did_contain_imports: + import_statement = indent + import_statement.lstrip() + else: + indent = new_indent + import_section += import_statement + else: + not_imports = True + + if not_imports: + if not was_in_quote and config.lines_before_imports > -1: + if line.strip() == "": + lines_before += line + continue + if not import_section: + output_stream.write("".join(lines_before)) + lines_before = [] + + raw_import_section: str = import_section + if ( + add_imports + and (stripped_line or end_of_file) + and not config.append_only + and not in_top_comment + and not was_in_quote + and not import_section + and not line.lstrip().startswith(COMMENT_INDICATORS) + and not (line.rstrip().endswith(DOCSTRING_INDICATORS) and "=" not in line) + ): + add_line_separator = line_separator or "\n" + import_section = add_line_separator.join(add_imports) + add_line_separator + if end_of_file and index != 0: + output_stream.write(add_line_separator) + contains_imports = True + add_imports = [] + + if next_import_section and not import_section: # pragma: no cover + raw_import_section = import_section = next_import_section + next_import_section = "" + + if import_section: + if add_imports and (contains_imports or not config.append_only) and not indent: + import_section = ( + line_separator.join(add_imports) + line_separator + import_section + ) + contains_imports = True + add_imports = [] + + if not indent: + import_section += line + raw_import_section += line + if not contains_imports: + output_stream.write(import_section) + + else: + leading_whitespace = import_section[: -len(import_section.lstrip())] + trailing_whitespace = import_section[len(import_section.rstrip()) :] + if first_import_section and not import_section.lstrip( + line_separator + ).startswith(COMMENT_INDICATORS): + import_section = import_section.lstrip(line_separator) + raw_import_section = raw_import_section.lstrip(line_separator) + first_import_section = False + + if indent: + import_section = "".join( + line[len(indent) :] for line in import_section.splitlines(keepends=True) + ) + + parsed_content = parse.file_contents(import_section, config=config) + verbose_output += parsed_content.verbose_output + + sorted_import_section = output.sorted_imports( + parsed_content, + _indented_config(config, indent), + extension, + import_type="cimport" if cimports else "import", + ) + if not (import_section.strip() and not sorted_import_section): + if indent: + sorted_import_section = ( + leading_whitespace + + textwrap.indent(sorted_import_section, indent).strip() + + trailing_whitespace + ) + + made_changes = made_changes or _has_changed( + before=raw_import_section, + after=sorted_import_section, + line_separator=line_separator, + ignore_whitespace=config.ignore_whitespace, + ) + output_stream.write(sorted_import_section) + if not line and not indent and next_import_section: + output_stream.write(line_separator) + + if indent: + output_stream.write(line) + if not next_import_section: + indent = "" + + if next_import_section: + cimports = next_cimports + contains_imports = True + else: + contains_imports = False + import_section = next_import_section + next_import_section = "" + else: + output_stream.write(line) + not_imports = False + + if stripped_line and not in_quote and not import_section and not next_import_section: + if stripped_line == "yield": + while not stripped_line or stripped_line == "yield": + new_line = input_stream.readline() + if not new_line: + break + + output_stream.write(new_line) + stripped_line = new_line.strip().split("#")[0] + + if stripped_line.startswith(("raise", "yield")): + while stripped_line.endswith("\\"): + new_line = input_stream.readline() + if not new_line: + break + + output_stream.write(new_line) + stripped_line = new_line.strip().split("#")[0] + + if made_changes and config.only_modified: + for output_str in verbose_output: + print(output_str) + + return made_changes + + +def _indented_config(config: Config, indent: str) -> Config: + if not indent: + return config + + return Config( + config=config, + line_length=max(config.line_length - len(indent), 0), + wrap_length=max(config.wrap_length - len(indent), 0), + lines_after_imports=1, + import_headings=config.import_headings if config.indented_import_headings else {}, + import_footers=config.import_footers if config.indented_import_headings else {}, + ) + + +def _has_changed(before: str, after: str, line_separator: str, ignore_whitespace: bool) -> bool: + if ignore_whitespace: + return ( + remove_whitespace(before, line_separator=line_separator).strip() + != remove_whitespace(after, line_separator=line_separator).strip() + ) + return before.strip() != after.strip() diff --git a/.venv/lib/python3.10/site-packages/isort/deprecated/__init__.py b/.venv/lib/python3.10/site-packages/isort/deprecated/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.10/site-packages/isort/deprecated/finders.py b/.venv/lib/python3.10/site-packages/isort/deprecated/finders.py new file mode 100644 index 0000000..1bfb300 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/deprecated/finders.py @@ -0,0 +1,392 @@ +"""Finders try to find right section for passed module name""" + +import importlib.machinery +import inspect +import os +import os.path +import re +import sys +import sysconfig +from abc import ABCMeta, abstractmethod +from collections.abc import Iterable, Iterator, Sequence +from contextlib import contextmanager +from fnmatch import fnmatch +from functools import lru_cache +from glob import glob +from pathlib import Path +from re import Pattern + +from isort import sections +from isort.settings import KNOWN_SECTION_MAPPING, Config +from isort.utils import exists_case_sensitive + +try: + from pipreqs import pipreqs # type: ignore + +except ImportError: + pipreqs = None + +try: + from pip_api import parse_requirements # type: ignore + +except ImportError: + parse_requirements = None # type: ignore[assignment] + + +@contextmanager +def chdir(path: str) -> Iterator[None]: + """Context manager for changing dir and restoring previous workdir after exit.""" + curdir = os.getcwd() + os.chdir(path) + try: + yield + finally: + os.chdir(curdir) + + +class BaseFinder(metaclass=ABCMeta): + def __init__(self, config: Config) -> None: + self.config = config + + @abstractmethod + def find(self, module_name: str) -> str | None: + raise NotImplementedError + + +class ForcedSeparateFinder(BaseFinder): + def find(self, module_name: str) -> str | None: + for forced_separate in self.config.forced_separate: + # Ensure all forced_separate patterns will match to end of string + path_glob = forced_separate + if not forced_separate.endswith("*"): + path_glob = f"{forced_separate}*" + + if fnmatch(module_name, path_glob) or fnmatch(module_name, "." + path_glob): + return forced_separate + return None + + +class LocalFinder(BaseFinder): + def find(self, module_name: str) -> str | None: + if module_name.startswith("."): + return "LOCALFOLDER" + return None + + +class KnownPatternFinder(BaseFinder): + def __init__(self, config: Config) -> None: + super().__init__(config) + + self.known_patterns: list[tuple[Pattern[str], str]] = [] + for placement in reversed(config.sections): + known_placement = KNOWN_SECTION_MAPPING.get(placement, placement).lower() + config_key = f"known_{known_placement}" + known_patterns = list( + getattr(self.config, config_key, self.config.known_other.get(known_placement, [])) + ) + known_patterns = [ + pattern + for known_pattern in known_patterns + for pattern in self._parse_known_pattern(known_pattern) + ] + for known_pattern in known_patterns: + regexp = "^" + known_pattern.replace("*", ".*").replace("?", ".?") + "$" + self.known_patterns.append((re.compile(regexp), placement)) + + def _parse_known_pattern(self, pattern: str) -> list[str]: + """Expand pattern if identified as a directory and return found sub packages""" + if pattern.endswith(os.path.sep): + patterns = [ + filename + for filename in os.listdir(os.path.join(self.config.directory, pattern)) + if os.path.isdir(os.path.join(self.config.directory, pattern, filename)) + ] + else: + patterns = [pattern] + + return patterns + + def find(self, module_name: str) -> str | None: + # Try to find most specific placement instruction match (if any) + parts = module_name.split(".") + module_names_to_check = (".".join(parts[:first_k]) for first_k in range(len(parts), 0, -1)) + for module_name_to_check in module_names_to_check: + for pattern, placement in self.known_patterns: + if pattern.match(module_name_to_check): + return placement + return None + + +class PathFinder(BaseFinder): + def __init__(self, config: Config, path: str = ".") -> None: + super().__init__(config) + + # restore the original import path (i.e. not the path to bin/isort) + root_dir = os.path.abspath(path) + src_dir = f"{root_dir}/src" + self.paths = [root_dir, src_dir] + + # virtual env + self.virtual_env = self.config.virtual_env or os.environ.get("VIRTUAL_ENV") + if self.virtual_env: + self.virtual_env = os.path.realpath(self.virtual_env) + self.virtual_env_src = "" + if self.virtual_env: + self.virtual_env_src = f"{self.virtual_env}/src/" + for venv_path in glob(f"{self.virtual_env}/lib/python*/site-packages"): + if venv_path not in self.paths: + self.paths.append(venv_path) + for nested_venv_path in glob(f"{self.virtual_env}/lib/python*/*/site-packages"): + if nested_venv_path not in self.paths: + self.paths.append(nested_venv_path) + for venv_src_path in glob(f"{self.virtual_env}/src/*"): + if os.path.isdir(venv_src_path): + self.paths.append(venv_src_path) + + # conda + self.conda_env = self.config.conda_env or os.environ.get("CONDA_PREFIX") or "" + if self.conda_env: + self.conda_env = os.path.realpath(self.conda_env) + for conda_path in glob(f"{self.conda_env}/lib/python*/site-packages"): + if conda_path not in self.paths: + self.paths.append(conda_path) + for nested_conda_path in glob(f"{self.conda_env}/lib/python*/*/site-packages"): + if nested_conda_path not in self.paths: + self.paths.append(nested_conda_path) + + # handle case-insensitive paths on windows + self.stdlib_lib_prefix = os.path.normcase(sysconfig.get_paths()["stdlib"]) + if self.stdlib_lib_prefix not in self.paths: + self.paths.append(self.stdlib_lib_prefix) + + # add system paths + for system_path in sys.path[1:]: + if system_path not in self.paths: + self.paths.append(system_path) + + def find(self, module_name: str) -> str | None: + for prefix in self.paths: + package_path = "/".join((prefix, module_name.split(".")[0])) + path_obj = Path(package_path).resolve() + is_module = ( + exists_case_sensitive(package_path + ".py") + or any( + exists_case_sensitive(package_path + ext_suffix) + for ext_suffix in importlib.machinery.EXTENSION_SUFFIXES + ) + or exists_case_sensitive(package_path + "/__init__.py") + ) + is_package = exists_case_sensitive(package_path) and os.path.isdir(package_path) + if is_module or is_package: + if ( + "site-packages" in prefix + or "dist-packages" in prefix + or (self.virtual_env and self.virtual_env_src in prefix) + ): + return sections.THIRDPARTY + if os.path.normcase(prefix) == self.stdlib_lib_prefix: + return sections.STDLIB + if self.conda_env and self.conda_env in prefix: + return sections.THIRDPARTY + for src_path in self.config.src_paths: + if src_path in path_obj.parents and not self.config.is_skipped(path_obj): + return sections.FIRSTPARTY + + if os.path.normcase(prefix).startswith(self.stdlib_lib_prefix): + return sections.STDLIB # pragma: no cover - edge case for one OS. Hard to test. + + return self.config.default_section + return None + + +class ReqsBaseFinder(BaseFinder): + enabled = False + + def __init__(self, config: Config, path: str = ".") -> None: + super().__init__(config) + self.path = path + if self.enabled: + self.mapping = self._load_mapping() + self.names = self._load_names() + + @abstractmethod + def _get_names(self, path: str) -> Iterator[str]: + raise NotImplementedError + + @abstractmethod + def _get_files_from_dir(self, path: str) -> Iterator[str]: + raise NotImplementedError + + @staticmethod + def _load_mapping() -> dict[str, str] | None: + """Return list of mappings `package_name -> module_name` + + Example: + django-haystack -> haystack + """ + if not pipreqs: + return None + path = os.path.dirname(inspect.getfile(pipreqs)) + path = os.path.join(path, "mapping") + with open(path) as f: + mappings: dict[str, str] = {} # pypi_name: import_name + for line in f: + import_name, _, pypi_name = line.strip().partition(":") + mappings[pypi_name] = import_name + return mappings + + def _load_names(self) -> list[str]: + """Return list of thirdparty modules from requirements""" + names: list[str] = [] + for path in self._get_files(): + names.extend(self._normalize_name(name) for name in self._get_names(path)) + return names + + @staticmethod + def _get_parents(path: str) -> Iterator[str]: + prev = "" + while path != prev: + prev = path + yield path + path = os.path.dirname(path) + + def _get_files(self) -> Iterator[str]: + """Return paths to all requirements files""" + path = os.path.abspath(self.path) + if os.path.isfile(path): + path = os.path.dirname(path) + + for path in self._get_parents(path): # noqa + yield from self._get_files_from_dir(path) + + def _normalize_name(self, name: str) -> str: + """Convert package name to module name + + Examples: + Django -> django + django-haystack -> django_haystack + Flask-RESTFul -> flask_restful + """ + if self.mapping: + name = self.mapping.get(name.replace("-", "_"), name) + return name.lower().replace("-", "_") + + def find(self, module_name: str) -> str | None: + # required lib not installed yet + if not self.enabled: + return None + + module_name, _sep, _submodules = module_name.partition(".") + module_name = module_name.lower() + if not module_name: + return None + + for name in self.names: + if module_name == name: + return sections.THIRDPARTY + return None + + +class RequirementsFinder(ReqsBaseFinder): + exts = (".txt", ".in") + enabled = bool(parse_requirements) + + def _get_files_from_dir(self, path: str) -> Iterator[str]: + """Return paths to requirements files from passed dir.""" + yield from self._get_files_from_dir_cached(path) + + @classmethod + @lru_cache(maxsize=16) + def _get_files_from_dir_cached(cls, path: str) -> list[str]: + results: list[str] = [] + + for fname in os.listdir(path): + if "requirements" not in fname: + continue + full_path = os.path.join(path, fname) + + # *requirements*/*.{txt,in} + if os.path.isdir(full_path): + for subfile_name in os.listdir(full_path): + results.extend( + os.path.join(full_path, subfile_name) + for ext in cls.exts + if subfile_name.endswith(ext) + ) + continue + + # *requirements*.{txt,in} + if os.path.isfile(full_path): + for ext in cls.exts: + if fname.endswith(ext): + results.append(full_path) + break + + return results + + def _get_names(self, path: str) -> Iterator[str]: + """Load required packages from path to requirements file""" + yield from self._get_names_cached(path) + + @classmethod + @lru_cache(maxsize=16) + def _get_names_cached(cls, path: str) -> list[str]: + result: list[str] = [] + + with chdir(os.path.dirname(path)): + requirements = parse_requirements(Path(path)) + result.extend(req.name for req in requirements.values() if req.name) + + return result + + +class DefaultFinder(BaseFinder): + def find(self, module_name: str) -> str | None: + return self.config.default_section + + +class FindersManager: + _default_finders_classes: Sequence[type[BaseFinder]] = ( + ForcedSeparateFinder, + LocalFinder, + KnownPatternFinder, + PathFinder, + RequirementsFinder, + DefaultFinder, + ) + + def __init__( + self, config: Config, finder_classes: Iterable[type[BaseFinder]] | None = None + ) -> None: + self.verbose: bool = config.verbose + + if finder_classes is None: + finder_classes = self._default_finders_classes + finders: list[BaseFinder] = [] + for finder_cls in finder_classes: + try: + finders.append(finder_cls(config)) + except Exception as exception: + # if one finder fails to instantiate isort can continue using the rest + if self.verbose: + print( + f"{finder_cls.__name__} encountered an error ({exception}) during " + "instantiation and cannot be used" + ) + self.finders: tuple[BaseFinder, ...] = tuple(finders) + + def find(self, module_name: str) -> str | None: + for finder in self.finders: + try: + section = finder.find(module_name) + if section is not None: + return section + except Exception as exception: + # isort has to be able to keep trying to identify the correct + # import section even if one approach fails + if self.verbose: + print( + f"{finder.__class__.__name__} encountered an error ({exception}) while " + f"trying to identify the {module_name} module" + ) + return None diff --git a/.venv/lib/python3.10/site-packages/isort/exceptions.py b/.venv/lib/python3.10/site-packages/isort/exceptions.py new file mode 100644 index 0000000..34d4e30 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/exceptions.py @@ -0,0 +1,197 @@ +"""All isort specific exception classes should be defined here""" + +from functools import partial +from pathlib import Path +from typing import Any + +from .profiles import profiles + + +class ISortError(Exception): + """Base isort exception object from which all isort sourced exceptions should inherit""" + + def __reduce__(self): # type: ignore + return (partial(type(self), **self.__dict__), ()) + + +class InvalidSettingsPath(ISortError): + """Raised when a settings path is provided that is neither a valid file or directory""" + + def __init__(self, settings_path: str): + super().__init__( + f"isort was told to use the settings_path: {settings_path} as the base directory or " + "file that represents the starting point of config file discovery, but it does not " + "exist." + ) + self.settings_path = settings_path + + +class ExistingSyntaxErrors(ISortError): + """Raised when isort is told to sort imports within code that has existing syntax errors""" + + def __init__(self, file_path: str): + super().__init__( + f"isort was told to sort imports within code that contains syntax errors: {file_path}." + ) + self.file_path = file_path + + +class IntroducedSyntaxErrors(ISortError): + """Raised when isort has introduced a syntax error in the process of sorting imports""" + + def __init__(self, file_path: str): + super().__init__( + f"isort introduced syntax errors when attempting to sort the imports contained within " + f"{file_path}." + ) + self.file_path = file_path + + +class FileSkipped(ISortError): + """Should be raised when a file is skipped for any reason""" + + def __init__(self, message: str, file_path: str): + super().__init__(message) + self.message = message + self.file_path = file_path + + +class FileSkipComment(FileSkipped): + """Raised when an entire file is skipped due to a isort skip file comment""" + + def __init__(self, file_path: str, **kwargs: str): + super().__init__( + f"{file_path} contains a file skip comment and was skipped.", file_path=file_path + ) + + +class FileSkipSetting(FileSkipped): + """Raised when an entire file is skipped due to provided isort settings""" + + def __init__(self, file_path: str, **kwargs: str): + super().__init__( + f"{file_path} was skipped as it's listed in 'skip' setting" + " or matches a glob in 'skip_glob' setting", + file_path=file_path, + ) + + +class ProfileDoesNotExist(ISortError): + """Raised when a profile is set by the user that doesn't exist""" + + def __init__(self, profile: str): + super().__init__( + f"Specified profile of {profile} does not exist. " + f"Available profiles: {','.join(profiles)}." + ) + self.profile = profile + + +class SortingFunctionDoesNotExist(ISortError): + """Raised when the specified sorting function isn't available""" + + def __init__(self, sort_order: str, available_sort_orders: list[str]): + super().__init__( + f"Specified sort_order of {sort_order} does not exist. " + f"Available sort_orders: {','.join(available_sort_orders)}." + ) + self.sort_order = sort_order + self.available_sort_orders = available_sort_orders + + +class FormattingPluginDoesNotExist(ISortError): + """Raised when a formatting plugin is set by the user that doesn't exist""" + + def __init__(self, formatter: str): + super().__init__(f"Specified formatting plugin of {formatter} does not exist. ") + self.formatter = formatter + + +class LiteralParsingFailure(ISortError): + """Raised when one of isorts literal sorting comments is used but isort can't parse the + the given data structure. + """ + + def __init__(self, code: str, original_error: Exception | type[Exception]): + super().__init__( + f"isort failed to parse the given literal {code}. It's important to note " + "that isort literal sorting only supports simple literals parsable by " + f"ast.literal_eval which gave the exception of {original_error}." + ) + self.code = code + self.original_error = original_error + + +class LiteralSortTypeMismatch(ISortError): + """Raised when an isort literal sorting comment is used, with a type that doesn't match the + supplied data structure's type. + """ + + def __init__(self, kind: type, expected_kind: type): + super().__init__( + f"isort was told to sort a literal of type {expected_kind} but was given " + f"a literal of type {kind}." + ) + self.kind = kind + self.expected_kind = expected_kind + + +class AssignmentsFormatMismatch(ISortError): + """Raised when isort is told to sort assignments but the format of the assignment section + doesn't match isort's expectation. + """ + + def __init__(self, code: str): + super().__init__( + "isort was told to sort a section of assignments, however the given code:\n\n" + f"{code}\n\n" + "Does not match isort's strict single line formatting requirement for assignment " + "sorting:\n\n" + "{variable_name} = {value}\n" + "{variable_name2} = {value2}\n" + "...\n\n" + ) + self.code = code + + +class UnsupportedSettings(ISortError): + """Raised when settings are passed into isort (either from config, CLI, or runtime) + that it doesn't support. + """ + + @staticmethod + def _format_option(name: str, value: Any, source: str) -> str: + return f"\t- {name} = {value} (source: '{source}')" + + def __init__(self, unsupported_settings: dict[str, dict[str, str]]): + errors = "\n".join( + self._format_option(name, **option) for name, option in unsupported_settings.items() + ) + + super().__init__( + "isort was provided settings that it doesn't support:\n\n" + f"{errors}\n\n" + "For a complete and up-to-date listing of supported settings see: " + "https://pycqa.github.io/isort/docs/configuration/options.\n" + ) + self.unsupported_settings = unsupported_settings + + +class UnsupportedEncoding(ISortError): + """Raised when isort encounters an encoding error while trying to read a file""" + + def __init__(self, filename: str | Path): + super().__init__(f"Unknown or unsupported encoding in {filename}") + self.filename = filename + + +class MissingSection(ISortError): + """Raised when isort encounters an import that matches a section that is not defined""" + + def __init__(self, import_module: str, section: str): + super().__init__( + f"Found {import_module} import while parsing, but {section} was not included " + "in the `sections` setting of your config. Please add it before continuing\n" + "See https://pycqa.github.io/isort/#custom-sections-and-ordering " + "for more info." + ) diff --git a/.venv/lib/python3.10/site-packages/isort/files.py b/.venv/lib/python3.10/site-packages/isort/files.py new file mode 100644 index 0000000..c23674a --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/files.py @@ -0,0 +1,41 @@ +import os +from collections.abc import Iterable, Iterator +from pathlib import Path + +from isort.settings import Config + + +def find( + paths: Iterable[str], config: Config, skipped: list[str], broken: list[str] +) -> Iterator[str]: + """Fines and provides an iterator for all Python source files defined in paths.""" + visited_dirs: set[Path] = set() + + for path in paths: + if os.path.isdir(path): + for dirpath, dirnames, filenames in os.walk( + path, topdown=True, followlinks=config.follow_links + ): + base_path = Path(dirpath) + for dirname in list(dirnames): + full_path = base_path / dirname + resolved_path = full_path.resolve() + if config.is_skipped(full_path): + skipped.append(str(full_path)) + dirnames.remove(dirname) + else: + if resolved_path in visited_dirs: # pragma: no cover + dirnames.remove(dirname) + visited_dirs.add(resolved_path) + + for filename in filenames: + filepath = os.path.join(dirpath, filename) + if config.is_supported_filetype(filepath): + if config.is_skipped(Path(os.path.abspath(filepath))): + skipped.append(os.path.abspath(filepath)) + else: + yield filepath + elif not os.path.exists(path): + broken.append(path) + else: + yield path diff --git a/.venv/lib/python3.10/site-packages/isort/format.py b/.venv/lib/python3.10/site-packages/isort/format.py new file mode 100644 index 0000000..5ad9f47 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/format.py @@ -0,0 +1,157 @@ +import re +import sys +from datetime import datetime +from difflib import unified_diff +from pathlib import Path +from typing import TextIO + +try: + import colorama +except ImportError: + colorama_unavailable = True +else: + colorama_unavailable = False + + +ADDED_LINE_PATTERN = re.compile(r"\+[^+]") +REMOVED_LINE_PATTERN = re.compile(r"-[^-]") + + +def format_simplified(import_line: str) -> str: + import_line = import_line.strip() + if import_line.startswith("from "): + import_line = import_line.replace("from ", "") + import_line = import_line.replace(" import ", ".") + elif import_line.startswith("import "): + import_line = import_line.replace("import ", "") + + return import_line + + +def format_natural(import_line: str) -> str: + import_line = import_line.strip() + if not import_line.startswith("from ") and not import_line.startswith("import "): + if "." not in import_line: + return f"import {import_line}" + parts = import_line.split(".") + end = parts.pop(-1) + return f"from {'.'.join(parts)} import {end}" + + return import_line + + +def show_unified_diff( + *, + file_input: str, + file_output: str, + file_path: Path | None, + output: TextIO | None = None, + color_output: bool = False, +) -> None: + """Shows a unified_diff for the provided input and output against the provided file path. + + - **file_input**: A string that represents the contents of a file before changes. + - **file_output**: A string that represents the contents of a file after changes. + - **file_path**: A Path object that represents the file path of the file being changed. + - **output**: A stream to output the diff to. If non is provided uses sys.stdout. + - **color_output**: Use color in output if True. + """ + printer = create_terminal_printer(color_output, output) + file_name = "" if file_path is None else str(file_path) + file_mtime = str( + datetime.now() if file_path is None else datetime.fromtimestamp(file_path.stat().st_mtime) + ) + unified_diff_lines = unified_diff( + file_input.splitlines(keepends=True), + file_output.splitlines(keepends=True), + fromfile=file_name + ":before", + tofile=file_name + ":after", + fromfiledate=file_mtime, + tofiledate=str(datetime.now()), + ) + for line in unified_diff_lines: + printer.diff_line(line) + + +def ask_whether_to_apply_changes_to_file(file_path: str) -> bool: + answer = None + while answer not in ("yes", "y", "no", "n", "quit", "q"): + answer = input(f"Apply suggested changes to '{file_path}' [y/n/q]? ") # nosec + answer = answer.lower() + if answer in ("no", "n"): + return False + if answer in ("quit", "q"): + sys.exit(1) + return True + + +def remove_whitespace(content: str, line_separator: str = "\n") -> str: + content = content.replace(line_separator, "").replace(" ", "").replace("\x0c", "") + return content + + +class BasicPrinter: + ERROR = "ERROR" + SUCCESS = "SUCCESS" + + def __init__(self, error: str, success: str, output: TextIO | None = None): + self.output = output or sys.stdout + self.success_message = success + self.error_message = error + + def success(self, message: str) -> None: + print(self.success_message.format(success=self.SUCCESS, message=message), file=self.output) + + def error(self, message: str) -> None: + print(self.error_message.format(error=self.ERROR, message=message), file=sys.stderr) + + def diff_line(self, line: str) -> None: + self.output.write(line) + + +class ColoramaPrinter(BasicPrinter): + def __init__(self, error: str, success: str, output: TextIO | None): + super().__init__(error, success, output=output) + + # Note: this constants are instance variables instead ofs class variables + # because they refer to colorama which might not be installed. + self.ERROR = self.style_text("ERROR", colorama.Fore.RED) + self.SUCCESS = self.style_text("SUCCESS", colorama.Fore.GREEN) + self.ADDED_LINE = colorama.Fore.GREEN + self.REMOVED_LINE = colorama.Fore.RED + + @staticmethod + def style_text(text: str, style: str | None = None) -> str: + if style is None: + return text + return style + text + str(colorama.Style.RESET_ALL) + + def diff_line(self, line: str) -> None: + style = None + if re.match(ADDED_LINE_PATTERN, line): + style = self.ADDED_LINE + elif re.match(REMOVED_LINE_PATTERN, line): + style = self.REMOVED_LINE + self.output.write(self.style_text(line, style)) + + +def create_terminal_printer( + color: bool, output: TextIO | None = None, error: str = "", success: str = "" +) -> BasicPrinter: + if color and colorama_unavailable: + no_colorama_message = ( + "\n" + "Sorry, but to use --color (color_output) the colorama python package is required.\n\n" + "Reference: https://pypi.org/project/colorama/\n\n" + "You can either install it separately on your system or as the colors extra " + "for isort. Ex: \n\n" + "$ pip install isort[colors]\n" + ) + print(no_colorama_message, file=sys.stderr) + sys.exit(1) + + if not colorama_unavailable: + colorama.init(strip=False) + return ( + ColoramaPrinter(error, success, output) if color else BasicPrinter(error, success, output) + ) diff --git a/.venv/lib/python3.10/site-packages/isort/hooks.py b/.venv/lib/python3.10/site-packages/isort/hooks.py new file mode 100644 index 0000000..af9020f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/hooks.py @@ -0,0 +1,93 @@ +"""Defines a git hook to allow pre-commit warnings and errors about import order. + +usage: + exit_code = git_hook(strict=True|False, modify=True|False) +""" + +import os +import subprocess # nosec +from pathlib import Path + +from isort import Config, api, exceptions + + +def get_output(command: list[str]) -> str: + """Run a command and return raw output + + :param str command: the command to run + :returns: the stdout output of the command + """ + result = subprocess.run(command, stdout=subprocess.PIPE, check=True) # nosec + return result.stdout.decode() + + +def get_lines(command: list[str]) -> list[str]: + """Run a command and return lines of output + + :param str command: the command to run + :returns: list of whitespace-stripped lines output by command + """ + stdout = get_output(command) + return [line.strip() for line in stdout.splitlines()] + + +def git_hook( + strict: bool = False, + modify: bool = False, + lazy: bool = False, + settings_file: str = "", + directories: list[str] | None = None, +) -> int: + """Git pre-commit hook to check staged files for isort errors + + :param bool strict - if True, return number of errors on exit, + causing the hook to fail. If False, return zero so it will + just act as a warning. + :param bool modify - if True, fix the sources if they are not + sorted properly. If False, only report result without + modifying anything. + :param bool lazy - if True, also check/fix unstaged files. + This is useful if you frequently use ``git commit -a`` for example. + If False, only check/fix the staged files for isort errors. + :param str settings_file - A path to a file to be used as + the configuration file for this run. + When settings_file is the empty string, the configuration file + will be searched starting at the directory containing the first + staged file, if any, and going upward in the directory structure. + :param list[str] directories - A list of directories to restrict the hook to. + + :return number of errors if in strict mode, 0 otherwise. + """ + # Get list of files modified and staged + diff_cmd = ["git", "diff-index", "--cached", "--name-only", "--diff-filter=ACMRTUXB", "HEAD"] + if lazy: + diff_cmd.remove("--cached") + if directories: + diff_cmd.extend(directories) + + files_modified = get_lines(diff_cmd) + if not files_modified: + return 0 + + errors = 0 + config = Config( + settings_file=settings_file, + settings_path=os.path.dirname(os.path.abspath(files_modified[0])), + ) + for filename in files_modified: + if filename.endswith(".py"): + # Get the staged contents of the file + staged_cmd = ["git", "show", f":{filename}"] + staged_contents = get_output(staged_cmd) + + try: + if not api.check_code_string( + staged_contents, file_path=Path(filename), config=config + ): + errors += 1 + if modify: + api.sort_file(filename, config=config) + except exceptions.FileSkipped: # pragma: no cover + pass + + return errors if strict else 0 diff --git a/.venv/lib/python3.10/site-packages/isort/identify.py b/.venv/lib/python3.10/site-packages/isort/identify.py new file mode 100644 index 0000000..fad5330 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/identify.py @@ -0,0 +1,208 @@ +"""Fast stream based import identification. +Eventually this will likely replace parse.py +""" + +from collections.abc import Iterator +from functools import partial +from pathlib import Path +from typing import NamedTuple, TextIO + +from isort.parse import normalize_line, skip_line, strip_syntax + +from .comments import parse as parse_comments +from .settings import DEFAULT_CONFIG, Config + +STATEMENT_DECLARATIONS: tuple[str, ...] = ("def ", "cdef ", "cpdef ", "class ", "@", "async def") + + +class Import(NamedTuple): + line_number: int + indented: bool + module: str + attribute: str | None = None + alias: str | None = None + cimport: bool = False + file_path: Path | None = None + + def statement(self) -> str: + import_cmd = "cimport" if self.cimport else "import" + if self.attribute: + import_string = f"from {self.module} {import_cmd} {self.attribute}" + else: + import_string = f"{import_cmd} {self.module}" + if self.alias: + import_string += f" as {self.alias}" + return import_string + + def __str__(self) -> str: + return ( + f"{self.file_path or ''}:{self.line_number} " + f"{'indented ' if self.indented else ''}{self.statement()}" + ) + + +def imports( + input_stream: TextIO, + config: Config = DEFAULT_CONFIG, + file_path: Path | None = None, + top_only: bool = False, +) -> Iterator[Import]: + """Parses a python file taking out and categorizing imports.""" + in_quote = "" + + indexed_input = enumerate(input_stream) + for index, raw_line in indexed_input: + (skipping_line, in_quote) = skip_line( + raw_line, in_quote=in_quote, index=index, section_comments=config.section_comments + ) + + if top_only and not in_quote and raw_line.startswith(STATEMENT_DECLARATIONS): + break + if skipping_line: + continue + + stripped_line = raw_line.strip().split("#")[0] + if stripped_line.startswith(("raise", "yield")): + if stripped_line == "yield": + while not stripped_line or stripped_line == "yield": + try: + index, next_line = next(indexed_input) + except StopIteration: + break + + stripped_line = next_line.strip().split("#")[0] + while stripped_line.endswith("\\"): + try: + index, next_line = next(indexed_input) + except StopIteration: + break + + stripped_line = next_line.strip().split("#")[0] + continue # pragma: no cover + + line, *end_of_line_comment = raw_line.split("#", 1) + statements = [line.strip() for line in line.split(";")] + if end_of_line_comment: + statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" + + for statement in statements: + line, _raw_line = normalize_line(statement) + if line.startswith(("import ", "cimport ")): + type_of_import = "straight" + elif line.startswith("from "): + type_of_import = "from" + else: + continue # pragma: no cover + + import_string, _ = parse_comments(line) + normalized_import_string = ( + import_string.replace("import(", "import (").replace("\\", " ").replace("\n", " ") + ) + cimports: bool = ( + " cimport " in normalized_import_string + or normalized_import_string.startswith("cimport") + ) + identified_import = partial( + Import, + index + 1, # line numbers use 1 based indexing + raw_line.startswith((" ", "\t")), + cimport=cimports, + file_path=file_path, + ) + + if "(" in line.split("#", 1)[0]: + while not line.split("#")[0].strip().endswith(")"): + try: + index, next_line = next(indexed_input) + except StopIteration: + break + + line, _ = parse_comments(next_line) + import_string += "\n" + line + else: + while line.strip().endswith("\\"): + try: + index, next_line = next(indexed_input) + except StopIteration: + break + + line, _ = parse_comments(next_line) + + # Still need to check for parentheses after an escaped line + if "(" in line.split("#")[0] and ")" not in line.split("#")[0]: + import_string += "\n" + line + + while not line.split("#")[0].strip().endswith(")"): + try: + index, next_line = next(indexed_input) + except StopIteration: + break + line, _ = parse_comments(next_line) + import_string += "\n" + line + else: + if import_string.strip().endswith( + (" import", " cimport") + ) or line.strip().startswith(("import ", "cimport ")): + import_string += "\n" + line + else: + import_string = ( + import_string.rstrip().rstrip("\\") + " " + line.lstrip() + ) + + if type_of_import == "from": + import_string = ( + import_string.replace("import(", "import (") + .replace("\\", " ") + .replace("\n", " ") + ) + parts = import_string.split(" cimport " if cimports else " import ") + + from_import = parts[0].split(" ") + import_string = (" cimport " if cimports else " import ").join( + [from_import[0] + " " + "".join(from_import[1:]), *parts[1:]] + ) + + just_imports = [ + item.replace("{|", "{ ").replace("|}", " }") + for item in strip_syntax(import_string).split() + ] + + direct_imports = just_imports[1:] + top_level_module = "" + if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): + while "as" in just_imports: + attribute = None + as_index = just_imports.index("as") + if type_of_import == "from": + attribute = just_imports[as_index - 1] + top_level_module = just_imports[0] + module = top_level_module + "." + attribute + alias = just_imports[as_index + 1] + direct_imports.remove(attribute) + direct_imports.remove(alias) + direct_imports.remove("as") + just_imports[1:] = direct_imports + if attribute == alias and config.remove_redundant_aliases: + yield identified_import(top_level_module, attribute) + else: + yield identified_import(top_level_module, attribute, alias=alias) + + else: + module = just_imports[as_index - 1] + alias = just_imports[as_index + 1] + just_imports.remove(alias) + just_imports.remove("as") + just_imports.remove(module) + if module == alias and config.remove_redundant_aliases: + yield identified_import(module) + else: + yield identified_import(module, alias=alias) + + if just_imports: + if type_of_import == "from": + module = just_imports.pop(0) + for attribute in just_imports: + yield identified_import(module, attribute) + else: + for module in just_imports: + yield identified_import(module) diff --git a/.venv/lib/python3.10/site-packages/isort/io.py b/.venv/lib/python3.10/site-packages/isort/io.py new file mode 100644 index 0000000..deeda1e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/io.py @@ -0,0 +1,73 @@ +"""Defines any IO utilities used by isort""" + +import dataclasses +import re +import tokenize +from collections.abc import Callable, Iterator +from contextlib import contextmanager +from io import BytesIO, StringIO, TextIOWrapper +from pathlib import Path +from typing import Any, TextIO + +from isort.exceptions import UnsupportedEncoding + +_ENCODING_PATTERN = re.compile(rb"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)") + + +@dataclasses.dataclass(frozen=True) +class File: + stream: TextIO + path: Path + encoding: str + + @staticmethod + def detect_encoding(filename: str | Path, readline: Callable[[], bytes]) -> str: + try: + return tokenize.detect_encoding(readline)[0] + except Exception: + raise UnsupportedEncoding(filename) + + @staticmethod + def from_contents(contents: str, filename: str) -> "File": + encoding = File.detect_encoding(filename, BytesIO(contents.encode("utf-8")).readline) + return File(stream=StringIO(contents), path=Path(filename).resolve(), encoding=encoding) + + @property + def extension(self) -> str: + return self.path.suffix.lstrip(".") + + @staticmethod + def _open(filename: str | Path) -> TextIOWrapper: + """Open a file in read only mode using the encoding detected by + detect_encoding(). + """ + buffer = open(filename, "rb") + try: + encoding = File.detect_encoding(filename, buffer.readline) + buffer.seek(0) + text = TextIOWrapper(buffer, encoding, line_buffering=True, newline="") + text.mode = "r" # type: ignore + return text + except Exception: + buffer.close() + raise + + @staticmethod + @contextmanager + def read(filename: str | Path) -> Iterator["File"]: + file_path = Path(filename).resolve() + stream = None + try: + stream = File._open(file_path) + yield File(stream=stream, path=file_path, encoding=stream.encoding) + finally: + if stream is not None: + stream.close() + + +class _EmptyIO(StringIO): + def write(self, *args: Any, **kwargs: Any) -> None: # type: ignore # skipcq: PTC-W0049 + pass + + +Empty = _EmptyIO() diff --git a/.venv/lib/python3.10/site-packages/isort/literal.py b/.venv/lib/python3.10/site-packages/isort/literal.py new file mode 100644 index 0000000..16ddcba --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/literal.py @@ -0,0 +1,115 @@ +import ast +from collections.abc import Callable +from pprint import PrettyPrinter +from typing import Any + +from isort.exceptions import ( + AssignmentsFormatMismatch, + LiteralParsingFailure, + LiteralSortTypeMismatch, +) +from isort.settings import DEFAULT_CONFIG, Config + + +class ISortPrettyPrinter(PrettyPrinter): + """an isort customized pretty printer for sorted literals""" + + def __init__(self, config: Config): + super().__init__(width=config.line_length, compact=True) + + +type_mapping: dict[str, tuple[type, Callable[[Any, ISortPrettyPrinter], str]]] = {} + + +def assignments(code: str) -> str: + values = {} + for line in code.splitlines(keepends=True): + if not line.strip(): + continue + if " = " not in line: + raise AssignmentsFormatMismatch(code) + variable_name, value = line.split(" = ", 1) + values[variable_name] = value + + return "".join( + f"{variable_name} = {values[variable_name]}" for variable_name in sorted(values.keys()) + ) + + +def assignment(code: str, sort_type: str, extension: str, config: Config = DEFAULT_CONFIG) -> str: + """Sorts the literal present within the provided code against the provided sort type, + returning the sorted representation of the source code. + """ + if sort_type == "assignments": + return assignments(code) + if sort_type not in type_mapping: + raise ValueError( + "Trying to sort using an undefined sort_type. " + f"Defined sort types are {', '.join(type_mapping.keys())}." + ) + + variable_name, literal = code.split("=") + variable_name = variable_name.strip() + literal = literal.lstrip() + try: + value = ast.literal_eval(literal) + except Exception as error: + raise LiteralParsingFailure(code, error) + + expected_type, sort_function = type_mapping[sort_type] + if type(value) is not expected_type: + raise LiteralSortTypeMismatch(type(value), expected_type) + + printer = ISortPrettyPrinter(config) + sorted_value_code = f"{variable_name} = {sort_function(value, printer)}" + if config.formatting_function: + sorted_value_code = config.formatting_function( + sorted_value_code, extension, config + ).rstrip() + + sorted_value_code += code[len(code.rstrip()) :] + return sorted_value_code + + +def register_type( + name: str, kind: type +) -> Callable[[Callable[[Any, ISortPrettyPrinter], str]], Callable[[Any, ISortPrettyPrinter], str]]: + """Registers a new literal sort type.""" + + def wrap( + function: Callable[[Any, ISortPrettyPrinter], str], + ) -> Callable[[Any, ISortPrettyPrinter], str]: + type_mapping[name] = (kind, function) + return function + + return wrap + + +@register_type("dict", dict) +def _dict(value: dict[Any, Any], printer: ISortPrettyPrinter) -> str: + return printer.pformat(dict(sorted(value.items(), key=lambda item: item[1]))) + + +@register_type("list", list) +def _list(value: list[Any], printer: ISortPrettyPrinter) -> str: + return printer.pformat(sorted(value)) + + +@register_type("unique-list", list) +def _unique_list(value: list[Any], printer: ISortPrettyPrinter) -> str: + return printer.pformat(sorted(set(value))) + + +@register_type("set", set) +def _set(value: set[Any], printer: ISortPrettyPrinter) -> str: + return "{" + printer.pformat(tuple(sorted(value)))[1:-1] + "}" + + +@register_type("tuple", tuple) +def _tuple(value: tuple[Any, ...], printer: ISortPrettyPrinter) -> str: + return printer.pformat(tuple(sorted(value))) + + +@register_type("unique-tuple", tuple) +def _unique_tuple(value: tuple[Any, ...], printer: ISortPrettyPrinter) -> str: + return printer.pformat(tuple(sorted(set(value)))) diff --git a/.venv/lib/python3.10/site-packages/isort/logo.py b/.venv/lib/python3.10/site-packages/isort/logo.py new file mode 100644 index 0000000..6377d86 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/logo.py @@ -0,0 +1,19 @@ +from ._version import __version__ + +ASCII_ART = rf""" + _ _ + (_) ___ ___ _ __| |_ + | |/ _/ / _ \/ '__ _/ + | |\__ \/\_\/| | | |_ + |_|\___/\___/\_/ \_/ + + isort your imports, so you don't have to. + + VERSION {__version__} +""" + +__doc__ = f""" +```python +{ASCII_ART} +``` +""" diff --git a/.venv/lib/python3.10/site-packages/isort/main.py b/.venv/lib/python3.10/site-packages/isort/main.py new file mode 100644 index 0000000..f5408f6 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/main.py @@ -0,0 +1,1308 @@ +"""Tool for sorting imports alphabetically, and automatically separated into sections.""" + +import argparse +import functools +import json +import os +import sys +from collections.abc import Sequence +from gettext import gettext as _ +from io import TextIOWrapper +from pathlib import Path +from typing import Any +from warnings import warn + +from . import __version__, api, files, sections +from .exceptions import FileSkipped, ISortError, UnsupportedEncoding +from .format import create_terminal_printer +from .logo import ASCII_ART +from .profiles import profiles +from .settings import VALID_PY_TARGETS, Config, find_all_configs +from .utils import Trie +from .wrap_modes import WrapModes + +DEPRECATED_SINGLE_DASH_ARGS = { + "-ac", + "-af", + "-ca", + "-cs", + "-df", + "-ds", + "-dt", + "-fas", + "-fass", + "-ff", + "-fgw", + "-fss", + "-lai", + "-lbt", + "-le", + "-ls", + "-nis", + "-nlb", + "-ot", + "-rr", + "-sd", + "-sg", + "-sl", + "-sp", + "-tc", + "-wl", + "-ws", +} +QUICK_GUIDE = f""" +{ASCII_ART} + +Nothing to do: no files or paths have been passed in! + +Try one of the following: + + `isort .` - sort all Python files, starting from the current directory, recursively. + `isort . --interactive` - Do the same, but ask before making any changes. + `isort . --check --diff` - Check to see if imports are correctly sorted within this project. + `isort --help` - In-depth information about isort's available command-line options. + +Visit https://pycqa.github.io/isort/ for complete information about how to use isort. +""" + + +class SortAttempt: + def __init__(self, incorrectly_sorted: bool, skipped: bool, supported_encoding: bool) -> None: + self.incorrectly_sorted = incorrectly_sorted + self.skipped = skipped + self.supported_encoding = supported_encoding + + +def sort_imports( + file_name: str, + config: Config, + check: bool = False, + ask_to_apply: bool = False, + write_to_stdout: bool = False, + **kwargs: Any, +) -> SortAttempt | None: + incorrectly_sorted: bool = False + skipped: bool = False + try: + if check: + try: + incorrectly_sorted = not api.check_file(file_name, config=config, **kwargs) + except FileSkipped: + skipped = True + return SortAttempt(incorrectly_sorted, skipped, True) + + try: + incorrectly_sorted = not api.sort_file( + file_name, + config=config, + ask_to_apply=ask_to_apply, + write_to_stdout=write_to_stdout, + **kwargs, + ) + except FileSkipped: + skipped = True + return SortAttempt(incorrectly_sorted, skipped, True) + except (OSError, ValueError) as error: + warn(f"Unable to parse file {file_name} due to {error}", stacklevel=2) + return None + except UnsupportedEncoding: + if config.verbose: + warn(f"Encoding not supported for {file_name}", stacklevel=2) + return SortAttempt(incorrectly_sorted, skipped, False) + except ISortError as error: + _print_hard_fail(config, message=str(error)) + sys.exit(1) + except Exception: + _print_hard_fail(config, offending_file=file_name) + raise + + +def _print_hard_fail( + config: Config, offending_file: str | None = None, message: str | None = None +) -> None: + """Fail on unrecoverable exception with custom message.""" + message = message or ( + f"Unrecoverable exception thrown when parsing {offending_file or ''}! " + "This should NEVER happen.\n" + "If encountered, please open an issue: https://github.com/PyCQA/isort/issues/new" + ) + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) + printer.error(message) + + +def _build_arg_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Sort Python import definitions alphabetically " + "within logical sections. Run with no arguments to see a quick " + "start guide, otherwise, one or more files/directories/stdin must be provided. " + "Use `-` as the first argument to represent stdin. Use --interactive to use the pre 5.0.0 " + "interactive behavior." + " " + "If you've used isort 4 but are new to isort 5, see the upgrading guide: " + "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html", + add_help=False, # prevent help option from appearing in "optional arguments" group + ) + + general_group = parser.add_argument_group("general options") + target_group = parser.add_argument_group("target options") + output_group = parser.add_argument_group("general output options") + inline_args_group = output_group.add_mutually_exclusive_group() + section_group = parser.add_argument_group("section output options") + deprecated_group = parser.add_argument_group("deprecated options") + + general_group.add_argument( + "-h", + "--help", + action="help", + default=argparse.SUPPRESS, + help=_("show this help message and exit"), + ) + general_group.add_argument( + "-V", + "--version", + action="store_true", + dest="show_version", + help="Displays the currently installed version of isort.", + ) + general_group.add_argument( + "--vn", + "--version-number", + action="version", + version=__version__, + help="Returns just the current version number without the logo", + ) + general_group.add_argument( + "-v", + "--verbose", + action="store_true", + dest="verbose", + help="Shows verbose output, such as when files are skipped or when a check is successful.", + ) + general_group.add_argument( + "--only-modified", + "--om", + dest="only_modified", + action="store_true", + help="Suppresses verbose output for non-modified files.", + ) + general_group.add_argument( + "--dedup-headings", + dest="dedup_headings", + action="store_true", + help="Tells isort to only show an identical custom import heading comment once, even if" + " there are multiple sections with the comment set.", + ) + general_group.add_argument( + "-q", + "--quiet", + action="store_true", + dest="quiet", + help="Shows extra quiet output, only errors are outputted.", + ) + general_group.add_argument( + "-d", + "--stdout", + help="Force resulting output to stdout, instead of in-place.", + dest="write_to_stdout", + action="store_true", + ) + general_group.add_argument( + "--overwrite-in-place", + help="Tells isort to overwrite in place using the same file handle. " + "Comes at a performance and memory usage penalty over its standard " + "approach but ensures all file flags and modes stay unchanged.", + dest="overwrite_in_place", + action="store_true", + ) + general_group.add_argument( + "--show-config", + dest="show_config", + action="store_true", + help="See isort's determined config, as well as sources of config options.", + ) + general_group.add_argument( + "--show-files", + dest="show_files", + action="store_true", + help="See the files isort will be run against with the current config options.", + ) + general_group.add_argument( + "--df", + "--diff", + dest="show_diff", + action="store_true", + help="Prints a diff of all the changes isort would make to a file, instead of " + "changing it in place", + ) + general_group.add_argument( + "-c", + "--check-only", + "--check", + action="store_true", + dest="check", + help="Checks the file for unsorted / unformatted imports and prints them to the " + "command line without modifying the file. Returns 0 when nothing would change and " + "returns 1 when the file would be reformatted.", + ) + general_group.add_argument( + "--ws", + "--ignore-whitespace", + action="store_true", + dest="ignore_whitespace", + help="Tells isort to ignore whitespace differences when --check-only is being used.", + ) + general_group.add_argument( + "--sp", + "--settings-path", + "--settings-file", + "--settings", + dest="settings_path", + help="Explicitly set the settings path or file instead of auto determining " + "based on file location.", + ) + general_group.add_argument( + "--cr", + "--config-root", + dest="config_root", + help="Explicitly set the config root for resolving all configs. When used " + "with the --resolve-all-configs flag, isort will look at all sub-folders " + "in this config root to resolve config files and sort files based on the " + "closest available config(if any)", + ) + general_group.add_argument( + "--resolve-all-configs", + dest="resolve_all_configs", + action="store_true", + help="Tells isort to resolve the configs for all sub-directories " + "and sort files in terms of its closest config files.", + ) + general_group.add_argument( + "--profile", + dest="profile", + type=str, + help="Base profile type to use for configuration. " + f"Profiles include: {', '.join(profiles.keys())}. As well as any shared profiles.", + ) + general_group.add_argument( + "--old-finders", + "--magic-placement", + dest="old_finders", + action="store_true", + help="Use the old deprecated finder logic that relies on environment introspection magic.", + ) + general_group.add_argument( + "-j", + "--jobs", + help="Number of files to process in parallel. Negative value means use number of CPUs.", + dest="jobs", + type=int, + nargs="?", + const=-1, + ) + general_group.add_argument( + "--ac", + "--atomic", + dest="atomic", + action="store_true", + help="Ensures the output doesn't save if the resulting file contains syntax errors.", + ) + general_group.add_argument( + "--interactive", + dest="ask_to_apply", + action="store_true", + help="Tells isort to apply changes interactively.", + ) + general_group.add_argument( + "--format-error", + dest="format_error", + help="Override the format used to print errors.", + ) + general_group.add_argument( + "--format-success", + dest="format_success", + help="Override the format used to print success.", + ) + general_group.add_argument( + "--srx", + "--sort-reexports", + dest="sort_reexports", + action="store_true", + help="Automatically sort all re-exports (module level __all__ collections)", + ) + + target_group.add_argument( + "files", nargs="*", help="One or more Python source files that need their imports sorted." + ) + target_group.add_argument( + "--filter-files", + dest="filter_files", + action="store_true", + help="Tells isort to filter files even when they are explicitly passed in as " + "part of the CLI command.", + ) + target_group.add_argument( + "-s", + "--skip", + help="Files that isort should skip over. If you want to skip multiple " + "files you should specify twice: --skip file1 --skip file2. Values can be " + "file names, directory names or file paths. To skip all files in a nested path " + "use --skip-glob.", + dest="skip", + action="append", + ) + target_group.add_argument( + "--extend-skip", + help="Extends --skip to add additional files that isort should skip over. " + "If you want to skip multiple " + "files you should specify twice: --skip file1 --skip file2. Values can be " + "file names, directory names or file paths. To skip all files in a nested path " + "use --skip-glob.", + dest="extend_skip", + action="append", + ) + target_group.add_argument( + "--sg", + "--skip-glob", + help="Files that isort should skip over.", + dest="skip_glob", + action="append", + ) + target_group.add_argument( + "--extend-skip-glob", + help="Additional files that isort should skip over (extending --skip-glob).", + dest="extend_skip_glob", + action="append", + ) + target_group.add_argument( + "--gitignore", + "--skip-gitignore", + action="store_true", + dest="skip_gitignore", + help="Treat project as a git repository and ignore files listed in .gitignore." + "\nNOTE: This requires git to be installed and accessible from the same shell as isort.", + ) + target_group.add_argument( + "--ext", + "--extension", + "--supported-extension", + dest="supported_extensions", + action="append", + help="Specifies what extensions isort can be run against.", + ) + target_group.add_argument( + "--blocked-extension", + dest="blocked_extensions", + action="append", + help="Specifies what extensions isort can never be run against.", + ) + target_group.add_argument( + "--dont-follow-links", + dest="dont_follow_links", + action="store_true", + help="Tells isort not to follow symlinks that are encountered when running recursively.", + ) + target_group.add_argument( + "--filename", + dest="filename", + help="Provide the filename associated with a stream.", + ) + target_group.add_argument( + "--allow-root", + action="store_true", + default=False, + help="Tells isort not to treat / specially, allowing it to be run against the root dir.", + ) + + output_group.add_argument( + "-a", + "--add-import", + dest="add_imports", + action="append", + help="Adds the specified import line to all files, " + "automatically determining correct placement.", + ) + output_group.add_argument( + "--append", + "--append-only", + dest="append_only", + action="store_true", + help="Only adds the imports specified in --add-import if the file" + " contains existing imports.", + ) + output_group.add_argument( + "--af", + "--force-adds", + dest="force_adds", + action="store_true", + help="Forces import adds even if the original file is empty.", + ) + output_group.add_argument( + "--rm", + "--remove-import", + dest="remove_imports", + action="append", + help="Removes the specified import from all files.", + ) + output_group.add_argument( + "--float-to-top", + dest="float_to_top", + action="store_true", + help="Causes all non-indented imports to float to the top of the file having its imports " + "sorted (immediately below the top of file comment).\n" + "This can be an excellent shortcut for collecting imports every once in a while " + "when you place them in the middle of a file to avoid context switching.\n\n" + "*NOTE*: It currently doesn't work with cimports and introduces some extra over-head " + "and a performance penalty.", + ) + output_group.add_argument( + "--dont-float-to-top", + dest="dont_float_to_top", + action="store_true", + help="Forces --float-to-top setting off. See --float-to-top for more information.", + ) + output_group.add_argument( + "--ca", + "--combine-as", + dest="combine_as_imports", + action="store_true", + help="Combines as imports on the same line.", + ) + output_group.add_argument( + "--cs", + "--combine-star", + dest="combine_star", + action="store_true", + help="Ensures that if a star import is present, " + "nothing else is imported from that namespace.", + ) + output_group.add_argument( + "-e", + "--balanced", + dest="balanced_wrapping", + action="store_true", + help="Balances wrapping to produce the most consistent line length possible", + ) + output_group.add_argument( + "--ff", + "--from-first", + dest="from_first", + action="store_true", + help="Switches the typical ordering preference, " + "showing from imports first then straight ones.", + ) + output_group.add_argument( + "--fgw", + "--force-grid-wrap", + nargs="?", + const=2, + type=int, + dest="force_grid_wrap", + help="Force number of from imports (defaults to 2 when passed as CLI flag without value) " + "to be grid wrapped regardless of line " + "length. If 0 is passed in (the global default) only line length is considered.", + ) + output_group.add_argument( + "-i", + "--indent", + help='String to place for indents defaults to " " (4 spaces).', + dest="indent", + type=str, + ) + output_group.add_argument( + "--lbi", "--lines-before-imports", dest="lines_before_imports", type=int + ) + output_group.add_argument( + "--lai", "--lines-after-imports", dest="lines_after_imports", type=int + ) + output_group.add_argument( + "--lbt", "--lines-between-types", dest="lines_between_types", type=int + ) + output_group.add_argument( + "--le", + "--line-ending", + dest="line_ending", + help="Forces line endings to the specified value. " + "If not set, values will be guessed per-file.", + ) + output_group.add_argument( + "--ls", + "--length-sort", + help="Sort imports by their string length.", + dest="length_sort", + action="store_true", + ) + output_group.add_argument( + "--lss", + "--length-sort-straight", + help="Sort straight imports by their string length. Similar to `length_sort` " + "but applies only to straight imports and doesn't affect from imports.", + dest="length_sort_straight", + action="store_true", + ) + output_group.add_argument( + "-m", + "--multi-line", + dest="multi_line_output", + choices=list(WrapModes.__members__.keys()) + + [str(mode.value) for mode in WrapModes.__members__.values()], + type=str, + help="Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, " + "5-vert-grid-grouped, 6-deprecated-alias-for-5, 7-noqa, " + "8-vertical-hanging-indent-bracket, 9-vertical-prefix-from-module-import, " + "10-hanging-indent-with-parentheses).", + ) + output_group.add_argument( + "-n", + "--ensure-newline-before-comments", + dest="ensure_newline_before_comments", + action="store_true", + help="Inserts a blank line before a comment following an import.", + ) + inline_args_group.add_argument( + "--nis", + "--no-inline-sort", + dest="no_inline_sort", + action="store_true", + help="Leaves `from` imports with multiple imports 'as-is' " + "(e.g. `from foo import a, c ,b`).", + ) + output_group.add_argument( + "--ot", + "--order-by-type", + dest="order_by_type", + action="store_true", + help="Order imports by type, which is determined by case, in addition to alphabetically.\n" + "\n**NOTE**: type here refers to the implied type from the import name capitalization.\n" + ' isort does not do type introspection for the imports. These "types" are simply: ' + "CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8" + " or a related coding standard and has many imports this is a good default, otherwise you " + "likely will want to turn it off. From the CLI the `--dont-order-by-type` option will turn " + "this off.", + ) + output_group.add_argument( + "--dt", + "--dont-order-by-type", + dest="dont_order_by_type", + action="store_true", + help="Don't order imports by type, which is determined by case, in addition to " + "alphabetically.\n\n" + "**NOTE**: type here refers to the implied type from the import name capitalization.\n" + ' isort does not do type introspection for the imports. These "types" are simply: ' + "CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8" + " or a related coding standard and has many imports this is a good default. You can turn " + "this on from the CLI using `--order-by-type`.", + ) + output_group.add_argument( + "--rr", + "--reverse-relative", + dest="reverse_relative", + action="store_true", + help="Reverse order of relative imports.", + ) + output_group.add_argument( + "--reverse-sort", + dest="reverse_sort", + action="store_true", + help="Reverses the ordering of imports.", + ) + output_group.add_argument( + "--sort-order", + dest="sort_order", + help="Specify sorting function. Can be built in (natural[default] = force numbers " + "to be sequential, native = Python's built-in sorted function) or an installable plugin.", + ) + inline_args_group.add_argument( + "--sl", + "--force-single-line-imports", + dest="force_single_line", + action="store_true", + help="Forces all from imports to appear on their own line", + ) + output_group.add_argument( + "--nsl", + "--single-line-exclusions", + help="One or more modules to exclude from the single line rule.", + dest="single_line_exclusions", + action="append", + ) + output_group.add_argument( + "--tc", + "--trailing-comma", + dest="include_trailing_comma", + action="store_true", + help="Includes a trailing comma on multi line imports that include parentheses.", + ) + output_group.add_argument( + "--up", + "--use-parentheses", + dest="use_parentheses", + action="store_true", + help="Use parentheses for line continuation on length limit instead of slashes." + " **NOTE**: This is separate from wrap modes, and only affects how individual lines that " + " are too long get continued, not sections of multiple imports.", + ) + output_group.add_argument( + "-l", + "-w", + "--line-length", + "--line-width", + help="The max length of an import line (used for wrapping long imports).", + dest="line_length", + type=int, + ) + output_group.add_argument( + "--wl", + "--wrap-length", + dest="wrap_length", + type=int, + help="Specifies how long lines that are wrapped should be, if not set line_length is used." + "\nNOTE: wrap_length must be LOWER than or equal to line_length.", + ) + output_group.add_argument( + "--case-sensitive", + dest="case_sensitive", + action="store_true", + help="Tells isort to include casing when sorting module names", + ) + output_group.add_argument( + "--remove-redundant-aliases", + dest="remove_redundant_aliases", + action="store_true", + help=( + "Tells isort to remove redundant aliases from imports, such as `import os as os`." + " This defaults to `False` simply because some projects use these seemingly useless " + " aliases to signify intent and change behaviour." + ), + ) + output_group.add_argument( + "--honor-noqa", + dest="honor_noqa", + action="store_true", + help="Tells isort to honor noqa comments to enforce skipping those comments.", + ) + output_group.add_argument( + "--treat-comment-as-code", + dest="treat_comments_as_code", + action="append", + help="Tells isort to treat the specified single line comment(s) as if they are code.", + ) + output_group.add_argument( + "--treat-all-comment-as-code", + dest="treat_all_comments_as_code", + action="store_true", + help="Tells isort to treat all single line comments as if they are code.", + ) + output_group.add_argument( + "--formatter", + dest="formatter", + type=str, + help="Specifies the name of a formatting plugin to use when producing output.", + ) + output_group.add_argument( + "--color", + dest="color_output", + action="store_true", + help="Tells isort to use color in terminal output.", + ) + output_group.add_argument( + "--ext-format", + dest="ext_format", + help="Tells isort to format the given files according to an extensions formatting rules.", + ) + output_group.add_argument( + "--star-first", + help="Forces star imports above others to avoid overriding directly imported variables.", + dest="star_first", + action="store_true", + ) + output_group.add_argument( + "--split-on-trailing-comma", + help="Split imports list followed by a trailing comma into VERTICAL_HANGING_INDENT mode", + dest="split_on_trailing_comma", + action="store_true", + ) + + section_group.add_argument( + "--sd", + "--section-default", + dest="default_section", + help="Sets the default section for import options: " + str(sections.DEFAULT), + ) + section_group.add_argument( + "--only-sections", + "--os", + dest="only_sections", + action="store_true", + help="Causes imports to be sorted based on their sections like STDLIB, THIRDPARTY, etc. " + "Within sections, the imports are ordered by their import style and the imports with " + "the same style maintain their relative positions.", + ) + section_group.add_argument( + "--ds", + "--no-sections", + help="Put all imports into the same section bucket", + dest="no_sections", + action="store_true", + ) + section_group.add_argument( + "--fas", + "--force-alphabetical-sort", + action="store_true", + dest="force_alphabetical_sort", + help="Force all imports to be sorted as a single section", + ) + section_group.add_argument( + "--fss", + "--force-sort-within-sections", + action="store_true", + dest="force_sort_within_sections", + help="Don't sort straight-style imports (like import sys) before from-style imports " + "(like from itertools import groupby). Instead, sort the imports by module, " + "independent of import style.", + ) + section_group.add_argument( + "--hcss", + "--honor-case-in-force-sorted-sections", + action="store_true", + dest="honor_case_in_force_sorted_sections", + help="Honor `--case-sensitive` when `--force-sort-within-sections` is being used. " + "Without this option set, `--order-by-type` decides module name ordering too.", + ) + section_group.add_argument( + "--srss", + "--sort-relative-in-force-sorted-sections", + action="store_true", + dest="sort_relative_in_force_sorted_sections", + help="When using `--force-sort-within-sections`, sort relative imports the same " + "way as they are sorted when not using that setting.", + ) + section_group.add_argument( + "--fass", + "--force-alphabetical-sort-within-sections", + action="store_true", + dest="force_alphabetical_sort_within_sections", + help="Force all imports to be sorted alphabetically within a section", + ) + section_group.add_argument( + "-t", + "--top", + help="Force specific imports to the top of their appropriate section.", + dest="force_to_top", + action="append", + ) + section_group.add_argument( + "--combine-straight-imports", + "--csi", + dest="combine_straight_imports", + action="store_true", + help="Combines all the bare straight imports of the same section in a single line. " + "Won't work with sections which have 'as' imports", + ) + section_group.add_argument( + "--nlb", + "--no-lines-before", + help="Sections which should not be split with previous by empty lines", + dest="no_lines_before", + action="append", + ) + section_group.add_argument( + "--src", + "--src-path", + dest="src_paths", + action="append", + help="Add an explicitly defined source path " + "(modules within src paths have their imports automatically categorized as first_party)." + " Glob expansion (`*` and `**`) is supported for this option.", + ) + section_group.add_argument( + "-b", + "--builtin", + dest="known_standard_library", + action="append", + help="Force isort to recognize a module as part of Python's standard library.", + ) + section_group.add_argument( + "--extra-builtin", + dest="extra_standard_library", + action="append", + help="Extra modules to be included in the list of ones in Python's standard library.", + ) + section_group.add_argument( + "-f", + "--future", + dest="known_future_library", + action="append", + help="Force isort to recognize a module as part of Python's internal future compatibility " + "libraries. WARNING: this overrides the behavior of __future__ handling and therefore" + " can result in code that can't execute. If you're looking to add dependencies such " + "as six, a better option is to create another section below --future using custom " + "sections. See: https://github.com/PyCQA/isort#custom-sections-and-ordering and the " + "discussion here: https://github.com/PyCQA/isort/issues/1463.", + ) + section_group.add_argument( + "-o", + "--thirdparty", + dest="known_third_party", + action="append", + help="Force isort to recognize a module as being part of a third party library.", + ) + section_group.add_argument( + "-p", + "--project", + dest="known_first_party", + action="append", + help="Force isort to recognize a module as being part of the current python project.", + ) + section_group.add_argument( + "--known-local-folder", + dest="known_local_folder", + action="append", + help="Force isort to recognize a module as being a local folder. " + "Generally, this is reserved for relative imports (from . import module).", + ) + section_group.add_argument( + "--virtual-env", + dest="virtual_env", + help="Virtual environment to use for determining whether a package is third-party", + ) + section_group.add_argument( + "--conda-env", + dest="conda_env", + help="Conda environment to use for determining whether a package is third-party", + ) + section_group.add_argument( + "--py", + "--python-version", + action="store", + dest="py_version", + choices=(*tuple(VALID_PY_TARGETS), "auto"), + help="Tells isort to set the known standard library based on the specified Python " + "version. Default is to assume any Python 3 version could be the target, and use a union " + "of all stdlib modules across versions. If auto is specified, the version of the " + "interpreter used to run isort " + f"(currently: {sys.version_info.major}{sys.version_info.minor}) will be used.", + ) + + # deprecated options + deprecated_group.add_argument( + "--recursive", + dest="deprecated_flags", + action="append_const", + const="--recursive", + help=argparse.SUPPRESS, + ) + deprecated_group.add_argument( + "-rc", dest="deprecated_flags", action="append_const", const="-rc", help=argparse.SUPPRESS + ) + deprecated_group.add_argument( + "--dont-skip", + dest="deprecated_flags", + action="append_const", + const="--dont-skip", + help=argparse.SUPPRESS, + ) + deprecated_group.add_argument( + "-ns", dest="deprecated_flags", action="append_const", const="-ns", help=argparse.SUPPRESS + ) + deprecated_group.add_argument( + "--apply", + dest="deprecated_flags", + action="append_const", + const="--apply", + help=argparse.SUPPRESS, + ) + deprecated_group.add_argument( + "-k", + "--keep-direct-and-as", + dest="deprecated_flags", + action="append_const", + const="--keep-direct-and-as", + help=argparse.SUPPRESS, + ) + + return parser + + +def parse_args(argv: Sequence[str] | None = None) -> dict[str, Any]: + argv = sys.argv[1:] if argv is None else list(argv) + remapped_deprecated_args = [] + for index, arg in enumerate(argv): + if arg in DEPRECATED_SINGLE_DASH_ARGS: + remapped_deprecated_args.append(arg) + argv[index] = f"-{arg}" + + parser = _build_arg_parser() + arguments = {key: value for key, value in vars(parser.parse_args(argv)).items() if value} + if remapped_deprecated_args: + arguments["remapped_deprecated_args"] = remapped_deprecated_args + if "dont_order_by_type" in arguments: + arguments["order_by_type"] = False + del arguments["dont_order_by_type"] + if "dont_follow_links" in arguments: + arguments["follow_links"] = False + del arguments["dont_follow_links"] + if "dont_float_to_top" in arguments: + del arguments["dont_float_to_top"] + if arguments.get("float_to_top", False): + sys.exit("Can't set both --float-to-top and --dont-float-to-top.") + else: + arguments["float_to_top"] = False + multi_line_output = arguments.get("multi_line_output", None) + if multi_line_output: + if multi_line_output.isdigit(): + arguments["multi_line_output"] = WrapModes(int(multi_line_output)) + else: + arguments["multi_line_output"] = WrapModes[multi_line_output] + + return arguments + + +def _preconvert(item: Any) -> str | list[Any]: + """Preconverts objects from native types into JSONifyiable types""" + if isinstance(item, (set, frozenset)): + return list(item) + if isinstance(item, WrapModes): + return str(item.name) + if isinstance(item, Path): + return str(item) + if callable(item) and hasattr(item, "__name__"): + return str(item.__name__) + raise TypeError(f"Unserializable object {item} of type {type(item)}") + + +def identify_imports_main( + argv: Sequence[str] | None = None, stdin: TextIOWrapper | None = None +) -> None: + parser = argparse.ArgumentParser( + description="Get all import definitions from a given file." + "Use `-` as the first argument to represent stdin." + ) + parser.add_argument( + "files", nargs="+", help="One or more Python source files that need their imports sorted." + ) + parser.add_argument( + "--top-only", + action="store_true", + default=False, + help="Only identify imports that occur in before functions or classes.", + ) + + target_group = parser.add_argument_group("target options") + target_group.add_argument( + "--follow-links", + action="store_true", + default=False, + help="Tells isort to follow symlinks that are encountered when running recursively.", + ) + + uniqueness = parser.add_mutually_exclusive_group() + uniqueness.add_argument( + "--unique", + action="store_true", + default=False, + help="If true, isort will only identify unique imports.", + ) + uniqueness.add_argument( + "--packages", + dest="unique", + action="store_const", + const=api.ImportKey.PACKAGE, + default=False, + help="If true, isort will only identify the unique top level modules imported.", + ) + uniqueness.add_argument( + "--modules", + dest="unique", + action="store_const", + const=api.ImportKey.MODULE, + default=False, + help="If true, isort will only identify the unique modules imported.", + ) + uniqueness.add_argument( + "--attributes", + dest="unique", + action="store_const", + const=api.ImportKey.ATTRIBUTE, + default=False, + help="If true, isort will only identify the unique attributes imported.", + ) + + arguments = parser.parse_args(argv) + + file_names = arguments.files + if file_names == ["-"]: + identified_imports = api.find_imports_in_stream( + sys.stdin if stdin is None else stdin, + unique=arguments.unique, + top_only=arguments.top_only, + follow_links=arguments.follow_links, + ) + else: + identified_imports = api.find_imports_in_paths( + file_names, + unique=arguments.unique, + top_only=arguments.top_only, + follow_links=arguments.follow_links, + ) + + for identified_import in identified_imports: + if arguments.unique == api.ImportKey.PACKAGE: + print(identified_import.module.split(".")[0]) + elif arguments.unique == api.ImportKey.MODULE: + print(identified_import.module) + elif arguments.unique == api.ImportKey.ATTRIBUTE: + print(f"{identified_import.module}.{identified_import.attribute}") + else: + print(str(identified_import)) + + +def main(argv: Sequence[str] | None = None, stdin: TextIOWrapper | None = None) -> None: + arguments = parse_args(argv) + if arguments.get("show_version"): + print(ASCII_ART) + return + + show_config: bool = arguments.pop("show_config", False) + show_files: bool = arguments.pop("show_files", False) + if show_config and show_files: + sys.exit("Error: either specify show-config or show-files not both.") + + if "settings_path" in arguments: + if os.path.isfile(arguments["settings_path"]): + arguments["settings_file"] = os.path.abspath(arguments["settings_path"]) + arguments["settings_path"] = os.path.dirname(arguments["settings_file"]) + else: + arguments["settings_path"] = os.path.abspath(arguments["settings_path"]) + + if "virtual_env" in arguments: + venv = arguments["virtual_env"] + arguments["virtual_env"] = os.path.abspath(venv) + if not os.path.isdir(arguments["virtual_env"]): + warn(f"virtual_env dir does not exist: {arguments['virtual_env']}", stacklevel=2) + + file_names = arguments.pop("files", []) + if not file_names and not show_config: + print(QUICK_GUIDE) + if arguments: + sys.exit("Error: arguments passed in without any paths or content.") + return + if "settings_path" not in arguments: + arguments["settings_path"] = ( + arguments.get("filename", None) or os.getcwd() + if file_names == ["-"] + else os.path.abspath(file_names[0] if file_names else ".") + ) + if not os.path.isdir(arguments["settings_path"]): + arguments["settings_path"] = os.path.dirname(arguments["settings_path"]) + + config_dict = arguments.copy() + ask_to_apply = config_dict.pop("ask_to_apply", False) + jobs = config_dict.pop("jobs", None) + check = config_dict.pop("check", False) + show_diff = config_dict.pop("show_diff", False) + write_to_stdout = config_dict.pop("write_to_stdout", False) + deprecated_flags = config_dict.pop("deprecated_flags", False) + remapped_deprecated_args = config_dict.pop("remapped_deprecated_args", False) + stream_filename = config_dict.pop("filename", None) + ext_format = config_dict.pop("ext_format", None) + allow_root = config_dict.pop("allow_root", None) + resolve_all_configs = config_dict.pop("resolve_all_configs", False) + wrong_sorted_files = False + all_attempt_broken = False + no_valid_encodings = False + + config_trie: Trie | None = None + if resolve_all_configs: + config_trie = find_all_configs(config_dict.pop("config_root", ".")) + + if "src_paths" in config_dict: + config_dict["src_paths"] = { + Path(src_path).resolve() for src_path in config_dict.get("src_paths", ()) + } + + config = Config(**config_dict) + if show_config: + print(json.dumps(config.__dict__, indent=4, separators=(",", ": "), default=_preconvert)) + return + if file_names == ["-"]: + file_path = Path(stream_filename) if stream_filename else None + if show_files: + sys.exit("Error: can't show files for streaming input.") + + input_stream = sys.stdin if stdin is None else stdin + if check: + incorrectly_sorted = not api.check_stream( + input_stream=input_stream, + config=config, + show_diff=show_diff, + file_path=file_path, + extension=ext_format, + ) + + wrong_sorted_files = incorrectly_sorted + else: + try: + api.sort_stream( + input_stream=input_stream, + output_stream=sys.stdout, + config=config, + show_diff=show_diff, + file_path=file_path, + extension=ext_format, + raise_on_skip=False, + ) + except FileSkipped: + sys.stdout.write(input_stream.read()) + elif "/" in file_names and not allow_root: + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) + printer.error("it is dangerous to operate recursively on '/'") + printer.error("use --allow-root to override this failsafe") + sys.exit(1) + else: + if stream_filename: + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) + printer.error("Filename override is intended only for stream (-) sorting.") + sys.exit(1) + skipped: list[str] = [] + broken: list[str] = [] + + if config.filter_files: + filtered_files = [] + for file_name in file_names: + if config.is_skipped(Path(file_name)): + skipped.append(str(Path(file_name).resolve())) + else: + filtered_files.append(file_name) + file_names = filtered_files + + file_names = files.find(file_names, config, skipped, broken) + if show_files: + for file_name in file_names: + print(file_name) + return + num_skipped = 0 + num_broken = 0 + num_invalid_encoding = 0 + if config.verbose: + print(ASCII_ART) + + if jobs: + import multiprocessing # noqa: PLC0415 + + executor = multiprocessing.Pool(jobs if jobs > 0 else multiprocessing.cpu_count()) + attempt_iterator = executor.imap( + functools.partial( + sort_imports, + config=config, + check=check, + ask_to_apply=ask_to_apply, + show_diff=show_diff, + write_to_stdout=write_to_stdout, + extension=ext_format, + config_trie=config_trie, + ), + file_names, + ) + else: + # https://github.com/python/typeshed/pull/2814 + attempt_iterator = ( + sort_imports( # type: ignore + file_name, + config=config, + check=check, + ask_to_apply=ask_to_apply, + show_diff=show_diff, + write_to_stdout=write_to_stdout, + extension=ext_format, + config_trie=config_trie, + ) + for file_name in file_names + ) + + # If any files passed in are missing considered as error, should be removed + is_no_attempt = True + any_encoding_valid = False + for sort_attempt in attempt_iterator: + if not sort_attempt: + continue # pragma: no cover - shouldn't happen, satisfies type constraint + incorrectly_sorted = sort_attempt.incorrectly_sorted + if arguments.get("check", False) and incorrectly_sorted: + wrong_sorted_files = True + if sort_attempt.skipped: + num_skipped += ( + 1 # pragma: no cover - shouldn't happen, due to skip in iter_source_code + ) + + if not sort_attempt.supported_encoding: + num_invalid_encoding += 1 + else: + any_encoding_valid = True + + is_no_attempt = False + + num_skipped += len(skipped) + if num_skipped and not config.quiet: + if config.verbose: + for was_skipped in skipped: + print( + f"{was_skipped} was skipped as it's listed in 'skip' setting, " + "matches a glob in 'skip_glob' setting, or is in a .gitignore file with " + "--skip-gitignore enabled." + ) + print(f"Skipped {num_skipped} files") + + num_broken += len(broken) + if num_broken and not config.quiet: + if config.verbose: + for was_broken in broken: + warn( + f"{was_broken} was broken path, make sure it exists correctly", stacklevel=2 + ) + print(f"Broken {num_broken} paths") + + if num_broken > 0 and is_no_attempt: + all_attempt_broken = True + if num_invalid_encoding > 0 and not any_encoding_valid: + no_valid_encodings = True + + if not config.quiet and (remapped_deprecated_args or deprecated_flags): + if remapped_deprecated_args: + warn( + "W0502: The following deprecated single dash CLI flags were used and translated: " + f"{', '.join(remapped_deprecated_args)}!", + stacklevel=2, + ) + if deprecated_flags: + warn( + "W0501: The following deprecated CLI flags were used and ignored: " + f"{', '.join(deprecated_flags)}!", + stacklevel=2, + ) + warn( + "W0500: Please see the 5.0.0 Upgrade guide: " + "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html", + stacklevel=2, + ) + + if wrong_sorted_files: + sys.exit(1) + + if all_attempt_broken: + sys.exit(1) + + if no_valid_encodings: + printer = create_terminal_printer( + color=config.color_output, error=config.format_error, success=config.format_success + ) + printer.error("No valid encodings.") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.10/site-packages/isort/output.py b/.venv/lib/python3.10/site-packages/isort/output.py new file mode 100644 index 0000000..9b72e16 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/output.py @@ -0,0 +1,686 @@ +import copy +import itertools +from collections.abc import Iterable +from functools import partial +from typing import Any + +from isort.format import format_simplified + +from . import parse, sorting, wrap +from .comments import add_to_line as with_comments +from .identify import STATEMENT_DECLARATIONS +from .settings import DEFAULT_CONFIG, Config + + +def sorted_imports( + parsed: parse.ParsedContent, + config: Config = DEFAULT_CONFIG, + extension: str = "py", + import_type: str = "import", +) -> str: + """Adds the imports back to the file. + + (at the index of the first import) sorted alphabetically and split between groups + + """ + if parsed.import_index == -1: + return _output_as_string(parsed.lines_without_imports, parsed.line_separator) + + formatted_output: list[str] = parsed.lines_without_imports.copy() + remove_imports = [format_simplified(removal) for removal in config.remove_imports] + + sections: Iterable[str] = itertools.chain(parsed.sections, config.forced_separate) + + if config.no_sections: + parsed.imports["no_sections"] = {"straight": {}, "from": {}} + base_sections: tuple[str, ...] = () + for section in sections: + if section == "FUTURE": + base_sections = ("FUTURE",) + continue + parsed.imports["no_sections"]["straight"].update( + parsed.imports[section].get("straight", {}) + ) + parsed.imports["no_sections"]["from"].update(parsed.imports[section].get("from", {})) + sections = (*base_sections, "no_sections") + + output: list[str] = [] + seen_headings: set[str] = set() + pending_lines_before = False + for section in sections: + straight_modules = parsed.imports[section]["straight"] + if not config.only_sections: + straight_modules = sorting.sort( + config, + straight_modules, + key=lambda key: sorting.module_key( + key, config, section_name=section, straight_import=True + ), + reverse=config.reverse_sort, + ) + + from_modules = parsed.imports[section]["from"] + if not config.only_sections: + from_modules = sorting.sort( + config, + from_modules, + key=lambda key: sorting.module_key(key, config, section_name=section), + reverse=config.reverse_sort, + ) + + if config.star_first: + star_modules = [] + other_modules = [] + for module in from_modules: + if "*" in parsed.imports[section]["from"][module]: + star_modules.append(module) + else: + other_modules.append(module) + from_modules = star_modules + other_modules + + straight_imports = _with_straight_imports( + parsed, config, straight_modules, section, remove_imports, import_type + ) + from_imports = _with_from_imports( + parsed, config, from_modules, section, remove_imports, import_type + ) + + lines_between = [""] * ( + config.lines_between_types if from_modules and straight_modules else 0 + ) + if config.from_first: + section_output = from_imports + lines_between + straight_imports + else: + section_output = straight_imports + lines_between + from_imports + + if config.force_sort_within_sections: + # collapse comments + comments_above = [] + new_section_output: list[str] = [] + for line in section_output: + if not line: + continue + if line.startswith("#"): + comments_above.append(line) + elif comments_above: + new_section_output.append(_LineWithComments(line, comments_above)) + comments_above = [] + else: + new_section_output.append(line) + # only_sections options is not imposed if force_sort_within_sections is True + new_section_output = sorting.sort( + config, + new_section_output, + key=partial(sorting.section_key, config=config), + reverse=config.reverse_sort, + ) + + # uncollapse comments + section_output = [] + for line in new_section_output: + comments = getattr(line, "comments", ()) + if comments: + section_output.extend(comments) + section_output.append(str(line)) + + section_name = section + no_lines_before = section_name in config.no_lines_before + + if section_output: + if section_name in parsed.place_imports: + parsed.place_imports[section_name] = section_output + continue + + section_title = config.import_headings.get(section_name.lower(), "") + if section_title and section_title not in seen_headings: + if config.dedup_headings: + seen_headings.add(section_title) + section_comment = f"# {section_title}" + if section_comment not in parsed.lines_without_imports[0:1]: # pragma: no branch + section_output.insert(0, section_comment) + + section_footer = config.import_footers.get(section_name.lower(), "") + if section_footer and section_footer not in seen_headings: + if config.dedup_headings: + seen_headings.add(section_footer) + section_comment_end = f"# {section_footer}" + if ( + section_comment_end not in parsed.lines_without_imports[-1:] + ): # pragma: no branch + section_output.append("") # Empty line for black compatibility + section_output.append(section_comment_end) + + if pending_lines_before or not no_lines_before: + output += [""] * config.lines_between_sections + + output += section_output + + pending_lines_before = False + else: + pending_lines_before = pending_lines_before or not no_lines_before + + if config.ensure_newline_before_comments: + output = _ensure_newline_before_comment(output) + + while output and output[-1].strip() == "": + output.pop() # pragma: no cover + while output and output[0].strip() == "": + output.pop(0) + + if config.formatting_function: + output = config.formatting_function( + parsed.line_separator.join(output), extension, config + ).splitlines() + + output_at = 0 + if parsed.import_index < parsed.original_line_count: + output_at = parsed.import_index + formatted_output[output_at:0] = output + + if output: + imports_tail = output_at + len(output) + while [ + character.strip() for character in formatted_output[imports_tail : imports_tail + 1] + ] == [""]: + formatted_output.pop(imports_tail) + + if len(formatted_output) > imports_tail: + next_construct = "" + tail = formatted_output[imports_tail:] + + for index, line in enumerate(tail): # pragma: no branch + should_skip, in_quote, *_ = parse.skip_line( + line, + in_quote="", + index=len(formatted_output), + section_comments=config.section_comments, + needs_import=False, + ) + if not should_skip and line.strip(): + if ( + line.strip().startswith("#") + and len(tail) > (index + 1) + and tail[index + 1].strip() + ): + continue + next_construct = line + break + if in_quote: # pragma: no branch + next_construct = line + break + + if config.lines_after_imports != -1: + lines_after_imports = config.lines_after_imports + if config.profile == "black" and extension == "pyi": # special case for black + lines_after_imports = 1 + formatted_output[imports_tail:0] = ["" for line in range(lines_after_imports)] + elif extension != "pyi" and next_construct.startswith(STATEMENT_DECLARATIONS): + formatted_output[imports_tail:0] = ["", ""] + else: + formatted_output[imports_tail:0] = [""] + + if config.lines_before_imports != -1: + lines_before_imports = config.lines_before_imports + if config.profile == "black" and extension == "pyi": # special case for black + lines_before_imports = 1 + formatted_output[:0] = ["" for line in range(lines_before_imports)] + + if parsed.place_imports: + new_out_lines = [] + for index, line in enumerate(formatted_output): + new_out_lines.append(line) + if line in parsed.import_placements: + new_out_lines.extend(parsed.place_imports[parsed.import_placements[line]]) + if ( + len(formatted_output) <= (index + 1) + or formatted_output[index + 1].strip() != "" + ): + new_out_lines.append("") + formatted_output = new_out_lines + + return _output_as_string(formatted_output, parsed.line_separator) + + +# Ignore DeepSource cyclomatic complexity check for this function. It was +# already complex when this check was enabled. +# skipcq: PY-R1000 +def _with_from_imports( + parsed: parse.ParsedContent, + config: Config, + from_modules: Iterable[str], + section: str, + remove_imports: list[str], + import_type: str, +) -> list[str]: + output: list[str] = [] + for module in from_modules: + if module in remove_imports: + continue + + import_start = f"from {module} {import_type} " + from_imports = list(parsed.imports[section]["from"][module]) + if ( + not config.no_inline_sort + or (config.force_single_line and module not in config.single_line_exclusions) + ) and not config.only_sections: + from_imports = sorting.sort( + config, + from_imports, + key=lambda key: sorting.module_key( + key, + config, + True, + config.force_alphabetical_sort_within_sections, + section_name=section, + ), + reverse=config.reverse_sort, + ) + if remove_imports: + from_imports = [ + line for line in from_imports if f"{module}.{line}" not in remove_imports + ] + + sub_modules = [f"{module}.{from_import}" for from_import in from_imports] + as_imports = { + from_import: [ + f"{from_import} as {as_module}" for as_module in parsed.as_map["from"][sub_module] + ] + for from_import, sub_module in zip(from_imports, sub_modules, strict=False) + if sub_module in parsed.as_map["from"] + } + if config.combine_as_imports and not ("*" in from_imports and config.combine_star): + if not config.no_inline_sort: + for as_import in as_imports: + if not config.only_sections: + as_imports[as_import] = sorting.sort(config, as_imports[as_import]) + for from_import in copy.copy(from_imports): + if from_import in as_imports: + idx = from_imports.index(from_import) + if parsed.imports[section]["from"][module][from_import]: + from_imports[(idx + 1) : (idx + 1)] = as_imports.pop(from_import) + else: + from_imports[idx : (idx + 1)] = as_imports.pop(from_import) + + only_show_as_imports = False + comments = parsed.categorized_comments["from"].pop(module, ()) + above_comments = parsed.categorized_comments["above"]["from"].pop(module, None) + while from_imports: + if above_comments: + output.extend(above_comments) + above_comments = None + + if "*" in from_imports and config.combine_star: + import_statement = wrap.line( + with_comments( + _with_star_comments(parsed, module, list(comments or ())), + f"{import_start}*", + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ), + parsed.line_separator, + config, + ) + from_imports = [ + from_import for from_import in from_imports if from_import in as_imports + ] + only_show_as_imports = True + elif config.force_single_line and module not in config.single_line_exclusions: + import_statement = "" + while from_imports: + from_import = from_imports.pop(0) + single_import_line = with_comments( + comments, + import_start + from_import, + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + comment = ( + parsed.categorized_comments["nested"].get(module, {}).pop(from_import, None) + ) + if comment: + single_import_line += ( + f"{(comments and ';') or config.comment_prefix} {comment}" + ) + if from_import in as_imports: + if ( + parsed.imports[section]["from"][module][from_import] + and not only_show_as_imports + ): + output.append( + wrap.line(single_import_line, parsed.line_separator, config) + ) + from_comments = parsed.categorized_comments["straight"].get( + f"{module}.{from_import}" + ) + + if not config.only_sections: + output.extend( + with_comments( + from_comments, + wrap.line( + import_start + as_import, parsed.line_separator, config + ), + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + for as_import in sorting.sort(config, as_imports[from_import]) + ) + + else: + output.extend( + with_comments( + from_comments, + wrap.line( + import_start + as_import, parsed.line_separator, config + ), + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + for as_import in as_imports[from_import] + ) + else: + output.append(wrap.line(single_import_line, parsed.line_separator, config)) + comments = None + else: + while from_imports and from_imports[0] in as_imports: + from_import = from_imports.pop(0) + + if not config.only_sections: + as_imports[from_import] = sorting.sort(config, as_imports[from_import]) + from_comments = ( + parsed.categorized_comments["straight"].get(f"{module}.{from_import}") or [] + ) + if ( + parsed.imports[section]["from"][module][from_import] + and not only_show_as_imports + ): + specific_comment = ( + parsed.categorized_comments["nested"] + .get(module, {}) + .pop(from_import, None) + ) + if specific_comment: + from_comments.append(specific_comment) + output.append( + wrap.line( + with_comments( + from_comments, + import_start + from_import, + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ), + parsed.line_separator, + config, + ) + ) + from_comments = [] + + for as_import in as_imports[from_import]: + specific_comment = ( + parsed.categorized_comments["nested"] + .get(module, {}) + .pop(as_import, None) + ) + if specific_comment: + from_comments.append(specific_comment) + + output.append( + wrap.line( + with_comments( + from_comments, + import_start + as_import, + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ), + parsed.line_separator, + config, + ) + ) + + from_comments = [] + + if "*" in from_imports: + output.append( + with_comments( + _with_star_comments(parsed, module, []), + f"{import_start}*", + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + ) + from_imports.remove("*") + + for from_import in copy.copy(from_imports): + comment = ( + parsed.categorized_comments["nested"].get(module, {}).pop(from_import, None) + ) + if comment: + # If the comment is a noqa and hanging indent wrapping is used, + # keep the name in the main list and hoist the comment to the statement. + if ( + comment.lower().startswith("noqa") + and config.multi_line_output == wrap.Modes.HANGING_INDENT # type: ignore[attr-defined] # noqa: E501 + ): + comments = list(comments) if comments else [] + comments.append(comment) + continue + + from_imports.remove(from_import) + if from_imports: + use_comments = [] + else: + use_comments = comments + comments = None + single_import_line = with_comments( + use_comments, + import_start + from_import, + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + single_import_line += ( + f"{(use_comments and ';') or config.comment_prefix} {comment}" + ) + output.append(wrap.line(single_import_line, parsed.line_separator, config)) + + from_import_section = [] + while from_imports and ( + from_imports[0] not in as_imports + or ( + config.combine_as_imports + and parsed.imports[section]["from"][module][from_import] + ) + ): + from_import_section.append(from_imports.pop(0)) + if config.combine_as_imports: + comments = (comments or []) + list( + parsed.categorized_comments["from"].pop(f"{module}.__combined_as__", ()) + ) + import_statement = with_comments( + comments, + import_start + (", ").join(from_import_section), + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + if not from_import_section: + import_statement = "" + + do_multiline_reformat = False + + force_grid_wrap = config.force_grid_wrap + if force_grid_wrap and len(from_import_section) >= force_grid_wrap: + do_multiline_reformat = True + + if len(import_statement) > config.line_length and len(from_import_section) > 1: + do_multiline_reformat = True + + # If line too long AND have imports AND we are + # NOT using GRID or VERTICAL wrap modes + if ( + len(import_statement) > config.line_length + and len(from_import_section) > 0 + and config.multi_line_output not in (wrap.Modes.GRID, wrap.Modes.VERTICAL) # type: ignore # noqa: E501 + ): + do_multiline_reformat = True + + if ( + import_statement + and config.split_on_trailing_comma + and module in parsed.trailing_commas + ): + import_statement = wrap.import_statement( + import_start=import_start, + from_imports=from_import_section, + comments=comments, + line_separator=parsed.line_separator, + config=config, + explode=True, + ) + + elif do_multiline_reformat: + import_statement = wrap.import_statement( + import_start=import_start, + from_imports=from_import_section, + comments=comments, + line_separator=parsed.line_separator, + config=config, + ) + if config.multi_line_output == wrap.Modes.GRID: # type: ignore + other_import_statement = wrap.import_statement( + import_start=import_start, + from_imports=from_import_section, + comments=comments, + line_separator=parsed.line_separator, + config=config, + multi_line_output=wrap.Modes.VERTICAL_GRID, # type: ignore + ) + if ( + max( + len(import_line) + for import_line in import_statement.split(parsed.line_separator) + ) + > config.line_length + ): + import_statement = other_import_statement + elif len(import_statement) > config.line_length: + import_statement = wrap.line(import_statement, parsed.line_separator, config) + + if import_statement: + output.append(import_statement) + return output + + +def _with_straight_imports( + parsed: parse.ParsedContent, + config: Config, + straight_modules: Iterable[str], + section: str, + remove_imports: list[str], + import_type: str, +) -> list[str]: + output: list[str] = [] + + as_imports = any(module in parsed.as_map["straight"] for module in straight_modules) + + # combine_straight_imports only works for bare imports, 'as' imports not included + if config.combine_straight_imports and not as_imports: + if not straight_modules: + return [] + + above_comments: list[str] = [] + inline_comments: list[str] = [] + + for module in straight_modules: + if module in parsed.categorized_comments["above"]["straight"]: + above_comments.extend(parsed.categorized_comments["above"]["straight"].pop(module)) + if module in parsed.categorized_comments["straight"]: + inline_comments.extend(parsed.categorized_comments["straight"][module]) + + combined_straight_imports = ", ".join(straight_modules) + if inline_comments: + combined_inline_comments = " ".join(inline_comments) + else: + combined_inline_comments = "" + + output.extend(above_comments) + + if combined_inline_comments: + output.append( + f"{import_type} {combined_straight_imports} # {combined_inline_comments}" + ) + else: + output.append(f"{import_type} {combined_straight_imports}") + + return output + + for module in straight_modules: + if module in remove_imports: + continue + + import_definition = [] + if module in parsed.as_map["straight"]: + if parsed.imports[section]["straight"][module]: + import_definition.append((f"{import_type} {module}", module)) + import_definition.extend( + (f"{import_type} {module} as {as_import}", f"{module} as {as_import}") + for as_import in parsed.as_map["straight"][module] + ) + else: + import_definition.append((f"{import_type} {module}", module)) + + comments_above = parsed.categorized_comments["above"]["straight"].pop(module, None) + if comments_above: + output.extend(comments_above) + output.extend( + with_comments( + parsed.categorized_comments["straight"].get(imodule), + idef, + removed=config.ignore_comments, + comment_prefix=config.comment_prefix, + ) + for idef, imodule in import_definition + ) + + return output + + +def _output_as_string(lines: list[str], line_separator: str) -> str: + return line_separator.join(_normalize_empty_lines(lines)) + + +def _normalize_empty_lines(lines: list[str]) -> list[str]: + while lines and lines[-1].strip() == "": + lines.pop(-1) + + lines.append("") + return lines + + +class _LineWithComments(str): + comments: list[str] + + def __new__( + cls: type["_LineWithComments"], value: Any, comments: list[str] + ) -> "_LineWithComments": + instance = super().__new__(cls, value) + instance.comments = comments + return instance + + +def _ensure_newline_before_comment(output: list[str]) -> list[str]: + new_output: list[str] = [] + + def is_comment(line: str | None) -> bool: + return line.startswith("#") if line else False + + for line, prev_line in zip(output, [None, *output], strict=False): + if is_comment(line) and prev_line != "" and not is_comment(prev_line): + new_output.append("") + new_output.append(line) + return new_output + + +def _with_star_comments(parsed: parse.ParsedContent, module: str, comments: list[str]) -> list[str]: + star_comment = parsed.categorized_comments["nested"].get(module, {}).pop("*", None) + if star_comment: + return [*comments, star_comment] + return comments diff --git a/.venv/lib/python3.10/site-packages/isort/parse.py b/.venv/lib/python3.10/site-packages/isort/parse.py new file mode 100644 index 0000000..2311c06 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/parse.py @@ -0,0 +1,601 @@ +"""Defines parsing functions used by isort for parsing import definitions""" + +import re +from collections import OrderedDict, defaultdict +from functools import partial +from itertools import chain +from typing import TYPE_CHECKING, Any, NamedTuple, TypedDict +from warnings import warn + +from . import place +from .comments import parse as parse_comments +from .exceptions import MissingSection +from .settings import DEFAULT_CONFIG, Config + +if TYPE_CHECKING: + CommentsAboveDict = TypedDict( + "CommentsAboveDict", {"straight": dict[str, Any], "from": dict[str, Any]} + ) + + CommentsDict = TypedDict( + "CommentsDict", + { + "from": dict[str, Any], + "straight": dict[str, Any], + "nested": dict[str, Any], + "above": CommentsAboveDict, + }, + ) + + +def _infer_line_separator(contents: str) -> str: + if "\r\n" in contents: + return "\r\n" + if "\r" in contents: + return "\r" + return "\n" + + +def normalize_line(raw_line: str) -> tuple[str, str]: + """Normalizes import related statements in the provided line. + + Returns (normalized_line: str, raw_line: str) + """ + line = re.sub(r"from(\.+)cimport ", r"from \g<1> cimport ", raw_line) + line = re.sub(r"from(\.+)import ", r"from \g<1> import ", line) + line = line.replace("import*", "import *") + line = re.sub(r" (\.+)import ", r" \g<1> import ", line) + line = re.sub(r" (\.+)cimport ", r" \g<1> cimport ", line) + line = line.replace("\t", " ") + return line, raw_line + + +def import_type(line: str, config: Config = DEFAULT_CONFIG) -> str | None: + """If the current line is an import line it will return its type (from or straight)""" + if config.honor_noqa and line.lower().rstrip().endswith("noqa"): + return None + if "isort:skip" in line or "isort: skip" in line or "isort: split" in line: + return None + if line.startswith(("import ", "cimport ")): + return "straight" + if line.startswith("from "): + return "from" + return None + + +def strip_syntax(import_string: str) -> str: + import_string = import_string.replace("_import", "[[i]]") + import_string = import_string.replace("_cimport", "[[ci]]") + for remove_syntax in ["\\", "(", ")", ","]: + import_string = import_string.replace(remove_syntax, " ") + import_list = import_string.split() + for key in ("from", "import", "cimport"): + if key in import_list: + import_list.remove(key) + import_string = " ".join(import_list) + import_string = import_string.replace("[[i]]", "_import") + import_string = import_string.replace("[[ci]]", "_cimport") + return import_string.replace("{ ", "{|").replace(" }", "|}") + + +def skip_line( + line: str, + in_quote: str, + index: int, + section_comments: tuple[str, ...], + needs_import: bool = True, +) -> tuple[bool, str]: + """Determine if a given line should be skipped. + + Returns back a tuple containing: + + (skip_line: bool, + in_quote: str,) + """ + should_skip = bool(in_quote) + if '"' in line or "'" in line: + char_index = 0 + while char_index < len(line): + if line[char_index] == "\\": + char_index += 1 + elif in_quote: + if line[char_index : char_index + len(in_quote)] == in_quote: + in_quote = "" + elif line[char_index] in ("'", '"'): + long_quote = line[char_index : char_index + 3] + if long_quote in ('"""', "'''"): + in_quote = long_quote + char_index += 2 + else: + in_quote = line[char_index] + elif line[char_index] == "#": + break + char_index += 1 + + if ";" in line.split("#")[0] and needs_import: + for part in (part.strip() for part in line.split(";")): + if ( + part + and not part.startswith("from ") + and not part.startswith(("import ", "cimport ")) + ): + should_skip = True + + return (bool(should_skip or in_quote), in_quote) + + +class ParsedContent(NamedTuple): + in_lines: list[str] + lines_without_imports: list[str] + import_index: int + place_imports: dict[str, list[str]] + import_placements: dict[str, str] + as_map: dict[str, dict[str, list[str]]] + imports: dict[str, dict[str, Any]] + categorized_comments: "CommentsDict" + change_count: int + original_line_count: int + line_separator: str + sections: Any + verbose_output: list[str] + trailing_commas: set[str] + + +def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedContent: + """Parses a python file taking out and categorizing imports.""" + line_separator: str = config.line_ending or _infer_line_separator(contents) + in_lines = contents.splitlines() + if contents and contents[-1] in ("\n", "\r"): + in_lines.append("") + + out_lines = [] + original_line_count = len(in_lines) + if config.old_finders: + from .deprecated.finders import FindersManager # noqa: PLC0415 + + finder = FindersManager(config=config).find + else: + finder = partial(place.module, config=config) + + line_count = len(in_lines) + + place_imports: dict[str, list[str]] = {} + import_placements: dict[str, str] = {} + as_map: dict[str, dict[str, list[str]]] = { + "straight": defaultdict(list), + "from": defaultdict(list), + } + imports: OrderedDict[str, dict[str, Any]] = OrderedDict() + verbose_output: list[str] = [] + + for section in chain(config.sections, config.forced_separate): + imports[section] = {"straight": OrderedDict(), "from": OrderedDict()} + categorized_comments: CommentsDict = { + "from": {}, + "straight": {}, + "nested": {}, + "above": {"straight": {}, "from": {}}, + } + + trailing_commas: set[str] = set() + + index = 0 + import_index = -1 + in_quote = "" + while index < line_count: + line = in_lines[index] + index += 1 + statement_index = index + (skipping_line, in_quote) = skip_line( + line, in_quote=in_quote, index=index, section_comments=config.section_comments + ) + + if ( + line in config.section_comments or line in config.section_comments_end + ) and not skipping_line: + if import_index == -1: # pragma: no branch + import_index = index - 1 + continue + + if "isort:imports-" in line and line.startswith("#"): + section = line.split("isort:imports-")[-1].split()[0].upper() + place_imports[section] = [] + import_placements[line] = section + elif "isort: imports-" in line and line.startswith("#"): + section = line.split("isort: imports-")[-1].split()[0].upper() + place_imports[section] = [] + import_placements[line] = section + + if skipping_line: + out_lines.append(line) + continue + + lstripped_line = line.lstrip() + if ( + config.float_to_top + and import_index == -1 + and line + and not in_quote + and not lstripped_line.startswith("#") + and not lstripped_line.startswith("'''") + and not lstripped_line.startswith('"""') + ): + if not lstripped_line.startswith("import") and not lstripped_line.startswith("from"): + import_index = index - 1 + while import_index and not in_lines[import_index - 1]: + import_index -= 1 + else: + commentless = line.split("#", 1)[0].strip() + if ( + ("isort:skip" in line or "isort: skip" in line) + and "(" in commentless + and ")" not in commentless + ): + import_index = index + + starting_line = line + while "isort:skip" in starting_line or "isort: skip" in starting_line: + commentless = starting_line.split("#", 1)[0] + if ( + "(" in commentless + and not commentless.rstrip().endswith(")") + and import_index < line_count + ): + while import_index < line_count and not commentless.rstrip().endswith( + ")" + ): + commentless = in_lines[import_index].split("#", 1)[0] + import_index += 1 + else: + import_index += 1 + + if import_index >= line_count: + break + + starting_line = in_lines[import_index] + + line, *end_of_line_comment = line.split("#", 1) + if ";" in line: + statements = [line.strip() for line in line.split(";")] + else: + statements = [line] + if end_of_line_comment: + statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" + + for statement in statements: + line, raw_line = normalize_line(statement) + type_of_import = import_type(line, config) or "" + raw_lines = [raw_line] + if not type_of_import: + out_lines.append(raw_line) + continue + + if import_index == -1: + import_index = index - 1 + nested_comments = {} + import_string, comment = parse_comments(line) + comments = [comment] if comment else [] + line_parts = [part for part in strip_syntax(import_string).strip().split(" ") if part] + if type_of_import == "from" and len(line_parts) == 2 and comments: + nested_comments[line_parts[-1]] = comments[0] + + if "(" in line.split("#", 1)[0] and index < line_count: + while not line.split("#")[0].strip().endswith(")") and index < line_count: + line, new_comment = parse_comments(in_lines[index]) + index += 1 + if new_comment: + comments.append(new_comment) + stripped_line = strip_syntax(line).strip() + if ( + type_of_import == "from" + and stripped_line + and " " not in stripped_line.replace(" as ", "") + and new_comment + ): + nested_comments[stripped_line] = comments[-1] + import_string += line_separator + line + raw_lines.append(line) + else: + while line.strip().endswith("\\"): + line, new_comment = parse_comments(in_lines[index]) + line = line.lstrip() + index += 1 + if new_comment: + comments.append(new_comment) + + # Still need to check for parentheses after an escaped line + if ( + "(" in line.split("#")[0] + and ")" not in line.split("#")[0] + and index < line_count + ): + stripped_line = strip_syntax(line).strip() + if ( + type_of_import == "from" + and stripped_line + and " " not in stripped_line.replace(" as ", "") + and new_comment + ): + nested_comments[stripped_line] = comments[-1] + import_string += line_separator + line + raw_lines.append(line) + + while not line.split("#")[0].strip().endswith(")") and index < line_count: + line, new_comment = parse_comments(in_lines[index]) + index += 1 + if new_comment: + comments.append(new_comment) + stripped_line = strip_syntax(line).strip() + if ( + type_of_import == "from" + and stripped_line + and " " not in stripped_line.replace(" as ", "") + and new_comment + ): + nested_comments[stripped_line] = comments[-1] + import_string += line_separator + line + raw_lines.append(line) + + stripped_line = strip_syntax(line).strip() + if ( + type_of_import == "from" + and stripped_line + and " " not in stripped_line.replace(" as ", "") + and new_comment + ): + nested_comments[stripped_line] = comments[-1] + if import_string.strip().endswith( + (" import", " cimport") + ) or line.strip().startswith(("import ", "cimport ")): + import_string += line_separator + line + else: + import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip() + + if type_of_import == "from": + cimports: bool + import_string = ( + import_string.replace("import(", "import (") + .replace("\\", " ") + .replace("\n", " ") + ) + if "import " not in import_string: + out_lines.extend(raw_lines) + continue + + if " cimport " in import_string: + parts = import_string.split(" cimport ") + cimports = True + + else: + parts = import_string.split(" import ") + cimports = False + + from_import = parts[0].split(" ") + import_string = (" cimport " if cimports else " import ").join( + [from_import[0] + " " + "".join(from_import[1:]), *parts[1:]] + ) + + just_imports = [ + item.replace("{|", "{ ").replace("|}", " }") + for item in strip_syntax(import_string).split() + ] + + attach_comments_to: list[Any] | None = None + direct_imports = just_imports[1:] + straight_import = True + top_level_module = "" + if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): + straight_import = False + while "as" in just_imports: + nested_module = None + as_index = just_imports.index("as") + if type_of_import == "from": + nested_module = just_imports[as_index - 1] + top_level_module = just_imports[0] + module = top_level_module + "." + nested_module + as_name = just_imports[as_index + 1] + direct_imports.remove(nested_module) + direct_imports.remove(as_name) + direct_imports.remove("as") + if nested_module == as_name and config.remove_redundant_aliases: + pass + elif as_name not in as_map["from"][module]: # pragma: no branch + as_map["from"][module].append(as_name) + + full_name = f"{nested_module} as {as_name}" + associated_comment = nested_comments.get(full_name) + if associated_comment: + categorized_comments["nested"].setdefault(top_level_module, {})[ + full_name + ] = associated_comment + if associated_comment in comments: # pragma: no branch + comments.pop(comments.index(associated_comment)) + else: + module = just_imports[as_index - 1] + as_name = just_imports[as_index + 1] + if module == as_name and config.remove_redundant_aliases: + pass + elif as_name not in as_map["straight"][module]: + as_map["straight"][module].append(as_name) + + if comments and attach_comments_to is None: + if nested_module and config.combine_as_imports: + attach_comments_to = categorized_comments["from"].setdefault( + f"{top_level_module}.__combined_as__", [] + ) + else: + if type_of_import == "from" or ( + config.remove_redundant_aliases and as_name == module.split(".")[-1] + ): + attach_comments_to = categorized_comments["straight"].setdefault( + module, [] + ) + else: + attach_comments_to = categorized_comments["straight"].setdefault( + f"{module} as {as_name}", [] + ) + del just_imports[as_index : as_index + 2] + + if type_of_import == "from": + import_from = just_imports.pop(0) + placed_module = finder(import_from) + if config.verbose and not config.only_modified: + print(f"from-type place_module for {import_from} returned {placed_module}") + + elif config.verbose: + verbose_output.append( + f"from-type place_module for {import_from} returned {placed_module}" + ) + if placed_module == "": + warn( + f"could not place module {import_from} of line {line} --" + " Do you need to define a default section?", + stacklevel=2, + ) + + if placed_module and placed_module not in imports: + raise MissingSection(import_module=import_from, section=placed_module) + + root = imports[placed_module][type_of_import] # type: ignore + for import_name in just_imports: + associated_comment = nested_comments.get(import_name) + if associated_comment: + categorized_comments["nested"].setdefault(import_from, {})[import_name] = ( + associated_comment + ) + if associated_comment in comments: # pragma: no branch + comments.pop(comments.index(associated_comment)) + if ( + config.force_single_line + and comments + and attach_comments_to is None + and len(just_imports) == 1 + ): + nested_from_comments = categorized_comments["nested"].setdefault( + import_from, {} + ) + existing_comment = nested_from_comments.get(just_imports[0], "") + nested_from_comments[just_imports[0]] = ( + f"{existing_comment}{'; ' if existing_comment else ''}{'; '.join(comments)}" + ) + comments = [] + + if comments and attach_comments_to is None: + attach_comments_to = categorized_comments["from"].setdefault(import_from, []) + + if len(out_lines) > max(import_index, 1) - 1: + last = out_lines[-1].rstrip() if out_lines else "" + while ( + last.startswith("#") + and not last.endswith('"""') + and not last.endswith("'''") + and "isort:imports-" not in last + and "isort: imports-" not in last + and not config.treat_all_comments_as_code + and last.strip() not in config.treat_comments_as_code + ): + categorized_comments["above"]["from"].setdefault(import_from, []).insert( + 0, out_lines.pop(-1) + ) + if out_lines: + last = out_lines[-1].rstrip() + else: + last = "" + if statement_index - 1 == import_index: # pragma: no cover + import_index -= len( + categorized_comments["above"]["from"].get(import_from, []) + ) + + if import_from not in root: + root[import_from] = OrderedDict( + (module, module in direct_imports) for module in just_imports + ) + else: + root[import_from].update( + (module, root[import_from].get(module, False) or module in direct_imports) + for module in just_imports + ) + + if comments and attach_comments_to is not None: + attach_comments_to.extend(comments) + + if ( + just_imports + and just_imports[-1] + and "," in import_string.split(just_imports[-1])[-1] + ): + trailing_commas.add(import_from) + else: + if comments and attach_comments_to is not None: + attach_comments_to.extend(comments) + comments = [] + + for module in just_imports: + if comments: + categorized_comments["straight"][module] = comments + comments = [] + + if len(out_lines) > max(import_index, +1, 1) - 1: + last = out_lines[-1].rstrip() if out_lines else "" + while ( + last.startswith("#") + and not last.endswith('"""') + and not last.endswith("'''") + and "isort:imports-" not in last + and "isort: imports-" not in last + and not config.treat_all_comments_as_code + and last.strip() not in config.treat_comments_as_code + ): + categorized_comments["above"]["straight"].setdefault(module, []).insert( + 0, out_lines.pop(-1) + ) + if out_lines: + last = out_lines[-1].rstrip() + else: + last = "" + if index - 1 == import_index: + import_index -= len( + categorized_comments["above"]["straight"].get(module, []) + ) + placed_module = finder(module) + if config.verbose and not config.only_modified: + print(f"else-type place_module for {module} returned {placed_module}") + + elif config.verbose: + verbose_output.append( + f"else-type place_module for {module} returned {placed_module}" + ) + if placed_module == "": + warn( + f"could not place module {module} of line {line} --" + " Do you need to define a default section?", + stacklevel=2, + ) + imports.setdefault("", {"straight": OrderedDict(), "from": OrderedDict()}) + + if placed_module and placed_module not in imports: + raise MissingSection(import_module=module, section=placed_module) + + straight_import |= imports[placed_module][type_of_import].get( # type: ignore + module, False + ) + imports[placed_module][type_of_import][module] = straight_import # type: ignore + + change_count = len(out_lines) - original_line_count + + return ParsedContent( + in_lines=in_lines, + lines_without_imports=out_lines, + import_index=import_index, + place_imports=place_imports, + import_placements=import_placements, + as_map=as_map, + imports=imports, + categorized_comments=categorized_comments, + change_count=change_count, + original_line_count=original_line_count, + line_separator=line_separator, + sections=config.sections, + verbose_output=verbose_output, + trailing_commas=trailing_commas, + ) diff --git a/.venv/lib/python3.10/site-packages/isort/place.py b/.venv/lib/python3.10/site-packages/isort/place.py new file mode 100644 index 0000000..2f863b3 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/place.py @@ -0,0 +1,146 @@ +"""Contains all logic related to placing an import within a certain section.""" + +import importlib +from collections.abc import Iterable +from fnmatch import fnmatch +from functools import lru_cache +from pathlib import Path + +from isort import sections +from isort.settings import DEFAULT_CONFIG, Config +from isort.utils import exists_case_sensitive + +LOCAL = "LOCALFOLDER" + + +def module(name: str, config: Config = DEFAULT_CONFIG) -> str: + """Returns the section placement for the given module name.""" + return module_with_reason(name, config)[0] + + +@lru_cache(maxsize=1000) +def module_with_reason(name: str, config: Config = DEFAULT_CONFIG) -> tuple[str, str]: + """Returns the section placement for the given module name alongside the reasoning.""" + return ( + _forced_separate(name, config) + or _local(name, config) + or _known_pattern(name, config) + or _src_path(name, config) + or (config.default_section, "Default option in Config or universal default.") + ) + + +def _forced_separate(name: str, config: Config) -> tuple[str, str] | None: + for forced_separate in config.forced_separate: + # Ensure all forced_separate patterns will match to end of string + path_glob = forced_separate + if not forced_separate.endswith("*"): + path_glob = f"{forced_separate}*" + + if fnmatch(name, path_glob) or fnmatch(name, "." + path_glob): + return (forced_separate, f"Matched forced_separate ({forced_separate}) config value.") + + return None + + +def _local(name: str, config: Config) -> tuple[str, str] | None: + if name.startswith("."): + return (LOCAL, "Module name started with a dot.") + + return None + + +def _known_pattern(name: str, config: Config) -> tuple[str, str] | None: + parts = name.split(".") + module_names_to_check = (".".join(parts[:first_k]) for first_k in range(len(parts), 0, -1)) + for module_name_to_check in module_names_to_check: + for pattern, placement in config.known_patterns: + if placement in config.sections and pattern.match(module_name_to_check): + return (placement, f"Matched configured known pattern {pattern}") + + return None + + +def _src_path( + name: str, + config: Config, + src_paths: Iterable[Path] | None = None, + prefix: tuple[str, ...] = (), +) -> tuple[str, str] | None: + if src_paths is None: + src_paths = config.src_paths + + root_module_name, *nested_module = name.split(".", 1) + new_prefix = (*prefix, root_module_name) + namespace = ".".join(new_prefix) + + for src_path in src_paths: + module_path = (src_path / root_module_name).resolve() + if not prefix and not module_path.is_dir() and src_path.name == root_module_name: + module_path = src_path.resolve() + if nested_module and ( + namespace in config.namespace_packages + or ( + config.auto_identify_namespace_packages + and _is_namespace_package(module_path, config.supported_extensions) + ) + ): + return _src_path(nested_module[0], config, (module_path,), new_prefix) + if ( + _is_module(module_path) + or _is_package(module_path) + or _src_path_is_module(src_path, root_module_name) + ): + return (sections.FIRSTPARTY, f"Found in one of the configured src_paths: {src_path}.") + + return None + + +def _is_module(path: Path) -> bool: + return ( + exists_case_sensitive(str(path.with_suffix(".py"))) + or any( + exists_case_sensitive(str(path.with_suffix(ext_suffix))) + for ext_suffix in importlib.machinery.EXTENSION_SUFFIXES + ) + or exists_case_sensitive(str(path / "__init__.py")) + ) + + +def _is_package(path: Path) -> bool: + return exists_case_sensitive(str(path)) and path.is_dir() + + +def _is_namespace_package(path: Path, src_extensions: frozenset[str]) -> bool: + if not _is_package(path): + return False + + init_file = path / "__init__.py" + if not init_file.exists(): + filenames = [ + filepath + for filepath in path.iterdir() + if filepath.suffix.lstrip(".") in src_extensions + or filepath.name.lower() in ("setup.cfg", "pyproject.toml") + ] + if filenames: + return False + else: + with init_file.open("rb") as open_init_file: + file_start = open_init_file.read(4096) + if ( + b"__import__('pkg_resources').declare_namespace(__name__)" not in file_start + and b'__import__("pkg_resources").declare_namespace(__name__)' not in file_start + and b"__path__ = __import__('pkgutil').extend_path(__path__, __name__)" + not in file_start + and b'__path__ = __import__("pkgutil").extend_path(__path__, __name__)' + not in file_start + ): + return False + return True + + +def _src_path_is_module(src_path: Path, module_name: str) -> bool: + return ( + module_name == src_path.name and src_path.is_dir() and exists_case_sensitive(str(src_path)) + ) diff --git a/.venv/lib/python3.10/site-packages/isort/profiles.py b/.venv/lib/python3.10/site-packages/isort/profiles.py new file mode 100644 index 0000000..f2f0243 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/profiles.py @@ -0,0 +1,96 @@ +"""Common profiles are defined here to be easily used within a project using --profile {name}""" + +from typing import Any + +black = { + "multi_line_output": 3, + "include_trailing_comma": True, + "split_on_trailing_comma": True, + "force_grid_wrap": 0, + "use_parentheses": True, + "ensure_newline_before_comments": True, + "line_length": 88, +} +django = { + "combine_as_imports": True, + "include_trailing_comma": True, + "multi_line_output": 5, + "line_length": 79, +} +pycharm = { + "multi_line_output": 3, + "force_grid_wrap": 2, + "lines_after_imports": 2, +} +google = { + "force_single_line": True, + "force_sort_within_sections": True, + "lexicographical": True, + "line_length": 1000, + "single_line_exclusions": ( + "collections.abc", + "six.moves", + "typing", + "typing_extensions", + ), + "order_by_type": False, + "group_by_package": True, +} +open_stack = { + "force_single_line": True, + "force_sort_within_sections": True, + "lexicographical": True, +} +plone = black.copy() +plone.update( + { + "force_alphabetical_sort": True, + "force_single_line": True, + "lines_after_imports": 2, + } +) +attrs = { + "atomic": True, + "force_grid_wrap": 0, + "include_trailing_comma": True, + "lines_after_imports": 2, + "lines_between_types": 1, + "multi_line_output": 3, + "use_parentheses": True, +} +hug = { + "multi_line_output": 3, + "include_trailing_comma": True, + "force_grid_wrap": 0, + "use_parentheses": True, + "line_length": 100, +} +wemake = { + "multi_line_output": 3, + "include_trailing_comma": True, + "use_parentheses": True, + "line_length": 80, +} +appnexus = { + **black, + "force_sort_within_sections": True, + "order_by_type": False, + "case_sensitive": False, + "reverse_relative": True, + "sort_relative_in_force_sorted_sections": True, + "sections": ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "APPLICATION", "LOCALFOLDER"], + "no_lines_before": "LOCALFOLDER", +} + +profiles: dict[str, dict[str, Any]] = { + "black": black, + "django": django, + "pycharm": pycharm, + "google": google, + "open_stack": open_stack, + "plone": plone, + "attrs": attrs, + "hug": hug, + "wemake": wemake, + "appnexus": appnexus, +} diff --git a/.venv/lib/python3.10/site-packages/isort/py.typed b/.venv/lib/python3.10/site-packages/isort/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.10/site-packages/isort/sections.py b/.venv/lib/python3.10/site-packages/isort/sections.py new file mode 100644 index 0000000..84671cb --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/sections.py @@ -0,0 +1,8 @@ +"""Defines all sections isort uses by default""" + +FUTURE: str = "FUTURE" +STDLIB: str = "STDLIB" +THIRDPARTY: str = "THIRDPARTY" +FIRSTPARTY: str = "FIRSTPARTY" +LOCALFOLDER: str = "LOCALFOLDER" +DEFAULT: tuple[str, ...] = (FUTURE, STDLIB, THIRDPARTY, FIRSTPARTY, LOCALFOLDER) diff --git a/.venv/lib/python3.10/site-packages/isort/settings.py b/.venv/lib/python3.10/site-packages/isort/settings.py new file mode 100644 index 0000000..f40d48a --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/settings.py @@ -0,0 +1,933 @@ +"""isort/settings.py. + +Defines how the default settings for isort should be loaded +""" + +import configparser +import fnmatch +import os +import posixpath +import re +import stat +import subprocess # nosec # Needed for gitignore support. +import sys +from collections.abc import Callable, Iterable +from dataclasses import dataclass, field +from pathlib import Path +from re import Pattern +from typing import TYPE_CHECKING, Any +from warnings import warn + +from . import sorting, stdlibs +from .exceptions import ( + FormattingPluginDoesNotExist, + InvalidSettingsPath, + ProfileDoesNotExist, + SortingFunctionDoesNotExist, + UnsupportedSettings, +) +from .profiles import profiles as profiles +from .sections import DEFAULT as SECTION_DEFAULTS +from .sections import FIRSTPARTY, FUTURE, LOCALFOLDER, STDLIB, THIRDPARTY +from .utils import Trie +from .wrap_modes import WrapModes +from .wrap_modes import from_string as wrap_mode_from_string + +if TYPE_CHECKING: + from importlib.metadata import EntryPoints + + tomllib: Any +else: + if sys.version_info >= (3, 11): + import tomllib + else: + from ._vendored import tomli as tomllib + +_SHEBANG_RE = re.compile(rb"^#!.*\bpython[23w]?\b") +CYTHON_EXTENSIONS = frozenset({"pyx", "pxd"}) +SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", *CYTHON_EXTENSIONS}) +BLOCKED_EXTENSIONS = frozenset({"pex"}) +FILE_SKIP_COMMENTS: tuple[str, ...] = ( + "isort:" + "skip_file", + "isort: " + "skip_file", +) # Concatenated to avoid this file being skipped +MAX_CONFIG_SEARCH_DEPTH: int = 25 # The number of parent directories to for a config file within +STOP_CONFIG_SEARCH_ON_DIRS: tuple[str, ...] = (".git", ".hg") +VALID_PY_TARGETS: tuple[str, ...] = tuple( + target.replace("py", "") for target in dir(stdlibs) if not target.startswith("_") +) +CONFIG_SOURCES: tuple[str, ...] = ( + ".isort.cfg", + "pyproject.toml", + "setup.cfg", + "tox.ini", + ".editorconfig", +) +DEFAULT_SKIP: frozenset[str] = frozenset( + { + ".venv", + "venv", + ".tox", + ".eggs", + ".git", + ".hg", + ".mypy_cache", + ".nox", + ".svn", + ".bzr", + "_build", + "buck-out", + "build", + "dist", + ".pants.d", + ".direnv", + "node_modules", + "__pypackages__", + ".pytype", + } +) + +CONFIG_SECTIONS: dict[str, tuple[str, ...]] = { + ".isort.cfg": ("settings", "isort"), + "pyproject.toml": ("tool.isort",), + "setup.cfg": ("isort", "tool:isort"), + "tox.ini": ("isort", "tool:isort"), + ".editorconfig": ("*", "*.py", "**.py", "*.{py}"), +} +FALLBACK_CONFIG_SECTIONS: tuple[str, ...] = ("isort", "tool:isort", "tool.isort") + +IMPORT_HEADING_PREFIX = "import_heading_" +IMPORT_FOOTER_PREFIX = "import_footer_" +KNOWN_PREFIX = "known_" +KNOWN_SECTION_MAPPING: dict[str, str] = { + STDLIB: "STANDARD_LIBRARY", + FUTURE: "FUTURE_LIBRARY", + FIRSTPARTY: "FIRST_PARTY", + THIRDPARTY: "THIRD_PARTY", + LOCALFOLDER: "LOCAL_FOLDER", +} + +RUNTIME_SOURCE = "runtime" + +DEPRECATED_SETTINGS = ("not_skip", "keep_direct_and_as_imports") + +_STR_BOOLEAN_MAPPING = { + "y": True, + "yes": True, + "t": True, + "on": True, + "1": True, + "true": True, + "n": False, + "no": False, + "f": False, + "off": False, + "0": False, + "false": False, +} + + +@dataclass(frozen=True) +class _Config: + """Defines the data schema and defaults used for isort configuration. + + NOTE: known lists, such as known_standard_library, are intentionally not complete as they are + dynamically determined later on. + """ + + py_version: str = "3" + force_to_top: frozenset[str] = frozenset() + skip: frozenset[str] = DEFAULT_SKIP + extend_skip: frozenset[str] = frozenset() + skip_glob: frozenset[str] = frozenset() + extend_skip_glob: frozenset[str] = frozenset() + skip_gitignore: bool = False + line_length: int = 79 + wrap_length: int = 0 + line_ending: str = "" + sections: tuple[str, ...] = SECTION_DEFAULTS + no_sections: bool = False + known_future_library: frozenset[str] = frozenset(("__future__",)) + known_third_party: frozenset[str] = frozenset() + known_first_party: frozenset[str] = frozenset() + known_local_folder: frozenset[str] = frozenset() + known_standard_library: frozenset[str] = frozenset() + extra_standard_library: frozenset[str] = frozenset() + known_other: dict[str, frozenset[str]] = field(default_factory=dict) + multi_line_output: WrapModes = WrapModes.GRID # type: ignore + forced_separate: tuple[str, ...] = () + indent: str = " " * 4 + comment_prefix: str = " #" + length_sort: bool = False + length_sort_straight: bool = False + length_sort_sections: frozenset[str] = frozenset() + add_imports: frozenset[str] = frozenset() + remove_imports: frozenset[str] = frozenset() + append_only: bool = False + reverse_relative: bool = False + force_single_line: bool = False + single_line_exclusions: tuple[str, ...] = () + default_section: str = THIRDPARTY + import_headings: dict[str, str] = field(default_factory=dict) + import_footers: dict[str, str] = field(default_factory=dict) + balanced_wrapping: bool = False + use_parentheses: bool = False + order_by_type: bool = True + atomic: bool = False + lines_before_imports: int = -1 + lines_after_imports: int = -1 + lines_between_sections: int = 1 + lines_between_types: int = 0 + combine_as_imports: bool = False + combine_star: bool = False + include_trailing_comma: bool = False + from_first: bool = False + verbose: bool = False + quiet: bool = False + force_adds: bool = False + force_alphabetical_sort_within_sections: bool = False + force_alphabetical_sort: bool = False + force_grid_wrap: int = 0 + force_sort_within_sections: bool = False + lexicographical: bool = False + group_by_package: bool = False + ignore_whitespace: bool = False + no_lines_before: frozenset[str] = frozenset() + no_inline_sort: bool = False + ignore_comments: bool = False + case_sensitive: bool = False + sources: tuple[dict[str, Any], ...] = () + virtual_env: str = "" + conda_env: str = "" + ensure_newline_before_comments: bool = False + directory: str = "" + profile: str = "" + honor_noqa: bool = False + src_paths: tuple[Path, ...] = () + old_finders: bool = False + remove_redundant_aliases: bool = False + float_to_top: bool = False + filter_files: bool = False + formatter: str = "" + formatting_function: Callable[[str, str, object], str] | None = None + color_output: bool = False + treat_comments_as_code: frozenset[str] = frozenset() + treat_all_comments_as_code: bool = False + supported_extensions: frozenset[str] = SUPPORTED_EXTENSIONS + blocked_extensions: frozenset[str] = BLOCKED_EXTENSIONS + constants: frozenset[str] = frozenset() + classes: frozenset[str] = frozenset() + variables: frozenset[str] = frozenset() + dedup_headings: bool = False + only_sections: bool = False + only_modified: bool = False + combine_straight_imports: bool = False + auto_identify_namespace_packages: bool = True + namespace_packages: frozenset[str] = frozenset() + follow_links: bool = True + indented_import_headings: bool = True + honor_case_in_force_sorted_sections: bool = False + sort_relative_in_force_sorted_sections: bool = False + overwrite_in_place: bool = False + reverse_sort: bool = False + star_first: bool = False + import_dependencies = dict[str, str] + git_ls_files: dict[Path, set[str]] = field(default_factory=dict) + format_error: str = "{error}: {message}" + format_success: str = "{success}: {message}" + sort_order: str = "natural" + sort_reexports: bool = False + split_on_trailing_comma: bool = False + + def __post_init__(self) -> None: + py_version = self.py_version + if py_version == "auto": # pragma: no cover + py_version = f"{sys.version_info.major}{sys.version_info.minor}" + + if py_version not in VALID_PY_TARGETS: + raise ValueError( + f"The python version {py_version} is not supported. " + "You can set a python version with the -py or --python-version flag. " + f"The following versions are supported: {VALID_PY_TARGETS}" + ) + + if py_version != "all": + object.__setattr__(self, "py_version", f"py{py_version}") + + if not self.known_standard_library: + object.__setattr__( + self, "known_standard_library", frozenset(getattr(stdlibs, self.py_version).stdlib) + ) + + if self.multi_line_output == WrapModes.VERTICAL_GRID_GROUPED_NO_COMMA: # type: ignore + vertical_grid_grouped = WrapModes.VERTICAL_GRID_GROUPED # type: ignore + object.__setattr__(self, "multi_line_output", vertical_grid_grouped) + if self.force_alphabetical_sort: + object.__setattr__(self, "force_alphabetical_sort_within_sections", True) + object.__setattr__(self, "no_sections", True) + object.__setattr__(self, "lines_between_types", 1) + object.__setattr__(self, "from_first", True) + if self.wrap_length > self.line_length: + raise ValueError( + "wrap_length must be set lower than or equal to line_length: " + f"{self.wrap_length} > {self.line_length}." + ) + + def __hash__(self) -> int: + return id(self) + + +_DEFAULT_SETTINGS = {**vars(_Config()), "source": "defaults"} + + +class Config(_Config): + def __init__( + self, + settings_file: str = "", + settings_path: str = "", + config: _Config | None = None, + **config_overrides: Any, + ): + self._known_patterns: list[tuple[Pattern[str], str]] | None = None + self._section_comments: tuple[str, ...] | None = None + self._section_comments_end: tuple[str, ...] | None = None + self._skips: frozenset[str] | None = None + self._skip_globs: frozenset[str] | None = None + self._sorting_function: Callable[..., list[str]] | None = None + + if config: + config_vars = vars(config).copy() + config_vars.update(config_overrides) + config_vars["py_version"] = config_vars["py_version"].replace("py", "") + config_vars.pop("_known_patterns") + config_vars.pop("_section_comments") + config_vars.pop("_section_comments_end") + config_vars.pop("_skips") + config_vars.pop("_skip_globs") + config_vars.pop("_sorting_function") + super().__init__(**config_vars) + return + + # We can't use self.quiet to conditionally show warnings before super.__init__() is called + # at the end of this method. _Config is also frozen so setting self.quiet isn't possible. + # Therefore we extract quiet early here in a variable and use that in warning conditions. + quiet = config_overrides.get("quiet", False) + + sources: list[dict[str, Any]] = [_DEFAULT_SETTINGS] + + config_settings: dict[str, Any] + project_root: str + if settings_file: + config_settings = _get_config_data( + settings_file, + CONFIG_SECTIONS.get(os.path.basename(settings_file), FALLBACK_CONFIG_SECTIONS), + ) + project_root = os.path.dirname(settings_file) + if not config_settings and not quiet: + warn( + f"A custom settings file was specified: {settings_file} but no configuration " + "was found inside. This can happen when [settings] is used as the config " + "header instead of [isort]. " + "See: https://pycqa.github.io/isort/docs/configuration/config_files" + "#custom-config-files for more information.", + stacklevel=2, + ) + elif settings_path: + if not os.path.exists(settings_path): + raise InvalidSettingsPath(settings_path) + + settings_path = os.path.abspath(settings_path) + project_root, config_settings = _find_config(settings_path) + else: + config_settings = {} + project_root = os.getcwd() + + profile_name = config_overrides.get("profile", config_settings.get("profile", "")) + profile: dict[str, Any] = {} + if profile_name: + if profile_name not in profiles: + for plugin in entry_points(group="isort.profiles"): + profiles.setdefault(plugin.name, plugin.load()) + + if profile_name not in profiles: + raise ProfileDoesNotExist(profile_name) + + profile = profiles[profile_name].copy() + profile["source"] = f"{profile_name} profile" + sources.append(profile) + + if config_settings: + sources.append(config_settings) + if config_overrides: + config_overrides["source"] = RUNTIME_SOURCE + sources.append(config_overrides) + + combined_config = {**profile, **config_settings, **config_overrides} + if "indent" in combined_config: + indent = str(combined_config["indent"]) + if indent.isdigit(): + indent = " " * int(indent) + else: + indent = indent.strip("'").strip('"') + if indent.lower() == "tab": + indent = "\t" + combined_config["indent"] = indent + + known_other = {} + import_headings = {} + import_footers = {} + for key, value in tuple(combined_config.items()): + # Collect all known sections beyond those that have direct entries + if key.startswith(KNOWN_PREFIX) and key not in ( + "known_standard_library", + "known_future_library", + "known_third_party", + "known_first_party", + "known_local_folder", + ): + import_heading = key[len(KNOWN_PREFIX) :].lower() + maps_to_section = import_heading.upper() + combined_config.pop(key) + if maps_to_section in KNOWN_SECTION_MAPPING: + section_name = f"known_{KNOWN_SECTION_MAPPING[maps_to_section].lower()}" + if section_name in combined_config and not quiet: + warn( + f"Can't set both {key} and {section_name} in the same config file.\n" + f"Default to {section_name} if unsure." + "\n\n" + "See: https://pycqa.github.io/isort/" + "#custom-sections-and-ordering.", + stacklevel=2, + ) + else: + combined_config[section_name] = frozenset(value) + else: + known_other[import_heading] = frozenset(value) + if maps_to_section not in combined_config.get("sections", ()) and not quiet: + warn( + f"`{key}` setting is defined, but {maps_to_section} is not" + " included in `sections` config option:" + f" {combined_config.get('sections', SECTION_DEFAULTS)}.\n\n" + "See: https://pycqa.github.io/isort/" + "#custom-sections-and-ordering.", + stacklevel=2, + ) + if key.startswith(IMPORT_HEADING_PREFIX): + import_headings[key[len(IMPORT_HEADING_PREFIX) :].lower()] = str(value) + if key.startswith(IMPORT_FOOTER_PREFIX): + import_footers[key[len(IMPORT_FOOTER_PREFIX) :].lower()] = str(value) + + # Coerce all provided config values into their correct type + default_value = _DEFAULT_SETTINGS.get(key, None) + if default_value is None: + continue + + combined_config[key] = type(default_value)(value) + + for section in combined_config.get("sections", ()): + if section in SECTION_DEFAULTS: + continue + + if section.lower() not in known_other: + config_keys = ", ".join(known_other.keys()) + warn( + f"`sections` setting includes {section}, but no known_{section.lower()} " + "is defined. " + f"The following known_SECTION config options are defined: {config_keys}.", + stacklevel=2, + ) + + if "directory" not in combined_config: + combined_config["directory"] = ( + os.path.dirname(config_settings["source"]) + if config_settings.get("source", None) + else os.getcwd() + ) + + path_root = Path(combined_config.get("directory", project_root)).resolve() + path_root = path_root if path_root.is_dir() else path_root.parent + if "src_paths" not in combined_config: + combined_config["src_paths"] = (path_root / "src", path_root) + else: + src_paths: list[Path] = [] + for src_path in combined_config.get("src_paths", ()): + full_paths = ( + path_root.glob(src_path) if "*" in str(src_path) else [path_root / src_path] + ) + for path in full_paths: + if path not in src_paths: + src_paths.append(path) + + combined_config["src_paths"] = tuple(src_paths) + + if "formatter" in combined_config: + for plugin in entry_points(group="isort.formatters"): + if plugin.name == combined_config["formatter"]: + combined_config["formatting_function"] = plugin.load() + break + else: + raise FormattingPluginDoesNotExist(combined_config["formatter"]) + + # Remove any config values that are used for creating config object but + # aren't defined in dataclass + combined_config.pop("source", None) + combined_config.pop("sources", None) + combined_config.pop("runtime_src_paths", None) + + deprecated_options_used = [ + option for option in combined_config if option in DEPRECATED_SETTINGS + ] + if deprecated_options_used: + for deprecated_option in deprecated_options_used: + combined_config.pop(deprecated_option) + if not quiet: + warn( + "W0503: Deprecated config options were used: " + f"{', '.join(deprecated_options_used)}." + "Please see the 5.0.0 upgrade guide: " + "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html", + stacklevel=2, + ) + + if known_other: + combined_config["known_other"] = known_other + if import_headings: + for import_heading_key in import_headings: + combined_config.pop(f"{IMPORT_HEADING_PREFIX}{import_heading_key}") + combined_config["import_headings"] = import_headings + if import_footers: + for import_footer_key in import_footers: + combined_config.pop(f"{IMPORT_FOOTER_PREFIX}{import_footer_key}") + combined_config["import_footers"] = import_footers + + unsupported_config_errors = {} + for option in set(combined_config.keys()).difference( + getattr(_Config, "__dataclass_fields__", {}).keys() + ): + for source in reversed(sources): + if option in source: + unsupported_config_errors[option] = { + "value": source[option], + "source": source["source"], + } + if unsupported_config_errors: + raise UnsupportedSettings(unsupported_config_errors) + + super().__init__(sources=tuple(sources), **combined_config) + + def is_supported_filetype(self, file_name: str) -> bool: + _root, ext = os.path.splitext(file_name) + ext = ext.lstrip(".") + if ext in self.supported_extensions: + return True + if ext in self.blocked_extensions: + return False + + # Skip editor backup files. + if file_name.endswith("~"): + return False + + try: + if stat.S_ISFIFO(os.stat(file_name).st_mode): + return False + except OSError: + pass + + try: + with open(file_name, "rb") as fp: + line = fp.readline(100) + except OSError: + return False + return bool(_SHEBANG_RE.match(line)) + + def _check_folder_git_ls_files(self, folder: str) -> Path | None: + env = {**os.environ, "LANG": "C.UTF-8"} + try: + topfolder_result = subprocess.check_output( # nosec # skipcq: PYL-W1510 + ["git", "-C", folder, "rev-parse", "--show-toplevel"], encoding="utf-8", env=env + ) + except subprocess.CalledProcessError: + return None + + git_folder = Path(topfolder_result.rstrip()).resolve() + + # files committed to git + tracked_files = ( + subprocess.check_output( # nosec # skipcq: PYL-W1510 + ["git", "-C", str(git_folder), "ls-files", "-z"], + encoding="utf-8", + env=env, + ) + .rstrip("\0") + .split("\0") + ) + # files that haven't been committed yet, but aren't ignored + tracked_files_others = ( + subprocess.check_output( # nosec # skipcq: PYL-W1510 + ["git", "-C", str(git_folder), "ls-files", "-z", "--others", "--exclude-standard"], + encoding="utf-8", + env=env, + ) + .rstrip("\0") + .split("\0") + ) + + self.git_ls_files[git_folder] = { + str(git_folder / Path(f)) for f in tracked_files + tracked_files_others + } + return git_folder + + def is_skipped(self, file_path: Path) -> bool: + """Returns True if the file and/or folder should be skipped based on current settings.""" + if self.directory and Path(self.directory) in file_path.resolve().parents: + file_name = os.path.relpath(file_path.resolve(), self.directory) + else: + file_name = str(file_path) + + os_path = str(file_path) + + normalized_path = os_path.replace("\\", "/") + if normalized_path[1:2] == ":": + normalized_path = normalized_path[2:] + + for skip_path in self.skips: + if posixpath.abspath(normalized_path) == posixpath.abspath( + skip_path.replace("\\", "/") + ): + return True + + position = os.path.split(file_name) + while position[1]: + if position[1] in self.skips: + return True + position = os.path.split(position[0]) + + for sglob in self.skip_globs: + if fnmatch.fnmatch(file_name, sglob) or fnmatch.fnmatch("/" + file_name, sglob): + return True + + if not (os.path.isfile(os_path) or os.path.isdir(os_path) or os.path.islink(os_path)): + return True + + if self.skip_gitignore: + if file_path.name == ".git": # pragma: no cover + return True + + git_folder = None + + file_paths = [file_path, file_path.resolve()] + for folder in self.git_ls_files: + if any(folder in path.parents for path in file_paths): + git_folder = folder + break + else: + git_folder = self._check_folder_git_ls_files(str(file_path.parent)) + + # git_ls_files are good files you should parse. If you're not in the allow list, skip. + + if ( + git_folder + and not file_path.is_dir() + and str(file_path.resolve()) not in self.git_ls_files[git_folder] + ): + return True + + return False + + @property + def known_patterns(self) -> list[tuple[Pattern[str], str]]: + if self._known_patterns is not None: + return self._known_patterns + + self._known_patterns = [] + pattern_sections = [STDLIB] + [section for section in self.sections if section != STDLIB] + for placement in reversed(pattern_sections): + known_placement = KNOWN_SECTION_MAPPING.get(placement, placement).lower() + config_key = f"{KNOWN_PREFIX}{known_placement}" + known_modules = getattr(self, config_key, self.known_other.get(known_placement, ())) + extra_modules = getattr(self, f"extra_{known_placement}", ()) + all_modules = set(extra_modules).union(known_modules) + known_patterns = [ + pattern + for known_pattern in all_modules + for pattern in self._parse_known_pattern(known_pattern) + ] + for known_pattern in known_patterns: + regexp = "^" + known_pattern.replace("*", ".*").replace("?", ".?") + "$" + self._known_patterns.append((re.compile(regexp), placement)) + + return self._known_patterns + + @property + def section_comments(self) -> tuple[str, ...]: + if self._section_comments is not None: + return self._section_comments + + self._section_comments = tuple(f"# {heading}" for heading in self.import_headings.values()) + return self._section_comments + + @property + def section_comments_end(self) -> tuple[str, ...]: + if self._section_comments_end is not None: + return self._section_comments_end + + self._section_comments_end = tuple(f"# {footer}" for footer in self.import_footers.values()) + return self._section_comments_end + + @property + def skips(self) -> frozenset[str]: + if self._skips is not None: + return self._skips + + self._skips = self.skip.union(self.extend_skip) + return self._skips + + @property + def skip_globs(self) -> frozenset[str]: + if self._skip_globs is not None: + return self._skip_globs + + self._skip_globs = self.skip_glob.union(self.extend_skip_glob) + return self._skip_globs + + @property + def sorting_function(self) -> Callable[..., list[str]]: + if self._sorting_function is not None: + return self._sorting_function + + if self.sort_order == "natural": + self._sorting_function = sorting.naturally + elif self.sort_order == "native": + self._sorting_function = sorted + else: + available_sort_orders = ["natural", "native"] + for sort_plugin in entry_points(group="isort.sort_function"): + available_sort_orders.append(sort_plugin.name) + if sort_plugin.name == self.sort_order: + self._sorting_function = sort_plugin.load() + break + else: + raise SortingFunctionDoesNotExist(self.sort_order, available_sort_orders) + + return self._sorting_function + + def _parse_known_pattern(self, pattern: str) -> list[str]: + """Expand pattern if identified as a directory and return found sub packages""" + if pattern.endswith(os.path.sep): + patterns = [ + filename + for filename in os.listdir(os.path.join(self.directory, pattern)) + if os.path.isdir(os.path.join(self.directory, pattern, filename)) + ] + else: + patterns = [pattern] + + return patterns + + +def _get_str_to_type_converter(setting_name: str) -> Callable[[str], Any] | type[Any]: + type_converter: Callable[[str], Any] | type[Any] = type(_DEFAULT_SETTINGS.get(setting_name, "")) + if type_converter == WrapModes: + type_converter = wrap_mode_from_string + return type_converter + + +def _as_list(value: str) -> list[str]: + if isinstance(value, list): + return [item.strip() for item in value] + filtered = [item.strip() for item in value.replace("\n", ",").split(",") if item.strip()] + return filtered + + +def _abspaths(cwd: str, values: Iterable[str]) -> set[str]: + paths = { + ( + os.path.join(cwd, value) + if not value.startswith(os.path.sep) and value.endswith(os.path.sep) + else value + ) + for value in values + } + return paths + + +def _find_config(path: str) -> tuple[str, dict[str, Any]]: + current_directory = path + tries = 0 + while current_directory and tries < MAX_CONFIG_SEARCH_DEPTH: + for config_file_name in CONFIG_SOURCES: + potential_config_file = os.path.join(current_directory, config_file_name) + if os.path.isfile(potential_config_file): + config_data: dict[str, Any] + try: + config_data = _get_config_data( + potential_config_file, CONFIG_SECTIONS[config_file_name] + ) + except Exception: + warn( + f"Failed to pull configuration information from {potential_config_file}", + stacklevel=2, + ) + config_data = {} + if config_data: + return (current_directory, config_data) + + for stop_dir in STOP_CONFIG_SEARCH_ON_DIRS: + if os.path.isdir(os.path.join(current_directory, stop_dir)): + return (current_directory, {}) + + new_directory = os.path.split(current_directory)[0] + if new_directory == current_directory: + break + + current_directory = new_directory + tries += 1 + + return (path, {}) + + +def find_all_configs(path: str) -> Trie: + """ + Looks for config files in the path provided and in all of its sub-directories. + Parses and stores any config file encountered in a trie and returns the root of + the trie + """ + trie_root = Trie("default", {}) + + for dirpath, _, _ in os.walk(path): + for config_file_name in CONFIG_SOURCES: + potential_config_file = os.path.join(dirpath, config_file_name) + if os.path.isfile(potential_config_file): + config_data: dict[str, Any] + try: + config_data = _get_config_data( + potential_config_file, CONFIG_SECTIONS[config_file_name] + ) + except Exception: + warn( + f"Failed to pull configuration information from {potential_config_file}", + stacklevel=2, + ) + config_data = {} + + if config_data: + trie_root.insert(potential_config_file, config_data) + break + + return trie_root + + +def _get_config_data(file_path: str, sections: tuple[str, ...]) -> dict[str, Any]: + settings: dict[str, Any] = {} + + if file_path.endswith(".toml"): + with open(file_path, "rb") as bin_config_file: + config = tomllib.load(bin_config_file) + for section in sections: + config_section = config + for key in section.split("."): + config_section = config_section.get(key, {}) + settings.update(config_section) + else: + with open(file_path, encoding="utf-8") as config_file: + if file_path.endswith(".editorconfig"): + line = "\n" + last_position = config_file.tell() + while line: + line = config_file.readline() + if "[" in line: + config_file.seek(last_position) + break + last_position = config_file.tell() + + config = configparser.ConfigParser(strict=False) + config.read_file(config_file) + for section in sections: + if section.startswith("*.{") and section.endswith("}"): + extension = section[len("*.{") : -1] + for config_key in config: + if ( + config_key.startswith("*.{") + and config_key.endswith("}") + and extension + in (text.strip() for text in config_key[len("*.{") : -1].split(",")) + ): + settings.update(config.items(config_key)) + + elif config.has_section(section): + settings.update(config.items(section)) + + if settings: + settings["source"] = file_path + + if file_path.endswith(".editorconfig"): + indent_style = settings.pop("indent_style", "").strip() + indent_size = settings.pop("indent_size", "").strip() + if indent_size == "tab": + indent_size = settings.pop("tab_width", "").strip() + + if indent_style == "space": + settings["indent"] = " " * ((indent_size and int(indent_size)) or 4) + + elif indent_style == "tab": + settings["indent"] = "\t" * ((indent_size and int(indent_size)) or 1) + + max_line_length = settings.pop("max_line_length", "").strip() + if max_line_length and (max_line_length == "off" or max_line_length.isdigit()): + settings["line_length"] = ( + float("inf") if max_line_length == "off" else int(max_line_length) + ) + settings = { + key: value + for key, value in settings.items() + if key in _DEFAULT_SETTINGS or key.startswith(KNOWN_PREFIX) + } + + for key, value in settings.items(): + existing_value_type = _get_str_to_type_converter(key) + if existing_value_type is tuple: + settings[key] = tuple(_as_list(value)) + elif existing_value_type is frozenset: + settings[key] = frozenset(_as_list(settings.get(key))) # type: ignore + elif existing_value_type is bool: + # Only some configuration formats support native boolean values. + if not isinstance(value, bool): + value = _as_bool(value) + settings[key] = value + elif key.startswith(KNOWN_PREFIX): + settings[key] = _abspaths(os.path.dirname(file_path), _as_list(value)) + elif key == "force_grid_wrap": + try: + result = existing_value_type(value) + except ValueError: # backwards compatibility for true / false force grid wrap + result = 0 if value.lower().strip() == "false" else 2 + settings[key] = result + elif key == "comment_prefix": + settings[key] = str(value).strip("'").strip('"') + else: + settings[key] = existing_value_type(value) + + return settings + + +def _as_bool(value: str) -> bool: + """Given a string value that represents True or False, returns the Boolean equivalent. + Heavily inspired from distutils strtobool. + """ + try: + return _STR_BOOLEAN_MAPPING[value.lower()] + except KeyError: + raise ValueError(f"invalid truth value {value}") + + +def entry_points(group: str) -> "EntryPoints": + """Call entry_point after lazy loading it. + + TODO: The reason for lazy loading here are unknown. + """ + from importlib.metadata import entry_points as ep # noqa: PLC0415 + + return ep(group=group) + + +DEFAULT_CONFIG = Config() diff --git a/.venv/lib/python3.10/site-packages/isort/setuptools_commands.py b/.venv/lib/python3.10/site-packages/isort/setuptools_commands.py new file mode 100644 index 0000000..3e6cc2e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/setuptools_commands.py @@ -0,0 +1,63 @@ +import glob +import os +import sys +from collections.abc import Iterator +from typing import Any +from warnings import warn + +import setuptools + +from . import api +from .settings import DEFAULT_CONFIG + + +class ISortCommand(setuptools.Command): + """The :class:`ISortCommand` class is used by setuptools to perform + imports checks on registered modules. + """ + + description = "Run isort on modules registered in setuptools" + # Potentially unused variable - check if can be safely removed + user_options: list[Any] = [] # type: ignore[misc] + + def initialize_options(self) -> None: + default_settings = vars(DEFAULT_CONFIG).copy() + for key, value in default_settings.items(): + setattr(self, key, value) + + def finalize_options(self) -> None: + """Get options from config files.""" + self.arguments: dict[str, Any] = {} # skipcq: PYL-W0201 + self.arguments["settings_path"] = os.getcwd() + + def distribution_files(self) -> Iterator[str]: + """Find distribution packages.""" + # This is verbatim from flake8 + if self.distribution.packages: # pragma: no cover + package_dirs = self.distribution.package_dir or {} + for package in self.distribution.packages: + pkg_dir = package + if package in package_dirs: + pkg_dir = package_dirs[package] + elif "" in package_dirs: # pragma: no cover + pkg_dir = package_dirs[""] + os.path.sep + pkg_dir + yield pkg_dir.replace(".", os.path.sep) + + if self.distribution.py_modules: + for filename in self.distribution.py_modules: + yield f"{filename}.py" + # Don't miss the setup.py file itself + yield "setup.py" + + def run(self) -> None: + arguments = self.arguments + wrong_sorted_files = False + for path in self.distribution_files(): + for python_file in glob.iglob(os.path.join(path, "*.py")): + try: + if not api.check_file(python_file, **arguments): + wrong_sorted_files = True # pragma: no cover + except OSError as error: # pragma: no cover + warn(f"Unable to parse file {python_file} due to {error}", stacklevel=2) + if wrong_sorted_files: + sys.exit(1) # pragma: no cover diff --git a/.venv/lib/python3.10/site-packages/isort/sorting.py b/.venv/lib/python3.10/site-packages/isort/sorting.py new file mode 100644 index 0000000..cb42b15 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/sorting.py @@ -0,0 +1,131 @@ +import re +from collections.abc import Callable, Iterable +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from .settings import Config +else: + Config = Any + +_import_line_intro_re = re.compile("^(?:from|import) ") +_import_line_midline_import_re = re.compile(" import ") + + +def module_key( + module_name: str, + config: Config, + sub_imports: bool = False, + ignore_case: bool = False, + section_name: Any | None = None, + straight_import: bool | None = False, +) -> str: + match = re.match(r"^(\.+)\s*(.*)", module_name) + if match: + sep = " " if config.reverse_relative else "_" + module_name = sep.join(match.groups()) + + prefix = "" + if ignore_case: + module_name = str(module_name).lower() + else: + module_name = str(module_name) + + if sub_imports and config.order_by_type: + if module_name in config.constants: + prefix = "A" + elif module_name in config.classes: + prefix = "B" + elif module_name in config.variables: + prefix = "C" + elif module_name.isupper() and len(module_name) > 1: # see issue #376 + prefix = "A" + elif module_name in config.classes or module_name[0:1].isupper(): + prefix = "B" + else: + prefix = "C" + if not config.case_sensitive: + module_name = module_name.lower() + + length_sort = ( + config.length_sort + or (config.length_sort_straight and straight_import) + or str(section_name).lower() in config.length_sort_sections + ) + _length_sort_maybe = (str(len(module_name)) + ":" + module_name) if length_sort else module_name + return f"{(module_name in config.force_to_top and 'A') or 'B'}{prefix}{_length_sort_maybe}" + + +def section_key(line: str, config: Config) -> str: + section = "B" + + if ( + not config.sort_relative_in_force_sorted_sections + and config.reverse_relative + and line.startswith("from .") + ): + match = re.match(r"^from (\.+)\s*(.*)", line) + if match: # pragma: no cover - regex always matches if line starts with "from ." + line = f"from {' '.join(match.groups())}" + if config.group_by_package and line.strip().startswith("from"): + line = line.split(" import ", 1)[0] + + if config.lexicographical: + line = _import_line_intro_re.sub("", _import_line_midline_import_re.sub(".", line)) + else: + line = re.sub("^from ", "", line) + line = re.sub("^import ", "", line) + if config.sort_relative_in_force_sorted_sections: + sep = " " if config.reverse_relative else "_" + line = re.sub(r"^(\.+)", rf"\1{sep}", line) + if line.split(" ")[0] in config.force_to_top: + section = "A" + # * If honor_case_in_force_sorted_sections is true, and case_sensitive and + # order_by_type are different, only ignore case in part of the line. + # * Otherwise, let order_by_type decide the sorting of the whole line. This + # is only "correct" if case_sensitive and order_by_type have the same value. + if config.honor_case_in_force_sorted_sections and config.case_sensitive != config.order_by_type: + split_module = line.split(" import ", 1) + if len(split_module) > 1: + module_name, names = split_module + if not config.case_sensitive: + module_name = module_name.lower() + if not config.order_by_type: + names = names.lower() + line = f"{module_name} import {names}" + elif not config.case_sensitive: + line = line.lower() + elif not config.order_by_type: + line = line.lower() + + return f"{section}{len(line) if config.length_sort else ''}{line}" + + +def sort( + config: Config, + to_sort: Iterable[str], + key: Callable[[str], Any] | None = None, + reverse: bool = False, +) -> list[str]: + return config.sorting_function(to_sort, key=key, reverse=reverse) + + +def naturally( + to_sort: Iterable[str], key: Callable[[str], Any] | None = None, reverse: bool = False +) -> list[str]: + """Returns a naturally sorted list""" + if key is None: + key_callback = _natural_keys + else: + + def key_callback(text: str) -> list[Any]: + return _natural_keys(key(text)) + + return sorted(to_sort, key=key_callback, reverse=reverse) + + +def _atoi(text: str) -> Any: + return int(text) if text.isdigit() else text + + +def _natural_keys(text: str) -> list[Any]: + return [_atoi(c) for c in re.split(r"(\d+)", text)] diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py new file mode 100644 index 0000000..3c80af8 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py @@ -0,0 +1,18 @@ +from . import all as _all +from . import py2, py3, py27, py36, py37, py38, py39, py310, py311, py312, py313, py314 + +__all__ = ( + "_all", + "py2", + "py3", + "py27", + "py36", + "py37", + "py38", + "py39", + "py310", + "py311", + "py312", + "py313", + "py314", +) diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/all.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/all.py new file mode 100644 index 0000000..08a365e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/all.py @@ -0,0 +1,3 @@ +from . import py2, py3 + +stdlib = py2.stdlib | py3.stdlib diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py2.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py2.py new file mode 100644 index 0000000..74af019 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py2.py @@ -0,0 +1,3 @@ +from . import py27 + +stdlib = py27.stdlib diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py27.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py27.py new file mode 100644 index 0000000..a9bc99d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py27.py @@ -0,0 +1,301 @@ +""" +File contains the standard library of Python 2.7. + +DO NOT EDIT. If the standard library changes, a new list should be created +using the mkstdlibs.py script. +""" + +stdlib = { + "AL", + "BaseHTTPServer", + "Bastion", + "CGIHTTPServer", + "Carbon", + "ColorPicker", + "ConfigParser", + "Cookie", + "DEVICE", + "DocXMLRPCServer", + "EasyDialogs", + "FL", + "FrameWork", + "GL", + "HTMLParser", + "MacOS", + "MimeWriter", + "MiniAEFrame", + "Nav", + "PixMapWrapper", + "Queue", + "SUNAUDIODEV", + "ScrolledText", + "SimpleHTTPServer", + "SimpleXMLRPCServer", + "SocketServer", + "StringIO", + "Tix", + "Tkinter", + "UserDict", + "UserList", + "UserString", + "W", + "__builtin__", + "_ast", + "_winreg", + "abc", + "aepack", + "aetools", + "aetypes", + "aifc", + "al", + "anydbm", + "applesingle", + "argparse", + "array", + "ast", + "asynchat", + "asyncore", + "atexit", + "audioop", + "autoGIL", + "base64", + "bdb", + "binascii", + "binhex", + "bisect", + "bsddb", + "buildtools", + "bz2", + "cPickle", + "cProfile", + "cStringIO", + "calendar", + "cd", + "cfmfile", + "cgi", + "cgitb", + "chunk", + "cmath", + "cmd", + "code", + "codecs", + "codeop", + "collections", + "colorsys", + "commands", + "compileall", + "compiler", + "contextlib", + "cookielib", + "copy", + "copy_reg", + "crypt", + "csv", + "ctypes", + "curses", + "datetime", + "dbhash", + "dbm", + "decimal", + "difflib", + "dircache", + "dis", + "distutils", + "dl", + "doctest", + "dumbdbm", + "dummy_thread", + "dummy_threading", + "email", + "encodings", + "ensurepip", + "errno", + "exceptions", + "fcntl", + "filecmp", + "fileinput", + "findertools", + "fl", + "flp", + "fm", + "fnmatch", + "formatter", + "fpectl", + "fpformat", + "fractions", + "ftplib", + "functools", + "future_builtins", + "gc", + "gdbm", + "gensuitemodule", + "getopt", + "getpass", + "gettext", + "gl", + "glob", + "grp", + "gzip", + "hashlib", + "heapq", + "hmac", + "hotshot", + "htmlentitydefs", + "htmllib", + "httplib", + "ic", + "icopen", + "imageop", + "imaplib", + "imgfile", + "imghdr", + "imp", + "importlib", + "imputil", + "inspect", + "io", + "itertools", + "jpeg", + "json", + "keyword", + "lib2to3", + "linecache", + "locale", + "logging", + "macerrors", + "macostools", + "macpath", + "macresource", + "mailbox", + "mailcap", + "marshal", + "math", + "md5", + "mhlib", + "mimetools", + "mimetypes", + "mimify", + "mmap", + "modulefinder", + "msilib", + "msvcrt", + "multifile", + "multiprocessing", + "mutex", + "netrc", + "new", + "nis", + "nntplib", + "ntpath", + "numbers", + "operator", + "optparse", + "os", + "ossaudiodev", + "parser", + "pdb", + "pickle", + "pickletools", + "pipes", + "pkgutil", + "platform", + "plistlib", + "popen2", + "poplib", + "posix", + "posixfile", + "posixpath", + "pprint", + "profile", + "pstats", + "pty", + "pwd", + "py_compile", + "pyclbr", + "pydoc", + "quopri", + "random", + "re", + "readline", + "resource", + "rexec", + "rfc822", + "rlcompleter", + "robotparser", + "runpy", + "sched", + "select", + "sets", + "sgmllib", + "sha", + "shelve", + "shlex", + "shutil", + "signal", + "site", + "smtpd", + "smtplib", + "sndhdr", + "socket", + "spwd", + "sqlite3", + "sre", + "sre_compile", + "sre_constants", + "sre_parse", + "ssl", + "stat", + "statvfs", + "string", + "stringprep", + "struct", + "subprocess", + "sunau", + "sunaudiodev", + "symbol", + "symtable", + "sys", + "sysconfig", + "syslog", + "tabnanny", + "tarfile", + "telnetlib", + "tempfile", + "termios", + "test", + "textwrap", + "thread", + "threading", + "time", + "timeit", + "token", + "tokenize", + "trace", + "traceback", + "ttk", + "tty", + "turtle", + "types", + "unicodedata", + "unittest", + "urllib", + "urllib2", + "urlparse", + "user", + "uu", + "uuid", + "videoreader", + "warnings", + "wave", + "weakref", + "webbrowser", + "whichdb", + "winsound", + "wsgiref", + "xdrlib", + "xml", + "xmlrpclib", + "zipfile", + "zipimport", + "zlib", +} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py3.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py3.py new file mode 100644 index 0000000..9d99ce0 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py3.py @@ -0,0 +1,13 @@ +from . import py36, py37, py38, py39, py310, py311, py312, py313, py314 + +stdlib = ( + py36.stdlib + | py37.stdlib + | py38.stdlib + | py39.stdlib + | py310.stdlib + | py311.stdlib + | py312.stdlib + | py313.stdlib + | py314.stdlib +) diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py310.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py310.py new file mode 100644 index 0000000..f676996 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py310.py @@ -0,0 +1,232 @@ +""" +File contains the standard library of Python 3.10. + +DO NOT EDIT. If the standard library changes, a new list should be created +using the mkstdlibs.py script. +""" + +stdlib = { + "_ast", + "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", + "email", + "encodings", + "ensurepip", + "enum", + "errno", + "faulthandler", + "fcntl", + "filecmp", + "fileinput", + "fnmatch", + "fractions", + "ftplib", + "functools", + "gc", + "genericpath", + "getopt", + "getpass", + "gettext", + "glob", + "graphlib", + "grp", + "gzip", + "hashlib", + "heapq", + "hmac", + "html", + "http", + "idlelib", + "imaplib", + "imghdr", + "imp", + "importlib", + "inspect", + "io", + "ipaddress", + "itertools", + "json", + "keyword", + "lib2to3", + "linecache", + "locale", + "logging", + "lzma", + "mailbox", + "mailcap", + "marshal", + "math", + "mimetypes", + "mmap", + "modulefinder", + "msilib", + "msvcrt", + "multiprocessing", + "netrc", + "nis", + "nntplib", + "nt", + "ntpath", + "nturl2path", + "numbers", + "opcode", + "operator", + "optparse", + "os", + "ossaudiodev", + "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", + "sre_compile", + "sre_constants", + "sre_parse", + "ssl", + "stat", + "statistics", + "string", + "stringprep", + "struct", + "subprocess", + "sunau", + "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", + "xx", + "xxlimited", + "xxlimited_35", + "xxsubtype", + "zipapp", + "zipfile", + "zipimport", + "zlib", + "zoneinfo", +} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py311.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py311.py new file mode 100644 index 0000000..9f50ec8 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py311.py @@ -0,0 +1,232 @@ +""" +File contains the standard library of Python 3.11. + +DO NOT EDIT. If the standard library changes, a new list should be created +using the mkstdlibs.py script. +""" + +stdlib = { + "_ast", + "abc", + "aifc", + "antigravity", + "argparse", + "array", + "ast", + "asynchat", + "asyncio", + "asyncore", + "atexit", + "audioop", + "base64", + "bdb", + "binascii", + "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", + "email", + "encodings", + "ensurepip", + "enum", + "errno", + "faulthandler", + "fcntl", + "filecmp", + "fileinput", + "fnmatch", + "fractions", + "ftplib", + "functools", + "gc", + "genericpath", + "getopt", + "getpass", + "gettext", + "glob", + "graphlib", + "grp", + "gzip", + "hashlib", + "heapq", + "hmac", + "html", + "http", + "idlelib", + "imaplib", + "imghdr", + "imp", + "importlib", + "inspect", + "io", + "ipaddress", + "itertools", + "json", + "keyword", + "lib2to3", + "linecache", + "locale", + "logging", + "lzma", + "mailbox", + "mailcap", + "marshal", + "math", + "mimetypes", + "mmap", + "modulefinder", + "msilib", + "msvcrt", + "multiprocessing", + "netrc", + "nis", + "nntplib", + "nt", + "ntpath", + "nturl2path", + "numbers", + "opcode", + "operator", + "optparse", + "os", + "ossaudiodev", + "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", + "sre_compile", + "sre_constants", + "sre_parse", + "ssl", + "stat", + "statistics", + "string", + "stringprep", + "struct", + "subprocess", + "sunau", + "symtable", + "sys", + "sysconfig", + "syslog", + "tabnanny", + "tarfile", + "telnetlib", + "tempfile", + "termios", + "textwrap", + "this", + "threading", + "time", + "timeit", + "tkinter", + "token", + "tokenize", + "tomllib", + "trace", + "traceback", + "tracemalloc", + "tty", + "turtle", + "turtledemo", + "types", + "typing", + "unicodedata", + "unittest", + "urllib", + "uu", + "uuid", + "venv", + "warnings", + "wave", + "weakref", + "webbrowser", + "winreg", + "winsound", + "wsgiref", + "xdrlib", + "xml", + "xmlrpc", + "xx", + "xxlimited", + "xxlimited_35", + "xxsubtype", + "zipapp", + "zipfile", + "zipimport", + "zlib", + "zoneinfo", +} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py312.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py312.py new file mode 100644 index 0000000..7feac7f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py312.py @@ -0,0 +1,227 @@ +""" +File contains the standard library of Python 3.12. + +DO NOT EDIT. If the standard library changes, a new list should be created +using the mkstdlibs.py script. +""" + +stdlib = { + "_ast", + "abc", + "aifc", + "antigravity", + "argparse", + "array", + "ast", + "asyncio", + "atexit", + "audioop", + "base64", + "bdb", + "binascii", + "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", + "doctest", + "email", + "encodings", + "ensurepip", + "enum", + "errno", + "faulthandler", + "fcntl", + "filecmp", + "fileinput", + "fnmatch", + "fractions", + "ftplib", + "functools", + "gc", + "genericpath", + "getopt", + "getpass", + "gettext", + "glob", + "graphlib", + "grp", + "gzip", + "hashlib", + "heapq", + "hmac", + "html", + "http", + "idlelib", + "imaplib", + "imghdr", + "importlib", + "inspect", + "io", + "ipaddress", + "itertools", + "json", + "keyword", + "lib2to3", + "linecache", + "locale", + "logging", + "lzma", + "mailbox", + "mailcap", + "marshal", + "math", + "mimetypes", + "mmap", + "modulefinder", + "msilib", + "msvcrt", + "multiprocessing", + "netrc", + "nis", + "nntplib", + "nt", + "ntpath", + "nturl2path", + "numbers", + "opcode", + "operator", + "optparse", + "os", + "ossaudiodev", + "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", + "smtplib", + "sndhdr", + "socket", + "socketserver", + "spwd", + "sqlite3", + "sre", + "sre_compile", + "sre_constants", + "sre_parse", + "ssl", + "stat", + "statistics", + "string", + "stringprep", + "struct", + "subprocess", + "sunau", + "symtable", + "sys", + "sysconfig", + "syslog", + "tabnanny", + "tarfile", + "telnetlib", + "tempfile", + "termios", + "textwrap", + "this", + "threading", + "time", + "timeit", + "tkinter", + "token", + "tokenize", + "tomllib", + "trace", + "traceback", + "tracemalloc", + "tty", + "turtle", + "turtledemo", + "types", + "typing", + "unicodedata", + "unittest", + "urllib", + "uu", + "uuid", + "venv", + "warnings", + "wave", + "weakref", + "webbrowser", + "winreg", + "winsound", + "wsgiref", + "xdrlib", + "xml", + "xmlrpc", + "xx", + "xxlimited", + "xxlimited_35", + "xxsubtype", + "zipapp", + "zipfile", + "zipimport", + "zlib", + "zoneinfo", +} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py313.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py313.py new file mode 100644 index 0000000..9015a18 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py313.py @@ -0,0 +1,207 @@ +""" +File contains the standard library of Python 3.13. + +DO NOT EDIT. If the standard library changes, a new list should be created +using the mkstdlibs.py script. +""" + +stdlib = { + "_ast", + "abc", + "antigravity", + "argparse", + "array", + "ast", + "asyncio", + "atexit", + "base64", + "bdb", + "binascii", + "bisect", + "builtins", + "bz2", + "cProfile", + "calendar", + "cmath", + "cmd", + "code", + "codecs", + "codeop", + "collections", + "colorsys", + "compileall", + "concurrent", + "configparser", + "contextlib", + "contextvars", + "copy", + "copyreg", + "csv", + "ctypes", + "curses", + "dataclasses", + "datetime", + "dbm", + "decimal", + "difflib", + "dis", + "doctest", + "email", + "encodings", + "ensurepip", + "enum", + "errno", + "faulthandler", + "fcntl", + "filecmp", + "fileinput", + "fnmatch", + "fractions", + "ftplib", + "functools", + "gc", + "genericpath", + "getopt", + "getpass", + "gettext", + "glob", + "graphlib", + "grp", + "gzip", + "hashlib", + "heapq", + "hmac", + "html", + "http", + "idlelib", + "imaplib", + "importlib", + "inspect", + "io", + "ipaddress", + "itertools", + "json", + "keyword", + "linecache", + "locale", + "logging", + "lzma", + "mailbox", + "marshal", + "math", + "mimetypes", + "mmap", + "modulefinder", + "msvcrt", + "multiprocessing", + "netrc", + "nt", + "ntpath", + "nturl2path", + "numbers", + "opcode", + "operator", + "optparse", + "os", + "pathlib", + "pdb", + "pickle", + "pickletools", + "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", + "smtplib", + "socket", + "socketserver", + "sqlite3", + "sre", + "sre_compile", + "sre_constants", + "sre_parse", + "ssl", + "stat", + "statistics", + "string", + "stringprep", + "struct", + "subprocess", + "symtable", + "sys", + "sysconfig", + "syslog", + "tabnanny", + "tarfile", + "tempfile", + "termios", + "textwrap", + "this", + "threading", + "time", + "timeit", + "tkinter", + "token", + "tokenize", + "tomllib", + "trace", + "traceback", + "tracemalloc", + "tty", + "turtle", + "turtledemo", + "types", + "typing", + "unicodedata", + "unittest", + "urllib", + "uuid", + "venv", + "warnings", + "wave", + "weakref", + "webbrowser", + "winreg", + "winsound", + "wsgiref", + "xml", + "xmlrpc", + "xx", + "xxlimited", + "xxlimited_35", + "xxsubtype", + "zipapp", + "zipfile", + "zipimport", + "zlib", + "zoneinfo", +} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py314.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py314.py new file mode 100644 index 0000000..66b9bd4 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py314.py @@ -0,0 +1,208 @@ +""" +File contains the standard library of Python 3.14. + +DO NOT EDIT. If the standard library changes, a new list should be created +using the mkstdlibs.py script. +""" + +stdlib = { + "_ast", + "abc", + "annotationlib", + "antigravity", + "argparse", + "array", + "ast", + "asyncio", + "atexit", + "base64", + "bdb", + "binascii", + "bisect", + "builtins", + "bz2", + "cProfile", + "calendar", + "cmath", + "cmd", + "code", + "codecs", + "codeop", + "collections", + "colorsys", + "compileall", + "concurrent", + "configparser", + "contextlib", + "contextvars", + "copy", + "copyreg", + "csv", + "ctypes", + "curses", + "dataclasses", + "datetime", + "dbm", + "decimal", + "difflib", + "dis", + "doctest", + "email", + "encodings", + "ensurepip", + "enum", + "errno", + "faulthandler", + "fcntl", + "filecmp", + "fileinput", + "fnmatch", + "fractions", + "ftplib", + "functools", + "gc", + "genericpath", + "getopt", + "getpass", + "gettext", + "glob", + "graphlib", + "grp", + "gzip", + "hashlib", + "heapq", + "hmac", + "html", + "http", + "idlelib", + "imaplib", + "importlib", + "inspect", + "io", + "ipaddress", + "itertools", + "json", + "keyword", + "linecache", + "locale", + "logging", + "lzma", + "mailbox", + "marshal", + "math", + "mimetypes", + "mmap", + "modulefinder", + "msvcrt", + "multiprocessing", + "netrc", + "nt", + "ntpath", + "nturl2path", + "numbers", + "opcode", + "operator", + "optparse", + "os", + "pathlib", + "pdb", + "pickle", + "pickletools", + "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", + "smtplib", + "socket", + "socketserver", + "sqlite3", + "sre", + "sre_compile", + "sre_constants", + "sre_parse", + "ssl", + "stat", + "statistics", + "string", + "stringprep", + "struct", + "subprocess", + "symtable", + "sys", + "sysconfig", + "syslog", + "tabnanny", + "tarfile", + "tempfile", + "termios", + "textwrap", + "this", + "threading", + "time", + "timeit", + "tkinter", + "token", + "tokenize", + "tomllib", + "trace", + "traceback", + "tracemalloc", + "tty", + "turtle", + "turtledemo", + "types", + "typing", + "unicodedata", + "unittest", + "urllib", + "uuid", + "venv", + "warnings", + "wave", + "weakref", + "webbrowser", + "winreg", + "winsound", + "wsgiref", + "xml", + "xmlrpc", + "xx", + "xxlimited", + "xxlimited_35", + "xxsubtype", + "zipapp", + "zipfile", + "zipimport", + "zlib", + "zoneinfo", +} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py36.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py36.py new file mode 100644 index 0000000..59ebd24 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py36.py @@ -0,0 +1,224 @@ +""" +File contains the standard library of Python 3.6. + +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", + "copy", + "copyreg", + "crypt", + "csv", + "ctypes", + "curses", + "datetime", + "dbm", + "decimal", + "difflib", + "dis", + "distutils", + "doctest", + "dummy_threading", + "email", + "encodings", + "ensurepip", + "enum", + "errno", + "faulthandler", + "fcntl", + "filecmp", + "fileinput", + "fnmatch", + "formatter", + "fpectl", + "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", +} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py37.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py37.py new file mode 100644 index 0000000..e0ad122 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py37.py @@ -0,0 +1,225 @@ +""" +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", +} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py38.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py38.py new file mode 100644 index 0000000..bf2cdf2 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py38.py @@ -0,0 +1,233 @@ +""" +File contains the standard library of Python 3.8. + +DO NOT EDIT. If the standard library changes, a new list should be created +using the mkstdlibs.py script. +""" + +stdlib = { + "_ast", + "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", + "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", + "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", + "xx", + "xxlimited", + "xxsubtype", + "zipapp", + "zipfile", + "zipimport", + "zlib", +} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py39.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py39.py new file mode 100644 index 0000000..7615952 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/stdlibs/py39.py @@ -0,0 +1,234 @@ +""" +File contains the standard library of Python 3.9. + +DO NOT EDIT. If the standard library changes, a new list should be created +using the mkstdlibs.py script. +""" + +stdlib = { + "_ast", + "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", + "email", + "encodings", + "ensurepip", + "enum", + "errno", + "faulthandler", + "fcntl", + "filecmp", + "fileinput", + "fnmatch", + "formatter", + "fractions", + "ftplib", + "functools", + "gc", + "genericpath", + "getopt", + "getpass", + "gettext", + "glob", + "graphlib", + "grp", + "gzip", + "hashlib", + "heapq", + "hmac", + "html", + "http", + "idlelib", + "imaplib", + "imghdr", + "imp", + "importlib", + "inspect", + "io", + "ipaddress", + "itertools", + "json", + "keyword", + "lib2to3", + "linecache", + "locale", + "logging", + "lzma", + "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", + "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", + "xx", + "xxlimited", + "xxsubtype", + "zipapp", + "zipfile", + "zipimport", + "zlib", + "zoneinfo", +} diff --git a/.venv/lib/python3.10/site-packages/isort/utils.py b/.venv/lib/python3.10/site-packages/isort/utils.py new file mode 100644 index 0000000..2c4016d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/utils.py @@ -0,0 +1,74 @@ +import os +import sys +from functools import lru_cache +from pathlib import Path +from typing import Any + + +class TrieNode: + def __init__(self, config_file: str = "", config_data: dict[str, Any] | None = None) -> None: + if not config_data: + config_data = {} + + self.nodes: dict[str, TrieNode] = {} + self.config_info: tuple[str, dict[str, Any]] = (config_file, config_data) + + +class Trie: + """ + A prefix tree to store the paths of all config files and to search the nearest config + associated with each file + """ + + def __init__(self, config_file: str = "", config_data: dict[str, Any] | None = None) -> None: + self.root: TrieNode = TrieNode(config_file, config_data) + + def insert(self, config_file: str, config_data: dict[str, Any]) -> None: + resolved_config_path_as_tuple = Path(config_file).parent.resolve().parts + + temp = self.root + + for path in resolved_config_path_as_tuple: + if path not in temp.nodes: + temp.nodes[path] = TrieNode() + + temp = temp.nodes[path] + + temp.config_info = (config_file, config_data) + + def search(self, filename: str) -> tuple[str, dict[str, Any]]: + """ + Returns the closest config relative to filename by doing a depth + first search on the prefix tree. + """ + resolved_file_path_as_tuple = Path(filename).resolve().parts + + temp = self.root + + last_stored_config: tuple[str, dict[str, Any]] = ("", {}) + + for path in resolved_file_path_as_tuple: + if temp.config_info[0]: + last_stored_config = temp.config_info + + if path not in temp.nodes: + break + + temp = temp.nodes[path] + + return last_stored_config + + +@lru_cache(maxsize=1000) +def exists_case_sensitive(path: str) -> bool: + """Returns if the given path exists and also matches the case on Windows. + + When finding files that can be imported, it is important for the cases to match because while + file os.path.exists("module.py") and os.path.exists("MODULE.py") both return True on Windows, + Python can only import using the case of the real file. + """ + result = os.path.exists(path) + if result and (sys.platform.startswith("win") or sys.platform == "darwin"): # pragma: no cover + directory, basename = os.path.split(path) + result = basename in os.listdir(directory) + return result diff --git a/.venv/lib/python3.10/site-packages/isort/wrap.py b/.venv/lib/python3.10/site-packages/isort/wrap.py new file mode 100644 index 0000000..5f0813d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/wrap.py @@ -0,0 +1,147 @@ +import copy +import re +from collections.abc import Sequence + +from .settings import DEFAULT_CONFIG, Config +from .wrap_modes import WrapModes as Modes +from .wrap_modes import formatter_from_string, vertical_hanging_indent + + +def import_statement( + import_start: str, + from_imports: list[str], + comments: Sequence[str] = (), + line_separator: str = "\n", + config: Config = DEFAULT_CONFIG, + multi_line_output: Modes | None = None, + explode: bool = False, +) -> str: + """Returns a multi-line wrapped form of the provided from import statement.""" + if explode: + formatter = vertical_hanging_indent + line_length = 1 + include_trailing_comma = True + else: + formatter = formatter_from_string((multi_line_output or config.multi_line_output).name) + line_length = config.wrap_length or config.line_length + include_trailing_comma = config.include_trailing_comma + dynamic_indent = " " * (len(import_start) + 1) + indent = config.indent + statement = formatter( + statement=import_start, + imports=copy.copy(from_imports), + white_space=dynamic_indent, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=config.comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=config.ignore_comments, + ) + if config.balanced_wrapping: + lines = statement.split(line_separator) + line_count = len(lines) + if len(lines) > 1: + minimum_length = min(len(line) for line in lines[:-1]) + else: + minimum_length = 0 + new_import_statement = statement + while len(lines[-1]) < minimum_length and len(lines) == line_count and line_length > 10: + statement = new_import_statement + line_length -= 1 + new_import_statement = formatter( + statement=import_start, + imports=copy.copy(from_imports), + white_space=dynamic_indent, + indent=indent, + line_length=line_length, + comments=comments, + line_separator=line_separator, + comment_prefix=config.comment_prefix, + include_trailing_comma=include_trailing_comma, + remove_comments=config.ignore_comments, + ) + lines = new_import_statement.split(line_separator) + if statement.count(line_separator) == 0: + return _wrap_line(statement, line_separator, config) + return statement + + +def line(content: str, line_separator: str, config: Config = DEFAULT_CONFIG) -> str: + """Returns a line wrapped to the specified line-length, if possible.""" + wrap_mode = config.multi_line_output + if len(content) > config.line_length and wrap_mode != Modes.NOQA: # type: ignore + line_without_comment = content + comment = None + if "#" in content: + line_without_comment, comment = content.split("#", 1) + for splitter in ("import ", "cimport ", ".", "as "): + exp = r"\b" + re.escape(splitter) + r"\b" + if re.search(exp, line_without_comment) and not line_without_comment.strip().startswith( + splitter + ): + line_parts = re.split(exp, line_without_comment) + if comment and not (config.use_parentheses and "noqa" in comment): + _comma_maybe = ( + "," + if ( + config.include_trailing_comma + and config.use_parentheses + and not line_without_comment.rstrip().endswith(",") + ) + else "" + ) + line_parts[-1] = ( + f"{line_parts[-1].strip()}{_comma_maybe}{config.comment_prefix}{comment}" + ) + next_line = [] + while (len(content) + 2) > ( + config.wrap_length or config.line_length + ) and line_parts: + next_line.append(line_parts.pop()) + content = splitter.join(line_parts) + if not content: + content = next_line.pop() + + cont_line = _wrap_line( + config.indent + splitter.join(next_line).lstrip(), + line_separator, + config, + ) + if config.use_parentheses: + if splitter == "as ": + output = f"{content}{splitter}{cont_line.lstrip()}" + else: + _comma = "," if config.include_trailing_comma and not comment else "" + + if wrap_mode in ( + Modes.VERTICAL_HANGING_INDENT, # type: ignore + Modes.VERTICAL_GRID_GROUPED, # type: ignore + ): + _separator = line_separator + else: + _separator = "" + noqa_comment = "" + if comment and "noqa" in comment: + noqa_comment = f"{config.comment_prefix}{comment}" + cont_line = cont_line.rstrip() + _comma = "," if config.include_trailing_comma else "" + output = ( + f"{content}{splitter}({noqa_comment}" + f"{line_separator}{cont_line}{_comma}{_separator})" + ) + lines = output.split(line_separator) + if config.comment_prefix in lines[-1] and lines[-1].endswith(")"): + content, comment = lines[-1].split(config.comment_prefix, 1) + lines[-1] = content + ")" + config.comment_prefix + comment[:-1] + output = line_separator.join(lines) + return output + return f"{content}{splitter}\\{line_separator}{cont_line}" + elif len(content) > config.line_length and wrap_mode == Modes.NOQA and "# NOQA" not in content: # type: ignore + return f"{content}{config.comment_prefix} NOQA" + + return content + + +_wrap_line = line diff --git a/.venv/lib/python3.10/site-packages/isort/wrap_modes.py b/.venv/lib/python3.10/site-packages/isort/wrap_modes.py new file mode 100644 index 0000000..cc4a97f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/isort/wrap_modes.py @@ -0,0 +1,375 @@ +"""Defines all wrap modes that can be used when outputting formatted imports""" + +import enum +from collections.abc import Callable +from inspect import signature +from typing import Any + +import isort.comments + +_wrap_modes: dict[str, Callable[..., str]] = {} + + +def from_string(value: str) -> "WrapModes": + return getattr(WrapModes, str(value), None) or WrapModes(int(value)) + + +def formatter_from_string(name: str) -> Callable[..., str]: + return _wrap_modes.get(name.upper(), grid) + + +def _wrap_mode_interface( + statement: str, + imports: list[str], + white_space: str, + indent: str, + line_length: int, + comments: list[str], + line_separator: str, + comment_prefix: str, + include_trailing_comma: bool, + remove_comments: bool, +) -> str: + """Defines the common interface used by all wrap mode functions""" + return "" + + +def _wrap_mode(function: Callable[..., str]) -> Callable[..., str]: + """Registers an individual wrap mode. Function name and order are significant and used for + creating enum. + """ + _wrap_modes[function.__name__.upper()] = function + function.__signature__ = signature(_wrap_mode_interface) # type: ignore + function.__annotations__ = _wrap_mode_interface.__annotations__ + return function + + +@_wrap_mode +def grid(**interface: Any) -> str: + if not interface["imports"]: + return "" + + interface["statement"] += "(" + interface["imports"].pop(0) + while interface["imports"]: + next_import = interface["imports"].pop(0) + next_statement = isort.comments.add_to_line( + interface["comments"], + interface["statement"] + ", " + next_import, + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + if ( + len(next_statement.split(interface["line_separator"])[-1]) + 1 + > interface["line_length"] + ): + lines = [f"{interface['white_space']}{next_import.split(' ')[0]}"] + for part in next_import.split(" ")[1:]: + new_line = f"{lines[-1]} {part}" + if len(new_line) + 1 > interface["line_length"]: + lines.append(f"{interface['white_space']}{part}") + else: + lines[-1] = new_line + next_import = interface["line_separator"].join(lines) + interface["statement"] = ( + isort.comments.add_to_line( + interface["comments"], + f"{interface['statement']},", + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + + f"{interface['line_separator']}{next_import}" + ) + interface["comments"] = [] + else: + interface["statement"] += ", " + next_import + return f"{interface['statement']}{',' if interface['include_trailing_comma'] else ''})" + + +@_wrap_mode +def vertical(**interface: Any) -> str: + if not interface["imports"]: + return "" + + first_import = ( + isort.comments.add_to_line( + interface["comments"], + interface["imports"].pop(0) + ",", + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + + interface["line_separator"] + + interface["white_space"] + ) + + _imports = ("," + interface["line_separator"] + interface["white_space"]).join( + interface["imports"] + ) + _comma_maybe = "," if interface["include_trailing_comma"] else "" + return f"{interface['statement']}({first_import}{_imports}{_comma_maybe})" + + +def _hanging_indent_end_line(line: str) -> str: + if not line.endswith(" "): + line += " " + return line + "\\" + + +@_wrap_mode +def hanging_indent(**interface: Any) -> str: + if not interface["imports"]: + return "" + + line_length_limit = interface["line_length"] - 3 + + next_import = interface["imports"].pop(0) + next_statement = interface["statement"] + next_import + # Check for first import + if len(next_statement) > line_length_limit: + next_statement = ( + _hanging_indent_end_line(interface["statement"]) + + interface["line_separator"] + + interface["indent"] + + next_import + ) + + interface["statement"] = next_statement + while interface["imports"]: + next_import = interface["imports"].pop(0) + next_statement = interface["statement"] + ", " + next_import + if len(next_statement.split(interface["line_separator"])[-1]) > line_length_limit: + next_statement = ( + _hanging_indent_end_line(interface["statement"] + ",") + + f"{interface['line_separator']}{interface['indent']}{next_import}" + ) + interface["statement"] = next_statement + + if interface["comments"]: + statement_with_comments = isort.comments.add_to_line( + interface["comments"], + interface["statement"], + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + if len(statement_with_comments.split(interface["line_separator"])[-1]) <= ( + line_length_limit + 2 + ): + return statement_with_comments + return ( + _hanging_indent_end_line(interface["statement"]) + + str(interface["line_separator"]) + + isort.comments.add_to_line( + interface["comments"], + interface["indent"], + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"].lstrip(), + ) + ) + return str(interface["statement"]) + + +@_wrap_mode +def vertical_hanging_indent(**interface: Any) -> str: + _line_with_comments = isort.comments.add_to_line( + interface["comments"], + "", + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + _imports = ("," + interface["line_separator"] + interface["indent"]).join(interface["imports"]) + _comma_maybe = "," if interface["include_trailing_comma"] else "" + return ( + f"{interface['statement']}({_line_with_comments}{interface['line_separator']}" + f"{interface['indent']}{_imports}{_comma_maybe}{interface['line_separator']})" + ) + + +def _vertical_grid_common(need_trailing_char: bool, **interface: Any) -> str: + if not interface["imports"]: + return "" + + interface["statement"] += ( + isort.comments.add_to_line( + interface["comments"], + "(", + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + + interface["line_separator"] + + interface["indent"] + + interface["imports"].pop(0) + ) + while interface["imports"]: + next_import = interface["imports"].pop(0) + next_statement = f"{interface['statement']}, {next_import}" + current_line_length = len(next_statement.split(interface["line_separator"])[-1]) + if interface["imports"] or interface["include_trailing_comma"]: + # We need to account for a comma after this import. + current_line_length += 1 + if not interface["imports"] and need_trailing_char: + # We need to account for a closing ) we're going to add. + current_line_length += 1 + if current_line_length > interface["line_length"]: + next_statement = ( + f"{interface['statement']},{interface['line_separator']}" + f"{interface['indent']}{next_import}" + ) + interface["statement"] = next_statement + if interface["include_trailing_comma"]: + interface["statement"] += "," + return str(interface["statement"]) + + +@_wrap_mode +def vertical_grid(**interface: Any) -> str: + return _vertical_grid_common(need_trailing_char=True, **interface) + ")" + + +@_wrap_mode +def vertical_grid_grouped(**interface: Any) -> str: + return ( + _vertical_grid_common(need_trailing_char=False, **interface) + + str(interface["line_separator"]) + + ")" + ) + + +@_wrap_mode +def vertical_grid_grouped_no_comma(**interface: Any) -> str: + # This is a deprecated alias for vertical_grid_grouped above. This function + # needs to exist for backwards compatibility but should never get called. + raise NotImplementedError + + +@_wrap_mode +def noqa(**interface: Any) -> str: + _imports = ", ".join(interface["imports"]) + retval = f"{interface['statement']}{_imports}" + comment_str = " ".join(interface["comments"]) + if interface["comments"]: + if ( + len(retval) + len(interface["comment_prefix"]) + 1 + len(comment_str) + <= interface["line_length"] + ): + return f"{retval}{interface['comment_prefix']} {comment_str}" + if "NOQA" in interface["comments"]: + return f"{retval}{interface['comment_prefix']} {comment_str}" + return f"{retval}{interface['comment_prefix']} NOQA {comment_str}" + + if len(retval) <= interface["line_length"]: + return retval + return f"{retval}{interface['comment_prefix']} NOQA" + + +@_wrap_mode +def vertical_hanging_indent_bracket(**interface: Any) -> str: + if not interface["imports"]: + return "" + statement = vertical_hanging_indent(**interface) + return f"{statement[:-1]}{interface['indent']})" + + +@_wrap_mode +def vertical_prefix_from_module_import(**interface: Any) -> str: + if not interface["imports"]: + return "" + + prefix_statement = interface["statement"] + output_statement = prefix_statement + interface["imports"].pop(0) + comments = interface["comments"] + + statement = output_statement + statement_with_comments = "" + for next_import in interface["imports"]: + statement = statement + ", " + next_import + statement_with_comments = isort.comments.add_to_line( + comments, + statement, + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + if ( + len(statement_with_comments.split(interface["line_separator"])[-1]) + 1 + > interface["line_length"] + ): + statement = ( + isort.comments.add_to_line( + comments, + output_statement, + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + + f"{interface['line_separator']}{prefix_statement}{next_import}" + ) + comments = [] + output_statement = statement + + if comments and statement_with_comments: + output_statement = statement_with_comments + return str(output_statement) + + +@_wrap_mode +def hanging_indent_with_parentheses(**interface: Any) -> str: + if not interface["imports"]: + return "" + + line_length_limit = interface["line_length"] - 1 + + interface["statement"] += "(" + next_import = interface["imports"].pop(0) + next_statement = interface["statement"] + next_import + # Check for first import + if len(next_statement) > line_length_limit: + next_statement = ( + isort.comments.add_to_line( + interface["comments"], + interface["statement"], + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + + f"{interface['line_separator']}{interface['indent']}{next_import}" + ) + interface["comments"] = [] + interface["statement"] = next_statement + while interface["imports"]: + next_import = interface["imports"].pop(0) + if ( + interface["line_separator"] not in interface["statement"] + and "#" in interface["statement"] + ): # pragma: no cover # TODO: fix, this is because of test run inconsistency. + line, comments = interface["statement"].split("#", 1) + next_statement = ( + f"{line.rstrip()}, {next_import}{interface['comment_prefix']}{comments}" + ) + else: + next_statement = isort.comments.add_to_line( + interface["comments"], + interface["statement"] + ", " + next_import, + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + current_line = next_statement.split(interface["line_separator"])[-1] + if len(current_line) > line_length_limit: + next_statement = ( + isort.comments.add_to_line( + interface["comments"], + interface["statement"] + ",", + removed=interface["remove_comments"], + comment_prefix=interface["comment_prefix"], + ) + + f"{interface['line_separator']}{interface['indent']}{next_import}" + ) + interface["comments"] = [] + interface["statement"] = next_statement + return f"{interface['statement']}{',' if interface['include_trailing_comma'] else ''})" + + +@_wrap_mode +def backslash_grid(**interface: Any) -> str: + interface["indent"] = interface["white_space"][:-1] + return hanging_indent(**interface) + + +WrapModes = enum.Enum( # type: ignore + "WrapModes", {wrap_mode: index for index, wrap_mode in enumerate(_wrap_modes.keys())} +) diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE new file mode 100644 index 0000000..8fd356e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE @@ -0,0 +1,25 @@ +Copyright © Ned Batchelder +Copyright © 2011-2013 Tarek Ziade +Copyright © 2013 Florent Xicluna + +Licensed under the terms of the Expat License + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA new file mode 100644 index 0000000..e25facd --- /dev/null +++ b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA @@ -0,0 +1,199 @@ +Metadata-Version: 2.1 +Name: mccabe +Version: 0.7.0 +Summary: McCabe checker, plugin for flake8 +Home-page: https://github.com/pycqa/mccabe +Author: Tarek Ziade +Author-email: tarek@ziade.org +Maintainer: Ian Stapleton Cordasco +Maintainer-email: graffatcolmingov@gmail.com +License: Expat license +Keywords: flake8 mccabe +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Quality Assurance +Requires-Python: >=3.6 +License-File: LICENSE + +McCabe complexity checker +========================= + +Ned's script to check McCabe complexity. + +This module provides a plugin for ``flake8``, the Python code checker. + + +Installation +------------ + +You can install, upgrade, or uninstall ``mccabe`` with these commands:: + + $ pip install mccabe + $ pip install --upgrade mccabe + $ pip uninstall mccabe + + +Standalone script +----------------- + +The complexity checker can be used directly:: + + $ python -m mccabe --min 5 mccabe.py + ("185:1: 'PathGraphingAstVisitor.visitIf'", 5) + ("71:1: 'PathGraph.to_dot'", 5) + ("245:1: 'McCabeChecker.run'", 5) + ("283:1: 'main'", 7) + ("203:1: 'PathGraphingAstVisitor.visitTryExcept'", 5) + ("257:1: 'get_code_complexity'", 5) + + +Plugin for Flake8 +----------------- + +When both ``flake8 2+`` and ``mccabe`` are installed, the plugin is +available in ``flake8``:: + + $ flake8 --version + 2.0 (pep8: 1.4.2, pyflakes: 0.6.1, mccabe: 0.2) + +By default the plugin is disabled. Use the ``--max-complexity`` switch to +enable it. It will emit a warning if the McCabe complexity of a function is +higher than the provided value:: + + $ flake8 --max-complexity 10 coolproject + ... + coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14) + +This feature is quite useful for detecting over-complex code. According to McCabe, +anything that goes beyond 10 is too complex. + +Flake8 has many features that mccabe does not provide. Flake8 allows users to +ignore violations reported by plugins with ``# noqa``. Read more about this in +`their documentation +`__. +To silence violations reported by ``mccabe``, place your ``# noqa: C901`` on +the function definition line, where the error is reported for (possibly a +decorator). + + +Links +----- + +* Feedback and ideas: http://mail.python.org/mailman/listinfo/code-quality + +* Cyclomatic complexity: http://en.wikipedia.org/wiki/Cyclomatic_complexity + +* Ned Batchelder's script: + http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html + +* McCabe complexity: http://en.wikipedia.org/wiki/Cyclomatic_complexity + + +Changes +------- + +0.7.0 - 2021-01-23 +`````````````````` + +* Drop support for all versions of Python lower than 3.6 + +* Add support for Python 3.8, 3.9, and 3.10 + +* Fix option declaration for Flake8 + +0.6.1 - 2017-01-26 +`````````````````` + +* Fix signature for ``PathGraphingAstVisitor.default`` to match the signature + for ``ASTVisitor`` + +0.6.0 - 2017-01-23 +`````````````````` + +* Add support for Python 3.6 + +* Fix handling for missing statement types + +0.5.3 - 2016-12-14 +`````````````````` + +* Report actual column number of violation instead of the start of the line + +0.5.2 - 2016-07-31 +`````````````````` + +* When opening files ourselves, make sure we always name the file variable + +0.5.1 - 2016-07-28 +`````````````````` + +* Set default maximum complexity to -1 on the class itself + +0.5.0 - 2016-05-30 +`````````````````` + +* PyCon 2016 PDX release + +* Add support for Flake8 3.0 + +0.4.0 - 2016-01-27 +`````````````````` + +* Stop testing on Python 3.2 + +* Add support for async/await keywords on Python 3.5 from PEP 0492 + +0.3.1 - 2015-06-14 +`````````````````` + +* Include ``test_mccabe.py`` in releases. + +* Always coerce the ``max_complexity`` value from Flake8's entry-point to an + integer. + +0.3 - 2014-12-17 +```````````````` + +* Computation was wrong: the mccabe complexity starts at 1, not 2. + +* The ``max-complexity`` value is now inclusive. E.g.: if the + value is 10 and the reported complexity is 10, then it passes. + +* Add tests. + + +0.2.1 - 2013-04-03 +`````````````````` + +* Do not require ``setuptools`` in setup.py. It works around an issue + with ``pip`` and Python 3. + + +0.2 - 2013-02-22 +```````````````` + +* Rename project to ``mccabe``. + +* Provide ``flake8.extension`` setuptools entry point. + +* Read ``max-complexity`` from the configuration file. + +* Rename argument ``min_complexity`` to ``threshold``. + + +0.1 - 2013-02-11 +```````````````` +* First release + + diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD new file mode 100644 index 0000000..69c4dbd --- /dev/null +++ b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD @@ -0,0 +1,9 @@ +__pycache__/mccabe.cpython-310.pyc,, +mccabe-0.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +mccabe-0.7.0.dist-info/LICENSE,sha256=EPvAA8uvims89xlbgNrJbIba85ADmyq_bntuc1r9fXQ,1221 +mccabe-0.7.0.dist-info/METADATA,sha256=oMxU_cw4ev2Q23YTL3NRg4pebHSqlrbF_DSSs-cpfBE,5035 +mccabe-0.7.0.dist-info/RECORD,, +mccabe-0.7.0.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110 +mccabe-0.7.0.dist-info/entry_points.txt,sha256=N2NH182GXTUyTm8r8XMgadb9C-CRa5dUr1k8OC91uGE,47 +mccabe-0.7.0.dist-info/top_level.txt,sha256=21cXuqZE-lpcfAqqANvX9EjI1ED1p8zcViv064u3RKA,7 +mccabe.py,sha256=g_kB8oPilNLemdOirPaZymQyyjqAH0kowrncUQaaw00,10654 diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL new file mode 100644 index 0000000..0b18a28 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt new file mode 100644 index 0000000..cc6645b --- /dev/null +++ b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[flake8.extension] +C90 = mccabe:McCabeChecker + diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt new file mode 100644 index 0000000..8831b36 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt @@ -0,0 +1 @@ +mccabe diff --git a/.venv/lib/python3.10/site-packages/mccabe.py b/.venv/lib/python3.10/site-packages/mccabe.py new file mode 100644 index 0000000..5746504 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/mccabe.py @@ -0,0 +1,346 @@ +""" Meager code path measurement tool. + Ned Batchelder + http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html + MIT License. +""" +from __future__ import with_statement + +import optparse +import sys +import tokenize + +from collections import defaultdict +try: + import ast + from ast import iter_child_nodes +except ImportError: # Python 2.5 + from flake8.util import ast, iter_child_nodes + +__version__ = '0.7.0' + + +class ASTVisitor(object): + """Performs a depth-first walk of the AST.""" + + def __init__(self): + self.node = None + self._cache = {} + + def default(self, node, *args): + for child in iter_child_nodes(node): + self.dispatch(child, *args) + + def dispatch(self, node, *args): + self.node = node + klass = node.__class__ + meth = self._cache.get(klass) + if meth is None: + className = klass.__name__ + meth = getattr(self.visitor, 'visit' + className, self.default) + self._cache[klass] = meth + return meth(node, *args) + + def preorder(self, tree, visitor, *args): + """Do preorder walk of tree using visitor""" + self.visitor = visitor + visitor.visit = self.dispatch + self.dispatch(tree, *args) # XXX *args make sense? + + +class PathNode(object): + def __init__(self, name, look="circle"): + self.name = name + self.look = look + + def to_dot(self): + print('node [shape=%s,label="%s"] %d;' % ( + self.look, self.name, self.dot_id())) + + def dot_id(self): + return id(self) + + +class PathGraph(object): + def __init__(self, name, entity, lineno, column=0): + self.name = name + self.entity = entity + self.lineno = lineno + self.column = column + self.nodes = defaultdict(list) + + def connect(self, n1, n2): + self.nodes[n1].append(n2) + # Ensure that the destination node is always counted. + self.nodes[n2] = [] + + def to_dot(self): + print('subgraph {') + for node in self.nodes: + node.to_dot() + for node, nexts in self.nodes.items(): + for next in nexts: + print('%s -- %s;' % (node.dot_id(), next.dot_id())) + print('}') + + def complexity(self): + """ Return the McCabe complexity for the graph. + V-E+2 + """ + num_edges = sum([len(n) for n in self.nodes.values()]) + num_nodes = len(self.nodes) + return num_edges - num_nodes + 2 + + +class PathGraphingAstVisitor(ASTVisitor): + """ A visitor for a parsed Abstract Syntax Tree which finds executable + statements. + """ + + def __init__(self): + super(PathGraphingAstVisitor, self).__init__() + self.classname = "" + self.graphs = {} + self.reset() + + def reset(self): + self.graph = None + self.tail = None + + def dispatch_list(self, node_list): + for node in node_list: + self.dispatch(node) + + def visitFunctionDef(self, node): + + if self.classname: + entity = '%s%s' % (self.classname, node.name) + else: + entity = node.name + + name = '%d:%d: %r' % (node.lineno, node.col_offset, entity) + + if self.graph is not None: + # closure + pathnode = self.appendPathNode(name) + self.tail = pathnode + self.dispatch_list(node.body) + bottom = PathNode("", look='point') + self.graph.connect(self.tail, bottom) + self.graph.connect(pathnode, bottom) + self.tail = bottom + else: + self.graph = PathGraph(name, entity, node.lineno, node.col_offset) + pathnode = PathNode(name) + self.tail = pathnode + self.dispatch_list(node.body) + self.graphs["%s%s" % (self.classname, node.name)] = self.graph + self.reset() + + visitAsyncFunctionDef = visitFunctionDef + + def visitClassDef(self, node): + old_classname = self.classname + self.classname += node.name + "." + self.dispatch_list(node.body) + self.classname = old_classname + + def appendPathNode(self, name): + if not self.tail: + return + pathnode = PathNode(name) + self.graph.connect(self.tail, pathnode) + self.tail = pathnode + return pathnode + + def visitSimpleStatement(self, node): + if node.lineno is None: + lineno = 0 + else: + lineno = node.lineno + name = "Stmt %d" % lineno + self.appendPathNode(name) + + def default(self, node, *args): + if isinstance(node, ast.stmt): + self.visitSimpleStatement(node) + else: + super(PathGraphingAstVisitor, self).default(node, *args) + + def visitLoop(self, node): + name = "Loop %d" % node.lineno + self._subgraph(node, name) + + visitAsyncFor = visitFor = visitWhile = visitLoop + + def visitIf(self, node): + name = "If %d" % node.lineno + self._subgraph(node, name) + + def _subgraph(self, node, name, extra_blocks=()): + """create the subgraphs representing any `if` and `for` statements""" + if self.graph is None: + # global loop + self.graph = PathGraph(name, name, node.lineno, node.col_offset) + pathnode = PathNode(name) + self._subgraph_parse(node, pathnode, extra_blocks) + self.graphs["%s%s" % (self.classname, name)] = self.graph + self.reset() + else: + pathnode = self.appendPathNode(name) + self._subgraph_parse(node, pathnode, extra_blocks) + + def _subgraph_parse(self, node, pathnode, extra_blocks): + """parse the body and any `else` block of `if` and `for` statements""" + loose_ends = [] + self.tail = pathnode + self.dispatch_list(node.body) + loose_ends.append(self.tail) + for extra in extra_blocks: + self.tail = pathnode + self.dispatch_list(extra.body) + loose_ends.append(self.tail) + if node.orelse: + self.tail = pathnode + self.dispatch_list(node.orelse) + loose_ends.append(self.tail) + else: + loose_ends.append(pathnode) + if pathnode: + bottom = PathNode("", look='point') + for le in loose_ends: + self.graph.connect(le, bottom) + self.tail = bottom + + def visitTryExcept(self, node): + name = "TryExcept %d" % node.lineno + self._subgraph(node, name, extra_blocks=node.handlers) + + visitTry = visitTryExcept + + def visitWith(self, node): + name = "With %d" % node.lineno + self.appendPathNode(name) + self.dispatch_list(node.body) + + visitAsyncWith = visitWith + + +class McCabeChecker(object): + """McCabe cyclomatic complexity checker.""" + name = 'mccabe' + version = __version__ + _code = 'C901' + _error_tmpl = "C901 %r is too complex (%d)" + max_complexity = -1 + + def __init__(self, tree, filename): + self.tree = tree + + @classmethod + def add_options(cls, parser): + flag = '--max-complexity' + kwargs = { + 'default': -1, + 'action': 'store', + 'type': int, + 'help': 'McCabe complexity threshold', + 'parse_from_config': 'True', + } + config_opts = getattr(parser, 'config_options', None) + if isinstance(config_opts, list): + # Flake8 2.x + kwargs.pop('parse_from_config') + parser.add_option(flag, **kwargs) + parser.config_options.append('max-complexity') + else: + parser.add_option(flag, **kwargs) + + @classmethod + def parse_options(cls, options): + cls.max_complexity = int(options.max_complexity) + + def run(self): + if self.max_complexity < 0: + return + visitor = PathGraphingAstVisitor() + visitor.preorder(self.tree, visitor) + for graph in visitor.graphs.values(): + if graph.complexity() > self.max_complexity: + text = self._error_tmpl % (graph.entity, graph.complexity()) + yield graph.lineno, graph.column, text, type(self) + + +def get_code_complexity(code, threshold=7, filename='stdin'): + try: + tree = compile(code, filename, "exec", ast.PyCF_ONLY_AST) + except SyntaxError: + e = sys.exc_info()[1] + sys.stderr.write("Unable to parse %s: %s\n" % (filename, e)) + return 0 + + complx = [] + McCabeChecker.max_complexity = threshold + for lineno, offset, text, check in McCabeChecker(tree, filename).run(): + complx.append('%s:%d:1: %s' % (filename, lineno, text)) + + if len(complx) == 0: + return 0 + print('\n'.join(complx)) + return len(complx) + + +def get_module_complexity(module_path, threshold=7): + """Returns the complexity of a module""" + code = _read(module_path) + return get_code_complexity(code, threshold, filename=module_path) + + +def _read(filename): + if (2, 5) < sys.version_info < (3, 0): + with open(filename, 'rU') as f: + return f.read() + elif (3, 0) <= sys.version_info < (4, 0): + """Read the source code.""" + try: + with open(filename, 'rb') as f: + (encoding, _) = tokenize.detect_encoding(f.readline) + except (LookupError, SyntaxError, UnicodeError): + # Fall back if file encoding is improperly declared + with open(filename, encoding='latin-1') as f: + return f.read() + with open(filename, 'r', encoding=encoding) as f: + return f.read() + + +def main(argv=None): + if argv is None: + argv = sys.argv[1:] + opar = optparse.OptionParser() + opar.add_option("-d", "--dot", dest="dot", + help="output a graphviz dot file", action="store_true") + opar.add_option("-m", "--min", dest="threshold", + help="minimum complexity for output", type="int", + default=1) + + options, args = opar.parse_args(argv) + + code = _read(args[0]) + tree = compile(code, args[0], "exec", ast.PyCF_ONLY_AST) + visitor = PathGraphingAstVisitor() + visitor.preorder(tree, visitor) + + if options.dot: + print('graph {') + for graph in visitor.graphs.values(): + if (not options.threshold or + graph.complexity() >= options.threshold): + graph.to_dot() + print('}') + else: + for graph in visitor.graphs.values(): + if graph.complexity() >= options.threshold: + print(graph.name, graph.complexity()) + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA new file mode 100644 index 0000000..f39386d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA @@ -0,0 +1,350 @@ +Metadata-Version: 2.4 +Name: platformdirs +Version: 4.5.0 +Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`. +Project-URL: Changelog, https://github.com/tox-dev/platformdirs/releases +Project-URL: Documentation, https://platformdirs.readthedocs.io +Project-URL: Homepage, https://github.com/tox-dev/platformdirs +Project-URL: Source, https://github.com/tox-dev/platformdirs +Project-URL: Tracker, https://github.com/tox-dev/platformdirs/issues +Maintainer-email: Bernát Gábor , Julian Berman , Ofek Lev , Ronny Pfannschmidt +License-Expression: MIT +License-File: LICENSE +Keywords: appdirs,application,cache,directory,log,user +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.10 +Provides-Extra: docs +Requires-Dist: furo>=2025.9.25; extra == 'docs' +Requires-Dist: proselint>=0.14; extra == 'docs' +Requires-Dist: sphinx-autodoc-typehints>=3.2; extra == 'docs' +Requires-Dist: sphinx>=8.2.3; extra == 'docs' +Provides-Extra: test +Requires-Dist: appdirs==1.4.4; extra == 'test' +Requires-Dist: covdefaults>=2.3; extra == 'test' +Requires-Dist: pytest-cov>=7; extra == 'test' +Requires-Dist: pytest-mock>=3.15.1; extra == 'test' +Requires-Dist: pytest>=8.4.2; extra == 'test' +Provides-Extra: type +Requires-Dist: mypy>=1.18.2; extra == 'type' +Description-Content-Type: text/x-rst + +The problem +=========== + +.. image:: https://badge.fury.io/py/platformdirs.svg + :target: https://badge.fury.io/py/platformdirs +.. image:: https://img.shields.io/pypi/pyversions/platformdirs.svg + :target: https://pypi.python.org/pypi/platformdirs/ +.. image:: https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg + :target: https://github.com/platformdirs/platformdirs/actions +.. image:: https://static.pepy.tech/badge/platformdirs/month + :target: https://pepy.tech/project/platformdirs + +When writing desktop application, finding the right location to store user data +and configuration varies per platform. Even for single-platform apps, there +may by plenty of nuances in figuring out the right location. + +For example, if running on macOS, you should use:: + + ~/Library/Application Support/ + +If on Windows (at least English Win) that should be:: + + C:\Users\\Application Data\Local Settings\\ + +or possibly:: + + C:\Users\\Application Data\\ + +for `roaming profiles `_ but that is another story. + +On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be:: + + ~/.local/share/ + +.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + +``platformdirs`` to the rescue +============================== + +This kind of thing is what the ``platformdirs`` package is for. +``platformdirs`` will help you choose an appropriate: + +- user data dir (``user_data_dir``) +- user config dir (``user_config_dir``) +- user cache dir (``user_cache_dir``) +- site data dir (``site_data_dir``) +- site config dir (``site_config_dir``) +- user log dir (``user_log_dir``) +- user documents dir (``user_documents_dir``) +- user downloads dir (``user_downloads_dir``) +- user pictures dir (``user_pictures_dir``) +- user videos dir (``user_videos_dir``) +- user music dir (``user_music_dir``) +- user desktop dir (``user_desktop_dir``) +- user runtime dir (``user_runtime_dir``) + +And also: + +- Is slightly opinionated on the directory names used. Look for "OPINION" in + documentation and code for when an opinion is being applied. + +Example output +============== + +On macOS: + +.. code-block:: pycon + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/Users/trentm/Library/Application Support/SuperApp' + >>> user_config_dir(appname, appauthor) + '/Users/trentm/Library/Application Support/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/Users/trentm/Library/Caches/SuperApp' + >>> site_data_dir(appname, appauthor) + '/Library/Application Support/SuperApp' + >>> site_config_dir(appname, appauthor) + '/Library/Application Support/SuperApp' + >>> user_log_dir(appname, appauthor) + '/Users/trentm/Library/Logs/SuperApp' + >>> user_documents_dir() + '/Users/trentm/Documents' + >>> user_downloads_dir() + '/Users/trentm/Downloads' + >>> user_pictures_dir() + '/Users/trentm/Pictures' + >>> user_videos_dir() + '/Users/trentm/Movies' + >>> user_music_dir() + '/Users/trentm/Music' + >>> user_desktop_dir() + '/Users/trentm/Desktop' + >>> user_runtime_dir(appname, appauthor) + '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' + +On Windows: + +.. code-block:: pycon + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' + >>> user_data_dir(appname, appauthor, roaming=True) + 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' + >>> user_config_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' + >>> user_cache_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' + >>> site_data_dir(appname, appauthor) + 'C:\\ProgramData\\Acme\\SuperApp' + >>> site_config_dir(appname, appauthor) + 'C:\\ProgramData\\Acme\\SuperApp' + >>> user_log_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' + >>> user_documents_dir() + 'C:\\Users\\trentm\\Documents' + >>> user_downloads_dir() + 'C:\\Users\\trentm\\Downloads' + >>> user_pictures_dir() + 'C:\\Users\\trentm\\Pictures' + >>> user_videos_dir() + 'C:\\Users\\trentm\\Videos' + >>> user_music_dir() + 'C:\\Users\\trentm\\Music' + >>> user_desktop_dir() + 'C:\\Users\\trentm\\Desktop' + >>> user_runtime_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Temp\\Acme\\SuperApp' + +On Linux: + +.. code-block:: pycon + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/home/trentm/.local/share/SuperApp' + >>> user_config_dir(appname) + '/home/trentm/.config/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/home/trentm/.cache/SuperApp' + >>> site_data_dir(appname, appauthor) + '/usr/local/share/SuperApp' + >>> site_data_dir(appname, appauthor, multipath=True) + '/usr/local/share/SuperApp:/usr/share/SuperApp' + >>> site_config_dir(appname) + '/etc/xdg/SuperApp' + >>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc" + >>> site_config_dir(appname, multipath=True) + '/etc/SuperApp:/usr/local/etc/SuperApp' + >>> user_log_dir(appname, appauthor) + '/home/trentm/.local/state/SuperApp/log' + >>> user_documents_dir() + '/home/trentm/Documents' + >>> user_downloads_dir() + '/home/trentm/Downloads' + >>> user_pictures_dir() + '/home/trentm/Pictures' + >>> user_videos_dir() + '/home/trentm/Videos' + >>> user_music_dir() + '/home/trentm/Music' + >>> user_desktop_dir() + '/home/trentm/Desktop' + >>> user_runtime_dir(appname, appauthor) + '/run/user/{os.getuid()}/SuperApp' + +On Android:: + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/data/data/com.myApp/files/SuperApp' + >>> user_config_dir(appname) + '/data/data/com.myApp/shared_prefs/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/data/data/com.myApp/cache/SuperApp' + >>> site_data_dir(appname, appauthor) + '/data/data/com.myApp/files/SuperApp' + >>> site_config_dir(appname) + '/data/data/com.myApp/shared_prefs/SuperApp' + >>> user_log_dir(appname, appauthor) + '/data/data/com.myApp/cache/SuperApp/log' + >>> user_documents_dir() + '/storage/emulated/0/Documents' + >>> user_downloads_dir() + '/storage/emulated/0/Downloads' + >>> user_pictures_dir() + '/storage/emulated/0/Pictures' + >>> user_videos_dir() + '/storage/emulated/0/DCIM/Camera' + >>> user_music_dir() + '/storage/emulated/0/Music' + >>> user_desktop_dir() + '/storage/emulated/0/Desktop' + >>> user_runtime_dir(appname, appauthor) + '/data/data/com.myApp/cache/SuperApp/tmp' + +Note: Some android apps like Termux and Pydroid are used as shells. These +apps are used by the end user to emulate Linux environment. Presence of +``SHELL`` environment variable is used by Platformdirs to differentiate +between general android apps and android apps used as shells. Shell android +apps also support ``XDG_*`` environment variables. + + +``PlatformDirs`` for convenience +================================ + +.. code-block:: pycon + + >>> from platformdirs import PlatformDirs + >>> dirs = PlatformDirs("SuperApp", "Acme") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp' + >>> dirs.user_config_dir + '/Users/trentm/Library/Application Support/SuperApp' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp' + >>> dirs.site_config_dir + '/Library/Application Support/SuperApp' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp' + >>> dirs.user_documents_dir + '/Users/trentm/Documents' + >>> dirs.user_downloads_dir + '/Users/trentm/Downloads' + >>> dirs.user_pictures_dir + '/Users/trentm/Pictures' + >>> dirs.user_videos_dir + '/Users/trentm/Movies' + >>> dirs.user_music_dir + '/Users/trentm/Music' + >>> dirs.user_desktop_dir + '/Users/trentm/Desktop' + >>> dirs.user_runtime_dir + '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' + +Per-version isolation +===================== + +If you have multiple versions of your app in use that you want to be +able to run side-by-side, then you may want version-isolation for these +dirs:: + + >>> from platformdirs import PlatformDirs + >>> dirs = PlatformDirs("SuperApp", "Acme", version="1.0") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp/1.0' + >>> dirs.user_config_dir + '/Users/trentm/Library/Application Support/SuperApp/1.0' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp/1.0' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp/1.0' + >>> dirs.site_config_dir + '/Library/Application Support/SuperApp/1.0' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp/1.0' + >>> dirs.user_documents_dir + '/Users/trentm/Documents' + >>> dirs.user_downloads_dir + '/Users/trentm/Downloads' + >>> dirs.user_pictures_dir + '/Users/trentm/Pictures' + >>> dirs.user_videos_dir + '/Users/trentm/Movies' + >>> dirs.user_music_dir + '/Users/trentm/Music' + >>> dirs.user_desktop_dir + '/Users/trentm/Desktop' + >>> dirs.user_runtime_dir + '/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0' + +Be wary of using this for configuration files though; you'll need to handle +migrating configuration files manually. + +Why this Fork? +============== + +This repository is a friendly fork of the wonderful work started by +`ActiveState `_ who created +``appdirs``, this package's ancestor. + +Maintaining an open source project is no easy task, particularly +from within an organization, and the Python community is indebted +to ``appdirs`` (and to Trent Mick and Jeff Rouse in particular) for +creating an incredibly useful simple module, as evidenced by the wide +number of users it has attracted over the years. + +Nonetheless, given the number of long-standing open issues +and pull requests, and no clear path towards `ensuring +that maintenance of the package would continue or grow +`_, this fork was +created. + +Contributions are most welcome. diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD new file mode 100644 index 0000000..1ac011f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD @@ -0,0 +1,22 @@ +platformdirs-4.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +platformdirs-4.5.0.dist-info/METADATA,sha256=mFxZl6Q-fO2nCdWWCJT4WOr4p7U12jZX4lk26MqGy1o,12804 +platformdirs-4.5.0.dist-info/RECORD,, +platformdirs-4.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +platformdirs-4.5.0.dist-info/licenses/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089 +platformdirs/__init__.py,sha256=iORRy6_lZ9tXLvO0W6fJPn8QV7F532ivl-f2WGmabBc,22284 +platformdirs/__main__.py,sha256=HnsUQHpiBaiTxwcmwVw-nFaPdVNZtQIdi1eWDtI-MzI,1493 +platformdirs/__pycache__/__init__.cpython-310.pyc,, +platformdirs/__pycache__/__main__.cpython-310.pyc,, +platformdirs/__pycache__/android.cpython-310.pyc,, +platformdirs/__pycache__/api.cpython-310.pyc,, +platformdirs/__pycache__/macos.cpython-310.pyc,, +platformdirs/__pycache__/unix.cpython-310.pyc,, +platformdirs/__pycache__/version.cpython-310.pyc,, +platformdirs/__pycache__/windows.cpython-310.pyc,, +platformdirs/android.py,sha256=r0DshVBf-RO1jXJGX8C4Til7F1XWt-bkdWMgmvEiaYg,9013 +platformdirs/api.py,sha256=wPHOlwOsfz2oqQZ6A2FcCu5kEAj-JondzoNOHYFQ0h8,9281 +platformdirs/macos.py,sha256=0XoOgin1NK7Qki7iskD-oS8xKxw6bXgoKEgdqpCRAFQ,6322 +platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +platformdirs/unix.py,sha256=WZmkUA--L3JNRGmz32s35YfoD3ica6xKIPdCV_HhLcs,10458 +platformdirs/version.py,sha256=sved76l3nstESjZInsYGzPryR4cPIaf3QHTJuTDYXNM,704 +platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125 diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..f35fed9 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010-202x The platformdirs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.venv/lib/python3.10/site-packages/platformdirs/__init__.py b/.venv/lib/python3.10/site-packages/platformdirs/__init__.py new file mode 100644 index 0000000..02daa59 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs/__init__.py @@ -0,0 +1,631 @@ +""" +Utilities for determining application-specific dirs. + +See for details and usage. + +""" + +from __future__ import annotations + +import os +import sys +from typing import TYPE_CHECKING + +from .api import PlatformDirsABC +from .version import __version__ +from .version import __version_tuple__ as __version_info__ + +if TYPE_CHECKING: + from pathlib import Path + from typing import Literal + +if sys.platform == "win32": + from platformdirs.windows import Windows as _Result +elif sys.platform == "darwin": + from platformdirs.macos import MacOS as _Result +else: + from platformdirs.unix import Unix as _Result + + +def _set_platform_dir_class() -> type[PlatformDirsABC]: + if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": + if os.getenv("SHELL") or os.getenv("PREFIX"): + return _Result + + from platformdirs.android import _android_folder # noqa: PLC0415 + + if _android_folder() is not None: + from platformdirs.android import Android # noqa: PLC0415 + + return Android # return to avoid redefinition of a result + + return _Result + + +if TYPE_CHECKING: + # Work around mypy issue: https://github.com/python/mypy/issues/10962 + PlatformDirs = _Result +else: + PlatformDirs = _set_platform_dir_class() #: Currently active platform +AppDirs = PlatformDirs #: Backwards compatibility with appdirs + + +def user_data_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: data directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_data_dir + + +def site_data_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + multipath: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: data directory shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + multipath=multipath, + ensure_exists=ensure_exists, + ).site_data_dir + + +def user_config_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: config directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_config_dir + + +def site_config_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + multipath: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: config directory shared by the users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + multipath=multipath, + ensure_exists=ensure_exists, + ).site_config_dir + + +def user_cache_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: cache directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_cache_dir + + +def site_cache_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: cache directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).site_cache_dir + + +def user_state_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: state directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_state_dir + + +def user_log_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: log directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_log_dir + + +def user_documents_dir() -> str: + """:returns: documents directory tied to the user""" + return PlatformDirs().user_documents_dir + + +def user_downloads_dir() -> str: + """:returns: downloads directory tied to the user""" + return PlatformDirs().user_downloads_dir + + +def user_pictures_dir() -> str: + """:returns: pictures directory tied to the user""" + return PlatformDirs().user_pictures_dir + + +def user_videos_dir() -> str: + """:returns: videos directory tied to the user""" + return PlatformDirs().user_videos_dir + + +def user_music_dir() -> str: + """:returns: music directory tied to the user""" + return PlatformDirs().user_music_dir + + +def user_desktop_dir() -> str: + """:returns: desktop directory tied to the user""" + return PlatformDirs().user_desktop_dir + + +def user_runtime_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: runtime directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_runtime_dir + + +def site_runtime_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: runtime directory shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).site_runtime_dir + + +def user_data_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: data path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_data_path + + +def site_data_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + multipath: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `multipath `. + :param ensure_exists: See `ensure_exists `. + :returns: data path shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + multipath=multipath, + ensure_exists=ensure_exists, + ).site_data_path + + +def user_config_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: config path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_config_path + + +def site_config_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + multipath: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: config path shared by the users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + multipath=multipath, + ensure_exists=ensure_exists, + ).site_config_path + + +def site_cache_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: cache directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).site_cache_path + + +def user_cache_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: cache path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_cache_path + + +def user_state_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: state path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_state_path + + +def user_log_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: log path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_log_path + + +def user_documents_path() -> Path: + """:returns: documents a path tied to the user""" + return PlatformDirs().user_documents_path + + +def user_downloads_path() -> Path: + """:returns: downloads path tied to the user""" + return PlatformDirs().user_downloads_path + + +def user_pictures_path() -> Path: + """:returns: pictures path tied to the user""" + return PlatformDirs().user_pictures_path + + +def user_videos_path() -> Path: + """:returns: videos path tied to the user""" + return PlatformDirs().user_videos_path + + +def user_music_path() -> Path: + """:returns: music path tied to the user""" + return PlatformDirs().user_music_path + + +def user_desktop_path() -> Path: + """:returns: desktop path tied to the user""" + return PlatformDirs().user_desktop_path + + +def user_runtime_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: runtime path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_runtime_path + + +def site_runtime_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: runtime path shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).site_runtime_path + + +__all__ = [ + "AppDirs", + "PlatformDirs", + "PlatformDirsABC", + "__version__", + "__version_info__", + "site_cache_dir", + "site_cache_path", + "site_config_dir", + "site_config_path", + "site_data_dir", + "site_data_path", + "site_runtime_dir", + "site_runtime_path", + "user_cache_dir", + "user_cache_path", + "user_config_dir", + "user_config_path", + "user_data_dir", + "user_data_path", + "user_desktop_dir", + "user_desktop_path", + "user_documents_dir", + "user_documents_path", + "user_downloads_dir", + "user_downloads_path", + "user_log_dir", + "user_log_path", + "user_music_dir", + "user_music_path", + "user_pictures_dir", + "user_pictures_path", + "user_runtime_dir", + "user_runtime_path", + "user_state_dir", + "user_state_path", + "user_videos_dir", + "user_videos_path", +] diff --git a/.venv/lib/python3.10/site-packages/platformdirs/__main__.py b/.venv/lib/python3.10/site-packages/platformdirs/__main__.py new file mode 100644 index 0000000..922c521 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs/__main__.py @@ -0,0 +1,55 @@ +"""Main entry point.""" + +from __future__ import annotations + +from platformdirs import PlatformDirs, __version__ + +PROPS = ( + "user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "user_documents_dir", + "user_downloads_dir", + "user_pictures_dir", + "user_videos_dir", + "user_music_dir", + "user_runtime_dir", + "site_data_dir", + "site_config_dir", + "site_cache_dir", + "site_runtime_dir", +) + + +def main() -> None: + """Run the main entry point.""" + app_name = "MyApp" + app_author = "MyCompany" + + print(f"-- platformdirs {__version__} --") # noqa: T201 + + print("-- app dirs (with optional 'version')") # noqa: T201 + dirs = PlatformDirs(app_name, app_author, version="1.0") + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 + + print("\n-- app dirs (without optional 'version')") # noqa: T201 + dirs = PlatformDirs(app_name, app_author) + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 + + print("\n-- app dirs (without optional 'appauthor')") # noqa: T201 + dirs = PlatformDirs(app_name) + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 + + print("\n-- app dirs (with disabled 'appauthor')") # noqa: T201 + dirs = PlatformDirs(app_name, appauthor=False) + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.10/site-packages/platformdirs/android.py b/.venv/lib/python3.10/site-packages/platformdirs/android.py new file mode 100644 index 0000000..92efc85 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs/android.py @@ -0,0 +1,249 @@ +"""Android.""" + +from __future__ import annotations + +import os +import re +import sys +from functools import lru_cache +from typing import TYPE_CHECKING, cast + +from .api import PlatformDirsABC + + +class Android(PlatformDirsABC): + """ + Follows the guidance `from here `_. + + Makes use of the `appname `, `version + `, `ensure_exists `. + + """ + + @property + def user_data_dir(self) -> str: + """:return: data directory tied to the user, e.g. ``/data/user///files/``""" + return self._append_app_name_and_version(cast("str", _android_folder()), "files") + + @property + def site_data_dir(self) -> str: + """:return: data directory shared by users, same as `user_data_dir`""" + return self.user_data_dir + + @property + def user_config_dir(self) -> str: + """ + :return: config directory tied to the user, e.g. \ + ``/data/user///shared_prefs/`` + """ + return self._append_app_name_and_version(cast("str", _android_folder()), "shared_prefs") + + @property + def site_config_dir(self) -> str: + """:return: config directory shared by the users, same as `user_config_dir`""" + return self.user_config_dir + + @property + def user_cache_dir(self) -> str: + """:return: cache directory tied to the user, e.g.,``/data/user///cache/``""" + return self._append_app_name_and_version(cast("str", _android_folder()), "cache") + + @property + def site_cache_dir(self) -> str: + """:return: cache directory shared by users, same as `user_cache_dir`""" + return self.user_cache_dir + + @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, same as `user_cache_dir` if not opinionated else ``log`` in it, + e.g. ``/data/user///cache//log`` + """ + path = self.user_cache_dir + if self.opinion: + path = os.path.join(path, "log") # noqa: PTH118 + return path + + @property + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``""" + return _android_documents_folder() + + @property + def user_downloads_dir(self) -> str: + """:return: downloads directory tied to the user e.g. ``/storage/emulated/0/Downloads``""" + return _android_downloads_folder() + + @property + def user_pictures_dir(self) -> str: + """:return: pictures directory tied to the user e.g. ``/storage/emulated/0/Pictures``""" + return _android_pictures_folder() + + @property + def user_videos_dir(self) -> str: + """:return: videos directory tied to the user e.g. ``/storage/emulated/0/DCIM/Camera``""" + return _android_videos_folder() + + @property + def user_music_dir(self) -> str: + """:return: music directory tied to the user e.g. ``/storage/emulated/0/Music``""" + return _android_music_folder() + + @property + def user_desktop_dir(self) -> str: + """:return: desktop directory tied to the user e.g. ``/storage/emulated/0/Desktop``""" + return "/storage/emulated/0/Desktop" + + @property + def user_runtime_dir(self) -> str: + """ + :return: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it, + e.g. ``/data/user///cache//tmp`` + """ + path = self.user_cache_dir + if self.opinion: + path = os.path.join(path, "tmp") # noqa: PTH118 + return path + + @property + def site_runtime_dir(self) -> str: + """:return: runtime directory shared by users, same as `user_runtime_dir`""" + return self.user_runtime_dir + + +@lru_cache(maxsize=1) +def _android_folder() -> str | None: # noqa: C901 + """:return: base folder for the Android OS or None if it cannot be found""" + result: str | None = None + # type checker isn't happy with our "import android", just don't do this when type checking see + # https://stackoverflow.com/a/61394121 + if not TYPE_CHECKING: + try: + # First try to get a path to android app using python4android (if available)... + from android import mActivity # noqa: PLC0415 + + context = cast("android.content.Context", mActivity.getApplicationContext()) # noqa: F821 + result = context.getFilesDir().getParentFile().getAbsolutePath() + except Exception: # noqa: BLE001 + result = None + if result is None: + try: + # ...and fall back to using plain pyjnius, if python4android isn't available or doesn't deliver any useful + # result... + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + result = context.getFilesDir().getParentFile().getAbsolutePath() + except Exception: # noqa: BLE001 + result = None + if result is None: + # and if that fails, too, find an android folder looking at path on the sys.path + # warning: only works for apps installed under /data, not adopted storage etc. + pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files") + for path in sys.path: + if pattern.match(path): + result = path.split("/files")[0] + break + else: + result = None + if result is None: + # one last try: find an android folder looking at path on the sys.path taking adopted storage paths into + # account + pattern = re.compile(r"/mnt/expand/[a-fA-F0-9-]{36}/(data|user/\d+)/(.+)/files") + for path in sys.path: + if pattern.match(path): + result = path.split("/files")[0] + break + else: + result = None + return result + + +@lru_cache(maxsize=1) +def _android_documents_folder() -> str: + """:return: documents folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + documents_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + except Exception: # noqa: BLE001 + documents_dir = "/storage/emulated/0/Documents" + + return documents_dir + + +@lru_cache(maxsize=1) +def _android_downloads_folder() -> str: + """:return: downloads folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + downloads_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + except Exception: # noqa: BLE001 + downloads_dir = "/storage/emulated/0/Downloads" + + return downloads_dir + + +@lru_cache(maxsize=1) +def _android_pictures_folder() -> str: + """:return: pictures folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + pictures_dir: str = context.getExternalFilesDir(environment.DIRECTORY_PICTURES).getAbsolutePath() + except Exception: # noqa: BLE001 + pictures_dir = "/storage/emulated/0/Pictures" + + return pictures_dir + + +@lru_cache(maxsize=1) +def _android_videos_folder() -> str: + """:return: videos folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + videos_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DCIM).getAbsolutePath() + except Exception: # noqa: BLE001 + videos_dir = "/storage/emulated/0/DCIM/Camera" + + return videos_dir + + +@lru_cache(maxsize=1) +def _android_music_folder() -> str: + """:return: music folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + music_dir: str = context.getExternalFilesDir(environment.DIRECTORY_MUSIC).getAbsolutePath() + except Exception: # noqa: BLE001 + music_dir = "/storage/emulated/0/Music" + + return music_dir + + +__all__ = [ + "Android", +] diff --git a/.venv/lib/python3.10/site-packages/platformdirs/api.py b/.venv/lib/python3.10/site-packages/platformdirs/api.py new file mode 100644 index 0000000..251600e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs/api.py @@ -0,0 +1,299 @@ +"""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: + from collections.abc import Iterator + from typing import Literal + + +class PlatformDirsABC(ABC): # noqa: PLR0904 + """Abstract base class for platform directories.""" + + def __init__( # noqa: PLR0913, PLR0917 + self, + appname: str | None = None, + appauthor: str | Literal[False] | None = 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 ``.``. + + """ + 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 `_). + + """ + 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) + + def _first_item_as_path_if_multipath(self, directory: str) -> Path: + if self.multipath: + # If multipath is True, the first path is returned. + directory = directory.partition(os.pathsep)[0] + return Path(directory) + + @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 a 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) + + def iter_config_dirs(self) -> Iterator[str]: + """:yield: all user and site configuration directories.""" + yield self.user_config_dir + yield self.site_config_dir + + def iter_data_dirs(self) -> Iterator[str]: + """:yield: all user and site data directories.""" + yield self.user_data_dir + yield self.site_data_dir + + def iter_cache_dirs(self) -> Iterator[str]: + """:yield: all user and site cache directories.""" + yield self.user_cache_dir + yield self.site_cache_dir + + def iter_runtime_dirs(self) -> Iterator[str]: + """:yield: all user and site runtime directories.""" + yield self.user_runtime_dir + yield self.site_runtime_dir + + def iter_config_paths(self) -> Iterator[Path]: + """:yield: all user and site configuration paths.""" + for path in self.iter_config_dirs(): + yield Path(path) + + def iter_data_paths(self) -> Iterator[Path]: + """:yield: all user and site data paths.""" + for path in self.iter_data_dirs(): + yield Path(path) + + def iter_cache_paths(self) -> Iterator[Path]: + """:yield: all user and site cache paths.""" + for path in self.iter_cache_dirs(): + yield Path(path) + + def iter_runtime_paths(self) -> Iterator[Path]: + """:yield: all user and site runtime paths.""" + for path in self.iter_runtime_dirs(): + yield Path(path) diff --git a/.venv/lib/python3.10/site-packages/platformdirs/macos.py b/.venv/lib/python3.10/site-packages/platformdirs/macos.py new file mode 100644 index 0000000..30ab368 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs/macos.py @@ -0,0 +1,146 @@ +"""macOS.""" + +from __future__ import annotations + +import os.path +import sys +from typing import TYPE_CHECKING + +from .api import PlatformDirsABC + +if TYPE_CHECKING: + from pathlib import Path + + +class MacOS(PlatformDirsABC): + """ + Platform directories for the macOS operating system. + + Follows the guidance from + `Apple documentation `_. + Makes use of the `appname `, + `version `, + `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 `_, the directory + will be under the Homebrew prefix, e.g. ``$homebrew_prefix/share/$appname/$version``. + If `multipath ` is enabled, and we're in Homebrew, + the response is a multi-path string separated by ":", e.g. + ``$homebrew_prefix/share/$appname/$version:/Library/Application Support/$appname/$version`` + """ + is_homebrew = "/opt/python" in sys.prefix + homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else "" + path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/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 site_data_path(self) -> Path: + """:return: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_data_dir) + + @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 `_, the directory + will be under the Homebrew prefix, e.g. ``$homebrew_prefix/var/cache/$appname/$version``. + If `multipath ` is enabled, and we're in Homebrew, + the response is a multi-path string separated by ":", e.g. + ``$homebrew_prefix/var/cache/$appname/$version:/Library/Caches/$appname/$version`` + """ + is_homebrew = "/opt/python" in sys.prefix + homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else "" + path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/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 site_cache_path(self) -> Path: + """:return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_cache_dir) + + @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", +] diff --git a/.venv/lib/python3.10/site-packages/platformdirs/py.typed b/.venv/lib/python3.10/site-packages/platformdirs/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.10/site-packages/platformdirs/unix.py b/.venv/lib/python3.10/site-packages/platformdirs/unix.py new file mode 100644 index 0000000..fc75d8d --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs/unix.py @@ -0,0 +1,272 @@ +"""Unix.""" + +from __future__ import annotations + +import os +import sys +from configparser import ConfigParser +from pathlib import Path +from typing import TYPE_CHECKING, NoReturn + +from .api import PlatformDirsABC + +if TYPE_CHECKING: + from collections.abc import Iterator + +if sys.platform == "win32": + + def getuid() -> NoReturn: + msg = "should only be used on Unix" + raise RuntimeError(msg) + +else: + from os import getuid + + +class Unix(PlatformDirsABC): # noqa: PLR0904 + """ + On Unix/Linux, we follow the `XDG Basedir Spec `_. + + The spec allows overriding directories with environment variables. The examples shown are the default values, + alongside the name of the environment variable that overrides them. Makes use of the `appname + `, `version `, `multipath + `, `opinion `, `ensure_exists + `. + + """ + + @property + def user_data_dir(self) -> str: + """ + :return: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or + ``$XDG_DATA_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_DATA_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.local/share") # noqa: PTH111 + return self._append_app_name_and_version(path) + + @property + def _site_data_dirs(self) -> list[str]: + path = os.environ.get("XDG_DATA_DIRS", "") + if not path.strip(): + path = f"/usr/local/share{os.pathsep}/usr/share" + return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)] + + @property + def site_data_dir(self) -> str: + """ + :return: data directories shared by users (if `multipath ` is + enabled and ``XDG_DATA_DIRS`` is set and a multi path the response is also a multi path separated by the + OS path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version`` + """ + # XDG default for $XDG_DATA_DIRS; only first, if multipath is False + dirs = self._site_data_dirs + if not self.multipath: + return dirs[0] + return os.pathsep.join(dirs) + + @property + def user_config_dir(self) -> str: + """ + :return: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or + ``$XDG_CONFIG_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_CONFIG_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.config") # noqa: PTH111 + return self._append_app_name_and_version(path) + + @property + def _site_config_dirs(self) -> list[str]: + path = os.environ.get("XDG_CONFIG_DIRS", "") + if not path.strip(): + path = "/etc/xdg" + return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)] + + @property + def site_config_dir(self) -> str: + """ + :return: config directories shared by users (if `multipath ` + is enabled and ``XDG_CONFIG_DIRS`` is set and a multi path the response is also a multi path separated by + the OS path separator), e.g. ``/etc/xdg/$appname/$version`` + """ + # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False + dirs = self._site_config_dirs + if not self.multipath: + return dirs[0] + return os.pathsep.join(dirs) + + @property + def user_cache_dir(self) -> str: + """ + :return: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or + ``~/$XDG_CACHE_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_CACHE_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.cache") # noqa: PTH111 + return self._append_app_name_and_version(path) + + @property + def site_cache_dir(self) -> str: + """:return: cache directory shared by users, e.g. ``/var/cache/$appname/$version``""" + return self._append_app_name_and_version("/var/cache") + + @property + def user_state_dir(self) -> str: + """ + :return: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or + ``$XDG_STATE_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_STATE_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.local/state") # noqa: PTH111 + return self._append_app_name_and_version(path) + + @property + def user_log_dir(self) -> str: + """:return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it""" + path = self.user_state_dir + if self.opinion: + path = os.path.join(path, "log") # noqa: PTH118 + self._optionally_create_directory(path) + return path + + @property + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user, e.g. ``~/Documents``""" + return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents") + + @property + def user_downloads_dir(self) -> str: + """:return: downloads directory tied to the user, e.g. ``~/Downloads``""" + return _get_user_media_dir("XDG_DOWNLOAD_DIR", "~/Downloads") + + @property + def user_pictures_dir(self) -> str: + """:return: pictures directory tied to the user, e.g. ``~/Pictures``""" + return _get_user_media_dir("XDG_PICTURES_DIR", "~/Pictures") + + @property + def user_videos_dir(self) -> str: + """:return: videos directory tied to the user, e.g. ``~/Videos``""" + return _get_user_media_dir("XDG_VIDEOS_DIR", "~/Videos") + + @property + def user_music_dir(self) -> str: + """:return: music directory tied to the user, e.g. ``~/Music``""" + return _get_user_media_dir("XDG_MUSIC_DIR", "~/Music") + + @property + def user_desktop_dir(self) -> str: + """:return: desktop directory tied to the user, e.g. ``~/Desktop``""" + return _get_user_media_dir("XDG_DESKTOP_DIR", "~/Desktop") + + @property + def user_runtime_dir(self) -> str: + """ + :return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or + ``$XDG_RUNTIME_DIR/$appname/$version``. + + For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/user/$(id -u)/$appname/$version`` if + exists, otherwise ``/tmp/runtime-$(id -u)/$appname/$version``, if``$XDG_RUNTIME_DIR`` + is not set. + """ + path = os.environ.get("XDG_RUNTIME_DIR", "") + if not path.strip(): + if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): + path = f"/var/run/user/{getuid()}" + if not Path(path).exists(): + path = f"/tmp/runtime-{getuid()}" # noqa: S108 + else: + path = f"/run/user/{getuid()}" + return self._append_app_name_and_version(path) + + @property + def site_runtime_dir(self) -> str: + """ + :return: runtime directory shared by users, e.g. ``/run/$appname/$version`` or \ + ``$XDG_RUNTIME_DIR/$appname/$version``. + + Note that this behaves almost exactly like `user_runtime_dir` if ``$XDG_RUNTIME_DIR`` is set, but will + fall back to paths associated to the root user instead of a regular logged-in user if it's not set. + + If you wish to ensure that a logged-in root user path is returned e.g. ``/run/user/0``, use `user_runtime_dir` + instead. + + For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/$appname/$version`` if ``$XDG_RUNTIME_DIR`` is not set. + """ + path = os.environ.get("XDG_RUNTIME_DIR", "") + if not path.strip(): + if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): + path = "/var/run" + else: + path = "/run" + return self._append_app_name_and_version(path) + + @property + def site_data_path(self) -> Path: + """:return: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_data_dir) + + @property + def site_config_path(self) -> Path: + """:return: config path shared by the users, returns the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_config_dir) + + @property + def site_cache_path(self) -> Path: + """:return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_cache_dir) + + def iter_config_dirs(self) -> Iterator[str]: + """:yield: all user and site configuration directories.""" + yield self.user_config_dir + yield from self._site_config_dirs + + def iter_data_dirs(self) -> Iterator[str]: + """:yield: all user and site data directories.""" + yield self.user_data_dir + yield from self._site_data_dirs + + +def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str: + media_dir = _get_user_dirs_folder(env_var) + if media_dir is None: + media_dir = os.environ.get(env_var, "").strip() + if not media_dir: + media_dir = os.path.expanduser(fallback_tilde_path) # noqa: PTH111 + + return media_dir + + +def _get_user_dirs_folder(key: str) -> str | None: + """ + Return directory from user-dirs.dirs config file. + + See https://freedesktop.org/wiki/Software/xdg-user-dirs/. + + """ + user_dirs_config_path = Path(Unix().user_config_dir) / "user-dirs.dirs" + if user_dirs_config_path.exists(): + parser = ConfigParser() + + with user_dirs_config_path.open() as stream: + # Add fake section header, so ConfigParser doesn't complain + parser.read_string(f"[top]\n{stream.read()}") + + if key not in parser["top"]: + return None + + path = parser["top"][key].strip('"') + # Handle relative home paths + return path.replace("$HOME", os.path.expanduser("~")) # noqa: PTH111 + + return None + + +__all__ = [ + "Unix", +] diff --git a/.venv/lib/python3.10/site-packages/platformdirs/version.py b/.venv/lib/python3.10/site-packages/platformdirs/version.py new file mode 100644 index 0000000..3575282 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs/version.py @@ -0,0 +1,34 @@ +# file generated by setuptools-scm +# don't change, don't track in version control + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] +else: + VERSION_TUPLE = object + COMMIT_ID = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = '4.5.0' +__version_tuple__ = version_tuple = (4, 5, 0) + +__commit_id__ = commit_id = None diff --git a/.venv/lib/python3.10/site-packages/platformdirs/windows.py b/.venv/lib/python3.10/site-packages/platformdirs/windows.py new file mode 100644 index 0000000..d7bc960 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/platformdirs/windows.py @@ -0,0 +1,272 @@ +"""Windows.""" + +from __future__ import annotations + +import os +import sys +from functools import lru_cache +from typing import TYPE_CHECKING + +from .api import PlatformDirsABC + +if TYPE_CHECKING: + from collections.abc import Callable + + +class Windows(PlatformDirsABC): + """ + `MSDN on where to store app data files `_. + + Makes use of the `appname `, `appauthor + `, `version `, `roaming + `, `opinion `, `ensure_exists + `. + + """ + + @property + def user_data_dir(self) -> str: + """ + :return: data directory tied to the user, e.g. + ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname`` (not roaming) or + ``%USERPROFILE%\\AppData\\Roaming\\$appauthor\\$appname`` (roaming) + """ + const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(get_win_folder(const)) + return self._append_parts(path) + + def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str: + params = [] + if self.appname: + if self.appauthor is not False: + author = self.appauthor or self.appname + params.append(author) + params.append(self.appname) + if opinion_value is not None and self.opinion: + params.append(opinion_value) + if self.version: + params.append(self.version) + path = os.path.join(path, *params) # noqa: PTH118 + self._optionally_create_directory(path) + return path + + @property + def site_data_dir(self) -> str: + """:return: data directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname``""" + path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA")) + return self._append_parts(path) + + @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 (if opinionated with ``Cache`` folder within ``$appname``) e.g. + ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname\\Cache\\$version`` + """ + path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA")) + return self._append_parts(path, opinion_value="Cache") + + @property + def site_cache_dir(self) -> str: + """:return: cache directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname\\Cache\\$version``""" + path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA")) + return self._append_parts(path, opinion_value="Cache") + + @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, same as `user_data_dir` if not opinionated else ``Logs`` in it""" + path = self.user_data_dir + if self.opinion: + path = os.path.join(path, "Logs") # noqa: PTH118 + self._optionally_create_directory(path) + return path + + @property + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents``""" + return os.path.normpath(get_win_folder("CSIDL_PERSONAL")) + + @property + def user_downloads_dir(self) -> str: + """:return: downloads directory tied to the user e.g. ``%USERPROFILE%\\Downloads``""" + return os.path.normpath(get_win_folder("CSIDL_DOWNLOADS")) + + @property + def user_pictures_dir(self) -> str: + """:return: pictures directory tied to the user e.g. ``%USERPROFILE%\\Pictures``""" + return os.path.normpath(get_win_folder("CSIDL_MYPICTURES")) + + @property + def user_videos_dir(self) -> str: + """:return: videos directory tied to the user e.g. ``%USERPROFILE%\\Videos``""" + return os.path.normpath(get_win_folder("CSIDL_MYVIDEO")) + + @property + def user_music_dir(self) -> str: + """:return: music directory tied to the user e.g. ``%USERPROFILE%\\Music``""" + return os.path.normpath(get_win_folder("CSIDL_MYMUSIC")) + + @property + def user_desktop_dir(self) -> str: + """:return: desktop directory tied to the user, e.g. ``%USERPROFILE%\\Desktop``""" + return os.path.normpath(get_win_folder("CSIDL_DESKTOPDIRECTORY")) + + @property + def user_runtime_dir(self) -> str: + """ + :return: runtime directory tied to the user, e.g. + ``%USERPROFILE%\\AppData\\Local\\Temp\\$appauthor\\$appname`` + """ + path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp")) # noqa: PTH118 + return self._append_parts(path) + + @property + def site_runtime_dir(self) -> str: + """:return: runtime directory shared by users, same as `user_runtime_dir`""" + return self.user_runtime_dir + + +def get_win_folder_from_env_vars(csidl_name: str) -> str: + """Get folder from environment variables.""" + result = get_win_folder_if_csidl_name_not_env_var(csidl_name) + if result is not None: + return result + + env_var_name = { + "CSIDL_APPDATA": "APPDATA", + "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE", + "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA", + }.get(csidl_name) + if env_var_name is None: + msg = f"Unknown CSIDL name: {csidl_name}" + raise ValueError(msg) + result = os.environ.get(env_var_name) + if result is None: + msg = f"Unset environment variable: {env_var_name}" + raise ValueError(msg) + return result + + +def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None: + """Get a folder for a CSIDL name that does not exist as an environment variable.""" + if csidl_name == "CSIDL_PERSONAL": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents") # noqa: PTH118 + + if csidl_name == "CSIDL_DOWNLOADS": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Downloads") # noqa: PTH118 + + if csidl_name == "CSIDL_MYPICTURES": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Pictures") # noqa: PTH118 + + if csidl_name == "CSIDL_MYVIDEO": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Videos") # noqa: PTH118 + + if csidl_name == "CSIDL_MYMUSIC": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Music") # noqa: PTH118 + return None + + +def get_win_folder_from_registry(csidl_name: str) -> str: + """ + Get folder from the registry. + + This is a fallback technique at best. I'm not sure if using the registry for these guarantees us the correct answer + for all CSIDL_* names. + + """ + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + "CSIDL_PERSONAL": "Personal", + "CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}", + "CSIDL_MYPICTURES": "My Pictures", + "CSIDL_MYVIDEO": "My Video", + "CSIDL_MYMUSIC": "My Music", + }.get(csidl_name) + if shell_folder_name is None: + msg = f"Unknown CSIDL name: {csidl_name}" + raise ValueError(msg) + if sys.platform != "win32": # only needed for mypy type checker to know that this code runs only on Windows + raise NotImplementedError + import winreg # noqa: PLC0415 + + key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") + directory, _ = winreg.QueryValueEx(key, shell_folder_name) + return str(directory) + + +def get_win_folder_via_ctypes(csidl_name: str) -> str: + """Get folder with ctypes.""" + # There is no 'CSIDL_DOWNLOADS'. + # Use 'CSIDL_PROFILE' (40) and append the default folder 'Downloads' instead. + # https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + + import ctypes # noqa: PLC0415 + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + "CSIDL_PERSONAL": 5, + "CSIDL_MYPICTURES": 39, + "CSIDL_MYVIDEO": 14, + "CSIDL_MYMUSIC": 13, + "CSIDL_DOWNLOADS": 40, + "CSIDL_DESKTOPDIRECTORY": 16, + }.get(csidl_name) + if csidl_const is None: + msg = f"Unknown CSIDL name: {csidl_name}" + raise ValueError(msg) + + buf = ctypes.create_unicode_buffer(1024) + windll = getattr(ctypes, "windll") # noqa: B009 # using getattr to avoid false positive with mypy type checker + windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if it has high-bit chars. + if any(ord(c) > 255 for c in buf): # noqa: PLR2004 + buf2 = ctypes.create_unicode_buffer(1024) + if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + if csidl_name == "CSIDL_DOWNLOADS": + return os.path.join(buf.value, "Downloads") # noqa: PTH118 + + return buf.value + + +def _pick_get_win_folder() -> Callable[[str], str]: + try: + import ctypes # noqa: PLC0415 + except ImportError: + pass + else: + if hasattr(ctypes, "windll"): + return get_win_folder_via_ctypes + try: + import winreg # noqa: PLC0415, F401 + except ImportError: + return get_win_folder_from_env_vars + else: + return get_win_folder_from_registry + + +get_win_folder = lru_cache(maxsize=None)(_pick_get_win_folder()) + +__all__ = [ + "Windows", +] diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA new file mode 100644 index 0000000..67dbe84 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA @@ -0,0 +1,277 @@ +Metadata-Version: 2.4 +Name: pylint +Version: 4.0.4 +Summary: python code static checker +Author-email: Python Code Quality Authority +License-Expression: GPL-2.0-or-later +Project-URL: Bug Tracker, https://github.com/pylint-dev/pylint/issues +Project-URL: Discord Server, https://discord.com/invite/Egy6P8AMB5 +Project-URL: Docs: Contributor Guide, https://pylint.readthedocs.io/en/latest/development_guide/contributor_guide/index.html +Project-URL: Docs: User Guide, https://pylint.readthedocs.io/en/latest/ +Project-URL: homepage, https://github.com/pylint-dev/pylint +Project-URL: Source Code, https://github.com/pylint-dev/pylint +Project-URL: What's New, https://pylint.readthedocs.io/en/latest/whatsnew/3/ +Keywords: lint,linter,python,static code analysis +Classifier: Development Status :: 6 - Mature +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Debuggers +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Topic :: Software Development :: Testing +Classifier: Typing :: Typed +Requires-Python: >=3.10.0 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: CONTRIBUTORS.txt +Requires-Dist: astroid<=4.1.dev0,>=4.0.2 +Requires-Dist: colorama>=0.4.5; sys_platform == "win32" +Requires-Dist: dill>=0.2; python_version < "3.11" +Requires-Dist: dill>=0.3.6; python_version >= "3.11" +Requires-Dist: dill>=0.3.7; python_version >= "3.12" +Requires-Dist: isort!=5.13,<8,>=5 +Requires-Dist: mccabe<0.8,>=0.6 +Requires-Dist: platformdirs>=2.2 +Requires-Dist: tomli>=1.1; python_version < "3.11" +Requires-Dist: tomlkit>=0.10.1 +Requires-Dist: typing-extensions>=3.10; python_version < "3.10" +Provides-Extra: spelling +Requires-Dist: pyenchant~=3.2; extra == "spelling" +Provides-Extra: testutils +Requires-Dist: gitpython>3; extra == "testutils" +Dynamic: license-file + +`Pylint`_ +========= + +.. _`Pylint`: https://pylint.readthedocs.io/ + +.. This is used inside the doc to recover the start of the introduction + +.. image:: https://github.com/pylint-dev/pylint/actions/workflows/tests.yaml/badge.svg?branch=main + :target: https://github.com/pylint-dev/pylint/actions + +.. image:: https://codecov.io/gh/pylint-dev/pylint/branch/main/graph/badge.svg?token=ZETEzayrfk + :target: https://codecov.io/gh/pylint-dev/pylint + +.. image:: https://img.shields.io/pypi/v/pylint.svg + :alt: PyPI Package version + :target: https://pypi.python.org/pypi/pylint + +.. image:: https://readthedocs.org/projects/pylint/badge/?version=latest + :target: https://pylint.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + +.. image:: https://img.shields.io/badge/linting-pylint-yellowgreen + :target: https://github.com/pylint-dev/pylint + +.. image:: https://results.pre-commit.ci/badge/github/pylint-dev/pylint/main.svg + :target: https://results.pre-commit.ci/latest/github/pylint-dev/pylint/main + :alt: pre-commit.ci status + +.. image:: https://bestpractices.coreinfrastructure.org/projects/6328/badge + :target: https://bestpractices.coreinfrastructure.org/projects/6328 + :alt: CII Best Practices + +.. image:: https://img.shields.io/ossf-scorecard/github.com/PyCQA/pylint?label=openssf%20scorecard&style=flat + :target: https://api.securityscorecards.dev/projects/github.com/PyCQA/pylint + :alt: OpenSSF Scorecard + +.. image:: https://img.shields.io/discord/825463413634891776.svg + :target: https://discord.gg/qYxpadCgkx + :alt: Discord + +What is Pylint? +--------------- + +Pylint is a `static code analyser`_ for Python 2 or 3. The latest version supports Python +3.10.0 and above. + +.. _`static code analyser`: https://en.wikipedia.org/wiki/Static_code_analysis + +Pylint analyses your code without actually running it. It checks for errors, enforces a +coding standard, looks for `code smells`_, and can make suggestions about how the code +could be refactored. + +.. _`code smells`: https://martinfowler.com/bliki/CodeSmell.html + +Install +------- + +.. This is used inside the doc to recover the start of the short text for installation + +For command line use, pylint is installed with:: + + pip install pylint + +Or if you want to also check spelling with ``enchant`` (you might need to +`install the enchant C library `_): + +.. code-block:: sh + + pip install pylint[spelling] + +It can also be integrated in most editors or IDEs. More information can be found +`in the documentation`_. + +.. _in the documentation: https://pylint.readthedocs.io/en/latest/user_guide/installation/index.html + +.. This is used inside the doc to recover the end of the short text for installation + +What differentiates Pylint? +--------------------------- + +Pylint is not trusting your typing and is inferring the actual values of nodes (for a +start because there was no typing when pylint started off) using its internal code +representation (astroid). If your code is ``import logging as argparse``, Pylint +can check and know that ``argparse.error(...)`` is in fact a logging call and not an +argparse call. This makes pylint slower, but it also lets pylint find more issues if +your code is not fully typed. + + [inference] is the killer feature that keeps us using [pylint] in our project despite how painfully slow it is. + - `Realist pylint user`_, 2022 + +.. _`Realist pylint user`: https://github.com/charliermarsh/ruff/issues/970#issuecomment-1381067064 + +pylint, not afraid of being a little slower than it already is, is also a lot more thorough than other linters. +There are more checks, including some opinionated ones that are deactivated by default +but can be enabled using configuration. + +How to use pylint +----------------- + +Pylint isn't smarter than you: it may warn you about things that you have +conscientiously done or check for some things that you don't care about. +During adoption, especially in a legacy project where pylint was never enforced, +it's best to start with the ``--errors-only`` flag, then disable +convention and refactor messages with ``--disable=C,R`` and progressively +re-evaluate and re-enable messages as your priorities evolve. + +Pylint is highly configurable and permits to write plugins in order to add your +own checks (for example, for internal libraries or an internal rule). Pylint also has an +ecosystem of existing plugins for popular frameworks and third-party libraries. + +.. note:: + + Pylint supports the Python standard library out of the box. Third-party + libraries are not always supported, so a plugin might be needed. A good place + to start is ``PyPI`` which often returns a plugin by searching for + ``pylint ``. `pylint-pydantic`_, `pylint-django`_ and + `pylint-sonarjson`_ are examples of such plugins. More information about plugins + and how to load them can be found at `plugins`_. + +.. _`plugins`: https://pylint.readthedocs.io/en/latest/development_guide/how_tos/plugins.html#plugins +.. _`pylint-pydantic`: https://pypi.org/project/pylint-pydantic +.. _`pylint-django`: https://github.com/pylint-dev/pylint-django +.. _`pylint-sonarjson`: https://github.com/cnescatlab/pylint-sonarjson-catlab + +Advised linters alongside pylint +-------------------------------- + +Projects that you might want to use alongside pylint include ruff_ (**really** fast, +with builtin auto-fix and a large number of checks taken from popular linters, but +implemented in ``rust``) or flake8_ (a framework to implement your own checks in python using ``ast`` directly), +mypy_, pyright_ / pylance or pyre_ (typing checks), bandit_ (security oriented checks), black_ and +isort_ (auto-formatting), autoflake_ (automated removal of unused imports or variables), pyupgrade_ +(automated upgrade to newer python syntax) and pydocstringformatter_ (automated pep257). + +.. _ruff: https://github.com/astral-sh/ruff +.. _flake8: https://github.com/PyCQA/flake8 +.. _bandit: https://github.com/PyCQA/bandit +.. _mypy: https://github.com/python/mypy +.. _pyright: https://github.com/microsoft/pyright +.. _pyre: https://github.com/facebook/pyre-check +.. _black: https://github.com/psf/black +.. _autoflake: https://github.com/myint/autoflake +.. _pyupgrade: https://github.com/asottile/pyupgrade +.. _pydocstringformatter: https://github.com/DanielNoord/pydocstringformatter +.. _isort: https://pycqa.github.io/isort/ + +Additional tools included in pylint +----------------------------------- + +Pylint ships with two additional tools: + +- pyreverse_ (standalone tool that generates package and class diagrams.) +- symilar_ (duplicate code finder that is also integrated in pylint) + +.. _pyreverse: https://pylint.readthedocs.io/en/latest/additional_tools/pyreverse/index.html +.. _symilar: https://pylint.readthedocs.io/en/latest/additional_tools/symilar/index.html + + +.. This is used inside the doc to recover the end of the introduction + +Contributing +------------ + +.. This is used inside the doc to recover the start of the short text for contribution + +We welcome all forms of contributions such as updates for documentation, new code, checking issues for duplicates or telling us +that we can close them, confirming that issues still exist, `creating issues because +you found a bug or want a feature`_, etc. Everything is much appreciated! + +Please follow the `code of conduct`_ and check `the Contributor Guides`_ if you want to +make a code contribution. + +.. _creating issues because you found a bug or want a feature: https://pylint.readthedocs.io/en/latest/contact.html#bug-reports-feedback +.. _code of conduct: https://github.com/pylint-dev/pylint/blob/main/CODE_OF_CONDUCT.md +.. _the Contributor Guides: https://pylint.readthedocs.io/en/latest/development_guide/contribute.html + +.. This is used inside the doc to recover the end of the short text for contribution + +Show your usage +----------------- + +You can place this badge in your README to let others know your project uses pylint. + + .. image:: https://img.shields.io/badge/linting-pylint-yellowgreen + :target: https://github.com/pylint-dev/pylint + +Learn how to add a badge to your documentation in `the badge documentation`_. + +.. _the badge documentation: https://pylint.readthedocs.io/en/latest/user_guide/installation/badge.html + +License +------- + +pylint is, with a few exceptions listed below, `GPLv2 `_. + +The icon files are licensed under the `CC BY-SA 4.0 `_ license: + +- `doc/logo.png `_ +- `doc/logo.svg `_ + +Support +------- + +Please check `the contact information`_. + +.. _`the contact information`: https://pylint.readthedocs.io/en/latest/contact.html + +.. |tideliftlogo| image:: https://raw.githubusercontent.com/pylint-dev/pylint/main/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png + :width: 200 + :alt: Tidelift + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for pylint is available as part of the `Tidelift + Subscription`_. Tidelift gives software development teams a single source for + purchasing and maintaining their software, with professional grade assurances + from the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-pylint?utm_source=pypi-pylint&utm_medium=referral&utm_campaign=readme diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD new file mode 100644 index 0000000..83717f3 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD @@ -0,0 +1,371 @@ +../../../bin/pylint,sha256=_JZ6ZVXY51NODFkOdzhjJ4mgGifYosPGa69byGZkSds,234 +../../../bin/pylint-config,sha256=Gkqx9qzro9VIkfwUp4Z-h2EipEOJZqWJ1LBnJnLtk-g,250 +../../../bin/pyreverse,sha256=p-UM5HRhrmYfd56gc8UcsCl72nuBWj0LUKFEjtO-QG4,240 +../../../bin/symilar,sha256=ZOjPYBZodF7yQWkzZRYmpxwPJgYTmFiGeyZYbaLI9vw,236 +pylint-4.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pylint-4.0.4.dist-info/METADATA,sha256=nerPzgR0iVcum1LsxxFZb-DSe6j5yk--SmEq4XnSu3U,12151 +pylint-4.0.4.dist-info/RECORD,, +pylint-4.0.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pylint-4.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +pylint-4.0.4.dist-info/entry_points.txt,sha256=0wBRzcsPs1QNE1gWMZ5yepPnOat1elSm-EwS_0ngOY4,149 +pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt,sha256=g_s20VXmEOY3GA1b8QsBD7s-CCQaLoVaSIluiV7gb6g,33198 +pylint-4.0.4.dist-info/licenses/LICENSE,sha256=-XsUCA3ouEkNYOs9Yg68QZlD4HeUZtHdDV1vaP4ZXc0,17984 +pylint-4.0.4.dist-info/top_level.txt,sha256=j6Z9i__pIuaiCka6Ul9YIy6yI5aw5QbCtldLvZlMksE,7 +pylint/__init__.py,sha256=rKFMFLSkAqRInnRBeVYb4_HJ5adpcStTPW_zI7GuVaY,3821 +pylint/__main__.py,sha256=dd_IH1XzarvFnYJeY2QqhYpNAcPSHX6nbA4p8JJdwwo,315 +pylint/__pkginfo__.py,sha256=3FSdantqN7TyAIn8i8EyVvBFLZjGsipXTEPRf-x5NJg,1359 +pylint/__pycache__/__init__.cpython-310.pyc,, +pylint/__pycache__/__main__.cpython-310.pyc,, +pylint/__pycache__/__pkginfo__.cpython-310.pyc,, +pylint/__pycache__/constants.cpython-310.pyc,, +pylint/__pycache__/exceptions.cpython-310.pyc,, +pylint/__pycache__/graph.cpython-310.pyc,, +pylint/__pycache__/interfaces.cpython-310.pyc,, +pylint/__pycache__/typing.cpython-310.pyc,, +pylint/checkers/__init__.py,sha256=2Seg87ewDYfL3WQGVo0aPrlMrAlhsafvHWdqKvCk6bw,4235 +pylint/checkers/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/__pycache__/async_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/bad_chained_comparison.cpython-310.pyc,, +pylint/checkers/__pycache__/base_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/clear_lru_cache.cpython-310.pyc,, +pylint/checkers/__pycache__/dataclass_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/deprecated.cpython-310.pyc,, +pylint/checkers/__pycache__/design_analysis.cpython-310.pyc,, +pylint/checkers/__pycache__/dunder_methods.cpython-310.pyc,, +pylint/checkers/__pycache__/ellipsis_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/exceptions.cpython-310.pyc,, +pylint/checkers/__pycache__/format.cpython-310.pyc,, +pylint/checkers/__pycache__/imports.cpython-310.pyc,, +pylint/checkers/__pycache__/lambda_expressions.cpython-310.pyc,, +pylint/checkers/__pycache__/logging.cpython-310.pyc,, +pylint/checkers/__pycache__/match_statements_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/method_args.cpython-310.pyc,, +pylint/checkers/__pycache__/misc.cpython-310.pyc,, +pylint/checkers/__pycache__/modified_iterating_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/nested_min_max.cpython-310.pyc,, +pylint/checkers/__pycache__/newstyle.cpython-310.pyc,, +pylint/checkers/__pycache__/non_ascii_names.cpython-310.pyc,, +pylint/checkers/__pycache__/raw_metrics.cpython-310.pyc,, +pylint/checkers/__pycache__/spelling.cpython-310.pyc,, +pylint/checkers/__pycache__/stdlib.cpython-310.pyc,, +pylint/checkers/__pycache__/strings.cpython-310.pyc,, +pylint/checkers/__pycache__/symilar.cpython-310.pyc,, +pylint/checkers/__pycache__/threading_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/typecheck.cpython-310.pyc,, +pylint/checkers/__pycache__/unicode.cpython-310.pyc,, +pylint/checkers/__pycache__/unsupported_version.cpython-310.pyc,, +pylint/checkers/__pycache__/utils.cpython-310.pyc,, +pylint/checkers/__pycache__/variables.cpython-310.pyc,, +pylint/checkers/async_checker.py,sha256=GAcE3XJTkRmjIWrXTL_7-0ZBhYUHqZfMiAmJCUpoB0I,4026 +pylint/checkers/bad_chained_comparison.py,sha256=3kximBOnJoj41mP0fOrZr-OVNI2-ki-e7fBcfTDnrOg,2238 +pylint/checkers/base/__init__.py,sha256=Tz0g36wveH6w6Iu3JZN2OZI8yHQCBF1eJLw8nIn8IlE,1697 +pylint/checkers/base/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/base/__pycache__/basic_checker.cpython-310.pyc,, +pylint/checkers/base/__pycache__/basic_error_checker.cpython-310.pyc,, +pylint/checkers/base/__pycache__/comparison_checker.cpython-310.pyc,, +pylint/checkers/base/__pycache__/docstring_checker.cpython-310.pyc,, +pylint/checkers/base/__pycache__/function_checker.cpython-310.pyc,, +pylint/checkers/base/__pycache__/pass_checker.cpython-310.pyc,, +pylint/checkers/base/basic_checker.py,sha256=h0s4owQ4iO8vL4qh6R4d22qMxcqOWeESPmnxulfjnvY,40129 +pylint/checkers/base/basic_error_checker.py,sha256=dLA2PMdnsLKmCA02FeZDVtNnYkzXZ_IkUpx4HEOnefo,24227 +pylint/checkers/base/comparison_checker.py,sha256=NoBRs7SoJk11IxBzaeqBNGIAsAY9U2M0wBmowCYpkhU,13876 +pylint/checkers/base/docstring_checker.py,sha256=FTOAtEfQkSsHXGq1tZPvXfwmHdACaaakms4f_D2JG-g,7647 +pylint/checkers/base/function_checker.py,sha256=tJZq0G7PtAMO5JXjwJCEHCUUjAEpqPtGLYg_W3v3eAU,5866 +pylint/checkers/base/name_checker/__init__.py,sha256=VK-zwZj9rKxprzTnoXQGeVDW3Yutnc5UL-6rHvvGmuo,699 +pylint/checkers/base/name_checker/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/base/name_checker/__pycache__/checker.cpython-310.pyc,, +pylint/checkers/base/name_checker/__pycache__/naming_style.cpython-310.pyc,, +pylint/checkers/base/name_checker/checker.py,sha256=zFzpl6kOr6il60RTJluL-tViBwxfSCNNqIjIxtoXzkw,31895 +pylint/checkers/base/name_checker/naming_style.py,sha256=U1Fgx9i3eskySSKHPeE3Hm2WAqfAGELG_ZwOF7qLHow,5911 +pylint/checkers/base/pass_checker.py,sha256=IrTskPQkMYvAQKSoixfY_zKrlDyJMMBh-cC1Wa4E608,1041 +pylint/checkers/base_checker.py,sha256=TaNiXaXDFkiSJ27sQ4OC1amvX26THAFb6XttCuZ1ilE,9221 +pylint/checkers/classes/__init__.py,sha256=V2WMlI77SPVkD1KaSBAaRTx2PgC51rYKOzse8VOw2-8,654 +pylint/checkers/classes/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/classes/__pycache__/class_checker.cpython-310.pyc,, +pylint/checkers/classes/__pycache__/special_methods_checker.cpython-310.pyc,, +pylint/checkers/classes/class_checker.py,sha256=GZpF_ipUiHR9BXe1WVhE4nxXTTJ5AsdA8QDpP1zsb6k,93474 +pylint/checkers/classes/special_methods_checker.py,sha256=VIxi68yQQ_z42uTdFUOFrYX6k8lAW5ym_bymKfcb3XU,15178 +pylint/checkers/clear_lru_cache.py,sha256=wKfHSibt2rKL8p7ITUsCy7ClVzxHD1ru8AkwtYUE7P0,1071 +pylint/checkers/dataclass_checker.py,sha256=3i_4NfSFXpQJRrJcplzY2Khu82ywETEHrwywyws0pRo,4502 +pylint/checkers/deprecated.py,sha256=UDvNQkc8Xy4y56HhF4mleyHZuq2hk4-Va2YPd5mvPcc,11549 +pylint/checkers/design_analysis.py,sha256=AoQ45dKLHCW9CPCM5KRarYE_sA32z4fqLdzJ1uccl_k,24682 +pylint/checkers/dunder_methods.py,sha256=kWECwPubeb0nhaTcqVQ-OyTHwGcX8iAjj_vUgdzl8hg,3892 +pylint/checkers/ellipsis_checker.py,sha256=Qw7l0vN_26OTOpH3yNOTgHQYRRCGF_sUb5enzbbJXpU,2024 +pylint/checkers/exceptions.py,sha256=xdJ5D4rQEjIHUju9T0h2YFtgAlPeBKi2EUPRagPBET8,26499 +pylint/checkers/format.py,sha256=c9uKUtWYfNXbFXcpa809WaAVJSD7VeowioX3Xn7SoCI,28293 +pylint/checkers/imports.py,sha256=ryzEDQepcNVEHcYyRdq2AYB7kglNI8PG5O5zveBgC9c,49657 +pylint/checkers/lambda_expressions.py,sha256=OmzNgzZO34cfOqF4OJz_LkxRYbgwpPrVS3K1bLsw2_0,3603 +pylint/checkers/logging.py,sha256=xs-iY6sqaPQIRbQHR4CxfovfpuOdQptq1lPU0RQ6Bx4,16207 +pylint/checkers/match_statements_checker.py,sha256=3OiruqvpBFaFgHqO64uWGuNf45BNXRph0ZaGKtbcls4,8372 +pylint/checkers/method_args.py,sha256=88dKdFAvs4w5EbX1FhmeuOBWiWEotGsYPBtDtgEzP18,4764 +pylint/checkers/misc.py,sha256=Zh9VdepO2kBzW78KZZbXd-rbamDLf5ze7TjN4reBwew,7059 +pylint/checkers/modified_iterating_checker.py,sha256=BUt1sGRUGn4kKTRnYM7WMuhPme58R793JFunkO7K89o,7578 +pylint/checkers/nested_min_max.py,sha256=dpfLsbxy3YuniUp4k_KBgyIJ6Qnmc1bFFkEkzaA7YIQ,5934 +pylint/checkers/newstyle.py,sha256=r_i-y_cYcDmeMOxA-PFTQhaWBxUbHH3eWiCxTCDaK94,3985 +pylint/checkers/non_ascii_names.py,sha256=xnmfGSwVc2mTzGyMtDBU1E0PK9pkx28w70CwZSDGbPE,7244 +pylint/checkers/raw_metrics.py,sha256=oN_us6e660qb4BIRxl5TkhLcYpOMiI2AKEahqhcOlGU,3705 +pylint/checkers/refactoring/__init__.py,sha256=g8LJprDaWRc6QEVtmz9HvQhGsS6rnwMPe9QxrrMKE7E,1124 +pylint/checkers/refactoring/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/refactoring/__pycache__/implicit_booleaness_checker.cpython-310.pyc,, +pylint/checkers/refactoring/__pycache__/not_checker.cpython-310.pyc,, +pylint/checkers/refactoring/__pycache__/recommendation_checker.cpython-310.pyc,, +pylint/checkers/refactoring/__pycache__/refactoring_checker.cpython-310.pyc,, +pylint/checkers/refactoring/implicit_booleaness_checker.py,sha256=sUiUuD4C82OlLY4viIfPdXUgFMXvRj3b6d6333EApBM,16768 +pylint/checkers/refactoring/not_checker.py,sha256=vvVEcw2TFzoV3UJdFp2vFdfDgu1q8V80-D4RsX3APwU,2976 +pylint/checkers/refactoring/recommendation_checker.py,sha256=XlBtjmbLarVU3uXiotaw5Zleq3nmOnlfNyD9VD-5v8g,18738 +pylint/checkers/refactoring/refactoring_checker.py,sha256=Tl_qjaiEmP4-hv0iyVElJO6WQuPYpXTk0HzTVpj4YHs,101282 +pylint/checkers/spelling.py,sha256=ln2QkDdxjBCCVxKlUA4WPxUnawmTe64i85mtY9dat1c,16334 +pylint/checkers/stdlib.py,sha256=GdpSRiSw4SvpsvRql1Gn0pd4qFREXnbOXDaOr_GYK0Y,37355 +pylint/checkers/strings.py,sha256=SQDASZqpm1PpdpudGnFonNNxiOwoTDWoWvve9dTjanE,44979 +pylint/checkers/symilar.py,sha256=IJgZ7DG1icg1ZevJOeuoH6ekKqwiQZwPfWOlpcSNX6g,33937 +pylint/checkers/threading_checker.py,sha256=H_fy_ySghjCd-wfG4pPMhS9eF6X9593wtDDV7nGo018,1951 +pylint/checkers/typecheck.py,sha256=zUqh4TrfFp78eq3keBJ-_ItsUIxb8LxGKdQ_JYizz_Y,91391 +pylint/checkers/unicode.py,sha256=4U0ABwAxjQoU3Jk7kbdLZuH7zYjcVJafL8Ambu-4k6Y,18474 +pylint/checkers/unsupported_version.py,sha256=gWzlJXbJ4CM7TsjmygzWh5LnX7AVXoz0v7NeyDfdIhE,7745 +pylint/checkers/utils.py,sha256=ydl-fHVeesi5hf3yQcdAnr5gONRbMMXxldWS9gjq_Ro,80376 +pylint/checkers/variables.py,sha256=UNI7OH0cl_6yYsjyJ-jQdKMWCICPMIHf0epuSXeRXwQ,140475 +pylint/config/__init__.py,sha256=Pq5uSrT_lQQe68zth16TBbe_EpzKUci3nKXxzZAFE20,387 +pylint/config/__pycache__/__init__.cpython-310.pyc,, +pylint/config/__pycache__/argument.cpython-310.pyc,, +pylint/config/__pycache__/arguments_manager.cpython-310.pyc,, +pylint/config/__pycache__/arguments_provider.cpython-310.pyc,, +pylint/config/__pycache__/callback_actions.cpython-310.pyc,, +pylint/config/__pycache__/config_file_parser.cpython-310.pyc,, +pylint/config/__pycache__/config_initialization.cpython-310.pyc,, +pylint/config/__pycache__/deprecation_actions.cpython-310.pyc,, +pylint/config/__pycache__/exceptions.cpython-310.pyc,, +pylint/config/__pycache__/find_default_config_files.cpython-310.pyc,, +pylint/config/__pycache__/help_formatter.cpython-310.pyc,, +pylint/config/__pycache__/utils.cpython-310.pyc,, +pylint/config/_breaking_changes/__init__.py,sha256=FNE1P6pTPQU13GtgMGxLRkMxAIVG_VC6Goi5nfgAIYQ,6658 +pylint/config/_breaking_changes/__pycache__/__init__.cpython-310.pyc,, +pylint/config/_pylint_config/__init__.py,sha256=B9J0yKzTWcEqJreLhgFmq0J4lGfz4Fg_GFNASCYO814,571 +pylint/config/_pylint_config/__pycache__/__init__.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/generate_command.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/help_message.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/main.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/setup.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/utils.cpython-310.pyc,, +pylint/config/_pylint_config/generate_command.py,sha256=kvUxyyvZNxKWVbmaxnYcX5Auv4pPra5bhaExDuSr_fM,1718 +pylint/config/_pylint_config/help_message.py,sha256=fLjKdnNZbRindEpGMRux7wTpdBwX3WLIE48om-5VYlE,2007 +pylint/config/_pylint_config/main.py,sha256=taUrBKRaS_N256KQPgvu9ElHLkSWStL5_aL4hfgDwCA,855 +pylint/config/_pylint_config/setup.py,sha256=imOS5tT_5mcmB0IclU1H1LqQZ6qCwskFyZh6Vi6l7-U,1613 +pylint/config/_pylint_config/utils.py,sha256=t8cbd_dQUga6Rt6YuT7aynnWqKXUEGJ73zEdbWFtTic,3558 +pylint/config/argument.py,sha256=A1VGmjAegYJYU_ZK0KHIQLl7hnSCZtpAqWBRqqTsY3Y,14859 +pylint/config/arguments_manager.py,sha256=Jz8UgGTqvCHpMg7-EFDecg7-Q3Qp94OxjDpAYE1AybI,14798 +pylint/config/arguments_provider.py,sha256=EG2Hw8zwbk4QvXCeqBGOYLWNL9xHf_DK8yBeYmdmd7M,2392 +pylint/config/callback_actions.py,sha256=fGPnUppIJR9MM0bEWwcp8RxcsFOaNUcFmt8y7oQ9L8c,13391 +pylint/config/config_file_parser.py,sha256=RhOf_peHAzFDpcahsOsEoIagdNeY8j7c6E8wEKk9fj8,4513 +pylint/config/config_initialization.py,sha256=1IOA61MshdiGhHKENCBgdKphZbMluUKjx51NX2hocnI,7570 +pylint/config/deprecation_actions.py,sha256=LAxHHR6kF0k82rCSTb_BOZT2wOSR027IzMzcS7mb4Yg,2983 +pylint/config/exceptions.py,sha256=GLd4fUNrbIky8ZgQrbOgGWLxXhcs3wlZtOvXjRLVap4,826 +pylint/config/find_default_config_files.py,sha256=WyuViPtEuTpl9ZV3fgbUgCe_qQ1q9oL3x323ZmXQKEY,4634 +pylint/config/help_formatter.py,sha256=Gd4ZYCVFu5VgnIlBLHTm3elByo0-UXNo1lQLJm3JuGo,2583 +pylint/config/utils.py,sha256=VjuKef8rQiWoHkarX3Ir8kB1lW5oP3a02emBXZj601A,8774 +pylint/constants.py,sha256=tP7w-gdZGXHmrAcqT-i6_sALrPpkMJvvQef0as5bPjY,9044 +pylint/exceptions.py,sha256=Ysrp0ddkUlebm2n4lWH8tYyqWdOImyyvdliMlGGbMgo,1726 +pylint/extensions/__init__.py,sha256=QNagpSKuKHwFXh3jUpvZGtFP27OT0NSec_vXfUS-cuk,585 +pylint/extensions/__pycache__/__init__.cpython-310.pyc,, +pylint/extensions/__pycache__/_check_docs_utils.cpython-310.pyc,, +pylint/extensions/__pycache__/bad_builtin.cpython-310.pyc,, +pylint/extensions/__pycache__/broad_try_clause.cpython-310.pyc,, +pylint/extensions/__pycache__/check_elif.cpython-310.pyc,, +pylint/extensions/__pycache__/code_style.cpython-310.pyc,, +pylint/extensions/__pycache__/comparison_placement.cpython-310.pyc,, +pylint/extensions/__pycache__/confusing_elif.cpython-310.pyc,, +pylint/extensions/__pycache__/consider_refactoring_into_while_condition.cpython-310.pyc,, +pylint/extensions/__pycache__/consider_ternary_expression.cpython-310.pyc,, +pylint/extensions/__pycache__/dict_init_mutate.cpython-310.pyc,, +pylint/extensions/__pycache__/docparams.cpython-310.pyc,, +pylint/extensions/__pycache__/docstyle.cpython-310.pyc,, +pylint/extensions/__pycache__/dunder.cpython-310.pyc,, +pylint/extensions/__pycache__/empty_comment.cpython-310.pyc,, +pylint/extensions/__pycache__/eq_without_hash.cpython-310.pyc,, +pylint/extensions/__pycache__/for_any_all.cpython-310.pyc,, +pylint/extensions/__pycache__/magic_value.cpython-310.pyc,, +pylint/extensions/__pycache__/mccabe.cpython-310.pyc,, +pylint/extensions/__pycache__/no_self_use.cpython-310.pyc,, +pylint/extensions/__pycache__/overlapping_exceptions.cpython-310.pyc,, +pylint/extensions/__pycache__/private_import.cpython-310.pyc,, +pylint/extensions/__pycache__/redefined_loop_name.cpython-310.pyc,, +pylint/extensions/__pycache__/redefined_variable_type.cpython-310.pyc,, +pylint/extensions/__pycache__/set_membership.cpython-310.pyc,, +pylint/extensions/__pycache__/typing.cpython-310.pyc,, +pylint/extensions/__pycache__/while_used.cpython-310.pyc,, +pylint/extensions/_check_docs_utils.py,sha256=onbbn32fvHpBbJxMxlIc3qNTcRkp0z_imyrZXJFkb6w,29676 +pylint/extensions/bad_builtin.py,sha256=vyKS9BQ3yElI58n-MHz8oB3_lCMqp-FmedHH_49mJug,2268 +pylint/extensions/broad_try_clause.py,sha256=56N3eMlJ_1cfHA9ek5LpkbIOhnoFk0aC6R8W7S0Pt3c,2268 +pylint/extensions/check_elif.py,sha256=XduStUiTNc4SOSlMmZyD5EPfknZNPfL2_eENK0YX4JY,2149 +pylint/extensions/code_style.py,sha256=P2EA9653eqE34xmKHQXcwHkgjAHiB78FCwfKCGSJL3k,14530 +pylint/extensions/comparison_placement.py,sha256=UTYXBM4jXsOV5VrCqRbP62kYdztGaCjQ42fNsGtqtVM,2362 +pylint/extensions/confusing_elif.py,sha256=ab3tf81CQnRmdxyGU40Fb4pM2On4jsI4YfYlpv6id5k,2049 +pylint/extensions/consider_refactoring_into_while_condition.py,sha256=XYivi6FUHIq47w9-2qVXGGHoJS-4Rk1hSxt9pT-G9fE,3321 +pylint/extensions/consider_ternary_expression.py,sha256=gNm1CrJqLCrCYNcZtTK30uwh-Mu4k7r7YK4UDYnPtrA,1640 +pylint/extensions/dict_init_mutate.py,sha256=3Q3RW0ALETIOXl6epxP7uodh8z9fTNPQdfatApRYheY,1782 +pylint/extensions/docparams.py,sha256=K6A-UiwFkJlg7K61XFdn4L-jt4tDgcH49D8C7xB9KZc,26456 +pylint/extensions/docstyle.py,sha256=wh5S31An6fYB6ZJu3RAhGuHTdMbgO6SrfxWiU4449Bo,2953 +pylint/extensions/dunder.py,sha256=Te-SuvRftTc4JnuA4q4fqRwaMNadr3mWAiPMYpCHhdM,2511 +pylint/extensions/empty_comment.py,sha256=B_NXCNYu_7EJmIrdENpt9ffuQGOnzRqy5TB7KmgZmsY,1963 +pylint/extensions/eq_without_hash.py,sha256=EghMfVWHCJGr-gqY26Qbzw6Do3XuyMkbgn7kTRfhcH8,1470 +pylint/extensions/for_any_all.py,sha256=9KskGf_XLjOCNsdQFDoAgD2Q-Sjk6YcLu1UMs5ridPE,5849 +pylint/extensions/magic_value.py,sha256=_eSTknhC7ZX4ZQWDrPTVnK8mJgHaooo4rN0StuS5CF0,4260 +pylint/extensions/mccabe.py,sha256=oapX2HZjfWRLd2YoAgR0sDZBk200HUNKyCEINpRFy0o,7521 +pylint/extensions/no_self_use.py,sha256=Dnr0IvBcpCPr1s8ghcuZi_yqUWUYLeS981aLXtZ0430,3694 +pylint/extensions/overlapping_exceptions.py,sha256=nO9Bb27sQfokoO8PFB-K3jN5rtK9uZyMAQVehuAIS1M,3270 +pylint/extensions/private_import.py,sha256=JoF_IaSCoV3ffXH9h-ymkxc952ZirwU2NCwWR64CG4g,11194 +pylint/extensions/redefined_loop_name.py,sha256=QvIVd8gD-8FpbnBT4VLXN6h1vszPgb9rJVLVgtcRGFw,3230 +pylint/extensions/redefined_variable_type.py,sha256=ga8WIDw57G6qWiWIY4SV5kzrDlK1RQ3BtkAJRoNxqZI,4105 +pylint/extensions/set_membership.py,sha256=GKnGo_kRnF462QJL15VcRBo-3Ml_HJTG0r8brQXFS-c,1806 +pylint/extensions/typing.py,sha256=ixCaGc0cI71GQ2mq3WGtnYh0Kf_XrNS3G_6-FugwAcU,22622 +pylint/extensions/while_used.py,sha256=IZKtKqTAsbqenOp_Gik4_iEL9dHPSdYbe3MPD8IJLq8,1103 +pylint/graph.py,sha256=gBkYH8T-w0FCBtG-lWzUGkJ_6cth7aecb_CTXXqTAeU,7101 +pylint/interfaces.py,sha256=91EedZoL9a6L76-3620eHeMnFFvtZ3x1LGaAEEdEv2w,1191 +pylint/lint/__init__.py,sha256=YH2V4VbgfBCHkle4vg7uRPKiNKdBoR5AK9xnpLQF4cs,1394 +pylint/lint/__pycache__/__init__.cpython-310.pyc,, +pylint/lint/__pycache__/base_options.cpython-310.pyc,, +pylint/lint/__pycache__/caching.cpython-310.pyc,, +pylint/lint/__pycache__/expand_modules.cpython-310.pyc,, +pylint/lint/__pycache__/message_state_handler.cpython-310.pyc,, +pylint/lint/__pycache__/parallel.cpython-310.pyc,, +pylint/lint/__pycache__/pylinter.cpython-310.pyc,, +pylint/lint/__pycache__/report_functions.cpython-310.pyc,, +pylint/lint/__pycache__/run.cpython-310.pyc,, +pylint/lint/__pycache__/utils.cpython-310.pyc,, +pylint/lint/base_options.py,sha256=wbQAoSdHlDPXoaG9bUFSFODeHGlxacqqy786aYKKGLc,21533 +pylint/lint/caching.py,sha256=mdNDPZBwYWJw17e7_1QvAE_l827KnJfxAL6CfSoJ2xw,2424 +pylint/lint/expand_modules.py,sha256=kkupMfK-onAI0uLusZ_a_taSkKyrE3Z5lpRuUuyp6wU,7190 +pylint/lint/message_state_handler.py,sha256=0x6jM8kjzmhPnMM9YEXKaHIROeI1SaS4s8V6KopW08k,17960 +pylint/lint/parallel.py,sha256=bchzKK-yKYXkGo2s-hfm_zL9Nx_vJG4DpX71Mq_QbUU,6425 +pylint/lint/pylinter.py,sha256=bWgIuTdLx2sADtGZ6krJ0xEutt7mBE_DosL_fl-atmM,52076 +pylint/lint/report_functions.py,sha256=ESzONaMBZRE-wj13RdglQ_YlDvRUFEyKE2_B7MlBSAg,2947 +pylint/lint/run.py,sha256=uemxV_n1-3XMh6mbtPW-Pb7QvHYVhA04hgfUDE56Ltg,9894 +pylint/lint/utils.py,sha256=r4u1nbT1budbfzL-Y-IGIaEKqUrNPo_wzrzj6TZGbOg,3357 +pylint/message/__init__.py,sha256=83tEoCsTjG15vPoEnVW99qg5Lpy1Ee14kwjrX_PK-eQ,632 +pylint/message/__pycache__/__init__.cpython-310.pyc,, +pylint/message/__pycache__/_deleted_message_ids.cpython-310.pyc,, +pylint/message/__pycache__/message.cpython-310.pyc,, +pylint/message/__pycache__/message_definition.cpython-310.pyc,, +pylint/message/__pycache__/message_definition_store.cpython-310.pyc,, +pylint/message/__pycache__/message_id_store.cpython-310.pyc,, +pylint/message/_deleted_message_ids.py,sha256=6xtPh55G_rOwh319gl8a2iNMZPkWzFwujnzQZVlYttY,7578 +pylint/message/message.py,sha256=Qndr3rNqYW5KxEfqRV5wEXO47NVGvElkFH1n2Cc6BL0,2165 +pylint/message/message_definition.py,sha256=Uzbvffv6fuFkxkBHBTZHN3DkSgwIqZTIt5dZUrAiTOg,5013 +pylint/message/message_definition_store.py,sha256=b7uFTDz9nj8KMms2BffjiWZtWJbD6BcymazX2DkRq5M,5083 +pylint/message/message_id_store.py,sha256=27ID6Ueo5qyH8jNScKOOqbAkWkjrSnn-SP3Dbp-0XBo,6370 +pylint/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pylint/pyreverse/__init__.py,sha256=eHl5rdin7-cPqgefwZteuF89noKc-nhavpyH1Y6ppyk,284 +pylint/pyreverse/__pycache__/__init__.cpython-310.pyc,, +pylint/pyreverse/__pycache__/diadefslib.cpython-310.pyc,, +pylint/pyreverse/__pycache__/diagrams.cpython-310.pyc,, +pylint/pyreverse/__pycache__/dot_printer.cpython-310.pyc,, +pylint/pyreverse/__pycache__/inspector.cpython-310.pyc,, +pylint/pyreverse/__pycache__/main.cpython-310.pyc,, +pylint/pyreverse/__pycache__/mermaidjs_printer.cpython-310.pyc,, +pylint/pyreverse/__pycache__/plantuml_printer.cpython-310.pyc,, +pylint/pyreverse/__pycache__/printer.cpython-310.pyc,, +pylint/pyreverse/__pycache__/printer_factory.cpython-310.pyc,, +pylint/pyreverse/__pycache__/utils.cpython-310.pyc,, +pylint/pyreverse/__pycache__/writer.cpython-310.pyc,, +pylint/pyreverse/diadefslib.py,sha256=GG7IT7ChcjQUCE6QMXg3Ur-sA0xliaHHSaZvYyBGpfs,11456 +pylint/pyreverse/diagrams.py,sha256=v8stLuI5ZOwaP8jJG6GZPo2PQ6Ew6ZFvRAq5n7NSUcE,13099 +pylint/pyreverse/dot_printer.py,sha256=oShyureybX_H_IvDE5DTug2vVBXPLrVxHuWN77X52XM,6661 +pylint/pyreverse/inspector.py,sha256=XB7SY5-fhgW9A3muMr_r2hHrkuR5xFGgR1enxF5kPCE,20876 +pylint/pyreverse/main.py,sha256=d4Z21tAhrmazJUig0XroCZhCCcSi6T73Bs-5uN4BmH0,11732 +pylint/pyreverse/mermaidjs_printer.py,sha256=oM0KaAWhyL0wi5tMcMEOtjvgt4aE6T1fJGjGg75JmhM,4508 +pylint/pyreverse/plantuml_printer.py,sha256=Iq4cAsv4DpVzDoAVXf0URt_qiYL9xELaJLnohvlbsaA,3578 +pylint/pyreverse/printer.py,sha256=xCf6OZMSCbJQh1yawoL_k1gpzq1LhAI6FJiRIF446oE,3758 +pylint/pyreverse/printer_factory.py,sha256=YOzS71r89gBWtuJdb35AuQNXqacHTNkppBIFNleDHCY,835 +pylint/pyreverse/utils.py,sha256=hd0feL7pm2OjNGdv6XYxOtzB6NdOCWiZdUOyMDSeC7M,8346 +pylint/pyreverse/writer.py,sha256=RcTBGIUX4jCRBwkS8LwFKGPehR2FPjRa7Hq43-Ue5DI,8038 +pylint/reporters/__init__.py,sha256=w2FTS9pTKhlQyLDhjf-JjJdTvlt3pnSt-FxitNsTDeY,1072 +pylint/reporters/__pycache__/__init__.cpython-310.pyc,, +pylint/reporters/__pycache__/base_reporter.cpython-310.pyc,, +pylint/reporters/__pycache__/collecting_reporter.cpython-310.pyc,, +pylint/reporters/__pycache__/json_reporter.cpython-310.pyc,, +pylint/reporters/__pycache__/multi_reporter.cpython-310.pyc,, +pylint/reporters/__pycache__/progress_reporters.cpython-310.pyc,, +pylint/reporters/__pycache__/reports_handler_mix_in.cpython-310.pyc,, +pylint/reporters/__pycache__/text.cpython-310.pyc,, +pylint/reporters/base_reporter.py,sha256=oBD52xuayLtGk9VthD-VETNnh9QwFCiNsvhCiwCn3G0,3087 +pylint/reporters/collecting_reporter.py,sha256=46dS4dzm2HBe9Lcr5AuQl1kx2OLa83YVpoorqGX5Nig,735 +pylint/reporters/json_reporter.py,sha256=1cjhYjQxmEoxOca55kX6a9-xEvCcu-E-qS0lE3-icN8,6395 +pylint/reporters/multi_reporter.py,sha256=tIceA90VF49orK3RtxBjLEi-VNwK9H-1zwEAbEpgoHI,3771 +pylint/reporters/progress_reporters.py,sha256=llNcEHHb35WVvpjPS5hMl5JL0GgWUQhsUhfDDro_Jsw,1083 +pylint/reporters/reports_handler_mix_in.py,sha256=QPThQi4t7RkiQsZlFQfwXG2c1_Fgghan12ADB1HfEpg,3069 +pylint/reporters/text.py,sha256=oxJScVsNOEI2ljQcBDItYz4vbDJ8ueWebDmRVVEnkJc,9999 +pylint/reporters/ureports/__init__.py,sha256=nLdCdbJuOW6AfeCVl5BZ90ZV_Ms1EOeMcg_xP8C-JqA,320 +pylint/reporters/ureports/__pycache__/__init__.cpython-310.pyc,, +pylint/reporters/ureports/__pycache__/base_writer.cpython-310.pyc,, +pylint/reporters/ureports/__pycache__/nodes.cpython-310.pyc,, +pylint/reporters/ureports/__pycache__/text_writer.cpython-310.pyc,, +pylint/reporters/ureports/base_writer.py,sha256=WcGtCmHJjlp3YT-KRFHLQ6w1KkrVQeQeITfXvrb5WYo,3440 +pylint/reporters/ureports/nodes.py,sha256=OhlNjL1OEZIure-4XhYcnZEUdfFTgwBRHxkvWtdQKJo,5315 +pylint/reporters/ureports/text_writer.py,sha256=4qsrpWU7vr8whu0xbZtgRXzZo7KSaQF-5mZHp8VnEUQ,3616 +pylint/testutils/__init__.py,sha256=PbMNUX0K1Ra_Fwwy4Lxha7xBtBX1ptSpMuCFcJ9oDEA,1319 +pylint/testutils/__pycache__/__init__.cpython-310.pyc,, +pylint/testutils/__pycache__/_run.cpython-310.pyc,, +pylint/testutils/__pycache__/checker_test_case.cpython-310.pyc,, +pylint/testutils/__pycache__/configuration_test.cpython-310.pyc,, +pylint/testutils/__pycache__/constants.cpython-310.pyc,, +pylint/testutils/__pycache__/decorator.cpython-310.pyc,, +pylint/testutils/__pycache__/get_test_info.cpython-310.pyc,, +pylint/testutils/__pycache__/global_test_linter.cpython-310.pyc,, +pylint/testutils/__pycache__/lint_module_test.cpython-310.pyc,, +pylint/testutils/__pycache__/output_line.cpython-310.pyc,, +pylint/testutils/__pycache__/pyreverse.cpython-310.pyc,, +pylint/testutils/__pycache__/reporter_for_tests.cpython-310.pyc,, +pylint/testutils/__pycache__/tokenize_str.cpython-310.pyc,, +pylint/testutils/__pycache__/unittest_linter.cpython-310.pyc,, +pylint/testutils/__pycache__/utils.cpython-310.pyc,, +pylint/testutils/_primer/__init__.py,sha256=ODIorqHjiCK9DXPi1NgBawd46drp6MlKM4-V9CEk7E4,389 +pylint/testutils/_primer/__pycache__/__init__.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/package_to_lint.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer_command.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer_compare_command.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer_prepare_command.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer_run_command.cpython-310.pyc,, +pylint/testutils/_primer/package_to_lint.py,sha256=bUaVe90Cn_o0-ZGDQ2lg5AitLKml3G6z8J5kFykCLP4,4741 +pylint/testutils/_primer/primer.py,sha256=txjjMeYhi6T4rQ3SDOQtlFkEcjZ_1JSgnaREP_tJyn0,4685 +pylint/testutils/_primer/primer_command.py,sha256=ow1w5XXtPvEm56fgA5SnwTgeBfejZv2zcipBS_foGqc,999 +pylint/testutils/_primer/primer_compare_command.py,sha256=dHV04WOMAUN1FQ5cZDIDoMXBkPDTisqhf6VQ9nkw-H0,6989 +pylint/testutils/_primer/primer_prepare_command.py,sha256=2crSGDxfhBEltEc2NH7Rgw_ujw-excceHfEGWSAzjCk,2040 +pylint/testutils/_primer/primer_run_command.py,sha256=AZKyb9w5d3NhWOUUMk5FviJvecqAUKCFVhSBCaHYj3Q,4520 +pylint/testutils/_run.py,sha256=lukrQmA9nD5iEuQ-hVOVgMMXHQu99NCw8FOh1RJF35Y,1430 +pylint/testutils/checker_test_case.py,sha256=jnTFL-m7ueC7BmHymsXnZ7R5cKtVsMpMRgQ4I0a6qbk,3325 +pylint/testutils/configuration_test.py,sha256=kH4h98m8dJyEFwBjGzYgUY1xXG1E7fiYZKlYZY8Oebk,5421 +pylint/testutils/constants.py,sha256=7GaUpGWSP6wFXBXUuaqsfXWpCHp3t2e6_C3CDm7L0jg,1376 +pylint/testutils/decorator.py,sha256=1s06oWObolRBPbwXcYd2fgmHhyBObYuaJTr7RX7kG9k,1262 +pylint/testutils/functional/__init__.py,sha256=Gl5HuUawpyTQWCbydzPzLLrvQVs-wX890rG_vwwLDkg,800 +pylint/testutils/functional/__pycache__/__init__.cpython-310.pyc,, +pylint/testutils/functional/__pycache__/find_functional_tests.cpython-310.pyc,, +pylint/testutils/functional/__pycache__/lint_module_output_update.cpython-310.pyc,, +pylint/testutils/functional/__pycache__/test_file.cpython-310.pyc,, +pylint/testutils/functional/find_functional_tests.py,sha256=8Qb7DlAXUMiZH_Cwm96AxVSK_fPPlj51caMxUxHDwjA,5345 +pylint/testutils/functional/lint_module_output_update.py,sha256=2AE432n8NHDUL47GP_-9eFLnd2vK61uyRuN32cJPHZ0,1529 +pylint/testutils/functional/test_file.py,sha256=u6Pvb7fWESu7IenrvQbiwJY8D7_PlsdldwyX_PHGFKc,4346 +pylint/testutils/get_test_info.py,sha256=wxPeK4mMELPV6FhOav8y5RJY3xwerB55a5NM_2-hjFI,2137 +pylint/testutils/global_test_linter.py,sha256=AAfD8FrniXJW_yPtm79FVCMjEYcJEYMj7V-V_xZamSA,695 +pylint/testutils/lint_module_test.py,sha256=g4zhMciClHi_lPPEa9em2kp6mrFSPWKlkDb8tSuYg_A,13268 +pylint/testutils/output_line.py,sha256=nKvWm8tnqy7nN0arPgFF5GlDnXtkmmQz1IZ_S4Xt7Rc,3994 +pylint/testutils/pyreverse.py,sha256=5M4uqY_gGEgCe7JSlYjFZMYc0Tx7k6t7j39RhUwnm9c,4403 +pylint/testutils/reporter_for_tests.py,sha256=59f73wUKi9-gXSpLT1UyCUnjC0MbAl5lDrVnN6nvVYk,2321 +pylint/testutils/testing_pylintrc,sha256=Y2Tex2VkUOP6dbE8pWQQRcWsTkue8J27y-nBpiAGPLU,264 +pylint/testutils/tokenize_str.py,sha256=TBA3SPx1kQX87AVJkYxCEG_UPID4zD3zIz_Qa6-OyuE,457 +pylint/testutils/unittest_linter.py,sha256=mtO59-zFLN0RoyJeVXByJ9UM9jENNdfJCSZuylhHOq0,2692 +pylint/testutils/utils.py,sha256=-k2ZsWgu5gABhmUvAOERG3L5nT-ShD3fjRZNbfbtWh0,3107 +pylint/typing.py,sha256=Na9VBos05Qx1t7WBq9iwQKeWa_qrXMgFBa_sGJGhD5I,3374 +pylint/utils/__init__.py,sha256=ci5XlmHI6bUyJlvUvLmQJiL0lQC7CsyL6CDeuF_VIvQ,1266 +pylint/utils/__pycache__/__init__.cpython-310.pyc,, +pylint/utils/__pycache__/ast_walker.cpython-310.pyc,, +pylint/utils/__pycache__/docs.cpython-310.pyc,, +pylint/utils/__pycache__/file_state.cpython-310.pyc,, +pylint/utils/__pycache__/linterstats.cpython-310.pyc,, +pylint/utils/__pycache__/pragma_parser.cpython-310.pyc,, +pylint/utils/__pycache__/utils.cpython-310.pyc,, +pylint/utils/ast_walker.py,sha256=blguZYCwdmk20z22B0mZmw4FxJxzB5RX2M_jPhOfJIY,3959 +pylint/utils/docs.py,sha256=pxGJ1bV9-2y8gR53rPEprwvTu5lGSo_VLW7Dha74X6o,3406 +pylint/utils/file_state.py,sha256=orrjTq59eGm9IGpasxuab7hrxwaqebuhS5UCwlX7Hes,9628 +pylint/utils/linterstats.py,sha256=Bv2uxJzM_XLyJGW7XpyvSOeWBmxVVDjG2A53OjGOVjM,13056 +pylint/utils/pragma_parser.py,sha256=VeSO93CgYtq7BpjI0qjbq2vZMPYv_6U41xx9O1A_CWk,5052 +pylint/utils/utils.py,sha256=xxg1jpCC9Rc0VyKj33R3LBMVCgPRlvS0jMXEIyjI9EA,10883 diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/REQUESTED b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL new file mode 100644 index 0000000..e7fa31b --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt new file mode 100644 index 0000000..09401fa --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt @@ -0,0 +1,5 @@ +[console_scripts] +pylint = pylint:run_pylint +pylint-config = pylint:_run_pylint_config +pyreverse = pylint:run_pyreverse +symilar = pylint:run_symilar diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt new file mode 100644 index 0000000..146e1b9 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt @@ -0,0 +1,699 @@ +# This file is autocompleted by 'contributors-txt', +# using the configuration in 'script/.contributors_aliases.json'. +# Do not add new persons manually and only add information without +# using '-' as the line first character. +# Please verify that your change are stable if you modify manually. + +Ex-maintainers +-------------- +- Claudiu Popa +- Sylvain Thénault : main author / maintainer +- Torsten Marek + + +Maintainers +----------- +- Pierre Sassoulas +- Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> +- Jacob Walls +- Marc Mueller <30130371+cdce8p@users.noreply.github.com> +- Hippo91 +- Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> +- Andreas Finkler <3929834+DudeNr33@users.noreply.github.com> +- Matus Valo +- Dani Alcala <112832187+clavedeluna@users.noreply.github.com> +- Łukasz Rogalski +- Nick Drozd : performance improvements to astroid +- Ashley Whetter +- Bryce Guinta +- Yu Shao, Pang <36848472+yushao2@users.noreply.github.com> +- Dimitri Prybysh + * multiple-imports, not-iterable, not-a-mapping, various patches. +- Roy Williams (Lyft) + * added check for implementing __eq__ without implementing __hash__, + * Added Python 3 check for accessing Exception.message. + * Added Python 3 check for calling encode/decode with invalid codecs. + * Added Python 3 check for accessing sys.maxint. + * Added Python 3 check for bad import statements. + * Added Python 3 check for accessing deprecated methods on the 'string' module, + various patches. +- Florian Bruhin +- Arianna Yang + + +Contributors +------------ + +We would not be here without folks that contributed patches, pull requests, +issues and their time to pylint. We're incredibly grateful to all of these +contributors: + +- Emile Anclin (Logilab): python 3 support +- Michal Nowikowski : + * wrong-spelling-in-comment + * wrong-spelling-in-docstring + * parallel execution on multiple CPUs +- Julthep Nandakwang +- Bruno Daniel : check_docs extension. +- Sushobhit <31987769+sushobhit27@users.noreply.github.com> (sushobhit27) + * Added new check 'comparison-with-itself'. + * Added new check 'useless-import-alias'. + * Added support of annotations in missing-type-doc and missing-return-type-doc. + * Added new check 'comparison-with-callable'. + * Removed six package dependency. + * Added new check 'chained-comparison'. + * Added new check 'useless-object-inheritance'. +- Brett Cannon : + * Port source code to be Python 2/3 compatible + * Python 3 checker +- Laura Médioni (Logilab, on behalf of the CNES): + * misplaced-comparison-constant + * no-classmethod-decorator + * no-staticmethod-decorator + * too-many-nested-blocks, + * too-many-boolean-expressions + * unneeded-not + * wrong-import-order + * ungrouped-imports, + * wrong-import-position + * redefined-variable-type +- Harutaka Kawamura +- Alexandre Fayolle (Logilab): TkInter gui, documentation, debian support +- Ville Skyttä +- Zen Lee <53538590+zenlyj@users.noreply.github.com> +- Julien Cristau (Logilab): python 3 support +- Moisés López <6644187+moylop260@users.noreply.github.com>: + * Support for deprecated-modules in modules not installed, + * Refactor wrong-import-order to integrate it with `isort` library + * Add check too-complex with mccabe for cyclomatic complexity + * Refactor wrong-import-position to skip try-import and nested cases + * Add consider-merging-isinstance, superfluous-else-return + * Fix consider-using-ternary for 'True and True and True or True' case + * Add bad-docstring-quotes and docstring-first-line-empty + * Add missing-timeout + * Fix false negative for `deprecated-module` when a `__import__` method is used instead of `import` sentence +- Adrien Di Mascio +- Frank Harrison (doublethefish) +- Pierre-Yves David +- David Shea : invalid sequence and slice index +- Gunung P. Wibisono <55311527+gunungpw@users.noreply.github.com> +- Derek Gustafson +- Cezar Elnazli : deprecated-method +- Joseph Young <80432516+jpy-git@users.noreply.github.com> (jpy-git) +- Tim Martin +- Ollie <46904826+ollie-iterators@users.noreply.github.com> +- Julian Grimm <51880314+Julfried@users.noreply.github.com> +- Tushar Sadhwani (tusharsadhwani) +- Nicolas Chauvat +- orSolocate <38433858+orSolocate@users.noreply.github.com> +- Radu Ciorba : not-context-manager and confusing-with-statement warnings. +- Holger Peters +- Cosmin Poieană : unichr-builtin and improvements to bad-open-mode. +- Yilei "Dolee" Yang +- Steven Myint : duplicate-except. +- Peter Kolbus (Garmin) +- Luigi Bertaco Cristofolini (luigibertaco) +- Glenn Matthews : + * autogenerated documentation for optional extensions, + * bug fixes and enhancements for docparams (née check_docs) extension +- crazybolillo +- correctmost <134317971+correctmost@users.noreply.github.com> +- Vlad Temian : redundant-unittest-assert and the JSON reporter. +- Julien Jehannet +- Boris Feld +- Anthony Sottile +- Andrew Haigh (nelfin) +- Robert Hofer +- Pedro Algarvio (s0undt3ch) +- Julien Palard +- Hugo van Kemenade +- David Liu (david-yz-liu) +- Dan Goldsmith : support for msg-template in HTML reporter. +- Buck Evan +- Mariatta Wijaya + * Added new check `logging-fstring-interpolation` + * Documentation typo fixes +- Jakub Wilk +- Eli Fine (eli88fine): Fixed false positive duplicate code warning for lines with symbols only +- Émile Crater +- Pavel Roskin +- David Gilman +- へーさん +- Thomas Hisch +- Marianna Polatoglou : minor contribution for wildcard import check +- Manuel Vázquez Acosta +- Luis Escobar (Vauxoo): Add bad-docstring-quotes and docstring-first-line-empty +- Lucas Cimon +- Konstantina Saketou <56515303+ksaketou@users.noreply.github.com> +- Konstantin +- Jim Robertson +- Ethan Leba +- Enji Cooper +- Drum Ogilvie +- David Lindquist : logging-format-interpolation warning. +- Daniel Harding +- Anthony Truchet +- Alexander Todorov : + * added new error conditions to 'bad-super-call', + * Added new check for incorrect len(SEQUENCE) usage, + * Added new extension for comparison against empty string constants, + * Added new extension which detects comparing integers to zero, + * Added new useless-return checker, + * Added new try-except-raise checker +- theirix +- Téo Bouvard +- Sviatoslav Sydorenko +- Stavros Ntentos <133706+stdedos@users.noreply.github.com> +- Nicolas Boulenguez +- Mihai Balint +- Mark Bell +- Levi Gruspe +- Jakub Kuczys +- Hornwitser : fix import graph +- Fureigh +- David Douard +- Daniel Balparda (Google): GPyLint maintainer (Google's pylint variant) +- Christian Clauss +- Bastien Vallet (Djailla) +- Aru Sahni : Git ignoring, regex-based ignores +- Andreas Freimuth : fix indentation checking with tabs +- Alexandru Coman +- jpkotta +- Thomas Grainger +- Takahide Nojima +- Taewon D. Kim +- Sneaky Pete +- Sergey B Kirpichev +- Sandro Tosi : Debian packaging +- Rogdham +- Rene Zhang +- Paul Lichtenberger +- Or Bahari +- Mr. Senko +- Mike Frysinger +- Martin von Gagern (Google): Added 'raising-format-tuple' warning. +- Martin Vielsmaier +- Martin Pool (Google): + * warnings for anomalous backslashes + * symbolic names for messages (like 'unused') + * etc. +- Martin Bašti + * Added new check for shallow copy of os.environ + * Added new check for useless `with threading.Lock():` statement +- Marcus Näslund (naslundx) +- Marco Pernigotti <7657251+mpernigo@users.noreply.github.com> +- Marco Forte +- James Addison <55152140+jayaddison@users.noreply.github.com> +- Ionel Maries Cristian +- Gergely Kalmár +- Damien Baty +- Benjamin Drung : contributing Debian Developer +- Anubhav <35621759+anubh-v@users.noreply.github.com> +- Antonio Quarta +- Andrew J. Simmons +- Alvaro Frias +- Alexey Pelykh +- Alex Prabhat Bara +- wtracy +- jessebrennan +- chohner +- aatle <168398276+aatle@users.noreply.github.com> +- Tiago Honorato <61059243+tiagohonorato@users.noreply.github.com> +- Steven M. Vascellaro +- Robin Tweedie <70587124+robin-wayve@users.noreply.github.com> +- Roberto Leinardi : PyCharm plugin maintainer +- Ricardo Gemignani +- Piotr Idzik <65706193+vil02@users.noreply.github.com> +- Pieter Engelbrecht +- Philipp Albrecht (pylbrecht) +- Nicolas Dickreuter +- Nick Bastin +- Nathaniel Manista : suspicious lambda checking +- Maksym Humetskyi (mhumetskyi) + * Fixed ignored empty functions by similarities checker with "ignore-signatures" option enabled + * Ignore function decorators signatures as well by similarities checker with "ignore-signatures" option enabled + * Ignore class methods and nested functions signatures as well by similarities checker with "ignore-signatures" option enabled +- Kylian +- Konstantin Manna +- Kai Mueller <15907922+kasium@users.noreply.github.com> +- Joshua Cannon +- John Leach +- James Morgensen : ignored-modules option applies to import errors. +- Jaehoon Hwang (jaehoonhwang) +- Huw Jones +- Gideon <87426140+GideonBear@users.noreply.github.com> +- Ganden Schaffner +- Frost Ming +- Federico Bond +- Erik Wright +- Erik Eriksson : Added overlapping-except error check. +- Emmanuel Ferdman +- Dave Bunten +- Daniel Wang +- Daniel Mouritzen +- Dan Hemberger <846186+hemberger@users.noreply.github.com> +- Chris Rebert : unidiomatic-typecheck. +- Aurelien Campeas +- Alexander Pervakov +- Alain Leufroy +- Akhil Kamat +- Adam Williamson +- Aaron Liu +- xmo-odoo +- tbennett0 +- purajit <7026198+purajit@users.noreply.github.com> +- omarandlorraine <64254276+omarandlorraine@users.noreply.github.com> +- craig-sh +- bernie gray +- azinneck0485 <123660683+azinneck0485@users.noreply.github.com> +- Wing Lian +- Wes Turner (Google): added new check 'inconsistent-quotes' +- Tyler Thieding +- Tobias Hernstig <30827238+thernstig@users.noreply.github.com> +- Smixi +- Simu Toni +- Sergei Lebedev <185856+superbobry@users.noreply.github.com> +- Scott Worley +- Saugat Pachhai +- Samuel FORESTIER +- Rémi Cardona +- Ryan Ozawa +- Roger Sheu <78449574+rogersheu@users.noreply.github.com> +- Raphael Gaschignard +- Ram Rachum (cool-RR) +- Radostin Stoyanov +- Peter Bittner +- Paul Renvoisé +- PHeanEX +- Omega Weapon +- Nikolai Kristiansen +- Nick Pesce +- Nedelcu Ioan-Andrei <138256980+nedelcu-ioan@users.noreply.github.com> +- Nathan Marrow +- Mikhail Fesenko +- Matthew Suozzo +- Matthew Beckers <17108752+mattlbeck@users.noreply.github.com> (mattlbeck) +- Mark Roman Miller : fix inline defs in too-many-statements +- MalanB +- Mads Kiilerich +- Maarten ter Huurne +- Lefteris Karapetsas +- LCD 47 +- Jérome Perrin +- Justin Li +- John Kirkham +- Jens H. Nielsen +- Jake Lishman +- Ioana Tagirta : fix bad thread instantiation check +- Ikraduya Edian : Added new checks 'consider-using-generator' and 'use-a-generator'. +- Hugues Bruant +- Hashem Nasarat +- Harut +- Grygorii Iermolenko +- Grizzly Nyo +- Gabriel R. Sezefredo : Fixed "exception-escape" false positive with generators +- Filipe Brandenburger +- Fantix King (UChicago) +- Eric McDonald <221418+emcd@users.noreply.github.com> +- Elias Dorneles : minor adjust to config defaults and docs +- Elazrod56 +- Edward K. Ream +- Derek Harland +- David Pursehouse +- Daniel Miller +- Christoph Blessing <33834216+cblessing24@users.noreply.github.com> +- Chris Murray +- Chris Lamb +- Charles Hebert +- Carli Freudenberg (CarliJoy) + * Fixed issue 5281, added Unicode checker + * Improve non-ascii-name checker +- Bruce Dawson +- Brian Shaginaw : prevent error on exception check for functions +- Benny Mueller +- Ben James +- Ben Green +- Batuhan Taskaya +- Artem Yurchenko +- Alexander Kapshuna +- Akshay Choudhary <153769403+Akshay9715@users.noreply.github.com> +- Adam Parkin +- 谭九鼎 <109224573@qq.com> +- Łukasz Sznuk +- zasca +- y2kbugger +- vinnyrose +- ttenhoeve-aa +- thinwybk +- temyurchenko <44875844+temyurchenko@users.noreply.github.com> +- syutbai +- sur.la.route <17788706+christopherpickering@users.noreply.github.com> +- sdet_liang +- pavan-msys <149513767+pavan-msys@users.noreply.github.com> +- paschich +- oittaa <8972248+oittaa@users.noreply.github.com> +- nyabkun <75878387+nyabkun@users.noreply.github.com> +- nhdsd +- moxian +- mar-chi-pan +- lrjball <50599110+lrjball@users.noreply.github.com> +- levon-d +- laike9m +- kyoto7250 <50972773+kyoto7250@users.noreply.github.com> +- kriek +- kdestin <101366538+kdestin@users.noreply.github.com> +- jaydesl <35102795+jaydesl@users.noreply.github.com> +- jab +- gracejiang16 <70730457+gracejiang16@users.noreply.github.com> +- glmdgrielson <32415403+glmdgrielson@users.noreply.github.com> +- glegoux +- gaurikholkar +- flyingbot91 +- fly +- fahhem +- fadedDexofan +- epenet <6771947+epenet@users.noreply.github.com> +- danields +- cosven +- cordis-dev +- cherryblossom <31467609+cherryblossom000@users.noreply.github.com> +- bluesheeptoken +- anatoly techtonik +- amelenty +- akirchhoff-modular +- agutole +- Zeckie <49095968+Zeckie@users.noreply.github.com> +- Zeb Nicholls + * Made W9011 compatible with 'of' syntax in return types +- Yuval Langer +- Yury Gribov +- Yuri Bochkarev : Added epytext support to docparams extension. +- Youngsoo Sung +- Yory <39745367+yory8@users.noreply.github.com> +- Yoichi Nakayama +- Yeting Li (yetingli) +- Yannack +- Yann Dirson +- Yang Yang +- Xi Shen +- Winston H <56998716+winstxnhdw@users.noreply.github.com> +- Will Shanks +- Viorel Știrbu : intern-builtin warning. +- VictorT +- Victor Jiajunsu <16359131+jiajunsu@users.noreply.github.com> +- ViRuSTriNiTy +- Val Lorentz +- Ulrich Eckhardt +- Udi Fuchs +- Trevor Bekolay + * Added --list-msgs-enabled command +- Tomer Chachamu : simplifiable-if-expression +- Tomasz Michalski +- Tomasz Magulski +- Tom +- Tim Hatch +- Tim Gates +- Tianyu Chen <124018391+UTsweetyfish@users.noreply.github.com> +- Théo Battrel +- Thomas Benhamou +- Theodore Ni <3806110+tjni@users.noreply.github.com> +- Tanvi Moharir <74228962+tanvimoharir@users.noreply.github.com>: Fix for invalid toml config +- T.Rzepka +- Svetoslav Neykov +- SubaruArai <78188579+SubaruArai@users.noreply.github.com> +- Stéphane Wirtel : nonlocal-without-binding +- Stephen Longofono <8992396+SLongofono@users.noreply.github.com> +- Stephane Odul <1504511+sodul@users.noreply.github.com> +- Stanislav Levin +- Sorin Sbarnea +- Slavfox +- Skip Montanaro +- Sigurd Spieckermann <2206639+sisp@users.noreply.github.com> +- Shiv Venkatasubrahmanyam +- Sebastian Müller +- Sayyed Faisal Ali <80758388+C0DE-SLAYER@users.noreply.github.com> +- Sasha Bagan +- Sardorbek Imomaliev +- Santiago Castro +- Samuel Freilich (sfreilich) +- Sam Vermeiren <88253337+PaaEl@users.noreply.github.com> +- Ryan McGuire +- Ry4an Brase +- Ruro +- Roshan Shetty +- Roman Ivanov +- Robert Schweizer +- Reverb Chu +- Renat Galimov +- Rebecca Turner (9999years) +- Randall Leeds +- Ranadheer Gorrepati <35244169+ranadheerg@users.noreply.github.com> +- Ramon Saraiva +- Ramiro Leal-Cavazos (ramiro050): Fixed bug preventing pylint from working with Emacs tramp +- RSTdefg <34202999+RSTdefg@users.noreply.github.com> +- R. N. West <98110034+rnwst@users.noreply.github.com> +- Qwiddle13 <32040075+Qwiddle13@users.noreply.github.com> +- Quentin Young +- Prajwal Borkar +- Petr Pulc : require whitespace around annotations +- Peter Dawyndt +- Peter Dave Hello +- Peter Aronoff +- Paul Cochrane +- Patrik +- Pascal Corpet +- Pablo Galindo Salgado + * Fix false positive 'Non-iterable value' with async comprehensions. +- Osher De Paz +- Oisín Moran +- Obscuron +- Noam Yorav-Raphael +- Noah-Agnel <138210920+Noah-Agnel@users.noreply.github.com> +- Nir Soffer +- Niko Wenselowski +- Nikita Sobolev +- Nick Smith +- Neowizard +- Ned Batchelder +- Natalie Serebryakova +- Naglis Jonaitis <827324+naglis@users.noreply.github.com> +- Moody +- Mitchell Young : minor adjustment to docparams +- Mitar +- Ming Lyu +- Mikhail f. Shiryaev +- Mike Fiedler (miketheman) +- Mike Bryant +- Mike Bernard +- Michka Popoff +- Michal Vasilek +- Michael Scott Cuthbert +- Michael Kefeder +- Michael K +- Michael Hudson-Doyle +- Michael Giuffrida +- Melvin Hazeleger <31448155+melvio@users.noreply.github.com> +- Meltem Kenis +- Mehdi Drissi +- Matěj Grabovský +- Matthijs Blom <19817960+MatthijsBlom@users.noreply.github.com> +- Matej Spiller Muys +- Matej Marušák +- Marzuk Rashid +- Markus Siebenhaar <41283549+siehar@users.noreply.github.com> +- Marco Edward Gorelli : Documented Jupyter integration +- Marcin Kurczewski (rr-) +- Maik Röder +- Lumír 'Frenzy' Balhar +- Ludovic Aubry +- Louis Sautier +- Lorena Buciu <46202743+lorena-b@users.noreply.github.com> +- Logan Miller <14319179+komodo472@users.noreply.github.com> +- Kári Tristan Helgason +- Kurian Benoy <70306694+kurianbenoy-aot@users.noreply.github.com> +- Krzysztof Czapla +- Kraig Brockschmidt +- Kound +- KotlinIsland <65446343+KotlinIsland@users.noreply.github.com> +- Kosarchuk Sergey +- Konrad Weihmann <46938494+priv-kweihmann@users.noreply.github.com> +- Kian Meng, Ang +- Kevin Phillips +- Kevin Jing Qiu +- Kenneth Schackart +- Kayran Schmidt <59456929+yumasheta@users.noreply.github.com> +- Karthik Nadig +- Jürgen Hermann +- Josselin Feist +- Jonathan Kotta +- John Paraskevopoulos : add 'differing-param-doc' and 'differing-type-doc' +- John McGehee +- John Gabriele +- John Belmonte +- Johannes Maron +- Joffrey Mander +- Jochen Preusche +- Jeroen Seegers : + * Fixed `toml` dependency issue +- Jeremy Fleischman +- Jason Owen +- Jason Lau +- Jared Garst +- Jared Deckard +- Janne Rönkkö +- Jamie Scott +- James Sinclair +- James M. Allen +- James Lingard +- James Broadhead +- Jakub Kulík +- Jakob Normark +- Jacques Kvam +- Jace Browning : updated default report format with clickable paths +- JZ +- JT Olds +- Iggy Eom +- Ige-kun <178478713+Ige-kun@users.noreply.github.com> +- Hayden Richards <62866982+SupImDos@users.noreply.github.com> + * Fixed "no-self-use" for async methods + * Fixed "docparams" extension for async functions and methods +- Harshil <37377066+harshil21@users.noreply.github.com> +- Harry +- Gwanbin Park +- Grégoire <96051754+gregoire-mullvad@users.noreply.github.com> +- Grant Welch +- Giuseppe Valente +- Gary Tyler McLeod +- Felix von Drigalski +- Felix Preuschoff <37065638+felixp98@users.noreply.github.com> +- Fabrice Douchant +- Fabio Natali +- Fabian Damken +- Eric Froemling +- Emmanuel Chaudron +- Elizabeth Bott <52465744+elizabethbott@users.noreply.github.com> +- Ekin Dursun +- Eisuke Kawashima +- Edgemaster +- Eddie Darling +- Drew Risinger +- Dr. Nick +- Don Kirkby +- Don Jayamanne +- Dominic Lavery +- Dmytro Kyrychuk +- Dionisio E Alonso +- DetachHead <57028336+DetachHead@users.noreply.github.com> +- Dennis Keck <26092524+fellhorn@users.noreply.github.com> +- Denis Laxalde +- David Lawson +- David Cain +- Danny Hermes +- Daniele Procida +- Daniela Plascencia +- Daniel Werner +- Daniel R. Neal (danrneal) +- Daniel Draper +- Daniel Dorani (doranid) +- Daniel Brookman <53625739+dbrookman@users.noreply.github.com> +- Dan Garrette +- Damien Nozay +- Cubicpath +- Craig Citro +- Cosmo +- Clément Schreiner +- Clément Pit-Claudel +- Christopher Zurcher +- ChandanChainani +- Carl Crowder : don't evaluate the value of arguments for 'dangerous-default-value' +- Carey Metcalfe : demoted `try-except-raise` from error to warning +- Cameron Olechowski +- Calin Don +- Caio Carrara +- C.A.M. Gerlach +- Bruno P. Kinoshita +- Brice Chardin +- Brian C. Lane +- Brandon W Maister +- BioGeek +- Berker ŞAL +- Benjamin Partzsch <32679788+bnjmnp@users.noreply.github.com> +- Benjamin Graham +- Benedikt Morbach +- Ben Greiner +- Barak Shoshany +- Banjamin Freeman +- Ayushi Kotiyal <70513726+Ayushikotiyal@users.noreply.github.com> +- Avram Lubkin +- Athos Ribeiro : Fixed dict-keys-not-iterating false positive for inverse containment checks +- Arun Persaud +- Arthur Lutz +- Antonio Ossa +- Antonio Gámiz Delgado <73933988+antoniogamizbadger@users.noreply.github.com> +- Anthony VEREZ +- Anthony Tan +- Anthony Foglia (Google): Added simple string slots check. +- Anentropic +- Andy Young +- Andy Palmer <25123779+ninezerozeronine@users.noreply.github.com> +- Andrzej Klajnert +- Andrew Howe +- Andres Perez Hortal +- Andre Hora +- Aman Salwan <121633121+AmanSal1@users.noreply.github.com> +- Alok Singh <8325708+alok@users.noreply.github.com> +- Allan Chandler <95424144+allanc65@users.noreply.github.com> (allanc65) + * Fixed issue 5452, false positive missing-param-doc for multi-line Google-style params +- Alex Waygood +- Alex Mor <5476113+nashcontrol@users.noreply.github.com> +- Alex Jurkiewicz +- Alex Hearn +- Alex Fortin +- Aleksander Mamla +- Alan Evangelista +- Alan Chan +- Aivar Annamaa +- Aidan Haase <44787650+haasea@users.noreply.github.com> +- Ahirnish Pareek : 'keyword-arg-before-var-arg' check +- Agustin Marquez +- Adrian Chirieac +- Aditya Gupta (adityagupta1089) + * Added ignore_signatures to duplicate checker +- Adam Tuft <73994535+adamtuft@users.noreply.github.com> +- Adam Dangoor +- 243f6a88 85a308d3 <33170174+243f6a8885a308d313198a2e037@users.noreply.github.com> + + +Co-Author +--------- +The following persons were credited manually but did not commit themselves +under this name, or we did not manage to find their commits in the history. + +- Agustin Toledo +- Amaury Forgeot d'Arc: check names imported from a module exists in the module +- Anthony Tan +- Axel Muller +- Benjamin Niemann: allow block level enabling/disabling of messages +- Bernard Nauwelaerts +- Bill Wendling +- Brian van den Broek: windows installation documentation +- Craig Henriques +- D. Alphus (Alphadelta14) +- Daniil Kharkov +- Eero Vuojolahti +- Fabio Zadrozny +- Gauthier Sebaux +- James DesLauriers +- manderj +- Mirko Friedenhagen +- Nicholas Smith +- Nuzula H. Yudaka (Nuzhuka) +- Pek Chhan +- Peter Hammond +- Pierre Rouleau +- Richard Goodman: simplifiable-if-expression (with Tomer Chachamu) +- Sebastian Ulrich +- Takashi Hirashima +- Thomas Snowden: fix missing-docstring for inner functions +- Wolfgang Grafen +- Yannick Brehon diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE new file mode 100644 index 0000000..8c4c849 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt new file mode 100644 index 0000000..7fb0ea1 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt @@ -0,0 +1 @@ +pylint diff --git a/.venv/lib/python3.10/site-packages/pylint/__init__.py b/.venv/lib/python3.10/site-packages/pylint/__init__.py new file mode 100644 index 0000000..3752a02 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/__init__.py @@ -0,0 +1,119 @@ +# 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__", + "modify_sys_path", + "run_pylint", + "run_pyreverse", + "run_symilar", + "version", +] + +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 + + sys.exit(PyreverseRun(argv or sys.argv[1:]).run()) + + +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.symilar import Run as SymilarRun + + SymilarRun(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) + + +def _catch_valueerror(unraisable: sys.UnraisableHookArgs) -> None: # pragma: no cover + """Overwrite sys.unraisablehook to catch incorrect ValueError. + + Python 3.12 introduced changes that sometimes cause astroid to emit ValueErrors + with 'generator already executing'. Fixed in Python 3.12.3 and 3.13. + + https://github.com/pylint-dev/pylint/issues/9138 + """ + if ( + isinstance(unraisable.exc_value, ValueError) + and unraisable.exc_value.args[0] == "generator already executing" + ): + return + + sys.__unraisablehook__(unraisable) + + +if (3, 12, 0) <= sys.version_info[:3] < (3, 12, 3) or sys.version_info >= (3, 12, 5): + sys.unraisablehook = _catch_valueerror + + +version = __version__ diff --git a/.venv/lib/python3.10/site-packages/pylint/__main__.py b/.venv/lib/python3.10/site-packages/pylint/__main__.py new file mode 100644 index 0000000..448ac55 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/__main__.py @@ -0,0 +1,10 @@ +#!/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() diff --git a/.venv/lib/python3.10/site-packages/pylint/__pkginfo__.py b/.venv/lib/python3.10/site-packages/pylint/__pkginfo__.py new file mode 100644 index 0000000..a8d436b --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/__pkginfo__.py @@ -0,0 +1,43 @@ +# 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__ = "4.0.4" + + +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__) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/__init__.py b/.venv/lib/python3.10/site-packages/pylint/checkers/__init__.py new file mode 100644 index 0000000..78f453a --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/__init__.py @@ -0,0 +1,140 @@ +# 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 from and , + 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"), + ] + + # pylint: disable=possibly-used-before-assignment + 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", + "BaseRawFileChecker", + "BaseTokenChecker", + "DeprecatedMixin", + "initialize", + "register_plugins", +] diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py new file mode 100644 index 0000000..7c61f7f --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py @@ -0,0 +1,97 @@ +# 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 +import astroid.bases +import astroid.exceptions +from astroid import nodes + +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: + match inferred := checker_utils.safe_infer(ctx_mgr): + case _ if not inferred: + continue + case nodes.AsyncFunctionDef(): + # Check if we are dealing with a function decorated + # with contextlib.asynccontextmanager. + if decorated_with(inferred, self._async_generators): + continue + case astroid.bases.AsyncGenerator(): + # Check if we are dealing with a function decorated + # with contextlib.asynccontextmanager. + if decorated_with(inferred.parent, self._async_generators): + continue + case _: + 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)) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py b/.venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py new file mode 100644 index 0000000..2e19121 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py @@ -0,0 +1,60 @@ +# 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)) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py new file mode 100644 index 0000000..355697c --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py @@ -0,0 +1,50 @@ +# 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__ = [ + "KNOWN_NAME_TYPES_WITH_STYLE", + "AnyStyle", + "CamelCaseStyle", + "NameChecker", + "NamingStyle", + "PascalCaseStyle", + "SnakeCaseStyle", + "UpperCaseStyle", +] + +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.function_checker import FunctionChecker +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)) + linter.register_checker(FunctionChecker(linter)) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py new file mode 100644 index 0000000..774b7e8 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py @@ -0,0 +1,962 @@ +# 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 bases, 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"): + 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, + 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, 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, nodes.TypeAlias) + ) + ): + 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, nodes.TryStar)) + 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") + 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 + match call.func: + case nodes.Attribute(expr=nodes.Call()): + # Chained call, the intermediate call might + # return something else (but we don't check that, yet). + return + + 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.keywords, node.args.kwarg): + return + elif call.keywords: + return + + if node.args.vararg: + if self._has_variadic_argument(call.starargs, node.args.vararg): + return + elif call.starargs: + 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() + match call_node.func.expr: + case nodes.Call(func=nodes.Name(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()): + match name: + case "exec": + self.add_message("exec-used", node=node) + case "reversed": + self._check_reversed(node) + case "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.""" + match node.test: + case nodes.Tuple(elts=elts) if len(elts) > 0: + self.add_message("assert-on-tuple", node=node, confidence=HIGH) + case nodes.Const(value=str() as val): + when = "never" if val else "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: + match k: + case nodes.Const(): + key = k.value + case nodes.Attribute(): + key = k.as_string() + case _: + 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: + match argument: + case util.UninferableBase(): + return + case 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 + + case nodes.List() | nodes.Tuple(): + return + + case astroid.Instance() if not self._py38_plus: + # 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 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 itertools.pairwise(pairs): + 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 + + match node.value: + case nodes.Name(): + if len(targets) != 1: + return + rhs_names = [node.value] + case 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]) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py new file mode 100644 index 0000000..a4e9208 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py @@ -0,0 +1,647 @@ +# 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 + +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__",)) +FORWARD_REF_QNAME = {"typing.ForwardRef", "annotationlib.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 (nodes.Break): the break node of interest. + + Returns: + nodes.For or nodes.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 (nodes.For, nodes.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 + + +def _extract_register_target(dec: nodes.NodeNG) -> nodes.NodeNG | None: + """ + If decorator `dec` looks like `@func.register(...)` or `@func.register`, + return the `func` target node (Name or Attribute). Otherwise return None. + """ + if isinstance(dec, nodes.Call): + func_part = dec.func + if isinstance(func_part, nodes.Attribute) and func_part.attrname == "register": + return func_part.expr + return None + + if isinstance(dec, nodes.Attribute) and dec.attrname == "register": + return dec.expr + + return None + + +def _inferred_has_singledispatchmethod(target: nodes.NodeNG) -> bool: + """ + Infer `target` and return True if the inferred object has a + @singledispatchmethod decorator. + """ + inferred = utils.safe_infer(target) + if not inferred: + return False + + if isinstance(inferred, (nodes.FunctionDef, nodes.AsyncFunctionDef)): + decorators = inferred.decorators + if isinstance(decorators, nodes.Decorators): + for dec in decorators.nodes: + inferred_dec = utils.safe_infer(dec) + if ( + inferred_dec + and inferred_dec.qname() == "functools.singledispatchmethod" + ): + return True + + return False + + +def _is_singledispatchmethod_registration(node: nodes.FunctionDef) -> bool: + """ + Return True if `node` is a function decorated like: + + @func.register(...) + def _(…): ... + + where `func` is a singledispatchmethod (i.e. its base was decorated + with @singledispatchmethod). + """ + decorators = node.decorators + if not decorators: + return False + + for dec in decorators.nodes: + target = _extract_register_target(dec) + if target is None: + continue + + if _inferred_has_singledispatchmethod(target): + 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 %r 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.", + ), + "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)}, + ), + "W0136": ( + "'continue' discouraged inside 'finally' clause", + "continue-in-finally", + "Emitted when the `continue` keyword is found " + "inside a finally clause. This will raise a SyntaxWarning " + "starting in Python 3.14.", + ), + "W0137": ( + "'break' discouraged inside 'finally' clause", + "break-in-finally", + "Emitted when the `break` keyword is found " + "inside a finally clause. This will raise a SyntaxWarning " + "starting in Python 3.14.", + ), + } + + @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(): + match elem: + case nodes.Tuple(): + return self._too_many_starred_for_tuple(elem) + case 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: + match assign_target := node.targets[0]: + case nodes.Starred(): + # Check *a = b + self.add_message("invalid-star-assignment-target", node=node) + case nodes.Tuple(): + # Check *a, *b = ... + 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 = {} + for arg in node.args.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", "break-in-finally") + 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) + ): + self.add_message("continue-in-finally", node=node) + if ( + isinstance(parent, nodes.Try) + and node in parent.finalbody + and isinstance(node, nodes.Break) + ): + self.add_message("break-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 + + if _is_singledispatchmethod_registration(node): + return + + # Skip typing.overload() functions. + if utils.is_overload_stub(node): + return + + # Exempt functions redefined on a condition. + if isinstance(node.parent, nodes.If): + match node.parent.test: + case nodes.UnaryOp(op="not", operand=nodes.Name(name=name)) if ( + name == node.name + ): + # Exempt "if not " cases + return + case nodes.Compare( + left=nodes.Name(name=name), + ops=[["is", nodes.Const(value=None)]], + ) if ( + name == node.name + ): + # Exempt "if is not None" cases + 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() in FORWARD_REF_QNAME + ): + return + + self.add_message( + "function-redefined", + node=node, + args=(redeftype, defined_self.fromlineno), + ) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py new file mode 100644 index 0000000..395a7a3 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py @@ -0,0 +1,352 @@ +# 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: + match node: + case nodes.Call(args=[nodes.Const(value=str() as value)]) if ( + value.lower() == "nan" + ): + return node.inferred()[0].pytype() == "builtins.float" # type: ignore[no-any-return] + return False + except AttributeError: + return False + + def _is_numpy_nan(node: nodes.NodeNG) -> bool: + match node: + case nodes.Attribute(attrname="NaN", expr=nodes.Name(name=name)): + return name in {"numpy", "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.""" + match literal: + case nodes.Const(value=bool() | None): + # Not interested in these values. + return + case nodes.Const(value=bytes() | str() | int() | float()): + pass + case nodes.List() | nodes.Dict() | nodes.Set(): + # Inline list, dict and set nodes + pass + case _: + return + + 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.as_string(), operator, right_operand.as_string()), + 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=node, left=left, right=right) + elif isinstance(left, nodes.Name) and _is_one_arg_pos_call(right): + # transforming Y == type(x) case to type(x) == Y + self._check_type_x_is_y(node=node, left=right, right=left) + + def _check_type_x_is_y( + self, node: nodes.Compare, left: nodes.NodeNG, 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 _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) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py new file mode 100644 index 0000000..710c98b --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py @@ -0,0 +1,203 @@ +# 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 isinstance(docstring, nodes.Const): + return str(docstring.value) + return None + + +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": "", + "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": "", + "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 + match node.body: + case [nodes.Expr(value=nodes.Call() as value), *_]: + # Most likely a string with a format call. Let's see. + match utils.safe_infer(value.func): + case astroid.BoundMethod( + bound=astroid.Instance(name="str" | "unicode" | "bytes") + ): + # Strings. + return + match node_type: + case "module": + message = "missing-module-docstring" + case "class": + message = "missing-class-docstring" + case _: + 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 + ) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py new file mode 100644 index 0000000..2826b40 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py @@ -0,0 +1,149 @@ +# 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 + +"""Function checker for Python code.""" + +from __future__ import annotations + +from itertools import chain + +from astroid import nodes + +from pylint.checkers import utils +from pylint.checkers.base.basic_checker import _BasicChecker + + +class FunctionChecker(_BasicChecker): + """Check if a function definition handles possible side effects.""" + + msgs = { + "W0135": ( + "The context used in function %r will not be exited.", + "contextmanager-generator-missing-cleanup", + "Used when a contextmanager is used inside a generator function" + " and the cleanup is not handled.", + ) + } + + @utils.only_required_for_messages("contextmanager-generator-missing-cleanup") + def visit_functiondef(self, node: nodes.FunctionDef) -> None: + self._check_contextmanager_generator_missing_cleanup(node) + + @utils.only_required_for_messages("contextmanager-generator-missing-cleanup") + def visit_asyncfunctiondef(self, node: nodes.AsyncFunctionDef) -> None: + self._check_contextmanager_generator_missing_cleanup(node) + + def _check_contextmanager_generator_missing_cleanup( + self, node: nodes.FunctionDef + ) -> None: + """Check a FunctionDef to find if it is a generator + that uses a contextmanager internally. + + If it is, check if the contextmanager is properly cleaned up. Otherwise, add message. + + :param node: FunctionDef node to check + :type node: nodes.FunctionDef + """ + # if function does not use a Yield statement, it can't be a generator + with_nodes = list(node.nodes_of_class(nodes.With)) + if not with_nodes: + return + # check for Yield inside the With statement + yield_nodes = list( + chain.from_iterable( + with_node.nodes_of_class(nodes.Yield) for with_node in with_nodes + ) + ) + if not yield_nodes: + return + + # infer the call that yields a value, and check if it is a contextmanager + for with_node in with_nodes: + for call, held in with_node.items: + if held is None: + # if we discard the value, then we can skip checking it + continue + + # safe infer is a generator + inferred_node = getattr(utils.safe_infer(call), "parent", None) + if not isinstance(inferred_node, nodes.FunctionDef): + continue + if self._node_fails_contextmanager_cleanup(inferred_node, yield_nodes): + self.add_message( + "contextmanager-generator-missing-cleanup", + node=with_node, + args=(node.name,), + ) + + @staticmethod + def _node_fails_contextmanager_cleanup( + node: nodes.FunctionDef, yield_nodes: list[nodes.Yield] + ) -> bool: + """Check if a node fails contextmanager cleanup. + + Current checks for a contextmanager: + - only if the context manager yields a non-constant value + - only if the context manager lacks a finally, or does not catch GeneratorExit + - only if some statement follows the yield, some manually cleanup happens + + :param node: Node to check + :type node: nodes.FunctionDef + :return: True if fails, False otherwise + :param yield_nodes: List of Yield nodes in the function body + :type yield_nodes: list[nodes.Yield] + :rtype: bool + """ + + def check_handles_generator_exceptions(try_node: nodes.Try) -> bool: + # needs to handle either GeneratorExit, Exception, or bare except + for handler in try_node.handlers: + if handler.type is None: + # handles all exceptions (bare except) + return True + inferred = utils.safe_infer(handler.type) + if inferred and inferred.qname() in { + "builtins.GeneratorExit", + "builtins.Exception", + }: + return True + return False + + # if context manager yields a non-constant value, then continue checking + if any( + yield_node.value is None or isinstance(yield_node.value, nodes.Const) + for yield_node in yield_nodes + ): + return False + + # Check if yield expression is last statement + yield_nodes = list(node.nodes_of_class(nodes.Yield)) + if len(yield_nodes) == 1: + n = yield_nodes[0].parent + while n is not node: + if n.next_sibling() is not None: + break + n = n.parent + else: + # No next statement found + return False + + # if function body has multiple Try, filter down to the ones that have a yield node + try_with_yield_nodes = [ + try_node + for try_node in node.nodes_of_class(nodes.Try) + if any(try_node.nodes_of_class(nodes.Yield)) + ] + if not try_with_yield_nodes: + # no try blocks at all, so checks after this line do not apply + return True + # if the contextmanager has a finally block, then it is fine + if all(try_node.finalbody for try_node in try_with_yield_nodes): + return False + # if the contextmanager catches GeneratorExit, then it is fine + if all( + check_handles_generator_exceptions(try_node) + for try_node in try_with_yield_nodes + ): + return False + return True diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py new file mode 100644 index 0000000..9339807 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py @@ -0,0 +1,25 @@ +# 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__ = [ + "KNOWN_NAME_TYPES_WITH_STYLE", + "AnyStyle", + "CamelCaseStyle", + "NameChecker", + "NamingStyle", + "PascalCaseStyle", + "SnakeCaseStyle", + "UpperCaseStyle", +] + +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, +) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py new file mode 100644 index 0000000..9b3ed0e --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py @@ -0,0 +1,804 @@ +# 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, cast + +import astroid +from astroid import bases, nodes, util +from astroid.typing import InferenceResult + +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)?(? 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 and 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 not (node_type == "method" and 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": "", + "help": "Good variable names which should always be accepted," + " separated by a comma.", + }, + ), + ( + "good-names-rgxs", + { + "default": "", + "type": "regexp_csv", + "metavar": "", + "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": "", + "help": "Bad variable names which should always be refused, " + "separated by a comma.", + }, + ), + ( + "bad-names-rgxs", + { + "default": "", + "type": "regexp_csv", + "metavar": "", + "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": "", + "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": "", + "help": "Include a hint for the correct naming format with invalid-name.", + }, + ), + ( + "property-classes", + { + "default": ("abc.abstractproperty",), + "type": "csv", + "metavar": "", + "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) + ) and not utils.is_assign_name_annotated_with(anodes[0], "Final"): + 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,too-many-statements + 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) + + elif isinstance(assign_type, nodes.TypeVar): + self._check_name("typevar", node.name, node) + + elif isinstance(assign_type, nodes.ParamSpec): + self._check_name("paramspec", node.name, node) + + elif isinstance(assign_type, nodes.TypeVarTuple): + self._check_name("typevartuple", node.name, node) + + elif isinstance(assign_type, nodes.TypeAlias): + self._check_name("typealias", node.name, node) + + # Check names defined in module scope + elif isinstance(frame, nodes.Module): + # Check names defined in AnnAssign nodes + if isinstance(assign_type, nodes.AnnAssign) and self._assigns_typealias( + assign_type.annotation + ): + self._check_name("typealias", node.name, node) + + # Check names defined in Assign nodes + elif isinstance(assign_type, (nodes.Assign, nodes.AnnAssign)): + inferred_assign_type = ( + utils.safe_infer(assign_type.value) if assign_type.value else None + ) + + # Check TypeVar's and TypeAliases assigned alone or in tuple assignment + if isinstance(node.parent, nodes.Assign): + if typevar_node_type := self._assigns_typevar(assign_type.value): + self._check_name( + typevar_node_type, 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 typevar_node_type := self._assigns_typevar(assigner): + self._check_name( + typevar_node_type, + 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 + + elif inferred_assign_type in (None, util.Uninferable): + return + + # Check classes (TypeVar's are classes so they need to be excluded first) + elif self._should_check_class_regex(inferred_assign_type): + self._check_name("class", node.name, node) + + # Don't emit if the name redefines an import in an ImportError except handler + # nor any other reassignment. + elif ( + not (redefines_import := _redefines_import(node)) + and not isinstance( + inferred_assign_type, (nodes.FunctionDef, nodes.Lambda) + ) + and not utils.is_reassigned_before_current(node, node.name) + and not utils.is_reassigned_after_current(node, node.name) + and not utils.get_node_first_ancestor_of_type( + node, (nodes.For, nodes.While) + ) + ): + if not self._meets_exception_for_non_consts( + inferred_assign_type, node.name + ): + self._check_name("const", node.name, node) + else: + node_type = "variable" + iattrs = tuple(node.frame().igetattr(node.name)) + if ( + util.Uninferable in iattrs + and self._name_regexps["const"].match(node.name) is not None + ): + return + # Do the exclusive assignment analysis on attrs, not iattrs. + # iattrs locations could be anywhere (inference result). + attrs = tuple(node.frame().getattr(node.name)) + if len(attrs) > 1 and all( + astroid.are_exclusive(*combo) + for combo in itertools.combinations(attrs, 2) + ): + node_type = "const" + if not self._meets_exception_for_non_consts( + inferred_assign_type, node.name + ): + self._check_name( + node_type, + node.name, + node, + disallowed_check_only=redefines_import, + ) + + # 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) and not any( + frame.local_attr_ancestors(node.name) + ): + 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 _meets_exception_for_non_consts( + self, inferred_assign_type: InferenceResult | None, name: str + ) -> bool: + if isinstance(inferred_assign_type, nodes.Const): + return False + regexp = self._name_regexps["variable"] + return regexp.match(name) is not None + + def _should_check_class_regex( + self, inferred_assign_type: InferenceResult | None + ) -> bool: + if isinstance(inferred_assign_type, nodes.ClassDef): + return True + if isinstance(inferred_assign_type, bases.Instance) and { + "EnumMeta", + "TypedDict", + }.intersection( + { + ancestor.name + for ancestor in cast(InferenceResult, inferred_assign_type).mro() + } + ): + return True + if ( + isinstance(inferred_assign_type, nodes.FunctionDef) + and inferred_assign_type.qname() == "typing.Annotated" + ): + return True + return False + + def _recursive_check_names(self, args: list[nodes.AssignName]) -> None: + """Check names in a possibly recursive list .""" + 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) -> str | None: + """Check if a node is assigning a TypeVar and return TypeVar type.""" + if isinstance(node, nodes.Call): + inferred = utils.safe_infer(node.func) + if isinstance(inferred, nodes.ClassDef): + qname = inferred.qname() + for typevar_node_typ, qnames in TYPE_VAR_QNAMES.items(): + if qname in qnames: + return typevar_node_typ + return None + + @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, bases.UnionType)): + qname = inferred.qname() + if qname == "typing.TypeAlias": + return True + if qname in {".Union", "builtins.Union", "builtins.UnionType"}: + # 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.""" + variance: TypeVarVariance = TypeVarVariance.invariant + match node.parent: + case nodes.Assign(): + keywords = node.assign_type().value.keywords + args = node.assign_type().value.args + case 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 + case _: # PEP 695 generic type nodes + keywords = () + args = () + variance = TypeVarVariance.inferred + + 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 + + match variance: + case TypeVarVariance.inferred: + # Ignore variance check for PEP 695 type parameters. + # The variance is inferred by the type checker. + # Adding _co or _contra suffix can help to reason about TypeVar. + pass + case 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, + ) + case TypeVarVariance.covariant if 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, + ) + case TypeVarVariance.contravariant if 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, + ) + case TypeVarVariance.invariant if name.endswith(("_co", "_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, + ) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py new file mode 100644 index 0000000..41f7bb8 --- /dev/null +++ b/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py @@ -0,0 +1,187 @@ +# 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", + "paramspec", + "typevartuple", + "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": "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
indicator_codeindicator_labelindicator_previous_codereporter_area_codereporter_area_labelpartner_area_codepartner_area_labelyearvalueflagflag_labelunitnotesdrop
0INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1995227.0NaNNaNthousand tripsNaNNaN
1INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1996270.0NaNNaNthousand tripsNaNNaN
2INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1997286.0NaNNaNthousand tripsNaNNaN
3INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1998336.0NaNNaNthousand tripsNaNNaN
4INBD_TRIP_TOTL_CRUS_EXCRinbound - trips - total - cruise ships - same-...CP_1_428Antigua and Barbuda999World1999328.0NaNNaNthousand tripsNaNNaN
\n", - "" - ], - "text/plain": [ - " indicator_code \\\n", - "0 INBD_TRIP_TOTL_CRUS_EXCR \n", - "1 INBD_TRIP_TOTL_CRUS_EXCR \n", - "2 INBD_TRIP_TOTL_CRUS_EXCR \n", - "3 INBD_TRIP_TOTL_CRUS_EXCR \n", - "4 INBD_TRIP_TOTL_CRUS_EXCR \n", - "\n", - " indicator_label indicator_previous_code \\\n", - "0 inbound - trips - total - cruise ships - same-... CP_1_4 \n", - "1 inbound - trips - total - cruise ships - same-... CP_1_4 \n", - "2 inbound - trips - total - cruise ships - same-... CP_1_4 \n", - "3 inbound - trips - total - cruise ships - same-... CP_1_4 \n", - "4 inbound - trips - total - cruise ships - same-... CP_1_4 \n", - "\n", - " reporter_area_code reporter_area_label partner_area_code \\\n", - "0 28 Antigua and Barbuda 999 \n", - "1 28 Antigua and Barbuda 999 \n", - "2 28 Antigua and Barbuda 999 \n", - "3 28 Antigua and Barbuda 999 \n", - "4 28 Antigua and Barbuda 999 \n", - "\n", - " partner_area_label year value flag flag_label unit notes drop \n", - "0 World 1995 227.0 NaN NaN thousand trips NaN NaN \n", - "1 World 1996 270.0 NaN NaN thousand trips NaN NaN \n", - "2 World 1997 286.0 NaN NaN thousand trips NaN NaN \n", - "3 World 1998 336.0 NaN NaN thousand trips NaN NaN \n", - "4 World 1999 328.0 NaN NaN thousand trips NaN NaN " - ] - }, - "execution_count": 39, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ - "df.head()" + "Data Analysis and Linear Regression Model\n" ] }, { - "cell_type": "code", - "execution_count": 41, - "id": "165677d1", + "cell_type": "markdown", + "id": "06622b47", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "RangeIndex: 3025 entries, 0 to 3024\n", - "Data columns (total 14 columns):\n", - " # Column Non-Null Count Dtype \n", - "--- ------ -------------- ----- \n", - " 0 indicator_code 3025 non-null object \n", - " 1 indicator_label 3025 non-null object \n", - " 2 indicator_previous_code 3025 non-null object \n", - " 3 reporter_area_code 3025 non-null int64 \n", - " 4 reporter_area_label 3025 non-null object \n", - " 5 partner_area_code 3025 non-null int64 \n", - " 6 partner_area_label 3025 non-null object \n", - " 7 year 3025 non-null int64 \n", - " 8 value 3025 non-null float64\n", - " 9 flag 2 non-null object \n", - " 10 flag_label 2 non-null object \n", - " 11 unit 3025 non-null object \n", - " 12 notes 2 non-null object \n", - " 13 drop 0 non-null float64\n", - "dtypes: float64(2), int64(3), object(9)\n", - "memory usage: 331.0+ KB\n" - ] - } - ], "source": [ - "df.info()" + "1. Understanding the Data Structure\n", + "\n", + " The dataset contains:\n", + "\n", + " type_of_visitors: Type of visitors (tourists, excursionists, visitors_total)\n", + "\n", + " country_receiving: Destination country\n", + "\n", + " where_tourist_from: Origin (all are \"World\")\n", + "\n", + " year: Year of data\n", + "\n", + " number_of_tourist: Number of visitors (in thousand trips)\n", + "\n", + " unit: Measurement unit (thousand trips)" ] }, { - "cell_type": "code", - "execution_count": 42, - "id": "b7213152", + "cell_type": "markdown", + "id": "1dcb8f74", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "2f1c0a05", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Series([], Name: count, dtype: int64)" - ] - }, - "execution_count": 42, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ - "df.value_counts() " + "2. Data Preparation for Linear Regression" ] }, { "cell_type": "code", - "execution_count": 44, - "id": "ec66ab0d", + "execution_count": 5, + "id": "6b73334d", "metadata": {}, "outputs": [ { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'matplotlib'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[44], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mmatplotlib\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mpyplot\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mplt\u001b[39;00m\n\u001b[1;32m 3\u001b[0m plt\u001b[38;5;241m.\u001b[39mscatter(df[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mYear\u001b[39m\u001b[38;5;124m'\u001b[39m], df[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mArrivals\u001b[39m\u001b[38;5;124m'\u001b[39m])\n\u001b[1;32m 4\u001b[0m plt\u001b[38;5;241m.\u001b[39mxlabel(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mYear\u001b[39m\u001b[38;5;124m'\u001b[39m)\n", - "\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'matplotlib'" + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of data points: 30\n", + "Years range: 1995 to 2024\n" ] } ], "source": [ + "import pandas as pd\n", + "import numpy as np\n", "import matplotlib.pyplot as plt\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.linear_model import LinearRegression\n", + "from sklearn.metrics import mean_squared_error, r2_score\n", + "\n", + "# Load the dataset (assuming it's in a CSV file)\n", + "df = pd.read_csv(\"UN_tourism_caribbean_countries_cleaned.csv\")\n", + "\n", + "# Let's focus on predicting total visitors for a specific country\n", + "# We'll use \"Dominican Republic\" as an example since it has comprehensive data\n", + "\n", + "# Filter for total visitors data for Dominican Republic\n", + "dr_data = df[\n", + " (df[\"country_receiving\"] == \"Dominican Republic\")\n", + " & (df[\"type_of_visitors\"] == \"visitors_total\")\n", + "].copy()\n", + "\n", + "# Sort by year\n", + "dr_data = dr_data.sort_values(\"year\")\n", + "\n", + "# Prepare features (X) and target (y)\n", + "# We'll use year as the feature to predict number of tourists\n", + "X = dr_data[[\"year\"]].values\n", + "y = dr_data[\"number_of_tourist\"].values\n", "\n", - "plt.scatter(df['Year'], df['Arrivals'])\n", - "plt.xlabel('Year')\n", - "plt.ylabel('Arrivals') " + "# Check if we have enough data\n", + "print(f\"Number of data points: {len(dr_data)}\")\n", + "print(f\"Years range: {dr_data['year'].min()} to {dr_data['year'].max()}\")\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "3eb6a166", + "id": "207ff3db", "metadata": {}, "outputs": [], "source": [] diff --git a/4_data_analysis/UN_tourism_caribbean_countries_cleaned.csv b/4_data_analysis/UN_tourism_caribbean_countries_cleaned.csv new file mode 100644 index 0000000..83dc6f9 --- /dev/null +++ b/4_data_analysis/UN_tourism_caribbean_countries_cleaned.csv @@ -0,0 +1,3026 @@ +type_of_visitors,country_receiving,where_tourist_from,year,number_of_tourist,unit +excursionists,Antigua and Barbuda,World,1995,227.0,thousand trips +excursionists,Antigua and Barbuda,World,1996,270.0,thousand trips +excursionists,Antigua and Barbuda,World,1997,286.0,thousand trips +excursionists,Antigua and Barbuda,World,1998,336.0,thousand trips +excursionists,Antigua and Barbuda,World,1999,328.0,thousand trips +excursionists,Antigua and Barbuda,World,2000,427.0,thousand trips +excursionists,Antigua and Barbuda,World,2001,409.0,thousand trips +excursionists,Antigua and Barbuda,World,2002,310.0,thousand trips +excursionists,Antigua and Barbuda,World,2003,384.0,thousand trips +excursionists,Antigua and Barbuda,World,2004,523.0,thousand trips +excursionists,Antigua and Barbuda,World,2005,467.0,thousand trips +excursionists,Antigua and Barbuda,World,2006,584.0,thousand trips +excursionists,Antigua and Barbuda,World,2007,673.0,thousand trips +excursionists,Antigua and Barbuda,World,2008,597.0,thousand trips +excursionists,Antigua and Barbuda,World,2009,710.0,thousand trips +excursionists,Antigua and Barbuda,World,2010,558.0,thousand trips +excursionists,Antigua and Barbuda,World,2011,606.5,thousand trips +excursionists,Antigua and Barbuda,World,2012,572.0,thousand trips +excursionists,Antigua and Barbuda,World,2013,534.0,thousand trips +excursionists,Antigua and Barbuda,World,2014,522.0,thousand trips +excursionists,Antigua and Barbuda,World,2015,644.0,thousand trips +excursionists,Antigua and Barbuda,World,2016,609.0,thousand trips +excursionists,Antigua and Barbuda,World,2017,793.0,thousand trips +excursionists,Antigua and Barbuda,World,2018,795.0,thousand trips +excursionists,Antigua and Barbuda,World,2019,734.0,thousand trips +excursionists,Antigua and Barbuda,World,2020,151.0,thousand trips +excursionists,Antigua and Barbuda,World,2021,80.6,thousand trips +excursionists,Antigua and Barbuda,World,2022,377.3,thousand trips +excursionists,Bahamas,World,1995,1543.0,thousand trips +excursionists,Bahamas,World,1996,1686.0,thousand trips +excursionists,Bahamas,World,1997,1751.0,thousand trips +excursionists,Bahamas,World,1998,1730.0,thousand trips +excursionists,Bahamas,World,1999,1981.0,thousand trips +excursionists,Bahamas,World,2000,2513.0,thousand trips +excursionists,Bahamas,World,2001,2552.0,thousand trips +excursionists,Bahamas,World,2002,2802.0,thousand trips +excursionists,Bahamas,World,2003,2970.0,thousand trips +excursionists,Bahamas,World,2004,3360.0,thousand trips +excursionists,Bahamas,World,2005,3079.0,thousand trips +excursionists,Bahamas,World,2006,3079.0,thousand trips +excursionists,Bahamas,World,2007,2971.0,thousand trips +excursionists,Bahamas,World,2008,2861.0,thousand trips +excursionists,Bahamas,World,2009,3256.0,thousand trips +excursionists,Bahamas,World,2010,3810.0,thousand trips +excursionists,Bahamas,World,2011,4161.0,thousand trips +excursionists,Bahamas,World,2012,4434.0,thousand trips +excursionists,Bahamas,World,2013,4709.0,thousand trips +excursionists,Bahamas,World,2014,4805.0,thousand trips +excursionists,Bahamas,World,2015,4513.0,thousand trips +excursionists,Bahamas,World,2016,4690.0,thousand trips +excursionists,Bahamas,World,2017,4626.0,thousand trips +excursionists,Bahamas,World,2018,4878.0,thousand trips +excursionists,Bahamas,World,2019,5433.0,thousand trips +excursionists,Bahamas,World,2020,1327.1,thousand trips +excursionists,Bahamas,World,2021,1115.0,thousand trips +excursionists,Bahamas,World,2022,5390.0,thousand trips +excursionists,Barbados,World,1995,485.0,thousand trips +excursionists,Barbados,World,1996,510.0,thousand trips +excursionists,Barbados,World,1997,518.0,thousand trips +excursionists,Barbados,World,1998,507.0,thousand trips +excursionists,Barbados,World,1999,433.0,thousand trips +excursionists,Barbados,World,2000,533.0,thousand trips +excursionists,Barbados,World,2001,528.0,thousand trips +excursionists,Barbados,World,2002,523.0,thousand trips +excursionists,Barbados,World,2003,559.0,thousand trips +excursionists,Barbados,World,2004,721.0,thousand trips +excursionists,Barbados,World,2005,563.0,thousand trips +excursionists,Barbados,World,2006,539.0,thousand trips +excursionists,Barbados,World,2007,616.0,thousand trips +excursionists,Barbados,World,2008,597.0,thousand trips +excursionists,Barbados,World,2009,635.0,thousand trips +excursionists,Barbados,World,2010,665.0,thousand trips +excursionists,Barbados,World,2011,619.0,thousand trips +excursionists,Barbados,World,2012,517.0,thousand trips +excursionists,Barbados,World,2013,570.0,thousand trips +excursionists,Barbados,World,2014,558.0,thousand trips +excursionists,Barbados,World,2015,587.0,thousand trips +excursionists,Barbados,World,2016,595.0,thousand trips +excursionists,Barbados,World,2017,681.0,thousand trips +excursionists,Barbados,World,2018,676.0,thousand trips +excursionists,Barbados,World,2019,443.0,thousand trips +excursionists,Barbados,World,2020,338.0,thousand trips +excursionists,Barbados,World,2021,97.4,thousand trips +excursionists,Barbados,World,2022,357.9,thousand trips +excursionists,Bermuda,World,1995,170.0,thousand trips +excursionists,Bermuda,World,1996,182.0,thousand trips +excursionists,Bermuda,World,1997,182.0,thousand trips +excursionists,Bermuda,World,1998,188.0,thousand trips +excursionists,Bermuda,World,1999,194.0,thousand trips +excursionists,Bermuda,World,2000,207.0,thousand trips +excursionists,Bermuda,World,2001,180.0,thousand trips +excursionists,Bermuda,World,2002,200.0,thousand trips +excursionists,Bermuda,World,2003,226.0,thousand trips +excursionists,Bermuda,World,2004,206.0,thousand trips +excursionists,Bermuda,World,2005,247.0,thousand trips +excursionists,Bermuda,World,2006,336.0,thousand trips +excursionists,Bermuda,World,2007,354.0,thousand trips +excursionists,Bermuda,World,2008,286.0,thousand trips +excursionists,Bermuda,World,2009,318.0,thousand trips +excursionists,Bermuda,World,2010,348.0,thousand trips +excursionists,Bermuda,World,2011,416.0,thousand trips +excursionists,Bermuda,World,2012,378.0,thousand trips +excursionists,Bermuda,World,2013,340.0,thousand trips +excursionists,Bermuda,World,2014,356.0,thousand trips +excursionists,Bermuda,World,2015,377.0,thousand trips +excursionists,Bermuda,World,2016,398.0,thousand trips +excursionists,Bermuda,World,2017,418.0,thousand trips +excursionists,Bermuda,World,2018,484.0,thousand trips +excursionists,Bermuda,World,2019,536.0,thousand trips +excursionists,Bermuda,World,2020,9.3,thousand trips +excursionists,Bermuda,World,2021,14.2,thousand trips +excursionists,Bermuda,World,2022,402.7,thousand trips +excursionists,Belize,World,1995,8.0,thousand trips +excursionists,Belize,World,1996,1.0,thousand trips +excursionists,Belize,World,1997,3.0,thousand trips +excursionists,Belize,World,1998,14.0,thousand trips +excursionists,Belize,World,1999,34.0,thousand trips +excursionists,Belize,World,2000,58.0,thousand trips +excursionists,Belize,World,2001,48.0,thousand trips +excursionists,Belize,World,2002,320.0,thousand trips +excursionists,Belize,World,2003,575.0,thousand trips +excursionists,Belize,World,2004,851.0,thousand trips +excursionists,Belize,World,2005,800.0,thousand trips +excursionists,Belize,World,2006,656.0,thousand trips +excursionists,Belize,World,2007,624.0,thousand trips +excursionists,Belize,World,2008,597.0,thousand trips +excursionists,Belize,World,2009,705.0,thousand trips +excursionists,Belize,World,2010,779.0,thousand trips +excursionists,Belize,World,2011,735.0,thousand trips +excursionists,Belize,World,2012,641.0,thousand trips +excursionists,Belize,World,2013,677.0,thousand trips +excursionists,Belize,World,2014,968.0,thousand trips +excursionists,Belize,World,2015,958.0,thousand trips +excursionists,Belize,World,2016,1005.0,thousand trips +excursionists,Belize,World,2017,1014.0,thousand trips +excursionists,Belize,World,2018,1208.0,thousand trips +excursionists,Belize,World,2019,1171.0,thousand trips +excursionists,Belize,World,2020,343.0,thousand trips +excursionists,Belize,World,2021,210.0,thousand trips +excursionists,Belize,World,2022,615.0,thousand trips +excursionists,British Virgin Islands,World,1995,122.0,thousand trips +excursionists,British Virgin Islands,World,1996,160.0,thousand trips +excursionists,British Virgin Islands,World,1997,105.0,thousand trips +excursionists,British Virgin Islands,World,1998,105.0,thousand trips +excursionists,British Virgin Islands,World,1999,182.0,thousand trips +excursionists,British Virgin Islands,World,2000,197.0,thousand trips +excursionists,British Virgin Islands,World,2001,202.0,thousand trips +excursionists,British Virgin Islands,World,2002,230.0,thousand trips +excursionists,British Virgin Islands,World,2003,304.0,thousand trips +excursionists,British Virgin Islands,World,2004,467.0,thousand trips +excursionists,British Virgin Islands,World,2005,449.0,thousand trips +excursionists,British Virgin Islands,World,2006,444.0,thousand trips +excursionists,British Virgin Islands,World,2007,575.0,thousand trips +excursionists,British Virgin Islands,World,2008,572.0,thousand trips +excursionists,British Virgin Islands,World,2009,530.0,thousand trips +excursionists,British Virgin Islands,World,2010,501.0,thousand trips +excursionists,British Virgin Islands,World,2011,485.0,thousand trips +excursionists,British Virgin Islands,World,2012,391.0,thousand trips +excursionists,British Virgin Islands,World,2013,367.0,thousand trips +excursionists,British Virgin Islands,World,2014,361.0,thousand trips +excursionists,British Virgin Islands,World,2015,516.0,thousand trips +excursionists,British Virgin Islands,World,2016,699.0,thousand trips +excursionists,British Virgin Islands,World,2017,410.0,thousand trips +excursionists,British Virgin Islands,World,2018,201.0,thousand trips +excursionists,British Virgin Islands,World,2019,575.1,thousand trips +excursionists,British Virgin Islands,World,2020,219.0,thousand trips +excursionists,British Virgin Islands,World,2021,70.0,thousand trips +excursionists,British Virgin Islands,World,2022,344.0,thousand trips +excursionists,Cayman Islands,World,1995,683.0,thousand trips +excursionists,Cayman Islands,World,1996,800.0,thousand trips +excursionists,Cayman Islands,World,1997,867.0,thousand trips +excursionists,Cayman Islands,World,1998,871.0,thousand trips +excursionists,Cayman Islands,World,1999,1036.0,thousand trips +excursionists,Cayman Islands,World,2000,1031.0,thousand trips +excursionists,Cayman Islands,World,2001,1215.0,thousand trips +excursionists,Cayman Islands,World,2002,1575.0,thousand trips +excursionists,Cayman Islands,World,2003,1819.0,thousand trips +excursionists,Cayman Islands,World,2004,1693.0,thousand trips +excursionists,Cayman Islands,World,2005,1799.0,thousand trips +excursionists,Cayman Islands,World,2006,1930.0,thousand trips +excursionists,Cayman Islands,World,2007,1716.0,thousand trips +excursionists,Cayman Islands,World,2008,1553.0,thousand trips +excursionists,Cayman Islands,World,2009,1520.0,thousand trips +excursionists,Cayman Islands,World,2010,1598.0,thousand trips +excursionists,Cayman Islands,World,2011,1401.0,thousand trips +excursionists,Cayman Islands,World,2012,1507.0,thousand trips +excursionists,Cayman Islands,World,2013,1376.0,thousand trips +excursionists,Cayman Islands,World,2014,1610.0,thousand trips +excursionists,Cayman Islands,World,2015,1717.0,thousand trips +excursionists,Cayman Islands,World,2016,1712.0,thousand trips +excursionists,Cayman Islands,World,2017,1728.0,thousand trips +excursionists,Cayman Islands,World,2018,1921.0,thousand trips +excursionists,Cayman Islands,World,2019,1831.0,thousand trips +excursionists,Cayman Islands,World,2020,538.0,thousand trips +excursionists,Cayman Islands,World,2022,743.4,thousand trips +excursionists,Cuba,World,1995,1.0,thousand trips +excursionists,Cuba,World,1996,2.0,thousand trips +excursionists,Cuba,World,1997,2.0,thousand trips +excursionists,Cuba,World,1998,8.0,thousand trips +excursionists,Cuba,World,1999,4.0,thousand trips +excursionists,Cuba,World,2000,10.0,thousand trips +excursionists,Cuba,World,2001,14.0,thousand trips +excursionists,Cuba,World,2002,6.0,thousand trips +excursionists,Cuba,World,2003,20.0,thousand trips +excursionists,Cuba,World,2004,5.0,thousand trips +excursionists,Cuba,World,2005,17.0,thousand trips +excursionists,Cuba,World,2006,30.0,thousand trips +excursionists,Cuba,World,2007,7.0,thousand trips +excursionists,Cuba,World,2008,5.0,thousand trips +excursionists,Cuba,World,2009,4.0,thousand trips +excursionists,Cuba,World,2010,2.0,thousand trips +excursionists,Cuba,World,2011,1.0,thousand trips +excursionists,Cuba,World,2012,3.0,thousand trips +excursionists,Cuba,World,2013,2.0,thousand trips +excursionists,Cuba,World,2014,8.0,thousand trips +excursionists,Cuba,World,2015,10.0,thousand trips +excursionists,Cuba,World,2016,17.0,thousand trips +excursionists,Dominica,World,1995,135.0,thousand trips +excursionists,Dominica,World,1996,193.0,thousand trips +excursionists,Dominica,World,1997,231.0,thousand trips +excursionists,Dominica,World,1998,245.0,thousand trips +excursionists,Dominica,World,1999,202.0,thousand trips +excursionists,Dominica,World,2000,240.0,thousand trips +excursionists,Dominica,World,2001,208.0,thousand trips +excursionists,Dominica,World,2002,137.0,thousand trips +excursionists,Dominica,World,2003,177.0,thousand trips +excursionists,Dominica,World,2004,384.0,thousand trips +excursionists,Dominica,World,2005,302.0,thousand trips +excursionists,Dominica,World,2006,380.0,thousand trips +excursionists,Dominica,World,2007,355.0,thousand trips +excursionists,Dominica,World,2008,386.0,thousand trips +excursionists,Dominica,World,2009,532.0,thousand trips +excursionists,Dominica,World,2010,518.0,thousand trips +excursionists,Dominica,World,2011,341.0,thousand trips +excursionists,Dominica,World,2012,266.5,thousand trips +excursionists,Dominica,World,2013,231.0,thousand trips +excursionists,Dominica,World,2014,287.0,thousand trips +excursionists,Dominica,World,2015,281.0,thousand trips +excursionists,Dominica,World,2016,277.0,thousand trips +excursionists,Dominica,World,2017,157.0,thousand trips +excursionists,Dominica,World,2018,134.0,thousand trips +excursionists,Dominica,World,2019,230.0,thousand trips +excursionists,Dominica,World,2020,118.0,thousand trips +excursionists,Dominica,World,2021,51.0,thousand trips +excursionists,Dominica,World,2022,170.0,thousand trips +excursionists,Dominican Republic,World,1995,30.0,thousand trips +excursionists,Dominican Republic,World,1996,111.0,thousand trips +excursionists,Dominican Republic,World,1997,271.0,thousand trips +excursionists,Dominican Republic,World,1998,393.0,thousand trips +excursionists,Dominican Republic,World,1999,283.0,thousand trips +excursionists,Dominican Republic,World,2000,183.0,thousand trips +excursionists,Dominican Republic,World,2001,208.0,thousand trips +excursionists,Dominican Republic,World,2002,247.0,thousand trips +excursionists,Dominican Republic,World,2003,398.0,thousand trips +excursionists,Dominican Republic,World,2004,457.0,thousand trips +excursionists,Dominican Republic,World,2005,290.0,thousand trips +excursionists,Dominican Republic,World,2006,303.0,thousand trips +excursionists,Dominican Republic,World,2007,385.0,thousand trips +excursionists,Dominican Republic,World,2008,475.0,thousand trips +excursionists,Dominican Republic,World,2009,497.0,thousand trips +excursionists,Dominican Republic,World,2010,353.0,thousand trips +excursionists,Dominican Republic,World,2011,348.0,thousand trips +excursionists,Dominican Republic,World,2012,338.0,thousand trips +excursionists,Dominican Republic,World,2013,424.0,thousand trips +excursionists,Dominican Republic,World,2014,435.0,thousand trips +excursionists,Dominican Republic,World,2015,529.0,thousand trips +excursionists,Dominican Republic,World,2016,809.0,thousand trips +excursionists,Dominican Republic,World,2017,1108.0,thousand trips +excursionists,Dominican Republic,World,2018,982.0,thousand trips +excursionists,Dominican Republic,World,2019,1104.0,thousand trips +excursionists,Dominican Republic,World,2020,343.0,thousand trips +excursionists,Dominican Republic,World,2021,333.0,thousand trips +excursionists,Dominican Republic,World,2022,1325.4,thousand trips +excursionists,Grenada,World,1995,250.0,thousand trips +excursionists,Grenada,World,1996,267.0,thousand trips +excursionists,Grenada,World,1997,247.0,thousand trips +excursionists,Grenada,World,1998,266.0,thousand trips +excursionists,Grenada,World,1999,246.0,thousand trips +excursionists,Grenada,World,2000,180.0,thousand trips +excursionists,Grenada,World,2001,147.0,thousand trips +excursionists,Grenada,World,2002,135.0,thousand trips +excursionists,Grenada,World,2003,147.0,thousand trips +excursionists,Grenada,World,2004,230.0,thousand trips +excursionists,Grenada,World,2005,275.0,thousand trips +excursionists,Grenada,World,2006,219.0,thousand trips +excursionists,Grenada,World,2007,270.0,thousand trips +excursionists,Grenada,World,2008,293.0,thousand trips +excursionists,Grenada,World,2009,343.0,thousand trips +excursionists,Grenada,World,2010,333.0,thousand trips +excursionists,Grenada,World,2011,310.0,thousand trips +excursionists,Grenada,World,2012,243.0,thousand trips +excursionists,Grenada,World,2013,197.0,thousand trips +excursionists,Grenada,World,2014,235.0,thousand trips +excursionists,Grenada,World,2015,280.5,thousand trips +excursionists,Grenada,World,2016,315.0,thousand trips +excursionists,Grenada,World,2017,299.0,thousand trips +excursionists,Grenada,World,2018,343.0,thousand trips +excursionists,Grenada,World,2019,338.0,thousand trips +excursionists,Grenada,World,2020,162.5,thousand trips +excursionists,Grenada,World,2021,25.0,thousand trips +excursionists,Grenada,World,2022,186.0,thousand trips +excursionists,Guadeloupe,World,1995,419.0,thousand trips +excursionists,Guadeloupe,World,1996,611.0,thousand trips +excursionists,Guadeloupe,World,1997,470.0,thousand trips +excursionists,Guadeloupe,World,1998,334.0,thousand trips +excursionists,Guadeloupe,World,1999,293.0,thousand trips +excursionists,Guadeloupe,World,2000,392.0,thousand trips +excursionists,Guadeloupe,World,2001,238.0,thousand trips +excursionists,Guadeloupe,World,2002,148.0,thousand trips +excursionists,Guadeloupe,World,2003,130.0,thousand trips +excursionists,Guadeloupe,World,2004,104.0,thousand trips +excursionists,Guadeloupe,World,2005,73.0,thousand trips +excursionists,Guadeloupe,World,2006,72.0,thousand trips +excursionists,Guadeloupe,World,2007,92.0,thousand trips +excursionists,Guadeloupe,World,2008,115.0,thousand trips +excursionists,Guadeloupe,World,2009,111.0,thousand trips +excursionists,Guadeloupe,World,2010,105.0,thousand trips +excursionists,Guadeloupe,World,2011,102.0,thousand trips +excursionists,Guadeloupe,World,2012,162.0,thousand trips +excursionists,Guadeloupe,World,2013,158.0,thousand trips +excursionists,Guadeloupe,World,2014,234.0,thousand trips +excursionists,Guadeloupe,World,2015,310.0,thousand trips +excursionists,Guadeloupe,World,2016,276.0,thousand trips +excursionists,Guadeloupe,World,2017,320.0,thousand trips +excursionists,Guadeloupe,World,2018,431.0,thousand trips +excursionists,Haiti,World,1995,225.0,thousand trips +excursionists,Haiti,World,1996,250.0,thousand trips +excursionists,Haiti,World,1997,238.0,thousand trips +excursionists,Haiti,World,1998,246.0,thousand trips +excursionists,Haiti,World,1999,243.0,thousand trips +excursionists,Haiti,World,2000,305.0,thousand trips +excursionists,Haiti,World,2001,357.0,thousand trips +excursionists,Haiti,World,2002,342.0,thousand trips +excursionists,Haiti,World,2003,382.0,thousand trips +excursionists,Haiti,World,2004,289.0,thousand trips +excursionists,Haiti,World,2005,368.0,thousand trips +excursionists,Haiti,World,2006,450.0,thousand trips +excursionists,Haiti,World,2007,482.0,thousand trips +excursionists,Haiti,World,2008,500.0,thousand trips +excursionists,Haiti,World,2009,439.0,thousand trips +excursionists,Haiti,World,2010,538.0,thousand trips +excursionists,Haiti,World,2011,597.0,thousand trips +excursionists,Haiti,World,2012,610.0,thousand trips +excursionists,Haiti,World,2013,644.0,thousand trips +excursionists,Haiti,World,2014,662.4,thousand trips +excursionists,Haiti,World,2015,674.0,thousand trips +excursionists,Haiti,World,2016,708.0,thousand trips +excursionists,Haiti,World,2017,795.0,thousand trips +excursionists,Haiti,World,2018,885.0,thousand trips +excursionists,Haiti,World,2019,652.0,thousand trips +excursionists,Haiti,World,2020,119.0,thousand trips +excursionists,Jamaica,World,1995,605.0,thousand trips +excursionists,Jamaica,World,1996,658.0,thousand trips +excursionists,Jamaica,World,1997,712.0,thousand trips +excursionists,Jamaica,World,1998,674.0,thousand trips +excursionists,Jamaica,World,1999,764.0,thousand trips +excursionists,Jamaica,World,2000,908.0,thousand trips +excursionists,Jamaica,World,2001,840.0,thousand trips +excursionists,Jamaica,World,2002,865.0,thousand trips +excursionists,Jamaica,World,2003,1133.0,thousand trips +excursionists,Jamaica,World,2004,1100.0,thousand trips +excursionists,Jamaica,World,2005,1136.0,thousand trips +excursionists,Jamaica,World,2006,1337.0,thousand trips +excursionists,Jamaica,World,2007,1180.0,thousand trips +excursionists,Jamaica,World,2008,1092.0,thousand trips +excursionists,Jamaica,World,2009,922.0,thousand trips +excursionists,Jamaica,World,2010,910.0,thousand trips +excursionists,Jamaica,World,2011,1125.0,thousand trips +excursionists,Jamaica,World,2012,1320.0,thousand trips +excursionists,Jamaica,World,2013,1265.0,thousand trips +excursionists,Jamaica,World,2014,1424.0,thousand trips +excursionists,Jamaica,World,2015,1569.0,thousand trips +excursionists,Jamaica,World,2016,1655.6,thousand trips +excursionists,Jamaica,World,2017,1923.0,thousand trips +excursionists,Jamaica,World,2018,1846.0,thousand trips +excursionists,Jamaica,World,2019,1553.0,thousand trips +excursionists,Jamaica,World,2020,449.3,thousand trips +excursionists,Jamaica,World,2021,71.0,thousand trips +excursionists,Jamaica,World,2022,852.3,thousand trips +excursionists,Martinique,World,1995,428.0,thousand trips +excursionists,Martinique,World,1996,408.0,thousand trips +excursionists,Martinique,World,1997,387.0,thousand trips +excursionists,Martinique,World,1998,415.0,thousand trips +excursionists,Martinique,World,1999,339.0,thousand trips +excursionists,Martinique,World,2000,290.0,thousand trips +excursionists,Martinique,World,2001,203.0,thousand trips +excursionists,Martinique,World,2002,207.0,thousand trips +excursionists,Martinique,World,2003,269.0,thousand trips +excursionists,Martinique,World,2004,159.0,thousand trips +excursionists,Martinique,World,2005,93.0,thousand trips +excursionists,Martinique,World,2006,96.0,thousand trips +excursionists,Martinique,World,2007,72.0,thousand trips +excursionists,Martinique,World,2008,87.0,thousand trips +excursionists,Martinique,World,2009,70.0,thousand trips +excursionists,Martinique,World,2010,75.0,thousand trips +excursionists,Martinique,World,2011,41.0,thousand trips +excursionists,Martinique,World,2012,92.0,thousand trips +excursionists,Martinique,World,2013,104.0,thousand trips +excursionists,Martinique,World,2014,178.0,thousand trips +excursionists,Martinique,World,2015,242.0,thousand trips +excursionists,Martinique,World,2016,281.0,thousand trips +excursionists,Martinique,World,2017,406.0,thousand trips +excursionists,Martinique,World,2018,393.0,thousand trips +excursionists,Martinique,World,2019,285.0,thousand trips +excursionists,Martinique,World,2020,170.4,thousand trips +excursionists,Martinique,World,2022,75.0,thousand trips +excursionists,Montserrat,World,2001,0.3,thousand trips +excursionists,Montserrat,World,2003,0.8,thousand trips +excursionists,Montserrat,World,2004,0.4,thousand trips +excursionists,Montserrat,World,2005,0.3,thousand trips +excursionists,Montserrat,World,2006,0.1,thousand trips +excursionists,Montserrat,World,2007,0.3,thousand trips +excursionists,Montserrat,World,2008,0.3,thousand trips +excursionists,Montserrat,World,2009,0.2,thousand trips +excursionists,Montserrat,World,2010,0.9,thousand trips +excursionists,Montserrat,World,2011,1.1,thousand trips +excursionists,Montserrat,World,2012,0.8,thousand trips +excursionists,Montserrat,World,2013,0.4,thousand trips +excursionists,Montserrat,World,2014,0.2,thousand trips +excursionists,Montserrat,World,2015,2.6,thousand trips +excursionists,Montserrat,World,2016,3.6,thousand trips +excursionists,Montserrat,World,2017,7.1,thousand trips +excursionists,Montserrat,World,2018,4.3,thousand trips +excursionists,Montserrat,World,2019,6.8,thousand trips +excursionists,Montserrat,World,2020,2.8,thousand trips +excursionists,Curaçao,World,1995,172.0,thousand trips +excursionists,Curaçao,World,1996,173.0,thousand trips +excursionists,Curaçao,World,1997,217.0,thousand trips +excursionists,Curaçao,World,1998,231.0,thousand trips +excursionists,Curaçao,World,1999,221.0,thousand trips +excursionists,Curaçao,World,2000,308.0,thousand trips +excursionists,Curaçao,World,2001,300.0,thousand trips +excursionists,Curaçao,World,2002,319.0,thousand trips +excursionists,Curaçao,World,2003,279.0,thousand trips +excursionists,Curaçao,World,2004,219.0,thousand trips +excursionists,Curaçao,World,2005,276.0,thousand trips +excursionists,Curaçao,World,2006,322.0,thousand trips +excursionists,Curaçao,World,2007,341.0,thousand trips +excursionists,Curaçao,World,2008,353.0,thousand trips +excursionists,Curaçao,World,2009,423.0,thousand trips +excursionists,Curaçao,World,2010,383.0,thousand trips +excursionists,Curaçao,World,2011,401.0,thousand trips +excursionists,Curaçao,World,2012,436.0,thousand trips +excursionists,Curaçao,World,2013,584.0,thousand trips +excursionists,Curaçao,World,2014,629.0,thousand trips +excursionists,Curaçao,World,2015,566.0,thousand trips +excursionists,Curaçao,World,2016,469.0,thousand trips +excursionists,Curaçao,World,2017,634.0,thousand trips +excursionists,Curaçao,World,2018,761.0,thousand trips +excursionists,Curaçao,World,2019,810.0,thousand trips +excursionists,Curaçao,World,2020,256.0,thousand trips +excursionists,Curaçao,World,2021,146.2,thousand trips +excursionists,Curaçao,World,2022,533.6,thousand trips +excursionists,Aruba,World,1995,293.0,thousand trips +excursionists,Aruba,World,1996,316.0,thousand trips +excursionists,Aruba,World,1997,297.0,thousand trips +excursionists,Aruba,World,1998,259.0,thousand trips +excursionists,Aruba,World,1999,289.0,thousand trips +excursionists,Aruba,World,2000,490.0,thousand trips +excursionists,Aruba,World,2001,487.0,thousand trips +excursionists,Aruba,World,2002,582.0,thousand trips +excursionists,Aruba,World,2003,542.0,thousand trips +excursionists,Aruba,World,2004,576.0,thousand trips +excursionists,Aruba,World,2005,553.0,thousand trips +excursionists,Aruba,World,2006,591.0,thousand trips +excursionists,Aruba,World,2007,482.0,thousand trips +excursionists,Aruba,World,2008,556.0,thousand trips +excursionists,Aruba,World,2009,607.0,thousand trips +excursionists,Aruba,World,2010,570.0,thousand trips +excursionists,Aruba,World,2011,600.0,thousand trips +excursionists,Aruba,World,2012,577.0,thousand trips +excursionists,Aruba,World,2013,688.0,thousand trips +excursionists,Aruba,World,2014,667.0,thousand trips +excursionists,Aruba,World,2015,607.0,thousand trips +excursionists,Aruba,World,2016,656.0,thousand trips +excursionists,Aruba,World,2017,792.4,thousand trips +excursionists,Aruba,World,2018,815.0,thousand trips +excursionists,Aruba,World,2019,832.0,thousand trips +excursionists,Aruba,World,2020,255.0,thousand trips +excursionists,Aruba,World,2021,136.0,thousand trips +excursionists,Aruba,World,2022,610.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1995,564.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1996,657.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1997,886.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1998,881.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1999,616.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2000,868.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2001,868.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2002,1055.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2003,1172.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2004,1348.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2005,1488.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2006,1422.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2007,1422.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2008,1346.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2009,1215.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2010,1513.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2011,1656.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2012,1753.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2013,1786.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2014,2002.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2015,1902.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2016,1669.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2017,1238.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2018,1597.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2019,1632.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2020,436.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2021,233.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2022,844.0,thousand trips +excursionists,Puerto Rico,World,1995,956.0,thousand trips +excursionists,Puerto Rico,World,1996,1045.0,thousand trips +excursionists,Puerto Rico,World,1997,1106.0,thousand trips +excursionists,Puerto Rico,World,1998,1266.0,thousand trips +excursionists,Puerto Rico,World,1999,1196.0,thousand trips +excursionists,Puerto Rico,World,2000,1221.0,thousand trips +excursionists,Puerto Rico,World,2001,1357.0,thousand trips +excursionists,Puerto Rico,World,2002,1276.0,thousand trips +excursionists,Puerto Rico,World,2003,1164.0,thousand trips +excursionists,Puerto Rico,World,2004,1348.0,thousand trips +excursionists,Puerto Rico,World,2005,1387.0,thousand trips +excursionists,Puerto Rico,World,2006,1300.0,thousand trips +excursionists,Puerto Rico,World,2007,1375.0,thousand trips +excursionists,Puerto Rico,World,2008,1497.0,thousand trips +excursionists,Puerto Rico,World,2009,1232.0,thousand trips +excursionists,Puerto Rico,World,2010,1194.0,thousand trips +excursionists,Puerto Rico,World,2011,1166.0,thousand trips +excursionists,Puerto Rico,World,2012,1128.0,thousand trips +excursionists,Puerto Rico,World,2013,1038.0,thousand trips +excursionists,Puerto Rico,World,2014,1210.0,thousand trips +excursionists,Puerto Rico,World,2015,1509.0,thousand trips +excursionists,Puerto Rico,World,2016,1267.0,thousand trips +excursionists,Puerto Rico,World,2017,1414.0,thousand trips +excursionists,Puerto Rico,World,2018,1192.0,thousand trips +excursionists,Puerto Rico,World,2019,1751.0,thousand trips +excursionists,Puerto Rico,World,2020,1264.6,thousand trips +excursionists,Puerto Rico,World,2022,364.4,thousand trips +excursionists,Saint Kitts and Nevis,World,1995,121.0,thousand trips +excursionists,Saint Kitts and Nevis,World,1996,86.0,thousand trips +excursionists,Saint Kitts and Nevis,World,1997,103.0,thousand trips +excursionists,Saint Kitts and Nevis,World,1998,154.0,thousand trips +excursionists,Saint Kitts and Nevis,World,1999,139.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2000,171.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2001,259.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2002,167.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2003,152.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2004,262.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2005,220.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2006,206.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2007,251.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2008,401.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2009,454.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2010,516.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2011,604.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2012,553.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2013,575.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2014,701.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2015,913.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2016,935.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2017,1074.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2018,1148.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2019,983.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2020,270.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2021,101.9,thousand trips +excursionists,Saint Kitts and Nevis,World,2022,506.4,thousand trips +excursionists,Anguilla,World,2004,6.0,thousand trips +excursionists,Anguilla,World,2005,5.0,thousand trips +excursionists,Anguilla,World,2006,6.0,thousand trips +excursionists,Anguilla,World,2007,4.0,thousand trips +excursionists,Anguilla,World,2008,0.5,thousand trips +excursionists,Anguilla,World,2009,2.0,thousand trips +excursionists,Anguilla,World,2010,2.0,thousand trips +excursionists,Anguilla,World,2011,2.0,thousand trips +excursionists,Anguilla,World,2012,3.0,thousand trips +excursionists,Anguilla,World,2013,3.0,thousand trips +excursionists,Anguilla,World,2014,6.0,thousand trips +excursionists,Anguilla,World,2015,7.0,thousand trips +excursionists,Anguilla,World,2016,3.0,thousand trips +excursionists,Anguilla,World,2017,3.0,thousand trips +excursionists,Anguilla,World,2018,3.5,thousand trips +excursionists,Anguilla,World,2019,6.1,thousand trips +excursionists,Anguilla,World,2020,1.4,thousand trips +excursionists,Saint Lucia,World,1995,170.0,thousand trips +excursionists,Saint Lucia,World,1996,180.0,thousand trips +excursionists,Saint Lucia,World,1997,310.0,thousand trips +excursionists,Saint Lucia,World,1998,372.0,thousand trips +excursionists,Saint Lucia,World,1999,395.0,thousand trips +excursionists,Saint Lucia,World,2000,444.0,thousand trips +excursionists,Saint Lucia,World,2001,490.0,thousand trips +excursionists,Saint Lucia,World,2002,387.0,thousand trips +excursionists,Saint Lucia,World,2003,393.0,thousand trips +excursionists,Saint Lucia,World,2004,481.0,thousand trips +excursionists,Saint Lucia,World,2005,394.0,thousand trips +excursionists,Saint Lucia,World,2006,360.0,thousand trips +excursionists,Saint Lucia,World,2007,610.0,thousand trips +excursionists,Saint Lucia,World,2008,620.0,thousand trips +excursionists,Saint Lucia,World,2009,699.0,thousand trips +excursionists,Saint Lucia,World,2010,670.0,thousand trips +excursionists,Saint Lucia,World,2011,630.0,thousand trips +excursionists,Saint Lucia,World,2012,572.0,thousand trips +excursionists,Saint Lucia,World,2013,594.0,thousand trips +excursionists,Saint Lucia,World,2014,641.0,thousand trips +excursionists,Saint Lucia,World,2015,677.0,thousand trips +excursionists,Saint Lucia,World,2016,587.0,thousand trips +excursionists,Saint Lucia,World,2017,669.0,thousand trips +excursionists,Saint Lucia,World,2018,760.0,thousand trips +excursionists,Saint Lucia,World,2019,787.0,thousand trips +excursionists,Saint Lucia,World,2020,297.9,thousand trips +excursionists,Saint Lucia,World,2021,94.0,thousand trips +excursionists,Saint Lucia,World,2022,350.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1995,127.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1996,128.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1997,107.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1998,114.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1999,137.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2000,162.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2001,168.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2002,157.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2003,149.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2004,162.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2005,152.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2006,200.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2007,231.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2008,160.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2009,190.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2010,153.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2011,130.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2012,122.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2013,126.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2014,132.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2015,130.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2016,147.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2017,226.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2018,275.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2019,313.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2020,128.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2021,32.4,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2022,151.0,thousand trips +excursionists,Trinidad and Tobago,World,1995,22.0,thousand trips +excursionists,Trinidad and Tobago,World,1996,51.0,thousand trips +excursionists,Trinidad and Tobago,World,1997,36.0,thousand trips +excursionists,Trinidad and Tobago,World,1998,43.0,thousand trips +excursionists,Trinidad and Tobago,World,1999,63.0,thousand trips +excursionists,Trinidad and Tobago,World,2000,104.0,thousand trips +excursionists,Trinidad and Tobago,World,2001,85.0,thousand trips +excursionists,Trinidad and Tobago,World,2002,67.0,thousand trips +excursionists,Trinidad and Tobago,World,2003,65.0,thousand trips +excursionists,Trinidad and Tobago,World,2004,58.0,thousand trips +excursionists,Trinidad and Tobago,World,2005,66.0,thousand trips +excursionists,Trinidad and Tobago,World,2006,73.0,thousand trips +excursionists,Trinidad and Tobago,World,2007,82.0,thousand trips +excursionists,Trinidad and Tobago,World,2008,45.0,thousand trips +excursionists,Trinidad and Tobago,World,2009,115.0,thousand trips +excursionists,Trinidad and Tobago,World,2010,102.0,thousand trips +excursionists,Trinidad and Tobago,World,2011,60.0,thousand trips +excursionists,Trinidad and Tobago,World,2012,49.0,thousand trips +excursionists,Trinidad and Tobago,World,2013,33.0,thousand trips +excursionists,Trinidad and Tobago,World,2014,43.0,thousand trips +excursionists,Trinidad and Tobago,World,2015,79.6,thousand trips +excursionists,Trinidad and Tobago,World,2016,83.0,thousand trips +excursionists,Trinidad and Tobago,World,2017,70.1,thousand trips +excursionists,Trinidad and Tobago,World,2018,125.6,thousand trips +excursionists,Trinidad and Tobago,World,2019,91.4,thousand trips +excursionists,Trinidad and Tobago,World,2020,45.6,thousand trips +excursionists,Trinidad and Tobago,World,2022,25.9,thousand trips +excursionists,Turks and Caicos Islands,World,2008,405.0,thousand trips +excursionists,Turks and Caicos Islands,World,2009,514.0,thousand trips +excursionists,Turks and Caicos Islands,World,2010,618.0,thousand trips +excursionists,Turks and Caicos Islands,World,2011,655.5,thousand trips +excursionists,Turks and Caicos Islands,World,2012,676.6,thousand trips +excursionists,Turks and Caicos Islands,World,2013,779.0,thousand trips +excursionists,Turks and Caicos Islands,World,2014,972.0,thousand trips +excursionists,Turks and Caicos Islands,World,2015,930.0,thousand trips +excursionists,Turks and Caicos Islands,World,2016,847.0,thousand trips +excursionists,Turks and Caicos Islands,World,2017,827.0,thousand trips +excursionists,Turks and Caicos Islands,World,2018,1022.0,thousand trips +excursionists,Turks and Caicos Islands,World,2019,1112.0,thousand trips +excursionists,Turks and Caicos Islands,World,2020,205.9,thousand trips +excursionists,Turks and Caicos Islands,World,2021,27.0,thousand trips +excursionists,United States Virgin Islands,World,1995,1171.0,thousand trips +excursionists,United States Virgin Islands,World,1996,1316.0,thousand trips +excursionists,United States Virgin Islands,World,1997,1619.0,thousand trips +excursionists,United States Virgin Islands,World,1998,1616.0,thousand trips +excursionists,United States Virgin Islands,World,1999,1404.0,thousand trips +excursionists,United States Virgin Islands,World,2000,1768.0,thousand trips +excursionists,United States Virgin Islands,World,2001,1891.0,thousand trips +excursionists,United States Virgin Islands,World,2002,1739.0,thousand trips +excursionists,United States Virgin Islands,World,2003,1774.0,thousand trips +excursionists,United States Virgin Islands,World,2004,1965.0,thousand trips +excursionists,United States Virgin Islands,World,2005,1913.0,thousand trips +excursionists,United States Virgin Islands,World,2006,1903.0,thousand trips +excursionists,United States Virgin Islands,World,2007,1918.0,thousand trips +excursionists,United States Virgin Islands,World,2008,1757.0,thousand trips +excursionists,United States Virgin Islands,World,2009,1582.0,thousand trips +excursionists,United States Virgin Islands,World,2010,1859.0,thousand trips +excursionists,United States Virgin Islands,World,2011,2009.0,thousand trips +excursionists,United States Virgin Islands,World,2012,1904.0,thousand trips +excursionists,United States Virgin Islands,World,2013,1999.0,thousand trips +excursionists,United States Virgin Islands,World,2014,2084.0,thousand trips +excursionists,United States Virgin Islands,World,2015,1879.0,thousand trips +excursionists,United States Virgin Islands,World,2016,1777.0,thousand trips +excursionists,United States Virgin Islands,World,2017,1304.0,thousand trips +excursionists,United States Virgin Islands,World,2018,1431.0,thousand trips +excursionists,United States Virgin Islands,World,2019,1433.0,thousand trips +excursionists,United States Virgin Islands,World,2020,442.0,thousand trips +excursionists,United States Virgin Islands,World,2021,245.7,thousand trips +excursionists,Bonaire,World,1995,11.0,thousand trips +excursionists,Bonaire,World,1996,15.0,thousand trips +excursionists,Bonaire,World,1997,20.0,thousand trips +excursionists,Bonaire,World,1998,20.0,thousand trips +excursionists,Bonaire,World,1999,15.0,thousand trips +excursionists,Bonaire,World,2000,44.0,thousand trips +excursionists,Bonaire,World,2001,43.0,thousand trips +excursionists,Bonaire,World,2002,42.0,thousand trips +excursionists,Bonaire,World,2003,45.0,thousand trips +excursionists,Bonaire,World,2004,53.0,thousand trips +excursionists,Bonaire,World,2005,40.0,thousand trips +excursionists,Bonaire,World,2006,62.0,thousand trips +excursionists,Bonaire,World,2007,98.0,thousand trips +excursionists,Bonaire,World,2008,176.0,thousand trips +excursionists,Bonaire,World,2009,213.0,thousand trips +excursionists,Bonaire,World,2010,226.0,thousand trips +tourists,Antigua and Barbuda,World,1995,220.0,thousand trips +tourists,Antigua and Barbuda,World,1996,228.0,thousand trips +tourists,Antigua and Barbuda,World,1997,240.0,thousand trips +tourists,Antigua and Barbuda,World,1998,234.0,thousand trips +tourists,Antigua and Barbuda,World,1999,232.0,thousand trips +tourists,Antigua and Barbuda,World,2000,207.0,thousand trips +tourists,Antigua and Barbuda,World,2001,215.0,thousand trips +tourists,Antigua and Barbuda,World,2002,218.0,thousand trips +tourists,Antigua and Barbuda,World,2003,239.0,thousand trips +tourists,Antigua and Barbuda,World,2004,246.0,thousand trips +tourists,Antigua and Barbuda,World,2005,245.0,thousand trips +tourists,Antigua and Barbuda,World,2006,254.0,thousand trips +tourists,Antigua and Barbuda,World,2007,262.0,thousand trips +tourists,Antigua and Barbuda,World,2008,266.0,thousand trips +tourists,Antigua and Barbuda,World,2009,234.0,thousand trips +tourists,Antigua and Barbuda,World,2010,230.0,thousand trips +tourists,Antigua and Barbuda,World,2011,241.0,thousand trips +tourists,Antigua and Barbuda,World,2012,247.0,thousand trips +tourists,Antigua and Barbuda,World,2013,243.0,thousand trips +tourists,Antigua and Barbuda,World,2014,249.0,thousand trips +tourists,Antigua and Barbuda,World,2015,250.0,thousand trips +tourists,Antigua and Barbuda,World,2016,265.0,thousand trips +tourists,Antigua and Barbuda,World,2017,247.0,thousand trips +tourists,Antigua and Barbuda,World,2018,269.0,thousand trips +tourists,Antigua and Barbuda,World,2019,301.0,thousand trips +tourists,Antigua and Barbuda,World,2020,125.0,thousand trips +tourists,Antigua and Barbuda,World,2021,169.4,thousand trips +tourists,Antigua and Barbuda,World,2022,265.1,thousand trips +tourists,Antigua and Barbuda,World,2023,281.8,thousand trips +tourists,Antigua and Barbuda,World,2024,330.2,thousand trips +tourists,Bahamas,World,1995,1598.0,thousand trips +tourists,Bahamas,World,1996,1633.0,thousand trips +tourists,Bahamas,World,1997,1618.0,thousand trips +tourists,Bahamas,World,1998,1528.0,thousand trips +tourists,Bahamas,World,1999,1577.0,thousand trips +tourists,Bahamas,World,2000,1544.0,thousand trips +tourists,Bahamas,World,2001,1538.0,thousand trips +tourists,Bahamas,World,2002,1513.0,thousand trips +tourists,Bahamas,World,2003,1505.0,thousand trips +tourists,Bahamas,World,2004,1561.0,thousand trips +tourists,Bahamas,World,2005,1625.0,thousand trips +tourists,Bahamas,World,2006,1613.0,thousand trips +tourists,Bahamas,World,2007,1538.0,thousand trips +tourists,Bahamas,World,2008,1469.0,thousand trips +tourists,Bahamas,World,2009,1336.0,thousand trips +tourists,Bahamas,World,2010,1378.0,thousand trips +tourists,Bahamas,World,2011,1355.0,thousand trips +tourists,Bahamas,World,2012,1431.0,thousand trips +tourists,Bahamas,World,2013,1377.0,thousand trips +tourists,Bahamas,World,2014,1444.0,thousand trips +tourists,Bahamas,World,2015,1496.0,thousand trips +tourists,Bahamas,World,2016,1499.0,thousand trips +tourists,Bahamas,World,2017,1452.0,thousand trips +tourists,Bahamas,World,2018,1633.0,thousand trips +tourists,Bahamas,World,2019,1807.0,thousand trips +tourists,Bahamas,World,2020,441.0,thousand trips +tourists,Bahamas,World,2021,892.0,thousand trips +tourists,Bahamas,World,2022,1452.4,thousand trips +tourists,Barbados,World,1995,442.0,thousand trips +tourists,Barbados,World,1996,447.0,thousand trips +tourists,Barbados,World,1997,472.0,thousand trips +tourists,Barbados,World,1998,512.0,thousand trips +tourists,Barbados,World,1999,515.0,thousand trips +tourists,Barbados,World,2000,545.0,thousand trips +tourists,Barbados,World,2001,507.0,thousand trips +tourists,Barbados,World,2002,498.0,thousand trips +tourists,Barbados,World,2003,531.0,thousand trips +tourists,Barbados,World,2004,552.0,thousand trips +tourists,Barbados,World,2005,548.0,thousand trips +tourists,Barbados,World,2006,563.0,thousand trips +tourists,Barbados,World,2007,575.0,thousand trips +tourists,Barbados,World,2008,568.0,thousand trips +tourists,Barbados,World,2009,519.0,thousand trips +tourists,Barbados,World,2010,532.0,thousand trips +tourists,Barbados,World,2011,568.0,thousand trips +tourists,Barbados,World,2012,536.0,thousand trips +tourists,Barbados,World,2013,509.0,thousand trips +tourists,Barbados,World,2014,521.0,thousand trips +tourists,Barbados,World,2015,592.0,thousand trips +tourists,Barbados,World,2016,632.0,thousand trips +tourists,Barbados,World,2017,664.0,thousand trips +tourists,Barbados,World,2018,680.0,thousand trips +tourists,Barbados,World,2019,523.0,thousand trips +tourists,Barbados,World,2020,207.4,thousand trips +tourists,Barbados,World,2021,144.8,thousand trips +tourists,Barbados,World,2022,539.7,thousand trips +tourists,Barbados,World,2023,636.0459,thousand trips +tourists,Barbados,World,2024,704.34,thousand trips +tourists,Bermuda,World,1995,387.0,thousand trips +tourists,Bermuda,World,1996,390.0,thousand trips +tourists,Bermuda,World,1997,380.0,thousand trips +tourists,Bermuda,World,1998,370.0,thousand trips +tourists,Bermuda,World,1999,355.0,thousand trips +tourists,Bermuda,World,2000,332.0,thousand trips +tourists,Bermuda,World,2001,278.0,thousand trips +tourists,Bermuda,World,2002,284.0,thousand trips +tourists,Bermuda,World,2003,257.0,thousand trips +tourists,Bermuda,World,2004,272.0,thousand trips +tourists,Bermuda,World,2005,270.0,thousand trips +tourists,Bermuda,World,2006,299.0,thousand trips +tourists,Bermuda,World,2007,306.0,thousand trips +tourists,Bermuda,World,2008,264.0,thousand trips +tourists,Bermuda,World,2009,236.0,thousand trips +tourists,Bermuda,World,2010,232.0,thousand trips +tourists,Bermuda,World,2011,236.0,thousand trips +tourists,Bermuda,World,2012,232.0,thousand trips +tourists,Bermuda,World,2013,236.0,thousand trips +tourists,Bermuda,World,2014,224.0,thousand trips +tourists,Bermuda,World,2015,220.0,thousand trips +tourists,Bermuda,World,2016,244.0,thousand trips +tourists,Bermuda,World,2017,270.0,thousand trips +tourists,Bermuda,World,2018,282.0,thousand trips +tourists,Bermuda,World,2019,269.0,thousand trips +tourists,Bermuda,World,2020,42.1,thousand trips +tourists,Bermuda,World,2021,72.2,thousand trips +tourists,Bermuda,World,2022,145.9,thousand trips +tourists,Belize,World,1995,131.0,thousand trips +tourists,Belize,World,1996,133.0,thousand trips +tourists,Belize,World,1997,146.0,thousand trips +tourists,Belize,World,1998,176.0,thousand trips +tourists,Belize,World,1999,181.0,thousand trips +tourists,Belize,World,2000,196.0,thousand trips +tourists,Belize,World,2001,196.0,thousand trips +tourists,Belize,World,2002,200.0,thousand trips +tourists,Belize,World,2003,221.0,thousand trips +tourists,Belize,World,2004,231.0,thousand trips +tourists,Belize,World,2005,237.0,thousand trips +tourists,Belize,World,2006,247.0,thousand trips +tourists,Belize,World,2007,251.0,thousand trips +tourists,Belize,World,2008,245.0,thousand trips +tourists,Belize,World,2009,232.0,thousand trips +tourists,Belize,World,2010,242.0,thousand trips +tourists,Belize,World,2011,250.0,thousand trips +tourists,Belize,World,2012,277.0,thousand trips +tourists,Belize,World,2013,294.0,thousand trips +tourists,Belize,World,2014,321.0,thousand trips +tourists,Belize,World,2015,341.0,thousand trips +tourists,Belize,World,2016,386.0,thousand trips +tourists,Belize,World,2017,427.0,thousand trips +tourists,Belize,World,2018,489.0,thousand trips +tourists,Belize,World,2019,503.0,thousand trips +tourists,Belize,World,2020,144.1,thousand trips +tourists,Belize,World,2021,219.0,thousand trips +tourists,Belize,World,2022,372.6,thousand trips +tourists,British Virgin Islands,World,1995,219.0,thousand trips +tourists,British Virgin Islands,World,1996,244.0,thousand trips +tourists,British Virgin Islands,World,1997,244.0,thousand trips +tourists,British Virgin Islands,World,1998,279.0,thousand trips +tourists,British Virgin Islands,World,1999,284.0,thousand trips +tourists,British Virgin Islands,World,2000,272.0,thousand trips +tourists,British Virgin Islands,World,2001,296.0,thousand trips +tourists,British Virgin Islands,World,2002,282.0,thousand trips +tourists,British Virgin Islands,World,2003,278.0,thousand trips +tourists,British Virgin Islands,World,2004,304.0,thousand trips +tourists,British Virgin Islands,World,2005,337.0,thousand trips +tourists,British Virgin Islands,World,2006,356.0,thousand trips +tourists,British Virgin Islands,World,2007,358.0,thousand trips +tourists,British Virgin Islands,World,2008,346.0,thousand trips +tourists,British Virgin Islands,World,2009,309.0,thousand trips +tourists,British Virgin Islands,World,2010,330.0,thousand trips +tourists,British Virgin Islands,World,2011,338.0,thousand trips +tourists,British Virgin Islands,World,2012,351.0,thousand trips +tourists,British Virgin Islands,World,2013,366.0,thousand trips +tourists,British Virgin Islands,World,2014,386.0,thousand trips +tourists,British Virgin Islands,World,2015,393.0,thousand trips +tourists,British Virgin Islands,World,2016,408.0,thousand trips +tourists,British Virgin Islands,World,2017,335.0,thousand trips +tourists,British Virgin Islands,World,2018,192.0,thousand trips +tourists,British Virgin Islands,World,2019,302.4,thousand trips +tourists,British Virgin Islands,World,2020,83.0,thousand trips +tourists,British Virgin Islands,World,2021,56.0,thousand trips +tourists,British Virgin Islands,World,2022,173.0,thousand trips +tourists,Cayman Islands,World,1995,361.0,thousand trips +tourists,Cayman Islands,World,1996,373.0,thousand trips +tourists,Cayman Islands,World,1997,381.0,thousand trips +tourists,Cayman Islands,World,1998,404.0,thousand trips +tourists,Cayman Islands,World,1999,395.0,thousand trips +tourists,Cayman Islands,World,2000,354.0,thousand trips +tourists,Cayman Islands,World,2001,334.0,thousand trips +tourists,Cayman Islands,World,2002,303.0,thousand trips +tourists,Cayman Islands,World,2003,294.0,thousand trips +tourists,Cayman Islands,World,2004,260.0,thousand trips +tourists,Cayman Islands,World,2005,168.0,thousand trips +tourists,Cayman Islands,World,2006,267.0,thousand trips +tourists,Cayman Islands,World,2007,292.0,thousand trips +tourists,Cayman Islands,World,2008,303.0,thousand trips +tourists,Cayman Islands,World,2009,272.0,thousand trips +tourists,Cayman Islands,World,2010,288.0,thousand trips +tourists,Cayman Islands,World,2011,309.0,thousand trips +tourists,Cayman Islands,World,2012,322.0,thousand trips +tourists,Cayman Islands,World,2013,345.0,thousand trips +tourists,Cayman Islands,World,2014,383.0,thousand trips +tourists,Cayman Islands,World,2015,385.0,thousand trips +tourists,Cayman Islands,World,2016,385.0,thousand trips +tourists,Cayman Islands,World,2017,418.0,thousand trips +tourists,Cayman Islands,World,2018,463.0,thousand trips +tourists,Cayman Islands,World,2019,503.0,thousand trips +tourists,Cayman Islands,World,2020,122.0,thousand trips +tourists,Cayman Islands,World,2021,17.3,thousand trips +tourists,Cayman Islands,World,2022,284.3,thousand trips +tourists,Cuba,World,1995,742.0,thousand trips +tourists,Cuba,World,1996,999.0,thousand trips +tourists,Cuba,World,1997,1153.0,thousand trips +tourists,Cuba,World,1998,1390.0,thousand trips +tourists,Cuba,World,1999,1561.0,thousand trips +tourists,Cuba,World,2000,1741.0,thousand trips +tourists,Cuba,World,2001,1736.0,thousand trips +tourists,Cuba,World,2002,1656.0,thousand trips +tourists,Cuba,World,2003,1847.0,thousand trips +tourists,Cuba,World,2004,2017.0,thousand trips +tourists,Cuba,World,2005,2261.0,thousand trips +tourists,Cuba,World,2006,2150.0,thousand trips +tourists,Cuba,World,2007,2119.0,thousand trips +tourists,Cuba,World,2008,2316.0,thousand trips +tourists,Cuba,World,2009,2405.0,thousand trips +tourists,Cuba,World,2010,2507.0,thousand trips +tourists,Cuba,World,2011,2688.0,thousand trips +tourists,Cuba,World,2012,2823.0,thousand trips +tourists,Cuba,World,2013,2839.0,thousand trips +tourists,Cuba,World,2014,2981.0,thousand trips +tourists,Cuba,World,2015,3506.0,thousand trips +tourists,Cuba,World,2016,3975.0,thousand trips +tourists,Cuba,World,2017,4594.0,thousand trips +tourists,Cuba,World,2018,4684.0,thousand trips +tourists,Cuba,World,2019,4263.0,thousand trips +tourists,Cuba,World,2020,1085.0,thousand trips +tourists,Cuba,World,2021,355.5,thousand trips +tourists,Cuba,World,2022,1613.4,thousand trips +tourists,Dominica,World,1995,60.0,thousand trips +tourists,Dominica,World,1996,63.0,thousand trips +tourists,Dominica,World,1997,65.0,thousand trips +tourists,Dominica,World,1998,66.0,thousand trips +tourists,Dominica,World,1999,74.0,thousand trips +tourists,Dominica,World,2000,70.0,thousand trips +tourists,Dominica,World,2001,66.0,thousand trips +tourists,Dominica,World,2002,69.0,thousand trips +tourists,Dominica,World,2003,73.0,thousand trips +tourists,Dominica,World,2004,80.0,thousand trips +tourists,Dominica,World,2005,79.0,thousand trips +tourists,Dominica,World,2006,84.0,thousand trips +tourists,Dominica,World,2007,81.0,thousand trips +tourists,Dominica,World,2008,81.0,thousand trips +tourists,Dominica,World,2009,75.0,thousand trips +tourists,Dominica,World,2010,77.0,thousand trips +tourists,Dominica,World,2011,76.0,thousand trips +tourists,Dominica,World,2012,79.0,thousand trips +tourists,Dominica,World,2013,78.0,thousand trips +tourists,Dominica,World,2014,81.5,thousand trips +tourists,Dominica,World,2015,75.0,thousand trips +tourists,Dominica,World,2016,78.0,thousand trips +tourists,Dominica,World,2017,72.0,thousand trips +tourists,Dominica,World,2018,63.0,thousand trips +tourists,Dominica,World,2019,90.0,thousand trips +tourists,Dominica,World,2020,22.0,thousand trips +tourists,Dominica,World,2021,15.0,thousand trips +tourists,Dominica,World,2022,61.0,thousand trips +tourists,Dominican Republic,World,1995,1776.0,thousand trips +tourists,Dominican Republic,World,1996,1926.0,thousand trips +tourists,Dominican Republic,World,1997,2211.0,thousand trips +tourists,Dominican Republic,World,1998,2309.0,thousand trips +tourists,Dominican Republic,World,1999,2649.0,thousand trips +tourists,Dominican Republic,World,2000,2978.0,thousand trips +tourists,Dominican Republic,World,2001,2882.0,thousand trips +tourists,Dominican Republic,World,2002,2811.0,thousand trips +tourists,Dominican Republic,World,2003,3282.0,thousand trips +tourists,Dominican Republic,World,2004,3450.0,thousand trips +tourists,Dominican Republic,World,2005,3691.0,thousand trips +tourists,Dominican Republic,World,2006,3965.0,thousand trips +tourists,Dominican Republic,World,2007,3980.0,thousand trips +tourists,Dominican Republic,World,2008,3980.0,thousand trips +tourists,Dominican Republic,World,2009,3992.0,thousand trips +tourists,Dominican Republic,World,2010,4125.0,thousand trips +tourists,Dominican Republic,World,2011,4306.0,thousand trips +tourists,Dominican Republic,World,2012,4563.0,thousand trips +tourists,Dominican Republic,World,2013,4690.0,thousand trips +tourists,Dominican Republic,World,2014,5141.4,thousand trips +tourists,Dominican Republic,World,2015,5600.0,thousand trips +tourists,Dominican Republic,World,2016,5959.3,thousand trips +tourists,Dominican Republic,World,2017,6188.0,thousand trips +tourists,Dominican Republic,World,2018,6569.0,thousand trips +tourists,Dominican Republic,World,2019,6446.0,thousand trips +tourists,Dominican Republic,World,2020,2405.31,thousand trips +tourists,Dominican Republic,World,2021,4994.313,thousand trips +tourists,Dominican Republic,World,2022,7163.414,thousand trips +tourists,Dominican Republic,World,2023,8058.67127402904,thousand trips +tourists,Dominican Republic,World,2024,8535.70072276276,thousand trips +tourists,Grenada,World,1995,108.0,thousand trips +tourists,Grenada,World,1996,108.0,thousand trips +tourists,Grenada,World,1997,111.0,thousand trips +tourists,Grenada,World,1998,116.0,thousand trips +tourists,Grenada,World,1999,125.0,thousand trips +tourists,Grenada,World,2000,129.0,thousand trips +tourists,Grenada,World,2001,123.0,thousand trips +tourists,Grenada,World,2002,132.0,thousand trips +tourists,Grenada,World,2003,142.0,thousand trips +tourists,Grenada,World,2004,134.0,thousand trips +tourists,Grenada,World,2005,99.0,thousand trips +tourists,Grenada,World,2006,119.0,thousand trips +tourists,Grenada,World,2007,130.0,thousand trips +tourists,Grenada,World,2008,130.0,thousand trips +tourists,Grenada,World,2009,114.0,thousand trips +tourists,Grenada,World,2010,110.0,thousand trips +tourists,Grenada,World,2011,118.0,thousand trips +tourists,Grenada,World,2012,125.0,thousand trips +tourists,Grenada,World,2013,128.0,thousand trips +tourists,Grenada,World,2014,150.0,thousand trips +tourists,Grenada,World,2015,155.0,thousand trips +tourists,Grenada,World,2016,156.0,thousand trips +tourists,Grenada,World,2017,168.0,thousand trips +tourists,Grenada,World,2018,186.0,thousand trips +tourists,Grenada,World,2019,188.0,thousand trips +tourists,Grenada,World,2020,54.2,thousand trips +tourists,Grenada,World,2021,47.0,thousand trips +tourists,Grenada,World,2022,149.0,thousand trips +tourists,Guadeloupe,World,1995,640.0,thousand trips +tourists,Guadeloupe,World,1996,625.0,thousand trips +tourists,Guadeloupe,World,1997,660.0,thousand trips +tourists,Guadeloupe,World,1998,580.0,thousand trips +tourists,Guadeloupe,World,1999,561.0,thousand trips +tourists,Guadeloupe,World,2000,603.0,thousand trips +tourists,Guadeloupe,World,2001,521.0,thousand trips +tourists,Guadeloupe,World,2003,439.0,thousand trips +tourists,Guadeloupe,World,2004,456.0,thousand trips +tourists,Guadeloupe,World,2005,372.0,thousand trips +tourists,Guadeloupe,World,2006,375.0,thousand trips +tourists,Guadeloupe,World,2007,408.0,thousand trips +tourists,Guadeloupe,World,2008,412.0,thousand trips +tourists,Guadeloupe,World,2009,347.0,thousand trips +tourists,Guadeloupe,World,2010,392.0,thousand trips +tourists,Guadeloupe,World,2011,317.0,thousand trips +tourists,Guadeloupe,World,2012,325.0,thousand trips +tourists,Guadeloupe,World,2013,487.0,thousand trips +tourists,Guadeloupe,World,2014,486.0,thousand trips +tourists,Guadeloupe,World,2015,512.0,thousand trips +tourists,Guadeloupe,World,2016,581.0,thousand trips +tourists,Guadeloupe,World,2017,650.0,thousand trips +tourists,Guadeloupe,World,2018,735.0,thousand trips +tourists,Haiti,World,1995,145.0,thousand trips +tourists,Haiti,World,1996,150.0,thousand trips +tourists,Haiti,World,1997,149.0,thousand trips +tourists,Haiti,World,1998,147.0,thousand trips +tourists,Haiti,World,1999,143.0,thousand trips +tourists,Haiti,World,2000,140.0,thousand trips +tourists,Haiti,World,2001,142.0,thousand trips +tourists,Haiti,World,2002,140.0,thousand trips +tourists,Haiti,World,2003,136.0,thousand trips +tourists,Haiti,World,2004,96.0,thousand trips +tourists,Haiti,World,2005,112.0,thousand trips +tourists,Haiti,World,2006,108.0,thousand trips +tourists,Haiti,World,2007,386.0,thousand trips +tourists,Haiti,World,2008,258.0,thousand trips +tourists,Haiti,World,2009,387.0,thousand trips +tourists,Haiti,World,2010,255.0,thousand trips +tourists,Haiti,World,2011,349.0,thousand trips +tourists,Haiti,World,2012,349.0,thousand trips +tourists,Haiti,World,2013,420.0,thousand trips +tourists,Haiti,World,2014,465.2,thousand trips +tourists,Haiti,World,2015,516.0,thousand trips +tourists,Haiti,World,2016,445.0,thousand trips +tourists,Haiti,World,2017,467.0,thousand trips +tourists,Haiti,World,2018,447.0,thousand trips +tourists,Haiti,World,2019,286.0,thousand trips +tourists,Haiti,World,2020,203.0,thousand trips +tourists,Haiti,World,2021,148.0,thousand trips +tourists,Jamaica,World,1995,1147.0,thousand trips +tourists,Jamaica,World,1996,1162.0,thousand trips +tourists,Jamaica,World,1997,1192.0,thousand trips +tourists,Jamaica,World,1998,1225.0,thousand trips +tourists,Jamaica,World,1999,1248.0,thousand trips +tourists,Jamaica,World,2000,1323.0,thousand trips +tourists,Jamaica,World,2001,1277.0,thousand trips +tourists,Jamaica,World,2002,1266.0,thousand trips +tourists,Jamaica,World,2003,1350.0,thousand trips +tourists,Jamaica,World,2004,1415.0,thousand trips +tourists,Jamaica,World,2005,1479.0,thousand trips +tourists,Jamaica,World,2006,1679.0,thousand trips +tourists,Jamaica,World,2007,1701.0,thousand trips +tourists,Jamaica,World,2008,1767.0,thousand trips +tourists,Jamaica,World,2009,1831.0,thousand trips +tourists,Jamaica,World,2010,1922.0,thousand trips +tourists,Jamaica,World,2011,1952.0,thousand trips +tourists,Jamaica,World,2012,1986.0,thousand trips +tourists,Jamaica,World,2013,2008.4,thousand trips +tourists,Jamaica,World,2014,2080.0,thousand trips +tourists,Jamaica,World,2015,2123.0,thousand trips +tourists,Jamaica,World,2016,2182.0,thousand trips +tourists,Jamaica,World,2017,2353.0,thousand trips +tourists,Jamaica,World,2018,2473.0,thousand trips +tourists,Jamaica,World,2019,2681.0,thousand trips +tourists,Jamaica,World,2020,880.4,thousand trips +tourists,Jamaica,World,2021,1464.0,thousand trips +tourists,Jamaica,World,2022,2478.4,thousand trips +tourists,Martinique,World,1995,457.0,thousand trips +tourists,Martinique,World,1996,477.0,thousand trips +tourists,Martinique,World,1997,513.0,thousand trips +tourists,Martinique,World,1998,549.0,thousand trips +tourists,Martinique,World,1999,564.0,thousand trips +tourists,Martinique,World,2000,526.0,thousand trips +tourists,Martinique,World,2001,460.0,thousand trips +tourists,Martinique,World,2002,447.0,thousand trips +tourists,Martinique,World,2003,453.0,thousand trips +tourists,Martinique,World,2004,471.0,thousand trips +tourists,Martinique,World,2005,484.0,thousand trips +tourists,Martinique,World,2006,503.0,thousand trips +tourists,Martinique,World,2007,501.0,thousand trips +tourists,Martinique,World,2008,481.0,thousand trips +tourists,Martinique,World,2009,442.0,thousand trips +tourists,Martinique,World,2010,478.0,thousand trips +tourists,Martinique,World,2011,497.0,thousand trips +tourists,Martinique,World,2012,488.0,thousand trips +tourists,Martinique,World,2013,490.0,thousand trips +tourists,Martinique,World,2014,490.0,thousand trips +tourists,Martinique,World,2015,487.0,thousand trips +tourists,Martinique,World,2016,519.0,thousand trips +tourists,Martinique,World,2017,536.0,thousand trips +tourists,Martinique,World,2018,537.0,thousand trips +tourists,Martinique,World,2019,556.0,thousand trips +tourists,Martinique,World,2020,312.3,thousand trips +tourists,Martinique,World,2021,291.0,thousand trips +tourists,Martinique,World,2022,556.0,thousand trips +tourists,Montserrat,World,1995,17.7,thousand trips +tourists,Montserrat,World,1996,8.7,thousand trips +tourists,Montserrat,World,1997,5.1,thousand trips +tourists,Montserrat,World,1998,7.5,thousand trips +tourists,Montserrat,World,1999,9.8,thousand trips +tourists,Montserrat,World,2000,10.3,thousand trips +tourists,Montserrat,World,2001,9.8,thousand trips +tourists,Montserrat,World,2002,9.8,thousand trips +tourists,Montserrat,World,2003,8.4,thousand trips +tourists,Montserrat,World,2004,10.1,thousand trips +tourists,Montserrat,World,2005,9.7,thousand trips +tourists,Montserrat,World,2006,8.0,thousand trips +tourists,Montserrat,World,2007,7.7,thousand trips +tourists,Montserrat,World,2008,7.4,thousand trips +tourists,Montserrat,World,2009,6.3,thousand trips +tourists,Montserrat,World,2010,6.0,thousand trips +tourists,Montserrat,World,2011,5.4,thousand trips +tourists,Montserrat,World,2012,7.31,thousand trips +tourists,Montserrat,World,2013,7.2,thousand trips +tourists,Montserrat,World,2014,8.8,thousand trips +tourists,Montserrat,World,2015,8.9,thousand trips +tourists,Montserrat,World,2016,8.7,thousand trips +tourists,Montserrat,World,2017,9.5,thousand trips +tourists,Montserrat,World,2018,10.2,thousand trips +tourists,Montserrat,World,2019,10.4,thousand trips +tourists,Montserrat,World,2020,4.5,thousand trips +tourists,Montserrat,World,2021,1.6,thousand trips +tourists,Curaçao,World,1995,224.0,thousand trips +tourists,Curaçao,World,1996,214.0,thousand trips +tourists,Curaçao,World,1997,205.0,thousand trips +tourists,Curaçao,World,1998,199.0,thousand trips +tourists,Curaçao,World,1999,198.0,thousand trips +tourists,Curaçao,World,2000,191.0,thousand trips +tourists,Curaçao,World,2001,205.0,thousand trips +tourists,Curaçao,World,2002,218.0,thousand trips +tourists,Curaçao,World,2003,221.0,thousand trips +tourists,Curaçao,World,2004,223.0,thousand trips +tourists,Curaçao,World,2005,222.0,thousand trips +tourists,Curaçao,World,2006,234.0,thousand trips +tourists,Curaçao,World,2007,300.0,thousand trips +tourists,Curaçao,World,2008,409.0,thousand trips +tourists,Curaçao,World,2009,367.0,thousand trips +tourists,Curaçao,World,2010,342.0,thousand trips +tourists,Curaçao,World,2011,390.0,thousand trips +tourists,Curaçao,World,2012,421.0,thousand trips +tourists,Curaçao,World,2013,441.0,thousand trips +tourists,Curaçao,World,2014,452.0,thousand trips +tourists,Curaçao,World,2015,468.0,thousand trips +tourists,Curaçao,World,2016,441.0,thousand trips +tourists,Curaçao,World,2017,399.0,thousand trips +tourists,Curaçao,World,2018,432.0,thousand trips +tourists,Curaçao,World,2019,464.0,thousand trips +tourists,Curaçao,World,2020,175.0,thousand trips +tourists,Curaçao,World,2021,265.0,thousand trips +tourists,Curaçao,World,2022,489.6,thousand trips +tourists,Aruba,World,1995,619.0,thousand trips +tourists,Aruba,World,1996,641.0,thousand trips +tourists,Aruba,World,1997,650.0,thousand trips +tourists,Aruba,World,1998,647.0,thousand trips +tourists,Aruba,World,1999,683.0,thousand trips +tourists,Aruba,World,2000,721.0,thousand trips +tourists,Aruba,World,2001,691.0,thousand trips +tourists,Aruba,World,2002,643.0,thousand trips +tourists,Aruba,World,2003,642.0,thousand trips +tourists,Aruba,World,2004,728.0,thousand trips +tourists,Aruba,World,2005,733.0,thousand trips +tourists,Aruba,World,2006,694.0,thousand trips +tourists,Aruba,World,2007,772.0,thousand trips +tourists,Aruba,World,2008,827.0,thousand trips +tourists,Aruba,World,2009,813.0,thousand trips +tourists,Aruba,World,2010,824.0,thousand trips +tourists,Aruba,World,2011,869.0,thousand trips +tourists,Aruba,World,2012,904.0,thousand trips +tourists,Aruba,World,2013,979.0,thousand trips +tourists,Aruba,World,2014,1072.0,thousand trips +tourists,Aruba,World,2015,1225.0,thousand trips +tourists,Aruba,World,2016,1102.0,thousand trips +tourists,Aruba,World,2017,1070.5,thousand trips +tourists,Aruba,World,2018,1082.0,thousand trips +tourists,Aruba,World,2019,1119.0,thousand trips +tourists,Aruba,World,2020,368.0,thousand trips +tourists,Aruba,World,2021,807.0,thousand trips +tourists,Aruba,World,2022,1101.0,thousand trips +tourists,Sint Maarten (Dutch part),World,1995,460.0,thousand trips +tourists,Sint Maarten (Dutch part),World,1996,365.0,thousand trips +tourists,Sint Maarten (Dutch part),World,1997,439.0,thousand trips +tourists,Sint Maarten (Dutch part),World,1998,458.0,thousand trips +tourists,Sint Maarten (Dutch part),World,1999,445.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2000,432.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2001,403.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2002,381.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2003,428.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2004,475.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2005,468.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2006,468.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2007,469.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2008,475.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2009,440.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2010,443.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2011,424.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2012,457.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2013,467.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2014,500.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2015,505.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2016,528.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2017,402.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2018,178.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2019,320.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2020,106.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2021,249.0,thousand trips +tourists,Sint Maarten (Dutch part),World,2022,373.0,thousand trips +tourists,Puerto Rico,World,1995,3131.0,thousand trips +tourists,Puerto Rico,World,1996,3065.0,thousand trips +tourists,Puerto Rico,World,1997,3242.0,thousand trips +tourists,Puerto Rico,World,1998,3396.0,thousand trips +tourists,Puerto Rico,World,1999,3024.0,thousand trips +tourists,Puerto Rico,World,2000,3341.0,thousand trips +tourists,Puerto Rico,World,2001,3551.0,thousand trips +tourists,Puerto Rico,World,2002,3087.0,thousand trips +tourists,Puerto Rico,World,2003,3238.0,thousand trips +tourists,Puerto Rico,World,2004,3541.0,thousand trips +tourists,Puerto Rico,World,2005,3686.0,thousand trips +tourists,Puerto Rico,World,2006,3722.0,thousand trips +tourists,Puerto Rico,World,2007,3687.0,thousand trips +tourists,Puerto Rico,World,2008,3716.0,thousand trips +tourists,Puerto Rico,World,2009,3183.0,thousand trips +tourists,Puerto Rico,World,2010,3186.0,thousand trips +tourists,Puerto Rico,World,2011,3048.0,thousand trips +tourists,Puerto Rico,World,2012,3069.0,thousand trips +tourists,Puerto Rico,World,2013,3172.0,thousand trips +tourists,Puerto Rico,World,2014,3246.0,thousand trips +tourists,Puerto Rico,World,2015,3542.0,thousand trips +tourists,Puerto Rico,World,2016,3736.0,thousand trips +tourists,Puerto Rico,World,2017,3513.0,thousand trips +tourists,Puerto Rico,World,2018,3068.0,thousand trips +tourists,Puerto Rico,World,2019,3180.0,thousand trips +tourists,Puerto Rico,World,2020,2617.4,thousand trips +tourists,Puerto Rico,World,2021,2754.7,thousand trips +tourists,Puerto Rico,World,2022,3273.1,thousand trips +tourists,Saint Kitts and Nevis,World,1995,79.0,thousand trips +tourists,Saint Kitts and Nevis,World,1996,84.0,thousand trips +tourists,Saint Kitts and Nevis,World,1997,88.0,thousand trips +tourists,Saint Kitts and Nevis,World,1998,93.0,thousand trips +tourists,Saint Kitts and Nevis,World,1999,84.0,thousand trips +tourists,Saint Kitts and Nevis,World,2000,73.0,thousand trips +tourists,Saint Kitts and Nevis,World,2001,71.0,thousand trips +tourists,Saint Kitts and Nevis,World,2002,69.0,thousand trips +tourists,Saint Kitts and Nevis,World,2003,91.0,thousand trips +tourists,Saint Kitts and Nevis,World,2004,118.0,thousand trips +tourists,Saint Kitts and Nevis,World,2005,141.0,thousand trips +tourists,Saint Kitts and Nevis,World,2006,139.0,thousand trips +tourists,Saint Kitts and Nevis,World,2007,123.0,thousand trips +tourists,Saint Kitts and Nevis,World,2008,128.0,thousand trips +tourists,Saint Kitts and Nevis,World,2009,83.0,thousand trips +tourists,Saint Kitts and Nevis,World,2010,98.0,thousand trips +tourists,Saint Kitts and Nevis,World,2011,104.0,thousand trips +tourists,Saint Kitts and Nevis,World,2012,104.0,thousand trips +tourists,Saint Kitts and Nevis,World,2013,107.0,thousand trips +tourists,Saint Kitts and Nevis,World,2014,113.0,thousand trips +tourists,Saint Kitts and Nevis,World,2015,118.0,thousand trips +tourists,Saint Kitts and Nevis,World,2016,116.0,thousand trips +tourists,Saint Kitts and Nevis,World,2017,115.0,thousand trips +tourists,Saint Kitts and Nevis,World,2018,125.0,thousand trips +tourists,Saint Kitts and Nevis,World,2019,120.0,thousand trips +tourists,Saint Kitts and Nevis,World,2020,30.0,thousand trips +tourists,Saint Kitts and Nevis,World,2021,20.2,thousand trips +tourists,Saint Kitts and Nevis,World,2022,79.8,thousand trips +tourists,Anguilla,World,1995,39.0,thousand trips +tourists,Anguilla,World,1996,37.0,thousand trips +tourists,Anguilla,World,1997,43.0,thousand trips +tourists,Anguilla,World,1998,44.0,thousand trips +tourists,Anguilla,World,1999,47.0,thousand trips +tourists,Anguilla,World,2000,44.0,thousand trips +tourists,Anguilla,World,2001,48.0,thousand trips +tourists,Anguilla,World,2002,44.0,thousand trips +tourists,Anguilla,World,2003,47.0,thousand trips +tourists,Anguilla,World,2004,54.0,thousand trips +tourists,Anguilla,World,2005,62.0,thousand trips +tourists,Anguilla,World,2006,73.0,thousand trips +tourists,Anguilla,World,2007,78.0,thousand trips +tourists,Anguilla,World,2008,68.0,thousand trips +tourists,Anguilla,World,2009,58.0,thousand trips +tourists,Anguilla,World,2010,62.0,thousand trips +tourists,Anguilla,World,2011,66.0,thousand trips +tourists,Anguilla,World,2012,65.0,thousand trips +tourists,Anguilla,World,2013,69.0,thousand trips +tourists,Anguilla,World,2014,71.0,thousand trips +tourists,Anguilla,World,2015,73.0,thousand trips +tourists,Anguilla,World,2016,79.0,thousand trips +tourists,Anguilla,World,2017,68.3,thousand trips +tourists,Anguilla,World,2018,55.0,thousand trips +tourists,Anguilla,World,2019,95.0,thousand trips +tourists,Anguilla,World,2020,25.4,thousand trips +tourists,Anguilla,World,2021,28.4,thousand trips +tourists,Anguilla,World,2022,74.1,thousand trips +tourists,Saint Lucia,World,1995,231.0,thousand trips +tourists,Saint Lucia,World,1996,236.0,thousand trips +tourists,Saint Lucia,World,1997,248.0,thousand trips +tourists,Saint Lucia,World,1998,252.0,thousand trips +tourists,Saint Lucia,World,1999,264.0,thousand trips +tourists,Saint Lucia,World,2000,270.0,thousand trips +tourists,Saint Lucia,World,2001,250.0,thousand trips +tourists,Saint Lucia,World,2002,253.0,thousand trips +tourists,Saint Lucia,World,2003,277.0,thousand trips +tourists,Saint Lucia,World,2004,298.0,thousand trips +tourists,Saint Lucia,World,2005,318.0,thousand trips +tourists,Saint Lucia,World,2006,303.0,thousand trips +tourists,Saint Lucia,World,2007,287.0,thousand trips +tourists,Saint Lucia,World,2008,296.0,thousand trips +tourists,Saint Lucia,World,2009,278.0,thousand trips +tourists,Saint Lucia,World,2010,306.0,thousand trips +tourists,Saint Lucia,World,2011,312.0,thousand trips +tourists,Saint Lucia,World,2012,307.0,thousand trips +tourists,Saint Lucia,World,2013,319.0,thousand trips +tourists,Saint Lucia,World,2014,338.0,thousand trips +tourists,Saint Lucia,World,2015,345.0,thousand trips +tourists,Saint Lucia,World,2016,348.0,thousand trips +tourists,Saint Lucia,World,2017,386.0,thousand trips +tourists,Saint Lucia,World,2018,395.0,thousand trips +tourists,Saint Lucia,World,2019,424.0,thousand trips +tourists,Saint Lucia,World,2020,130.7,thousand trips +tourists,Saint Lucia,World,2021,199.0,thousand trips +tourists,Saint Lucia,World,2022,356.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,1995,60.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,1996,58.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,1997,65.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,1998,67.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,1999,68.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2000,73.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2001,71.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2002,78.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2003,79.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2004,87.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2005,96.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2006,97.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2007,90.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2008,84.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2009,75.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2010,72.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2011,74.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2012,74.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2013,72.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2014,71.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2015,75.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2016,79.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2017,76.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2018,80.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2019,84.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2020,27.0,thousand trips +tourists,Saint Vincent and the Grenadines,World,2021,24.2,thousand trips +tourists,Saint Vincent and the Grenadines,World,2022,59.0,thousand trips +tourists,Trinidad and Tobago,World,1995,260.0,thousand trips +tourists,Trinidad and Tobago,World,1996,266.0,thousand trips +tourists,Trinidad and Tobago,World,1997,324.0,thousand trips +tourists,Trinidad and Tobago,World,1998,334.0,thousand trips +tourists,Trinidad and Tobago,World,1999,358.0,thousand trips +tourists,Trinidad and Tobago,World,2000,399.0,thousand trips +tourists,Trinidad and Tobago,World,2001,383.0,thousand trips +tourists,Trinidad and Tobago,World,2002,384.0,thousand trips +tourists,Trinidad and Tobago,World,2003,409.0,thousand trips +tourists,Trinidad and Tobago,World,2004,443.0,thousand trips +tourists,Trinidad and Tobago,World,2005,463.0,thousand trips +tourists,Trinidad and Tobago,World,2006,461.0,thousand trips +tourists,Trinidad and Tobago,World,2007,449.0,thousand trips +tourists,Trinidad and Tobago,World,2008,437.0,thousand trips +tourists,Trinidad and Tobago,World,2009,419.0,thousand trips +tourists,Trinidad and Tobago,World,2010,388.0,thousand trips +tourists,Trinidad and Tobago,World,2011,431.0,thousand trips +tourists,Trinidad and Tobago,World,2012,455.0,thousand trips +tourists,Trinidad and Tobago,World,2013,434.0,thousand trips +tourists,Trinidad and Tobago,World,2014,412.8,thousand trips +tourists,Trinidad and Tobago,World,2015,439.8,thousand trips +tourists,Trinidad and Tobago,World,2016,410.0,thousand trips +tourists,Trinidad and Tobago,World,2017,394.7,thousand trips +tourists,Trinidad and Tobago,World,2018,375.5,thousand trips +tourists,Trinidad and Tobago,World,2019,388.6,thousand trips +tourists,Trinidad and Tobago,World,2020,95.3,thousand trips +tourists,Trinidad and Tobago,World,2021,40.6,thousand trips +tourists,Trinidad and Tobago,World,2022,226.5,thousand trips +tourists,Trinidad and Tobago,World,2023,309.0,thousand trips +tourists,Trinidad and Tobago,World,2024,334.0,thousand trips +tourists,Turks and Caicos Islands,World,1995,79.0,thousand trips +tourists,Turks and Caicos Islands,World,1996,88.0,thousand trips +tourists,Turks and Caicos Islands,World,1997,93.0,thousand trips +tourists,Turks and Caicos Islands,World,1998,111.0,thousand trips +tourists,Turks and Caicos Islands,World,1999,121.0,thousand trips +tourists,Turks and Caicos Islands,World,2000,152.0,thousand trips +tourists,Turks and Caicos Islands,World,2001,166.0,thousand trips +tourists,Turks and Caicos Islands,World,2002,155.0,thousand trips +tourists,Turks and Caicos Islands,World,2003,164.0,thousand trips +tourists,Turks and Caicos Islands,World,2004,173.0,thousand trips +tourists,Turks and Caicos Islands,World,2005,176.0,thousand trips +tourists,Turks and Caicos Islands,World,2006,248.0,thousand trips +tourists,Turks and Caicos Islands,World,2007,265.0,thousand trips +tourists,Turks and Caicos Islands,World,2008,352.0,thousand trips +tourists,Turks and Caicos Islands,World,2009,351.0,thousand trips +tourists,Turks and Caicos Islands,World,2010,281.0,thousand trips +tourists,Turks and Caicos Islands,World,2011,354.0,thousand trips +tourists,Turks and Caicos Islands,World,2012,291.7,thousand trips +tourists,Turks and Caicos Islands,World,2013,290.6,thousand trips +tourists,Turks and Caicos Islands,World,2014,358.0,thousand trips +tourists,Turks and Caicos Islands,World,2015,386.0,thousand trips +tourists,Turks and Caicos Islands,World,2016,449.0,thousand trips +tourists,Turks and Caicos Islands,World,2017,416.4,thousand trips +tourists,Turks and Caicos Islands,World,2018,441.0,thousand trips +tourists,Turks and Caicos Islands,World,2019,487.0,thousand trips +tourists,Turks and Caicos Islands,World,2020,164.5,thousand trips +tourists,Turks and Caicos Islands,World,2021,405.0,thousand trips +tourists,United States Virgin Islands,World,1995,454.0,thousand trips +tourists,United States Virgin Islands,World,1996,373.0,thousand trips +tourists,United States Virgin Islands,World,1997,393.0,thousand trips +tourists,United States Virgin Islands,World,1998,422.0,thousand trips +tourists,United States Virgin Islands,World,1999,484.0,thousand trips +tourists,United States Virgin Islands,World,2000,546.0,thousand trips +tourists,United States Virgin Islands,World,2001,527.0,thousand trips +tourists,United States Virgin Islands,World,2002,520.0,thousand trips +tourists,United States Virgin Islands,World,2003,538.0,thousand trips +tourists,United States Virgin Islands,World,2004,563.0,thousand trips +tourists,United States Virgin Islands,World,2005,594.0,thousand trips +tourists,United States Virgin Islands,World,2006,571.0,thousand trips +tourists,United States Virgin Islands,World,2007,587.0,thousand trips +tourists,United States Virgin Islands,World,2008,574.0,thousand trips +tourists,United States Virgin Islands,World,2009,563.0,thousand trips +tourists,United States Virgin Islands,World,2010,572.0,thousand trips +tourists,United States Virgin Islands,World,2011,567.0,thousand trips +tourists,United States Virgin Islands,World,2012,619.0,thousand trips +tourists,United States Virgin Islands,World,2013,590.0,thousand trips +tourists,United States Virgin Islands,World,2014,615.0,thousand trips +tourists,United States Virgin Islands,World,2015,642.0,thousand trips +tourists,United States Virgin Islands,World,2016,667.0,thousand trips +tourists,United States Virgin Islands,World,2017,535.0,thousand trips +tourists,United States Virgin Islands,World,2018,381.0,thousand trips +tourists,United States Virgin Islands,World,2019,514.0,thousand trips +tourists,United States Virgin Islands,World,2020,302.7,thousand trips +tourists,United States Virgin Islands,World,2021,684.8,thousand trips +tourists,Bonaire,World,1995,59.0,thousand trips +tourists,Bonaire,World,1996,65.0,thousand trips +tourists,Bonaire,World,1997,63.0,thousand trips +tourists,Bonaire,World,1998,62.0,thousand trips +tourists,Bonaire,World,1999,61.0,thousand trips +tourists,Bonaire,World,2000,51.0,thousand trips +tourists,Bonaire,World,2001,50.0,thousand trips +tourists,Bonaire,World,2002,52.0,thousand trips +tourists,Bonaire,World,2003,62.0,thousand trips +tourists,Bonaire,World,2004,63.0,thousand trips +tourists,Bonaire,World,2005,63.0,thousand trips +tourists,Bonaire,World,2006,64.0,thousand trips +tourists,Bonaire,World,2007,74.0,thousand trips +tourists,Bonaire,World,2008,74.0,thousand trips +tourists,Bonaire,World,2009,67.0,thousand trips +tourists,Bonaire,World,2010,71.0,thousand trips +tourists,Saba,World,1995,10.0,thousand trips +tourists,Saba,World,1996,9.8,thousand trips +tourists,Saba,World,1997,10.6,thousand trips +tourists,Saba,World,1998,10.6,thousand trips +tourists,Saba,World,1999,9.3,thousand trips +tourists,Saba,World,2000,9.1,thousand trips +tourists,Saba,World,2001,9.0,thousand trips +tourists,Saba,World,2002,10.8,thousand trips +tourists,Saba,World,2003,10.3,thousand trips +tourists,Saba,World,2004,11.0,thousand trips +tourists,Saba,World,2005,11.5,thousand trips +tourists,Saba,World,2006,11.0,thousand trips +tourists,Saba,World,2007,11.7,thousand trips +tourists,Saba,World,2008,12.0,thousand trips +tourists,Saba,World,2009,12.0,thousand trips +tourists,Saba,World,2010,12.3,thousand trips +tourists,St. Eustatius,World,1995,8.8,thousand trips +tourists,St. Eustatius,World,1996,8.2,thousand trips +tourists,St. Eustatius,World,1997,8.5,thousand trips +tourists,St. Eustatius,World,1998,8.6,thousand trips +tourists,St. Eustatius,World,1999,9.2,thousand trips +tourists,St. Eustatius,World,2000,9.1,thousand trips +tourists,St. Eustatius,World,2001,9.7,thousand trips +tourists,St. Eustatius,World,2002,9.8,thousand trips +tourists,St. Eustatius,World,2003,10.5,thousand trips +tourists,St. Eustatius,World,2004,11.1,thousand trips +tourists,St. Eustatius,World,2005,10.4,thousand trips +tourists,St. Eustatius,World,2006,9.6,thousand trips +tourists,St. Eustatius,World,2007,11.6,thousand trips +tourists,St. Eustatius,World,2008,11.8,thousand trips +tourists,St. Eustatius,World,2009,12.0,thousand trips +tourists,St. Eustatius,World,2010,11.4,thousand trips +excursionists,Bahamas,World,1995,1640.0,thousand trips +excursionists,Bahamas,World,1996,1785.0,thousand trips +excursionists,Bahamas,World,1997,1828.0,thousand trips +excursionists,Bahamas,World,1998,1820.0,thousand trips +excursionists,Bahamas,World,1999,2071.0,thousand trips +excursionists,Bahamas,World,2000,2660.0,thousand trips +excursionists,Bahamas,World,2001,2645.0,thousand trips +excursionists,Bahamas,World,2002,2893.0,thousand trips +excursionists,Bahamas,World,2003,3089.0,thousand trips +excursionists,Bahamas,World,2004,3443.0,thousand trips +excursionists,Bahamas,World,2005,3154.0,thousand trips +excursionists,Bahamas,World,2006,3118.0,thousand trips +excursionists,Bahamas,World,2007,3063.0,thousand trips +excursionists,Bahamas,World,2008,2925.0,thousand trips +excursionists,Bahamas,World,2009,3309.0,thousand trips +excursionists,Bahamas,World,2010,3877.0,thousand trips +excursionists,Bahamas,World,2011,4233.0,thousand trips +excursionists,Bahamas,World,2012,4509.0,thousand trips +excursionists,Bahamas,World,2013,4774.0,thousand trips +excursionists,Bahamas,World,2014,4876.0,thousand trips +excursionists,Bahamas,World,2015,4616.0,thousand trips +excursionists,Bahamas,World,2016,4766.0,thousand trips +excursionists,Bahamas,World,2017,4684.0,thousand trips +excursionists,Bahamas,World,2018,4990.0,thousand trips +excursionists,Bahamas,World,2019,5444.0,thousand trips +excursionists,Bahamas,World,2020,1354.0,thousand trips +excursionists,Bahamas,World,2021,1209.0,thousand trips +excursionists,Bahamas,World,2022,5548.6,thousand trips +excursionists,Barbados,World,1995,485.0,thousand trips +excursionists,Barbados,World,1996,510.0,thousand trips +excursionists,Barbados,World,1997,518.0,thousand trips +excursionists,Barbados,World,1998,507.0,thousand trips +excursionists,Barbados,World,1999,433.0,thousand trips +excursionists,Barbados,World,2000,533.0,thousand trips +excursionists,Barbados,World,2001,528.0,thousand trips +excursionists,Barbados,World,2002,523.0,thousand trips +excursionists,Barbados,World,2003,559.0,thousand trips +excursionists,Barbados,World,2004,721.0,thousand trips +excursionists,Barbados,World,2005,563.0,thousand trips +excursionists,Barbados,World,2006,539.0,thousand trips +excursionists,Barbados,World,2007,616.0,thousand trips +excursionists,Barbados,World,2008,597.0,thousand trips +excursionists,Barbados,World,2009,635.0,thousand trips +excursionists,Barbados,World,2010,665.0,thousand trips +excursionists,Barbados,World,2011,619.0,thousand trips +excursionists,Barbados,World,2012,517.0,thousand trips +excursionists,Barbados,World,2013,570.0,thousand trips +excursionists,Barbados,World,2014,558.0,thousand trips +excursionists,Barbados,World,2015,587.0,thousand trips +excursionists,Barbados,World,2016,595.0,thousand trips +excursionists,Barbados,World,2017,681.0,thousand trips +excursionists,Barbados,World,2018,676.0,thousand trips +excursionists,Barbados,World,2019,443.0,thousand trips +excursionists,Barbados,World,2020,338.4,thousand trips +excursionists,Barbados,World,2021,97.4,thousand trips +excursionists,Barbados,World,2022,357.9,thousand trips +excursionists,Bermuda,World,1995,170.0,thousand trips +excursionists,Bermuda,World,1996,182.0,thousand trips +excursionists,Bermuda,World,1997,182.0,thousand trips +excursionists,Bermuda,World,1998,188.0,thousand trips +excursionists,Bermuda,World,1999,194.0,thousand trips +excursionists,Bermuda,World,2000,207.0,thousand trips +excursionists,Bermuda,World,2001,180.0,thousand trips +excursionists,Bermuda,World,2002,200.0,thousand trips +excursionists,Bermuda,World,2003,226.0,thousand trips +excursionists,Bermuda,World,2004,206.0,thousand trips +excursionists,Bermuda,World,2005,247.0,thousand trips +excursionists,Bermuda,World,2006,336.0,thousand trips +excursionists,Bermuda,World,2007,354.0,thousand trips +excursionists,Bermuda,World,2008,286.0,thousand trips +excursionists,Bermuda,World,2009,318.0,thousand trips +excursionists,Bermuda,World,2010,348.0,thousand trips +excursionists,Bermuda,World,2011,416.0,thousand trips +excursionists,Bermuda,World,2012,378.0,thousand trips +excursionists,Bermuda,World,2013,340.0,thousand trips +excursionists,Bermuda,World,2014,356.0,thousand trips +excursionists,Bermuda,World,2015,377.0,thousand trips +excursionists,Bermuda,World,2016,398.0,thousand trips +excursionists,Bermuda,World,2017,418.0,thousand trips +excursionists,Bermuda,World,2018,484.0,thousand trips +excursionists,Bermuda,World,2019,536.0,thousand trips +excursionists,Bermuda,World,2020,9.3,thousand trips +excursionists,Bermuda,World,2021,14.2,thousand trips +excursionists,Bermuda,World,2022,402.7,thousand trips +excursionists,Belize,World,1995,211.0,thousand trips +excursionists,Belize,World,1996,216.0,thousand trips +excursionists,Belize,World,1997,159.0,thousand trips +excursionists,Belize,World,1998,124.0,thousand trips +excursionists,Belize,World,1999,159.0,thousand trips +excursionists,Belize,World,2000,178.0,thousand trips +excursionists,Belize,World,2001,189.0,thousand trips +excursionists,Belize,World,2002,494.0,thousand trips +excursionists,Belize,World,2003,778.0,thousand trips +excursionists,Belize,World,2004,1098.0,thousand trips +excursionists,Belize,World,2005,800.0,thousand trips +excursionists,Belize,World,2006,656.0,thousand trips +excursionists,Belize,World,2007,629.0,thousand trips +excursionists,Belize,World,2008,601.0,thousand trips +excursionists,Belize,World,2009,709.0,thousand trips +excursionists,Belize,World,2010,812.0,thousand trips +excursionists,Belize,World,2011,856.0,thousand trips +excursionists,Belize,World,2012,751.0,thousand trips +excursionists,Belize,World,2013,728.0,thousand trips +excursionists,Belize,World,2014,968.0,thousand trips +excursionists,Belize,World,2015,958.0,thousand trips +excursionists,Belize,World,2016,1005.0,thousand trips +excursionists,Belize,World,2017,1014.0,thousand trips +excursionists,Belize,World,2018,1208.0,thousand trips +excursionists,Belize,World,2019,1171.0,thousand trips +excursionists,Belize,World,2020,343.1,thousand trips +excursionists,Belize,World,2021,218.0,thousand trips +excursionists,British Virgin Islands,World,1995,146.0,thousand trips +excursionists,British Virgin Islands,World,1996,168.0,thousand trips +excursionists,British Virgin Islands,World,1997,121.0,thousand trips +excursionists,British Virgin Islands,World,1998,113.0,thousand trips +excursionists,British Virgin Islands,World,1999,200.0,thousand trips +excursionists,British Virgin Islands,World,2000,247.0,thousand trips +excursionists,British Virgin Islands,World,2001,239.0,thousand trips +excursionists,British Virgin Islands,World,2002,262.0,thousand trips +excursionists,British Virgin Islands,World,2003,336.0,thousand trips +excursionists,British Virgin Islands,World,2004,509.0,thousand trips +excursionists,British Virgin Islands,World,2005,483.0,thousand trips +excursionists,British Virgin Islands,World,2006,469.0,thousand trips +excursionists,British Virgin Islands,World,2007,590.0,thousand trips +excursionists,British Virgin Islands,World,2008,588.0,thousand trips +excursionists,British Virgin Islands,World,2009,548.0,thousand trips +excursionists,British Virgin Islands,World,2010,512.0,thousand trips +excursionists,British Virgin Islands,World,2011,493.0,thousand trips +excursionists,British Virgin Islands,World,2012,402.0,thousand trips +excursionists,British Virgin Islands,World,2013,375.7,thousand trips +excursionists,British Virgin Islands,World,2014,386.5,thousand trips +excursionists,British Virgin Islands,World,2015,529.0,thousand trips +excursionists,British Virgin Islands,World,2016,717.0,thousand trips +excursionists,British Virgin Islands,World,2019,592.5,thousand trips +excursionists,Cayman Islands,World,1995,683.0,thousand trips +excursionists,Cayman Islands,World,1996,800.0,thousand trips +excursionists,Cayman Islands,World,1997,867.0,thousand trips +excursionists,Cayman Islands,World,1998,871.0,thousand trips +excursionists,Cayman Islands,World,1999,1036.0,thousand trips +excursionists,Cayman Islands,World,2000,1031.0,thousand trips +excursionists,Cayman Islands,World,2001,1215.0,thousand trips +excursionists,Cayman Islands,World,2002,1575.0,thousand trips +excursionists,Cayman Islands,World,2003,1819.0,thousand trips +excursionists,Cayman Islands,World,2004,1693.0,thousand trips +excursionists,Cayman Islands,World,2005,1799.0,thousand trips +excursionists,Cayman Islands,World,2006,1930.0,thousand trips +excursionists,Cayman Islands,World,2007,1716.0,thousand trips +excursionists,Cayman Islands,World,2008,1553.0,thousand trips +excursionists,Cayman Islands,World,2009,1520.0,thousand trips +excursionists,Cayman Islands,World,2010,1598.0,thousand trips +excursionists,Cayman Islands,World,2011,1401.0,thousand trips +excursionists,Cayman Islands,World,2012,1507.0,thousand trips +excursionists,Cayman Islands,World,2013,1376.0,thousand trips +excursionists,Cayman Islands,World,2014,1610.0,thousand trips +excursionists,Cayman Islands,World,2015,1717.0,thousand trips +excursionists,Cayman Islands,World,2016,1712.0,thousand trips +excursionists,Cayman Islands,World,2017,1728.0,thousand trips +excursionists,Cayman Islands,World,2018,1921.0,thousand trips +excursionists,Cayman Islands,World,2019,1831.0,thousand trips +excursionists,Cayman Islands,World,2020,538.0,thousand trips +excursionists,Cayman Islands,World,2022,743.4,thousand trips +excursionists,Cuba,World,1995,3.0,thousand trips +excursionists,Cuba,World,1996,5.0,thousand trips +excursionists,Cuba,World,1997,17.0,thousand trips +excursionists,Cuba,World,1998,26.0,thousand trips +excursionists,Cuba,World,1999,42.0,thousand trips +excursionists,Cuba,World,2000,33.0,thousand trips +excursionists,Cuba,World,2001,39.0,thousand trips +excursionists,Cuba,World,2002,30.0,thousand trips +excursionists,Cuba,World,2003,59.0,thousand trips +excursionists,Cuba,World,2004,32.0,thousand trips +excursionists,Cuba,World,2005,58.0,thousand trips +excursionists,Cuba,World,2006,71.0,thousand trips +excursionists,Cuba,World,2007,33.0,thousand trips +excursionists,Cuba,World,2008,32.0,thousand trips +excursionists,Cuba,World,2009,25.0,thousand trips +excursionists,Cuba,World,2010,25.0,thousand trips +excursionists,Cuba,World,2011,28.0,thousand trips +excursionists,Cuba,World,2012,24.0,thousand trips +excursionists,Cuba,World,2013,23.0,thousand trips +excursionists,Cuba,World,2014,33.0,thousand trips +excursionists,Cuba,World,2015,34.0,thousand trips +excursionists,Cuba,World,2016,34.0,thousand trips +excursionists,Dominica,World,1995,143.0,thousand trips +excursionists,Dominica,World,1996,198.0,thousand trips +excursionists,Dominica,World,1997,234.0,thousand trips +excursionists,Dominica,World,1998,246.0,thousand trips +excursionists,Dominica,World,1999,206.0,thousand trips +excursionists,Dominica,World,2000,242.0,thousand trips +excursionists,Dominica,World,2001,210.0,thousand trips +excursionists,Dominica,World,2002,139.0,thousand trips +excursionists,Dominica,World,2003,181.0,thousand trips +excursionists,Dominica,World,2004,387.0,thousand trips +excursionists,Dominica,World,2005,303.0,thousand trips +excursionists,Dominica,World,2006,381.0,thousand trips +excursionists,Dominica,World,2007,356.0,thousand trips +excursionists,Dominica,World,2008,387.0,thousand trips +excursionists,Dominica,World,2009,533.0,thousand trips +excursionists,Dominica,World,2010,519.0,thousand trips +excursionists,Dominica,World,2011,342.0,thousand trips +excursionists,Dominica,World,2012,269.0,thousand trips +excursionists,Dominica,World,2013,233.0,thousand trips +excursionists,Dominica,World,2014,289.0,thousand trips +excursionists,Dominica,World,2015,283.0,thousand trips +excursionists,Dominica,World,2016,278.0,thousand trips +excursionists,Dominica,World,2017,158.0,thousand trips +excursionists,Dominica,World,2018,136.0,thousand trips +excursionists,Dominica,World,2019,232.0,thousand trips +excursionists,Dominica,World,2020,118.0,thousand trips +excursionists,Dominica,World,2021,51.0,thousand trips +excursionists,Dominica,World,2022,174.0,thousand trips +excursionists,Dominican Republic,World,1995,30.0,thousand trips +excursionists,Dominican Republic,World,1996,111.0,thousand trips +excursionists,Dominican Republic,World,1997,271.0,thousand trips +excursionists,Dominican Republic,World,1998,393.0,thousand trips +excursionists,Dominican Republic,World,1999,283.0,thousand trips +excursionists,Dominican Republic,World,2000,183.0,thousand trips +excursionists,Dominican Republic,World,2001,208.0,thousand trips +excursionists,Dominican Republic,World,2002,247.0,thousand trips +excursionists,Dominican Republic,World,2003,398.0,thousand trips +excursionists,Dominican Republic,World,2004,457.0,thousand trips +excursionists,Dominican Republic,World,2005,290.0,thousand trips +excursionists,Dominican Republic,World,2006,303.0,thousand trips +excursionists,Dominican Republic,World,2007,385.0,thousand trips +excursionists,Dominican Republic,World,2008,475.0,thousand trips +excursionists,Dominican Republic,World,2009,497.0,thousand trips +excursionists,Dominican Republic,World,2010,353.0,thousand trips +excursionists,Dominican Republic,World,2011,348.0,thousand trips +excursionists,Dominican Republic,World,2012,338.0,thousand trips +excursionists,Dominican Republic,World,2013,424.0,thousand trips +excursionists,Dominican Republic,World,2014,435.0,thousand trips +excursionists,Dominican Republic,World,2015,529.0,thousand trips +excursionists,Dominican Republic,World,2016,809.0,thousand trips +excursionists,Dominican Republic,World,2017,1108.0,thousand trips +excursionists,Dominican Republic,World,2018,982.0,thousand trips +excursionists,Dominican Republic,World,2019,1104.0,thousand trips +excursionists,Dominican Republic,World,2020,342.872,thousand trips +excursionists,Dominican Republic,World,2021,333.134,thousand trips +excursionists,Dominican Republic,World,2022,1325.442,thousand trips +excursionists,Dominican Republic,World,2023,2258.941,thousand trips +excursionists,Dominican Republic,World,2024,2626.529,thousand trips +excursionists,Grenada,World,1995,261.0,thousand trips +excursionists,Grenada,World,1996,278.0,thousand trips +excursionists,Grenada,World,1997,257.0,thousand trips +excursionists,Grenada,World,1998,276.0,thousand trips +excursionists,Grenada,World,1999,254.0,thousand trips +excursionists,Grenada,World,2000,187.0,thousand trips +excursionists,Grenada,World,2001,154.0,thousand trips +excursionists,Grenada,World,2002,139.0,thousand trips +excursionists,Grenada,World,2003,152.0,thousand trips +excursionists,Grenada,World,2004,236.0,thousand trips +excursionists,Grenada,World,2005,281.0,thousand trips +excursionists,Grenada,World,2006,224.0,thousand trips +excursionists,Grenada,World,2007,275.0,thousand trips +excursionists,Grenada,World,2008,298.0,thousand trips +excursionists,Grenada,World,2009,346.0,thousand trips +excursionists,Grenada,World,2010,335.0,thousand trips +excursionists,Grenada,World,2011,328.0,thousand trips +excursionists,Grenada,World,2012,245.0,thousand trips +excursionists,Grenada,World,2013,199.0,thousand trips +excursionists,Grenada,World,2014,237.0,thousand trips +excursionists,Grenada,World,2015,282.0,thousand trips +excursionists,Grenada,World,2016,318.0,thousand trips +excursionists,Grenada,World,2017,300.0,thousand trips +excursionists,Grenada,World,2018,343.0,thousand trips +excursionists,Grenada,World,2019,338.0,thousand trips +excursionists,Grenada,World,2020,162.5,thousand trips +excursionists,Grenada,World,2021,25.0,thousand trips +excursionists,Grenada,World,2022,185.8,thousand trips +excursionists,Guadeloupe,World,1995,419.0,thousand trips +excursionists,Guadeloupe,World,1996,611.0,thousand trips +excursionists,Guadeloupe,World,1997,470.0,thousand trips +excursionists,Guadeloupe,World,1998,334.0,thousand trips +excursionists,Guadeloupe,World,1999,293.0,thousand trips +excursionists,Guadeloupe,World,2000,392.0,thousand trips +excursionists,Guadeloupe,World,2001,238.0,thousand trips +excursionists,Guadeloupe,World,2002,148.0,thousand trips +excursionists,Guadeloupe,World,2003,130.0,thousand trips +excursionists,Guadeloupe,World,2004,104.0,thousand trips +excursionists,Guadeloupe,World,2005,73.0,thousand trips +excursionists,Guadeloupe,World,2006,72.0,thousand trips +excursionists,Guadeloupe,World,2007,92.0,thousand trips +excursionists,Guadeloupe,World,2008,115.0,thousand trips +excursionists,Guadeloupe,World,2009,111.0,thousand trips +excursionists,Guadeloupe,World,2010,105.0,thousand trips +excursionists,Guadeloupe,World,2011,102.0,thousand trips +excursionists,Guadeloupe,World,2012,162.0,thousand trips +excursionists,Guadeloupe,World,2013,158.0,thousand trips +excursionists,Guadeloupe,World,2014,234.0,thousand trips +excursionists,Guadeloupe,World,2015,310.0,thousand trips +excursionists,Guadeloupe,World,2016,276.0,thousand trips +excursionists,Guadeloupe,World,2017,320.0,thousand trips +excursionists,Guadeloupe,World,2018,431.0,thousand trips +excursionists,Haiti,World,1995,225.0,thousand trips +excursionists,Haiti,World,1996,250.0,thousand trips +excursionists,Haiti,World,1997,238.0,thousand trips +excursionists,Haiti,World,1998,246.0,thousand trips +excursionists,Haiti,World,1999,243.0,thousand trips +excursionists,Haiti,World,2000,305.0,thousand trips +excursionists,Haiti,World,2001,357.0,thousand trips +excursionists,Haiti,World,2002,342.0,thousand trips +excursionists,Haiti,World,2003,382.0,thousand trips +excursionists,Haiti,World,2004,289.0,thousand trips +excursionists,Haiti,World,2005,368.0,thousand trips +excursionists,Haiti,World,2006,450.0,thousand trips +excursionists,Haiti,World,2007,482.0,thousand trips +excursionists,Haiti,World,2008,500.0,thousand trips +excursionists,Haiti,World,2009,439.0,thousand trips +excursionists,Haiti,World,2010,538.0,thousand trips +excursionists,Haiti,World,2011,597.0,thousand trips +excursionists,Haiti,World,2012,610.0,thousand trips +excursionists,Haiti,World,2013,644.0,thousand trips +excursionists,Haiti,World,2014,662.4,thousand trips +excursionists,Haiti,World,2015,674.0,thousand trips +excursionists,Haiti,World,2016,708.0,thousand trips +excursionists,Haiti,World,2017,795.0,thousand trips +excursionists,Haiti,World,2018,885.0,thousand trips +excursionists,Haiti,World,2019,652.0,thousand trips +excursionists,Haiti,World,2020,119.0,thousand trips +excursionists,Jamaica,World,1995,605.0,thousand trips +excursionists,Jamaica,World,1996,658.0,thousand trips +excursionists,Jamaica,World,1997,712.0,thousand trips +excursionists,Jamaica,World,1998,674.0,thousand trips +excursionists,Jamaica,World,1999,764.0,thousand trips +excursionists,Jamaica,World,2000,908.0,thousand trips +excursionists,Jamaica,World,2001,840.0,thousand trips +excursionists,Jamaica,World,2002,865.0,thousand trips +excursionists,Jamaica,World,2003,1133.0,thousand trips +excursionists,Jamaica,World,2004,1100.0,thousand trips +excursionists,Jamaica,World,2005,1136.0,thousand trips +excursionists,Jamaica,World,2006,1337.0,thousand trips +excursionists,Jamaica,World,2007,1180.0,thousand trips +excursionists,Jamaica,World,2008,1092.0,thousand trips +excursionists,Jamaica,World,2009,922.0,thousand trips +excursionists,Jamaica,World,2010,910.0,thousand trips +excursionists,Jamaica,World,2011,1125.0,thousand trips +excursionists,Jamaica,World,2012,1320.0,thousand trips +excursionists,Jamaica,World,2013,1265.0,thousand trips +excursionists,Jamaica,World,2014,1424.0,thousand trips +excursionists,Jamaica,World,2015,1569.0,thousand trips +excursionists,Jamaica,World,2016,1655.6,thousand trips +excursionists,Jamaica,World,2017,1923.0,thousand trips +excursionists,Jamaica,World,2018,1846.0,thousand trips +excursionists,Jamaica,World,2019,1553.0,thousand trips +excursionists,Jamaica,World,2020,449.3,thousand trips +excursionists,Jamaica,World,2021,71.0,thousand trips +excursionists,Jamaica,World,2022,852.3,thousand trips +excursionists,Martinique,World,1995,428.0,thousand trips +excursionists,Martinique,World,1996,408.0,thousand trips +excursionists,Martinique,World,1997,387.0,thousand trips +excursionists,Martinique,World,1998,415.0,thousand trips +excursionists,Martinique,World,1999,339.0,thousand trips +excursionists,Martinique,World,2000,290.0,thousand trips +excursionists,Martinique,World,2001,203.0,thousand trips +excursionists,Martinique,World,2002,207.0,thousand trips +excursionists,Martinique,World,2003,269.0,thousand trips +excursionists,Martinique,World,2004,159.0,thousand trips +excursionists,Martinique,World,2005,93.0,thousand trips +excursionists,Martinique,World,2006,96.0,thousand trips +excursionists,Martinique,World,2007,72.0,thousand trips +excursionists,Martinique,World,2008,87.0,thousand trips +excursionists,Martinique,World,2009,70.0,thousand trips +excursionists,Martinique,World,2010,75.0,thousand trips +excursionists,Martinique,World,2011,41.0,thousand trips +excursionists,Martinique,World,2012,115.0,thousand trips +excursionists,Martinique,World,2013,133.0,thousand trips +excursionists,Martinique,World,2014,206.0,thousand trips +excursionists,Martinique,World,2015,274.0,thousand trips +excursionists,Martinique,World,2016,323.0,thousand trips +excursionists,Martinique,World,2017,467.0,thousand trips +excursionists,Martinique,World,2018,465.0,thousand trips +excursionists,Martinique,World,2019,363.0,thousand trips +excursionists,Martinique,World,2020,211.3,thousand trips +excursionists,Martinique,World,2021,21.2,thousand trips +excursionists,Martinique,World,2022,105.0,thousand trips +excursionists,Montserrat,World,1995,1.6,thousand trips +excursionists,Montserrat,World,1996,0.3,thousand trips +excursionists,Montserrat,World,1997,1.0,thousand trips +excursionists,Montserrat,World,1998,1.9,thousand trips +excursionists,Montserrat,World,1999,3.2,thousand trips +excursionists,Montserrat,World,2000,4.0,thousand trips +excursionists,Montserrat,World,2001,5.8,thousand trips +excursionists,Montserrat,World,2002,5.2,thousand trips +excursionists,Montserrat,World,2003,5.2,thousand trips +excursionists,Montserrat,World,2004,5.1,thousand trips +excursionists,Montserrat,World,2005,3.4,thousand trips +excursionists,Montserrat,World,2006,1.5,thousand trips +excursionists,Montserrat,World,2007,1.0,thousand trips +excursionists,Montserrat,World,2008,1.0,thousand trips +excursionists,Montserrat,World,2009,1.0,thousand trips +excursionists,Montserrat,World,2010,1.7,thousand trips +excursionists,Montserrat,World,2011,2.0,thousand trips +excursionists,Montserrat,World,2012,2.595,thousand trips +excursionists,Montserrat,World,2013,1.5,thousand trips +excursionists,Montserrat,World,2014,1.9,thousand trips +excursionists,Montserrat,World,2015,4.3,thousand trips +excursionists,Montserrat,World,2016,4.8,thousand trips +excursionists,Montserrat,World,2017,9.0,thousand trips +excursionists,Montserrat,World,2018,6.5,thousand trips +excursionists,Montserrat,World,2019,8.9,thousand trips +excursionists,Montserrat,World,2020,3.1,thousand trips +excursionists,Curaçao,World,1995,172.0,thousand trips +excursionists,Curaçao,World,1996,173.0,thousand trips +excursionists,Curaçao,World,1997,217.0,thousand trips +excursionists,Curaçao,World,1998,239.0,thousand trips +excursionists,Curaçao,World,1999,235.0,thousand trips +excursionists,Curaçao,World,2000,322.0,thousand trips +excursionists,Curaçao,World,2001,314.0,thousand trips +excursionists,Curaçao,World,2002,327.0,thousand trips +excursionists,Curaçao,World,2003,286.0,thousand trips +excursionists,Curaçao,World,2004,229.0,thousand trips +excursionists,Curaçao,World,2005,288.0,thousand trips +excursionists,Curaçao,World,2006,338.0,thousand trips +excursionists,Curaçao,World,2007,364.0,thousand trips +excursionists,Curaçao,World,2008,364.0,thousand trips +excursionists,Curaçao,World,2009,441.0,thousand trips +excursionists,Curaçao,World,2010,420.0,thousand trips +excursionists,Curaçao,World,2011,447.0,thousand trips +excursionists,Curaçao,World,2012,487.0,thousand trips +excursionists,Curaçao,World,2013,630.0,thousand trips +excursionists,Curaçao,World,2014,677.0,thousand trips +excursionists,Curaçao,World,2015,604.0,thousand trips +excursionists,Curaçao,World,2016,502.0,thousand trips +excursionists,Curaçao,World,2017,659.0,thousand trips +excursionists,Curaçao,World,2018,778.0,thousand trips +excursionists,Curaçao,World,2019,829.0,thousand trips +excursionists,Curaçao,World,2020,261.0,thousand trips +excursionists,Curaçao,World,2021,153.4,thousand trips +excursionists,Curaçao,World,2022,544.6,thousand trips +excursionists,Aruba,World,1995,293.0,thousand trips +excursionists,Aruba,World,1996,316.0,thousand trips +excursionists,Aruba,World,1997,297.0,thousand trips +excursionists,Aruba,World,1998,259.0,thousand trips +excursionists,Aruba,World,1999,289.0,thousand trips +excursionists,Aruba,World,2000,490.0,thousand trips +excursionists,Aruba,World,2001,487.0,thousand trips +excursionists,Aruba,World,2002,582.0,thousand trips +excursionists,Aruba,World,2003,542.0,thousand trips +excursionists,Aruba,World,2004,576.0,thousand trips +excursionists,Aruba,World,2005,553.0,thousand trips +excursionists,Aruba,World,2006,591.0,thousand trips +excursionists,Aruba,World,2007,482.0,thousand trips +excursionists,Aruba,World,2008,556.0,thousand trips +excursionists,Aruba,World,2009,607.0,thousand trips +excursionists,Aruba,World,2010,570.0,thousand trips +excursionists,Aruba,World,2011,600.0,thousand trips +excursionists,Aruba,World,2012,577.0,thousand trips +excursionists,Aruba,World,2013,688.0,thousand trips +excursionists,Aruba,World,2014,667.0,thousand trips +excursionists,Aruba,World,2015,607.0,thousand trips +excursionists,Aruba,World,2016,656.0,thousand trips +excursionists,Aruba,World,2017,792.4,thousand trips +excursionists,Aruba,World,2018,815.0,thousand trips +excursionists,Aruba,World,2019,832.0,thousand trips +excursionists,Aruba,World,2020,255.0,thousand trips +excursionists,Aruba,World,2021,136.0,thousand trips +excursionists,Aruba,World,2022,610.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1995,564.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1996,657.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1997,886.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1998,881.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,1999,616.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2000,868.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2001,868.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2002,1055.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2003,1172.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2004,1348.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2005,1488.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2006,1422.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2007,1422.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2008,1346.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2009,1215.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2010,1513.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2011,1656.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2012,1753.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2013,1786.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2014,2002.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2015,1902.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2016,1669.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2017,1238.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2018,1597.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2019,1632.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2020,436.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2021,233.0,thousand trips +excursionists,Sint Maarten (Dutch part),World,2022,844.0,thousand trips +excursionists,Puerto Rico,World,1995,956.0,thousand trips +excursionists,Puerto Rico,World,1996,1045.0,thousand trips +excursionists,Puerto Rico,World,1997,1108.0,thousand trips +excursionists,Puerto Rico,World,1998,1275.0,thousand trips +excursionists,Puerto Rico,World,1999,1198.0,thousand trips +excursionists,Puerto Rico,World,2000,1225.0,thousand trips +excursionists,Puerto Rico,World,2001,1357.0,thousand trips +excursionists,Puerto Rico,World,2002,1277.0,thousand trips +excursionists,Puerto Rico,World,2003,1164.0,thousand trips +excursionists,Puerto Rico,World,2004,1348.0,thousand trips +excursionists,Puerto Rico,World,2005,1387.0,thousand trips +excursionists,Puerto Rico,World,2006,1300.0,thousand trips +excursionists,Puerto Rico,World,2007,1375.0,thousand trips +excursionists,Puerto Rico,World,2008,1497.0,thousand trips +excursionists,Puerto Rico,World,2009,1232.0,thousand trips +excursionists,Puerto Rico,World,2010,1194.0,thousand trips +excursionists,Puerto Rico,World,2011,1166.0,thousand trips +excursionists,Puerto Rico,World,2012,1128.0,thousand trips +excursionists,Puerto Rico,World,2013,1038.0,thousand trips +excursionists,Puerto Rico,World,2014,1210.0,thousand trips +excursionists,Puerto Rico,World,2015,1509.0,thousand trips +excursionists,Puerto Rico,World,2016,1267.0,thousand trips +excursionists,Puerto Rico,World,2017,1414.0,thousand trips +excursionists,Puerto Rico,World,2018,1192.0,thousand trips +excursionists,Puerto Rico,World,2019,1751.0,thousand trips +excursionists,Puerto Rico,World,2020,1264.6,thousand trips +excursionists,Puerto Rico,World,2022,364.4,thousand trips +excursionists,Saint Kitts and Nevis,World,1995,124.0,thousand trips +excursionists,Saint Kitts and Nevis,World,1996,88.0,thousand trips +excursionists,Saint Kitts and Nevis,World,1997,106.0,thousand trips +excursionists,Saint Kitts and Nevis,World,1998,157.0,thousand trips +excursionists,Saint Kitts and Nevis,World,1999,142.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2000,174.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2001,263.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2002,171.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2003,156.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2004,265.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2005,224.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2006,211.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2007,256.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2008,405.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2009,465.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2010,526.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2011,619.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2012,562.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2013,588.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2014,704.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2015,918.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2016,939.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2017,1078.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2018,1152.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2019,987.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2020,271.0,thousand trips +excursionists,Saint Kitts and Nevis,World,2021,102.4,thousand trips +excursionists,Saint Kitts and Nevis,World,2022,509.3,thousand trips +excursionists,Anguilla,World,1995,68.0,thousand trips +excursionists,Anguilla,World,1996,49.0,thousand trips +excursionists,Anguilla,World,1997,71.0,thousand trips +excursionists,Anguilla,World,1998,70.0,thousand trips +excursionists,Anguilla,World,1999,60.0,thousand trips +excursionists,Anguilla,World,2000,68.0,thousand trips +excursionists,Anguilla,World,2001,57.0,thousand trips +excursionists,Anguilla,World,2002,67.0,thousand trips +excursionists,Anguilla,World,2003,62.0,thousand trips +excursionists,Anguilla,World,2004,67.0,thousand trips +excursionists,Anguilla,World,2005,81.0,thousand trips +excursionists,Anguilla,World,2006,94.0,thousand trips +excursionists,Anguilla,World,2007,86.0,thousand trips +excursionists,Anguilla,World,2008,60.0,thousand trips +excursionists,Anguilla,World,2009,54.0,thousand trips +excursionists,Anguilla,World,2010,56.0,thousand trips +excursionists,Anguilla,World,2011,58.0,thousand trips +excursionists,Anguilla,World,2012,64.0,thousand trips +excursionists,Anguilla,World,2013,82.0,thousand trips +excursionists,Anguilla,World,2014,106.0,thousand trips +excursionists,Anguilla,World,2015,113.0,thousand trips +excursionists,Anguilla,World,2016,97.0,thousand trips +excursionists,Anguilla,World,2017,82.4,thousand trips +excursionists,Anguilla,World,2018,33.0,thousand trips +excursionists,Anguilla,World,2019,71.0,thousand trips +excursionists,Anguilla,World,2020,15.7,thousand trips +excursionists,Anguilla,World,2021,0.3,thousand trips +excursionists,Anguilla,World,2022,21.7,thousand trips +excursionists,Saint Lucia,World,1995,176.0,thousand trips +excursionists,Saint Lucia,World,1996,186.0,thousand trips +excursionists,Saint Lucia,World,1997,315.0,thousand trips +excursionists,Saint Lucia,World,1998,377.0,thousand trips +excursionists,Saint Lucia,World,1999,402.0,thousand trips +excursionists,Saint Lucia,World,2000,457.0,thousand trips +excursionists,Saint Lucia,World,2001,497.0,thousand trips +excursionists,Saint Lucia,World,2002,395.0,thousand trips +excursionists,Saint Lucia,World,2003,406.0,thousand trips +excursionists,Saint Lucia,World,2004,492.0,thousand trips +excursionists,Saint Lucia,World,2005,402.0,thousand trips +excursionists,Saint Lucia,World,2006,367.0,thousand trips +excursionists,Saint Lucia,World,2007,618.0,thousand trips +excursionists,Saint Lucia,World,2008,629.0,thousand trips +excursionists,Saint Lucia,World,2009,704.0,thousand trips +excursionists,Saint Lucia,World,2010,678.0,thousand trips +excursionists,Saint Lucia,World,2011,638.0,thousand trips +excursionists,Saint Lucia,World,2012,582.0,thousand trips +excursionists,Saint Lucia,World,2013,602.0,thousand trips +excursionists,Saint Lucia,World,2014,649.0,thousand trips +excursionists,Saint Lucia,World,2015,686.0,thousand trips +excursionists,Saint Lucia,World,2016,600.0,thousand trips +excursionists,Saint Lucia,World,2017,678.0,thousand trips +excursionists,Saint Lucia,World,2018,770.0,thousand trips +excursionists,Saint Lucia,World,2019,796.0,thousand trips +excursionists,Saint Lucia,World,2020,301.8,thousand trips +excursionists,Saint Lucia,World,2021,98.0,thousand trips +excursionists,Saint Lucia,World,2022,358.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1995,158.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1996,158.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1997,135.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1998,135.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,1999,155.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2000,183.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2001,183.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2002,170.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2003,163.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2004,175.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2005,161.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2006,209.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2007,238.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2008,166.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2009,196.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2010,159.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2011,134.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2012,126.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2013,128.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2014,134.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2015,131.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2016,148.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2017,227.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2018,274.7,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2019,315.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2020,128.0,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2021,32.5,thousand trips +excursionists,Saint Vincent and the Grenadines,World,2022,151.0,thousand trips +excursionists,Trinidad and Tobago,World,1995,22.0,thousand trips +excursionists,Trinidad and Tobago,World,1996,51.0,thousand trips +excursionists,Trinidad and Tobago,World,1997,36.0,thousand trips +excursionists,Trinidad and Tobago,World,1998,43.0,thousand trips +excursionists,Trinidad and Tobago,World,1999,63.0,thousand trips +excursionists,Trinidad and Tobago,World,2000,104.0,thousand trips +excursionists,Trinidad and Tobago,World,2001,85.0,thousand trips +excursionists,Trinidad and Tobago,World,2002,67.0,thousand trips +excursionists,Trinidad and Tobago,World,2003,65.0,thousand trips +excursionists,Trinidad and Tobago,World,2004,58.0,thousand trips +excursionists,Trinidad and Tobago,World,2005,66.0,thousand trips +excursionists,Trinidad and Tobago,World,2006,73.0,thousand trips +excursionists,Trinidad and Tobago,World,2007,82.0,thousand trips +excursionists,Trinidad and Tobago,World,2008,45.0,thousand trips +excursionists,Trinidad and Tobago,World,2009,115.0,thousand trips +excursionists,Trinidad and Tobago,World,2010,102.0,thousand trips +excursionists,Trinidad and Tobago,World,2011,60.0,thousand trips +excursionists,Trinidad and Tobago,World,2012,49.0,thousand trips +excursionists,Trinidad and Tobago,World,2013,33.0,thousand trips +excursionists,Trinidad and Tobago,World,2014,45.8,thousand trips +excursionists,Trinidad and Tobago,World,2015,82.0,thousand trips +excursionists,Trinidad and Tobago,World,2016,88.3,thousand trips +excursionists,Trinidad and Tobago,World,2017,75.2,thousand trips +excursionists,Trinidad and Tobago,World,2018,132.3,thousand trips +excursionists,Trinidad and Tobago,World,2019,93.0,thousand trips +excursionists,Trinidad and Tobago,World,2020,45.8,thousand trips +excursionists,Trinidad and Tobago,World,2021,0.5,thousand trips +excursionists,Trinidad and Tobago,World,2022,26.8,thousand trips +excursionists,Trinidad and Tobago,World,2023,139.0,thousand trips +excursionists,Trinidad and Tobago,World,2024,152.0,thousand trips +excursionists,Turks and Caicos Islands,World,2008,405.0,thousand trips +excursionists,Turks and Caicos Islands,World,2009,514.0,thousand trips +excursionists,Turks and Caicos Islands,World,2010,618.0,thousand trips +excursionists,Turks and Caicos Islands,World,2011,655.6,thousand trips +excursionists,Turks and Caicos Islands,World,2012,676.6,thousand trips +excursionists,Turks and Caicos Islands,World,2013,779.0,thousand trips +excursionists,Turks and Caicos Islands,World,2014,972.0,thousand trips +excursionists,Turks and Caicos Islands,World,2015,930.0,thousand trips +excursionists,Turks and Caicos Islands,World,2016,847.0,thousand trips +excursionists,Turks and Caicos Islands,World,2017,827.0,thousand trips +excursionists,Turks and Caicos Islands,World,2018,1022.0,thousand trips +excursionists,Turks and Caicos Islands,World,2019,1112.0,thousand trips +excursionists,Turks and Caicos Islands,World,2020,205.9,thousand trips +excursionists,Turks and Caicos Islands,World,2021,27.0,thousand trips +excursionists,United States Virgin Islands,World,1995,1287.0,thousand trips +excursionists,United States Virgin Islands,World,1996,1406.0,thousand trips +excursionists,United States Virgin Islands,World,1997,1735.0,thousand trips +excursionists,United States Virgin Islands,World,1998,1717.0,thousand trips +excursionists,United States Virgin Islands,World,1999,1481.0,thousand trips +excursionists,United States Virgin Islands,World,2000,1850.0,thousand trips +excursionists,United States Virgin Islands,World,2001,1970.0,thousand trips +excursionists,United States Virgin Islands,World,2002,1817.0,thousand trips +excursionists,United States Virgin Islands,World,2003,1857.0,thousand trips +excursionists,United States Virgin Islands,World,2004,2057.0,thousand trips +excursionists,United States Virgin Islands,World,2005,2009.0,thousand trips +excursionists,United States Virgin Islands,World,2006,2000.0,thousand trips +excursionists,United States Virgin Islands,World,2007,2019.0,thousand trips +excursionists,United States Virgin Islands,World,2008,1861.0,thousand trips +excursionists,United States Virgin Islands,World,2009,1682.0,thousand trips +excursionists,United States Virgin Islands,World,2010,1959.0,thousand trips +excursionists,United States Virgin Islands,World,2011,2119.0,thousand trips +excursionists,United States Virgin Islands,World,2012,2021.0,thousand trips +excursionists,United States Virgin Islands,World,2013,2111.0,thousand trips +excursionists,United States Virgin Islands,World,2014,2199.0,thousand trips +excursionists,United States Virgin Islands,World,2015,2001.0,thousand trips +excursionists,United States Virgin Islands,World,2016,1906.0,thousand trips +excursionists,United States Virgin Islands,World,2017,1419.0,thousand trips +excursionists,United States Virgin Islands,World,2018,1541.0,thousand trips +excursionists,United States Virgin Islands,World,2019,1560.0,thousand trips +excursionists,United States Virgin Islands,World,2020,558.5,thousand trips +excursionists,United States Virgin Islands,World,2021,387.6,thousand trips +excursionists,Bonaire,World,1995,11.0,thousand trips +excursionists,Bonaire,World,1996,15.0,thousand trips +excursionists,Bonaire,World,1997,23.0,thousand trips +excursionists,Bonaire,World,1998,23.0,thousand trips +excursionists,Bonaire,World,1999,17.0,thousand trips +excursionists,Bonaire,World,2000,45.0,thousand trips +excursionists,Bonaire,World,2001,44.0,thousand trips +excursionists,Bonaire,World,2002,42.0,thousand trips +excursionists,Bonaire,World,2003,45.0,thousand trips +excursionists,Bonaire,World,2004,53.0,thousand trips +excursionists,Bonaire,World,2005,40.0,thousand trips +excursionists,Bonaire,World,2006,62.0,thousand trips +excursionists,Bonaire,World,2007,98.0,thousand trips +excursionists,Bonaire,World,2008,176.0,thousand trips +excursionists,Bonaire,World,2009,213.0,thousand trips +excursionists,Bonaire,World,2010,226.0,thousand trips +excursionists,Saba,World,1995,15.0,thousand trips +excursionists,Saba,World,1996,14.5,thousand trips +excursionists,Saba,World,1997,18.0,thousand trips +excursionists,Saba,World,1998,14.8,thousand trips +excursionists,Saba,World,1999,15.0,thousand trips +excursionists,Saba,World,2000,15.3,thousand trips +excursionists,Saba,World,2001,14.9,thousand trips +excursionists,Saba,World,2002,10.7,thousand trips +excursionists,Saba,World,2003,10.2,thousand trips +excursionists,Saba,World,2004,12.2,thousand trips +excursionists,Saba,World,2005,13.4,thousand trips +excursionists,Saba,World,2006,11.9,thousand trips +excursionists,Saba,World,2007,10.0,thousand trips +excursionists,Saba,World,2008,10.5,thousand trips +excursionists,Saba,World,2009,11.7,thousand trips +excursionists,Saba,World,2010,10.2,thousand trips +visitors_total,Antigua and Barbuda,World,1995,447.0,thousand trips +visitors_total,Antigua and Barbuda,World,1996,498.0,thousand trips +visitors_total,Antigua and Barbuda,World,1997,526.0,thousand trips +visitors_total,Antigua and Barbuda,World,1998,570.0,thousand trips +visitors_total,Antigua and Barbuda,World,1999,560.0,thousand trips +visitors_total,Antigua and Barbuda,World,2000,634.0,thousand trips +visitors_total,Antigua and Barbuda,World,2001,624.0,thousand trips +visitors_total,Antigua and Barbuda,World,2002,528.0,thousand trips +visitors_total,Antigua and Barbuda,World,2003,623.0,thousand trips +visitors_total,Antigua and Barbuda,World,2004,769.0,thousand trips +visitors_total,Antigua and Barbuda,World,2005,712.0,thousand trips +visitors_total,Antigua and Barbuda,World,2006,838.0,thousand trips +visitors_total,Antigua and Barbuda,World,2007,935.0,thousand trips +visitors_total,Antigua and Barbuda,World,2008,863.0,thousand trips +visitors_total,Antigua and Barbuda,World,2009,944.0,thousand trips +visitors_total,Antigua and Barbuda,World,2010,788.0,thousand trips +visitors_total,Antigua and Barbuda,World,2011,848.0,thousand trips +visitors_total,Antigua and Barbuda,World,2012,819.0,thousand trips +visitors_total,Antigua and Barbuda,World,2013,777.0,thousand trips +visitors_total,Antigua and Barbuda,World,2014,771.0,thousand trips +visitors_total,Antigua and Barbuda,World,2015,894.0,thousand trips +visitors_total,Antigua and Barbuda,World,2016,874.0,thousand trips +visitors_total,Antigua and Barbuda,World,2017,1040.0,thousand trips +visitors_total,Antigua and Barbuda,World,2018,1064.0,thousand trips +visitors_total,Antigua and Barbuda,World,2019,1035.0,thousand trips +visitors_total,Antigua and Barbuda,World,2020,143.0,thousand trips +visitors_total,Antigua and Barbuda,World,2021,186.4,thousand trips +visitors_total,Antigua and Barbuda,World,2022,300.1,thousand trips +visitors_total,Antigua and Barbuda,World,2023,320.8,thousand trips +visitors_total,Antigua and Barbuda,World,2024,371.2,thousand trips +visitors_total,Bahamas,World,1995,3239.0,thousand trips +visitors_total,Bahamas,World,1996,3416.0,thousand trips +visitors_total,Bahamas,World,1997,3446.0,thousand trips +visitors_total,Bahamas,World,1998,3348.0,thousand trips +visitors_total,Bahamas,World,1999,3648.0,thousand trips +visitors_total,Bahamas,World,2000,4204.0,thousand trips +visitors_total,Bahamas,World,2001,4183.0,thousand trips +visitors_total,Bahamas,World,2002,4406.0,thousand trips +visitors_total,Bahamas,World,2003,4594.0,thousand trips +visitors_total,Bahamas,World,2004,5004.0,thousand trips +visitors_total,Bahamas,World,2005,4779.0,thousand trips +visitors_total,Bahamas,World,2006,4731.0,thousand trips +visitors_total,Bahamas,World,2007,4601.0,thousand trips +visitors_total,Bahamas,World,2008,4394.0,thousand trips +visitors_total,Bahamas,World,2009,4645.0,thousand trips +visitors_total,Bahamas,World,2010,5255.0,thousand trips +visitors_total,Bahamas,World,2011,5588.0,thousand trips +visitors_total,Bahamas,World,2012,5940.0,thousand trips +visitors_total,Bahamas,World,2013,6151.0,thousand trips +visitors_total,Bahamas,World,2014,6320.0,thousand trips +visitors_total,Bahamas,World,2015,6112.0,thousand trips +visitors_total,Bahamas,World,2016,6265.0,thousand trips +visitors_total,Bahamas,World,2017,6136.0,thousand trips +visitors_total,Bahamas,World,2018,6622.0,thousand trips +visitors_total,Bahamas,World,2019,7250.0,thousand trips +visitors_total,Bahamas,World,2020,1794.5,thousand trips +visitors_total,Bahamas,World,2021,2101.0,thousand trips +visitors_total,Bahamas,World,2022,7000.7,thousand trips +visitors_total,Barbados,World,1995,927.0,thousand trips +visitors_total,Barbados,World,1996,957.0,thousand trips +visitors_total,Barbados,World,1997,990.0,thousand trips +visitors_total,Barbados,World,1998,1019.0,thousand trips +visitors_total,Barbados,World,1999,947.0,thousand trips +visitors_total,Barbados,World,2000,1078.0,thousand trips +visitors_total,Barbados,World,2001,1035.0,thousand trips +visitors_total,Barbados,World,2002,1021.0,thousand trips +visitors_total,Barbados,World,2003,1090.0,thousand trips +visitors_total,Barbados,World,2004,1273.0,thousand trips +visitors_total,Barbados,World,2005,1111.0,thousand trips +visitors_total,Barbados,World,2006,1102.0,thousand trips +visitors_total,Barbados,World,2007,1191.0,thousand trips +visitors_total,Barbados,World,2008,1165.0,thousand trips +visitors_total,Barbados,World,2009,1154.0,thousand trips +visitors_total,Barbados,World,2010,1197.0,thousand trips +visitors_total,Barbados,World,2011,1187.0,thousand trips +visitors_total,Barbados,World,2012,1053.0,thousand trips +visitors_total,Barbados,World,2013,1079.0,thousand trips +visitors_total,Barbados,World,2014,1079.0,thousand trips +visitors_total,Barbados,World,2015,1179.0,thousand trips +visitors_total,Barbados,World,2016,1227.0,thousand trips +visitors_total,Barbados,World,2017,1345.0,thousand trips +visitors_total,Barbados,World,2018,1356.0,thousand trips +visitors_total,Barbados,World,2019,966.0,thousand trips +visitors_total,Barbados,World,2020,545.8,thousand trips +visitors_total,Barbados,World,2021,242.2,thousand trips +visitors_total,Barbados,World,2022,897.6,thousand trips +visitors_total,Barbados,World,2023,636.0459,thousand trips +visitors_total,Barbados,World,2024,704.34,thousand trips +visitors_total,Bermuda,World,1995,557.0,thousand trips +visitors_total,Bermuda,World,1996,570.0,thousand trips +visitors_total,Bermuda,World,1997,562.0,thousand trips +visitors_total,Bermuda,World,1998,558.0,thousand trips +visitors_total,Bermuda,World,1999,549.0,thousand trips +visitors_total,Bermuda,World,2000,539.0,thousand trips +visitors_total,Bermuda,World,2001,458.0,thousand trips +visitors_total,Bermuda,World,2002,484.0,thousand trips +visitors_total,Bermuda,World,2003,483.0,thousand trips +visitors_total,Bermuda,World,2004,478.0,thousand trips +visitors_total,Bermuda,World,2005,517.0,thousand trips +visitors_total,Bermuda,World,2006,635.0,thousand trips +visitors_total,Bermuda,World,2007,660.0,thousand trips +visitors_total,Bermuda,World,2008,550.0,thousand trips +visitors_total,Bermuda,World,2009,554.0,thousand trips +visitors_total,Bermuda,World,2010,580.0,thousand trips +visitors_total,Bermuda,World,2011,652.0,thousand trips +visitors_total,Bermuda,World,2012,610.0,thousand trips +visitors_total,Bermuda,World,2013,576.0,thousand trips +visitors_total,Bermuda,World,2014,580.0,thousand trips +visitors_total,Bermuda,World,2015,597.0,thousand trips +visitors_total,Bermuda,World,2016,642.0,thousand trips +visitors_total,Bermuda,World,2017,688.0,thousand trips +visitors_total,Bermuda,World,2018,766.0,thousand trips +visitors_total,Bermuda,World,2019,805.0,thousand trips +visitors_total,Bermuda,World,2020,51.4,thousand trips +visitors_total,Bermuda,World,2021,86.4,thousand trips +visitors_total,Bermuda,World,2022,548.5,thousand trips +visitors_total,Belize,World,1995,342.0,thousand trips +visitors_total,Belize,World,1996,349.0,thousand trips +visitors_total,Belize,World,1997,305.0,thousand trips +visitors_total,Belize,World,1998,300.0,thousand trips +visitors_total,Belize,World,1999,340.0,thousand trips +visitors_total,Belize,World,2000,374.0,thousand trips +visitors_total,Belize,World,2001,385.0,thousand trips +visitors_total,Belize,World,2002,694.0,thousand trips +visitors_total,Belize,World,2003,999.0,thousand trips +visitors_total,Belize,World,2004,1329.0,thousand trips +visitors_total,Belize,World,2005,1037.0,thousand trips +visitors_total,Belize,World,2006,903.0,thousand trips +visitors_total,Belize,World,2007,880.0,thousand trips +visitors_total,Belize,World,2008,846.0,thousand trips +visitors_total,Belize,World,2009,941.0,thousand trips +visitors_total,Belize,World,2010,1054.0,thousand trips +visitors_total,Belize,World,2011,1106.0,thousand trips +visitors_total,Belize,World,2012,1028.0,thousand trips +visitors_total,Belize,World,2013,1022.0,thousand trips +visitors_total,Belize,World,2014,1289.0,thousand trips +visitors_total,Belize,World,2015,1299.0,thousand trips +visitors_total,Belize,World,2016,1391.0,thousand trips +visitors_total,Belize,World,2017,1441.0,thousand trips +visitors_total,Belize,World,2018,1697.0,thousand trips +visitors_total,Belize,World,2019,1674.0,thousand trips +visitors_total,Belize,World,2020,487.0,thousand trips +visitors_total,Belize,World,2021,437.0,thousand trips +visitors_total,British Virgin Islands,World,1995,365.0,thousand trips +visitors_total,British Virgin Islands,World,1996,412.0,thousand trips +visitors_total,British Virgin Islands,World,1997,365.0,thousand trips +visitors_total,British Virgin Islands,World,1998,392.0,thousand trips +visitors_total,British Virgin Islands,World,1999,484.0,thousand trips +visitors_total,British Virgin Islands,World,2000,519.0,thousand trips +visitors_total,British Virgin Islands,World,2001,535.0,thousand trips +visitors_total,British Virgin Islands,World,2002,544.0,thousand trips +visitors_total,British Virgin Islands,World,2003,614.0,thousand trips +visitors_total,British Virgin Islands,World,2004,813.0,thousand trips +visitors_total,British Virgin Islands,World,2005,821.0,thousand trips +visitors_total,British Virgin Islands,World,2006,825.0,thousand trips +visitors_total,British Virgin Islands,World,2007,948.0,thousand trips +visitors_total,British Virgin Islands,World,2008,934.0,thousand trips +visitors_total,British Virgin Islands,World,2009,857.0,thousand trips +visitors_total,British Virgin Islands,World,2010,842.0,thousand trips +visitors_total,British Virgin Islands,World,2011,831.0,thousand trips +visitors_total,British Virgin Islands,World,2012,753.0,thousand trips +visitors_total,British Virgin Islands,World,2013,742.0,thousand trips +visitors_total,British Virgin Islands,World,2014,773.0,thousand trips +visitors_total,British Virgin Islands,World,2015,922.0,thousand trips +visitors_total,British Virgin Islands,World,2016,1124.0,thousand trips +visitors_total,British Virgin Islands,World,2019,895.0,thousand trips +visitors_total,Cayman Islands,World,1995,1044.0,thousand trips +visitors_total,Cayman Islands,World,1996,1173.0,thousand trips +visitors_total,Cayman Islands,World,1997,1248.0,thousand trips +visitors_total,Cayman Islands,World,1998,1275.0,thousand trips +visitors_total,Cayman Islands,World,1999,1431.0,thousand trips +visitors_total,Cayman Islands,World,2000,1385.0,thousand trips +visitors_total,Cayman Islands,World,2001,1549.0,thousand trips +visitors_total,Cayman Islands,World,2002,1878.0,thousand trips +visitors_total,Cayman Islands,World,2003,2113.0,thousand trips +visitors_total,Cayman Islands,World,2004,1953.0,thousand trips +visitors_total,Cayman Islands,World,2005,1967.0,thousand trips +visitors_total,Cayman Islands,World,2006,2197.0,thousand trips +visitors_total,Cayman Islands,World,2007,2008.0,thousand trips +visitors_total,Cayman Islands,World,2008,1856.0,thousand trips +visitors_total,Cayman Islands,World,2009,1792.0,thousand trips +visitors_total,Cayman Islands,World,2010,1886.0,thousand trips +visitors_total,Cayman Islands,World,2011,1710.0,thousand trips +visitors_total,Cayman Islands,World,2012,1829.0,thousand trips +visitors_total,Cayman Islands,World,2013,1721.0,thousand trips +visitors_total,Cayman Islands,World,2014,1993.0,thousand trips +visitors_total,Cayman Islands,World,2015,2102.0,thousand trips +visitors_total,Cayman Islands,World,2016,2097.0,thousand trips +visitors_total,Cayman Islands,World,2017,2147.0,thousand trips +visitors_total,Cayman Islands,World,2018,2384.0,thousand trips +visitors_total,Cayman Islands,World,2019,2334.0,thousand trips +visitors_total,Cayman Islands,World,2020,660.0,thousand trips +visitors_total,Cayman Islands,World,2022,1027.7,thousand trips +visitors_total,Cuba,World,1995,745.0,thousand trips +visitors_total,Cuba,World,1996,1004.0,thousand trips +visitors_total,Cuba,World,1997,1170.0,thousand trips +visitors_total,Cuba,World,1998,1416.0,thousand trips +visitors_total,Cuba,World,1999,1603.0,thousand trips +visitors_total,Cuba,World,2000,1774.0,thousand trips +visitors_total,Cuba,World,2001,1775.0,thousand trips +visitors_total,Cuba,World,2002,1686.0,thousand trips +visitors_total,Cuba,World,2003,1906.0,thousand trips +visitors_total,Cuba,World,2004,2049.0,thousand trips +visitors_total,Cuba,World,2005,2319.0,thousand trips +visitors_total,Cuba,World,2006,2221.0,thousand trips +visitors_total,Cuba,World,2007,2152.0,thousand trips +visitors_total,Cuba,World,2008,2348.0,thousand trips +visitors_total,Cuba,World,2009,2430.0,thousand trips +visitors_total,Cuba,World,2010,2532.0,thousand trips +visitors_total,Cuba,World,2011,2716.0,thousand trips +visitors_total,Cuba,World,2012,2847.0,thousand trips +visitors_total,Cuba,World,2013,2862.0,thousand trips +visitors_total,Cuba,World,2014,3014.0,thousand trips +visitors_total,Cuba,World,2015,3540.0,thousand trips +visitors_total,Cuba,World,2016,4009.0,thousand trips +visitors_total,Cuba,World,2017,4654.0,thousand trips +visitors_total,Cuba,World,2018,4712.0,thousand trips +visitors_total,Cuba,World,2019,4276.0,thousand trips +visitors_total,Cuba,World,2020,1086.0,thousand trips +visitors_total,Cuba,World,2021,356.4,thousand trips +visitors_total,Cuba,World,2022,1614.1,thousand trips +visitors_total,Dominica,World,1995,203.0,thousand trips +visitors_total,Dominica,World,1996,261.0,thousand trips +visitors_total,Dominica,World,1997,299.0,thousand trips +visitors_total,Dominica,World,1998,312.0,thousand trips +visitors_total,Dominica,World,1999,280.0,thousand trips +visitors_total,Dominica,World,2000,312.0,thousand trips +visitors_total,Dominica,World,2001,276.0,thousand trips +visitors_total,Dominica,World,2002,208.0,thousand trips +visitors_total,Dominica,World,2003,254.0,thousand trips +visitors_total,Dominica,World,2004,466.0,thousand trips +visitors_total,Dominica,World,2005,381.0,thousand trips +visitors_total,Dominica,World,2006,465.0,thousand trips +visitors_total,Dominica,World,2007,437.0,thousand trips +visitors_total,Dominica,World,2008,468.0,thousand trips +visitors_total,Dominica,World,2009,608.0,thousand trips +visitors_total,Dominica,World,2010,595.0,thousand trips +visitors_total,Dominica,World,2011,418.0,thousand trips +visitors_total,Dominica,World,2012,348.0,thousand trips +visitors_total,Dominica,World,2013,311.0,thousand trips +visitors_total,Dominica,World,2014,370.0,thousand trips +visitors_total,Dominica,World,2015,358.0,thousand trips +visitors_total,Dominica,World,2016,356.0,thousand trips +visitors_total,Dominica,World,2017,230.0,thousand trips +visitors_total,Dominica,World,2018,199.0,thousand trips +visitors_total,Dominica,World,2019,322.0,thousand trips +visitors_total,Dominica,World,2020,140.0,thousand trips +visitors_total,Dominica,World,2021,66.0,thousand trips +visitors_total,Dominica,World,2022,235.0,thousand trips +visitors_total,Dominican Republic,World,1995,1806.0,thousand trips +visitors_total,Dominican Republic,World,1996,2037.0,thousand trips +visitors_total,Dominican Republic,World,1997,2482.0,thousand trips +visitors_total,Dominican Republic,World,1998,2702.0,thousand trips +visitors_total,Dominican Republic,World,1999,2932.0,thousand trips +visitors_total,Dominican Republic,World,2000,3161.0,thousand trips +visitors_total,Dominican Republic,World,2001,3090.0,thousand trips +visitors_total,Dominican Republic,World,2002,3058.0,thousand trips +visitors_total,Dominican Republic,World,2003,3680.0,thousand trips +visitors_total,Dominican Republic,World,2004,3907.0,thousand trips +visitors_total,Dominican Republic,World,2005,3981.0,thousand trips +visitors_total,Dominican Republic,World,2006,4268.0,thousand trips +visitors_total,Dominican Republic,World,2007,4365.0,thousand trips +visitors_total,Dominican Republic,World,2008,4455.0,thousand trips +visitors_total,Dominican Republic,World,2009,4489.0,thousand trips +visitors_total,Dominican Republic,World,2010,4478.0,thousand trips +visitors_total,Dominican Republic,World,2011,4654.0,thousand trips +visitors_total,Dominican Republic,World,2012,4901.0,thousand trips +visitors_total,Dominican Republic,World,2013,5114.0,thousand trips +visitors_total,Dominican Republic,World,2014,5576.0,thousand trips +visitors_total,Dominican Republic,World,2015,6129.0,thousand trips +visitors_total,Dominican Republic,World,2016,6769.0,thousand trips +visitors_total,Dominican Republic,World,2017,7296.0,thousand trips +visitors_total,Dominican Republic,World,2018,7551.0,thousand trips +visitors_total,Dominican Republic,World,2019,7550.0,thousand trips +visitors_total,Dominican Republic,World,2020,2748.182,thousand trips +visitors_total,Dominican Republic,World,2021,5327.447,thousand trips +visitors_total,Dominican Republic,World,2022,8488.856,thousand trips +visitors_total,Dominican Republic,World,2023,10317.612274029,thousand trips +visitors_total,Dominican Republic,World,2024,11162.2297227628,thousand trips +visitors_total,Grenada,World,1995,369.0,thousand trips +visitors_total,Grenada,World,1996,386.0,thousand trips +visitors_total,Grenada,World,1997,368.0,thousand trips +visitors_total,Grenada,World,1998,392.0,thousand trips +visitors_total,Grenada,World,1999,379.0,thousand trips +visitors_total,Grenada,World,2000,316.0,thousand trips +visitors_total,Grenada,World,2001,277.0,thousand trips +visitors_total,Grenada,World,2002,271.0,thousand trips +visitors_total,Grenada,World,2003,294.0,thousand trips +visitors_total,Grenada,World,2004,370.0,thousand trips +visitors_total,Grenada,World,2005,380.0,thousand trips +visitors_total,Grenada,World,2006,342.0,thousand trips +visitors_total,Grenada,World,2007,406.0,thousand trips +visitors_total,Grenada,World,2008,428.0,thousand trips +visitors_total,Grenada,World,2009,460.0,thousand trips +visitors_total,Grenada,World,2010,445.0,thousand trips +visitors_total,Grenada,World,2011,447.0,thousand trips +visitors_total,Grenada,World,2012,370.0,thousand trips +visitors_total,Grenada,World,2013,327.0,thousand trips +visitors_total,Grenada,World,2014,387.0,thousand trips +visitors_total,Grenada,World,2015,437.0,thousand trips +visitors_total,Grenada,World,2016,474.0,thousand trips +visitors_total,Grenada,World,2017,468.0,thousand trips +visitors_total,Grenada,World,2018,529.0,thousand trips +visitors_total,Grenada,World,2019,526.0,thousand trips +visitors_total,Grenada,World,2020,217.0,thousand trips +visitors_total,Grenada,World,2021,72.0,thousand trips +visitors_total,Grenada,World,2022,335.0,thousand trips +visitors_total,Guadeloupe,World,1995,1059.0,thousand trips +visitors_total,Guadeloupe,World,1996,1236.0,thousand trips +visitors_total,Guadeloupe,World,1997,1130.0,thousand trips +visitors_total,Guadeloupe,World,1998,914.0,thousand trips +visitors_total,Guadeloupe,World,1999,854.0,thousand trips +visitors_total,Guadeloupe,World,2000,994.0,thousand trips +visitors_total,Guadeloupe,World,2001,759.0,thousand trips +visitors_total,Guadeloupe,World,2003,569.0,thousand trips +visitors_total,Guadeloupe,World,2004,560.0,thousand trips +visitors_total,Guadeloupe,World,2005,445.0,thousand trips +visitors_total,Guadeloupe,World,2006,447.0,thousand trips +visitors_total,Guadeloupe,World,2007,500.0,thousand trips +visitors_total,Guadeloupe,World,2008,527.0,thousand trips +visitors_total,Guadeloupe,World,2009,458.0,thousand trips +visitors_total,Guadeloupe,World,2010,497.0,thousand trips +visitors_total,Guadeloupe,World,2011,419.0,thousand trips +visitors_total,Guadeloupe,World,2012,487.0,thousand trips +visitors_total,Guadeloupe,World,2013,645.0,thousand trips +visitors_total,Guadeloupe,World,2014,720.0,thousand trips +visitors_total,Guadeloupe,World,2015,822.0,thousand trips +visitors_total,Guadeloupe,World,2016,857.0,thousand trips +visitors_total,Guadeloupe,World,2017,970.0,thousand trips +visitors_total,Guadeloupe,World,2018,1166.0,thousand trips +visitors_total,Haiti,World,1995,370.0,thousand trips +visitors_total,Haiti,World,1996,400.0,thousand trips +visitors_total,Haiti,World,1997,387.0,thousand trips +visitors_total,Haiti,World,1998,393.0,thousand trips +visitors_total,Haiti,World,1999,386.0,thousand trips +visitors_total,Haiti,World,2000,445.0,thousand trips +visitors_total,Haiti,World,2001,499.0,thousand trips +visitors_total,Haiti,World,2002,482.0,thousand trips +visitors_total,Haiti,World,2003,518.0,thousand trips +visitors_total,Haiti,World,2004,385.0,thousand trips +visitors_total,Haiti,World,2005,480.0,thousand trips +visitors_total,Haiti,World,2006,558.0,thousand trips +visitors_total,Haiti,World,2007,868.0,thousand trips +visitors_total,Haiti,World,2008,758.0,thousand trips +visitors_total,Haiti,World,2009,826.0,thousand trips +visitors_total,Haiti,World,2010,793.0,thousand trips +visitors_total,Haiti,World,2011,946.0,thousand trips +visitors_total,Haiti,World,2012,959.0,thousand trips +visitors_total,Haiti,World,2013,1064.0,thousand trips +visitors_total,Haiti,World,2014,1127.6,thousand trips +visitors_total,Haiti,World,2015,1190.0,thousand trips +visitors_total,Haiti,World,2016,1153.0,thousand trips +visitors_total,Haiti,World,2017,1262.0,thousand trips +visitors_total,Haiti,World,2018,1333.0,thousand trips +visitors_total,Haiti,World,2019,938.0,thousand trips +visitors_total,Haiti,World,2020,322.0,thousand trips +visitors_total,Haiti,World,2021,148.0,thousand trips +visitors_total,Jamaica,World,1995,1752.0,thousand trips +visitors_total,Jamaica,World,1996,1821.0,thousand trips +visitors_total,Jamaica,World,1997,1904.0,thousand trips +visitors_total,Jamaica,World,1998,1899.0,thousand trips +visitors_total,Jamaica,World,1999,2013.0,thousand trips +visitors_total,Jamaica,World,2000,2230.0,thousand trips +visitors_total,Jamaica,World,2001,2117.0,thousand trips +visitors_total,Jamaica,World,2002,2131.0,thousand trips +visitors_total,Jamaica,World,2003,2483.0,thousand trips +visitors_total,Jamaica,World,2004,2515.0,thousand trips +visitors_total,Jamaica,World,2005,2615.0,thousand trips +visitors_total,Jamaica,World,2006,3016.0,thousand trips +visitors_total,Jamaica,World,2007,2880.0,thousand trips +visitors_total,Jamaica,World,2008,2859.0,thousand trips +visitors_total,Jamaica,World,2009,2753.0,thousand trips +visitors_total,Jamaica,World,2010,2832.0,thousand trips +visitors_total,Jamaica,World,2011,3077.0,thousand trips +visitors_total,Jamaica,World,2012,3306.0,thousand trips +visitors_total,Jamaica,World,2013,3274.0,thousand trips +visitors_total,Jamaica,World,2014,3504.0,thousand trips +visitors_total,Jamaica,World,2015,3692.0,thousand trips +visitors_total,Jamaica,World,2016,3837.0,thousand trips +visitors_total,Jamaica,World,2017,4276.0,thousand trips +visitors_total,Jamaica,World,2018,4319.0,thousand trips +visitors_total,Jamaica,World,2019,4234.0,thousand trips +visitors_total,Jamaica,World,2020,1329.7,thousand trips +visitors_total,Jamaica,World,2021,1535.0,thousand trips +visitors_total,Jamaica,World,2022,3330.7,thousand trips +visitors_total,Martinique,World,1995,885.0,thousand trips +visitors_total,Martinique,World,1996,885.0,thousand trips +visitors_total,Martinique,World,1997,900.0,thousand trips +visitors_total,Martinique,World,1998,964.0,thousand trips +visitors_total,Martinique,World,1999,903.0,thousand trips +visitors_total,Martinique,World,2000,816.0,thousand trips +visitors_total,Martinique,World,2001,663.0,thousand trips +visitors_total,Martinique,World,2002,654.0,thousand trips +visitors_total,Martinique,World,2003,722.0,thousand trips +visitors_total,Martinique,World,2004,630.0,thousand trips +visitors_total,Martinique,World,2005,577.0,thousand trips +visitors_total,Martinique,World,2006,599.0,thousand trips +visitors_total,Martinique,World,2007,573.0,thousand trips +visitors_total,Martinique,World,2008,568.0,thousand trips +visitors_total,Martinique,World,2009,511.0,thousand trips +visitors_total,Martinique,World,2010,553.0,thousand trips +visitors_total,Martinique,World,2011,538.0,thousand trips +visitors_total,Martinique,World,2012,603.0,thousand trips +visitors_total,Martinique,World,2013,622.0,thousand trips +visitors_total,Martinique,World,2014,696.0,thousand trips +visitors_total,Martinique,World,2015,762.0,thousand trips +visitors_total,Martinique,World,2016,842.0,thousand trips +visitors_total,Martinique,World,2017,1003.0,thousand trips +visitors_total,Martinique,World,2018,1002.0,thousand trips +visitors_total,Martinique,World,2019,919.0,thousand trips +visitors_total,Martinique,World,2020,523.6,thousand trips +visitors_total,Martinique,World,2021,312.0,thousand trips +visitors_total,Martinique,World,2022,661.0,thousand trips +visitors_total,Montserrat,World,1995,19.3,thousand trips +visitors_total,Montserrat,World,1996,9.0,thousand trips +visitors_total,Montserrat,World,1997,6.1,thousand trips +visitors_total,Montserrat,World,1998,9.4,thousand trips +visitors_total,Montserrat,World,1999,13.0,thousand trips +visitors_total,Montserrat,World,2000,14.3,thousand trips +visitors_total,Montserrat,World,2001,15.6,thousand trips +visitors_total,Montserrat,World,2002,15.0,thousand trips +visitors_total,Montserrat,World,2003,13.6,thousand trips +visitors_total,Montserrat,World,2004,15.2,thousand trips +visitors_total,Montserrat,World,2005,13.1,thousand trips +visitors_total,Montserrat,World,2006,9.5,thousand trips +visitors_total,Montserrat,World,2007,8.7,thousand trips +visitors_total,Montserrat,World,2008,8.4,thousand trips +visitors_total,Montserrat,World,2009,7.3,thousand trips +visitors_total,Montserrat,World,2010,7.7,thousand trips +visitors_total,Montserrat,World,2011,7.4,thousand trips +visitors_total,Montserrat,World,2012,9.905,thousand trips +visitors_total,Montserrat,World,2013,8.7,thousand trips +visitors_total,Montserrat,World,2014,10.7,thousand trips +visitors_total,Montserrat,World,2015,13.3,thousand trips +visitors_total,Montserrat,World,2016,13.5,thousand trips +visitors_total,Montserrat,World,2017,18.6,thousand trips +visitors_total,Montserrat,World,2018,16.7,thousand trips +visitors_total,Montserrat,World,2019,19.3,thousand trips +visitors_total,Montserrat,World,2020,7.7,thousand trips +visitors_total,Montserrat,World,2021,1.6,thousand trips +visitors_total,Curaçao,World,1995,396.0,thousand trips +visitors_total,Curaçao,World,1996,387.0,thousand trips +visitors_total,Curaçao,World,1997,422.0,thousand trips +visitors_total,Curaçao,World,1998,437.0,thousand trips +visitors_total,Curaçao,World,1999,433.0,thousand trips +visitors_total,Curaçao,World,2000,514.0,thousand trips +visitors_total,Curaçao,World,2001,519.0,thousand trips +visitors_total,Curaçao,World,2002,545.0,thousand trips +visitors_total,Curaçao,World,2003,508.0,thousand trips +visitors_total,Curaçao,World,2004,452.0,thousand trips +visitors_total,Curaçao,World,2005,510.0,thousand trips +visitors_total,Curaçao,World,2006,572.0,thousand trips +visitors_total,Curaçao,World,2007,664.0,thousand trips +visitors_total,Curaçao,World,2008,773.0,thousand trips +visitors_total,Curaçao,World,2009,808.0,thousand trips +visitors_total,Curaçao,World,2010,762.0,thousand trips +visitors_total,Curaçao,World,2011,837.0,thousand trips +visitors_total,Curaçao,World,2012,908.0,thousand trips +visitors_total,Curaçao,World,2013,1071.0,thousand trips +visitors_total,Curaçao,World,2014,1129.0,thousand trips +visitors_total,Curaçao,World,2015,1072.0,thousand trips +visitors_total,Curaçao,World,2016,944.0,thousand trips +visitors_total,Curaçao,World,2017,1058.0,thousand trips +visitors_total,Curaçao,World,2018,1210.0,thousand trips +visitors_total,Curaçao,World,2019,1293.0,thousand trips +visitors_total,Curaçao,World,2020,436.0,thousand trips +visitors_total,Curaçao,World,2021,418.0,thousand trips +visitors_total,Curaçao,World,2022,1034.2,thousand trips +visitors_total,Aruba,World,1995,912.0,thousand trips +visitors_total,Aruba,World,1996,957.0,thousand trips +visitors_total,Aruba,World,1997,947.0,thousand trips +visitors_total,Aruba,World,1998,906.0,thousand trips +visitors_total,Aruba,World,1999,972.0,thousand trips +visitors_total,Aruba,World,2000,1211.0,thousand trips +visitors_total,Aruba,World,2001,1178.0,thousand trips +visitors_total,Aruba,World,2002,1225.0,thousand trips +visitors_total,Aruba,World,2003,1184.0,thousand trips +visitors_total,Aruba,World,2004,1304.0,thousand trips +visitors_total,Aruba,World,2005,1286.0,thousand trips +visitors_total,Aruba,World,2006,1285.0,thousand trips +visitors_total,Aruba,World,2007,1254.0,thousand trips +visitors_total,Aruba,World,2008,1383.0,thousand trips +visitors_total,Aruba,World,2009,1420.0,thousand trips +visitors_total,Aruba,World,2010,1394.0,thousand trips +visitors_total,Aruba,World,2011,1469.0,thousand trips +visitors_total,Aruba,World,2012,1481.0,thousand trips +visitors_total,Aruba,World,2013,1667.0,thousand trips +visitors_total,Aruba,World,2014,1739.0,thousand trips +visitors_total,Aruba,World,2015,1832.0,thousand trips +visitors_total,Aruba,World,2016,1758.0,thousand trips +visitors_total,Aruba,World,2017,1863.0,thousand trips +visitors_total,Aruba,World,2018,1897.0,thousand trips +visitors_total,Aruba,World,2019,1951.0,thousand trips +visitors_total,Aruba,World,2020,623.0,thousand trips +visitors_total,Aruba,World,2021,943.0,thousand trips +visitors_total,Aruba,World,2022,1711.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,1995,1024.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,1996,1022.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,1997,1325.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,1998,1339.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,1999,1061.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2000,1300.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2001,1271.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2002,1436.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2003,1600.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2004,1823.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2005,1956.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2006,1890.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2007,1891.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2008,1821.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2009,1655.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2010,1956.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2011,2080.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2012,2210.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2013,2253.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2014,2502.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2015,2407.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2016,2197.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2017,1640.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2018,1775.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2019,1952.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2020,542.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2021,482.0,thousand trips +visitors_total,Sint Maarten (Dutch part),World,2022,1217.0,thousand trips +visitors_total,Puerto Rico,World,1995,4087.0,thousand trips +visitors_total,Puerto Rico,World,1996,4110.0,thousand trips +visitors_total,Puerto Rico,World,1997,4350.0,thousand trips +visitors_total,Puerto Rico,World,1998,4671.0,thousand trips +visitors_total,Puerto Rico,World,1999,4221.0,thousand trips +visitors_total,Puerto Rico,World,2000,4566.0,thousand trips +visitors_total,Puerto Rico,World,2001,4908.0,thousand trips +visitors_total,Puerto Rico,World,2002,4364.0,thousand trips +visitors_total,Puerto Rico,World,2003,4402.0,thousand trips +visitors_total,Puerto Rico,World,2004,4889.0,thousand trips +visitors_total,Puerto Rico,World,2005,5073.0,thousand trips +visitors_total,Puerto Rico,World,2006,5022.0,thousand trips +visitors_total,Puerto Rico,World,2007,5062.0,thousand trips +visitors_total,Puerto Rico,World,2008,5213.0,thousand trips +visitors_total,Puerto Rico,World,2009,4415.0,thousand trips +visitors_total,Puerto Rico,World,2010,4379.0,thousand trips +visitors_total,Puerto Rico,World,2011,4214.0,thousand trips +visitors_total,Puerto Rico,World,2012,4197.0,thousand trips +visitors_total,Puerto Rico,World,2013,4210.0,thousand trips +visitors_total,Puerto Rico,World,2014,4456.0,thousand trips +visitors_total,Puerto Rico,World,2015,5051.0,thousand trips +visitors_total,Puerto Rico,World,2016,5003.0,thousand trips +visitors_total,Puerto Rico,World,2017,4927.0,thousand trips +visitors_total,Puerto Rico,World,2018,4260.0,thousand trips +visitors_total,Puerto Rico,World,2019,4931.0,thousand trips +visitors_total,Puerto Rico,World,2020,3882.0,thousand trips +visitors_total,Puerto Rico,World,2021,2754.7,thousand trips +visitors_total,Puerto Rico,World,2022,3637.5,thousand trips +visitors_total,Saint Kitts and Nevis,World,1995,203.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,1996,172.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,1997,194.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,1998,250.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,1999,226.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2000,247.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2001,334.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2002,240.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2003,247.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2004,383.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2005,365.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2006,350.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2007,379.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2008,533.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2009,548.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2010,624.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2011,723.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2012,666.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2013,695.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2014,818.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2015,1036.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2016,1055.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2017,1194.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2018,1278.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2019,1107.0,thousand trips +visitors_total,Saint Kitts and Nevis,World,2020,301.4,thousand trips +visitors_total,Saint Kitts and Nevis,World,2021,122.6,thousand trips +visitors_total,Saint Kitts and Nevis,World,2022,589.1,thousand trips +visitors_total,Anguilla,World,1995,107.0,thousand trips +visitors_total,Anguilla,World,1996,86.0,thousand trips +visitors_total,Anguilla,World,1997,114.0,thousand trips +visitors_total,Anguilla,World,1998,114.0,thousand trips +visitors_total,Anguilla,World,1999,107.0,thousand trips +visitors_total,Anguilla,World,2000,112.0,thousand trips +visitors_total,Anguilla,World,2001,105.0,thousand trips +visitors_total,Anguilla,World,2002,111.0,thousand trips +visitors_total,Anguilla,World,2003,109.0,thousand trips +visitors_total,Anguilla,World,2004,121.0,thousand trips +visitors_total,Anguilla,World,2005,143.0,thousand trips +visitors_total,Anguilla,World,2006,167.0,thousand trips +visitors_total,Anguilla,World,2007,164.0,thousand trips +visitors_total,Anguilla,World,2008,128.0,thousand trips +visitors_total,Anguilla,World,2009,112.0,thousand trips +visitors_total,Anguilla,World,2010,118.0,thousand trips +visitors_total,Anguilla,World,2011,124.0,thousand trips +visitors_total,Anguilla,World,2012,129.0,thousand trips +visitors_total,Anguilla,World,2013,151.0,thousand trips +visitors_total,Anguilla,World,2014,177.0,thousand trips +visitors_total,Anguilla,World,2015,186.0,thousand trips +visitors_total,Anguilla,World,2016,176.0,thousand trips +visitors_total,Anguilla,World,2017,151.0,thousand trips +visitors_total,Anguilla,World,2018,87.0,thousand trips +visitors_total,Anguilla,World,2019,166.0,thousand trips +visitors_total,Anguilla,World,2020,41.1,thousand trips +visitors_total,Anguilla,World,2021,28.7,thousand trips +visitors_total,Anguilla,World,2022,95.8,thousand trips +visitors_total,Saint Lucia,World,1995,407.0,thousand trips +visitors_total,Saint Lucia,World,1996,422.0,thousand trips +visitors_total,Saint Lucia,World,1997,563.0,thousand trips +visitors_total,Saint Lucia,World,1998,629.0,thousand trips +visitors_total,Saint Lucia,World,1999,666.0,thousand trips +visitors_total,Saint Lucia,World,2000,727.0,thousand trips +visitors_total,Saint Lucia,World,2001,747.0,thousand trips +visitors_total,Saint Lucia,World,2002,648.0,thousand trips +visitors_total,Saint Lucia,World,2003,683.0,thousand trips +visitors_total,Saint Lucia,World,2004,791.0,thousand trips +visitors_total,Saint Lucia,World,2005,720.0,thousand trips +visitors_total,Saint Lucia,World,2006,670.0,thousand trips +visitors_total,Saint Lucia,World,2007,905.0,thousand trips +visitors_total,Saint Lucia,World,2008,925.0,thousand trips +visitors_total,Saint Lucia,World,2009,982.0,thousand trips +visitors_total,Saint Lucia,World,2010,984.0,thousand trips +visitors_total,Saint Lucia,World,2011,950.0,thousand trips +visitors_total,Saint Lucia,World,2012,889.0,thousand trips +visitors_total,Saint Lucia,World,2013,921.0,thousand trips +visitors_total,Saint Lucia,World,2014,987.0,thousand trips +visitors_total,Saint Lucia,World,2015,1031.0,thousand trips +visitors_total,Saint Lucia,World,2016,948.0,thousand trips +visitors_total,Saint Lucia,World,2017,1064.0,thousand trips +visitors_total,Saint Lucia,World,2018,1165.0,thousand trips +visitors_total,Saint Lucia,World,2019,1220.0,thousand trips +visitors_total,Saint Lucia,World,2020,432.5,thousand trips +visitors_total,Saint Lucia,World,2021,297.0,thousand trips +visitors_total,Saint Lucia,World,2022,714.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,1995,218.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,1996,216.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,1997,200.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,1998,202.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,1999,223.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2000,256.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2001,254.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2002,247.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2003,242.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2004,262.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2005,256.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2006,306.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2007,328.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2008,250.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2009,271.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2010,231.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2011,208.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2012,200.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2013,200.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2014,205.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2015,207.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2016,227.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2017,303.6,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2018,356.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2019,399.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2020,155.0,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2021,56.7,thousand trips +visitors_total,Saint Vincent and the Grenadines,World,2022,210.0,thousand trips +visitors_total,Trinidad and Tobago,World,1995,282.0,thousand trips +visitors_total,Trinidad and Tobago,World,1996,317.0,thousand trips +visitors_total,Trinidad and Tobago,World,1997,360.0,thousand trips +visitors_total,Trinidad and Tobago,World,1998,377.0,thousand trips +visitors_total,Trinidad and Tobago,World,1999,421.0,thousand trips +visitors_total,Trinidad and Tobago,World,2000,503.0,thousand trips +visitors_total,Trinidad and Tobago,World,2001,468.0,thousand trips +visitors_total,Trinidad and Tobago,World,2002,451.0,thousand trips +visitors_total,Trinidad and Tobago,World,2003,474.0,thousand trips +visitors_total,Trinidad and Tobago,World,2004,501.0,thousand trips +visitors_total,Trinidad and Tobago,World,2005,529.0,thousand trips +visitors_total,Trinidad and Tobago,World,2006,534.0,thousand trips +visitors_total,Trinidad and Tobago,World,2007,531.0,thousand trips +visitors_total,Trinidad and Tobago,World,2008,482.0,thousand trips +visitors_total,Trinidad and Tobago,World,2009,534.0,thousand trips +visitors_total,Trinidad and Tobago,World,2010,490.0,thousand trips +visitors_total,Trinidad and Tobago,World,2011,491.0,thousand trips +visitors_total,Trinidad and Tobago,World,2012,504.0,thousand trips +visitors_total,Trinidad and Tobago,World,2013,467.0,thousand trips +visitors_total,Trinidad and Tobago,World,2014,458.6,thousand trips +visitors_total,Trinidad and Tobago,World,2015,521.7,thousand trips +visitors_total,Trinidad and Tobago,World,2016,498.3,thousand trips +visitors_total,Trinidad and Tobago,World,2017,469.8,thousand trips +visitors_total,Trinidad and Tobago,World,2018,507.8,thousand trips +visitors_total,Trinidad and Tobago,World,2019,481.5,thousand trips +visitors_total,Trinidad and Tobago,World,2020,141.1,thousand trips +visitors_total,Trinidad and Tobago,World,2021,41.1,thousand trips +visitors_total,Trinidad and Tobago,World,2022,253.3,thousand trips +visitors_total,Trinidad and Tobago,World,2023,448.0,thousand trips +visitors_total,Trinidad and Tobago,World,2024,486.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2008,757.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2009,865.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2010,899.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2011,1010.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2012,968.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2013,1069.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2014,1330.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2015,1316.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2016,1296.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2017,1243.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2018,1463.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2019,1599.0,thousand trips +visitors_total,Turks and Caicos Islands,World,2020,370.4,thousand trips +visitors_total,Turks and Caicos Islands,World,2021,432.0,thousand trips +visitors_total,United States Virgin Islands,World,1995,1741.0,thousand trips +visitors_total,United States Virgin Islands,World,1996,1779.0,thousand trips +visitors_total,United States Virgin Islands,World,1997,2128.0,thousand trips +visitors_total,United States Virgin Islands,World,1998,2139.0,thousand trips +visitors_total,United States Virgin Islands,World,1999,1964.0,thousand trips +visitors_total,United States Virgin Islands,World,2000,2396.0,thousand trips +visitors_total,United States Virgin Islands,World,2001,2497.0,thousand trips +visitors_total,United States Virgin Islands,World,2002,2337.0,thousand trips +visitors_total,United States Virgin Islands,World,2003,2395.0,thousand trips +visitors_total,United States Virgin Islands,World,2004,2620.0,thousand trips +visitors_total,United States Virgin Islands,World,2005,2602.0,thousand trips +visitors_total,United States Virgin Islands,World,2006,2571.0,thousand trips +visitors_total,United States Virgin Islands,World,2007,2606.0,thousand trips +visitors_total,United States Virgin Islands,World,2008,2435.0,thousand trips +visitors_total,United States Virgin Islands,World,2009,2245.0,thousand trips +visitors_total,United States Virgin Islands,World,2010,2531.0,thousand trips +visitors_total,United States Virgin Islands,World,2011,2686.0,thousand trips +visitors_total,United States Virgin Islands,World,2012,2640.0,thousand trips +visitors_total,United States Virgin Islands,World,2013,2702.0,thousand trips +visitors_total,United States Virgin Islands,World,2014,2814.0,thousand trips +visitors_total,United States Virgin Islands,World,2015,2643.0,thousand trips +visitors_total,United States Virgin Islands,World,2016,2574.0,thousand trips +visitors_total,United States Virgin Islands,World,2017,1954.0,thousand trips +visitors_total,United States Virgin Islands,World,2018,1922.0,thousand trips +visitors_total,United States Virgin Islands,World,2019,2074.0,thousand trips +visitors_total,United States Virgin Islands,World,2020,862.0,thousand trips +visitors_total,United States Virgin Islands,World,2021,1072.4,thousand trips +visitors_total,Bonaire,World,1995,70.0,thousand trips +visitors_total,Bonaire,World,1996,80.0,thousand trips +visitors_total,Bonaire,World,1997,86.0,thousand trips +visitors_total,Bonaire,World,1998,85.0,thousand trips +visitors_total,Bonaire,World,1999,78.0,thousand trips +visitors_total,Bonaire,World,2000,96.0,thousand trips +visitors_total,Bonaire,World,2001,94.0,thousand trips +visitors_total,Bonaire,World,2002,94.0,thousand trips +visitors_total,Bonaire,World,2003,107.0,thousand trips +visitors_total,Bonaire,World,2004,116.0,thousand trips +visitors_total,Bonaire,World,2005,103.0,thousand trips +visitors_total,Bonaire,World,2006,126.0,thousand trips +visitors_total,Bonaire,World,2007,172.0,thousand trips +visitors_total,Bonaire,World,2008,250.0,thousand trips +visitors_total,Bonaire,World,2009,280.0,thousand trips +visitors_total,Bonaire,World,2010,297.0,thousand trips +visitors_total,Saba,World,1995,25.0,thousand trips +visitors_total,Saba,World,1996,24.3,thousand trips +visitors_total,Saba,World,1997,28.6,thousand trips +visitors_total,Saba,World,1998,25.4,thousand trips +visitors_total,Saba,World,1999,24.3,thousand trips +visitors_total,Saba,World,2000,24.4,thousand trips +visitors_total,Saba,World,2001,23.9,thousand trips +visitors_total,Saba,World,2002,21.5,thousand trips +visitors_total,Saba,World,2003,20.5,thousand trips +visitors_total,Saba,World,2004,23.2,thousand trips +visitors_total,Saba,World,2005,24.9,thousand trips +visitors_total,Saba,World,2006,22.9,thousand trips +visitors_total,Saba,World,2007,21.7,thousand trips +visitors_total,Saba,World,2008,22.5,thousand trips +visitors_total,Saba,World,2009,23.7,thousand trips +visitors_total,Saba,World,2010,22.5,thousand trips +visitors_total,St. Eustatius,World,1995,25.0,thousand trips +visitors_total,St. Eustatius,World,1996,24.0,thousand trips +visitors_total,St. Eustatius,World,1997,25.0,thousand trips +visitors_total,St. Eustatius,World,1998,25.0,thousand trips +visitors_total,St. Eustatius,World,1999,25.0,thousand trips +visitors_total,St. Eustatius,World,2000,27.0,thousand trips +visitors_total,St. Eustatius,World,2001,30.0,thousand trips From a1f50c241988d40c19b9426a8bc24beb8eb43dab Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 10:18:13 -0500 Subject: [PATCH 04/18] Understanding the Data and Loading and Exploring the Data --- 4_data_analysis/MLProject.ipynb | 262 +++++++++++++++++++++++++++++--- 1 file changed, 244 insertions(+), 18 deletions(-) diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index 1bdb2ea..4ec64f4 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -10,7 +10,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 11, "id": "9beb769c", "metadata": {}, "outputs": [ @@ -20,34 +20,163 @@ "'/home/clif_lastrophysicien/ELO2_LAPERLE_HT/4_data_analysis'" ] }, - "execution_count": 1, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "import os\n", - "import pandas as pd\n", - "import numpy as np \n", + "import os \n", "import sys\n", "\n", "os.getcwd()\n" ] }, + { + "cell_type": "markdown", + "id": "4031f41a", + "metadata": {}, + "source": [ + "Step 1: Understanding the Data" + ] + }, { "cell_type": "code", - "execution_count": 2, - "id": "4906fae4", + "execution_count": 15, + "id": "cd59ce1b", "metadata": {}, "outputs": [], "source": [ - "data = pd.read_csv('UN_tourism_caribbean_countries_cleaned.csv')" + "# Import necessary libraries\n", + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.linear_model import LinearRegression\n", + "from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error\n", + "from sklearn.preprocessing import LabelEncoder, StandardScaler\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "# Set display options\n", + "pd.set_option(\"display.max_columns\", None)\n", + "pd.set_option(\"display.max_rows\", 100)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "7c11579f", + "metadata": {}, + "outputs": [], + "source": [ + "# Set style for better visualizations\n", + "plt.style.use(\"seaborn-v0_8-darkgrid\")\n", + "sns.set_palette(\"husl\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "7dfb6d43", + "metadata": {}, + "source": [ + "2: Loading and Exploring the Data" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "3b222fd2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== DATASET INFORMATION ===\n", + "Shape: (3025, 6)\n", + "\n", + "Columns: ['type_of_visitors', 'country_receiving', 'where_tourist_from', 'year', 'number_of_tourist', 'unit']\n", + "\n", + "Data Types:\n", + "type_of_visitors object\n", + "country_receiving object\n", + "where_tourist_from object\n", + "year int64\n", + "number_of_tourist float64\n", + "unit object\n", + "dtype: object\n", + "\n", + "Missing Values:\n", + "type_of_visitors 0\n", + "country_receiving 0\n", + "where_tourist_from 0\n", + "year 0\n", + "number_of_tourist 0\n", + "unit 0\n", + "dtype: int64\n", + "\n", + "First 5 rows:\n", + " type_of_visitors country_receiving where_tourist_from year \\\n", + "0 excursionists Antigua and Barbuda World 1995 \n", + "1 excursionists Antigua and Barbuda World 1996 \n", + "2 excursionists Antigua and Barbuda World 1997 \n", + "3 excursionists Antigua and Barbuda World 1998 \n", + "4 excursionists Antigua and Barbuda World 1999 \n", + "\n", + " number_of_tourist unit \n", + "0 227.0 thousand trips \n", + "1 270.0 thousand trips \n", + "2 286.0 thousand trips \n", + "3 336.0 thousand trips \n", + "4 328.0 thousand trips \n", + "\n", + "=== BASIC STATISTICS ===\n", + " year number_of_tourist\n", + "count 3025.000000 3025.000000\n", + "mean 2008.196033 821.920078\n", + "std 7.987771 1142.864089\n", + "min 1995.000000 0.100000\n", + "25% 2001.000000 170.000000\n", + "50% 2008.000000 424.000000\n", + "75% 2015.000000 972.000000\n", + "max 2024.000000 11162.229723\n", + "\n", + "=== UNIQUE VALUES ===\n", + "Visitor types: ['excursionists' 'tourists' 'visitors_total']\n", + "Number of countries: 30\n", + "Years range: 1995 to 2024\n" + ] + } + ], + "source": [ + "# Load the dataset\n", + "df = pd.read_csv(\"UN_tourism_caribbean_countries_cleaned.csv\")\n", + "\n", + "# Display basic information\n", + "print(\"=== DATASET INFORMATION ===\")\n", + "print(f\"Shape: {df.shape}\")\n", + "print(f\"\\nColumns: {df.columns.tolist()}\")\n", + "print(f\"\\nData Types:\\n{df.dtypes}\")\n", + "print(f\"\\nMissing Values:\\n{df.isnull().sum()}\")\n", + "print(f\"\\nFirst 5 rows:\")\n", + "print(df.head())\n", + "\n", + "print(f\"\\n=== BASIC STATISTICS ===\")\n", + "print(df.describe())\n", + "\n", + "print(f\"\\n=== UNIQUE VALUES ===\")\n", + "print(f\"Visitor types: {df['type_of_visitors'].unique()}\")\n", + "print(f\"Number of countries: {df['country_receiving'].nunique()}\")\n", + "print(f\"Years range: {df['year'].min()} to {df['year'].max()}\")\n" ] }, { "cell_type": "code", - "execution_count": 6, - "id": "bf9cb96f", + "execution_count": 20, + "id": "acc8bd2f", "metadata": {}, "outputs": [ { @@ -68,19 +197,118 @@ "dtypes: float64(1), int64(1), object(4)\n", "memory usage: 141.9+ KB\n" ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
type_of_visitorscountry_receivingwhere_tourist_fromyearnumber_of_touristunit
0excursionistsAntigua and BarbudaWorld1995227.0thousand trips
1excursionistsAntigua and BarbudaWorld1996270.0thousand trips
2excursionistsAntigua and BarbudaWorld1997286.0thousand trips
3excursionistsAntigua and BarbudaWorld1998336.0thousand trips
4excursionistsAntigua and BarbudaWorld1999328.0thousand trips
\n", + "
" + ], + "text/plain": [ + " type_of_visitors country_receiving where_tourist_from year \\\n", + "0 excursionists Antigua and Barbuda World 1995 \n", + "1 excursionists Antigua and Barbuda World 1996 \n", + "2 excursionists Antigua and Barbuda World 1997 \n", + "3 excursionists Antigua and Barbuda World 1998 \n", + "4 excursionists Antigua and Barbuda World 1999 \n", + "\n", + " number_of_tourist unit \n", + "0 227.0 thousand trips \n", + "1 270.0 thousand trips \n", + "2 286.0 thousand trips \n", + "3 336.0 thousand trips \n", + "4 328.0 thousand trips " + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "data.info()\n" + "data.info()\n", + "\n", + "data.head()\n" ] }, { "cell_type": "markdown", - "id": "7dfb6d43", + "id": "c7b16997", "metadata": {}, - "source": [ - "Data Analysis and Linear Regression Model\n" - ] + "source": [] }, { "cell_type": "markdown", @@ -114,9 +342,7 @@ "cell_type": "markdown", "id": "2f1c0a05", "metadata": {}, - "source": [ - "2. Data Preparation for Linear Regression" - ] + "source": [] }, { "cell_type": "code", From 88cfab5a57fe6440107b381aa065042324666b51 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 10:21:55 -0500 Subject: [PATCH 05/18] Data Preprocessing --- 4_data_analysis/MLProject.ipynb | 120 ++++++++++++++++++++++++-------- 1 file changed, 90 insertions(+), 30 deletions(-) diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index 4ec64f4..2865e8d 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -315,7 +315,7 @@ "id": "06622b47", "metadata": {}, "source": [ - "1. Understanding the Data Structure\n", + " Understanding the Data Structure\n", "\n", " The dataset contains:\n", "\n", @@ -344,61 +344,121 @@ "metadata": {}, "source": [] }, + { + "cell_type": "markdown", + "id": "fa4fca55", + "metadata": {}, + "source": [ + "3. Data Preprocessing" + ] + }, { "cell_type": "code", - "execution_count": 5, - "id": "6b73334d", + "execution_count": 21, + "id": "207ff3db", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Number of data points: 30\n", - "Years range: 1995 to 2024\n" + "=== HANDLING MISSING VALUES ===\n", + "Missing values before cleaning: 0\n", + "Missing values after cleaning: 0\n", + "\n", + "=== ENCODING CATEGORICAL VARIABLES ===\n", + "Encoded 30 countries\n", + "Encoded visitor types: ['excursionists' 'tourists' 'visitors_total']\n", + "\n", + "=== FEATURE ENGINEERING ===\n", + "New features created: decade, post_2000, post_2010, covid_period\n", + "\n", + "=== DATA DISTRIBUTION ===\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA00AAAIeCAYAAABnbuTBAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAUgtJREFUeJzt3Xd0FGXfxvFr02iBAEmQh9CDhJJCMIiJgSACFqQjKIgoCIIFkGZApEm3PDSlBKSJlEcBRYqAinRQpEvvBIUklBSEtHn/4LCvSxmSJbAh+X7O2XPYmfue+c3ca8yVmbnXYhiGIQAAAADAbTk5ugAAAAAAyM4ITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQCQCRMmTJCfn98D2Ve7du3Url076/utW7fKz89PK1eufCD7j4yMVN26dR/IvuyVlJSkDz74QE8++aT8/Pw0fPhwR5eUIX5+fpowYYKjy3CYdu3a6YUXXnB0GQCQYS6OLgAAHGXRokXq16+f9b2bm5s8PDzk5+eniIgINW/eXO7u7ve8n3PnzmnhwoWqV6+eKleufM/by0rZubaMmDJlihYvXqy33npLpUqVkq+v7y1tJkyYoIkTJ951W48//rjmzJlzP8rMEkuXLlVcXJxee+21DLWvW7euoqOj9corr+jDDz+0Wbd161a9+uqrGjdunJ599tn7UC0A5CyEJgC5Xrdu3VSyZEmlpqYqNjZW27Zt04gRIzRz5kx98cUXqlSpkrVt165d1blz50xt//z585o4caJ8fHwyFUymT5+eqf3Yw6y2jz76SIZh3Pca7sWWLVsUFBSkd955545t6tevr9KlS1vfX7lyRYMHD1b9+vVVv35963IvL6/7Wuu/7d69W87Ozpnq88MPP+jw4cMZDk03LFy4UJ07d9YjjzySqX4AgP9HaAKQ69WuXVsBAQHW92+++aY2b96sLl266K233tLy5cuVN29eSZKLi4tcXO7vj85//vlH+fLlk5ub233dz924uro6dP8ZERcXpwoVKpi2qVSpkk3wvXDhggYPHiw/Pz81adLkfpdolZ6erpSUFOXJk0d58uR5IPt89NFHdfz4cUVFRWnAgAEPZJ/Zxb/PNwDcK55pAoDbCA0N1VtvvaXo6Gh9//331uW3e6Zp48aNevnllxUSEqLg4GA988wz+uyzzyRdvw2qZcuWkqR+/frJz89Pfn5+WrRokaT/f7Zj7969atu2rYKCgqx9b36m6Yb09HR99tlnevLJJ1WtWjV16dJFf/31l02bunXrKjIy8pa+/97m3Wq73TNNV65c0ahRoxQRESF/f38988wzmj59+i1XpPz8/DR06FCtWbNGL7zwgvz9/dWwYUOtW7fO7LRbxcXFqX///goLC1NAQIAaN26sxYsXW9ffeL7rzJkzWrt2rbX2M2fOZGj7t7N582a1adNG1apVU0hIiLp27aqjR4/atLnTc163+1zcOAfff/+9GjZsqICAAK1fv9667t/PNCUmJmr48OGqW7eu/P39FRoaqtdff1379u2TdH3c1q5dq+joaOuxZuR5Mx8fHzVp0kQLFy7UuXPnTNvac2wrVqzQ888/r8DAQLVu3VoHDx6UJM2fP1/169dXQECA2rVrd8dx2bt3r1566SUFBgaqbt26mjdv3i1tkpOTNX78eNWvX1/+/v6KiIjQmDFjlJycfNuabne+ly1bpubNmys4OFjVq1dXo0aNNGvWLNPzAQD/xpUmALiDJk2a6LPPPtOGDRvUqlWr27Y5fPiw3nzzTfn5+albt25yc3PTyZMn9ccff0iSfH191a1bN40fP16tW7fWY489JkmqXr26dRuXLl1Sp06d1LBhQzVu3Fienp6mdU2aNEkWi0WdOnVSXFycZs2apddee03fffed9YpYRmSktn8zDENdu3a1hq3KlStr/fr1GjNmjM6dO6f+/fvbtN++fbtWrVqlNm3aqECBApozZ466deumX375RUWKFLljXVevXlW7du106tQptW3bViVLltTKlSsVGRmp+Ph4tW/fXr6+vhozZoxGjhyp4sWL6/XXX5ckFS1aNMPH/2+bNm1Sp06dVLJkSb3zzju6evWqvvrqK7388statGiRSpYsadd2t2zZohUrVqht27YqUqSIfHx8bttu0KBB+vHHH/XKK6/I19dXly5d0vbt23X06FFVrVpVXbp0UUJCgv7++2/rc3gFChTIUA1du3bVd999l+VXm37//Xf9/PPPatOmjSRp6tSp6tKli9544w19/fXXatOmjS5fvqxp06apf//+mj17tk3/y5cvq3PnznruuefUsGFDrVixQoMHD5arq6s1zKenp6tr167a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UUxMjMLCwqx9ChYsqKCgIO3YsUMNGzbUjh07VKhQIQUEBFjbhIWFycnJSbt371b9+vVv2a+rq7Mslvt/fHfj5uYsi8Uii9P1V0ZZnCyyWK73d3Nzvo8V4n5xcWHcciPGPfdi7HMnxj13YtxzJoeGps6dOysxMVHPPfecnJ2dlZaWpvfee0+NGzeWJMXExEiSPD09bfp5enoqNjZWkhQbG6uiRYvarHdxcZGHh4e1/81SUtKy+lDskpycJsMwZKRff2WUkW7IMK73T07OHseCzGPscifGPfdi7HMnxj13YtxzHoeGphUrVmjp0qX69NNPVaFCBe3fv18jR45UsWLF1KxZM0eWBgAAAACSHByaxowZo86dO6thw4aSJD8/P509e1ZTpkxRs2bN5O3tLUmKi4tTsWLFrP3i4uJUqVIlSZKXl5cuXLhgs93U1FRdvnzZ2h8AAAAA7OXQiSCuXr0qy00PFzk7O1unHC9ZsqS8vb21efNm6/rExETt2rVLwcHBkq4/FxUfH6+9e/da22zZskXp6ekKDAx8AEcBAAAAICdz6JWmp556SpMnT1aJEiWst+fNmDFDLVq0kCRZLBa9+uqrmjRpksqUKaOSJUtq3LhxKlasmOrVqydJ8vX1Va1atfThhx9qyJAhSklJ0UcffaSGDRvqkUceceThAQAAAMgBHBqaBgwYoHHjxmnIkCHWW/Bat26tt99+29qmU6dO+ueffzRw4EDFx8frscce07Rp05QnTx5rm08++UQfffSR2rdvLycnJzVo0EADBgxwxCEBAAAAyGEcGprc3d31wQcf6IMPPrhjG4vFou7du6t79+53bFO4cGF9+umn96NEAAAAALmcQ59pAgAAAIDsjtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABgwsXRBcA+ySnJOnPmdKb7FSxYSF5eXvehIgAAACBnIjQ9hC4kJerEyZOaOPwjueXJk6m+bu4FNHrcFwQnAAAAIIMITQ+hpGtX5WZx0ruP15Fv8RIZ7hd9MU7jtq1VQkI8oQkAAADIIELTQ8zHo6jKexd3dBkAAABAjsZEEAAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABgwuGh6dy5c+rdu7dq1qypwMBANWrUSHv27LGuNwxD48aNU3h4uAIDA/Xaa6/pxIkTNtu4dOmSevXqperVqyskJET9+/dXUlLSAz4SAAAAADmRQ0PT5cuX9fLLL8vV1VVRUVFatmyZ3n//fXl4eFjbREVFac6cORo8eLAWLlyofPnyqWPHjrp27Zq1Te/evXXkyBHNmDFDkydP1u+//66BAwc64pAAAAAA5DAujtx5VFSUihcvrpEjR1qXlSpVyvpvwzA0e/Zsde3aVfXq1ZMkjRkzRmFhYVqzZo0aNmyoo0ePav369frmm28UEBAgSRowYIA6d+6svn376pFHHnmwBwUAAAAgR3Holaaff/5Z/v7+6tatm0JDQ9W0aVMtXLjQuv7MmTOKiYlRWFiYdVnBggUVFBSkHTt2SJJ27NihQoUKWQOTJIWFhcnJyUm7d+9+cAcDAAAAIEdy6JWm06dPa968eXr99dfVpUsX7dmzR8OGDZOrq6uaNWummJgYSZKnp6dNP09PT8XGxkqSYmNjVbRoUZv1Li4u8vDwsPa/maursyyW+3BAmeTm5iyLxSKL0/VXRlmcLJJFdvWzWK7v183N2Z6SkUVcXDj/uRHjnnsx9rkT4547Me45k0NDk2EY8vf3V8+ePSVJVapU0eHDhzV//nw1a9bsvu03JSXtvm07M5KT02QYhoz066+MMtINyZBd/Qzj+n6Tk7PHOcjNGIPciXHPvRj73Ilxz50Y95zHobfneXt7y9fX12ZZ+fLldfbsWet6SYqLi7NpExcXJy8vL0mSl5eXLly4YLM+NTVVly9ftvYHAAAAAHs5NDRVr15dx48ft1l24sQJ+fj4SJJKliwpb29vbd682bo+MTFRu3btUnBwsCQpODhY8fHx2rt3r7XNli1blJ6ersDAwAdwFAAAAAByMoeGpvbt22vXrl2aPHmyTp48qaVLl2rhwoVq06aNJMlisejVV1/VpEmT9NNPP+ngwYPq27evihUrZp1Nz9fXV7Vq1dKHH36o3bt3a/v27froo4/UsGFDZs4DAAAAcM8c+kxTYGCgJk6cqM8++0yff/65SpYsqf79+6tx48bWNp06ddI///yjgQMHKj4+Xo899pimTZumPHnyWNt88skn+uijj9S+fXs5OTmpQYMGGjBggCMOCQAAAEAO49DQJElPPfWUnnrqqTuut1gs6t69u7p3737HNoULF9ann356P8oDAAAAkMs59PY8AAAAAMjuCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYMKu0HT69OmsrgMAAAAAsiW7QlP9+vXVrl07fffdd7p27VpW1wQAAAAA2YZdoWnx4sXy8/PTqFGj9OSTT2rgwIHavXt3VtcGAAAAAA5nV2iqXLmyBgwYoPXr12vEiBE6f/682rRpoxdeeEEzZszQhQsXsrpOAAAAAHCIe5oIwsXFRQ0aNND48ePVu3dvnTx5UqNHj1ZERIT69u2r8+fPZ1WdAAAAAOAQLvfSec+ePfr222+1fPly5cuXTx06dFDLli117tw5TZw4UW+99Za++eabrKoVAAAAAB44u0LTjBkztGjRIh0/fly1a9e2Xl1ycrp+4apUqVIaNWqU6tatm6XFAgAAAMCDZldomjdvnlq0aKFmzZqpWLFit21TtGhRDR8+/J6KAwAAAABHsys0rVq16q5t3Nzc1KxZM3s2DwAAAADZhl0TQXz77bdasWLFLctXrFihxYsX33NRAAAAAJBd2BWapk6dqiJFityy3NPTU5MnT77nogAAAAAgu7ArNJ09e1YlS5a8ZXmJEiX0119/3XNRAAAAAJBd2BWaPD09dfDgwVuWHzhwQIULF77XmgAAAAAg27BrIoiGDRtq+PDhKlCggGrUqCFJ2rZtm0aMGKGGDRtmaYEAAAAA4Eh2habu3bsrOjpar732mlxcrm8iPT1dTZo00XvvvZelBQIAAACAI9kVmtzc3DR27FgdP35cBw4cUN68eVWxYkX5+PhkdX0AAAAA4FB2haYbypUrp3LlymVVLQAAAACQ7dgVmtLS0rRo0SJt2bJFcXFxSk9Pt1k/e/bsLCkOAAAAABzNrtA0fPhwLV68WBEREXr00UdlsViyui4AAAAAyBbsCk3Lli3T2LFjFRERkdX1AAAAAEC2Ytf3NLm6uqp06dJZXQsAAAAAZDt2haYOHTpo9uzZMgwjq+sBAAAAgGzFrtvztm/frq1bt2rdunV69NFHrd/VdMPEiROzpDgAAAAAcDS7QlOhQoVUv379rK4FAAAAALIdu0LTyJEjs7oOAAAAAMiW7HqmSZJSU1O1adMmzZ8/X4mJiZKkc+fOKSkpKcuKAwAAAABHs+tKU3R0tN544w399ddfSk5O1pNPPil3d3dFRUUpOTlZQ4cOzeo6AQAAAMAh7LrSNHz4cPn7+2vbtm3KkyePdXn9+vW1ZcuWLCsOAAAAABzN7tnz5s2bJzc3N5vlPj4+OnfuXJYUBgAAAADZgV1XmtLT05Wenn7L8r///lsFChS456IAAAAAILuwKzQ9+eSTmjVrls2ypKQkTZgwQREREVlSGAAAAABkB3aFpsjISP3xxx96/vnnlZycrN69e6tu3bo6d+6cevfundU1AgAAAIDD2PVMU/HixfXdd99p2bJlOnjwoK5cuaKWLVuqUaNGyps3b1bXCAAAAAAOY1dokiQXFxc1adIkK2sBAAAAgGzHrtC0ZMkS0/VNmza1Z7MAAAAAkO3YFZqGDx9u8z41NVX//POPXF1dlS9fPkITAAAAgBzDrtD022+/3bLsxIkTGjx4sDp27HjPRQEAAABAdmH3M003K1u2rHr16qU+ffpo5cqVWbVZZLHklGSdOXM60/0KFiwkLy+v+1ARAAAAkL1lWWiSrk8Ocf78+azcJLLQhaREnTh5UhOHfyS3PHky1dfNvYBGj/uC4AQAAIBcx67Q9NNPP9m8NwxDMTExmjt3rqpXr54lhSHrJV27KjeLk959vI58i5fIcL/oi3Eat22tEhLiCU0AAADIdewKTW+//bbNe4vFoqJFi+qJJ57Q+++/nyWF4f7x8Siq8t7FHV0GAAAA8FCwKzQdOHAgq+sAAAAAgGzJydEFAAAAAEB2ZteVppEjR2a4bb9+/ezZBQAAAABkC3aFpj///FP79+9XamqqypUrJ+n69zQ5OTmpSpUq1nYWiyVrqgQAAAAAB7ErNNWtW1cFChTQ6NGj5eHhIUm6fPmy+vXrp5CQEHXo0CFLiwQAAAAAR7HrmaYvv/xSvXr1sgYmSfLw8FCPHj305ZdfZllxAAAAAOBodoWmxMREXbhw4ZblFy5cUFJS0j0XBQAAAADZhV2hqX79+urXr59WrVqlv//+W3///bd+/PFHffDBB2rQoEFW1wgAAAAADmPXM01DhgzR6NGj1atXL6WmpkqSnJ2d1bJlS/Xt2zdLCwQAAAAAR7IrNOXLl0+DBw9W3759derUKUlS6dKllT9//iwtDgAAAAAc7Z6+3DYmJkYxMTEqW7as8ufPL8MwsqouAAAAAMgW7LrSdPHiRfXo0UNbt26VxWLRqlWrVKpUKfXv318eHh6KjIzM6joBAAAAwCHsutI0cuRIubi4aO3atcqbN691+fPPP6/169dnWXEAAAAA4Gh2XWnauHGjpk+fruLFi9ssL1u2rM6ePZslhQEAAABAdmDXlaYrV67YXGG64dKlS3Jzc7vnogAAAAAgu7ArNIWEhGjJkiU2y9LT0zVt2jTVrFkzK+oCAAAAgGzBrtvz+vTpo9dee0179+5VSkqKPv74Yx05ckSXL1/WvHnzsrpGAAAAAHAYu0JTxYoV9eOPP+qrr75SgQIFdOXKFdWvX19t27ZVsWLFsrpGAAAAAHCYTIemlJQUvfHGGxoyZIi6du16P2oCAAAAgGwj0880ubq66uDBg/ejFgAAAADIduyaCKJx48b65ptvsroWAAAAAMh27HqmKS0tTfPmzdOmTZvk7++vfPny2azv169flhQHAAAAAI6WqdB0+vRp+fj46NChQ6pSpYok6fjx4zZtLBZL1lUHAAAAAA6WqdDUoEEDbdiwQXPmzJEk9ejRQwMGDJCXl9d9KQ4AAAAAHC1TzzQZhmHzft26dfrnn3+ypJCpU6fKz89Pw4cPty67du2ahgwZopo1ayo4OFjvvvuuYmNjbfqdPXtWnTt3VlBQkEJDQzV69GilpqZmSU0AAAAAYNdEEDfcHKLstXv3bs2fP19+fn42y0eMGKFffvlFY8eO1Zw5c3T+/Hm988471vVpaWl68803lZKSovnz52vUqFFavHixxo8fnyV1AQAAAECmQpPFYsnyZ5aSkpLUp08fDRs2TB4eHtblCQkJ+vbbbxUZGanQ0FD5+/trxIgR2rFjh3bu3ClJ2rBhg44cOaKPP/5YlStXVkREhLp37665c+cqOTk5S+sEAAAAkDtl6pkmwzAUGRkpNzc3SVJycrIGDx58y+x5EydOzPA2hw4dqoiICIWFhWnSpEnW5Xv37lVKSorCwsKsy3x9fVWiRAnt3LlT1apV086dO1WxYkWbZ6rCw8M1ePBgHTlyxDpZxc1cXZ2VHearcHNzvh5Ena6/MsriZJEseqD9LJbr9bq5OWe4H+7MxYXzmBsx7rkXY587Me65E+OeM2UqNDVr1szmfePGje9p58uWLdOff/552+98io2NlaurqwoVKmSz3NPTUzExMdY2N09CceP9jTa3k5KSdk91Z5Xk5DQZhiEj/foro4x0QzL0QPsZxvV6k5Ozx7nLCTiXuRPjnnsx9rkT4547Me45T6ZC08iRI7Nsx3/99ZeGDx+uL7/8Unny5Mmy7QIAAABAVrLry22zwr59+xQXF6fmzZtbl6Wlpem3337T3LlzNX36dKWkpCg+Pt7malNcXJy8vb0lXb+qtHv3bpvt3phd70YbAAAAALgXDgtNTzzxhJYuXWqzrF+/fipfvrw6deqk//znP3J1ddXmzZv1zDPPSJKOHTums2fPqlq1apKkatWqafLkyYqLi5Onp6ckadOmTXJ3d1eFChUe6PEAAAAAyJkcFprc3d1VsWJFm2X58+dX4cKFrctbtGihUaNGycPDQ+7u7ho2bJiCg4OtoSk8PFwVKlRQ37591adPH8XExGjs2LFq27atdbIKAAAAALgXDgtNGdG/f385OTmpW7duSk5OVnh4uAYNGmRd7+zsrMmTJ2vw4MFq3bq18uXLp2bNmqlbt24OrBoAAABATpKtQtOcOXNs3ufJk0eDBg2yCUo38/HxUVRU1P0uDQAAAEAulakvtwUAAACA3IbQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmCE0AAAAAYILQBAAAAAAmHBqapkyZohYtWig4OFihoaF66623dOzYMZs2165d05AhQ1SzZk0FBwfr3XffVWxsrE2bs2fPqnPnzgoKClJoaKhGjx6t1NTUB3koAAAAAHIoh4ambdu2qW3btlq4cKFmzJih1NRUdezYUVeuXLG2GTFihH755ReNHTtWc+bM0fnz5/XOO+9Y16elpenNN99USkqK5s+fr1GjRmnx4sUaP368Iw4JAAAAQA7j0NA0ffp0NW/eXI8++qgqVaqkUaNG6ezZs9q3b58kKSEhQd9++60iIyMVGhoqf39/jRgxQjt27NDOnTslSRs2bNCRI0f08ccfq3LlyoqIiFD37t01d+5cJScnO/DoAAAAAOQE2eqZpoSEBEmSh4eHJGnv3r1KSUlRWFiYtY2vr69KlChhDU07d+5UxYoV5eXlZW0THh6uxMREHTly5MEVDwAAACBHcnF0ATekp6drxIgRql69uipWrChJio2NlaurqwoVKmTT1tPTUzExMdY2/w5Mkqzvb7S5maursyyWrD6CzHNzc5bFYpHF6foroyxOFsmiB9rPYrler5ubc4b74c5cXDiPuRHjnnsx9rkT4547Me45U7YJTUOGDNHhw4f19ddf3/d9paSk3fd9ZERycpoMw5CRfv2VUUa6IRl6oP0M43q9ycnZ49zlBJzL3Ilxz70Y+9yJcc+dGPecJ1vcnjd06FCtXbtWs2bNUvHixa3Lvby8lJKSovj4eJv2cXFx8vb2tra5eTa9G+9vtAEAAAAAezk0NBmGoaFDh2r16tWaNWuWSpUqZbPe399frq6u2rx5s3XZsWPHdPbsWVWrVk2SVK1aNR06dEhxcXHWNps2bZK7u7sqVKjwQI4DAAAAQM7l0NvzhgwZoh9++EFffPGFChQoYH0GqWDBgsqbN68KFiyoFi1aaNSoUfLw8JC7u7uGDRum4OBga2gKDw9XhQoV1LdvX/Xp00cxMTEaO3as2rZtKzc3NwceHQAAAICcwKGhad68eZKkdu3a2SwfOXKkmjdvLknq37+/nJyc1K1bNyUnJys8PFyDBg2ytnV2dtbkyZM1ePBgtW7dWvny5VOzZs3UrVu3B3cgAAAAAHIsh4amgwcP3rVNnjx5NGjQIJugdDMfHx9FRUVlZWkAAAAAICmbTAQBAAAAANlVtplyHNlbckqyzpw5nel+BQsWuuV7tAAAAICHCaEJd3UhKVEnTp7UxOEfyS1Pnkz1dXMvoNHjviA4AQAA4KFFaMJdJV27KjeLk959vI58i5fIcL/oi3Eat22tEhLiCU0AAAB4aBGakGE+HkVV3rv43RsCAAAAOQgTQQAAAACACUITAAAAAJggNAEAAACACUITAAAAAJggNAEAAACACUITAAAAAJggNAEAAACACUITAAAAAJggNAEAAACACUITAAAAAJggNAEAAACACRdHF4CcLTklWWfOnM50v4IFC8nLy+s+VAQAAABkDqEJ982FpESdOHlSE4d/JLc8eTLV1829gEaP+4LgBAAAAIcjNOG+Sbp2VW4WJ737eB35Fi+R4X7RF+M0bttaJSTEE5oAAADgcIQm3Hc+HkVV3ru4o8sAAAAA7MJEEAAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABgwsXRBQC3k5ySrDNnTtvVt2DBQvLy8sriigAAAJBbEZqQ7VxIStSJkyc1cfhHcsuTJ9P93dwLaPS4LwhOAAAAyBKEJmQ7Sdeuys3ipHcfryPf4iUy1Tf6YpzGbVurhIR4QhMAAACyBKEJ2ZaPR1GV9y7u6DIAAACQyzERBAAAAACYIDQBAAAAgAlCEwAAAACYIDQBAAAAgAlCEwAAAACYIDQBAAAAgAlCEwAAAACYIDQBAAAAgAlCEwAAAACYIDQBAAAAgAlCEwAAAACYIDQBAAAAgAlCEwAAAACYcHF0AUBWS05J1pkzpzPdr2DBQvLy8roPFQEAAOBhRmhCjnIhKVEnTp7UxOEfyS1Pnkz1dXMvoNHjviA4AQAAwAahCTlK0rWrcrM46d3H68i3eIkM94u+GKePN67S/v37VLJkqUztkytUAAAAORuhCTmSj0dRlfcunuH2XKECAADAnRCaAN3bFapx29YqISGe0AQAAJBDEZqAf8nsFSoAAADkfIQm4B7ZM1ufm5uz8uQpwNUpAACAhwChCbgH9j4LZbFIrgV4FgoAAOBhQGgC7oHdz0JdvqBxW37hWSgAAICHAKEJyAKZfRbK4mS5j9UAAAAgKzk5ugAAAAAAyM4ITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACZcHF0AkFslpyTrzJnTme5XsGAheXl53YeKAAAAcDuEJsABLiQl6sTJk5o4/CO55cmTqb5u7gU0etwXBCcAAIAHhNAEOEDStatyszjp3cfryLd4iQz3i74Yp3Hb1iohIZ7QBAAA8IAQmgAH8vEoqvLexR1dBgAAAEwQmoBcIjY2VgkJ8ZnuxzNUAAAgtyM0AblAbGys3u/+lpITkzLd13BzVc9+H6pIkSKZ6kfYAgAAOQWhCcgFEhLilZyYpO6P15FPEc8M9/sz+pT6L/lao/u/n+kJKwhbAAAgpyA0AQ8Ze6YqP3PmtFJTU+VTxDNTz1CdvhBr14QVhC0AAJCTEJqAh4i9U5VfufqPYv7+WykpKXbtN7MTVjgibOWGqdjtfS4tJSVFrq6ume5HEAUA4DpCE/AQsXeq8t+PH9aY6MVKS027j9Xd6kGFrdwwFbu9z6UlpyTr5JkzKleqtFxcMvcjPzcEUQAAMoLQBDyE7AkjDxN7pmK357ZF6eG5mmLvc2m/Hz+sMadO6+3HamU6iH68cZX279+nkiVLZarWh+WcAgCQUYQmAA89e29blO7takpmb5dzc3NWcnKaXbfL3ctzaVLmg+i9nFOeSwMA5DSEJgAPPXtvW7yXqykXL17Qf0cNl64lZ7iPxSJdS7bvdrl7fS4ts+w9pzyXBgDIiQhNAHKMB3k15UaIGd28ncp6P5KhPhYni347esiu2+V4Lg0AAMfJMaFp7ty5mj59umJiYlSpUiV9+OGHCgwMdHRZALIxe6+mSP8fYoq7e2Q4VFicLDoVGyOJ59Kykr2zCj7o2wHtrVPi1kUAcLQcEZqWL1+ukSNHasiQIQoKCtKsWbPUsWNHrVy5Up6eGX9gGkDuZM8v+A9biHkY2DOZhz23Sd7wIG8HjI2NsWv2wxty+nNiD0vwBZB75YjQNGPGDLVq1UotWrSQJA0ZMkRr167Vt99+q86dOzu4OgDA3dzrd5Bl5jZJ6d6eZ7NnIo9z587qyqXL6hVWL1OzH0qO+bJoe7/by55+9xJ8H5Yw+aBDIVc1H34Pyx8SctNn7aEPTcnJydq3b5/efPNN6zInJyeFhYVpx44dDqws46IvxmWq/bnLF6/3u3xBeWPy0s+B+7S3398PSZ30y9q+FifLQ3OMD7rfsfN/yc3ipJYVg+STif+J7os+rdnRZ5WWlrlnvS5fSbIrpNn7vVf/XPtH5//6WympqZmqU5KuJF+z69wcizmnCT8vz3TYsvcY7e13I/h2r9tQ/ylSNMP97D0+SVIeV70XmfmwlVlubs46dy5GY0eNkHHtWuY3YEedFy9esH9/du4Ttm7MlGqvexrDBzh+9/pZe9gm/7EYhmE4uoh7ce7cOdWuXVvz589XcHCwdfmYMWP022+/6X//+58DqwMAAADwsHNydAEAAAAAkJ099KGpSJEicnZ2Vlyc7S1ucXFxD83lPgAAAADZ10Mfmtzc3FS1alVt3rzZuiw9PV2bN2+2uV0PAAAAAOzx0E8EIUmvv/663n//ffn7+yswMFCzZs3SP//8o+bNmzu6NAAAAAAPuRwRmp5//nlduHBB48ePV0xMjCpXrqxp06Zxex4AAACAe/bQ3553wyuvvKJffvlFe/fu1f/+9z8FBQU5uqS7mjt3rurWrauAgAC9+OKL2r17t6NLQgZNmTJFLVq0UHBwsEJDQ/XWW2/p2LFjNm2uXbumIUOGqGbNmgoODta7776r2FjbL0Q9e/asOnfurKCgIIWGhmr06NFKvWlK4q1bt6pZs2by9/dX/fr1tWjRovt+fMiYqVOnys/PT8OHD7cuY9xzpnPnzql3796qWbOmAgMD1ahRI+3Zs8e63jAMjRs3TuHh4QoMDNRrr72mEydO2Gzj0qVL6tWrl6pXr66QkBD1799fSUm2X3Z74MABtWnTRgEBAYqIiFBUVNSDODzcRlpamsaOHau6desqMDBQ9erV0+eff65/TzrMuOcMv/32m7p06aLw8HD5+flpzZo1Nusf5DivWLFCzz77rAICAtSoUSP9+uuvWX68sJMBh1i2bJlRtWpV45tvvjEOHz5sDBgwwAgJCTFiY2MdXRoyoEOHDsa3335rHDp0yNi/f7/RqVMno06dOkZSUpK1zcCBA42IiAhj06ZNxp49e4xWrVoZrVu3tq5PTU01XnjhBeO1114z/vzzT2Pt2rVGzZo1jU8//dTa5tSpU0ZQUJAxcuRI48iRI8acOXOMypUrG+vWrXugx4tb7dq1y3jqqaeMRo0aGcOGDbMuZ9xznkuXLhlPPfWUERkZaezatcs4deqUsX79euPkyZPWNlOmTDEee+wxY/Xq1cb+/fuNLl26GHXr1jWuXr1qbdOxY0ejcePGxs6dO43ffvvNqF+/vtGzZ0/r+oSEBCMsLMzo1auXcejQIeOHH34wAgMDjfnz5z/Q48V1kyZNMh5//HHjl19+MU6fPm2sWLHCqFatmjFr1ixrG8Y9Z1i7dq3x2WefGatWrTIqVqxorF692mb9gxrn7du3G5UrVzaioqKMI0eOGP/973+NqlWrGgcPHrz/JwF3RWhykJYtWxpDhgyxvk9LSzPCw8ONKVOmOLAq2CsuLs6oWLGisW3bNsMwDCM+Pt6oWrWqsWLFCmubI0eOGBUrVjR27NhhGMb1H9KVKlUyYmJirG2+/vpro3r16sa1a9cMwzCMMWPGGA0bNrTZV48ePYwOHTrc5yOCmcTERKNBgwbGxo0bjVdeecUamhj3nOnjjz82Xn755TuuT09PN5588klj2rRp1mXx8fGGv7+/8cMPPxiG8f+fg927d1vb/Prrr4afn5/x999/G4ZhGHPnzjVq1Khh/Rzc2PczzzyT1YeEDOjcubPRr18/m2XvvPOO0atXL8MwGPec6ubQ9CDHuXv37kbnzp1t6nnxxReNDz/8MGsPEnbJMbfnPUySk5O1b98+hYWFWZc5OTkpLCxMO3bscGBlsFdCQoIkycPDQ5K0d+9epaSk2Iyxr6+vSpQooZ07d0qSdu7cqYoVK9o8exceHq7ExEQdOXLE2iY0NNRmX+Hh4dZtwDGGDh2qiIgIm/GVGPec6ueff5a/v7+6deum0NBQNW3aVAsXLrSuP3PmjGJiYmzGvWDBggoKCrL+TN+xY4cKFSqkgIAAa5uwsDA5OTlZb83euXOnQkJC5ObmZm0THh6u48eP6/Lly/f7MHGT4OBgbdmyRcePH5d0/daq7du3q3bt2pIY99ziQY4zP/uztxwxEcTD5uLFi0pLS5Onp6fNck9Pz1uei0H2l56erhEjRqh69eqqWLGiJCk2Nlaurq4qVKiQTVtPT0/FxMRY29w8WcmN93drk5iYqKtXrypv3rz35ZhwZ8uWLdOff/6pb7755pZ1jHvOdPr0ac2bN0+vv/66unTpoj179mjYsGFydXVVs2bNrON2u5/pN55ni42NVdGiRW3Wu7i4yMPDw2bcS5YsadPmxucgNjbW+kcZPBidO3dWYmKinnvuOTk7OystLU3vvfeeGjduLEmMey7xIMf5dj/7/70fOBahCbhHQ4YM0eHDh/X11187uhTcZ3/99ZeGDx+uL7/8Unny5HF0OXhADMOQv7+/evbsKUmqUqWKDh8+rPnz56tZs2YOrg73y4oVK7R06VJ9+umnqlChgvbv36+RI0eqWLFijDuQC3F7ngMUKVJEzs7OiouLs1keFxfHNOkPmaFDh2rt2rWaNWuWihcvbl3u5eWllJQUxcfH27SPi4uTt7e3tc3Nfz268f5ubdzd3bna4AD79u1TXFycmjdvripVqqhKlSratm2b5syZoypVqjDuOZS3t7d8fX1tlpUvX15nz561rpdk+jPdy8tLFy5csFmfmpqqy5cvZ+izwf8bHrwxY8aoc+fOatiwofz8/NS0aVO1b99eU6ZMkcS45xYPcpxv14bfDbMPQpMDuLm5qWrVqtq8ebN1WXp6ujZv3qzg4GAHVoaMMgxDQ4cO1erVqzVr1iyVKlXKZr2/v79cXV1txvjYsWM6e/asqlWrJkmqVq2aDh06ZPODeNOmTXJ3d1eFChWsbbZs2WKz7U2bNlm3gQfriSee0NKlS7VkyRLry9/fX40aNbL+m3HPeapXr259ruWGEydOyMfHR5JUsmRJeXt724x7YmKidu3aZf2ZHhwcrPj4eO3du9faZsuWLUpPT1dgYKCk6+P++++/KyUlxdpm06ZNKleuHLdoOcDVq1dlsVhsljk7O1unHGfcc4cHOc787M/mHDwRRa61bNkyw9/f31i0aJFx5MgR48MPPzRCQkJsZtRC9jVo0CDjscceM7Zu3WqcP3/e+vrnn3+sbQYOHGjUqVPH2Lx5s7Fnzx6jdevWt516ukOHDsb+/fuNdevWGU888cRtp54ePXq0ceTIEeOrr75i6uls5t+z5xkG454T7dq1y6hSpYoxadIk48SJE8b3339vBAUFGd999521zZQpU4yQkBBjzZo1xoEDB4yuXbvedkripk2bGrt27TJ+//13o0GDBjZTEsfHxxthYWFGnz59jEOHDhnLli0zgoKCmHraQd5//32jVq1a1inHV61aZdSsWdMYM2aMtQ3jnjMkJiYaf/75p/Hnn38aFStWNGbMmGH8+eefRnR0tGEYD26ct2/fblSpUsWYPn26ceTIEWP8+PFMOZ6NEJocaM6cOUadOnWMqlWrGi1btjR27tzp6JKQQRUrVrzt69tvv7W2uXr1qjF48GCjRo0aRlBQkPH2228b58+ft9nOmTNnjDfeeMMIDAw0atasaYwaNcpISUmxabNlyxajSZMmRtWqVY2nn37aZh9wvJtDE+OeM/3888/GCy+8YPj7+xvPPvussWDBApv16enpxtixY42wsDDD39/faN++vXHs2DGbNhcvXjR69uxpVKtWzahevboRGRlpJCYm2rTZv3+/8fLLLxv+/v5GrVq1+BoKB0pISDCGDRtm1KlTxwgICDCefvpp47PPPrOZMppxzxm2bNly2/+nv//++4ZhPNhxXr58udGgQQOjatWqRsOGDY21a9fevwNHplgM419fbQ0AAAAAsMEzTQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQAAAABggtAEAAAAACYITQCQzZw5c0Z+fn7av3+/o0uxOnr0qFq1aqWAgAA1adLE0eVYRUZG6q233nqg+0xOTlb9+vX1xx9/mLZzRG0PSlZ/RrP6XL333nv68ssvs2x7AEBoAoCbREZGys/PT1OnTrVZvmbNGvn5+TmoKseaMGGC8uXLp5UrV2rmzJm3rPfz8zN9TZgw4b7U9cEHH2jUqFEZaptVv5jPnz9fJUuWVPXq1SVlz5CbHdStW/e2n5Xbycw4ZkTXrl01efJkJSQkZNk2AeRuLo4uAACyozx58igqKkqtW7eWh4eHo8vJEsnJyXJzc7Or76lTp1SnTh35+Pjcdv2GDRus/16+fLnGjx+vlStXWpflz5/frv3eSVpamiwWiwoWLJil270bwzA0d+5cdevW7YHuN6e6X+NYsWJFlSpVSt9//73atm2bpdsGkDtxpQkAbiMsLExeXl6aMmXKHdtMmDDhllvVZs6cqbp161rf37i6MXnyZIWFhSkkJEQTJ05UamqqRo8erccff1y1a9fWt99+e8v2jx07ppdeekkBAQF64YUXtG3bNpv1hw4d0htvvKHg4GCFhYWpT58+unDhgnV9u3btNHToUA0fPlw1a9ZUx44db3sc6enpmjhxomrXri1/f381adJE69ats6738/PTvn379Pnnn9/xqpG3t7f1VbBgQVksFut7T09PzZgx447b37p1q/z8/BQfH29dtn//fvn5+enMmTOSpEWLFikkJEQ//fSTnn/+eQUEBOjs2bO3XD1auXKlGjVqpMDAQNWsWVOvvfaarly5ogkTJmjx4sX66aefrFe/tm7dquTkZA0dOlTh4eEKCAjQU089ZTrme/fu1alTpxQREWFd9vTTT0uSmjZtKj8/P7Vr186mz/Tp0xUeHq6aNWtqyJAhSklJsa67fPmy+vbtqxo1aigoKEhvvPGGTpw4YV2fkc/Y1q1b1bJlS1WrVk0hISF66aWXFB0dLel62O3atavCwsIU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hw/Lx8dHZcqUsXllZqY6d3d3FStW7JbvHPrjjz9UoUKFez6GjGz/xu1tMTEx1vX2XuWyWCx67LHH1K1bNy1ZskSurq5as2aNpOtXOG4XTN3d3fX8889r2LBh+u9//6sff/xRly5duu32K1eurGPHjtmMuaurq6TrM8Flhq+vr1JTU7Vr1y7rsosXL+r48eM25yYjn7EqVarozTff1Pz581WxYkX98MMPkqQdO3aoWbNmql+/vvz8/OTl5WWdJCIzdR48eFDXrl2zLvv3Z/NO7nS+M2r37t0273ft2qUyZcpYr9y5uLgoLCxMffv21ffff6/o6Ght2bLF2v7QoUMqXry49fMFAPeC0AQAd+Hn56dGjRppzpw5Nstr1qypCxcuKCoqSqdOndLcuXO1fv36LNvv119/rdWrV+vo0aMaOnSoLl++rBYtWkiS2rRpo8uXL6tnz57avXu3Tp06pfXr16tfv36Z/uW9Y8eOioqK0vLly3Xs2DF98sknOnDggF599dUsOY67bb906dL6z3/+owkTJujEiRNau3atXV9MumvXLk2ePFl79uzR2bNntWrVKl24cMEaNH18fHTw4EEdO3ZMFy5cUEpKimbMmKEffvhBR48e1fHjx7Vy5Up5e3urUKFCt91HzZo1deXKFR0+fNi6zNPTU3nz5tX69esVGxub4e8GKlu2rJ5++ml9+OGH+v3333XgwAH16dNHjzzyiHVGvrt9xk6fPq1PP/1UO3bsUHR0tDZs2KATJ05Yj7lMmTJavXq19u/frwMHDqhXr16ZDjIvvPCCLBaLBgwYoCNHjujXX3/N0Pj4+Pjot99+07lz52xmdcyos2fPauTIkTp27Jh++OEHffXVV9bPzC+//KLZs2dr//79io6O1pIlS5Senm69LVW6PmnHk08+men9AsDt8EwTAGRAt27dtHz5cptlvr6+GjRokKZMmaJJkyapQYMG6tChgxYuXJgl++zVq5emTp2q/fv3q0yZMpo0aZL1r+aPPPKI5s2bp08++UQdO3ZUcnKySpQooVq1atk8P5URr776qhITEzVq1ChduHBBvr6++uKLL1S2bNksOY67bd/V1VWffvqpBg8erMaNGysgIEA9evRQ9+7dM7Ufd3d3/fbbb5o1a5YSExNVokQJRUZGWqcHb9WqlbZt26YWLVroypUrmj17tgoUKKBp06bp5MmTcnJyUkBAgKZOnXrHc1ikSBHVq1dPS5cuVa9evSRdv+IxYMAAff755xo/frxCQkJuCdh3MnLkSA0fPlxdunRRSkqKQkJCNHXqVOvVq7t9xvLly6djx45p8eLFunTpkooVK6a2bdvqpZdeknR9dr7+/fvrpZdeUpEiRdSpUyclJSVl6rwWKFBAkydP1qBBg9S0aVNVqFBBvXv31rvvvmvar1u3bho4cKDq1aun5ORkHTx4MFP7bdq0qa5evaoXX3xRzs7OevXVV9W6dWtJUsGCBbV69WpNnDhR165dU5kyZfTpp59anxW8du2a1qxZo2nTpmVqnwBwJxbjft20DABADnTgwAF16NBBq1evVoECBRxdTo7Url07VapUSR988IFd/b/++mutWbPGriuWAHA73J4HAEAmVKpUSb1797Z+8S6yH1dXVw0YMMDRZQDIQbg9DwCATGrevLmjS4CJF1980dElAMhhuD0PAAAAAExwex4AAAAAmCA0AQAAAIAJQhMAAAAAmCA0AQAAAIAJQhMAAAAAmCA0AQAAAIAJQhMAAAAAmCA0AQAAAICJ/wPTs0tXEeWbUQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Skewness: 2.99\n" ] } ], "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from sklearn.model_selection import train_test_split\n", - "from sklearn.linear_model import LinearRegression\n", - "from sklearn.metrics import mean_squared_error, r2_score\n", + "# Step 3.1: Handle missing values\n", + "print(\"=== HANDLING MISSING VALUES ===\")\n", + "print(f\"Missing values before cleaning: {df.isnull().sum().sum()}\")\n", "\n", - "# Load the dataset (assuming it's in a CSV file)\n", - "df = pd.read_csv(\"UN_tourism_caribbean_countries_cleaned.csv\")\n", + "# Since this is time series data, we'll fill missing values with forward/backward fill\n", + "df[\"number_of_tourist\"] = (\n", + " df[\"number_of_tourist\"].fillna(method=\"ffill\").fillna(method=\"bfill\")\n", + ")\n", + "\n", + "print(f\"Missing values after cleaning: {df.isnull().sum().sum()}\")\n", + "\n", + "# Step 3.2: Convert categorical variables to numerical\n", + "print(\"\\n=== ENCODING CATEGORICAL VARIABLES ===\")\n", "\n", - "# Let's focus on predicting total visitors for a specific country\n", - "# We'll use \"Dominican Republic\" as an example since it has comprehensive data\n", + "# Create label encoders\n", + "country_encoder = LabelEncoder()\n", + "visitor_type_encoder = LabelEncoder()\n", "\n", - "# Filter for total visitors data for Dominican Republic\n", - "dr_data = df[\n", - " (df[\"country_receiving\"] == \"Dominican Republic\")\n", - " & (df[\"type_of_visitors\"] == \"visitors_total\")\n", - "].copy()\n", + "# Encode categorical variables\n", + "df[\"country_encoded\"] = country_encoder.fit_transform(df[\"country_receiving\"])\n", + "df[\"visitor_type_encoded\"] = visitor_type_encoder.fit_transform(df[\"type_of_visitors\"])\n", "\n", - "# Sort by year\n", - "dr_data = dr_data.sort_values(\"year\")\n", + "print(f\"Encoded {len(country_encoder.classes_)} countries\")\n", + "print(f\"Encoded visitor types: {visitor_type_encoder.classes_}\")\n", "\n", - "# Prepare features (X) and target (y)\n", - "# We'll use year as the feature to predict number of tourists\n", - "X = dr_data[[\"year\"]].values\n", - "y = dr_data[\"number_of_tourist\"].values\n", + "# Step 3.3: Feature Engineering\n", + "print(\"\\n=== FEATURE ENGINEERING ===\")\n", "\n", - "# Check if we have enough data\n", - "print(f\"Number of data points: {len(dr_data)}\")\n", - "print(f\"Years range: {dr_data['year'].min()} to {dr_data['year'].max()}\")\n" + "# Create additional features that might be useful\n", + "df[\"decade\"] = (df[\"year\"] // 10) * 10\n", + "df[\"post_2000\"] = (df[\"year\"] > 2000).astype(int)\n", + "df[\"post_2010\"] = (df[\"year\"] > 2010).astype(int)\n", + "\n", + "# COVID-19 impact indicator (2020-2021)\n", + "df[\"covid_period\"] = ((df[\"year\"] >= 2020) & (df[\"year\"] <= 2021)).astype(int)\n", + "\n", + "print(\"New features created: decade, post_2000, post_2010, covid_period\")\n", + "\n", + "# Step 3.4: Check data distribution\n", + "print(\"\\n=== DATA DISTRIBUTION ===\")\n", + "plt.figure(figsize=(10, 6))\n", + "plt.hist(df[\"number_of_tourist\"], bins=50, edgecolor=\"black\", alpha=0.7)\n", + "plt.title(\"Distribution of Tourist Numbers\")\n", + "plt.xlabel(\"Number of Tourists (thousand trips)\")\n", + "plt.ylabel(\"Frequency\")\n", + "plt.grid(True, alpha=0.3)\n", + "plt.show()\n", + "\n", + "print(f\"Skewness: {df['number_of_tourist'].skew():.2f}\")\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "207ff3db", + "id": "f6d23bc6", "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "markdown", + "id": "b7a28935", + "metadata": {}, + "source": [] } ], "metadata": { From e8e6678ce7d213a9ca4808484df94efa82180395 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 10:24:19 -0500 Subject: [PATCH 06/18] Exploratory Data Analysis (EDA) --- 4_data_analysis/MLProject.ipynb | 116 ++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index 2865e8d..75c2fc2 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -458,6 +458,122 @@ "cell_type": "markdown", "id": "b7a28935", "metadata": {}, + "source": [ + "4. Exploratory Data Analysis (EDA)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "f9b014da", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== CORRELATION ANALYSIS ===\n" + ] + }, + { + "data": { + "image/png": "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RVaIiIiIiKhC44vO32APHhERERERkYpggEdERERERKQiGOARERERERGpCAZ4REREREREKoKLrBARERERUYXGRVbeYA8eERERERGRimCAR0REREREpCI4RJPKBZmA3eofQxx5oqyzUOFEObYr6yxUSI5Rx8s6CxWOTMbPtY8hFfDZ84cSyqRlnYUKyfLlP2WdhYrHzrKsc0DFwE9RIiIiIiIiFcEePCIiIiIiqtAEQvZb5WNNEBERERERqQgGeERERERERCqCAR4REREREZGKYIBHRERERESkIrjIChERERERVWgCIV9Nk489eERERERERCqCPXhERERERFSh8TUJb7AmiIiIiIiIVAQDPCIiIiIiIhXBAI+IiIiIiEhFMMAjIiIiIiJSEVxkhYiIiIiIKjYBX5OQjz14REREREREKoIBHhERERERkYpggEdERERERKQiGOARERERERGpCC6yQkREREREFZpAyEVW8rEHj4iIiIiISEUwwCMiIiIiIlIRDPCIiIiIiIhUBAM8IiIiIiIiFcFFVoiIiIiIqEITCNlvlY81QUREREREpCIY4BEREREREakIBnhEREREREQqgnPwiIiIiIioQuOLzt9gDx4REREREZGKYIBHRERERESkIhjgERERERERqYgSC/ACAgLQpUuXEk9LZSskJAT169f/5POIxWKcPn26BHJERERERERFKbFFVoYOHYr+/ft/VFofHx8kJSVh3bp1JZWdIrVs2RIDBw7E4MGDS/1aVPp27NiBXTt3KmyztLTEpsDAIo9JSUnBtm3bcP6vv5CcnIyqpqYYNXIkGnzxxSfl5cGDB1i3di1u374NAwMDdOrcGT179pTv//XXX7Fq5UqFY0QiEQ4eOvRJ1y0NMpkMO3YE4eSJ40hNTYWTszPGjRsPCwuLdx535PAh7N+/DxKJBDY2thg9ZizEYrF8f1ZWFjYHbsKZM2HIzs5GvXruGDvOG0ZGRgrn+fXXUwg9EIKnT59CW1sbTZo0xdhx3qVS1rJm3KQ+bKcMg0G9WtAyr4q/u49FzKHfyjpbJeLw4cPYv+91e7C1xZgxYxTaw9vOnj2LoO3bERMTA3MLCwwdMkThvpTJZNgRFIQTJ04gNTUVzs7OGOftrdAuk5OTsX7dOoSHh0MoFOLLL7/EqNGjUalSJXmay5cvY0dQEB49egSRSIRatWtjxIgRMDU1LZSnmzdvYsb06ahRowbWrF1bQjVT8krrnj1+/BjC/vwDd+/eQ3p6GoL37IOurm5pF6dEFKe9KPO+dpuVlYXAwECcCXv9OebujnHjxil8jm1Yvx63bt1CdHQ0rK2tC7WdmJgYDFHyPWTlypVwdHL6tIJ/gkOHj2Df/v2QSCSwtbHB2DGj33nPnjl7FtuDdiAmJgYW5uYYOnQIvmjQQL5fJpMhaMcOHD9x8vXvwAnjx41T+B3cuXsXP/+8Bbfv3IFQKESTLxtj5IgRCvfsug0bcOvWLTyMfggrayusW7OmdCqglOw5/Re2Hw9DXGIyHKyqYXr/rqhlZ600bcif4Tj612Xce/ICAOBUwwLjerRXSL/xwCmcDL+KmLgEiNTV4VTDAmN7tEftIs75X8EXnb9RYjWho6NT6EtaSaT9EFlZWSV+Tir/qlevjh07d8p/fli+vMi02dnZmPXdd4iNicF3s2YhcPNmTJwwASaVK39SHtJSUzF71ixUrVoV/gEBGDZsGHbt3Injx44ppNPW1lbI69Zt2z7puqVl3769OHzoIMZ5T8DKVauhpaWFOXNmvfMeOxMWhsDAQPTt2x/+AWtgY2uLOXNmISEhQZ4mcNNGXLoUjpkzZ2HJ0h8QHx8Hv4ULFM5zIGQ/grZvQ8+evbB+w0b4LVqMeu7upVXUMqemo42k61G4MWF+WWelRIWFhSFw0yb07dcPAQEBsLWxwZzZsxXaQ0G3bt3C0iVL4Nm2LQLWrIGHhwcWLFiA6OhoeZp9e/fi0KFD8B4/HqtWv26Xs2crtMtly5bh0aNH8Fu0CPPmzcONGzfg7+8v3//ixQv4zp+Puq6uWLNmDRb6+SEpMRELFyi2QyDvYdCK5cvh6upaUtVSakrrns3MzEQ99/r4plevz1CKklWc9vK24rTbTRs34lJ4OGZ+9x2WLluG+Lg4LFy4sNC52nh64qtmzd6Zx0WLFin8TbB3cPjo8n6qsLAzCAwMRP++fbEmwB+2tjaYNWfOO+/ZJUuXoa2nJ9YG+MPDwwO+CxYq3LN79+3DwUOHMcF7HFavWgktLS3MmjNH/juIi4vDzO9mwdzcHKtXrcTCBb54+PARVqxcVeh6nm088dVXX5VG0UvVqfCrWPnLYYzs0gY753+Lmlbm8F6+GfFJKUrTX468h7aNXLHRZxS2zPGGqbEhxi0PRGx8ojyNtVkVzBjQFcF+U/DTrLGoVtkY434IhKSIc9J/T7EDvODgYDRp0gRSqVRh+5gxYzBz5sxCwy7Dw8PRo0cPuLq6on79+ujduzeePn0KQHGIZkBAAA4cOIDffvsNYrEYYrEY4eHhAICoqCgMHDgQderUQcOGDTFnzhykpqbKr+Hj44OxY8di/fr1aNKkCdq1a/fOMgwYMABPnz7F4sWL5ddKS0tDvXr1cOLECYW0p0+fhqurK1JSUvDkyROIxWIcPXoUvXv3Ru3atdGxY0dcunRJ4Zjbt29j+PDhcHNzQ+PGjTFt2jTEx8cXq36lUik2btyIli1bok6dOujcubNCnsLDwyEWi3HhwgV069YNdevWRe/evXH//n2F8/z+++/o3r07ateujYYNG2LcuHHyfYmJiZg+fToaNGiAunXrYvjw4QofxEDekMzmzZujbt26GDdunNIP9tOnT8PLywu1a9dGq1atsGbNGuTk5Mj3R0dHo1+/fqhduza+/vpr/PXXX8Wqg4+lpqYGY2Nj+Y+BgUGRaU+dOoXk5GTMmTsXLi4uMDU1Re06dWBraytPI5VKERwcjCGDB6Nrly4YN3Yszp09+848/PHHH8jOzsa3kyahevXqaNa8OTp37owDBw4opBMIBAp5LY0HHZ9KJpPhYOgB9OrdBx4eHrCxscWUKdMQHxeHCxfOF3ncgQMhaNeuHdp4esLaujq8vcdDS1MTp06dBACkpqbi1KmTGD5iJOq6usLBwQHfTpqCiIhbiIyMAJDX+xIUtB2Tp0xF8xYtUK2aOWxsbNGokcdnKXtZeHnyDG5/vxoxB1VrCPOBAwfQrn17eHp6wrp6dXiPHw9NTU2cOnVKafqDBw/CvX599OjRA9bW1hg4cCDs7Oxw+PBhAHntMjQ0FL17937dLm0wZepUxMXF4cL5vHb56NEjXP77b0yYOBGOjo5wqVULo8eMwZmwMMTFxQEA7t65A6lUioEDB6KauTns7e3RvXt33L9/X+FzDADWBASgeYsWZdqjUhyldc8CQNeuXvjmm15wdHT8HEUpMcVpL8q8r93mfY6dwogRI+D6+nNs0uTJiLh1C5EREfLzjB4zBp06dYKZmdk786mnr6/wN0FdvezeXhVy4ADatWsHT882qG5tjfHe3tDU1MLJIu7Z0IOHUN/dHT17dIe1tTUGDRwAezs7HDp8BEDe7+BA6EH06d0LHh4esLWxwbQpUxAXF4/zFy4AAMIvXYK6ujrGjR0DK0tLiGvWxHhvb5z76y88e/ZMfq2xo0ejc6eO763P8mjHiTPwatYQnb9qAFsLU3w3uBu0NEQ4eOaS0vR+o/vim1aNIa5uARvzqpgzrCdkUhku3bojT9Peww0NXWrCsqoJ7CzNMLlvJ6SmZ+DO4+efq1hUzhU7wGvXrh0SEhLkwRcAJCQk4OzZs+jcubNC2pycHIwbNw4NGjTAoUOHEBwcjF69ekEgKPx+iqFDh6J9+/Zo2rQpzp07h3PnzsHNzQ1paWkYNmwYDAwMsG/fPqxevRrnz5/Hgreesl64cAEPHjzAli1bsHHjxneWISAgAGZmZpgwYYL8Wtra2ujQoQNCQkIU0u7fvx9t27ZVGIqybNkyDBkyBKGhoXB1dcXo0aMhkUgAAElJSRg0aBCcnZ2xb98+bN68GXFxcfj222+LVb8bN25EaGgo5s+fj6NHj2Lw4MGYNm1aoSBy1apV8PHxwf79+6GmpobvvvtOvu/PP/+Et7c3mjVrhtDQUGzbtg116tSR7/fx8cGNGzewfv16BAcHQyaTYeTIkcjOzgYAXLt2DbNmzUK/fv0QGhqKhg0bYv369QrX//vvvzFjxgwMHDgQx44dg6+vL0JCQrBhwwYAecHR+PHjIRKJsHfvXsyfPx/L39GjVhKePn2K/v36YeiQIVi2dCliY2OLTBt+8SKcnJywbu1a9O3TB2NGj0bw7t3Izc2Vp9kTHIzff/sN3uPHY/2GDejq5YUffvgB/16/XuR5IyIjUat2bYhEIvm2eu7uePLkCZKTk+Xb0tPTMWjQIAwcMAC+8+fj4cOHn1j6kvfixQtIJBK4urrJt+no6EAsdlT4AlNQdnY27t69o3CMUCiEq6ubPHi7e+cOcnJyFNJYWVmhSpWqiHh93qtXrkAqlSIuLg6jRo3AwAH9sXiRH16+fFkaRaVSkp2djbt37ij0fOW1B9ci21BkRATc3uopc3d3l6eXt0u3t9ulGBGRkfJz6OrqombNmvI0bm5uEAgEiHqdxt7BAQKBAL/++ityc3ORmpqK337/Ha6urgpfrk+dOoUXL16gX79+n1QXn0Np3bMVWXHay9uK027v5H+Oub31OVa1apHnfRff+fPRp3dvTJ0yBRcvXvzg40tKdnY27ty9q3APCoVCuLm6FlmuiMhIuLm5Kmxzd68nT5//Oyh4Th0dHTiKxYiIiJRfV11dHcICQ+s0NTUAADdu3iyBkpWt7JwcREY/xRcub3pmhUIhvnBxwL93i/f3PyMzCzm5udDX1S7yGiF/XISuthYcrM1LJN8VlUAoKLOf8qbYAZ6BgQG++uor+dNUADh58iSMjIzQsGFDhbQpKSlITk5GixYtYG1tDTs7O3h5ecHcvHDD09HRgZaWFjQ0NFClShVUqVIFGhoaOHLkCLKysrB06VLUrFkTHh4emDt3Lg4ePIhXr17Jj9fW1sbChQvh4OAAh/cMbTA0NISamhp0dHTk1wKAnj174ty5c/LAIC4uDmfOnEH37t0Vju/Xrx/atm0LOzs7zJs3D3p6eti3bx+AvLlgzs7OmDx5Muzs7ODs7IxFixYhPDwcDx48eGe+srKysHHjRixatAhNmzaFlZUVunXrhs6dOyM4OFgh7aRJk/DFF1/A3t4eI0eOxJUrV5CZmQkA2LBhA77++mtMmDABdnZ2cHR0xKhRowDk9ar9/vvvWLhwIerXrw9HR0csX74cMTEx8sVPtm/fjqZNm2LEiBGwsbHBwIED0aRJE4Xrr1mzBiNHjoSXlxesrKzw5ZdfYuLEidi9ezcA4Pz587h//z6WLl0KR0dHNGjQAJMmTXpn+T+FWCzG5ClTsGDhQozz9kZMTAymTZuGtLQ0pelfvHiBc+fOQSqVYr6vL3r36YOQkBB5/rOzshAcHIxvJ02Cu7s7qlWrhjZt2qBFy5Y4dvx4kfmQxMfDyNBQYVv+v/MfAlhaWmLSpEmYO3cupk2bBqlUiimTJ+NVOQte8vNrZGSosN3Q0FC+721JSUmQSqUwVHZMvER+XnV1UaH5O0ZGb877/MVzyGQy7AnejZEjR+G7WbOQnJKM2bNmyh9EUPmX3x7e7qE2NDJCfBFtSCKRwFBJ+vy28aZdvjvN2z34ampq0NPTk6cxMzODn58ftm3dii6dO6Nnjx6Ie/UKMws8LHv69Cm2btmCqdOmQU1N7UOL/9mV1j1bkRWnvbytOO0273NMvfDnmKEhJMUcsQMAWlpaGD5iBGZ+9x3mz58PFxcXLPD1LbMg72Pag0QigaGhkvRv3bOF7usCaerWrQuJRIK9+/YjOzsbycnJ+HnLVgBAvAq0w4TkVORKpTAxUGwvJga6eJWYXMRRivz3HENlQ300dFb8jnvm6i00GTkLHsO/w66TZ7Fu2kgY6emUWN6pYvugsQCdOnXCnDlzMG/ePGhoaODw4cPo0KGDwpMXIO/m7datG4YNG4Yvv/wSHh4eaN++PapWrVrsa927dw9isRja2m+eWNSrVw9SqRQPHjxA5ddzpmrWrAkNDY0PKUYhderUgb29PUJDQzFy5EgcOnQI5ubmaFBgojCQ9yQ4n7q6OmrVqiUfIhkZGYnw8HCFNPkePXoEGxubIq//8OFDpKenY+jQoQrbs7Oz4fTW0KCCk53zA9S4uDiYm5sjIiJCYVGPgu7duwd1dXXUrVtXvs3IyAg2Nja4d++ePE3r1q0VjnN1dcXZAsMTIyMj8c8//8h77AAgNzcXmZmZSE9Px71792BmZqawWIGyOikpBX9HNjY2EIvFGDxoEM6ePYu2bdsWSi+VyWBoaIjxEyZATU0NDg4OiIuLw/59+9CvXz88e/4cmZmZmFXgyx6Q1ytta2cHABg9apT8YYBLrVqFepWL4uTkpPD7dHJ2xqiRI3Hs+HEMHDjwg8teUv7443esCXgzR2nefN8yy4tMJkNOTg5GjR6DevXy5t3NmOGD/v364vr1a3B3//QVXem/LT4+Hj/6+6NV69Zo3qwZ0tLTsSMoCIv8/OC3aBGkUimWLV2Kfv37w9LSsqyzq1R5umfLiz9+/x0BAQHyf8+fX77ntBoYGKBbt27yf9cUixEXH4/9+/ahUaNGZZizz6tG9eqYOnkyNm0OxJatW6EmFKJzl84wMjKEsBz2inxuW478jlPhV7HJZzQ0NUQK+xo42eOXBZOQkJyKA2Hh8FkbhG3fT4CxfsVYBIlK1wcFeC1btsTs2bPx559/onbt2vj7778xc+ZMpWkXL16MAQMG4OzZszh+/DhWr16NLVu2lPhk9YKrLH2Knj17YufOnRg5ciRCQkLQrVs3pUNKi5KWloYWLVpg6tSphfblB2LvOhbIG6b59ipubwevBYcQ5ecvf16klpZWsfP7sdLS0jB+/Hh4enoW2qepqVnq138fXV1dWFhYKIzdL8jYyAjq6uoKT+WtrKwgkUiQnZ2N9PR0AHlfDt5eeCV/+OV8X1/5kM7834+RsTEkb81XzP93UfPs1NXVYWdnh+dF5PVzadiwEcTiN/NrsrPzJr9LJAkwNjaRb09ISFCYq1iQvr4+hEIhEiQJCtsTEhJgZJxXfiMjI+TkZCMlJUXh6bdEkiCvI2MjYwCAtfWblcAMDAyhr6/PYZoVSH57eLunJEEigXER94ORkRESlKTPbxv5/5VIJDA2NlZIk//wxcjICImJiQrnyM3NRXJysvz4I0eOQEdbG8OGDZOnmTZtGgYOHIioyEhYWlnhzp07uHfvHta/XtlZJpNBJpOhY4cOWOjnV+aLrnyue7YiadioEcSOBeskr8f/Xe3lbcVpt3mfYzmFP8cSEmBU4DofQywW48o//3zSOT7Wx7QHIyOjQvP0ExISCt2zCRIJTAr+Dt5qly1aNEeLFs0hkUigpaUFgUCAAwdCK+R8u7cZ6ulATShEXKLi4idxiSmobKD3zmO3H/sTW4/+gfXTRyodellJUwNWppVhZVoZte2ro+v0pQgNu4ShnVqWaBmoYvqgVTQ1NTXh6emJw4cP48iRI7CxsYGLi0uR6Z2dnTFq1Cjs3r0bNWvWxJEjR5SmE4lEhRZvsbOzQ1RUlMJQu3/++QdCofCdvWHvo+xaANC5c2c8e/YM27dvx927d+Hl5VUozdWrV+X/n5OTg5s3b8o/pFxcXHDnzh1YWFigevXqCj8FeyGVsbOzg4aGBp49e1bo2GrVqhW7bDVr1sSF1xOXlV0jJycH165dk2+TSCR48OAB7O3t5WmuvzXPrGB6IO93+uDBg0L5rF69OoRCIezs7PDixQuFeXAF6620paen4/nz5wp/0AtydnHBs2fPFNrA06dPYWxsDJFIBGtra4hEIsS+fAlzc3OFn/xA3dTUVL4tvyfZydERN/79V2GRhitXrsDS0hJ6eso/xHNzcxEdHf3JXwo+lba2tkI5ra2rw8jICNeuXZWnSUtLRVRUZJGLTYhEItjbO+BqgWOkUimuXr0KR8e8Y+wdHKCuro5rBdrDkyeP8fJlrLxn09nZ+fX2J/I0ycnJSEpK+qARAFS2RCIR7B0cFH7X8vZQRBtydHIq9Flx5coVeXozM7O8dlkgTVpqKqKiouD0+ou9o5MTUlJScOfOm8UIrl29CplMJv/yn5mZWWgpbeHrBz5SmQza2tpYt3491qxdK//5+uuvYWlpiTVr15aLxUY+1z1bkRSuE+v3tpe3FafdOrz+HLuq8Dn2BC9jY4s8b3Hdv3+/yL9dpU0kEsHB3l5peyiqXE6Ojrh6VfE7wj9XrsjT59+zVwt8j0hNS0NkVBScnAqf08jICJUqVULYmTMQiUSoV4qjfz4Xkbo6HGtY4H+37sq3SaVS/O/WXdS2r17kcduO/oHNh37DminD4WxjVaxrSaVSZL+1UNR/DefgvfHByzV16tQJo0aNwp07dwotrpLv8ePH2LNnD1q2bImqVaviwYMHiI6OLvLl5hYWFjh37hzu378PQ0ND6OnpoVOnTvD394ePjw+8vb0RHx+PBQsWoEuXLvIv1R/DwsIC//vf/9ChQweIRCL5h6mBgQHatGmDZcuW4csvv1T65GjXrl2oUaMGbG1tsW3bNiQmJsrn6fXt2xd79uzB5MmTMXz4cBgaGuLhw4c4duwYFi5c+M55HLq6uhg6dCgWL14MmUwGd3d3JCcn459//oGurq7SYFMZb29vDB48GNbW1ujQoQNycnIQFhaGkSNHokaNGmjVqhXmzJmD+fPnQ1dXF8uXL4epqSlatWoFIG+V0T59+uCnn35Cq1atcO7cOYXhmQAwbtw4jB49Gubm5mjbti2EQiEiIyNx+/ZtTJo0CY0bN0aNGjXg4+OD6dOnIyUlBatWFV7uuKRsDgxEw4YNUdXUFHFxcdixYweEQiGav16aevny5TAxMcGQIUMAAB06dMDhQ4ewccMGdHod1O8JDpa3ZW1tbXTr3h2BmzZBJpXCxcUFqWlpuHXzJrS1tdG6TRul+WjeogV27dqF1atXo2fPnoiOjsbB10N+8+3auROOjo6oZm6O1NRU7N+3D7GxsWinZChpWRIIBOjS1Qu7d/8Cc3NzmJmaIShoO4xNTODh0Vie7ruZPvBo3BidOuXVnZdXN6xcuRwODg6oWVOMgwcPICMzA23a5PX26ujowNOzLQIDN0FXTw/a2trYsGEdHJ2c5F8oLSwt0aiRBzZt3ADv8ROhra2NbVu3wNLSEnXq1C2cWRWgpqMNHfs3PZbaNpbQr+uIrPhEZFTgFdG8vLywcsWKvPYgFuNgaCgyMzPR5vU99Pa92aVLF8yYPh0h+/ejwRdfICwsDHfu3MH4CRMA5LXLrl27Yvfu3TC3sICpqSmCgoJgYmICj8Z57dLa2hru9evD/8cf4T1+PHJycrBu/Xp81awZTEzyerYaNGiA0AMHsGvnTjRr3hzp6enYtnUrqlatCjs7OwiFQtSoUUOhLAaGhtDQ0Ci0vbworXsWyBvSKpFI5CMNoqOjUalSJVStWrXIh1flQXHaCwDM9PFB48aN0alzfp28u93mfY55IjAwEHr5n2Pr18PJyUkhmH727BnS09MhkUiQmZkpnwqR/xDx9K+/Ql0kgt3r3sTzf/2FX0+dwoSJEz9XFRXSzcsLy1euhIODA8Q1a+LAwYPIyMyA5+uy/7B8BUxMTDB0yGAAQNcunTFthg/2h4TgiwYN8GfYGdy5cxcTx48HkPc78OraBb/s3i1vl9uDgmBiYozGHm9WRj50+DCcnJxQSasS/rlyBT/9/DOGDB6s0EOqWJ9ZheqzPOvf7it8HxgMJxtL1LK1wq6TZ5GemYXOTfOmmMzd+AuqGBlg/DdfAwC2Hv0DG0JOwm90X1SrbIRXCUkAAG0tTWhraSI9Mws/HfoNzdycUdlQHwnJqdjz23m8TEhC6wZ1iswH/bd8cIDXqFEjGBgY4MGDB+jUqZPSNJUqVcL9+/dx4MABJCQkoGrVqujXrx969+6tNP0333yDS5cuoXv37khLS8P27dvRsGFD/PTTT/Dz80OPHj1QqVIleHp6wsfH50OzrGDChAmYO3cuWrdujaysLERFRcn39ejRA0eOHCm0uEq+KVOmYNOmTYiIiED16tWxfv16eYBoamqKX375BcuXL8ewYcOQlZUFc3NzNG3atNAcRWW+/fZbGBsbY+PGjXjy5An09PTg7OyM0aNHF7tsDRs2xI8//oh169Zh06ZN0NXVVZijtnjxYvj5+WH06NHIzs5G/fr1sWnTJvmHo6urKxYsWICAgAD4++e902bMmDEKL6Bv2rQpNmzYgLVr1yIwMBDq6uqwtbWVz/0TCoVYs2YNZs2ahR49esDCwgKzZ8/G8OHDi12OD/Hq1SssXboUSUlJMDAwgIuLC1atWgWD1xO/X8bGQlhgqG2VKlWw0M8PmzZuxLixY2FiYoIuXbqgR4G5iwMHDoSBgQH27NmDFy9eQEdHB/b29u98D5SOjg4W+vlh3dq1mDB+PPT19dG3b1+0//preZqUlBT86O8PSXw89PT0YG9vjxUrVsC6etFP8cpKjx49kZGRgYAAf6SmpMDZxQULfBcqDBl+/vwZkgoMh/uqWTMkJiViR1BQ3ktybW3h67tQYYjqiJGjIBAIsMhvgfwFwWPHKr7AfMrUqdi0aSPmzZsLoUCAWrVrw3eBX5kuH16aDNxrweO3IPm/nZfnzf98vD0E14cpHwJfETRr1gxJiYkI2rEDkvh42NrZwXfBAnl7ePvedHZ2xvQZM7B92zZs3boVFhYWmDNnjkJQ1aPn63bp74+UlBS4uLjAd8EChXY5ffp0rFu3Dt/NnAmBQIAvv/wSo8eMke93dXXF9OnTsW/fPuzbtw+amppwcnLCgoULy8Uw849VWvfs8WNHsWvXTvm/Z0zPm4bw7aTJCoFgeVSc9vL8+XMkJiXJ//2+dgsAI0eNgkAohN/ChcjOzoa7uzvGFnglEQD8uHo1/v33X/m/x3vnfc5t2bpVPhXjl127EBsbCzU1NVhaWcHHxwdNmjYtlboojmbNvkJiUiKCgnbI28NCX1952WNfvlToqXB2dsaM6dOwbXsQtm7dBnMLC8ydM1vhnu3ZowcyMjLgHxCAlJRUuLg4Y6Gv4u8gKuo2gnbsREZ6OiytrDDe2xutWykOM1z1o79CfY4bn/fgZ+uWn2H21tSW8sazoSskSanYEHIScYnJqGltjoCpw2Hyeojmi/gEhXrd9/sFZOfkYvqaIIXzjOzaBqO8PCEUCBD9PBZHzv2NhJRUGOjqwMXGEpu/Gws7y4o/rJVKhkAmk8nKOhPlRWhoKBYvXoyzZ88qfPg8efIErVq1QmhoaKFFT6hk3HvrfX5UPDJZ+RsWUN5FOb77fZmknGNU0avIknK8Pz+OQMCvJR9KKCs89YTer8rLW2WdhQpHt5Hy0XvlQezMsluwruri7WV2bWVU85H4B0pPT8fLly8RGBiI3r17f/KqnERERERERGVBpQK8v//+GyNGjChy/5UrV5Ru37x5MzZs2ID69esrzJkqKc+ePUOHDh2K3H/06FGl7wgkIiIiIqJiKMaUqP8KlRqimZGRgZiYmCL3Vy+juU45OTl4+vRpkfstLCxUdn5RcXGI5sfhELAPxyGaH4dDND8c78+PwyGaH45DND8Oh2h+uHI9RHPW4DK7dlW/rWV2bWVUKqrQ0tIqsyDuXdTV1ctlvoiIiIiISLWwL5OIiIiIiEhFMMAjIiIiIiJSESo1RJOIiIiIiP57BALOe87HHjwiIiIiIiIVwQCPiIiIiIhIRTDAIyIiIiIiUhEM8IiIiIiIiFQEF1khIiIiIqIKTSBkv1U+1gQREREREZGKYIBHRERERESkIhjgERERERERqQgGeERERERERCqCi6wQEREREVGFJhAKyjoL5QZ78IiIiIiIiFQEe/CIiIiIiKhi42sS5FgTREREREREKoIBHhERERERkYpggEdERERERKQiGOARERERERGpCC6yQkREREREFRpfk/AGe/CIiIiIiIhUBAM8IiIiIiKiUrJz5060bNkStWvXRs+ePXH9+vV3pk9KSsL8+fPRpEkT1KpVC23btkVYWFixr8chmkRERERERKXg2LFjWLx4MebPn4+6deti27ZtGDZsGE6cOAETE5NC6bOysjBkyBCYmJjgxx9/hKmpKZ49ewZ9ff1iX5MBHhERERERUSnYsmULvvnmG3Tv3h0AMH/+fPz555/Yv38/Ro4cWSj9/v37kZiYiN27d0MkEgEALC0tP+iaHKJJREREREQVmkAgLLOfomRlZeHmzZto3LixfJtQKETjxo1x5coVpcf8/vvvcHV1ha+vLxo3boyOHTtiw4YNyM3NLXZdMMAjIiIiIiIqYRKJBLm5uYWGYpqYmODVq1dKj3n8+DFOnjyJ3NxcbNq0CWPHjsWWLVuwfv36Yl+XQzSJiIiIiIjKAZlMBhMTEyxYsABqamqoVasWYmJi8NNPP8Hb27tY52CAR0REREREVMKMjIygpqaGuLg4he1xcXGoXLmy0mOqVKkCdXV1qKmpybfZ2tri5cuXyMrKgoaGxnuvyyGaREREREREJUxDQwMuLi64cOGCfJtUKsWFCxfg5uam9Jh69erh0aNHkEql8m3R0dGoUqVKsYI7gD14VE48autV1lmokKqfCCnrLFQ4jlHHyzoLFVKkuH1ZZ6HCqTOiVllnoUJKeBRf1lmocOx6NC/rLFRIqQ3alXUWKhzdss7AuwgFZZ0DpYYMGYIZM2agVq1aqFOnDrZt24b09HR069YNADB9+nSYmppiypQpAIA+ffpgx44d8PPzQ//+/fHw4UNs3LgRAwYMKPY1GeARERERERGVgq+//hrx8fHw9/fHy5cv4eTkhM2bN8uHaD5//hxC4ZtBldWqVcNPP/2ExYsXo3PnzjA1NcXAgQMxYsSIYl+TAR4REREREVEp6d+/P/r37690X1BQUKFtbm5u2LNnz0dfj3PwiIiIiIiIVAR78IiIiIiIqEITCNlvlY81QUREREREpCIY4BEREREREakIBnhEREREREQqggEeERERERGRiuAiK0REREREVKEJyumLzssCe/CIiIiI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qPw5gvqVqsOq51D5z9Z9RwEA0vZtRvq+LaVUk7KTduI3aJuZwa57L9mDzu/ewb3vx8kXXtGxsVUYgfEiJhqQSmHXvS90rKxRkJaKjHNn8Cx6hbyMVcu2AADPOQsUzvVo/hykHj1U+pUqA00D3SDKzsXPJy4jOSsXfrYWWNz5c1gZGQAAnqVnK/wtfF+Hrj0ApECzQLdPHbLKNapTC6npGVixaQdSUtPg7eGKuZPHwPLlEM3nSckQvrYKY5JIhN6jJst/3rTrADbtOoDgCv5YNF32dy7+zj0M/362vEzUatnD4pt/XgcTh/Uvi2pROSeQSt8Yc0CkAr/5VH53ISrG7eAOVYegdqQf8eWDgHi/91sFj4pU+qaiqkNQS6kPVf/MVHXj1bGBqkNQS1nVS150jpSzqVDz3YVUJPMX1d3cMx44+92FyhDn4BEREREREWkIJnhEREREREQaggkeERERERGRhmCCR0REREREpCG4iiYREREREak1gYD9Vq+wJYiIiIiIiDQEEzwiIiIiIiINwQSPiIiIiIhIQ3AOHhERERERqTehQNURlBvswSMiIiIiItIQTPCIiIiIiIg0BBM8IiIiIiIiDcEEj4iIiIiISENwkRUiIiIiIlJrAiH7rV5hSxAREREREWkIJnhEREREREQaggkeERERERGRhmCCR0REREREpCG4yAoREREREak3gUDVEZQb7MEjIiIiIiLSEOzBIyIiIiIi9cbHJMixJYiIiIiIiDQEEzwiIiIiIiINwQSPiIiIiIhIQzDBIyIiIiIi0hBcZIWIiIiIiNQbH5Mgxx48IiIiIiIiDcEEj4iIiIiISEMwwSMiIiIiItIQTPCIiIiIiIg0BBdZISIiIiIitSYQst/qFbYEERERERGRhmCCR0REREREpCE4RJPKBZ+mvqoOQS3l8ZkvH0wqZZt9jErfVFR1CGrn8vIrqg5BLTVc20vVIaidnKC6qg6BiMoR9uARERERERFpCPbgERERERGRehOw3+oVtgQREREREZGGYIJHRERERESkIZjgERERERERaQgmeERERERERBqCi6wQEREREZF6E/IxSK+wB4+IiIiIiEhDMMEjIiIiIiLSEEzwiIiIiIiINAQTPCIiIiIiIg3BRVaIiIiIiEitCQTst3qFLUFERERERKQh2INHRERERETqjY9JkGMPHhERERER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ERERERGVEY6rlmOCV4re9VyLT8nW1haxsbFvfZ+IiIiIiDQbEzwNoa2tXW4fAE1ERERERGWDc/CIiIiIiIg0BBM8IiIiIiIiDcEhmkREREREpN6E7Ld6hS1BRERERESkIZjgERERERERaQgmeERERERERBqCCR4REREREZGG4CIrRERERESk3gQCVUdQbrAHj4iIiIiISEMwwSMiIiIiItIQTPCIiIiIiIg0BBM8IiIiIiIiDcFFVoiIiIiISL0J2G/1CluCiIiIiIhIQzDBIyIiIiIi0hBM8IiIiIiIiDQE5+AREREREZF6E7Lf6hW2BBERERERkYZggkdERERERKQhOESTiIiIiIjUm0Cg6gjKDfbgERERERERaQgmeERERERERBqCCR4REREREZGGYIJHRERERESkIbjIChERERERqTcB+61eYUsQERERERFpCCZ4REREREREGoIJHhERERERkYZggkdERERERKQhuMgKlQtJN5+pOgS1ZCaVqDoEtSPhJOyPkvowRdUhqJ2Ga3upOgS1dOzrNaoOQe00Ouik6hDUUpadt6pDoE9JIFB1BOUGv+kQERERERFpCCZ4REREREREGoIJHhERERERkYbgHDwiIiIiIlJvQvZbvcKWICIiIiIi0hBM8IiIiIiIiDQEEzwiIiIiIiINwQSPiIiIiIhIQ3CRFSIiIiIiUmtSPuhcjj14REREREREGoIJHhERERERkYZggkdERERERKQhmOARERERERFpCC6yQkRERERE6k3AfqtX2BJEREREREQagj14RERERESk3tiDJ8eWICIiIiIi0hBM8IiIiIiIiDQEEzwiIiIiIiINwQSPiIiIiIhIQ3CRFSIiIiIiUmtSgUDVIZQb7MEjIiIiIiLSEEzwiIiIiIiINAQTPCIiIiIiIg3BBI+IiIiIiEhDcJEVIiIiIiJSbwL2W73CliAiIiIiItIQTPCIiIiIiIg0BBM8IiIiIiIiDcEEj4iIiIiISENwkRUiIiIiIlJvAoGqIyg32INHRERERESkIdiDp6bi4uLQs2dP/PnnnzA1NX2vfZYuXYrDhw/j7t270NfXR5UqVTB69Gh4enrKy+Tl5WHOnDnYv38/8vPzUadOHUyZMgXW1tbyMjNmzMD58+dx8+ZNeHl5YdeuXcXOFR8fj4iICPzzzz+wtLRE9+7d8c033/z7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g/bt2wMAxo4dCzs7O4waNQqAbFjmwoULMXfuXDg5OSExMREAYGhoCCMjo/c6JxM8IiIiIiJSb+94XIGqtGjRAikpKVi4cCESExMREBCAFStWyIdoPn36FEJhUewxMTEQi8UYPny4wnGGDh2KYcOGvdc5meARERERERGVku7du6N79+5K34uOjlb4+dixY//6fOUz1SUiIiIiIqIPxh48Khf8B3ZUdQhqKQElT+wl5YRSiapDUEteHRuoOgS1kxNUV9UhqKVGB51UHYLaOdpspqpDUEuu1/9QdQhqx+rdRagcYA8eERERERGRhmAPHhERERERqTUpRzXJsQePiIiIiIhIQzDBIyIiIiIi0hBM8IiIiIiIiDQEEzwiIiIiIiINwUVWiIiIiIhIrUkF7Ld6hS1BRERERESkIZjgERERERERaQgmeERERERERBqCCR4REREREZGG4CIrRERERESk3rjIihxbgoiIiIiISEMwwSMiIiIiItIQTPCIiIiIiIg0BOfgERERERGRWpMKBKoOodxgDx4REREREZGGYIJHRERERESkIZjgERERERERaQgmeERERERERBqCi6wQEREREZFak/JB53JsCSIiIiIiIg3BBI+IiIiIiEhDcIgmERERERGpNz4HT449eERERERERBqCCR4REREREZGGYIJHRERERESkITgHj4iIiIiI1Bofk1CELUFERERERKQhmOARERERERFpCCZ4REREREREGoIJnors2LED1apVe2uZqKgotG3btowiKt04Hj9+DD8/P1y/fv0TRUVERERERG/iIisq0qJFC9SvX1/VYbyXPn36oHv37qoO45OJOfMP1v5xEUmZ2fC1t0J4m7oIcrFTWvbIlTtY+ft5PEpOg7hQAjdrM/SoE4zWVf3kZZIzsrHg4BmcufUIGbn5qOrugPA2deFmbV5GNfr39uzZg23bt0MkEsHTwwODBg2Cn59fieVPnDiBddHReP78OZwcHdG7Tx/UqF5d/r5UKkX0+vU4ePAgsrKyEBgYiKFDhsDJyQkAcPnyZYwLD1d67AULFsDP1xfPnz9Hr969i70/b948BPj7/8sa/3u79+xVaLPBgwa+tc3+OHEC66LXy9usT5/eStvswMFDL9ssAMNeazMAuHX7NlatWo2bt25BKBSizme10f+bb2BgYCAvs+SXX3Dt2jU8uP8A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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== TIME SERIES ANALYSIS ===\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABAcAAAIzCAYAAAB8/3DDAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAsjlJREFUeJzs3Xl8VPX1//HXvTOTTHb2fUcIKiDuZVEUWVS0LnWpVhGl0lq1akWrlSqKrdaqP7RatahUbIuixQUV+QJuLKKIIFBZBGRHJED2zGRm7v39McmQSQJkyCQzk3k/Hw8embn3zr0n4YqZM+dzjmHbto2IiIiIiIiIJC0z1gGIiIiIiIiISGwpOSAiIiIiIiKS5JQcEBEREREREUlySg6IiIiIiIiIJDklB0RERERERESSnJIDIiIiIiIiIklOyQERERERERGRJKfkgIiIiIiIiEiSU3JAREREREREJMkpOSAiIk3KF198QW5uLl988UWsQ6mzv/3tb+Tm5sY6DK699louuOCCWIcRN2688UYmTpx41K8fNmwY99xzT+h5It6bDWHWrFnk5uayY8eOBjn/FVdcwWOPPdYg5xYRacqcsQ5AREQSX13f2E6fPp3TTz/9sMc8//zzHHPMMQwfPjwaodUqmvHGyp49e5g5cybDhw/n2GOPjXU4dfL+++/zu9/9jgcffJCf//znNfY/8MADvPnmm/z3v/+lT58+MYjwoOXLl7N48WLmzJlT6/5PP/2U8ePH07p1az777DNMM/qft2zbto0XX3yRxYsX8+OPP+JyuejduzfnnXceV155JW63O+rXjNS///1v0tLSuPTSS2MdSsiNN97IXXfdxfXXX0/r1q1jHY6ISMJQckBEROqt+qd077zzDosXL66xvWfPnkc81wsvvMCoUaMaNDkQzXij4aabbmL8+PERvebHH3/kmWeeoWPHjgmTHBg9ejRvvfUWTzzxBMOHD6dVq1ahfatWrWLmzJlcf/31MU8MALz00ksMHDiQrl271rr/3XffpWPHjuzcuZOlS5cyaNCgqF7/k08+4bbbbiMlJYWLLrqI3r174/P5WL58OX/961/ZuHEjkydPjuo1j8aMGTNo3rx5RMmBiy66iNGjR5OSktIgMZ1zzjlkZmbyn//8h9tuu61BriEi0hQpOSAiIvV20UUXhT3/5ptvWLx4cY3t8SJe4i0tLSU9PR2n04nTmRz/S540aRIXXHABjzzyCE888QQAgUCA+++/n/bt23Prrbc2eAy2beP1eg/5yfu+ffv49NNPmTRpUq37S0tL+eijj/jd737HrFmzmD17dlSTA9u3b+eOO+6gQ4cOvPLKK7Rp0ya07xe/+AVbt27lk08+idr1Gkvl/e5wOHA4HA12HdM0GTVqFO+88w6//e1vMQyjwa4lItKUqOeAiIg0itLSUh599FGGDh1K3759GTVqFC+99BK2bYeOyc3NpbS0lLfeeovc3Fxyc3NDa7Z37tzJpEmTGDVqFP379+f000/nt7/9bYOtW65LvDt27CA3N5dZs2bVeH1ubi5/+9vfQs8r+wps3LiRO++8k1NPPZWrr746bF9Vixcv5qqrruKUU07hxBNPZNSoUTz55JNAcO36ZZddBsC9994b+llVxrFlyxZuvfVWBg8eTL9+/TjzzDO54447KCoqqtP3vmbNGn7+85/Tv39/hg0bxowZM0L7SkpKGDBgAA8//HCN1/3www8ce+yxvPDCC4c8d6dOnbjlllt47733WLx4MQCvvvoqa9euZdKkSaSlpVFeXs7TTz/NiBEj6Nu3L0OHDuWxxx6jvLw87Fz//e9/GTNmDAMHDqRv376cf/75/Oc//6lxzWHDhvGrX/2KhQsXcumll9K/f39ee+21Q8b4ySef4Pf7D/mGf968eXg8Hs4991zOP/98/u///g+v13vI80XqxRdfpLS0lD/96U9hiYFKXbt25brrrgs99/v9PPvsswwfPpy+ffsybNgwnnzyyRo/r+r3ZKXqvREqewIsX76cRx55hJ/85CcMGDCAm2++mf3794e97rvvvuPLL78M3YPXXntt2Dm+/PJLJk2axMCBAxk6dGjYvur/7X766adcffXVDBgwgBNPPJHx48fz3XffhR2zd+9e7r33Xs4880z69u3LkCFDuOmmm2qca9CgQezcuZO1a9ce9mctIiIHJcfHFCIiElO2bXPTTTeF3tQee+yxLFy4kMcee4w9e/bwhz/8AQiW+0+cOJH+/ftzxRVXANClSxcAVq9ezYoVKxg9ejTt2rVj586dzJgxgzFjxvD++++TlpbW6PEejdtuu42uXbtyxx13hCUaqvruu+/41a9+RW5uLr/97W9JSUlh69atfP3110BwucNvf/tbnn76aa688kpOPvlkAE466STKy8sZN24c5eXlXHPNNbRq1Yo9e/bwySefUFhYSFZW1mHjKygoYPz48Zx33nmMHj2aOXPmMGnSJFwuF5dddhkZGRkMHz6cOXPmcO+994Z9Avzee+9h2zYXXnjhYa8xduxYZs+ezaRJk3jxxRd56qmnGD16NGeeeSaWZXHTTTexfPlyrrjiCnr27MmGDRt45ZVX2LJlC3//+99D55kxYwa9evVi2LBhOJ1OPv74Yx588EFs2+YXv/hF2DW///577rzzTq688kquuOIKunfvfsj4VqxYQbNmzejYsWOt+2fPns3pp59O69atGT16NE888QQfffQR55133mG/77r6+OOP6dy5MyeddFKdjp84cSJvvfUWo0aN4vrrr2fVqlW88MILbNq0iWefffao43j44YfJzs7mlltuYefOnbzyyis89NBDTJkyBYA//OEPTJ48mfT0dH79618DhC0VAXjwwQdp0aIFN998M6WlpYe81ttvv80999zDkCFDmDBhAmVlZcyYMYOrr76at956i06dOgFw6623snHjRq655ho6duzI/v37Wbx4Mbt37w4dA9C3b18Avv76a4477rij/hmIiCQTJQdERKTBLViwgKVLl3L77bdz0003AcHy6N/+9rdMnz6da665hi5dunDRRRcxadIkOnfuXKPE/6yzzuLcc88N23b22Wdz5ZVXMnfuXC6++OJGj/do9OnTJ1ROfyiLFy/G5/MxdepUWrRoUWN/q1atOPPMM3n66acZMGBA2M9q7dq17Nixg6eeeirs53XLLbfUKb4ff/yRe+65h+uvvx4g9Gb6ySef5KKLLsLlcnHxxRcze/ZsFi9ezJlnnhl67bvvvsupp55Khw4dDnsNp9PJ5MmTQ+d2Op2hhMvs2bNZsmQJr776KqecckroNb169eKBBx7g66+/Dr1p/te//hW2NOCaa65h3LhxTJs2rUZyYOvWrbz44oucccYZR/wZbN68+ZCJgX379vH555+Hlhx06NCBAQMGMHv27KgkB4qLi9mzZw/nnHNOnY5ft24db731FpdffnmomuMXv/gFLVq04OWXX2bp0qX85Cc/OapYmjVrxssvvxwqy7csi1dffZWioiKysrIYPnw4U6ZMoXnz5odckpOTk8M///nPwy4jKCkp4U9/+hOXX355WB+FSy65hHPPPZcXXniByZMnU1hYyIoVK7j77rsZN25c6Lhf/epXNc7Ztm1bXC4XGzduPKrvXUQkGWlZgYiINLjPPvsMh8MRKjmudMMNN2DbNp999tkRz1H1TaDP5+PAgQN06dKF7Oxsvv3227iL91Bq69JfXXZ2NhBMUliWFdH5MzMzAVi0aBFlZWURx+d0OrnyyitDz1NSUrjyyivZt28f//vf/4BgyXabNm2YPXt26LgNGzawfv16fvrTn9bpOv379+fnP/85+fn5/O53vwt94vzhhx/Ss2dPevTowf79+0N/Kt/gVh0DWPWeKCoqYv/+/Zx22mls3769xhKKTp061SkxAJCfn09OTk6t+95//30Mw2DkyJGhbRdccAGfffYZBQUFdTr/4RQXFwOQkZFRp+M//fRTgFAyp9INN9wQtv9oXHHFFWHr9U855RQCgQA7d+6M6BxH6i+wZMkSCgsLGT16dNjfuWmanHDCCaG/c7fbjcvl4ssvv6zTzzonJ4cDBw7UOVYRkWSnygEREWlwO3fupE2bNqE3rpUqpwHU5c2Gx+PhhRdeYNasWezZsyesJL+ua+kbM95DqVr6fCjnn38+b7zxBhMnTuSJJ55g4MCBjBgxgnPPPfeII/M6d+7M9ddfz7Rp05g9ezannHIKw4YN46c//ekRlxQAtGnThvT09LBt3bp1A4Lf94ABAzBNkwsvvJAZM2ZQVlZGWloas2fPJjU1tUZ1x+H069cPOFgCDsFP+Ddt2sTAgQNrfc2+fftCj5cvX87f/vY3Vq5cWSMRUvnpdqW6/NyrOtSSj3fffZf+/fuTn59Pfn4+AMceeyw+n48PP/wwLLFyNCrvuZKSkjodv3PnTkzTrFHJ0rp1a7Kzs+t1r1avAKlMWhUWFtb5HHX5uW/ZsgUgrI9CVZU/k5SUFCZMmMBf/vIXBg8ezAknnMBZZ53FxRdfXOvIQtu21YxQRCQCSg6IiEhCmDx5MrNmzeK6665jwIABZGVlYRjGYdfuN7RDvfEIBAKHfE1qauoRz+t2u/n3v//NF198wSeffMLChQv54IMPeP3113n55ZeP+EnsPffcwyWXXMKCBQtYvHgxDz/8MC+88AIzZ86kXbt2R7x+XVx88cW89NJLzJ8/nwsuuID33nuPs846q04JiMOxLIvevXtz77331rq/Mv5t27YxduxYevTowT333EP79u1xuVx8+umn/POf/6xRcXGoyQS1adasWa1vgLds2cLq1asBwioHKs2ePTsqyYE2bdrUaMR3JPV5E3yo+/VQiahI/nury/1eeb7HHnus1jf5Ve/3sWPHMmzYMObPn8+iRYt46qmn+Mc//sErr7xSo7dAYWEhzZs3r3OsIiLJTskBERFpcB07duTzzz+nuLg47NP4zZs3h/YfSWVfgapd1b1eb9SrBiKJt7L0vPobyV27dtU7BtM0GThwIAMHDuTee+/l+eef5//9v//HF198waBBg474ZrCye/xvfvMbvv76a6666ipmzJjBHXfccdjX/fjjj6GRc5UqP9mt+vfUu3dvjjvuOGbPnk27du3YtWsXEydOPPpvuEKXLl1Yt24dAwcOPOz3+NFHH1FeXs5zzz0X9gl31WUHR6tHjx783//9X43ts2fPxuVy8dhjj9V447x8+XJeffVVdu3adcSeC0dy9tln8/rrr7NixQpOPPHEwx7bsWNHLMti69atocoWgLy8PAoLC8P+znJycmrcq+Xl5ezdu/eoY43GJ/OdO3cGoGXLlnUaCdmlSxduuOEGbrjhBrZs2cLFF1/Myy+/zOOPPx46Zs+ePfh8vrCfiYiIHJ56DoiISIM788wzCQQC/Pvf/w7b/s9//hPDMMKa2qWnp9f6qW1tn5a/+uqrh/2UvqHjzczMpHnz5nz11Vdhx9U2Ti8SleXqVR177LEAofF0ldMZqv+siouL8fv9Ydt69+6NaZo1RtvVxu/38/rrr4eel5eX8/rrr9OiRQuOP/74sGMvuugiFi9ezCuvvEKzZs3C/h6P1nnnnceePXuYOXNmjX0ejyfU8b7yfqi+vOS///1vvWMYMGAABQUFbN++PWz77NmzOfnkkzn//PM599xzw/788pe/BIITG+rrl7/8Jenp6UycOJG8vLwa+7dt28Yrr7wCEBoPWPm80rRp08L2Q/BNePV7debMmfX6bygtLS2iZQa1OeOMM8jMzOSFF17A5/PV2F85PrGsrKzGyMguXbqQkZFR495es2YNwBGTKyIicpAqB0REpMENGzaM008/nf/3//4fO3fuJDc3l8WLF7NgwQKuu+66sPXSxx9/PJ9//jnTpk2jTZs2dOrUKbS2+J133iEzM5NjjjmGlStXsmTJEpo1axbTeC+//HL+8Y9/cN9999G3b1+++uorvv/++3pd/9lnn+Wrr75i6NChdOzYkX379vGf//yHdu3ahcYWVjZjfO2118jIyCA9PZ3+/fuzfv16HnroIc4991y6detGIBDgnXfeweFwMGrUqCNeu02bNkydOpWdO3fSrVs3PvjgA9auXcvkyZNxuVxhx15wwQX89a9/Zd68eVx11VU19h+Niy66iDlz5vDAAw/wxRdfcNJJJxEIBNi8eTMffvghL774Iv369WPw4MG4XC5+/etf8/Of/5ySkhLeeOMNWrZsWa9PwiE4GcPpdLJkyZLQMoFvvvmGrVu31piCUKlt27ahSorx48fX6/pdunTh8ccf54477uD888/noosuonfv3pSXl7NixQo+/PBDLr30UiA4/eKSSy7h9ddfp7CwkFNPPZXVq1fz1ltvMXz48LBJBZdffjkPPPAAt956K4MGDWLdunUsWrSoXqX3xx9/PDNmzODvf/87Xbt2pUWLFofsF3EomZmZTJo0ibvvvptLL72U888/nxYtWrBr1y4+/fRTTjrpJO6//362bNnC2LFjOffccznmmGNwOBzMnz+fvLw8Ro8eHXbOJUuW0KFDB40xFBGJgJIDIiLS4EzT5LnnnuPpp5/mgw8+YNasWXTs2JG777471FW90j333MP999/PlClT8Hg8XHLJJZxwwgncd999mKbJ7Nmz8Xq9nHTSSUybNi30iW2s4r355pvZv38/c+fOZc6cOZx55pm8+OKLEb9BqmrYsGHs3LmT//73vxw4cIDmzZtz2mmnceutt4bW9LtcLh599FGefPJJJk2ahN/v55FHHuHUU09lyJAhfPzxx+zZs4e0tDRyc3OZOnUqAwYMOOK1c3JyePTRR3n44YeZOXMmrVq14v777+eKK66ocWyrVq0YPHgwn3766SFH2UXKNE2effZZ/vnPf/LOO+8wb9480tLS6NSpE9deey3du3cHgqX/Tz/9NFOmTOEvf/kLrVq14qqrrqJFixahsYhHq3JU5Jw5c0LJgcrJDMOGDTvk64YNG8bf/vY31q1bR58+feoVwznnnMO7777LSy+9xIIFC5gxYwYpKSnk5uZyzz33hP19PPzww3Tq1Im33nqL+fPn06pVK371q1/VGF95xRVXsGPHDt58800WLlzIySefzLRp0xg7duxRx3nzzTeza9cuXnzxRUpKSjjttNOO6t6/8MILadOmDf/4xz946aWXKC8vp23btpxyyimhREi7du0YPXo0n3/+Oe+++y4Oh4MePXowZcqUsMSXZVnMnTuXyy67TA0JRUQiYNix6uIkIiIiCe/mm29mw4YNzJs3L9ahRNVXX33Ftddey5w5c0LTGiQxzJ8/nzvvvJN58+bRpk2bWIcjIpIw1HNAREREjsqPP/4Y1aqBeHLKKacwePBgXnzxxViHIhGaOnUqv/jFL5QYEBGJkCoHREREJCLbt2/n66+/5s0332T16tXMmzev1hF0IiIikjhUOSAiIiIRWbZsGXfffTc7duzg0UcfVWJARESkCVDlgIiIiIiIiEiSU+WAiIiIiIiISJJTckBEREREREQkySk5ICIiIiIiIpLknLEOoCnbu7co1iHUicvlwOcLxDoMSXC6jyQadB9JNOg+kvrSPSTRoPtIouFo7qPWrbOO6lqqHBAMI9YRSFOg+0iiQfeRRIPuI6kv3UMSDbqPJBoa8z5SckBEREREREQkySk5ICIiIiIiIpLklBwQERERERERSXJKDoiIiIiIiIgkOSUHRERERERERJKckgMiIiIiIiIiSU7JAREREREREZEkp+SAiIiIiIiISJJTckBEREREREQkySk5ICIiIiIiIpLklBwQERERERERSXJKDoiIiIiIiIgkOSUHRERERERERJKckgMiIiIiIiIiSc4Z6wBEREREREQahGXh2L4bo7gUOzOdQOf2YOrzUZHaKDkgIiIiIiJNjnP9ZlLnLcIsKglts7Iy8I4Ygj+3RwwjE4lPSpuJiIiIiEiT4ly/GfesuRhVEgMARlEJ7llzca7fHKPIROKXKgdERERERKThNXSJvz+AUVyCWVBE6gefAGBUO8QAbCB13mL8vbppiYFIFUoOiIiIiIhIg6p3ib/Ph1FUgllUglFYHPxaVFyxrRijsASztKxOsRiAUVSMY/tuAl07HuV3JNL0KDkgIiIiIiINprLEv7rKEn/PhedgtWuFURh8w1/5xt+s+tzjjXpcRnFp1M8pksiUHBARERERkYZhWaTOWwQcusQ/bfaCel3CNgzsrAzsrAysrEywbVx16ClgZ6bX67oiTY2SAyIiIiIi0iAc23eHLSWornrCoDrbNLGzg2/6gwmATKysDOzsiq9ZmdgZaeG9AywLx9//hVFUUuv5bcDOygz2PBCRECUHRERERESkQRiFRXU6zt+pHYHO7YNv/rMr3vRnZWCn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WqWTJ09q+vTprucdDoemTZsmu92udevWaeHChdqyZYuWLl3qGpOdna1p06apd+/eevXVVzVhwgTNnTtX27dvd4158803lZaWpttvv11btmxR586dNXnyZOXm5nrnlwEAAAAAQC0xGYZh+DqIi5WXl6ekpCStXr1av/vd71RQUKCkpCQtXrxYAwcOlCRlZmZq8ODBWr9+vXr27KkPP/xQt956q7Zv367o6GhJ0tq1a7V48WJlZGTIarVq0aJF+vDDD/XGG2+43uvPf/6zzpw5o2XLlkmSRo0ape7du+v//u//JElOp1N9+/bVzTffrKlTp54z3pycgtr8dXiM1WpRebnD12EgwJFH8ATyCJ5AHqGmyCF4AnkET/gteRQT0+g3vZdfzxz4uYKCypvtiIgISdKePXtkt9vVp08f15j27dvrkksu0ZdffilJ+vLLL9WpUydXYUCSkpOTVVhYqEOHDrnGJCUlub1XcnKy6zXKy8u1d+9et/cxm83q06ePdu3a5fHzBAAAAADAm4J8HcDFcjqdevjhh3XZZZepU6dOkqRTp04pODhY4eHhbmOjoqKUk5PjGlO9MCDJ9fhCYwoLC1VaWqr8/Hw5HA5FRUWd9T4/74FQXXCwRSbTbzhZLwsKsvg6BNQB5BE8gTyCJ5BHqClyCJ5AHsETvJlHAVMcmD9/vr755hutWbPG16FcNLs9cKYRMeUJnkAewRPII3gCeYSaIofgCeQRPMFbeRQQywoeeOAB/fOf/9SKFSsUGxvrOh4dHS273a4zZ864jc/NzVVMTIxrzM93L6h6fKExNptNoaGhioyMlMViOav5YG5u7lkzDgAAAAAACDR+XRwwDEMPPPCAtm3bphUrVqhly5ZuzyckJCg4OFgZGRmuY1lZWTp27Jh69uwpSerZs6cOHjzodmO/Y8cO2Ww2dejQwTXm008/dXvtHTt2uF7DarWqW7dubu/jdDqVkZGhxMRET54yAAAAAABe59fFgfnz5+u1117TY489poYNGyonJ0c5OTkqLS2VJDVq1EgjRozQwoUL9emnn2rPnj267777lJiY6LqxT05OVocOHTRr1izt379f27dv15IlSzRu3DhZrVZJ0pgxY5Sdna1HH31UmZmZSk9P11tvvaWJEye6Yrnlllu0YcMGbdmyRZmZmZo3b55KSko0fPhwb/9aAAAAAADwKL/e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KSoqytfhww+cK4cGDRqke+65R9HR0W5FyqqL9B07dig2NlahoaG+Cht+JjMzU1OnTtWQIUN01113yWKxyG63Kzg4WFOnTlVpaak+/vhjZWVluXoOvPzyy7Lb7WftzIP661x5VL1A0KVLF9fYqqZx27ZtU0REhFtvC9RvBw4c0O23367IyEhlZ2crLi5OkyZN0vXXX6958+bp3nvv5doIv+hcOTRlyhQNGjRITZs2VUxMjOs+zJPXRuxWUE8dPnxYKSkpuvHGG3XZZZfp0KFDevLJJ9W/f39NmDBBl19+udt4h8OhJ554Qps2bdKaNWtclSrUb0VFRZo8ebISEhI0d+5c5eTkaN++fZo5c6a6dOmixx57TFFRUXryySeVkZGhL774Ql27dtXJkyf1/PPPq2vXrr4+BfjYL+VQQkKC5s2b5/Z5c+zYMa1atUqbN2/WypUrFR8f78Po4S+OHz+u2267TSUlJbJarRowYICmT58uSSovL5fVapUkbd68WW+99Za2b9+ujh07qqioSE8++SSfRZD0y3lUvUBQJTMzU+vXr9crr7yilStX0iAVkqQjR47o5ptv1g033KBbb71V3333nVavXi2z2ax58+a5Po+4NsL5/FIOPfjggzKbzW5f0Hry2oiZA/VUenq6kpKSdN9997mOff/993rzzTclSaGhoerWrZsk6dNPP9WaNWv0+eef64UXXqAwAJegoCCVl5frsssukyTFxMQoJiZG69atU0pKiubMmaNnn31W06dPV0pKivbu3auGDRsqLi5OsbGxPo4e/uBCObRw4UItXbpUFotFu3bt0saNG7Vr1y699NJLFAYgqXIW2xtvvKGmTZtqwoQJ+ve//62tW7dKqlwnbrVaXQWC4cOHa/jw4dq9e7fCwsIUERGhmJgYH58B/MGF8ujnMwgOHDigzZs3KyMjg8IAXMrLy7VmzRolJibqrrvuktVqVUJCgnr16qVFixZp5syZruLA9OnTNW7cOP3nP//h2gguF5NDkZGRrvG7d+/WunXrPHZtRHGgnvrhhx8UHR0tSSosLJTNZlOrVq3Uq1cvHTx4UO+++666deumsrIyWa1WxcbGauXKlerQoYOPI4c/cTgcOnXqlLKyslzH7Ha72rZtq5deekkpKSl68sknNX36dEVFRen3v/+9D6OFP7qYHHruueeUmpqqxMRElZeX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AwAAoNY0b95cc+bM0SOPPKLs7GwZhqE5c+boyiuvVNeuXTVlyhQlJiaqT58+mjlzpvLy8lw/+9FHHyklJUW9evVS7969NW3aNB05csT1/NGjRxUfH68333xT48ePV/fu3fX666/74jQBAAh4JsMwDF8HAQAA6rbU1FQVFBSof//+evrpp7V161Zdf/31GjVqlP7nf/5HZWVlWrx4sSoqKrRy5UpJ0jvvvCOTyaT4+HgVFxfr8ccf1/fff69XX31VZrNZR48e1bXXXqu4uDjNnj1bXbp0UUhIiJo2berjswUAIPBQHAAAALUuNzdX119/vfLz8/XEE0/o4MGD+uKLL7Rs2TLXmBMnTqhv3756++231bZt27NeIy8vT0lJSXr99dfVqVMnV3Hgvvvu04QJE7x5OgAA1DlBvg4AAADUfVFRUbrpppv0/vvv67rrrtNrr72mnTt3KjEx8ayxR44cUdu2bXX48GEtXbpUX331lX788UdVfZ9x/PhxderUyTU+ISHBa+cBAEBdRXEAAAB4RVBQkCwWiySpuLhY11xzjf7yl7+cNS4mJkaSdOuttyouLk4LFixQ06ZN5XQ6dcMNN5zVdDAsLKz2gwcAoI6jOAAAALyuW7dueueddxQXF6egoLMvR3788Ud9++23WrBggXr16iVJ+vzzz70dJgAA9Qa7FQAAAK8bO3as8vPzdc8992j37t06cuSItm/frnvvvVcOh0MRERFq3Lix1q9fr++++04ZGRlauHChr8MGAKDOojgAAAC8rlmzZlq7dq2cTqcmT56sG2+8UQ8//LAaNWoks9kss9msv//979q7d69uuOEGpaWladasWb4OGwCAOovdCgAAAAAAqOeYOQAAAAAAQD1HcQAAAAAAgHqO4gAAAAAAAPUcxQEAAAAAAOo5igMAAAAAANRzFAcAAAAAAKjnKA4AAAAAAFDPURwAAAAAAKCeozgAAAAAAEA9R3EAAAAAAIB6juIAAAAAAAD13P8H1YIfZswCyfsAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== VISITOR TYPE ANALYSIS ===\n", + " mean sum count\n", + "type_of_visitors \n", + "excursionists 624.687716 9.045478e+05 1448\n", + "tourists 696.608350 5.593765e+05 803\n", + "visitors_total 1320.909455 1.022384e+06 774\n" + ] + } + ], + "source": [ + "# Step 4.1: Correlation Analysis\n", + "print(\"=== CORRELATION ANALYSIS ===\")\n", + "\n", + "# Select numerical features for correlation\n", + "numerical_features = [\n", + " \"year\",\n", + " \"country_encoded\",\n", + " \"visitor_type_encoded\",\n", + " \"decade\",\n", + " \"post_2000\",\n", + " \"post_2010\",\n", + " \"covid_period\",\n", + "]\n", + "\n", + "# Create correlation matrix\n", + "correlation_matrix = df[numerical_features + [\"number_of_tourist\"]].corr()\n", + "\n", + "plt.figure(figsize=(10, 8))\n", + "sns.heatmap(correlation_matrix, annot=True, cmap=\"coolwarm\", center=0)\n", + "plt.title(\"Correlation Matrix\")\n", + "plt.show()\n", + "\n", + "# Step 4.2: Time Series Analysis\n", + "print(\"\\n=== TIME SERIES ANALYSIS ===\")\n", + "\n", + "# Aggregate data by year\n", + "yearly_tourists = df.groupby(\"year\")[\"number_of_tourist\"].sum().reset_index()\n", + "\n", + "plt.figure(figsize=(12, 6))\n", + "plt.plot(\n", + " yearly_tourists[\"year\"],\n", + " yearly_tourists[\"number_of_tourist\"],\n", + " marker=\"o\",\n", + " linewidth=2,\n", + ")\n", + "plt.title(\"Total Tourists by Year (All Countries)\")\n", + "plt.xlabel(\"Year\")\n", + "plt.ylabel(\"Total Tourists (thousand trips)\")\n", + "plt.grid(True, alpha=0.3)\n", + "plt.xticks(rotation=45)\n", + "plt.show()\n", + "\n", + "# Step 4.3: Visitor Type Analysis\n", + "print(\"\\n=== VISITOR TYPE ANALYSIS ===\")\n", + "visitor_type_stats = df.groupby(\"type_of_visitors\")[\"number_of_tourist\"].agg(\n", + " [\"mean\", \"sum\", \"count\"]\n", + ")\n", + "print(visitor_type_stats)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a269c6f", + "metadata": {}, + "outputs": [], "source": [] } ], From be1d4b690ee3eee0c6a6d9839ca213ca1e7cdf22 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 10:27:29 -0500 Subject: [PATCH 07/18] Preparing Data for Regression --- 4_data_analysis/MLProject.ipynb | 67 ++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index 75c2fc2..2b1fe0c 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -568,10 +568,75 @@ "print(visitor_type_stats)\n" ] }, + { + "cell_type": "markdown", + "id": "3c32b2c6", + "metadata": {}, + "source": [ + "5. Preparing Data for Regression" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "142d44a3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== PREPARING DATA FOR REGRESSION ===\n", + "Features shape: (3025, 7)\n", + "Target shape: (3025,)\n", + "\n", + "Training set size: 2420 samples\n", + "Testing set size: 605 samples\n", + "Features scaled using StandardScaler\n" + ] + } + ], + "source": [ + "# Step 5.1: Select features and target variable\n", + "print(\"=== PREPARING DATA FOR REGRESSION ===\")\n", + "\n", + "# Define features (X) and target (y)\n", + "X = df[\n", + " [\n", + " \"year\",\n", + " \"country_encoded\",\n", + " \"visitor_type_encoded\",\n", + " \"decade\",\n", + " \"post_2000\",\n", + " \"post_2010\",\n", + " \"covid_period\",\n", + " ]\n", + "]\n", + "y = df[\"number_of_tourist\"]\n", + "\n", + "print(f\"Features shape: {X.shape}\")\n", + "print(f\"Target shape: {y.shape}\")\n", + "\n", + "# Step 5.2: Split data into training and testing sets\n", + "X_train, X_test, y_train, y_test = train_test_split(\n", + " X, y, test_size=0.2, random_state=42, shuffle=True\n", + ")\n", + "\n", + "print(f\"\\nTraining set size: {X_train.shape[0]} samples\")\n", + "print(f\"Testing set size: {X_test.shape[0]} samples\")\n", + "\n", + "# Step 5.3: Scale the features (optional, but good practice)\n", + "scaler = StandardScaler()\n", + "X_train_scaled = scaler.fit_transform(X_train)\n", + "X_test_scaled = scaler.transform(X_test)\n", + "\n", + "print(\"Features scaled using StandardScaler\")\n" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "5a269c6f", + "id": "fceebc18", "metadata": {}, "outputs": [], "source": [] From 714463a7dab68ea126a1f5a84567bd65cef15d8b Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 10:30:12 -0500 Subject: [PATCH 08/18] Building the Linear Regression Model --- 4_data_analysis/MLProject.ipynb | 57 ++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index 2b1fe0c..f9a8bf8 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -634,11 +634,60 @@ ] }, { - "cell_type": "code", - "execution_count": null, - "id": "fceebc18", + "cell_type": "markdown", + "id": "ab51a910", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "87025184", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "69f4c652", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "00a83671", + "metadata": {}, + "source": [ + "6. Building the Linear Regression Model" + ] + }, + { + "cell_type": "markdown", + "id": "3231b2b5", + "metadata": {}, + "source": [ + "# Step 6.1: Create and train the model\n", + "print(\"=== BUILDING LINEAR REGRESSION MODEL ===\")\n", + "\n", + "# Initialize the model\n", + "model = LinearRegression()\n", + "\n", + "# Train the model\n", + "model.fit(X_train_scaled, y_train)\n", + "\n", + "print(\"Model training completed!\")\n", + "print(f\"\\nModel Coefficients: {model.coef_}\")\n", + "print(f\"Model Intercept: {model.intercept_:.2f}\")\n", + "\n", + "# Step 6.2: Make predictions\n", + "y_pred_train = model.predict(X_train_scaled)\n", + "y_pred_test = model.predict(X_test_scaled)\n", + "\n", + "print(\"\\nPredictions generated for training and testing sets\")" + ] + }, + { + "cell_type": "markdown", + "id": "59382706", "metadata": {}, - "outputs": [], "source": [] } ], From 928feeabd0d896c49bc2575fd6c234a3c65f4fb8 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 10:34:34 -0500 Subject: [PATCH 09/18] Model Evaluation --- 4_data_analysis/MLProject.ipynb | 134 +++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index f9a8bf8..b064cfa 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -686,7 +686,139 @@ }, { "cell_type": "markdown", - "id": "59382706", + "id": "ab4b0414", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "59a1c65c", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "0a94562b", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "8a0d6569", + "metadata": {}, + "source": [ + "7. Model Evaluation" + ] + }, + { + "cell_type": "markdown", + "id": "57d0ecd7", + "metadata": {}, + "source": [ + "# Step 7.1: Calculate evaluation metrics\n", + "print(\"=== MODEL EVALUATION ===\")\n", + "\n", + "# Training set metrics\n", + "train_mse = mean_squared_error(y_train, y_pred_train)\n", + "train_rmse = np.sqrt(train_mse)\n", + "train_mae = mean_absolute_error(y_train, y_pred_train)\n", + "train_r2 = r2_score(y_train, y_pred_train)\n", + "\n", + "# Testing set metrics\n", + "test_mse = mean_squared_error(y_test, y_pred_test)\n", + "test_rmse = np.sqrt(test_mse)\n", + "test_mae = mean_absolute_error(y_test, y_pred_test)\n", + "test_r2 = r2_score(y_test, y_pred_test)\n", + "\n", + "print(\"\\n=== TRAINING SET METRICS ===\")\n", + "print(f\"Mean Squared Error (MSE): {train_mse:.2f}\")\n", + "print(f\"Root Mean Squared Error (RMSE): {train_rmse:.2f}\")\n", + "print(f\"Mean Absolute Error (MAE): {train_mae:.2f}\")\n", + "print(f\"R-squared Score: {train_r2:.2f}\")\n", + "\n", + "print(\"\\n=== TESTING SET METRICS ===\")\n", + "print(f\"Mean Squared Error (MSE): {test_mse:.2f}\")\n", + "print(f\"Root Mean Squared Error (RMSE): {test_rmse:.2f}\")\n", + "print(f\"Mean Absolute Error (MAE): {test_mae:.2f}\")\n", + "print(f\"R-squared Score: {test_r2:.2f}\")\n", + "\n", + "# Step 7.2: Visualize predictions vs actual values\n", + "print(\"\\n=== VISUALIZING PREDICTIONS ===\")\n", + "\n", + "fig, axes = plt.subplots(1, 2, figsize=(14, 6))\n", + "\n", + "# Training set\n", + "axes[0].scatter(y_train, y_pred_train, alpha=0.5)\n", + "axes[0].plot([y_train.min(), y_train.max()], [y_train.min(), y_train.max()], \n", + " 'r--', lw=2)\n", + "axes[0].set_xlabel('Actual Values')\n", + "axes[0].set_ylabel('Predicted Values')\n", + "axes[0].set_title(f'Training Set (R² = {train_r2:.2f})')\n", + "axes[0].grid(True, alpha=0.3)\n", + "\n", + "# Testing set\n", + "axes[1].scatter(y_test, y_pred_test, alpha=0.5)\n", + "axes[1].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], \n", + " 'r--', lw=2)\n", + "axes[1].set_xlabel('Actual Values')\n", + "axes[1].set_ylabel('Predicted Values')\n", + "axes[1].set_title(f'Testing Set (R² = {test_r2:.2f})')\n", + "axes[1].grid(True, alpha=0.3)\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# Step 7.3: Residual Analysis\n", + "print(\"\\n=== RESIDUAL ANALYSIS ===\")\n", + "\n", + "residuals = y_test - y_pred_test\n", + "\n", + "plt.figure(figsize=(12, 5))\n", + "\n", + "plt.subplot(1, 2, 1)\n", + "plt.scatter(y_pred_test, residuals, alpha=0.5)\n", + "plt.axhline(y=0, color='r', linestyle='--')\n", + "plt.xlabel('Predicted Values')\n", + "plt.ylabel('Residuals')\n", + "plt.title('Residuals vs Predicted Values')\n", + "plt.grid(True, alpha=0.3)\n", + "\n", + "plt.subplot(1, 2, 2)\n", + "plt.hist(residuals, bins=30, edgecolor='black', alpha=0.7)\n", + "plt.xlabel('Residuals')\n", + "plt.ylabel('Frequency')\n", + "plt.title('Distribution of Residuals')\n", + "plt.grid(True, alpha=0.3)\n", + "\n", + "plt.tight_layout()\n", + "plt.show()\n", + "\n", + "# Check residual statistics\n", + "print(f\"Residual mean: {residuals.mean():.2f}\")\n", + "print(f\"Residual std: {residuals.std():.2f}\")" + ] + }, + { + "cell_type": "markdown", + "id": "e8f8472f", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "40f4d419", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "aaac01dc", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "a9e3f8b3", "metadata": {}, "source": [] } From a7b2d5985d99292086b6a5aea44f63c57acdc162 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 10:40:12 -0500 Subject: [PATCH 10/18] Feature Importance Analysis --- 4_data_analysis/MLProject.ipynb | 349 +++++++++++++++++++++++++++++--- 1 file changed, 319 insertions(+), 30 deletions(-) diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index b064cfa..4d791ae 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -660,9 +660,26 @@ ] }, { - "cell_type": "markdown", - "id": "3231b2b5", + "cell_type": "code", + "execution_count": 26, + "id": "68a2a49b", "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== BUILDING LINEAR REGRESSION MODEL ===\n", + "Model training completed!\n", + "\n", + "Model Coefficients: [ 267.66304278 -40.46069619 283.31148248 -24.50942546 -14.0767014\n", + " -4.19786876 -206.83550342]\n", + "Model Intercept: 819.83\n", + "\n", + "Predictions generated for training and testing sets\n" + ] + } + ], "source": [ "# Step 6.1: Create and train the model\n", "print(\"=== BUILDING LINEAR REGRESSION MODEL ===\")\n", @@ -681,7 +698,7 @@ "y_pred_train = model.predict(X_train_scaled)\n", "y_pred_test = model.predict(X_test_scaled)\n", "\n", - "print(\"\\nPredictions generated for training and testing sets\")" + "print(\"\\nPredictions generated for training and testing sets\")\n" ] }, { @@ -690,12 +707,6 @@ "metadata": {}, "source": [] }, - { - "cell_type": "markdown", - "id": "59a1c65c", - "metadata": {}, - "source": [] - }, { "cell_type": "markdown", "id": "0a94562b", @@ -711,9 +722,69 @@ ] }, { - "cell_type": "markdown", - "id": "57d0ecd7", + "cell_type": "code", + "execution_count": 27, + "id": "d52e32c5", "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== MODEL EVALUATION ===\n", + "\n", + "=== TRAINING SET METRICS ===\n", + "Mean Squared Error (MSE): 1179779.33\n", + "Root Mean Squared Error (RMSE): 1086.18\n", + "Mean Absolute Error (MAE): 709.85\n", + "R-squared Score: 0.11\n", + "\n", + "=== TESTING SET METRICS ===\n", + "Mean Squared Error (MSE): 1159019.68\n", + "Root Mean Squared Error (RMSE): 1076.58\n", + "Mean Absolute Error (MAE): 700.87\n", + "R-squared Score: 0.07\n", + "\n", + "=== VISUALIZING PREDICTIONS ===\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABW0AAAJOCAYAAADMCCWlAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjcsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvTLEjVAAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XmcTfUfx/H3XWbfMURSkqWyNyUaSUk0qahsISLti2hDyFpSISKyNJKlohWVrfSzhKxFyVKhGMPMnX3u3Ht+f8jNhNxhZs69M6/n49Hj0f3ec+55X5+uvvOZ7/0ei2EYhgAAAAAAAAAAPsFqdgAAAAAAAAAAwD9o2gIAAAAAAACAD6FpCwAAAAAAAAA+hKYtAAAAAAAAAPgQmrYAAAAAAAAA4ENo2gIAAAAAAACAD6FpCwAAAAAAAAA+hKYtAAAAAAAAAPgQmrYAAAAAAAAA4ENo2gIokZ5//nndeOON53Tum2++qZo1axZyIv+ydetW1a5dWwcOHDA7Solw7Ngx1a9fX998843ZUQAAQAnFHJY5bGH79ttv1aBBAx09etTsKECpZDc7AIDSxduJZGJioho1alTEaXzT8uXLNX36dO3evVuZmZkqV66cateurbvuukvXX399gV9v8uTJuuyyy9SiRQuvz3njjTeUkJCgCy+80DPWtWtXff/9957HQUFBuvjii3XXXXepW7duslr/+T3gM888o/379ysvL09BQUF66aWXVK1atQJnPx/Lli3ThAkT9Ouvv6ps2bJq166dHnnkEdntZ/9f36RJk7RlyxZt3bpVycnJeuyxx/T444+fctyePXs0d+5cbd26VT/++KNyc3O1bNkyVa5cOd9xMTExuvvuuzVu3Dg1a9as0N4jAAAwX3HOb7OysvTOO+/ommuu8bm5MnPYwnE+c1i3261p06Zpzpw5SkpK0iWXXKIHH3xQt912W77j/uu/2SZNmmjGjBmSpOuvv15VqlTR22+/rRdeeOH83hiAArMYhmGYHQJA6fHJJ5+c8vh///ufRo8enW/8uuuuU7ly5c75Ok6nU4ZhKDAwsMDn5uXlyeVyKSgo6Jyvf66mTZum0aNH65prrtFNN92k4OBg/fbbb1qzZo1q1aqll19+ucCv2aBBA91yyy1en7tjxw7deeedmjt3rho0aOAZ79q1q37//Xc9/fTTko6vHv3888+1bds2PfTQQ+rTp4/n2L1796pq1aqSpBEjRmjnzp2aNWtWgbOfq2+++UYPPvigrrnmGt1222365ZdfNHv2bLVv314vvfTSWc+vWbOmYmNjVbNmTX333XdnbNouWLBAAwYM0GWXXSabzaYdO3actmkrSbt379att96qmTNnqnHjxoXyPgEAgPmKa34rSUePHlXjxo1POzdhDssc9rXXXtOUKVPUvn171alTR8uWLdPKlSv1+uuvKyEhwXPcv/+blaTt27crMTFRzzzzjHr16uUZf//99zV69Gh99913Cg8PL5w3CsArrLQFUKzuuOOOfI+3bNmi//3vf6eM/1tWVpZCQkK8vk5AQMA55ZMku93u1W+yC1teXp7eeustXXfddZo+ffopzycnJxdLjo8++kiVKlVS/fr1T3kuIiIiX606deqk1q1ba9asWXriiSdks9kkyTPZlSTDMPKtYCgOo0ePVs2aNTV9+nRPLcPCwvT222+rW7duZ10xcaLxeuIHozO58cYbtX79eoWHh2vatGnasWPHGY+tVq2aatSooYULF9K0BQCgBDnX+W1hYw5buuewhw4d0owZM3Tvvfdq0KBBkqR77rlHXbp00ejRo9WqVSvP+zzdf5vff/+9LBbLKatyb7nlFg0fPlxLlizR3XffXVhvFYAX2NMWgM/p2rWrbrvtNm3fvl333nuv6tWrp9dff12StHTpUvXu3Vvx8fGqXbu2WrRooYkTJ8rlcuV7jX/vabt//37VrFlT06ZN07x589SiRQvP17W2bt2a79zT7QdWs2ZNDR06VEuXLtVtt92m2rVrKyEhQd9+++0p+detW6d27dqpTp06atGihebOnevVHmPHjh1Tenq6GjZseNrny5Ytm+9xbm6uxo8fr5tvvlm1a9dWs2bNNHr0aOXm5ubLnZmZqYULF6pmzZqqWbOmnn/++f/MsWzZMl177bWyWCz/eZx0/OtltWvXVkZGxmkn5GvWrNGHH36ovn37nvW1Csuvv/6qX3/9Ve3bt8/3g0vnzp1lGIa+/PLLs77G6VbKnk50dHSBVhw0adJEK1asEF9yAQCgdHG73Zo5c6YSEhJUp04dNWnSRIMGDVJqamq+47Zt26aePXuqUaNGqlu3rm688UbP19L379/v+cXvhAkTPHO7N998UxJz2NI+h126dKmcTqc6d+7sGbNYLOrUqZP++usvbdq06Yzn5ubm6quvvtLVV1+tCy64IN9zZcuWVc2aNbVs2bJzfGcAzhUrbQH4pJSUFD3wwANKSEjQ7bff7pnsLVy4UKGhoerRo4dCQ0O1du1ajR8/Xunp6XruuefO+rqff/65MjIy1KFDB1ksFr3zzjt6/PHHtXTp0rOuzt24caO++uorde7cWWFhYZ7fzK9YsUIxMTGSpJ9++km9evVSbGysHn/8cbndbk2cOFFlypQ5a7ayZcsqODhYy5cvV5cuXRQdHX3GY91utx5++GFt3LhR7du3V7Vq1fTLL7/o3Xff1b59+/TWW29JOv7b+oEDB6pu3bpq3769JKlKlSpnfN1Dhw7p4MGDuuKKK86a94QDBw7IYrEoMjIy3/jWrVv11FNPaeTIkapbt+5ZX8fbGxyEh4f/57YXP/30kySpTp06+cYrVKigCy644D9Xwxa1K6+8UjNnztSuXbtUo0YN03IAAIDiNWjQIC1cuFDt2rVT165dtX//fs2ePVs//fST5syZo4CAACUnJ6tnz56KiYlR7969FRkZqf379+vrr7+WJJUpU0ZDhgzRkCFDdPPNN+vmm2+WdPY9dZnDnl5Jm8Pu2LFDoaGhp6zGPfEeduzYobi4uNOe+80338jhcOj2228/7fNXXnmlli5d+p/XB1D4aNoC8ElJSUl66aWX1LFjx3zjr732moKDgz2PO3XqpEGDBmnOnDnq06fPWfewPXjwoL766itFRUVJOv4VqEceeUTfffedmjdv/p/n7t69W4sWLfJMGBs1aqQ77rhDX3zxhbp06SJJGj9+vGw2m+bMmaMKFSpIklq3bq1bb731rO/ZarWqZ8+emjhxopo3b664uDhdddVVatq0qa688sp8x3722WdavXq1Zs2alW/yVb16dQ0ePFg//PCDGjZsqDvuuENDhgzRRRdd5NVX9Pbs2SPpzCtNXS6XZ2KakpKiDz/8UNu3b9cNN9yQry5bt27VI488ohEjRnh98whvtwwYNWqU2rVrd8bnk5KSJEmxsbGnPBcbG6vDhw97dZ2icNFFF0k6vpKCpi0AAKXDhg0b9MEHH2jMmDFq06aNZ7xRo0bq1auXlixZojZt2mjTpk1KTU3VtGnT8jXuTuy5GhoaqltuuUVDhgxRzZo1vd5+gTls6ZjDJiUlqWzZsqesND7xev91/meffabAwEDdcsstp33+oosu0rFjx5ScnHzKymkARYemLQCfFBgYeNpJzcmTqvT0dOXm5iouLk7z5s3Tnj17VKtWrf983VtvvdXTsJXkmSz+8ccfZ83UpEmTfL/hr1WrlsLDwz3nulwurVmzRi1atPBMdiXp4osvVtOmTbVixYqzXuOJJ57QpZdeqvfff1/fffedvv32W73xxhu64oorNGbMGM9vzpcsWaJq1arp0ksvzffb/WuvvVbS8a+3nekrav/l2LFjknTKioMT9uzZc8rE9MYbb9SIESPyjT366KOSpHfffVfvvvuuwsPDNWnSpP+89om71J7NZZdd9p/PZ2dnS9JpG/hBQUFKT0/36jpF4cSf64k/ZwAAUPItWbJEERERuu666/LN26688kqFhoZq3bp1atOmjSIiIiRJK1euVK1atc7rHg0nYw5bOuaw2dnZZzz35Nf/t/T0dK1cuVLNmjU745/fyXNYmrZA8aFpC8AnVahQ4bSTjl27dmns2LFau3btKROXtLS0s75uxYoV8z0+0cB1OBwFPvfE+SfOTU5OVnZ2ti6++OJTjjvd2Jncdtttuu2225Senq4tW7ZowYIF+vzzz/XQQw/p888/V1BQkH777Tft3r37jL/ZP98bPpxpz9ULL7xQw4cPl9vt1u+//67Jkyfr2LFjp9yleNWqVQW+ZpMmTc4p67+daOyfvC/aCTk5Ofka/2bxZq81AABQMvz2229KS0s767ztmmuu0S233KIJEyZo5syZuuaaa9SiRQu1adPmrN8m+y/MYUvHHDY4OPiM5578+v/25ZdfKicnJ98q8H878efKHBYoXjRtAfik000qHA6HunTpovDwcD3xxBOqUqWKgoKC9OOPP2rMmDFyu91nfd0Td0z9N29uDHU+556L8PBwXXfddbruuusUEBCghQsXasuWLbrmmmvkdrtVo0YNz40p/u3fNxDw1ol9zc7UxA4NDc03MW3YsKHatWunN954QwMHDjyna55w4ithZxMREfGfk9YTXwFLSko65YeUpKQkr/YmKyonbjZy4s8ZAACUfG63W2XLltWYMWNO+/yJfWMtFovGjx+vzZs3a8WKFVq1apX69++vGTNmaN68eQoLCzun6zOHLR1z2NjYWK1bt06GYeRrrp7IV758+dOe99lnnykiIuI/t4o78efKHBYoXjRtAfiN77//XikpKZowYYKu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hElbIiIiIiIiIiIiojLCpC0RERERERERERFRGWHSloiIiIiIiIiIiKiMMGlLREREREREREREVEaYtCUiIiIiIiIiIiIqI0zaEhEREREREREREZURJm2JiIiIiIiIiIiIygiTtkRERERERERERERlRImIjPdJEBEREREREREREZHFSlsiIiIiIiIiIiKiMsKkLREREREREREREVEZYdKWiIiIiIiIiIiIqIwwaUtERERERERERERURpi0LXP33HMPzj//fCxduhSf/vSn8eqrr473KU14d911F6644gqsXLkSa9euxX/8j/8RO3fuLNkmn8/jtttuw5o1a7By5Ur81V/9FZqbm0u22b9/PzZt2oTly5dj7dq1+Md//Ef4vl+yzXPPPYfLL78cS5YswUc/+lHcf//9o359E9l3v/tdLFiwAHfccUfxNd6LsXPo0CF8+ctfxpo1a7Bs2TJccskl2LZtW/F9EcE//dM/Yf369Vi2bBmuu+467Nq1q2Qf7e3t+NKXvoRVq1Zh9erV+Lu/+ztkMpmSbd5880386Z/+KZYuXYpzzjkH3/ve98bi8iaMIAjw7W9/G+effz6WLVuGCy64AP/8z/+MvnNDeS9GzwsvvIDPf/7zWL9+PRYsWIDf/OY3Je+P5Wf/2GOP4eMf/ziWLl2KSy65BL/73e9G/HqJxgpj2rHFeHfiYjxc/hgzTwyMqcsbY+4hEipbjzzyiCxevFg2b94s77zzjvzX//pfZfXq1dLc3DzepzahXX/99XLffffJ22+/Ldu3b5e//Mu/lHPPPVcymUxxm1tvvVXOOecceeaZZ2Tbtm3ymc98Rq688sri+77vy8UXXyzXXXedvPHGG/LUU0/JmjVr5Jvf/GZxm927d8vy5cvlzjvvlB07dsiPf/xjWbhwofz+978f0+udKF555RU577zz5JJLLpHbb7+9+Drvxdhob2+X8847T77yla/IK6+8Irt375Y//OEP8v777xe3ueuuu+S0006TJ554QrZv3y6f//zn5fzzz5dcLlfc5oYbbpBLL71Utm7dKi+88IJ89KMflZtvvrn4fldXl6xbt06+9KUvydtvvy0PP/ywLFu2TO69994xvd5y9i//8i9yxhlnyG9/+1vZs2ePPPbYY7JixQq5++67i9vwXoyep556Sr71rW/Jr3/9a5k/f7488cQTJe+P1Wf/0ksvycKFC+V73/ue7NixQ/73//7fsnjxYnnrrbdG/0MgGmGMacce492JifFw+WPMPHEwpi5vjLmHhknbMrZx40a57bbbin8PgkDWr18vd9111zie1QdPS0uLzJ8/X55//nkREens7JTFixfLY489Vtxmx44dMn/+fHn55ZdFxP6COfXUU6Wpqam4zU9/+lNZtWqV5PN5ERH5xje+IZ/85CdLjvWf/tN/kuuvv36Ur2jiSafTcuGFF8q///u/y9VXX10MUnkvxs7//J//U6666qpB3zfGyFlnnSX/9m//Vnyts7NTlixZIg8//LCI9N6bV199tbjN7373O1mwYIEcPHhQRETuueceOf3004v3pufYH/vYx0b6kiasTZs2yX/5L/+l5LWbbrpJvvSlL4kI78VYOjKAHMvP/q//+q9l06ZNJefz6U9/Wr761a+O7EUSjQHGtOOP8W75Yzw8MTBmnjgYU08cjLkHx/YIZapQKOD111/HunXriq9prbFu3Tq8/PLL43hmHzxdXV0AgOrqagDAa6+9Bs/zSj77efPmYdq0adi6dSsAYOvWrZg/fz4aGhqK26xfvx7pdBo7duwobrN27dqSY61fv764D+r19a9/Heecc07JZw7wXoylJ598EkuWLMEXv/hFrF27Fpdddhl+8YtfFN/fu3cvmpqaSu5FZWUlli9fXvyd9PLLL6OqqgpLly4tbrNu3TporYvLYLdu3YrVq1cjGo0Wt1m/fj3ee+89dHR0jPZlTggrV67Es88+i/feew+AXdLz0ksv4U/+5E8A8F6Mp7H87Pl7iz4oGNOWB8a75Y/x8MTAmHniYEw9cTHm7hUZ7xOggbW1tSEIAtTX15e8Xl9f368fFR0/Ywz+4R/+AatWrcL8+fMBAM3NzXBdF1VVVSXb1tfXo6mpqbhN36AIQPHvx9omnU4jl8shHo+PyjVNNI888gjeeOMNbN68ud97vBdjZ8+ePfjZz36Gz33uc/j85z+Pbdu24fbbb4frurj88suLn+VAv5N6eqo1Nzejrq6u5P1IJILq6uqSezFjxoySbXruTXNzc/HL5IfZpk2bkE6n8YlPfAKO4yAIAvzN3/wNLr30UgDgvRhHY/nZD/R7q+9xiCYKxrTjj/Fu+WM8PHEwZp44GFNPXIy5ezFpSx9qt912G9555x389Kc/He9T+VA6cOAA7rjjDvzf//t/EYvFxvt0PtREBEuWLMHNN98MAFi0aBHeeecd3Hvvvbj88svH+ew+XB577DH86le/wje/+U2cfPLJ2L59O+68805MmjSJ94KIiIaN8W55Yzw8sTBmnjgYU9MHAdsjlKna2lo4joOWlpaS11taWvo9BaDj8/Wvfx1PPfUU7r77bkyZMqX4ekNDAzzPQ2dnZ8n2LS0taGxsLG5z5JOXnr8fa5uKigo+yQ69/vrraGlpwac+9SksWrQIixYtwvPPP48f//jHWLRoEe/FGGpsbMS8efNKXps7dy72799ffB/AUX8nNTQ0oLW1teR93/fR0dExpPvF323WN77xDWzatAmf/OQnsWDBAlx22WW49tprcddddwHgvRhPY/nZD7QNYwCaiBjTji/Gu+WP8fDEwph54mBMPXEx5u7FpG2ZikajWLx4MbZs2VJ8zRiDLVu2YOXKleN4ZhOfiODrX/86nnjiCdx9992YOXNmyftLliyB67oln/3OnTuxf/9+rFixAgCwYsUKvP322yW/RJ555hlUVFTg5JNPLm7z7LPPluz7mWeeKe6DgDPPPBO/+tWv8MADDxT/LFmyBJdccknxn3kvxsaqVauK/Z567Nq1C9OnTwcAzJgxA42NjSX3Ip1O45VXXin+Tlq5ciU6Ozvx2muvFbd59tlnYYzBsmXLANh78eKLL8LzvOI2zzzzDE466SQuHQrlcjkopUpecxwHIgKA92I8jeVnz99b9EHBmHZ8MN6dOBgPTyyMmScOxtQTF2PuPsZzChod3SOPPCJLliyR+++/X3bs2CFf/epXZfXq1SVTQWn4vva1r8lpp50mzz33nBw+fLj4J5vNFre59dZb5dxzz5UtW7bItm3b5Morr5Qrr7yy+L7v+3LxxRfL9ddfL9u3b5ff//73cuaZZ8o3v/nN4ja7d++W5cuXyz/+4z/Kjh075Cc/+YksXLhQfv/734/p9U40faflivBejJVXXnlFFi1aJP/yL/8iu3btkoceekiWL18uDz74YHGbu+66S1avXi2/+c1v5M0335Qbb7xRzj//fMnlcsVtbrjhBrnsssvklVdekRdffFEuvPBCufnmm4vvd3Z2yrp16+Q//+f/LG+//bY88sgjsnz5crn33nvH9HrL2S233CJnn322/Pa3v5U9e/bIr3/9a1mzZo184xvfKG7DezF60um0vPHGG/LGG2/I/Pnz5Qc/+IG88cYbsm/fPhEZu8/+pZdekkWLFsn3v/992bFjh3znO9+RxYsXy1tvvTV2HwbRCGFMO/YY705sjIfLF2PmiYMxdXljzD00TNqWuR//+Mdy7rnnyuLFi2Xjxo2ydevW8T6lCW/+/PkD/rnvvvuK2+RyOflv/+2/yemnny7Lly+XL3zhC3L48OGS/ezdu1f+4i/+QpYtWyZr1qyR//E//od4nleyzbPPPisbNmyQxYsXy0c+8pGSY9DAjgxSeS/GzpNPPikXX3yxLFmyRD7+8Y/Lz3/+85L3jTHy7W9/W9atWydLliyRa6+9Vnbu3FmyTVtbm9x8882yYsUKWbVqlXzlK1+RdDpdss327dvlqquukiVLlsjZZ58td91116hf20TS1dUlt99+u5x77rmydOlS+chHPiLf+ta3JJ/PF7fhvRg9zz777ID/jbjllltEZGw/+0cffVQuvPBCWbx4sXzyk5+Up556avQunGiUMaYdW4x3JzbGw+WNMfPEwJi6vDHmHholEtaGExEREREREREREdG4Y09bIiIiIiIiIiIiojLCpC0RERERERERERFRGWHSloiIiIiIiIiIiKiMMGlLREREREREREREVEaYtCUiIiIiIiIiIiIqI0zaEhEREREREREREZURJm2JiIiIiIiIiIiIygiTtkRERERERERERERlhElbIqIPsAULFuA3v/nNqB7jmmuuwR133DGqxyAiIiKiDy/GtET0YcSkLRHRCHj55ZexcOFCbNq0adg/e/755+OHP/zhyJ/UMXz+85/HDTfcMOB7L774IhYsWIA333xzjM+KiIiIiMYLY1oiovLBpC0R0QjYvHkzrr76arzwwgs4dOjQeJ/OkGzcuBHPPPMMDh482O+9++67D0uWLMGpp546DmdGREREROOBMS0RUflg0paI6ARlMhk8+uijuOqqq3Duuefil7/8Zb9tnnzySVxxxRVYunQp1qxZgy984QsA7DKsffv24c4778SCBQuwYMECAMD/+T//Bxs2bCjZxw9/+EOcf/75xb+/+uqr+NznPoc1a9bgtNNOw9VXX43XX399yOd97rnnoq6uDvfff3+/63n88cexceNGtLW14eabb8bZZ5+N5cuX45JLLsHDDz981P0OtHxt9erVJcc5cOAA/vqv/xqrV6/GGWecgRtvvBF79+4tvv/cc89h48aNWLFiBVavXo3Pfvaz2Ldv35CvjYiIiIiGhzFtKca0RDTemLQlIjpBjz32GObOnYu5c+fi0ksvxX333QcRKb7/1FNP4aabbsI555yDBx54AHfffTeWLVsGwAayU6ZMwRe/+EU8/fTTePrpp4d83Ewmg8suuww//elP8Ytf/AKzZ8/Gpk2bkE6nh/TzkUgEGzZswC9/+cuS83388cdhjMHFF1+MQqGAxYsX47vf/S4efvhhfOYzn8Hf/u3f4tVXXx3yeR7J8zzccMMNSKVSuOeee/Czn/0MyWQSf/EXf4FCoQDf9/GFL3wBp59+Oh566CH8/Oc/x5VXXgml1HEfk4iIiIiOjjHt8DCmJaLRFhnvEyAimug2b96MSy+9FABw9tlno6urC88//zzWrFkDAPjXf/1XXHTRRfjiF79Y/JmeJVo1NTVwHAepVAqNjY3DOu7atWtL/v7f//t/x+rVq/HCCy/gvPPOG9I+rrjiCnz/+98vOd/7778fF154ISorK1FZWVnSI+yaa67B008/jccee6wYpA/Xo48+CmMM7rjjjmLQeuedd+L000/H888/jyVLlqCrqwvnnXceZs2aBQCYN2/ecR2LiIiIiIaGMe3wMKYlotHGpC0R0QnYuXMntm3bhn/+538GYJ/0X3TRRdi8eXMxYNy+fTs+/elPj/ixm5ub8e1vfxvPP/88WlpaYIxBNpvF/v37h7yPefPmYeXKlbjvvvuwZs0avP/++3jxxRfxox/9CAAQBAH+9V//FY8//jgOHToEz/NQKBQQj8eP+7zffPNN7N69G6tWrSp5PZ/PY/fu3Vi/fj0+9alP4YYbbsBZZ52FtWvX4hOf+AQmTZp03MckIiIiosExph0+xrRENNqYtCUiOgGbN2+G7/s4++yzi6+JCKLRKG699VZUVlYeVzColCpZ3gUAvu+X/P2WW25Be3s7/v7v/x7Tpk1DNBrFlVdeCc/zhnWsjRs34vbbb8ett96K+++/H7NmzcIZZ5wBAPj+97+PH/3oR/i7v/s7LFiwAIlEAv/wD/9w1GMc69y7u7uxePFi/K//9b/6/WxdXR0AW6VwzTXX4A9/+AMee+wxfPvb38YPfvADrFixYljXRkRERETHxph2+OfOmJaIRht72hIRHSff9/Hggw/iK1/5Ch544IHinwcffBCTJk0qDjeYP38+tmzZMuh+XNeFMabktbq6OjQ3N5cEitu3by/Z5o9//COuueYanHPOOTjllFMQjUbR1tY27Ov4xCc+AaUUHn74YTzwwAO44ooriku8/vjHP+IjH/kINmzYgFNPPRUzZ87Erl27jrq/uro6HD58uPj3Xbt2IZvNFv++ePFivP/++6ivr8fs2bNL/lRWVha3W7RoEf7Df/gPuPfeezF//vxjDosgIiIiouFjTDswxrRENN6YtCUiOk5PPfUUOjo6sHHjRsyfP7/kz4UXXojNmzcDAG666SY88sgj+M53voN3330Xb731Fr773e8W9zN9+nS88MILOHToEFpbWwEAa9asQWtrK773ve9h9+7duOeee/CHP/yh5Phz5szBQw89hHfffRevvPIKvvzlLx9XBUQqlcJFF12Eb33rW2hqasLll19efG/27Nl45pln8Mc//hHvvvsubr31VjQ3Nx91f2eeeSbuuecevPHGG9i2bRu+9rWvwXXd4vuXXHIJamtrceONN+LFF1/Enj178Nxzz+H222/HwYMHsWfPHnzzm9/Eyy+/jH379uHpp5/Grl27MHfu3GFfGxEREREdHWPagTGmJaLxxqQtEdFx2rx5M9atW1fyJL3Hxz72Mbz22mt48803sWbNGvzTP/0TnnzySWzYsAHXXnsttm3bVtz2i1/8Ivbt24cLLrigOIhh3rx5+NrXvoaf/vSn2LBhA1599VVcf/31Jce444470NHRgcsvvxx/+7d/i2uuuQb19fXHdS0bN25ER0cH1q9fj8mTJxdfv/HGG7Fo0SLccMMNuOaaa9DQ0IALLrjgqPu65ZZbMHXqVPzZn/0ZvvzlL+P6668vCbwTiQR+8pOfYNq0abjppptw0UUX4e///u+Rz+dRUVGBRCKBnTt34q/+6q/wsY99DLfeeiv+7M/+DJ/97GeP69qIiIiIaHCMaQfGmJaIxpuSI5u0EBEREREREREREdG4YaUtERERERERERERURlh0paIiIiIiIiIiIiojDBpS0RERERERERERFRGmLQlIiIiIiIiIiIiKiNM2hIRERERERERERGVESZtiYiIiIiIiIiIiMoIk7ZEREREREREREREZYRJWyIiIiIiIiIiIqIywqQtERERERERERERURlh0paIiIiIiIiIiIiojDBpS0RERERERERERFRGmLQlIiIiIiIiIiIiKiP/P51otAamIczKAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== RESIDUAL ANALYSIS ===\n" + ] + }, + { + "data": { + "image/png": "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VivGjh0Lq9WKu+66C7GxsUhLS8P333+P3NxchIWF1XpMRPVKEFG9+vDDD0VSUpLYtm2byMjIEGfOnBGff/65uPzyy0WnTp3EmTNnHMeOGTNGXH/99aK4uNhxm6ZpYsSIEWLQoEGO24YOHSrGjRtX5fMuXrxYJCUlOX4+cOCASEpKEjNnznQ67vHHHxdJSUli8eLFjtuefvpp0b9//2rPKYQQhYWF5Y675557xFVXXeV021133SXuuusux88PPPCAGDJkSJWvoSLr1q0TSUlJ4tChQ063Dx48WIwePdrxsytzVJHt27eLpKQk8cEHH4iMjAyRlpYmvv/+e9G/f3+RnJws9u7dK4S4MBePP/640+NPnjwpOnbsKJYuXep0+6FDh8TFF1/suN1qtYrevXuLG2+80en9fu+990RSUpLTXJ04cUIkJSWJDz/80HHbnXfeKbp16yZOnTrl9Dyapjn++/XXXxdJSUnixIkT9T7Gyrz99tsiKSlJbNmyxXGbqqqib9++YsSIEY7bKvocPfPMM6JLly5Oz132s1nyfm3fvt3psRXNmTv/fRERkW8p+b5V8nu6Ipdeeqm46aabHD+X/V6zatUqkZSUJDIyMio9x969e8v9filx1113iaSkJLF27doK7yv9e7Pk91ffvn2FxWJx3L5p0yaRlJQk3nrrLcdt/fv3F08//XS156xqbGV/f3711VciKSlJvPrqq07HPfLIIyI5OVkcO3bMcVtSUpK45JJLnG4r+V65Zs2acs9V2ptvvimSkpLExx9/7LjNarWKESNGiK5duzq99v79+7v8+7d///7innvuERkZGSIjI0McOnRIPPXUUyIpKUnMmjXLcdyOHTtEUlKS+OSTT5we/8MPPzjdXjIfVX1+hBDlvjM/+OCDonPnzk7fx/7880/RsWNHp89WRd9LKjtnRd+Jdu/eLZKSksTGjRsdt5X9DrR//36RlJQkNm/eXOVrIPI1XL5H5CF33303evfujX79+mH8+PEICgrC0qVLER8fD0D/y9r27dtx3XXXIS8vD5mZmcjMzERWVhZSU1Nx9OhRx2594eHhOHz4MI4ePery8//3v/8FgHKl42PGjKnT6yr9VzyLxYLMzEz07NkTJ06cqLJUODw8HGfPnsXevXtr9HxXX301DAYDNm3a5Ljtjz/+wJ9//unUm6s2c1TalClT0Lt3b/Tt2xfjxo1DYWEh5s+f79SnAgBuv/12p5+/+uoraJqG6667zvEeZmZmIjY2Fq1atXIsM9u3bx8yMjJw++23O/WZGDZsWLV/ycrMzMSOHTtw8803o3nz5k73SZJU7WvzxBhLDB48GEaj0WkJ3y+//IK0tDSnirjSn6OSz3+PHj1QWFiIv//+26Xnqkp9//siIiLfFxwcjPz8/ErvL6kS/uabb6BpWq2ew2QyYfjw4S4ff9NNNyE0NNTx87XXXou4uDjH97b68sMPP0BRlHLfC++55x4IIcotzevTpw9atmzp+LlDhw4IDQ2tdmfiH374AXFxcbj++usdtxmNRowaNQoFBQXYsWNHrV/Djz/+iN69e6N379644YYbHG0JJk6c6Djm888/R1hYGK644gqn7zyXXHIJgoODHd95Sr7XfP/997DZbC49v6qq+PHHHzFw4ECn72Pt2rVDampqrV9X6e9ENpsNWVlZaNmyJcLDw7F///5KH1fyOfrxxx9RWFhY6+cn8jQu3yPykOnTp6NNmzawWCz48MMPsWPHDqcL/ePHj0MIgUWLFmHRokUVniMjIwNNmzbF+PHj8eCDD+Kaa65BUlISUlNTceONNzrtKFPWqVOnIMuy0xcKAGjbtm2dXteuXbvw8ssvY8+ePeV+AVoslkrDi3vvvRfbtm3DrbfeilatWuGKK67A9ddf7yg3r0x0dDQuv/xybN68GY8++igAfemewWBwLL8CUKs5Ku2hhx5Cjx49IMsyoqKi0K5duwqboSYmJjr9fPToUQghMGjQoArPW3KO06dPAwBatWrldL/RaESLFi2qHFvJF8CkpCSXXktZnhhjiaioKKSmpuKrr77CrFmzEBAQgP/85z8wGAy47rrrHMcdPnwYL730ErZv315u22539EGo739fRETk+woKCqpcVjV48GC8//77mDZtGl544QX07t0bV199Na699lrIsmt/y2/atGmNmpqX/R0rSRJatWrlaPNQX06dOoUmTZo4BWKAHqiU3F9aRUv2IyIikJubW+3ztGrVqtz8lTxPyXeN2ujSpQseffRRqKqKw4cPY+nSpcjNzXVq6n7s2DFYLBb07t27wnNkZGQAAHr27IlrrrkGS5YswZtvvomePXti4MCBuOGGGyp9PzMzM1FUVFTuPQSANm3a1DpYLCoqwvLly7FhwwakpaU59fiq6jtRixYt8M9//hOrVq3Cp59+ih49emDAgAEYOnQol+6RT2MoReQhKSkpjiqbgQMH4o477sATTzyBzz//HCEhIY6/yN1zzz3o27dvhecoCZQuu+wyfPXVV/jmm2+wdetWfPDBB3jrrbcwa9Ys3HrrrXUea2XVNmWbTh8/fhx333032rZti0mTJqFZs2YwGo3473//izfffLPKvzK2a9cOn3/+Ob7//nts2bIFX375Jd5991089NBDGD9+fJXjGzJkCCZPnowDBw6gY8eO2Lx5My6//HKn3gN1naOkpCT06dOn2uPK7sKiaRokScKKFSsq3DK4PnbnqylPj3Ho0KH47rvv8N1332HAgAH48ssvccUVVzjer9zcXNx1110IDQ3F+PHj0bJlSwQEBOD333/HwoULq/wcVfZZLfsYX/r3RUREnnf27FlYLJZyf5wrLTAwEO+88w5+/vlnx/eTTZs24b333sMbb7xR4e/Mis7hKaqqujQmd6jseUSZpuieFBUV5fiu1rdvX7Rt2xb33XcfVq9e7ehLpWkaYmJisHDhwgrPUfJdRJIkLF68GHv27MF3332HLVu2YMqUKVi1ahXee+89hISE1Gmsrn63BoDZs2djw4YNGDNmDLp27YqwsDBIkuTUv7QykyZNwrBhwxzfYebMmYPly5dj/fr1jtUZRL6GoRSRFyiKgscffxyjR4/GO++8g3HjxjkqT4xGo0thSGRkJG6++WbcfPPNyM/Px1133YWXX3650ovmhIQEaJqG48ePO1VHVbQ0Kjw8vMK/fJX9a9a3334Lq9WKpUuXOpUtl90JrTLBwcEYPHgwBg8eDKvVikceeQTLli3DfffdV+WWuwMHDsT06dMdS/iOHj2K++67r9xxNZ0jd2jZsiWEEEhMTESbNm0qPa5kvo4dO+b01zubzYaTJ09WWZVT8ln5448/qhxLZV+APDHG0gYMGICQkBBHhVROTo7T0r1ffvkF2dnZWLJkCS677DLH7a7sMlOy1KLsXw7L/oW3vv99ERGRb/v4448BoNplVbIsO5aETZ48GcuWLcO///1v/Pzzz+jTp49Ly+Rr4tixY04/CyFw7Ngxp10BK6tIOn36tFPlck3GlpCQgJ9++gl5eXlO1VIl3wsTEhJcPld1z3Po0CFomuZULVXyPGXbENTFP/7xD/Ts2RPLli3DiBEjEBwcjJYtW+Knn35C9+7dXQoMu3btiq5du+Kxxx7Dp59+iieffBKbNm2q8Pd/dHQ0AgMDy72HAHDkyBGnn0uar5d9HyuqFPviiy9w0003Oe24WFxc7HLleMnuhQ8++CD+97//YeTIkVi7di0ee+wxlx5P5GnsKUXkJSXbzb711lsoLi5GTEwMevbsiffeew/nzp0rd3xmZqbjv7OyspzuCwkJQcuWLWG1Wit9viuvvBIAsGbNGqfb33rrrXLHtmzZEhaLBQcPHnTcdu7cuXI7kJT81axsWfGHH35Y6Tgqew0mkwnt2rWDEKLatfzh4eFITU3F5s2b8dlnn8FoNGLgwIFVnt+VOXKHQYMGQVEULFmypNxfs4QQjnF16tQJ0dHRWLdundOYNm7cWG0pfHR0NC677DJ8+OGH5b7MlH7Okt1Zyn6J8cQYSwsMDMTVV1+N//73v1i7di2Cg4Nx1VVXOe4v+ZJaeixWqxXvvvtutedOSEiAoijlelKsXbvW6ef6/vdFRES+66effsKrr76KxMREDB06tNLjsrOzy93WsWNHAHD8Dij53VqT34NV+eijj5yWrX/++edIT093fG8D9D+s/Prrr06/h7777jucOXPG6Vw1GduVV14JVVXxzjvvON3+5ptvQpIkp+eviyuvvBLp6elOvUDtdjvWrFmD4OBgpz9GucO//vUvZGdnY/369QCA6667Dqqq4tVXXy13rN1ud8xVTk5Oue9EZd/7shRFQWpqKr7++mun72N//fUXfvzxR6djQ0NDERUVhZ07dzrdXtF3nYqq0tasWVNhVVVpeXl5sNvtTrclJSVBlmV+hyGfxkopIi8aO3YsJkyYgA0bNmDkyJGYMWMG7rjjDtxwww247bbb0KJFC5jNZuzZswdnz57FJ598AkBfvtazZ09ccskliIyMxG+//YYvvvgCd911V6XP1bFjR1x//fV49913YbFY0K1bN2zfvr3Cv+4MHjwYCxcuxMMPP4xRo0ahqKgIa9euRZs2bfD77787jrviiitgNBpx//334/bbb0d+fj7ef/99xMTEID09vdrXHhsbi+7duyMmJgZ///033n77bfTr169cf4OKDB48GE899RTeffddpKamOipmStRmjtyhZcuWePTRR/HCCy/g1KlTGDhwIEJCQnDy5El8/fXXuO222zB27FgYjUY8+uijmD59OsaMGYPBgwfj5MmT2LBhg0v9mqZNm4aRI0di2LBhGDFiBBITE3Hq1Cl8//33jr8GX3LJJQCAf//7346G4/379/fYGEsbOnQoPvroI/z444+44YYbnJYIduvWDREREZg0aRJGjRoFSZLw8ccfu7QkICwsDNdeey3efvttSJKEFi1a4Pvvv3f0iCitPv99ERGRb/jhhx/w999/Q1VVmM1m/Pzzz9i6dSuaN2+OpUuXVlmJ/corr2Dnzp3o168fEhISkJGRgXfffRfx8fGOnpclDafXrVuHkJAQBAcHIyUlpca/F0tERETgjjvuwPDhw5GRkYG33noLrVq1wm233eY45tZbb8UXX3yBf/3rX7juuutw/PhxfPrpp+WWItZkbAMGDECvXr3w73//G6dOnUJycjK2bt2Kb775BmPGjKlymWNNjBgxAu+99x4mTZqE33//HQkJCfjiiy/wv//9D1OmTHHpO19N9OvXD0lJSXjzzTdx5513omfPnhgxYgSWL1+OAwcOOL67Hj16FJ9//jmmTp2Ka6+9Fhs3bsTatWsxcOBAtGzZEvn5+Vi/fj1CQ0OrDOgeeeQRbNmyBXfeeSdGjhwJVVXx9ttvo3379jh06JDTsbfeeitee+01TJ06FZ06dcLOnTvLVVQBesXXxx9/jNDQULRv3x579uzBtm3bEBkZWeVr3759O5599llce+21aN26NVRVxccffwxFUXDNNdfUaj6JPIGhFJEXDRo0CC1btsQbb7yB2267De3bt8eHH36IJUuWYOPGjcjOzkZ0dDQuvvhiPPTQQ47HjRo1Ct9++y22bt0Kq9WK5s2b49FHH8XYsWOrfL65c+ciKioKn376Kb755hv06tULr732Gvr16+d0XFRUFJYsWYL58+fj//7v/5CYmIjHH38cx44dcwql2rZti8WLF+Oll17C888/j9jYWIwcORLR0dGYMmVKlWMZMWIEPv30U6xatQoFBQWIj4/HqFGj8OCDD7o0dwMGDEBgYCDy8/Oddt2r6xy5w7hx49C6dWu8+eabeOWVVwAA8fHxuOKKKzBgwADHcSNGjICqqli5ciUWLFiApKQkLF26tNJG3KV16NAB69evx6JFi7B27VoUFxejefPmTs3DU1JSMGHCBKxbtw5btmyBpmn45ptvEBwc7JExlnb55ZcjLi4O6enpTkv3AP3ztmzZMjz//PN46aWX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06b1/hqIiIjIf7FSioiIiIgAAHl5eZAkCeHh4QCA3bt3Izw83BFIAUCfPn0gyzL27t3rrWESERGRn2ClFBERERGhuLgYCxcuxJAhQxAaGgoAMJvNiI6OdjrOYDAgIiIC6enp3hhmo2a1WXHy5IkqjzGZFFitarnbw8LCERsbW19DIyIiqhWGUkRERESNnM1mw4QJEyCEwKxZs+p0LqNRgSTp/20wKG4YnW8wmfTXJckSJFmq8eMlWQLq8PiswnwcPXYMr8ydA1OAqfLnkSQIIcrdbgoLxYuvLEVsbFyNn5sq50+f8YaCc+55nHPPamzzzVCKiIiIqBGz2Wx49NFHcfr0abz11luOKikAiI2NRWZmptPxdrsdOTk5iIurONyw2ZyrdCqq2mmIrFYVQgBCExBa+dCnOkITQB0en1dYCJMk4+HL+qFdfPNKj5Nkqdz5T2VlYNEv3yMjIxvh4dGVPJJqy18+4w0J59zzOOee1Zjmm6EUERERUSNVEkgdO3YMq1evRlRUlNP93bp1Q25uLvbt24dOnToBALZv3w5N05CSkuKNITd6CRHRaBsXX+n9FYVSREREvoqhFBEREZGfys/Px/Hjxx0/nzx5EgcOHEBERATi4uIwfvx47N+/H8uXL4eqqo4+URERETCZTGjXrh369u2LZ555BrNmzYLNZsPs2bMxZMgQ7rxHREREdcZQioiIiMhP7du3D6NHj3b8PG/ePADAsGHD8PDDD+Pbb78FANx4441Oj1u9ejV69eoFAFi4cCFmz56NMWPGQJZlDBo0CNOmTfPQKyAiIiJ/xlCKiIiIyE/16tULhw4dqvT+qu4rERkZiRdeeMGdwyIiIiICAMjeHgARERERERERETU+DKWIiIiIiIiIiMjjGEoREREREREREZHHMZQiIiIiIiIiIiKPYyhFREREREREREQex1CKiIiIiIiIiIg8jqEUERERERERERF5HEMpIiIiIiIiIiLyOIZSRERERERERETkcQyliIiIiIiIiIjI4xhKERERERERERGRxzGUIiIiIiIiIiIij2MoRUREREREREREHsdQioiIiIiIiIiIPI6hFBEREREREREReRxDKSIiIiIiIiIi8jiGUkRERERERERE5HEMpYiIiIiIiIiIyOMYShERERERERERkccxlCIiIiIiIiIiIo9jKEVERERERERERB7HUIqIiIiIiIiIiDyOoRQREREREREREXkcQykiIiIiIiIiIvI4hlJERERERERERORxDKWIiIiIiIiIiMjjGEoREREREREREZHHMZQiIiIiIiIiIiKPYyhFREREREREREQex1CKiIiIiIiIiIg8jqEUERERERERERF5HEMpIiIiIiIiIiLyOIZSRERERERERETkcQyliIiIiIiIiIjI4xhKERERERERERGRxzGUIiIiIiIiIiIij2MoRUREREREREREHsdQioiIiIiIiIiIPI6hFBEREREREREReRxDKSIiIiIiIiIi8jiGUkRERERERERE5HEMpYiIiIiIiIiIyOMaVCiVlpaGJ598Er169UJKSgpuuOEG/Pbbb477hRBYtGgRUlNTkZKSgrvvvhtHjx51Okd2djaeeOIJdO/eHT169MCUKVOQn5/vdMzBgwdxxx13oHPnzujXrx9WrFjhiZdHRERERERERNRoNJhQKicnByNHjoTRaMSKFSvw2Wef4emnn0ZERITjmBUrVmDNmjWYOXMm1q9fj6CgIIwdOxbFxcWOY5588kn8+eefWLVqFZYtW4adO3di+vTpjvvz8vIwduxYNG/eHBs2bMDEiROxZMkSvPfeex59vURERERERERE/szg7QG4asWKFYiPj8e8efMct7Vo0cLx30IIrF69Gg888AAGDhwIAFiwYAH69OmDr7/+GkOGDMFff/2FLVu24IMPPkDnzp0BANOmTcO4ceMwceJENG3aFJ988glsNhvmzp0Lk8mEiy66CAcOHMCqVaswYsQIz75oIiIiIiIiIiI/1WAqpb799lt06tQJ48ePR+/evXHTTTdh/fr1jvtPnjyJ9PR09OnTx3FbWFgYunTpgt27dwMAdu/ejfDwcEcgBQB9+vSBLMvYu3cvAGDPnj3o0aMHTCaT45jU1FQcOXIEOTk59f0yiYiIiIiIiIgahQYTSp04cQJr165F69atsXLlSowcORJz5szBxo0bAQDp6ekAgJiYGKfHxcTEwGw2AwDMZjOio6Od7jcYDIiIiHA83mw2IzY21umYkp9LzkNERERERERERHXTYJbvCSHQqVMnPP744wCAiy++GIcPH8a6deswbNgwr47NaFQgSXU7h8GguGcwfoLz4Yzz4Yzz4Yzz4Yzz4YzzUV5jmpMdO3Zg5cqV2LdvH9LT0/HKK6842hwA+verxYsX4/3330dubi66d++OmTNnonXr1o5jsrOzMXv2bHz33XeQZRmDBg3C1KlTERIS4oVXRERERP6kwYRScXFxaNeundNtbdu2xRdffOG4HwAyMjLQpEkTxzEZGRno0KEDAL3iKTMz0+kcdrsdOTk5jsfHxsaWq4gq+blsBVUJm02t7ctyYrW65zz+gvPhjPPhjPPhjPPhjPPhjPNRXmOZk4KCAiQnJ+Pmm2/Gww8/XO7+kk1i5s+fj8TERCxatAhjx47Fpk2bEBAQAEDfJCY9PR2rVq2CzWbDlClTMH36dLzwwguefjlERETkZxrM8r3u3bvjyJEjTrcdPXoUCQkJAIDExETExcXhp59+ctyfl5eHX3/9Fd26dQMAdOvWDbm5udi3b5/jmO3bt0PTNKSkpAAAunbtip07d8JmszmO2bZtG9q0aeO00x8RERGRr+vXrx8ee+wxXH311eXuK7tJTIcOHbBgwQKcO3cOX3/9NQA4NomZM2cOunTpgh49emDatGn47LPPkJaW5umXQ0RERH6mwYRSY8aMwa+//oply5bh2LFj+PTTT7F+/XrccccdAABJkjB69GgsXboU33zzDQ4dOoSJEyeiSZMmjjL1du3aoW/fvnjmmWewd+9e7Nq1C7Nnz8aQIUPQtGlTAMANN9wAo9GIqVOn4vDhw9i0aRNWr16Nf/7zn1577URERETu5q5NYoiIiIhqq8Es30tJScGSJUvw4osv4pVXXkFiYiKmTJmCoUOHOo659957UVhYiOnTpyM3NxeXXnopXn/9dUf5OQAsXLgQs2fPxpgxYxx9EaZNm+a4PywsDCtXrsSzzz6L4cOHIyoqCg8++CBGjBjh0ddLREREVJ/ctUkMERERUW01mFAKAPr374/+/ftXer8kSZgwYQImTJhQ6TGRkZHV9kDo0KED3n333VqPk4iIiKixKr0BjD81lTeZ9NclyRIkueY73EiyBHjg8bIkQSuzFkKSJUiS/hpMJv95T3yBP33GGwrOuedxzj2rsc13gwqliIiIiMg93LVJTFllN4Dxl6byVqsKIQChCQhN1PjxQhOABx6vySh3v9AEhNBfg7+8H76Ec+p5nHPP45x7VmOa7wbTU4qIiIiI3Mddm8QQERER1RYrpYiIiIj8VH5+Po4fP+74+eTJkzhw4AAiIiLQvHlzxyYxrVq1QmJiIhYtWlTpJjGzZs2CzWYrt0kMERERUW0xlCIiIiLyU/v27cPo0aMdP8+bNw8AMGzYMMyfP98tm8QQERER1RZDKSIiIiI/1atXLxw6dKjS+921SQwRERFRbbCnFBEREREREREReRxDKSIiIiIiIiIi8jiGUkRERERERERE5HEMpYiIiIiIiIiIyOMYShERERERERERkccxlCIiIiIiIiIiIo9jKEVERERERERERB7HUIqIiIiIiIiIiDyOoRQREREREREREXkcQykiIiIiIiIiIvI4hlJERERERERERORxDKWIiIiIiIiIiMjjGEoREREREREREZHHMZQiIiIiIiIiIiKPYyhFREREREREREQeZ/D2AIiIiDxGCEg5FkhWG4TJCBERBkiSt0dFRERERNQoMZQiIqJGQTZnQTl0BEpmFmC3AwYD1OgoqMltoMVGeXt4RERERESNDkMpIiLye7I5C8adeyEVFEELDwWMBsBmh5KWDtliga1HCoMpIiIiIiIPY08pIiLyb0JAOXRED6Rio4AAEyDLQIAJWmwUpIIiKH8cAYTw9kiJiIiIiBoVhlJEROTXpBwLlMwsvUKqbP8oSYIWHgolIwtSjsU7AyQiIiIiaqQYShERkV+TrDa9h5SxkhXrRgNgt+vHERERERGRxzCUIiIivyZMRsCg95CqkE1vei5MRs8OjIiIiIiokWMoRUREvk8ISNm5kM9lQMrOrVH/JxERBjU6CnJuXvnHCQE5Nw9qTBRERJibB01ERERERFXh7ntEROTTZHMWlENHoGRm6cvwDAao0VFQk9u4tmOeJEFNbgPZYoFsznLafU/OzYMIDoSa1KZ8vykiIiIiIqpXDKWIiMhnyeYsGHfu1XfOKxUmKWnpkC0W2HqkuBRMabFRsPVIuRBuWc6HW/FxUJNcDLeIiIiIiMitGEoREZFvEgLKoSN6IBUbdaGSKcAELTZKr6D64wi0mEiXqpy02ChoMZGw51ggWW0QJqO+ZI8VUkREREREXsFQioiIfJKUY4GSeX65XdngSJKghYdCyciCPccCERnu4kkliMhwuN6RioiIiIiI6gsbnRMRkU+SrDa9h5Sxkr+fGA2A3a4fR0REREREDQ5DKSIi8knCZAQMeg+pCtn0vlDCZPTswIiIiIiIyC0YShERkXcJASk7F/K5DEjZuYDQF9eJiDCo0VGQc/Mct5V+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y/nNb34Tn/rUp/CLX/wCjuMAAD75yU/ihz/8If7zP/8TAAebl8tl/OM//mP0uv/1v/4XNmzYEAWdX3rppbj66qtx9dVXA+CufhdffDFuuukmyZQSBEEQBOGkY2hoCJdddhm+/e1vY9u2bdHjN998M+677z78y7/8y1GcnSAIgiAIxzP6aE+gFYaGhvD6178eq1atwnXXXYfx8XGMjIxEGVAAcOWVV8JxHPx//9//h127duH222/HV7/6VbzpTW+KxrzhDW/Az372M3z5y1/GU089hb/7u7/DI488gquuugoAoJTCG97wBvzDP/wD/vu//xtPPPEE3v3ud6O/vz/qxicIgiAIgnAy0dXVBcuyMDbW6E6qd5sLgiAIgiAshuPCKfXd734X733ve5s+98QTT0R/fvzxx3HjjTfi4YcfRldXF6666qoZ4Zt33HEHPvWpT+HAgQNYv349/uqv/grPec5zoueJCLfccgv++Z//GdlsFhdccAGuv/56nHbaactzcoIgCIIgCMc4r371q7Flyxa8//3vBwAYY3D55Zc3/a4lCIIgCILQKseFKCUIgiAIgiAcPW6//XZcd911uPHGG7FlyxZ85StfwR133IE77rhD3FKCIAiCICyaoxp0LgiCIAiCIBz7/N7v/R7Gx8dxyy23YGRkBGeffTa++MUviiAlCIIgCMJhcVxkSp3ofOMb38Bzn/tcnHvuuXj1q1+N3/72t0d7SkvO5z73Obzyla/Etm3bcPHFF+NP//RPZ3Tsef3rX4+NGzc2/Pc3f/M3DWMOHjyIa6+9Flu3bsXFF1+Mj370o/CPw24Df/d3fzfjXF/84hdHz1cqFdxwww246KKLsG3bNvzFX/zFjM6RJ8paAMBzn/vcGeuxceNG3HDDDQBO/L1x33334S1veQsuueQSbNy4EXfeeWfD80SET3/607jkkkuwZcsWvPGNb8SePXsaxkxOTuKd73wnzj//fFx44YV43/veh0Kh0DDm8ccfx//+3/8b5557Lp7znOfgC1/4wnKf2qKYaz08z8PHPvYxXHnllTjvvPNwySWX4N3vfjeGhoYajtFsT33+859vGHMirAcAvOc975lxrtdcc03DmBNpfwDzr0mznycbN27EF7/4xWjMibRHjhRXXXUVvva1r+FlL3sZxsbG8PrXvx7Pf/7zccstt8B13YaxrazdHXfcgRe/+MU499xzceWVV+InP/lJw/Ot/OwTapwM3yeXg1a+oy7V97Jf/epXeMUrXoHNmzfjBS94Ab773e8u+/kd63z+85/Hxo0b8aEPfSh6TNZ76RkaGsK73vUuXHTRRdiyZQuuvPJKPPzww9HzJ9t3zeUmCAJ86lOfwnOf+1xs2bIFz3/+8/H3f//3qC9SkzWvg4Sjyg9+8APatGkTfec736Fdu3bRX//1X9OFF15Io6OjR3tqS8rVV19Nt912G+3cuZN27NhBf/zHf0yXX345FQqFaMxVV11Ff/3Xf03Dw8PRf7lcLnre93166UtfSm984xvpscceo7vuuosuuugi+sQnPnE0TumwuOWWW+glL3lJw7mOjY1Fz//N3/wNPec5z6G7776bHn74YXrNa15Dr33ta6PnT6S1ICIaGxtrWItf/OIXtGHDBrrnnnuI6MTfG3fddRd98pOfpB/+8Ie0YcMG+tGPftTw/Oc+9zm64IIL6Ec/+hHt2LGD3vKWt9Bzn/tcKpfL0ZhrrrmGXvayl9GDDz5I9913H73gBS+gd7zjHdHzuVyOtm/fTu985ztp586d9B//8R+0ZcsW+va3v33EzrNV5lqPbDZLb3zjG+kHP/gBPfXUU/TAAw/Qq171KnrFK17RcIwrrriCPvOZzzTsmfqfNyfKehARXXfddXTNNdc0nOvk5GTDmBNpfxDNvyb1azE8PEzf+c53aOPGjbRv375ozIm0R44kP/nJT+g973kP/exnP6N9+/bRnXfeSRdffDHddNNN0ZhW1u7Xv/41nX322fSFL3yBnnzySfq///f/0qZNm+iJJ56IxrTys09gTpbvk8tBK99Rl+J72b59+2jr1q30kY98hJ588kn62te+RmeffTb99Kc/PaLneyzx0EMP0RVXXEFXXnklffCDH4wel/VeWiYnJ+mKK66g97znPfTQQw/Rvn376Gc/+xnt3bs3GnOyfddcbv7hH/6BnvWsZ9GPf/xjGhgYoDvuuIPOO+88+spXvhKNkTWvIaLUUeZVr3oV3XDDDdHfgyCgSy65hD73uc8dxVktP2NjY7Rhwwa69957o8euuuqqhn+QpnPXXXfRWWedRSMjI9Fj3/zmN+n888+nSqWyrPNdam655RZ62cte1vS5bDZLmzZtojvuuCN67Mknn6QNGzbQAw88QEQn1lo044Mf/CA9//nPJ2MMEZ1ce2P6DbYxhn7nd36HvvjFL0aPZbNZ2rx5M/3Hf/wHEdX2x29/+9tozE9+8hPauHEjDQ4OEhHRN77xDXrmM5/ZsB4f+9jH6EUvetFyn9Jh0UxwmM5DDz1EGzZsoAMHDkSPXXHFFXTrrbfO+poTaT2uu+46eutb3zrra07k/UHU2h5561vfSm94wxsaHjtR98jR4Atf+AI997nPjf7eytq97W1vo2uvvbbhOK9+9avp/e9/PxG19rNPqHGyfp9cDqZ/R12q72U333wzveQlL2l4r7e//e109dVXL/MZHZvk83l64QtfSL/4xS8avufJei89H/vYx+gP//APZ33+ZP+uuRxce+219N73vrfhsT//8z+nd77znUQkaz4dKd87iriui0cffRTbt2+PHtNaY/v27XjggQeO4syWn1wuBwDo6OhoePz73/8+LrroIrz0pS/FJz7xCZRKpei5Bx98EBs2bGjIr7jkkkuQz+fx5JNPHpmJLyF79+7FJZdcguc973l45zvfiYMHDwIAHnnkEXie17AvzjjjDKxevRoPPvgggBNvLepxXRf//u//jle+8pVQSkWPn0x7o579+/djZGSkYT+0tbVh69at0c+JBx54AO3t7Tj33HOjMdu3b4fWOirfePDBB3HhhRciFotFYy655BI8/fTTmJqaOkJnszzk83kopdDe3t7w+Be+8AVcdNFFePnLX44vfvGLDbb+E2097r33Xlx88cV40YtehOuvvx4TExPRcyf7/hgdHcVPfvITvOpVr5rx3Mm0R5aTXC7X8O95K2v34IMP4uKLL244ziWXXBL9O9fKzz6BOZm/Ty4H07+jLtX3svn2/MnGjTfeiOc85zkN6wrIei8H//M//4PNmzfjL//yL3HxxRfj5S9/Of75n/85el6+ay4927Ztwz333IOnn34aAJfY/frXv8Zll10GQNZ8OhJ0fhSZmJhAEATo6elpeLynp2dGLfuJhDEGH/7wh3H++edjw4YN0eMvfelLsXr1avT39+OJJ57Axz/+cTz99NP4zGc+A4BvLKYHqlb/PjIycuROYAnYsmULPvKRj+C0007DyMgI/v7v/x6ve93r8P3vfx+jo6NwHGfGDXZPT090nifSWkznzjvvRC6Xwyte8YrosZNpb0ynOv9mPyeq+Qqjo6Po7u5ueN62bXR0dDTsmbVr1zaM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xzY3NzNnzhyio6M7/N7V1RVHR0fy8/MpLS3l0KFDbNy4kXfffZfPPvvMsnpMREREej8nJyc8PDwASE9PJyIigh07djBy5Eig82wTHBxMcXExBw8e5Ntvv2XBggXMmzePhISEHp2LwWDo8NDw9q8wdzf3jB07lv379/P1119z+PBh4uLiMBqN/5PN3EXENrTRuYjYTHswcnNz69LXWsaOHUt1dTUDBgzAw8PD6q9v37707duXgQMHcuLECctvbt682emGmCNHjqS1tZWysrI79rd/7e7WrVuWNk9PTxwdHamvr+9wHq6urpZjTp06ZTXWP8+rMyaTiWPHjlFcXMzx48cxmUyWvh9++IHo6GiCg4MZMWIEjo6OXL58+a5jtRfrLl68aGmrqKiwOmbMmDH8+uuvHa7Fw8PDEkTt7e0xGo3Ex8dTUFBAXV0d3333XZeuR0RERHofOzs7lixZQlZWVpeyDfydKyIjI1m7dq3l63130pUc1L9/fy5dumRVmPrnRunQ/dwDfz/gCwsLIzU1lXfeeYfCwkKrPUtFpHdTUUpEeq1Zs2bRr18/li5dytGjR7lw4QLff/89qamplk0s58+fT3Z2NkVFRVRWVrJq1SqrL8/dbsiQIURGRvLmm29SVFRkGfOrr74CwN3dHYPBQElJCY2NjVy9ehUXFxdiYmLIyMggPz+fmpoaTp8+TV5eHvn5+QDMmTOHqqoqMjMzOXfuHLt377b03Yu/vz8eHh4kJCQwfPhwq6Xxw4YNo6CggMrKSk6cOMFrr73W6WqvoUOHWjZsr6qqoqSkhJycHKtjFi1axPHjx1m9ejXl5eVUVVVRVFTE6tWrASguLuajjz6ivLycuro6Pv/8c1pbW3nssce6dD0iIiLSO02fPh07Ozu2b99+z2yTlZVFUVER1dXVnD17lpKSEjw9Pe84bldyUGBgII2NjWRnZ1NTU8PWrVv55ptvrI7pbu7Jzc3lyy+/pLKykvPnz7N3714GDhyold0i/yIqSolIr+Xk5MTHH3+Mm5sby5YtIywsjMTERK5du2b5AktMTAzh4eEkJCQwZ84cnJ2dmTZtWqfjJicnExoaSnJyMjNmzGDlypW0tLQAMHjwYGJjY1m3bh1Go5GUlBQA4uLiePnllzGbzYSFhbFw4UJKSkosm366ubmxadMm9u/fT0REBNu2beOVV17p0nUaDAZmz55NU1MTs2fPtupLS0ujqamJyMhI4uPjiY6OZsCAAXcdy8HBgXXr1nHu3DnCw8PJzs4mLi7O6phRo0aRl5dHVVUVc+fOJTIyko0bNzJo0CAA+vbty759+3jhhRcICwtj27ZtrFu3jhEjRnTpekRERKR3sre3Jyoqis2bN7NkyZJOs42DgwPr168nPDycqKgo7OzsWL9+/R3H7UoO8vT05K233uKTTz4hIiKCkydPdtjTqru5x9nZmc2bN2MymTCZTNTV1fHBBx9YtjMQkd7P0Ha33YBFRERERERERET+T1RCFhERERERERERm1NRSkREREREREREbE5FKRERERERERERsTkVpURERERERERExOZUlBIREREREREREZtTUUpERERERERERGxORSkREREREREREbE5FaVERERERERERMTmVJQSERERERERERGbU1FKRERERERERERsTkUpERERERERERGxORWlRERERERERETE5v4DdgtBOv//hpEAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Residual mean: 21.46\n", + "Residual std: 1077.25\n" + ] + } + ], "source": [ "# Step 7.1: Calculate evaluation metrics\n", "print(\"=== MODEL EVALUATION ===\")\n", @@ -749,20 +820,20 @@ "\n", "# Training set\n", "axes[0].scatter(y_train, y_pred_train, alpha=0.5)\n", - "axes[0].plot([y_train.min(), y_train.max()], [y_train.min(), y_train.max()], \n", - " 'r--', lw=2)\n", - "axes[0].set_xlabel('Actual Values')\n", - "axes[0].set_ylabel('Predicted Values')\n", - "axes[0].set_title(f'Training Set (R² = {train_r2:.2f})')\n", + "axes[0].plot(\n", + " [y_train.min(), y_train.max()], [y_train.min(), y_train.max()], \"r--\", lw=2\n", + ")\n", + "axes[0].set_xlabel(\"Actual Values\")\n", + "axes[0].set_ylabel(\"Predicted Values\")\n", + "axes[0].set_title(f\"Training Set (R² = {train_r2:.2f})\")\n", "axes[0].grid(True, alpha=0.3)\n", "\n", "# Testing set\n", "axes[1].scatter(y_test, y_pred_test, alpha=0.5)\n", - "axes[1].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], \n", - " 'r--', lw=2)\n", - "axes[1].set_xlabel('Actual Values')\n", - "axes[1].set_ylabel('Predicted Values')\n", - "axes[1].set_title(f'Testing Set (R² = {test_r2:.2f})')\n", + "axes[1].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], \"r--\", lw=2)\n", + "axes[1].set_xlabel(\"Actual Values\")\n", + "axes[1].set_ylabel(\"Predicted Values\")\n", + "axes[1].set_title(f\"Testing Set (R² = {test_r2:.2f})\")\n", "axes[1].grid(True, alpha=0.3)\n", "\n", "plt.tight_layout()\n", @@ -777,17 +848,17 @@ "\n", "plt.subplot(1, 2, 1)\n", "plt.scatter(y_pred_test, residuals, alpha=0.5)\n", - "plt.axhline(y=0, color='r', linestyle='--')\n", - "plt.xlabel('Predicted Values')\n", - "plt.ylabel('Residuals')\n", - "plt.title('Residuals vs Predicted Values')\n", + "plt.axhline(y=0, color=\"r\", linestyle=\"--\")\n", + "plt.xlabel(\"Predicted Values\")\n", + "plt.ylabel(\"Residuals\")\n", + "plt.title(\"Residuals vs Predicted Values\")\n", "plt.grid(True, alpha=0.3)\n", "\n", "plt.subplot(1, 2, 2)\n", - "plt.hist(residuals, bins=30, edgecolor='black', alpha=0.7)\n", - "plt.xlabel('Residuals')\n", - "plt.ylabel('Frequency')\n", - "plt.title('Distribution of Residuals')\n", + "plt.hist(residuals, bins=30, edgecolor=\"black\", alpha=0.7)\n", + "plt.xlabel(\"Residuals\")\n", + "plt.ylabel(\"Frequency\")\n", + "plt.title(\"Distribution of Residuals\")\n", "plt.grid(True, alpha=0.3)\n", "\n", "plt.tight_layout()\n", @@ -795,9 +866,15 @@ "\n", "# Check residual statistics\n", "print(f\"Residual mean: {residuals.mean():.2f}\")\n", - "print(f\"Residual std: {residuals.std():.2f}\")" + "print(f\"Residual std: {residuals.std():.2f}\")\n" ] }, + { + "cell_type": "markdown", + "id": "57d0ecd7", + "metadata": {}, + "source": [] + }, { "cell_type": "markdown", "id": "e8f8472f", @@ -820,6 +897,218 @@ "cell_type": "markdown", "id": "a9e3f8b3", "metadata": {}, + "source": [ + "8. Feature Importance Analysis" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "379e444a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== FEATURE IMPORTANCE ANALYSIS ===\n", + "\n", + "Feature Importance (sorted by absolute coefficient value):\n", + " Feature Coefficient\n", + "2 visitor_type_encoded 283.311482\n", + "0 year 267.663043\n", + "6 covid_period -206.835503\n", + "1 country_encoded -40.460696\n", + "3 decade -24.509425\n", + "4 post_2000 -14.076701\n", + "5 post_2010 -4.197869\n" + ] + }, + { + "data": { + "image/png": "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AABgaARjAAAAAIChEYwBAAAAAIZGMAYAAAAAGBrBGAAAAABgaARjAAAAAIChEYwBAAAAAIZGMAYAAAAAGBrBGAAAAABgaARjAAAAAIChEYwBAAAAAIZGMAYAAAAAGBrBGAAAAABgaARjAAAAAIChEYwBAAAAAIZmm94FAAAAAABeXGvWrNTu3d/r8uVLsre3V9mynurdu78KFixs7hMWFqqFCz/Qzz8fVGTkfRUsWEgdOnRRjRq1zH2GDx+ss2fP6M6d23J2dpaPTyX17j1ALi6uqW57zJgx+vHHH3Xjxg05ODjI29tbQ4cOVbFixcx9Jk2apMOHD+vMmTMqVqyYPv/88yfex6d2xDgoKEiNGjV66n2RvoKDg+Xj4/Ofx3F3d9c333zzFCoCAAAA8DwdOXJYTZu20JIlKzVnzgLFxcVp8OB+evDggbnPpEljdeXKZU2dOkurV3+s119/Q2PGjNCZM6fMfcqX99GECVO1fv2nmjRpuq5du6ZRo4Y/cttlypRRYGCgduzYoRUrVshkMqlr166Kj4+36NesWTPVr1//X+/jUwvGXbp00apVq/5V34CAAPXp0+dplfJINWvWTHOdAAAAAGB0s2cHqX59fxUtWkwlSrjp/ffH6a+/ruv06ZPmPidOHFezZq1UurSH8uXLr06dusnJyVmnT/8djFu1aisPj7LKnTuPypb1Urt2HfXbb78qLi4u1W23atVKFStWVP78+VWmTBkNGjRIf/75p65du2buM2rUKLVt21YFChT41/v41IKxo6OjsmXL9tT7PomYmJinPiYAAAAA4G/379+TJGXOnNnc5uHhqe++26nw8LtKSEjQN998rZiYaHl7V0hxjPDwu/rf/76Sh4enbG3TdoVvZGSkgoODlT9/fuXOnfu/78hD0hyMN27cKD8/PyUkJFi09+7dWyNGjEh2evSBAwfUvHlzlStXTj4+PmrdurU51T/cNygoSJ999pm+/fZbubu7y93dXQcOHJAknT59Wh06dJCnp6cqV66s0aNH6/79++ZtJB1pXrRokfz8/FS3bt1H7kP79u117do1BQYGmrcVGRmp8uXL66uvvrLo+80336hcuXK6d++erl69Knd3d23fvl2tW7dW2bJl1bBhQx08eNBinTNnzqhbt27y9vZWlSpV9N577+nWrVtpen8TEhK0ZMkS1axZU56ennr77bctajpw4IDc3d21f/9+NW3aVF5eXmrdurUuXLhgMc53332nZs2aqWzZsqpcubL69u1rXnb37l0NGzZMFStWlJeXl7p166ZLly5ZrB8cHKwaNWrIy8tLffv21Z07d5LV+s0336hJkyYqW7asatWqpfnz51v8lefSpUtq27atypYtq/r16+uHH35I03sAAAAA4Pmzskr7l8mUoHnzZsnT00vFihU3t0+cOFVxcXGqX7+W3njDVzNmTNGUKTNVoEABi/UXLZqnN9/0U/36tfTXX9c1bdqsZNv4p3Xr1snb21ve3t7as2ePVq5cqQwZMjzV9yDNwbhu3bq6c+eOObRK0p07d7R37169/fbbFn3j4uLUt29fVaxYUV988YU2btyoVq1aySqFvezSpYvq1aunatWqad++fdq3b5+8vb0VGRmprl27KkuWLNq8ebPmzp2rH3/8URMnTrRYf//+/bp48aJWrlypJUuWPHIfgoKClDt3bg0YMMC8LQcHBzVo0EDBwcEWfT/99FPVqVNHTk5O5rbp06erc+fO2rJli8qVK6devXrp9u3bkqTw8HB17NhRpUuX1ubNm7V8+XKFhYVp0KBBaXp/lyxZoi1btmj8+PHavn27OnXqpPfeey9Z+J4zZ44CAgL06aefysbGRu+//7552a5du9SvXz9Vr15dW7Zs0erVq+Xp6WleHhAQoBMnTmjRokXauHGjTCaTevToodjYWEnSsWPHNHLkSLVt21ZbtmxR5cqVtWjRIovtHzp0SMOHD1eHDh20Y8cOTZgwQcHBwVq8eLGkxIDfv39/2dnZ6ZNPPtH48eM1c+bMNL0HAAAAAJ4/FxfnNH8tWDBbly9fVFDQPIv2tWtXKCoqUqtWrVJwcLC6dOmisWNHKCwsxKJfv369tWXLFn344Yeyt7fTtGkTlSOHk0Wff3r77bf12Wefae3atSpcuLAGDRqk6Ojop/oepPmu1FmyZNHrr7+urVu3ytfXV5L09ddfK1u2bKpcubIOHTpk7nvv3j1FRETojTfeUMGCBSXJ4q5hD3N0dFTGjBkVExMjV9e/70a2ZcsWxcTEaNq0aXJwcJCUeEeyXr16aejQoXJxcZEkOTg4aNKkSWn6i0HWrFllY2MjR0dHi221aNFCrVu31o0bN5QzZ06FhYWZ/xLxsLZt26pOnTqSpHHjxmnv3r3avHmzunfvrrVr16p06dIaMmSIuf+UKVNUvXp1Xbx4UUWKFEm1rpiYGC1ZskQrV66Ut7e3JKlAgQL65ZdftHHjRlWqVMncd/DgwebXPXr0UI8ePRQdHS17e3stXrxY9evX14ABA8z9S5YsKSnxKO53332nDRs2qHz58pKkmTNnqkaNGvrmm29Ur149ffTRR6pWrZq6d+8uSSpSpIiOHDmivXv3msebP3++evTooSZNmpjrHDhwoGbMmKF+/frpxx9/1IULF7R8+XLlypXLXHPSmAAAAABeLKGhEWnqN2vWNO3bt0cLFiyVra2jeb2rV69q7dq1WrNmo4oWTcx9rVt31P79B7RixSoNG/b+Q6PYydnZRe7uLho9eqKaNGmg3bt/lIfH3wf0/hmOnZ2d5ezsrMKFC8vLy0uVKlXSzp071bBhw/+24w95osc1+fv7a/To0Ro3bpwyZMigrVu3qkGDBrK2tjzwnDVrVjVt2lRdu3ZV1apV5evrq3r16ilnzpxp3tb58+fl7u5uDsWSVL58eSUkJOjixYvmYOzm5vafD6N7enqqePHi2rJli3r06KEvvvhCefPmVcWKFS36JYVWSbK1tZWHh4f5VOZTp07pwIEDFn2SXLly5ZHB+PLly3rw4IG6dOli0R4bG6tSpUpZtLm7u5u/Twr3YWFhyps3r06ePKkWLVqkuI3z58/L1tZWXl5e5rZs2bKpSJEiOn/+vLnPm2++abFeuXLlLILxqVOndPjwYfMRYkmKj49XdHS0Hjx4oPPnzyt37tzmUCwpxfcEAAAAwIvBZHrccpPmzJmuPXt2KShoifLkyWexTlRUlCTJysraot3a2loJCaZUx4+PT1wQHR3z2Br+Wc/Tvr/UEwXjmjVratSoUdq1a5fKli2rQ4cOacSIESn2DQwMVPv27bV37159+eWXmjt3rlauXKly5co9jbrNMmXK9FTGadGihdatW6cePXooODhYTZs2TfHU79RERkbqjTfe0NChQ5Mte/jodGrrSomnUz8cKCUlC/0PX5ieVF/Sdd8ZM2ZMc73/VmRkpPr376/atWsnW2Zvb//Mtw8AAADg+Zo1a5q++eYrBQbOkoODg8LCQiVJTk5OsrfPqEKFCit//gKaMWOK+vYdqCxZsmrPnl36+ecDmj59jiTpt99O6NSp3+TpWU7Ozpl17dpVLV++SPny5TcfLb5584YGDuytWbNmytPTU3/88Yd27NihqlWrKnv27Lp+/bqWLl2qjBkzqnr16ub6Ll++rMjISN28eVNRUVE6eTLxbtnFihVL80HUJwrG9vb2ql27trZu3arLly+rSJEiKlOmTKr9S5curdKlS6tnz55q1aqVtm3blmIwtrOzS3ZTr2LFiumzzz5TZGSk+ajx4cOHZW1t/cijr4+T0rakxPPWZ8yYoY8++kjnzp0znyr8sKNHj5qPIsfFxem3335T27ZtJSU+X+vrr79Wvnz50nxXtSRJP7CQkBCL06aflJubm/bv369mzZqluI24uDgdO3bMfCr17du3dfHiRRUvXtzc5/jx4xbrHTt2zOJ16dKldfHiRRUqVCjVfbl+/br5tHQp8X0DAAAA8HLasmWzJKl//54W7e+/P1b16/vL1tZWM2Z8oMWLgzR8+BA9eBCpfPkKaOTIcfL19ZOUeBBv9+7vtWLFUkVFPVCOHC6qXNlXEyZ0NYfXuLg4Xbly2fx85AwZMujQoUNavXq1wsPDlSNHDvn4+GjDhg3KkSOHuY5Ro0ZZ3JupcePGkqRvv/1W+fPnT9M+PlmCU+Lp1D179tTZs2eT3XQryR9//KFNmzapZs2aypkzpy5evKhLly5Z3LX6Yfny5dO+fft04cIFZc2aVc7OzvL399e8efMUEBCgfv366datW5o4caIaNWpkPo3638iXL59+/vlnNWjQQHZ2dsqePbukxGuo33rrLU2fPl1Vq1ZN8fbf69evV+HChVW0aFGtXr1ad+/eNYfQd955R5s2bdKQIUPUrVs3Zc2aVZcvX9aOHTs0adIk2djYpFqTk5OTunTposDAQJlMJlWoUEERERE6fPiwnJycUgzpKenXr586deqkggULqkGDBoqLi9Pu3bvVo0cPFS5cWLVq1dLo0aM1fvx4OTk5aebMmcqVK5dq1aolKfGu3W3atNGKFStUq1Yt7du3z+I0aknq27evevXqpbx586pOnTqytrbWqVOndObMGQ0ePFhVqlRR4cKFFRAQoGHDhunevXuaM2dOmuoHAAAA8OLZt+/QY/sUKFBQkyfPSHV5sWLFNW/e4lSXS1KePHm1b98hubomXmOcK1cuLVu27LHbXrNmzWP7PM4TP8f4tddeU5YsWXTx4kX5+/un2CdTpky6cOGC+vfvrzp16mjMmDFq27atWrdunWL/li1bqkiRImrWrJl8fX11+PBhZcqUSStWrNCdO3fUvHlzDRw4UL6+vho9evSTlmxhwIABunbtmt58803zTcSSNG/eXLGxsSkecZWkd999V0uXLlWjRo30yy+/aNGiReZgnStXLm3YsEEJCQnq2rWr/P39NWXKFDk7Oye7BjslgwYNUp8+fbRkyRLVr19f3bp1065du9L8Fw5Jqly5sj744AN99913atSokTp27Khff/3VvDwwMFBlypRRr1691KpVK5lMJi1dulR2dnaSEq8nnjhxoj766CM1atRI+/btU+/evS22Ua1aNS1evFj79u1T8+bN1bJlS61atUr58uWTlHgdwfz58xUVFaXmzZtr5MiRGjx4cJr3AQAAAACeNyuT6Ukuc361bdmyRYGBgdq7d6/FuehXr15VrVq1tGXLlmQ3w8LTETVkenqXAAAAABhSxIjej+/0HCUdMX6envhU6lfRgwcPdPPmTS1btkytW7d+6g+LBgAAAAC8uF6pYHzo0KFHPi/3yJEjKbYvX75cixcvlo+Pj3r06PHU6woJCVGDBg1SXb59+3blzZv3qW8XAAAAAPB4r9Sp1FFRUfrrr79SXZ7anZSftbi4OF27di3V5f/mTtavGk6lBgAAANIHp1K/YkeMM2bMmG7h91FsbW1fyLoAAAAAAP/irtQAAAAAALxKCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQrEwmkym9iwDu3o1UTEx8epeBF0CGDDbMBTAPIIl5gL8xFyAxD4zE1dX5uW+TI8YAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNNv0LgCQJPvx82Wf3kXghcFcgMQ8QCLmAZIwFyC9nPMgYkTv9C4BacARYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABiabXoXAAAAAABGtWbNSu3e/b0uX74ke3t7lS3rqd69+6tgwcIW/U6cOK6lSxfq999PyNraRiVKuGn27CDZ22fU4cOHNGBArxTHX7ZstUqVKpPq9h81bpIff9ynlSuX6fz5c8qQIYO8vcsrMHDWU9n/FwXBGAAAAADSyZEjh9W0aQuVLFla8fHxWrp0gQYP7qe1az9RpkyZJCWG13ff7a927Tpr0KD3ZGtro7Nnz8rKKvEE4LJlvfT5519ZjLt8+WIdOvSzSpYsneq2HzeuJO3a9a2mTZusnj37qHz5ioqPj9eFC+efwTuRvqxMJpMpvYuAccTHx8vKykrW1pZn8UcNmZ5OFQEAAADPTsSI3k/U//bt2/L3f0vz5y9VuXLlJUk9enRSxYqV1b172saKi4tT48b11Lx5K3Xq1C3Vfo8bNy4uTi1avK2uXXuoYcPGT7Qf/4Wrq/Nz21YSrjE2sC1btqhy5cqKiYmxaO/Tp4/ee+89SdI333yjJk2aqGzZsqpVq5bmz5+vuLg4c9+VK1fK399f5cqVU/Xq1TVu3Djdv3/fvDw4OFg+Pj769ttvVb9+fZUtW1YhISHPZwcBAACAl8z9+/ckSZkzZ5Yk3b59S7//fkLZsmVTr15d5O9fW/369dCxY0dTHWPfvt0KD7+r+vX9U+2TlnHPnDmlmzdvyMrKWp07v6NGjero3XcH6MKFc09lX18kBGMDq1u3ruLj4/Xtt9+a28LCwrR79241a9ZMhw4d0vDhw9WhQwft2LFDEyZMUHBwsBYvXmzub2VlpZEjR2rbtm2aOnWqfvrpJ82YMcNiO1FRUVq2bJkmTZqkbdu2KUeOHM9tHwEAAID0ZGWV9i+TKUHz5s2Sp6eXihUrLisrKSTkmiTpww+X6e23G2v27Hlyc3PXoEG9dfXqlRTH2bbtc1Wq9Jpy5cqV6rbSMu6ffyb1WaqOHbtq+vS5ypzZWf3791RExN0n2rcn+UoPXGNsYBkzZlTDhg0VHBysevXqSZK++OIL5cmTR5UrV1bnzp3Vo0cPNWnSRJJUoEABDRw4UDNmzFC/fv0kSZ06dTKPlz9/fg0aNEhjx47VuHHjzO2xsbEaN26cSpYs+dz2DQAAAHgRuLik/bTgsWPH6vLli1q/fr15vcyZE2+C1aZNa3Xs2FaSVKVKRR09+ou+++4rvfvuuxZjXL9+XQcP/qS5c+c+cttpGdfR0V6S1LdvH7Vo0ViSVKlSOb3++us6eHCfWrduneZ9e9ERjA2uZcuWat68uf766y/lypVLwcHBatKkiaysrHTq1CkdPnzY4ghxfHy8oqOj9eDBA2XKlEk//vijlixZogsXLujevXvJlkuSnZ2d3N3d02sXAQAAgHQTGhqRpn6zZk3Tvn17tGDBUtnaOprXs7V1kCTlypXPYqz8+Qvp4sUrycZfs2aDMmfOIi+vSo/cdlrGzZDBSZLk4pLXok+ePHl1/vylNO/bk3qSPyY8LQRjgytdurRKliypLVu2qGrVqjp37pyaNm0qSYqMjFT//v1Vu3btZOvZ29vr6tWr6tmzp9q0aaPBgwcrS5Ys+uWXXzRy5EjFxsaag3HGjBlllV7nRAAAAADp6HG3OjaZTJozZ7r27NmloKAlypMnn8U6uXPnlYuLqy5fvmzR/scfl/Xaa1Ut2kwmk7Zv36q6dRvIxsb2kdtOy7ju7iWVIUMGXblySZ6e5SQl3pDrzz//VK5ceR67by8TgjHUvHlzrV69Wn/99ZeqVKmiPHnySEoMzRcvXlShQoVSXO+3336TyWRSQECA+S7TX3755XOrGwAAAHjZzZo1Td9885UCA2fJwcFBYWGhkiQnJyfZ2yceYHrnnfZasWKJihcvoRIl3PXll9t0+fJlTZpk+WSXX375WX/+eU3+/o2TbefmzRsaOLC3Ro0ar9KlPdI0rqOjkxo1aqYVK5YqZ87cyp07t9avXyNJeuONN5/tG/OcEYwhf39/TZ8+XZs2bdL06X9/uPr27atevXopb968qlOnjqytrXXq1CmdOXNGgwcPVqFChRQbG6s1a9aoZs2a+uWXX/Txxx+n454AAAAAL5ctWzZLkvr372nR/v77Y813lW7Z8h1FR8coKGiOwsPvqnhxN82Zs0D58uW3WGfbts9VtqynChUqnGw7cXFxunLlsqKiosxtaRm3b9+BsrGx0cSJYxQdHa3Spcvogw8Wme+a/argOcaQJA0bNky7d+/W3r17lSFDBnP73r17tWDBAp08eVK2trYqWrSoWrRooZYtW0qSVq1apeXLlysiIkI+Pj7y9/fX8OHD9fPPPytz5swKDg7WlClTdOjQoUdun+cYAwAA4FX0pM8xRvo8x5hgDElSx44dVaJECY0aNSpdtk8wBgAAwKuIYPzk0iMY8xxjg7t796527typgwcP6p133knvcgAAAADgueMaY4Nr0qSJ7t69q6FDh6po0aLpXQ4AAAAAPHcEY4P77rvv0rsEAAAAAEhXnEoNAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQ7MymUym9C4CuHs3UjEx8eldBl4AGTLYMBfAPIAk5gH+xlyAxDwwEldX5+e+TY4YAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNBs07sAQJLsx8+XfXoXgRcGcwES8wCJmAdI8l/nQsSI3k+lDgCvJo4YAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxukkODhYPj4+j+wTFBSkRo0aPaeKnm0dV69elbu7u06ePPmUqgIAAHj64uLitHDhPHXo0EpvvumnRo3qauLEMQoNvWnRLzz8rsaPH6Xataurbt0aCgycoMjISIs+JpNJ69evUevWTfXGG75q3LieVq9e8cjtX7lyWQEBQ9SgQS3Vrl1dvXt31eHDh1Lse/fuHTVpUl9+fj6KiIj4bzsOGJxtehdgVPXr11f16tXTu4w06dKli9q1a5feZQAAADxzUVFROnPmlDp27KYSJUooPDxCH3wwU8OHD9GKFWvM/caPH62wsFDNmbNAcXFxCgwcr+nTJ2vcuMnmPh98MFMHD/6kfv0GqmjR4goPD1dExN1Hbn/YsMEqUKCAPvhgsezt7bVp0wYNGzZIGzduUY4cLhZ9p06dqGLFiuvmzRtP900ADIgjxukkY8aMypEjR3qX8Ugmk0lxcXFydHRUtmzZ0rscAACAZ87JyUlz5y5UrVpvqWDBwvLwKKshQ4bp9OmTun79uiTp0qWLOnDgRwUEjFKZMh7y8iqnQYPe07ff/s98ZPnSpYv67LPNmjp1lvz8qitv3nwqWbKUKlZ8LdVt37lzR1evXlG7dp1UvHgJFShQUL1791NUVJQuXDhv0fezzzYrIiJCbdq0f3ZvBmAgBONHSEhI0LJly/TWW2/Jw8NDNWrU0KJFiyRJp0+fVocOHeTp6anKlStr9OjRun//viRp3759Klu2rMLDwy3GmzRpkjp06CAp5VOply5dqipVqsjb21vvv/++oqOj01xrQECA+vTpo/nz5+u1115T+fLlNWbMGMXExFjsz5IlS1SzZk15enrq7bff1ldffWVefuDAAbm7u2v37t1q2rSpypYtq19++SXZqdQJCQmaP3++Xn/9dXl4eKhRo0bas2ePRT3Hjx9X48aNVbZsWTVt2pRTqAEAwEvr3r17srKykrOzkyTpxInjcnJyVsmSpc19fHwqydraWr/9dkKS9MMPe5Q3bz798MM+tWjxtpo399fUqRMVHp76EeMsWbKoYMFC+uqr7Xrw4IHi4uK0ZUuwsmXLLnf3UuZ+Fy9e0KpVyzRq1ARZWVk9o70GjIVg/AizZs3SsmXL1KdPH+3YsUMzZ86Ui4uLIiMj1bVrV2XJkkWbN2/W3Llz9eOPP2rixImSJF9fX2XOnFlff/21eaz4+Hh9+eWX8vf3T3FbO3bsUFBQkAYPHqxPP/1Urq6uWr9+/RPVu3//fp0/f15r1qzR7NmztXPnTi1YsMC8fMmSJdqyZYvGjx+v7du3q1OnTnrvvfd08ODBZPv97rvvaseOHXJ3d0+2nY8++kgrV67U8OHD9cUXX8jPz099+vTRpUuXJEn3799Xz549VaxYMQUHB6t///6aNm3aE+0LAADA02Rl9e++YmKitXhxkN58s46cnJxkZSXduhWmbNmyWfSzs7OVs3Nm3b4dJisrKSTkmv7667q+//4bjRo1XiNHjtXp0yc1atTwVLdlbW2lDz5YqDNnTqt27ddVq1ZVbdy4TrNnz1OWLJllZSXFxsZo/PiR6tt3oPLkya2kXPxv9+9l+jLKfvKVPr8juMY4Fffu3dNHH32kMWPGqEmTJpKkggULysfHR5s2bVJMTIymTZsmBwcHSdKYMWPUq1cvDR06VC4uLqpfv762bdumFi1aSEoMreHh4apTp06K2/voo4/UvHlzc//Bgwdr//79T3TUOEOGDJoyZYoyZcqkEiVKaMCAAZo+fboGDhyouLg4LVmyRCtXrpS3t7ckqUCBAvrll1+0ceNGVapUyTzOgAEDVLVq1VS3s2LFCnXv3l0NGjSQJL333ns6cOCAVq9erbFjx2rbtm1KSEjQlClTZG9vrxIlSuj69esaN25cmvcFAADgaXJxcU6x/YsvvtDYsWPNr5ctW2Y+qy82Nlb9+78nGxtrTZ06WU5OiUeMHR3tZWNjnWxMa2srOTray8XFWfb2toqJidHs2TNVpEgRSVK+fDnVtGlThYffVNGiRZPVYjKZNHr0MOXK5aqxY0crY8aM+uSTTxQQ8K42b96snDlzKjAwUG5uJdS2bStJUpYsif8WzZHDSZkzp7yPAB6PYJyKCxcuKCYmRq+9lvw6kPPnz8vd3d0ciiWpfPnySkhI0MWLF+Xi4iJ/f3+1atVKf/31l3LlyqWtW7eqRo0aypw5c4rbO3/+vFq3bm3RVq5cOR04cCDNNbu7uytTpkzm197e3oqMjNSff/6pyMhIPXjwQF26dLFYJzY2VqVKlbJoK1u2bKrbuHfvnm7cuKHy5ctbtJcvX16nTp0y74u7u7vs7e0tagEAAEgvoaEp37XZy6uSVq5cZ37t6uqq0NAIxcXFafToAIWEXNO8eYsUFWVSVFTiGBkzOiksLMxizLi4ON29e1f29k4KDY2Qo2MW2djYyNnZxdwva9ackqRTp84rc2bXZLUcOnRQu3bt0ldffSdHx8QQ3q/fu9q7d5/Wrduo9u07ad++H3XhwjnzmYkmk0mS9Nprr6lDhy7q1q3nf32rXlh2djaKjY1P7zLwHKT2h6xniWCciodD3b/h6empggULaseOHWrTpo127typqVOnPqXqnlzS4wOWLFmiXLlyWSzLkCGDxeuHwzUAAMCr4P/zYzIODo5ycHC0aIuNTQzFV69e0bx5S5Q5c1aL9cuU8VRERIROnjypkiUTDzD88sshJSQkqHRpD5lMUtmyXoqPj9fVq1eVL19+SdLly1ckSbly5UmxngcPov7/O2uL5VZWVkpISJDJJE2ePF3R0VHmZSdP/q7AwAlasGCZ8uXLn+p+vipe9f1D+uEa41QULlxYGTNm1E8//ZRsWbFixXT69GmLZ9UdPnxY1tbW5lNlJMnf319bt27Vd999J2tra9WoUSPV7RUrVkzHjh2zaPvn68c5ffq0oqL+/kV59OhROTg4KE+ePCpWrJgyZMigkJAQFSpUyOIrT548ad6Gk5OTcubMqcOHD1u0Hz58WMWLFzfvy+nTpy1OAz969OgT7QsAAEB6iIuL06hRiXehHjNmkhIS4hUWFqqwsFDFxsZKkgoXLqLKlato+vRJ+v33Ezp+/Khmz56uWrVqy8Ul8Uiwj08lubmVVGDgBJ05c0qnTp3UjBlTVLFiZRUsWEiS9PvvJ/TOO83Mj1vy8PCUs7OzJk8eq7Nnz+jKlctasOAD/flniHx9/SRJ+fLlV9Gixc1fefLklSQVKlRE2bJlf95vF/DK4IhxKuzt7dW9e3fNmDFDdnZ2Kl++vG7duqWzZ8/K399f8+bNU0BAgPr166dbt25p4sSJatSokVxc/n6+nL+/v4KCgrR48WLVqVMn2ZHZh3Xo0EEBAQHy8PBQ+fLltXXrVp09e1YFChRIc80xMTEaOXKkevfurWvXrikoKEjt2rWTtbW1nJyc1KVLFwUGBspkMqlChQqKiIjQ4cOH5eTkZL6OOi26du2qoKAgFSxYUCVLllRwcLBOnTqlmTNnSpIaNmyoOXPmaNSoUerZs6euXbumDz/8MM3jAwAApJebN29o377Ep2107vyOxbJ58xarfPnE64/Hjp2o2bOna+DAPrK2tlL16jU1aNB75r7W1taaPn2O5syZrr59eyhTpkx67bUq6tdvkLlPVFSUrly5rLi4OElS1qxZNWtWkJYuXaiBA3srLi5ORYoUVWDgLJUo4faM9xwwNoLxI/Tp00c2NjaaN2+ebty4IVdXV7Vu3VqZMmXSihUrNHnyZDVv3lyZMmVS7dq1FRAQYLF+oUKF5OnpqePHj+v9999/5Lbq16+vK1euaMaMGYqOjladOnXUpk0b7du3L831+vr6qlChQmrbtq1iYmLUsGFD9e/f37x80KBByp49u5YsWaKrV6/K2dlZpUuXVq9evZ7ofenQoYPu3bunqVOn6tatWypWrJgWLlyowoULS5IcHR21ePFijR07Vo0bN1bx4sU1dOhQi1oAAABeRHny5NW+fYce2y9z5iwaN27yI/u4uLhq8uQZqS4vX94n2bZKliyt2bPnp63YVMYA8OSsTCbO1H8VBAQEKDw8XAsXLkzvUv6VqCHT07sEAADwCosY0Tu9S8B/lCGDjWJiuPmWEbi6Pv+bb3GNMQAAAADA0DiV+iXxqMcdLVu27DlWAgAAAACvFoLxS2LLli2pLsuVK5f5QfQAAAAAgCdDMH5JFCpUKL1LAAAAAIBXEtcYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0K5PJZErvIoC7dyMVExOf3mXgBZAhgw1zAcwDSGIe4G/MBUjMAyNxdXV+7tvkiDEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMzTa9CwAkyX78fNmndxF4rIgRvdO7BAAAAOCp44gxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAN46mbMmCI/Px9t2rTeoj08/K7Gjx+l2rWrq27dGgoMnKDIyMg0jWkymfTuuwPk5+ejPXt2JVu+Y8dWdezYWjVrVlHDhm9p1qxpT2NXAAAAYAAEYzzWgQMH5O7urvDw8P80Ts2aNbVq1aqnUxReWLt3f6/ffjshFxfXZMvGjx+tixcvaM6cBZo2ba6OHTui6dMnp2ncTZvWy8oq5WUff7xWS5cuVNu2nbRmzSbNnbtQlSu/9l92AwAAAAZCMH6Mq1evyt3dXSdPnkzvUoAX3s2bNzR37gyNGTNRtra2FssuXbqoAwd+VEDAKJUp4yEvr3IaNOg9ffvt/xQaevOR4549e1off7xOI0aMSbYsPDxcy5Yt0qhR41W7dl3ly5dfxYuXkJ9f9ae6bwAAAHh1EYyfkpiYmPQuAUhXCQkJmjhxjNq0aa+iRYslW37ixHE5OTmrZMnS5jYfn0qytrbWb7+dSHXcqKgojR8/SkOGDFOOHC7Jlv/88wGZTCbdvHlDbds2V5Mm9TV6dID++uv609kxAAAAvPJe+GCckJCgZcuW6a233pKHh4dq1KihRYsWSZJOnz6tDh06yNPTU5UrV9bo0aN1//5987rt27fX5MmWp2n26dNHAQEB5tc1a9bU4sWLNWLECHl7e6tGjRrauHGjeXmtWrUkSY0bN5a7u7vat28vSQoICFCfPn20aNEi+fn5qW7dupo/f74aNmyYbB8aNWqkuXPnpml/P/nkE9WrV09ly5ZV3bp1tW7dOvOypKPX//vf/9S+fXt5eXnp7bff1pEjRyzG+OWXX8zLK1asqK5du+ru3buSEgP8pEmT5Ovrq7Jly6pNmzY6fvy4xfq7d+9WnTp15Onpqfbt2+vatWvJ6jx06JDeeecdeXp6qnr16po0aZLFtaJhYWHq1auXPD09VbNmTX3xxRdp2n+8vNatWy0bGxu1aNE6xeW3boUpW7ZsFm22trZyds6sW7fCUh133rxZ8vDwVLVqNVJcHhJyTQkJCVqzZqUGDHhXEydOU3h4uAYP7qvY2Nh/vT8AAAAwDtvHd0lfs2bN0ieffKIRI0aoQoUKunHjhi5evKjIyEh17dpV3t7e2rx5s8LCwjRq1ChNnDhRU6dOfaJtrFy5UgMGDFCvXr309ddfa9y4capYsaKKFi2qTz75RC1atNCqVatUvHhx2dnZmdfbv3+/nJyctHLlSkmSs7OzFixYoOPHj8vT01OS9Pvvv+v06dOaP3/+Y+v44osv9MEHH2jMmDEqVaqUTp48qdGjR8vBwUFNmjQx95szZ46GDx+uQoUKac6cOXr33Xf1v//9T7a2tjp58qQ6deqkZs2aaeTIkbKxsdGBAwcUHx8vSZo+fbq+/vprTZ06Vfny5dPy5cvVrVs3/e9//1PWrFn1559/ql+/fmrbtq1atmypEydOaNo0y5sYXblyRd27d9fAgQM1ZcoU3bp1SxMnTtTEiRMVGBgoKfEPBzdu3NBHH30kW1tbTZo0SWFhqYcfvBySrvH9+usvNWPGFHP7jBlz9cknH+vDD9fK2toqxXVSe53U9nB70vd79+7W4cOHtHLlumTLk16bTAmKi4vToEHvma8rHj9+st5+u46OHDmkypV9/9W+Iv2ldk05jIV5gCTMBUjMAzw7L3Qwvnfvnj766CONGTPGHAwLFiwoHx8fbdq0STExMZo2bZocHBwkSWPGjFGvXr00dOhQubgkP+UyNa+//rratm0rSerevbtWrVqlAwcOqGjRosqePbskKWvWrHJ1tbyZkIODgyZNmqQMGTKY2/z8/BQcHGwOxsHBwapYsaIKFCjw2DqCgoIUEBCg2rVrS5IKFCigc+fOaePGjRbBuEuXLqpRo4YkacCAAWrQoIEuX76sYsWKafny5fLw8NC4cePM/UuUKCFJioyM1Mcff6zAwEBVr554/eXEiRP1ww8/aPPmzerWrZs2bNigggULmo+qFy1aVGfOnNGyZcvM4y1ZskT+/v7q1KmTJKlw4cIaOXKk2rdvr3HjxikkJER79uzRJ598Yn4fJk+erPr16z/2PcCLzcXFWZLUqFF9+flVNrd/9dVXun37lpo1+/uMifj4eM2fP1effrpR3333nQoVyqe7d++Yx5CkuLg4RUSEq3Dh/BbtSX7//ZiuXbuqunXfsGgfOXKYfHx8tGbNGhUunF+SVKFCWfMYLi7OypYtm+7fv5PiuAAAAMDDXuhgfOHCBcXExOi115LfXfb8+fNyd3c3h2JJKl++vBISEnTx4sUnCsbu7u7m762srOTi4pKmo5tubm4WoViSWrZsqffff18jRoyQlZWVtm7dqhEjRjx2rMjISF25ckUjR47U6NGjze1xcXFydrb8h/3D9SaF9Vu3bqlYsWI6efKk6tatm+I2rly5otjYWJUvX97cZmdnJ09PT50/f15S4vuaFGaTlCtXzuL1qVOndPr0aW3dutXcZjKZlJCQoKtXr+rixYuytbWVh4eHeXmxYsWUOXPmx74PeLGFhkaYv3d0zG7+/s0368vbu5JF38GD+6tu3fqqX99foaERKlzYTeHh4dq376BKliwlSTpw4CclJCQof/5i5rHt7GwUG5t4hkPz5u/orbcs/6DSvn1rDRgwRFWrVlNoaISKFEn8PBw9+pt8fBwlJT4W6vbt23J0zGZRM14eD88DGBfzAEmYC5CYB0aSHgc2XuhgbG9v/5/Wt7KykslksmiLi4tL1u+fd89Nab2UZMqUKVnbG2+8oQwZMmjnzp2ys7NTXFxcqkH1YUnX506cOFFeXl4Wy6ytLS8Ff/h0bqv/P58kISFBkpQxY8bHbuu/ioyMVOvWrc3XWz8sT548unjx4jOvAekjtY9F5sxZlTlzVos2W1tbZc+eQwULFpbJJBUqVESVK1fRtGmTNHToCMXFxWn27OmqVau2XFxcZTIl3tV60KA+GjlynEqX9lD27C7Knj35H7ly5sytPHnyyWSSChQopGrVqmvu3JkaNmykHB0dtXjxAhUsWFjly/ukWjNefPzsIDEP8DfmAiTmAZ6dF/rmW4ULF1bGjBn1008/JVtWrFgxnT592uKGT4cPH5a1tbWKFCkiScqePbtu3vz7MTDx8fE6e/bsE9WQFEKTrtF9HFtbWzVu3FjBwcEKDg5WgwYN0hRWXVxclDNnTv3xxx8qVKiQxVdaTsNO4u7urv3796e4rGDBgrKzs9Phw4fNbbGxsfr1119VvHhxSYnv66+//mqx3rFjxyxely5dWufOnUtWZ6FChZQhQwYVLVpUcXFxOnHi7zsNX7hw4T8/Bxkvv7FjJ6pgwcIaOLCP3ntvoDw9vTRs2Ejz8ri4OF2+fElRUVFPNO6oUeNVurSH3ntvkPr16ylbW1vNmjUv2R+9AAAAgJS80P9qtLe3V/fu3TVjxgzZ2dmpfPnyunXrls6ePSt/f3/NmzdPAQEB6tevn/kGUI0aNTKfRv3aa69p6tSp2rVrlwoUKKBVq1Y9cTjLkSOHMmbMqL179yp37tyyt7dPdmrzP7Vo0cJ8Pe2GDRvSvK0BAwZo0qRJcnZ2VrVq1RQTE6MTJ04oPDxcnTt3TtMYPXr0kL+/v8aNG6fWrVvLzs5OBw4cUN26dZU9e3a1adNG06dPV5YsWZQ3b14tX75cUVFRat68uSSpdevW+vDDDzVt2jS1aNFCv/32mz777DOLbXTv3l2tWrXShAkT1KJFC2XKlEnnzp3Tjz/+qDFjxqho0aKqVq2axo4dq3HjxsnGxkZTpkx5Lkez8eLYvHlrsrbMmbNo3LjJKfROlCdPXh08eEQxMan/IWrfvkPJ2hwdnTRixJgUn3MMAAAAPM4LHYylxMcr2djYaN68ebpx44ZcXV3VunVrZcqUSStWrNDkyZPVvHlzZcqUSbVr17Z4FFOzZs106tQpDR8+XDY2NurUqZMqV678iK0lZ2trq1GjRmnBggWaN2+e+YY/j1K4cGF5e3vr7t27yU6LfpQWLVooY8aMWrFihaZPny4HBwe5ubmpY8eOaR6jSJEi+vDDDzV79mzzeJ6enubHSA0dOlQmk0nDhg3T/fv35eHhoeXLlytLliySpLx58yooKEiBgYFau3atPD09NXjwYL3//vvmbZQsWVJr1qzR3Llz9c4770hKvFHYwzfXCgwM1KhRo9SuXTu5uLho4MCBmjdvXpr3AwAAAACeFytTWi6mxRMxmUyqXbu23nnnnTQf6TW6qCHT07sEpEHEiN7PfBsZMtg88ogxjIF5AIl5gL8xFyAxD4zE1ZWbb730bt26pe3btys0NFRNmzZN73IAAAAAAI9BMH7KfH19lS1bNk2YMMF8enISb2/vVNdbtmyZfHx8nnV5AAAAAIB/IBg/ZadPn0512ZYtW1JdlitXrmdQDQAAAADgcQjGz1GhQoXSuwQAAAAAwD+80M8xBgAAAADgWSMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQ7NN7wIASYoe208xMfHpXQYAAAAAA+KIMQAAAADA0AjGAAAAAABDIxgDAAAAAAyNYAwAAAAAMDSCMQAAAADA0AjGAAAAAABDIxgDAAAAAAyNYAwAAAAAMDSCMQAAAADA0AjGAAAAAABDIxgDAAAAAAyNYAwAAAAAMDTb9C4AkCT78fNln95FpKOIEb3TuwQAAADAsDhiDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQbNO7AACpi4uL09KlC/XTTz8oJOSaHB2d5ONTSb1795eLi2uy/jExMerRo5POnTujlSvXqUQJ91TH7tevh44ePWzR1qhRU7333vvm13PnztDx48d08eJ5FSpURKtWrX96OwcAAAC8IAjGr4D27durZMmSGjlyZLps/+rVq6pVq5a2bNmiUqVKpUsNr6qoqCidOXNKHTt2U4kSJRQeHqEPPpip4cOHaMWKNcn6L1w4Ty4uLjp37kyaxvf3b6Ju3XqaX2fMmDFZnwYN3tbvv5/Q+fPn/v2OAAAAAC8wgjHwAnNyctLcuQst2oYMGabu3Tvq+vXryp07t7l9//4f9PPPP2nSpOn66acf0zR+xowZlSOHS6rLBw16T5J0585tgjEAAABeWQRj4CVz7949WVlZydnZydx261aYpk+frMDAmSke9U3Nzp1f6n//26Hs2XOoatXX1alTtydaHwAAAHgVEIxfMpGRkRo3bpx27twpR0dHdenSxWJ5TEyM5syZo23btikiIkIlSpTQ0KFDVblyZXOfX375RXPnztXx48eVIUMGeXp6avbs2cqSJYv27NmjRYsW6ezZs7KxsVG5cuU0cuRIFSxY0Lz+8ePHNWbMGJ0/f14lSpRQ7969k9V55swZTZ8+Xb/88osyZcqkqlWrasSIEcqePfuze3NeYlZWaesXHR2txYuD9OabdeTklBiMTSaTpkwZr8aNm6pUqdL688+QNI1bu3Zd5c6dRy4urjp37qwWLQrSlSuXFRg4I9X60lrnf/W8toMXG/MAEvMAf2MuQGIe4NkhGL9kpk+frp9//lkLFy5U9uzZNWfOHP32228qWbKkJGnChAk6d+6c5syZo5w5c2rnzp3q1q2btm7dqsKFC+vkyZPq1KmTmjVrppEjR8rGxkYHDhxQfHy8JOnBgwfq3Lmz3N3dFRkZqQ8++EB9+/bV559/Lmtra92/f189e/ZUlSpVNGPGDF29elWTJ0+2qDE8PFwdO3ZUixYtNGLECEVHR2vmzJkaNGiQPvroo+f+nr0MXFycJUlffPGFxo4da25ftmyZfHx8JEmxsbHq3/892dhYa+rUyeZg/NFHHykmJkqDBw+QjY2NoqIcJUnZsjmax01J164dzd9XruytokULqFOnToqMvG3xhxBJcnCwl62t9SPHAwAAAF5WBOOXyP3797V582bNmDFDvr6+kqSpU6eqevXqkqSQkBAFBwfr+++/V65cuSRJXbt21d69exUcHKwhQ4Zo+fLl8vDw0Lhx48zjlihRwvx9nTp1LLY5ZcoU+fr66ty5c3Jzc9O2bduUkJCgKVOmyN7eXiVKlND169ctxlu7dq1Kly6tIUOGWIxTvXp1Xbx4UUWKFHnab81LLzQ0QpLk5VVJK1euM7e7uroqNDRCcXFxGj06QCEh1zRv3iJFRZkUFZW4zp49+3T06FGVLVvWYsxmzZrprbfqavTo8WmqIX/+YpKkX389JQeHbBbLIiOjFReXYK7zWbKzs1FsbPwz3w5ebMwDSMwD/I25AIl5YCTpcTCGYPwS+eOPPxQbGysvLy9zW9asWc1B88yZM4qPj1fdunUt1ouJiVHWrFklSSdPnky2/GGXLl3SvHnzdOzYMd2+fVsmk0mS9Oeff8rNzU3nz5+Xu7u77O3tzet4e3tbjHHq1CkdOHAgWbskXblyhWCcgv9/m+Xg4CgHB0eLZbGxiaH46tUrmjdviTJnzmruL0kDB76n7t3/Pp09NDRUQ4b00/jxU1S6tIdF30c5c+a0JCl7dpdk6yS9TutY/9Xz2g5ebMwDSMwD/I25AIl5gGeHYPwKiYyMlI2NjT799FPZ2NhYLHNwcJCU8uN4HtarVy/ly5dPkyZNUs6cOZWQkKCGDRsqNjb2iep44403NHTo0GTLXF2TP3sXqYuLi9OoUcN05sxpTZs2RwkJ8QoLC5UkZc6cRXZ2dhZ3ppakTJkSf9b58uVXzpyJZw7cvHlDAwf21qhR41W6tIeuXbuqnTu/0muvVVWWLFl0/vxZzZs3W+XKlVfx4n+fQXD16h968CBSt26FKTo6SmfPJobnwoWLys7O7nm8BQAAAMAzRzB+iRQoUEB2dnY6duyY8ubNK0m6e/euLl26pIoVK6pUqVKKj4/XrVu3zNel/pO7u7v279+vAQMGJFt2+/ZtXbx4UZMmTTKvf+jQIYs+xYoV0+eff67o6GjzUeOjR49a9ClTpoy+/vpr5cuXT7a2TLH/4ubNG9q3b48kqXPndyyWzZu3WOXLp/xz/qe4uDhduXJZUVFRkiRbW1sdOnRQmzZtUFTUA+XMmUs1atRUx45dLdabOnWijh49bH7duXNbSdInn3yhPHny/uv9AgAAAF4kpJaXiKOjo5o1a6YZM2Yoa9asypEjh+bMmSOr/789X5EiReTv769hw4YpICBApUqV0u3bt7V//365u7urRo0a6tGjh/z9/TVu3Di1bt1adnZ2OnDggOrWrausWbMqa9as2rhxo1xdXRUSEqJZs2ZZ1NCwYUPNmTNHo0aNUs+ePXXt2jV9+OGHFn3eeecdbdq0SUOGDFG3bt2UNWtWXb58WTt27NCkSZOSHc1G6vLkyat9+w49vuNj1vlnW65cuTV//tLHjpWWPgAAAMDLzjq9C8CTGTZsmCpUqKDevXurc+fOqlChgjw8PMzLAwMD1bhxY02dOlX16tVTnz599OuvvypPnjySEsPzhx9+qFOnTqlFixZq3bq1vv32W9na2sra2tp8l+uGDRsqMDBQw4YNs9i+o6OjFi9erDNnzqhx48aaM2dOslOmc+XKpQ0bNighIUFdu3aVv7+/pkyZImdnZ1lbM+UAAAAAvFisTCYuYUf6ixoyPb1LSFcRI5I/C9qoMmSwUUwMd5w0OuYBJOYB/sZcgMQ8MBJX1+d/V2oO3wEAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADM02vQsAJCl6bD/FxMSndxkAAAAADIgjxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA02/QuAJAk+/HzZZ/eRfxDxIje6V0CAAAAgOeAI8YAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIx8IR27/5Ogwf3Vf36teTn56OzZ0+n2tdkMunddwfIz89He/bseuS4JpNJy5cvVqNGdVSzZlUNHNhHf/xxxaLPlSuXFRAwRA0a1FLt2tXVu3dXHT586GnsFgAAAGBYBOOX1IEDB+Tu7q7w8PA0r7NkyRI1a9ZM3t7e8vX1VZ8+fXThwgWLPtHR0Ro/frwqV64sb29v9e/fX6GhoRZ9QkJC1KNHD3l5ecnX11fTpk1TXFxcsvqaNGkiDw8PvfXWWwoODv73O/uCefDggTw9y6l37/6P7btp03pZWaVt3HXrVmvz5o81dOgILV26SpkyZdSQIf0VHR1t7jNs2GDFx8frgw8Wa8WKNSpe3E3Dhg1SWFjoI0YGAAAA8CgEYwM5ePCg2rZtq02bNmnlypWKi4tT165dFRkZae4zZcoUff/995o7d67WrFmjGzduqF+/fubl8fHx6tmzp2JjY/Xxxx9r6tSp+uyzzzRv3jxznz/++EM9e/ZU5cqV9fnnn6tjx44aNWqU9u7d+1z391mpW7eBOnfuLh+fSo/sd/bsaX388TqNGDHmsWOaTCZ98skGdejQVdWq1VDx4iU0atQEhYXd1N69uyRJd+7c0dWrV9SuXScVL15CBQoUVO/e/RQVFaULF87/9x0DAAAADIpg/Ay1b99eEyZM0IQJE1ShQgVVrlxZc+fOlclkkiTdvXtXw4YNU8WKFeXl5aVu3brp0qVL5vWvXbumXr16qWLFiipXrpwaNGig3bt36+rVq+rQoYMkqWLFinJ3d1dAQMBj61mxYoWaNm2qEiVKqGTJkpo6dapCQkL022+/SZIiIiL06aefKiAgQL6+vvLw8NCUKVN05MgRHT16VJK0b98+nTt3TjNmzFCpUqVUvXp1DRw4UOvWrVNMTIwk6eOPP1b+/PkVEBCgYsWKqV27dqpTp45WrVr19N7cF1xUVJTGjx+lIUOGKUcOl8f2Dwm5prCwMFWs+HfYdnJyUunSHjpx4ldJUpYsWVSwYCF99dV2PXjwQHFxcdqyJVjZsmWXu3upZ7YvAAAAwKvONr0LeNV99tlnat68uT755BOdOHFCY8aMUd68edWyZUsFBATo8uXLWrRokZycnDRjxgz16NFD27dvl52dnSZMmKDY2FitXbtWDg4OOnfunBwcHJQnTx4FBQWpf//++uqrr+Tk5KSMGTM+cW0RERGSEgOXJJ04cUKxsbGqUqWKuU+xYsWUN29eHT16VOXKldPRo0fl5uYmF5e/w56fn5/GjRunc+fOqXTp0jp69Kh8fX0ttuXn56cpU6b8m7cw3TzuFOiHl/+zb1DQLHl4eOr112tY9EltzNu3wyRJ2bPnsOiTPXt23boV9v/rWumDDxYqIGCoatd+XdbW1sqaNZtmz56nLFkyP8GevfjSevo5Xm3MA0jMA/yNuQCJeYBnh2D8jOXJk0fvv/++rKysVLRoUZ05c0arVq1SpUqV9N1332nDhg0qX768JGnmzJmqUaOGvvnmG9WrV08hISGqU6eO3N3dJUkFChQwj5sUZnPkyKHMmZ88FCUkJGjKlCkqX7683NzcJEmhoaGys7NLNl6OHDl08+ZNc5+HQ7Ek8+vH9bl3756ioqL+VYhPDy4uzvriiy80duxYc9uyZcvk4+MjSYqKcpQkZcvmKBcXZ3Ofb7/9VkePHtZnn30mR0dHc3vmzJks+j0sSxYHSVL27JZjZchgKysrK7m4OMtkMmn06GHKlctVY8eOVsaMGfXJJ58oIOBdbd68WTlz5nx6Ow8AAAAYCMH4GfPy8pLVQ3/aKleunFauXKlz587J1tZWXl5e5mXZsmVTkSJFdP584vWiHTp00Lhx47Rv3z5VqVJFtWvXVsmSJZ9KXePHj9fZs2e1fv36pzLeqyg0NEJeXpW0cuU6c5urq6tCQxOPtN++fd/836Q2Sfr++726cuWKKlasaDFe//795eVVTvPnL022LRubTJKkc+euyNo6k7n9+vUbKlHCTaGhETp06KB27dqlr776To6OTpKkfv3e1d69+7Ru3Ua1b9/p6ex4OrOzs1FsbHx6l4F0xjyAxDzA35gLkJgHRpLawaRniWD8AmvRooX8/Py0a9cu/fDDD1q6dKmGDx+u9u3b/6dxJ0yYoF27dmnt2rXKnTu3ud3FxUWxsbEKDw+3OGocFhYmV1dXc5/jx49bjJd01+qH+/zzTtahoaH/+pTv9GIySQ4OjnJwcEzW/vB///l9u3Yd5e/fyGKdDh1aq3//IapatZpF3yR58uRTjhw5dOjQzypRIvEMgfv37+n330+oceNmMpmkBw+i/r+3tcUYVlZWSkhISHHcl9WrtC/495gHkJgH+BtzARLzAM8ON996xv4ZIo8dO6ZChQqpePHiiouL07Fjx8zLbt++rYsXL6p48eLmtjx58qhNmzaaP3++OnfurE2bNkmS7OzsJCXeJTqtTCaTJkyYoJ07d2r16tUWp2ZLkoeHh+zs7LR//35z24ULFxQSEqJy5cpJSjzifebMGYWFhZn7/Pjjj3JycjLXXa5cOf30008WY//444/mMV524eF3dfbsaV26lPioqytXLuvs2dPmRyblyOGiokWLW3xJUq5cuZU3bz7zOO+800y7d38vKTHctmjRRqtXr9C+fbt1/vw5TZo0VjlyuKpatRqSJA8PTzk7O2vy5LE6e/aMrly5rAULPtCff4bI19fvOb4DAAAAwKuFI8bPWEhIiAIDA9WqVSv9/vvvWrt2rYYPH67ChQurVq1aGj16tMaPHy8nJyfNnDlTuXLlUq1atSRJkydP1uuvv67ChQsrPDxcBw4cULFixSRJ+fLlk5WVlXbt2qXq1avL3t7e4nrWlIwfP17btm3TwoUL5ejoaL4m2NnZWRkzZpSzs7OaNWumqVOnKkuWLHJyctKkSZPk7e1tDrV+fn4qXry4hg0bpvfee083b97U3Llz1bZtW2XIkEGS1Lp1a61bt07Tp09Xs2bN9NNPP+nLL7/UkiVLntG7/Hzt27dHU6aMN78eO/Z9SVLnzt3VtWvPNI9z5cpl3b9/z/y6bduOioqK0vTpU3TvXoTKli2nWbPmyd7eXpKUNWtWzZoVpKVLF2rgwN6Ki4tTkSJFFRg4SyVKuD2lvQMAAACMx8pk4oSEZ6V9+/YqXry4EhIStG3bNtnY2KhNmzYaNGiQrKysdPfuXU2ePFnfffedYmNj5ePjo9GjR6tw4cKSpIkTJ2rPnj26fv26nJycVK1aNY0YMULZsmWTJC1YsEAbNmxQaGioGjdurKlTpz6ynqSbeP1TYGCgmjZtKkmKjo7W1KlTtX37dsXExMjPz09jx441nyYtJT5Gaty4cTp48KAyZcqkJk2a6N1335Wt7d9/Zzlw4IACAwN17tw55c6dW3369DFvIyVRQ6an6T19niJG9E7vEgwpQwYbxcRw/ZDRMQ8gMQ/wN+YCJOaBkbi6Pv9rjAnGz1D79u1VsmRJjRw5Mr1LeeERjJGE/+lBYh4gEfMASZgLkJgHRpIewZhrjAEAAAAAhsY1xq+IkJAQNWjQINXl27dvV968eZ9jRQAAAADwciAYP0Nr1qx5btvKmTOntmzZ8sjlAAAAAIDkCMavCFtbWxUqVCi9ywAAAACAlw7XGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGAMAAAAADI1gDAAAAAAwNNv0LgCQpOix/RQTE5/eZQAAAAAwII4YAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNAIxgAAAAAAQyMYAwAAAAAMjWAMAAAAADA0gjEAAAAAwNBs07sAQJLsx8+XfRr7Rozo/UxrAQAAAGAsHDEGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGM8UqaMWOK/Px8tGnT+kf2W7Nmpbp166C33npdDRu+pREj3tWVK5cs+kRHR2vWrGmqX7+W3nqrmkaOfE+3boU9w+oBAAAAPE8E45fUgQMH5O7urvDw8DSvs2TJEjVr1kze3t7y9fVVnz59dOHCBYs+0dHRGj9+vCpXrixvb2/1799foaGhFn0mTZqkpk2bysPDQ40aNUpxW6dOndI777yjsmXLqnr16lq2bNmT7+S/tHv39/rttxNycXF9bN8jRw6radMWWrJkpebMWaC4uDgNHtxPDx48MPcJCpqtH37Yo4kTpyooaKlCQ0M1cuR7z3IXAAAAADxHBGMDOXjwoNq2batNmzZp5cqViouLU9euXRUZGWnuM2XKFH3//feaO3eu1qxZoxs3bqhfv37JxmrWrJnq16+f4nbu3bunrl27Km/evAoODtawYcM0f/58bdy48ZntW5KbN29o7twZGjNmomxtbR/bf/bsINWv76+iRYupRAk3vf/+OP3113WdPn1SUuK+bNv2ufr3H6wKFSqqZMlSev/9sfr11+M6ceLXZ707AAAAAJ6DxycH/Gvt27dXiRIlJEmff/65bG1t1aZNGw0cOFBWVla6e/euJk+erO+//14xMTGqWLGiRo0apcKFC0uSrl27pokTJ+qXX35RbGys8uXLp2HDhqlYsWLq0KGDJKlixYqSpCZNmmjq1KmPrGfFihUWr6dOnSpfX1/99ttvqlixoiIiIvTpp59q5syZ8vX1lZQYlOvXr6+jR4+qXLlykqRRo0ZJkm7duqXTp08n284XX3yh2NhYTZkyRRkyZFCJEiV08uRJrVy5Uq1atfp3b2YaJCQkaOLEMWrTpr2KFi32r8a4f/+eJClz5sySpNOnTyouLk4+PpXNfQoVKqxcuXLrt9+Oy8Oj7H8vHAAAAEC6Ihg/Y5999pmaN2+uTz75RCdOnNCYMWOUN29etWzZUgEBAbp8+bIWLVokJycnzZgxQz169ND27dtlZ2enCRMmKDY2VmvXrpWDg4POnTsnBwcH5cmTR0FBQerfv7+++uorOTk5KWPGjE9cW0REhCQpS5YskqQTJ04oNjZWVapUMfcpVqyY8ubNaxGMH+fo0aPy8fFRhgwZzG1+fn5atmyZ7t69a97ev2VllXL7unWrZWNjo5YtW1v0Sa3/PyUkJGjevFny9PRSsWLFJUm3boXJzs5OmTM7W/TNnj27bt0KS/PYeDK8r5CYB0jEPEAS5gIk5gGeHYLxM5YnTx69//77srKyUtGiRXXmzBmtWrVKlSpV0nfffacNGzaofPnykqSZM2eqRo0a+uabb1SvXj2FhISoTp06cnd3lyQVKFDAPG5SuMyRI4f56OaTSEhI0JQpU1S+fHm5ublJkkJDQ/8/BFqOlyNHDt28eTPNY4eGhip//vwWbS4uLuZl/zUYu7g464svvtDYsWPNbUuWLNGnn25UcHCwXF0T67exsZajo71cXJxTG8rC2LFjdfnyRa1fv968jrNzRvM2H2Zra6NMmTKkeWwAAAAALy6C8TPm5eUlq4f+tFWuXDmtXLlS586dk62trby8vMzLsmXLpiJFiuj8+fOSpA4dOmjcuHHat2+fqlSpotq1a6tkyZJPpa7x48fr7NmzWr/+0XdtfhGFhkbIy6uSVq5cZ2777rtvFBYWpjfeeMPcFh8fr2nTpmnlylX69NOtjxxz1qxp2rdvjxYsWCpbW0eFhiYeTc+QwVGxsbG6eDFEzs5/h+AbN24qUyZncz88PXZ2NoqNjU/vMpDOmAeQmAf4G3MBEvPASNLj4BPB+AXWokUL+fn5adeuXfrhhx+0dOlSDR8+XO3bt/9P406YMEG7du3S2rVrlTt3bnO7i4uLYmNjFR4ebnHUOCwsTK6uj7/D88Pj/PNO1kmvk44c/xcmk+Tg4CgHB0dz29tvN1HVqtUs+g0Z0l916tRXgwb+MplSG8ukOXOma8+eXQoKWqI8efJZ9HVzKyVbW1sdOnRQNWrUkiRduXJJf/11XWXKeKY6Lv4b3ldIzAMkYh4gCXMBEvMAzw53pX7Gjh8/bvH62LFjKlSokIoXL664uDgdO3bMvOz27du6ePGiihcvbm7LkyeP2rRpo/nz56tz587atGmTJMnOzk5S4lHRtDKZTJowYYJ27typ1atXW5yaLUkeHh6ys7PT/v37zW0XLlxQSEhImq8vlhKPih86dEixsbHmth9//FFFihT5z6dRpyZLlqwqWrS4xZetra1y5MihggULm/sNHNhbn376992xZ82apv/970uNHTtJDg4OCgsLVVhYqKKjoyRJTk5OatiwkYKC5ujw4UM6deqkpkyZIA8PT268BQAAALwiOGL8jIWEhCgwMFCtWrXS77//rrVr12r48OEqXLiwatWqpdGjR2v8+PFycnLSzJkzlStXLtWqlXhkcvLkyXr99ddVuHBhhYeH68CBAypWLPFuy/ny5ZOVlZV27dql6tWry97eXo6Ojo8qRePHj9e2bdu0cOFCOTo6mq8bdnZ2VsaMGeXs7KxmzZpp6tSpypIli5ycnDRp0iR5e3tbBOPLly8rMjJSN2/eVFRUlE6eTHy0UbFixZQhQwb5+/trwYIFGjlypLp3766zZ8/qo48+0ogRI57BO/xkrl27qjt37phfb9myWZLUv39Pi37vvz9W9ev7//+yIbKystbIkcMUGxujSpV89e67w59bzQAAAACeLSuTiRMSnpX27durePHiSkhI0LZt22RjY6M2bdpo0KBBFo9r+u677xQbGysfHx+NHj3a/LimiRMnas+ePbp+/bqcnJxUrVo1jRgxQtmyZZMkLViwQBs2bFBoaKgaN2782Mc1Jd3E658CAwPVtGlTSVJ0dLSmTp2q7du3KyYmRn5+fho7dqzFqdTt27fXwYMHk43z7bffmm+6derUKU2YMEG//vqrsmXLpnbt2qlHjx6p1hY1ZPoja39YxIjeae6Ll0+GDDaKieH6IaNjHkBiHuBvzAVIzAMjcXV9/tcYE4yfofbt26tkyZIaOXJkepfywiMYIwn/04PEPEAi5gGSMBcgMQ+MJD2CMdcYAwAAAAAMjWuMXxEhISFq0KBBqsu3b9+uvHnzPseKAAAAAODlQDB+htasWfPctpUzZ05t2bLlkcsBAAAAAMkRjF8Rtra2KlSoUHqXAQAAAAAvHa4xBgAAAAAYGsEYAAAAAGBoBGMAAAAAgKERjAEAAAAAhkYwBgAAAAAYGsEYAAAAAGBoBGMAAAAAgKERjAEAAAAAhkYwBgAAAAAYGsEYAAAAAGBoBGMAAAAAgKERjAEAAAAAhkYwBgAAAAAYGsEYAAAAAGBotuldACBJ0WP7KSYmPr3LAAAAAGBAHDEGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGRjAGAAAAABgawRgAAAAAYGgEYwAAAACAoRGMAQAAAACGZmUymUzpXQQAAAAAAOmFI8YAAAAAAEMjGAMAAAAADI1gDAAAAAAwNIIxAAAAAMDQCMYAAAAAAEMjGCPdrVu3TjVr1lTZsmXVokULHT9+PL1LwjMUFBQkd3d3i6+6deual0dHR2v8+PGqXLmyvL291b9/f4WGhqZjxXgafv75Z/Xq1Ut+fn5yd3fXN998Y7HcZDLpgw8+kJ+fnzw9PdWpUyddunTJos+dO3f07rvvqnz58vLx8dH777+v+/fvP8e9wNPwuLkQEBCQ7HdE165dLfowF15uS5YsUbNmzeTt7S1fX1/16dNHFy5csOiTlv8XhISEqEePHvLy8pKvr6+mTZumuLi457kr+I/SMhfat2+f7HfCmDFjLPowF/A0EIyRrnbs2KHAwED17dtXn332mUqWLKmuXbsqLCwsvUvDM1SiRAnt27fP/LV+/XrzsilTpuj777/X3LlztWbNGt24cUP9+vVLx2rxNERGRsrd3V1jx45NcfmyZcu0Zs0ajRs3Tps2bVKmTJnUtWtXRUdHm/sMHTpU586d08qVK7V48WIdOnQo2T+O8OJ73FyQpGrVqln8jpg9e7bFcubCy+3gwYNq27atNm3apJUrVyouLk5du3ZVZGSkuc/j/l8QHx+vnj17KjY2Vh9//LGmTp2qzz77TPPmzUuPXcK/lJa5IEktW7a0+J0wbNgw8zLmAp4aE5COmjdvbho/frz5dXx8vMnPz8+0ZMmSdKwKz9K8efNMb7/9dorLwsPDTWXKlDF9+eWX5rZz586Z3NzcTEeOHHlOFeJZc3NzM+3cudP8OiEhwVS1alXT8uXLzW3h4eEmDw8P07Zt20wm09/z4Pjx4+Y+u3fvNrm7u5uuX7/+/IrHU/XPuWAymUzDhw839e7dO9V1mAuvnrCwMJObm5vp4MGDJpMpbf8v2LVrl6lkyZKmmzdvmvusX7/eVL58eVN0dPRzrR9Pzz/ngslkMrVr1840adKkVNdhLuBp4Ygx0k1MTIx+++03ValSxdxmbW2tKlWq6MiRI+lYGZ61y5cvy8/PT7Vq1dK7776rkJAQSdKJEycUGxtrMSeKFSumvHnz6ujRo+lULZ61q1ev6ubNmxY/d2dnZ3l5eZl/Fxw5ckSZM2dW2bJlzX2qVKkia2trLr94BR08eFC+vr6qU6eOxo4dq9u3b5uXMRdePREREZKkLFmySErb/wuOHj0qNzc3ubi4mPv4+fnp3r17Onfu3PMrHk/VP+dCkq1bt6py5cpq2LChZs2apQcPHpiXMRfwtNimdwEwrtu3bys+Pl45cuSwaM+RI0ey60vw6vD09FRgYKCKFCmimzdvasGCBWrbtq22bt2q0NBQ2dnZKXPmzBbr5MiRQzdv3kynivGsJf1sU/pdkHRNYWhoqLJnz26x3NbWVlmyZGFuvGKqVaumt956S/nz59cff/yh2bNnq3v37tq4caNsbGyYC6+YhIQETZkyReXLl5ebm5skpen/BaGhoRZBSJL5NfPg5ZTSXJCkhg0bKm/evMqZM6dOnz6tmTNn6uLFi5o/f74k5gKeHoIxgOeqevXq5u9LliwpLy8vvfHGG/ryyy+VMWPGdKwMwIugQYMG5u+TbrTz5ptvmo8i49Uyfvx4nT171uJeEzCm1OZCq1atzN+7u7vL1dVVnTp10pUrV1SwYMHnXSZeYZxKjXSTLVs22djYJLvRVlhYWLK//OHVlTlzZhUuXFhXrlyRi4uLYmNjFR4ebtEnLCxMrq6u6VQhnrWkn+2jfhe4uLjo1q1bFsvj4uJ09+5d5sYrrkCBAsqWLZsuX74sibnwKpkwYYJ27dql1atXK3fu3Ob2tPy/wMXFJdldqpNeMw9ePqnNhZR4eXlJksXvBOYCngaCMdJNhgwZVKZMGe3fv9/clpCQoP3798vb2zsdK8PzdP/+ff3xxx9ydXWVh4eH7OzsLObEhQsXFBISonLlyqVfkXim8ufPL1dXV4uf+71793Ts2DHz7wJvb2+Fh4frxIkT5j4//fSTEhIS5Onp+dxrxvNz/fp13blzx/wPXObCy89kMmnChAnauXOnVq9erQIFClgsT8v/C8qVK6czZ85Y/EHtxx9/lJOTk4oXL/5c9gP/3ePmQkpOnjwp6e/Qy1zA08Kp1EhXnTt31vDhw+Xh4SFPT0+tXr1aDx48UNOmTdO7NDwj06ZN0xtvvKG8efPqxo0bCgoKkrW1tRo2bChnZ2c1a9ZMU6dOVZYsWeTk5KRJ/9fenQfndD1+HH+nISIRIkisDaIescauJKWVSVQGTSRtlVDaWqNqSywhiXzV2okl1qIm6EwjghJt0JahYytaJtYETSwhxkSMZMj2+yPj/hqSCF+trz6f14wZz7kn5557njuT+dxzzs1//kPbtm0VjF9x9+/fJzU11fh89epVzp49S7Vq1ahbty6DBw9mxYoVODs7U79+fRYvXoyjoyOenp5A0Yt3PDw8mDFjBhEREeTm5hIZGYmPjw9OTk4v67LkOZR1L1SrVo3o6Gi8vb2pWbMmaWlpLFiwAGdnZzw8PADdC/8GERER7Ny5k+XLl2Nra2vsA7Wzs8Pa2rpcvwvc3d1p0qQJwcHBTJ48mYyMDBYtWsTAgQOxsrJ6iVcnz+Jp90Jqaio7duyge/fu2Nvbc/78eebMmUPHjh1p1qwZoHtBXhyLwsLCwpfdCTFvGzduZO3atWRkZODq6kpoaKixTEb+fcaPH8+xY8fIzMzEwcGB9u3bM378eGOf0IMHD5g7dy4JCQk8fPgQd3d3wsLCtBzqFXfkyBEGDx78RLmvry9z586lsLCQJUuWEBsbS1ZWFu3btycsLIxGjRoZdTMzM4mMjOTnn3/mtddew8vLi9DQUGxtbf/JS5H/Uln3Qnh4OGPGjOHMmTPcu3cPR0dHunXrxrhx44ptsdG98GozmUwlls+ZM8d4MF6e3wXXrl0jPDyco0ePUrlyZXx9fZk4cSIVKmje51XxtHvhxo0bTJ48mYsXL5KdnU2dOnXw9PRk9OjRVKlSxaive0FeBAVjERERERERMWvaYywiIiIiIiJmTcFYREREREREzJqCsYiIiIiIiJg1BWMRERERERExawrGIiIiIiIiYtYUjEVERERERMSsKRiLiIiIiIiIWVMwFhEREREREbOmYCwiIvKCHDlyBJPJRFZW1t92jsDAQGbPnv23tf+/IiMjg6FDh+Lm5kaHDh1KLTOZTOzdu7dcbS5dupR+/fr9bX3+u73q/RcR+V+mYCwiIvIMTp48iaurK8OHD3/ZXSmXq1evYjKZOHv27AtpLyMjg8jISHr27EnLli3p3r07I0eO5NChQy+k/UfWr19PRkYG27ZtIzExsdSygwcP8tZbb5WrzWHDhrF+/foX2s/4+HgjpJdm3bp1dOzYkQcPHjxxLCcnh3bt2hETE/NC+yUiIs9GwVhEROQZxMXFMWjQII4dO8bNmzdfdnf+UVevXsXPz4/Dhw8THBzMjh07WLNmDZ07dyYiIuKFnistLY0WLVrQsGFDatSoUWpZrVq1sLKyKlebtra2VK9e/YX2szz69etHTk4Ou3fvfuJYYmIiubm59O3b9x/vl4iI/D8FYxERkXK6f/8+u3btYsCAAfTo0YOtW7eWWO/EiRP06dOHVq1a8f7773PhwgXj2LVr1xg5ciQdO3bEzc0NHx8f9u/fbxw/evQo/v7+tGzZEnd3dxYuXEheXl6pfSppKXGHDh2Ij48HoGfPngC89957mEwmAgMDjXqbN2/m3XffpVWrVvTq1YtNmzaVef0RERFYWFiwefNmvL29adSoEW+88QZDhw4lNjbWqHf9+nVGjRpF27ZtadeuHePGjeP27dvF2tq7dy++vr60atWKnj17Eh0dbVznO++8Q2JiItu2bcNkMjFlypQSy0q6/vT0dCZMmECnTp1wc3PDz8+PP/74Ayh5KXJZY/Botn337t0EBgbSpk0b+vbty8mTJ4GipfNTp07l3r17mEwmTCYTS5cufWLcatSowdtvv82WLVueOLZlyxY8PT2xt7dnwYIFeHt706ZNG3r27MmiRYvIzc0t9fsoaVn96NGjjbEBePjwIfPmzcPDwwM3NzcCAgI4cuRIqW2KiJirCi+7AyIiIq+KH374gcaNG9O4cWP69u3Ll19+yYgRI7CwsChWb/78+UyfPp2aNWsSFRXFyJEjSUxMpGLFisyaNYvc3Fw2btyIjY0NycnJ2NjYAHDz5k2GDx+Or68v8+bN4/Lly4SGhlKpUiXGjh37XH3evHkzAQEBrF+/niZNmlCxYkUAvv/+exYvXszMmTNxdXXl7NmzzJgxAxsbG3x9fZ9oJzMzkwMHDjB+/Hijv39VtWpVAAoKChg9ejQ2NjZs2LCB/Px8IiIiGD9+PBs2bADgt99+IyQkhNDQUDp06EBqaiozZswAICgoiLi4OIKDg6lSpQrTp0/H2tqa3NzcJ8oed//+fQYNGoSTkxPLly+nVq1aJCUlUVBQUOLYlHcMoqKiCAkJwdnZmaioKCZOnMju3btp27Yt06ZNY8mSJfz4448AJY4NgL+/PyNGjODatWvUq1cPKJoBP3bsGGvXrgWKZrTnzJmDo6MjFy5cYMaMGdja2vLZZ5+V2GZ5zJo1i+TkZKKionB0dGTPnj18+umn7Nixg4YNGz53uyIi/zYKxiIiIuUUFxdnLHn18PDg3r17HD16lM6dOxerFxQURLdu3QCYO3cu3bt3Z8+ePfTu3Zvr16/j7e2NyWQCoEGDBsbPffvtt9SuXZuZM2diYWGBi4sLN2/eZOHChYwZM4bXXnv2hV4ODg4A2NvbU6tWLaN86dKlTJkyBS8vL6MfycnJfPfddyUG49TUVAoLC2ncuHGZ5zt06BAXLlzgp59+ok6dOkDRgwIfHx9OnTpF69atiY6ONh4APDr3uHHjWLBgAUFBQTg4OGBlZYW1tXWxPpdU9lc7d+7kzp07xMXFYW9vD4Czs3OpfS3vGAwbNowePXoA8Pnnn+Pj48Off/6Ji4sLdnZ2WFhYlNqnR9zd3XF0dCQ+Pt54yBEfH0+dOnV48803gaLZ3kfq16/P5cuXSUhIeO5gfP36deLj4/nll19wcnIC4JNPPuHAgQPEx8czYcKE52pXROTfSMFYRESkHC5dusTp06dZtmwZABUqVKB3797ExcU9EYzd3NyM/9vb29OoUSMuXboEwODBgwkPD+fgwYN07doVLy8vmjVrBkBKSgpt27YtNgPdvn17srOzSU9Pp27dui/kWrKzs0lNTWX69OnGTC1AXl4ednZ2Jf5MYWFhudpOSUmhdu3aRigGaNKkCVWrVuXSpUu0bt2ac+fOceLECVauXGnUyc/P58GDB+Tk5FC5cuXnuq6zZ8/SvHlzIxSX5VnG4NFDDMAIwHfu3MHFxaXcfbO0tMTX15etW7cSFBREYWEh27Ztw8/Pz3jgsWvXLmJiYkhLSyM7O5u8vDyqVKlS7nM87sKFC+Tn59OrV69i5Q8fPizXGImImBMFYxERkXKIi4sjLy8PDw8Po6ywsBArKytmzpxZaqB8XEBAAO7u7uzbt49ff/2V1atXExISUmzv77OwsLB4IrSWtScZikIhQGRkJG3atCl2rLRZaWdnZywsLIyA/9/Izs5m7NixxkztX1WqVOm52y1peXVZfYDyjcGj5eeA8dCitOXZZenfvz+rVq3i8OHDFBQUcOPGDfz8/ICit51PmjSJsWPH4u7ujp2dHQkJCXzzzTeltve07z47OxtLS0u2bNmCpaVlsXqlLfkWETFXCsYiIiJPkZeXx/bt25kyZYqxRPqRMWPGsHPnTgYMGGCU/f7778bs7t27d7ly5UqxJch16tRhwIABDBgwgK+++orY2FgCAwNxcXEhMTGRwsJCI4AdP34cW1tbateuXWLfHBwcuHXrlvH5ypUr5OTkGJ8fhbr8/HyjrGbNmjg6OpKWllbutyHb29vj7u7Opk2bCAwMfCJYZWVlUbVqVVxcXEhPT+fGjRvGrHFycjJZWVnGDGvz5s25fPlymcucn4fJZGLz5s1kZmY+dUb0ecagJBUrViw2tmV5/fXX6dixI3FxcQB07drV2G988uRJ6taty6hRo4z6169fL7M9BwcHMjIyjM/5+flcvHjRWMHg6upKfn4+d+7ceeqflBIRMXd6K7WIiMhT7Nu3j7t37+Lv70/Tpk2L/fPy8jKCziPLly839tpOmTKF6tWr4+npCcDs2bM5cOAAaWlpJCUlceTIESMwfvTRR6SnpxMZGUlKSgp79+5l6dKlDB06tNSZ3C5durBp0ybOnDnD6dOnCQsLKzbDWaNGDaytrTlw4AC3b9/m3r17QNFe2dWrVxMTE8Ply5c5f/48W7ZsKXOGMiwsjIKCAgICAkhMTOTKlSukpKQQExPDBx98ABSFvaZNmzJp0iSSkpI4deoUwcHBdOrUiVatWgFFDxO2b99OdHQ0Fy9eJCUlhYSEBKKiop7zGyri4+NDzZo1GTNmDMePHyctLY3ExETjLdKPe54xeFy9evXIzs7m0KFD3Llzp9hDiZL4+/uzZ88e9uzZg7+/v1Hu7OzMjRs3SEhIIDU1lZiYmCfeNv64Ll26sH//fvbt20dKSgrh4eFkZWUZxxs1akSfPn0IDg5m9+7dpKWlcerUKVatWsW+ffvKfY0iIuZAM8YiIiJPERcXR9euXUtcLu3t7c2aNWs4d+6cUTZx4kRmz57NlStXcHV1ZcWKFcbf2i0oKGDWrFmkp6dTpUoVPDw8mDp1KgBOTk6sXr2a+fPnExsbi729Pf7+/sVmER8XEhLCtGnTGDhwII6OjkybNo2kpCTjeIUKFQgNDWXZsmUsWbKEDh06sGHDBgICArC2tmbt2rXMnz8fGxsbmjZtypAhQ0o9V4MGDYiPj2flypXMmzePW7du4eDgQIsWLQgPDweKlvcuX76cyMhIBg0ahIWFBR4eHsX28Xp4eLBy5UqWLVvG119/TYUKFWjcuDEBAQHl+0JKYWVlxbp165g3bx7Dhw8nPz8fFxcXwsLCSqz/PGPwuHbt2vHhhx/yxRdfkJmZSVBQUJlvEPf29iYyMhJLS0vjYQkU/VmtIUOGMGvWLB4+fEiPHj0YNWoU0dHRpbbVv39/zp07R0hICJaWlnz88cdP7HefM2cOK1asYO7cudy6dQt7e3vc3NyMl4mJiEgRi8Lyvk1DRERERERE5F9IS6lFRERERETErCkYi4iIiIiIiFlTMBYRERERERGzpmAsIiIiIiIiZk3BWERERERERMyagrGIiIiIiIiYNQVjERERERERMWsKxiIiIiIiImLWFIxFRERERETErCkYi4iIiIiIiFlTMBYRERERERGz9n8bA4FkgNShqwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Step 8.1: Analyze feature importance\n", + "print(\"=== FEATURE IMPORTANCE ANALYSIS ===\")\n", + "\n", + "# Create a DataFrame with feature names and coefficients\n", + "feature_importance = pd.DataFrame({\"Feature\": X.columns, \"Coefficient\": model.coef_})\n", + "\n", + "# Sort by absolute value of coefficients\n", + "feature_importance[\"Abs_Coefficient\"] = np.abs(feature_importance[\"Coefficient\"])\n", + "feature_importance = feature_importance.sort_values(\"Abs_Coefficient\", ascending=False)\n", + "\n", + "print(\"\\nFeature Importance (sorted by absolute coefficient value):\")\n", + "print(feature_importance[[\"Feature\", \"Coefficient\"]])\n", + "\n", + "# Visualize feature importance\n", + "plt.figure(figsize=(10, 6))\n", + "bars = plt.barh(feature_importance[\"Feature\"], feature_importance[\"Abs_Coefficient\"])\n", + "plt.xlabel(\"Absolute Coefficient Value\")\n", + "plt.title(\"Feature Importance in Linear Regression Model\")\n", + "plt.gca().invert_yaxis() # Highest importance at top\n", + "plt.grid(True, alpha=0.3, axis=\"x\")\n", + "\n", + "# Add coefficient values on bars\n", + "for bar, coeff in zip(bars, feature_importance[\"Coefficient\"]):\n", + " plt.text(\n", + " bar.get_width() * 1.01,\n", + " bar.get_y() + bar.get_height() / 2,\n", + " f\"{coeff:.2f}\",\n", + " va=\"center\",\n", + " )\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "b042449d", + "metadata": {}, + "source": [ + "# Step 8.1: Analyze feature importance\n", + "print(\"=== FEATURE IMPORTANCE ANALYSIS ===\")\n", + "\n", + "# Create a DataFrame with feature names and coefficients\n", + "feature_importance = pd.DataFrame({\n", + " 'Feature': X.columns,\n", + " 'Coefficient': model.coef_\n", + "})\n", + "\n", + "# Sort by absolute value of coefficients\n", + "feature_importance['Abs_Coefficient'] = np.abs(feature_importance['Coefficient'])\n", + "feature_importance = feature_importance.sort_values('Abs_Coefficient', ascending=False)\n", + "\n", + "print(\"\\nFeature Importance (sorted by absolute coefficient value):\")\n", + "print(feature_importance[['Feature', 'Coefficient']])\n", + "\n", + "# Visualize feature importance\n", + "plt.figure(figsize=(10, 6))\n", + "bars = plt.barh(feature_importance['Feature'], feature_importance['Abs_Coefficient'])\n", + "plt.xlabel('Absolute Coefficient Value')\n", + "plt.title('Feature Importance in Linear Regression Model')\n", + "plt.gca().invert_yaxis() # Highest importance at top\n", + "plt.grid(True, alpha=0.3, axis='x')\n", + "\n", + "# Add coefficient values on bars\n", + "for bar, coeff in zip(bars, feature_importance['Coefficient']):\n", + " plt.text(bar.get_width() * 1.01, bar.get_y() + bar.get_height()/2,\n", + " f'{coeff:.2f}', va='center')\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "ae4cd4fd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== MAKING PREDICTIONS ===\n", + "\n", + "=== EXAMPLE PREDICTIONS ===\n" + ] + }, + { + "ename": "ValueError", + "evalue": "Unknown format code 'f' for object of type 'str'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[25], line 57\u001b[0m\n\u001b[1;32m 55\u001b[0m \u001b[38;5;66;03m# Example 1\u001b[39;00m\n\u001b[1;32m 56\u001b[0m pred1 \u001b[38;5;241m=\u001b[39m predict_tourist_numbers(\u001b[38;5;241m2025\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mBahamas\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtourists\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m---> 57\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPredicted tourists for Bahamas in 2025: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mpred1\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m.0f\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m thousand trips\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 59\u001b[0m \u001b[38;5;66;03m# Example 2\u001b[39;00m\n\u001b[1;32m 60\u001b[0m pred2 \u001b[38;5;241m=\u001b[39m predict_tourist_numbers(\u001b[38;5;241m2023\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDominican Republic\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvisitors_total\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "\u001b[0;31mValueError\u001b[0m: Unknown format code 'f' for object of type 'str'" + ] + } + ], + "source": [ + "# Step 9.1: Create a prediction function\n", + "print(\"=== MAKING PREDICTIONS ===\")\n", + "\n", + "\n", + "def predict_tourist_numbers(year, country_name, visitor_type):\n", + " \"\"\"\n", + " Predict number of tourists for given parameters\n", + "\n", + " Parameters:\n", + " year: int (e.g., 2025)\n", + " country_name: str (e.g., 'Bahamas')\n", + " visitor_type: str (e.g., 'tourists', 'excursionists', 'visitors_total')\n", + " \"\"\"\n", + " try:\n", + " # Encode inputs\n", + " country_code = country_encoder.transform([country_name])[0]\n", + " visitor_code = visitor_type_encoder.transform([visitor_type])[0]\n", + "\n", + " # Calculate derived features\n", + " decade = (year // 10) * 10\n", + " post_2000 = 1 if year > 2000 else 0\n", + " post_2010 = 1 if year > 2010 else 0\n", + " covid_period = 1 if year >= 2020 and year <= 2021 else 0\n", + "\n", + " # Create feature array\n", + " features = np.array(\n", + " [\n", + " [\n", + " year,\n", + " country_code,\n", + " visitor_code,\n", + " decade,\n", + " post_2000,\n", + " post_2010,\n", + " covid_period,\n", + " ]\n", + " ]\n", + " )\n", + "\n", + " # Scale features\n", + " features_scaled = scaler.transform(features)\n", + "\n", + " # Make prediction\n", + " prediction = model.predict(features_scaled)[0]\n", + "\n", + " return max(prediction, 0) # Ensure non-negative prediction\n", + "\n", + " except Exception as e:\n", + " return f\"Error: {str(e)}\"\n", + "\n", + "\n", + "# Step 9.2: Test predictions with examples\n", + "print(\"\\n=== EXAMPLE PREDICTIONS ===\")\n", + "\n", + "# Example 1\n", + "pred1 = predict_tourist_numbers(2025, \"Bahamas\", \"tourists\")\n", + "print(f\"Predicted tourists for Bahamas in 2025: {pred1:.0f} thousand trips\")\n", + "\n", + "# Example 2\n", + "pred2 = predict_tourist_numbers(2023, \"Dominican Republic\", \"visitors_total\")\n", + "print(\n", + " f\"Predicted total visitors for Dominican Republic in 2023: {pred2:.0f} thousand trips\"\n", + ")\n", + "\n", + "# Example 3\n", + "pred3 = predict_tourist_numbers(2026, \"Jamaica\", \"excursionists\")\n", + "print(f\"Predicted excursionists for Jamaica in 2026: {pred3:.0f} thousand trips\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e258546", + "metadata": {}, + "outputs": [], "source": [] } ], From 6b064f4aaffbc08a7c65b1e32f57218826f661f2 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 10:45:02 -0500 Subject: [PATCH 11/18] Making Predictions on New Data --- 4_data_analysis/MLProject.ipynb | 63 +++++++++------------------------ 1 file changed, 17 insertions(+), 46 deletions(-) diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index 4d791ae..baa86b6 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -969,47 +969,26 @@ "plt.show()\n" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e258546", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "markdown", - "id": "b042449d", + "id": "355e2e68", "metadata": {}, "source": [ - "# Step 8.1: Analyze feature importance\n", - "print(\"=== FEATURE IMPORTANCE ANALYSIS ===\")\n", - "\n", - "# Create a DataFrame with feature names and coefficients\n", - "feature_importance = pd.DataFrame({\n", - " 'Feature': X.columns,\n", - " 'Coefficient': model.coef_\n", - "})\n", - "\n", - "# Sort by absolute value of coefficients\n", - "feature_importance['Abs_Coefficient'] = np.abs(feature_importance['Coefficient'])\n", - "feature_importance = feature_importance.sort_values('Abs_Coefficient', ascending=False)\n", - "\n", - "print(\"\\nFeature Importance (sorted by absolute coefficient value):\")\n", - "print(feature_importance[['Feature', 'Coefficient']])\n", - "\n", - "# Visualize feature importance\n", - "plt.figure(figsize=(10, 6))\n", - "bars = plt.barh(feature_importance['Feature'], feature_importance['Abs_Coefficient'])\n", - "plt.xlabel('Absolute Coefficient Value')\n", - "plt.title('Feature Importance in Linear Regression Model')\n", - "plt.gca().invert_yaxis() # Highest importance at top\n", - "plt.grid(True, alpha=0.3, axis='x')\n", - "\n", - "# Add coefficient values on bars\n", - "for bar, coeff in zip(bars, feature_importance['Coefficient']):\n", - " plt.text(bar.get_width() * 1.01, bar.get_y() + bar.get_height()/2,\n", - " f'{coeff:.2f}', va='center')\n", - "\n", - "plt.show()" + "9. Making Predictions on New Data\n" ] }, { "cell_type": "code", - "execution_count": 25, - "id": "ae4cd4fd", + "execution_count": 29, + "id": "59e0c6ab", "metadata": {}, "outputs": [ { @@ -1018,18 +997,10 @@ "text": [ "=== MAKING PREDICTIONS ===\n", "\n", - "=== EXAMPLE PREDICTIONS ===\n" - ] - }, - { - "ename": "ValueError", - "evalue": "Unknown format code 'f' for object of type 'str'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[25], line 57\u001b[0m\n\u001b[1;32m 55\u001b[0m \u001b[38;5;66;03m# Example 1\u001b[39;00m\n\u001b[1;32m 56\u001b[0m pred1 \u001b[38;5;241m=\u001b[39m predict_tourist_numbers(\u001b[38;5;241m2025\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mBahamas\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtourists\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m---> 57\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPredicted tourists for Bahamas in 2025: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mpred1\u001b[38;5;132;01m:\u001b[39;00m\u001b[38;5;124m.0f\u001b[39m\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m thousand trips\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 59\u001b[0m \u001b[38;5;66;03m# Example 2\u001b[39;00m\n\u001b[1;32m 60\u001b[0m pred2 \u001b[38;5;241m=\u001b[39m predict_tourist_numbers(\u001b[38;5;241m2023\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mDominican Republic\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvisitors_total\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", - "\u001b[0;31mValueError\u001b[0m: Unknown format code 'f' for object of type 'str'" + "=== EXAMPLE PREDICTIONS ===\n", + "Predicted tourists for Bahamas in 2025: 1503 thousand trips\n", + "Predicted total visitors for Dominican Republic in 2023: 1730 thousand trips\n", + "Predicted excursionists for Jamaica in 2026: 1129 thousand trips\n" ] } ], @@ -1106,7 +1077,7 @@ { "cell_type": "code", "execution_count": null, - "id": "1e258546", + "id": "e46cbf49", "metadata": {}, "outputs": [], "source": [] From 62d51da32c69417af7bcfb9e39de1c8805885065 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 10:57:25 -0500 Subject: [PATCH 12/18] Model Interpretation and Summary --- 4_data_analysis/MLProject.ipynb | 107 +++++++++++++++++- .../model_artifacts/country_encoder.pkl | Bin 0 -> 941 bytes .../model_artifacts/feature_importance.csv | 8 ++ .../linear_regression_model.pkl | Bin 0 -> 673 bytes 4_data_analysis/model_artifacts/scaler.pkl | Bin 0 -> 1119 bytes .../model_artifacts/visitor_type_encoder.pkl | Bin 0 -> 517 bytes 6 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 4_data_analysis/model_artifacts/country_encoder.pkl create mode 100644 4_data_analysis/model_artifacts/feature_importance.csv create mode 100644 4_data_analysis/model_artifacts/linear_regression_model.pkl create mode 100644 4_data_analysis/model_artifacts/scaler.pkl create mode 100644 4_data_analysis/model_artifacts/visitor_type_encoder.pkl diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index baa86b6..ca83bca 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -633,12 +633,6 @@ "print(\"Features scaled using StandardScaler\")\n" ] }, - { - "cell_type": "markdown", - "id": "ab51a910", - "metadata": {}, - "source": [] - }, { "cell_type": "markdown", "id": "87025184", @@ -1081,6 +1075,107 @@ "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "markdown", + "id": "23b7b079", + "metadata": {}, + "source": [ + "10. Model Interpretation and Summary" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "fb356334", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== MODEL INTERPRETATION ===\n", + "\n", + "1. MODEL PERFORMANCE:\n", + " - The model explains approximately 7.1% of variance in tourist numbers\n", + " - Average prediction error (RMSE): 1077 thousand trips\n", + " - Average absolute error (MAE): 701 thousand trips\n", + "\n", + "2. KEY FINDINGS:\n", + " - Year has a significant impact on tourist numbers\n", + " - Country and visitor type are important predictors\n", + " - COVID-19 period indicator helps capture pandemic effects\n", + "\n", + "3. MODEL LIMITATIONS:\n", + " - Linear model may not capture complex non-linear relationships\n", + " - Model doesn't account for economic factors or events\n", + " - Predictions for extreme years may be less accurate\n", + "\n", + "4. RECOMMENDATIONS FOR IMPROVEMENT:\n", + " - Add more features (GDP, flight availability, marketing budget)\n", + " - Try polynomial regression for non-linear relationships\n", + " - Use time series models (ARIMA, Prophet) for temporal patterns\n", + " - Implement ensemble methods (Random Forest, Gradient Boosting)\n", + "\n", + "=== SAVING MODEL ARTIFACTS ===\n", + "Model artifacts saved to 'model_artifacts/' directory\n" + ] + } + ], + "source": [ + "# Step 10.1: Provide model interpretation\n", + "print(\"=== MODEL INTERPRETATION ===\")\n", + "print(\"\\n1. MODEL PERFORMANCE:\")\n", + "print(\n", + " f\" - The model explains approximately {test_r2 * 100:.1f}% of variance in tourist numbers\"\n", + ")\n", + "print(f\" - Average prediction error (RMSE): {test_rmse:.0f} thousand trips\")\n", + "print(f\" - Average absolute error (MAE): {test_mae:.0f} thousand trips\")\n", + "\n", + "print(\"\\n2. KEY FINDINGS:\")\n", + "print(\" - Year has a significant impact on tourist numbers\")\n", + "print(\" - Country and visitor type are important predictors\")\n", + "print(\" - COVID-19 period indicator helps capture pandemic effects\")\n", + "\n", + "print(\"\\n3. MODEL LIMITATIONS:\")\n", + "print(\" - Linear model may not capture complex non-linear relationships\")\n", + "print(\" - Model doesn't account for economic factors or events\")\n", + "print(\" - Predictions for extreme years may be less accurate\")\n", + "\n", + "print(\"\\n4. RECOMMENDATIONS FOR IMPROVEMENT:\")\n", + "print(\" - Add more features (GDP, flight availability, marketing budget)\")\n", + "print(\" - Try polynomial regression for non-linear relationships\")\n", + "print(\" - Use time series models (ARIMA, Prophet) for temporal patterns\")\n", + "print(\" - Implement ensemble methods (Random Forest, Gradient Boosting)\")\n", + "\n", + "# Step 10.2: Save the model (optional)\n", + "print(\"\\n=== SAVING MODEL ARTIFACTS ===\")\n", + "\n", + "import joblib\n", + "import os\n", + "\n", + "# Create directory for model artifacts\n", + "os.makedirs(\"model_artifacts\", exist_ok=True)\n", + "\n", + "# Save model and preprocessing objects\n", + "joblib.dump(model, \"model_artifacts/linear_regression_model.pkl\")\n", + "joblib.dump(scaler, \"model_artifacts/scaler.pkl\")\n", + "joblib.dump(country_encoder, \"model_artifacts/country_encoder.pkl\")\n", + "joblib.dump(visitor_type_encoder, \"model_artifacts/visitor_type_encoder.pkl\")\n", + "\n", + "# Save feature importance\n", + "feature_importance.to_csv(\"model_artifacts/feature_importance.csv\", index=False)\n", + "\n", + "print(\"Model artifacts saved to 'model_artifacts/' directory\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62013b02", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/4_data_analysis/model_artifacts/country_encoder.pkl b/4_data_analysis/model_artifacts/country_encoder.pkl new file mode 100644 index 0000000000000000000000000000000000000000..be8e53ee60ba6f7b7b2e0baa304ce7647daeb34d GIT binary patch literal 941 zcmb7DOHUL*5N3gO$$~~95{-oP5iXPHMQ>(zAt(#0VG&O{sp;wLwDfe3{lKiz1P??> z`eySR{2~4@t?7aII@yQms_LrxzIx2*{Kax>CLX_Ljs*iPlS;EnE6R-Fa+0J1GA4pw z+N4pg^yvwFHix-Fp~zq@E`R7qpCqc)QMw~*n?Ss1jk(6DRO^%Qge$Nl_O-Rqv5I4%TW zuY1z+i30*MCw!=*ZNHG_c)PGpXoCCclt^K*JxYq zYi_wI$Pw2QF3D3PFf&Ho7{IiI-wgAE2(d758oqx6g&13^Px;2#L?TNHs=x_d#3C^w!~XcV9_s818}%e(||u+A4CuDFV)q zp|0}w7`U`#$gMT84(K50eMMWyw#4u5Z!+ literal 0 HcmV?d00001 diff --git a/4_data_analysis/model_artifacts/feature_importance.csv b/4_data_analysis/model_artifacts/feature_importance.csv new file mode 100644 index 0000000..2f6e477 --- /dev/null +++ b/4_data_analysis/model_artifacts/feature_importance.csv @@ -0,0 +1,8 @@ +Feature,Coefficient,Abs_Coefficient +visitor_type_encoded,283.31148247758387,283.31148247758387 +year,267.66304278214784,267.66304278214784 +covid_period,-206.83550341605488,206.83550341605488 +country_encoded,-40.460696191478945,40.460696191478945 +decade,-24.509425463626023,24.509425463626023 +post_2000,-14.076701401267819,14.076701401267819 +post_2010,-4.197868757249495,4.197868757249495 diff --git a/4_data_analysis/model_artifacts/linear_regression_model.pkl b/4_data_analysis/model_artifacts/linear_regression_model.pkl new file mode 100644 index 0000000000000000000000000000000000000000..56e388d923a48f1ddf60a45bc1095c4d7d355ada GIT binary patch literal 673 zcmYjPO=uHA6i$qr#$ZdTwp9xSE7dAxK@VDs$d)Rtu0$weJt*VsW|A4ZJG-6PZOB2; zLfQnzi%w2T$lY;L9|XbR(*$=ZyxXGd+(dOJ8QXkEHZJB z&pQY*syl?L8@OH$IXbW)M^a=iN6uMPV2E?#QTb3FyC?6+QPG_z0U$I$%tn47DlDt1SHTvjV9a#oI-2@yXlv)SyWEoISR#sX=HG;|!V z2wWHXYTUsN&!`)K4k=I<(EwPb0C8z%LMziw|KHv|SpV?orEfg`{A%F)#QEmdm)#$K zyjU=PY3T-88gJf+w+a`Ijy3Be<-yf=ndWBe$->$v(yX27h!ztXgLFPx+AdCLA#{{- z68Chp!M%7N?rTqr2k?G;01x3q6+GOgqD3_Nue@L1*t}+NdOTq^W(>b^b!ly7#)v;Z zWe)dE8#5Paq4j#wD6`T?@#mOPL^qcYr;ix5TigB9f0b8y^+yZPf$cG*yP*@1&gMmu s+fXr665ye!)I|52r}fpUA(z#1MF#x&cwiB+=xYh>gnn8-DML&D4Q&S;od5s; literal 0 HcmV?d00001 diff --git a/4_data_analysis/model_artifacts/scaler.pkl b/4_data_analysis/model_artifacts/scaler.pkl new file mode 100644 index 0000000000000000000000000000000000000000..40f447e97a568a6ae882cc680a606eec93f9a623 GIT binary patch literal 1119 zcmb7^Uq}=|9LGJq%RkBar^`w~(F!eOQF|z&*bSk=qZ9R^AY_``9o^Oa$IPB`WTX%N zi+R$y=s^;~APmYLe5;2Zs)r0Ag2b07q0}OL6Oy=Q_q^1isCn4^%`!9J?|eT$w%?yP z?aS~6EhF7Jg4h&njBISlh!D-}6o3L9$Y`+5fu;hn(k4S4VK&Z=46tjghUWHajtUF} zO*TSvya{m>Ui)Rsj`Md>2ZBxvBVa-U5ullX(LG&ORM(<{88hrSur--WGg=gJr)MyR z@rxMR_I8;Z5{t?@B!tm`w~Nti*AHLIz0d$rp^aELdyv-_R`^!xYvi@!#QAr%AYJjb znawBI=*Tc@Wi{a}>g!{XNTeyrhxZ7J4vW$}sOwfQFbrt3QCgPr>8SyzYn`TnOb0~c z4k9dE7>o7?604~gGiZsoSOBtxkzmAhM@zY+`2Zu?GKqs@vg4Wl{UYIvLDuMtvXncY zdmlCH|Gba3b1MEgN6Bb`Y{g6m#{n{BOF;^wp&pHBj)j4n%hu;z3X-A18RXg)aX>>| zUENk)&+B~I>d_RynP`^65YI?Ryi8v?Xo5}1DZb%sT3`YK4O{2L2|_OOKjyNF@Rs6k z$Cv(;&k4^t@8u##HEQZ-3`HcF;Sk@ ZD+HUr4bX!y(JYhms27e44J;NFegKwb%SQkJ literal 0 HcmV?d00001 diff --git a/4_data_analysis/model_artifacts/visitor_type_encoder.pkl b/4_data_analysis/model_artifacts/visitor_type_encoder.pkl new file mode 100644 index 0000000000000000000000000000000000000000..e44fb60c11e9747f010a27f9a84490abe6a48651 GIT binary patch literal 517 zcmb7BO-lnY5bauvMXFQ0j~h zX?E8h#FKMK-h?+ZZ(gRWi|wRQMcB6wk`tp)Zg_4qYC4|mZd0!}|hupvi z9KON}^g^3SVlB5A!p^;>Qc$FPmKQh|EcS!2H7L__V@PppNS^;Hww_k?5L#6ggf%rJ zB{xn8EjuPThqO71SetTgNcs^ET@l~Moh6FgmFrWOzrVo^^wMTXo?$Q;Tzo@=eAeDH!fr)Im}$dN=A{$0%Fw}v zGp(#Mp1Df&-$l~K4EhO$T3u4^Gj9G*`}{6?y}_TDH&$pRtg{f>PJ2_s&55wWX=Aa| ej!58fU$i(_%J1Tz&&3m7HHao2p%Ziro}zC&Gtevm literal 0 HcmV?d00001 From 07f5fd369c6498348d97c69e6340ef23906817ff Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 13:54:13 -0500 Subject: [PATCH 13/18] pyproject.toml file creation --- .ls-lint.yml | 4 +++- notes/pyproject.toml | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 notes/pyproject.toml diff --git a/.ls-lint.yml b/.ls-lint.yml index 11468c9..68adadf 100644 --- a/.ls-lint.yml +++ b/.ls-lint.yml @@ -9,9 +9,11 @@ ignore: - .git - .github - .vscode + - .venv - venv - .ruff_cache - .pytest_cache - __pycache__ - .ls-lint.yml - - .markdownlint.yml + - .ls-lint + - .ls diff --git a/notes/pyproject.toml b/notes/pyproject.toml new file mode 100644 index 0000000..bc59192 --- /dev/null +++ b/notes/pyproject.toml @@ -0,0 +1,2 @@ +[tool.ruff.format] +exclude = ["*.ipynb"] From bf4f3cbb4dd7e0f0bd514d7260cb25fbd0073ac5 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 14:17:11 -0500 Subject: [PATCH 14/18] ls-lint.yml modification --- .ls-lint.yml | 14 +- .../data_exploration_caribbean.ipynb | 1 - 4_data_analysis/MLProject.ipynb | 148 +----------------- 3 files changed, 17 insertions(+), 146 deletions(-) diff --git a/.ls-lint.yml b/.ls-lint.yml index 68adadf..b016971 100644 --- a/.ls-lint.yml +++ b/.ls-lint.yml @@ -4,16 +4,20 @@ ls: .md: snake_case | regex:[0-9A-Z\-]+ .txt: snake_case | kebab-case .yml: snake_case | kebab-case + .ipynb: snake_case + .csv: snake_case + .py: snake_case ignore: - .git - .github - .vscode - - .venv - - venv - .ruff_cache - .pytest_cache - __pycache__ - - .ls-lint.yml - - .ls-lint - - .ls + - .venv + - **/site-packages/** + - 1_datasets + - data + - dist + - build diff --git a/3_data_exploration/data_exploration_caribbean.ipynb b/3_data_exploration/data_exploration_caribbean.ipynb index 2de4079..f452497 100644 --- a/3_data_exploration/data_exploration_caribbean.ipynb +++ b/3_data_exploration/data_exploration_caribbean.ipynb @@ -10,7 +10,6 @@ "# Import modules \n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", - "import numpy as np\n", "import seaborn as sns\n", "\n", "sns.set(style = \"whitegrid\")\n", diff --git a/4_data_analysis/MLProject.ipynb b/4_data_analysis/MLProject.ipynb index d3aa7f3..67b9f84 100644 --- a/4_data_analysis/MLProject.ipynb +++ b/4_data_analysis/MLProject.ipynb @@ -10,7 +10,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "9beb769c", "metadata": {}, "outputs": [ @@ -26,8 +26,8 @@ } ], "source": [ - "import os \n", - "import sys\n", + "import os\n", + "import joblib\n", "\n", "os.getcwd()\n" ] @@ -161,149 +161,18 @@ "print(f\"\\nColumns: {df.columns.tolist()}\")\n", "print(f\"\\nData Types:\\n{df.dtypes}\")\n", "print(f\"\\nMissing Values:\\n{df.isnull().sum()}\")\n", - "print(f\"\\nFirst 5 rows:\")\n", + "print(\"\\nFirst 5 rows:\")\n", "print(df.head())\n", "\n", - "print(f\"\\n=== BASIC STATISTICS ===\")\n", + "print(\"\\n=== BASIC STATISTICS ===\")\n", "print(df.describe())\n", "\n", - "print(f\"\\n=== UNIQUE VALUES ===\")\n", + "print(\"\\n=== UNIQUE VALUES ===\")\n", "print(f\"Visitor types: {df['type_of_visitors'].unique()}\")\n", "print(f\"Number of countries: {df['country_receiving'].nunique()}\")\n", "print(f\"Years range: {df['year'].min()} to {df['year'].max()}\")\n" ] }, - { - "cell_type": "code", - "execution_count": 20, - "id": "acc8bd2f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "RangeIndex: 3025 entries, 0 to 3024\n", - "Data columns (total 6 columns):\n", - " # Column Non-Null Count Dtype \n", - "--- ------ -------------- ----- \n", - " 0 type_of_visitors 3025 non-null object \n", - " 1 country_receiving 3025 non-null object \n", - " 2 where_tourist_from 3025 non-null object \n", - " 3 year 3025 non-null int64 \n", - " 4 number_of_tourist 3025 non-null float64\n", - " 5 unit 3025 non-null object \n", - "dtypes: float64(1), int64(1), object(4)\n", - "memory usage: 141.9+ KB\n" - ] - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
type_of_visitorscountry_receivingwhere_tourist_fromyearnumber_of_touristunit
0excursionistsAntigua and BarbudaWorld1995227.0thousand trips
1excursionistsAntigua and BarbudaWorld1996270.0thousand trips
2excursionistsAntigua and BarbudaWorld1997286.0thousand trips
3excursionistsAntigua and BarbudaWorld1998336.0thousand trips
4excursionistsAntigua and BarbudaWorld1999328.0thousand trips
\n", - "
" - ], - "text/plain": [ - " type_of_visitors country_receiving where_tourist_from year \\\n", - "0 excursionists Antigua and Barbuda World 1995 \n", - "1 excursionists Antigua and Barbuda World 1996 \n", - "2 excursionists Antigua and Barbuda World 1997 \n", - "3 excursionists Antigua and Barbuda World 1998 \n", - "4 excursionists Antigua and Barbuda World 1999 \n", - "\n", - " number_of_tourist unit \n", - "0 227.0 thousand trips \n", - "1 270.0 thousand trips \n", - "2 286.0 thousand trips \n", - "3 336.0 thousand trips \n", - "4 328.0 thousand trips " - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "data.info()\n", - "\n", - "data.head()\n" - ] - }, { "cell_type": "markdown", "id": "c7b16997", @@ -1086,7 +955,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "id": "fb356334", "metadata": {}, "outputs": [ @@ -1151,8 +1020,7 @@ "# Step 10.2: Save the model (optional)\n", "print(\"\\n=== SAVING MODEL ARTIFACTS ===\")\n", "\n", - "import joblib\n", - "import os\n", + "\n", "\n", "# Create directory for model artifacts\n", "os.makedirs(\"model_artifacts\", exist_ok=True)\n", From 782befb39da52f956ae25dde3a69de2dd40c4837 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 14:21:16 -0500 Subject: [PATCH 15/18] .gitignore modification --- .gitignore | 466 ----------------------------------------------------- 1 file changed, 466 deletions(-) diff --git a/.gitignore b/.gitignore index 0d0fa46..b1c6cec 100644 --- a/.gitignore +++ b/.gitignore @@ -5,469 +5,3 @@ venv/ *.db *.idea *.ruff_cache - - -"git restore --staged ..." to unstage) - new file: .venv/bin/get_gprof - new file: .venv/bin/get_objgraph - new file: .venv/bin/isort - new file: .venv/bin/isort-identify-imports - new file: .venv/bin/pylint - new file: .venv/bin/pylint-config - new file: .venv/bin/pyreverse - new file: .venv/bin/symilar - new file: .venv/bin/undill - new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER - new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA - new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD - new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL - new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt - new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE - new file: .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt - new file: .venv/lib/python3.10/site-packages/astroid/__init__.py - new file: .venv/lib/python3.10/site-packages/astroid/__pkginfo__.py - new file: .venv/lib/python3.10/site-packages/astroid/_ast.py - new file: .venv/lib/python3.10/site-packages/astroid/arguments.py - new file: .venv/lib/python3.10/site-packages/astroid/astroid_manager.py - new file: .venv/lib/python3.10/site-packages/astroid/bases.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/__init__.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_http.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_io.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_random.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_re.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_six.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_type.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py - new file: .venv/lib/python3.10/site-packages/astroid/brain/helpers.py - new file: .venv/lib/python3.10/site-packages/astroid/builder.py - new file: .venv/lib/python3.10/site-packages/astroid/const.py - new file: .venv/lib/python3.10/site-packages/astroid/constraint.py - new file: .venv/lib/python3.10/site-packages/astroid/context.py - new file: .venv/lib/python3.10/site-packages/astroid/decorators.py - new file: .venv/lib/python3.10/site-packages/astroid/exceptions.py - new file: .venv/lib/python3.10/site-packages/astroid/filter_statements.py - new file: .venv/lib/python3.10/site-packages/astroid/helpers.py - new file: .venv/lib/python3.10/site-packages/astroid/inference_tip.py - new file: .venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py - new file: .venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py - new file: .venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py - new file: .venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py - new file: .venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py - new file: .venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py - new file: .venv/lib/python3.10/site-packages/astroid/manager.py - new file: .venv/lib/python3.10/site-packages/astroid/modutils.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/__init__.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/as_string.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/const.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py - new file: .venv/lib/python3.10/site-packages/astroid/nodes/utils.py - new file: .venv/lib/python3.10/site-packages/astroid/objects.py - new file: .venv/lib/python3.10/site-packages/astroid/protocols.py - new file: .venv/lib/python3.10/site-packages/astroid/raw_building.py - new file: .venv/lib/python3.10/site-packages/astroid/rebuilder.py - new file: .venv/lib/python3.10/site-packages/astroid/test_utils.py - new file: .venv/lib/python3.10/site-packages/astroid/transforms.py - new file: .venv/lib/python3.10/site-packages/astroid/typing.py - new file: .venv/lib/python3.10/site-packages/astroid/util.py - new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER - new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE - new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA - new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD - new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL - new file: .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt - new file: .venv/lib/python3.10/site-packages/dill/__diff.py - new file: .venv/lib/python3.10/site-packages/dill/__info__.py - new file: .venv/lib/python3.10/site-packages/dill/__init__.py - new file: .venv/lib/python3.10/site-packages/dill/_dill.py - new file: .venv/lib/python3.10/site-packages/dill/_objects.py - new file: .venv/lib/python3.10/site-packages/dill/_shims.py - new file: .venv/lib/python3.10/site-packages/dill/detect.py - new file: .venv/lib/python3.10/site-packages/dill/logger.py - new file: .venv/lib/python3.10/site-packages/dill/objtypes.py - new file: .venv/lib/python3.10/site-packages/dill/pointers.py - new file: .venv/lib/python3.10/site-packages/dill/session.py - new file: .venv/lib/python3.10/site-packages/dill/settings.py - new file: .venv/lib/python3.10/site-packages/dill/source.py - new file: .venv/lib/python3.10/site-packages/dill/temp.py - new file: .venv/lib/python3.10/site-packages/dill/tests/__init__.py - new file: .venv/lib/python3.10/site-packages/dill/tests/__main__.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_abc.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_check.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_classdef.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_detect.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_diff.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_file.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_functions.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_functors.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_logger.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_mixins.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_module.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_nested.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_objects.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_properties.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_recursive.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_registered.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_restricted.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_selected.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_session.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_source.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_sources.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_temp.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_threads.py - new file: .venv/lib/python3.10/site-packages/dill/tests/test_weakref.py - new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER - new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA - new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD - new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL - new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt - new file: .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE - new file: .venv/lib/python3.10/site-packages/isort/__init__.py - new file: .venv/lib/python3.10/site-packages/isort/__main__.py - new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE - new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py - new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py - new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py - new file: .venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed - new file: .venv/lib/python3.10/site-packages/isort/_version.py - new file: .venv/lib/python3.10/site-packages/isort/api.py - new file: .venv/lib/python3.10/site-packages/isort/comments.py - new file: .venv/lib/python3.10/site-packages/isort/core.py - new file: .venv/lib/python3.10/site-packages/isort/deprecated/__init__.py - new file: .venv/lib/python3.10/site-packages/isort/deprecated/finders.py - new file: .venv/lib/python3.10/site-packages/isort/exceptions.py - new file: .venv/lib/python3.10/site-packages/isort/files.py - new file: .venv/lib/python3.10/site-packages/isort/format.py - new file: .venv/lib/python3.10/site-packages/isort/hooks.py - new file: .venv/lib/python3.10/site-packages/isort/identify.py - new file: .venv/lib/python3.10/site-packages/isort/io.py - new file: .venv/lib/python3.10/site-packages/isort/literal.py - new file: .venv/lib/python3.10/site-packages/isort/logo.py - new file: .venv/lib/python3.10/site-packages/isort/main.py - new file: .venv/lib/python3.10/site-packages/isort/output.py - new file: .venv/lib/python3.10/site-packages/isort/parse.py - new file: .venv/lib/python3.10/site-packages/isort/place.py - new file: .venv/lib/python3.10/site-packages/isort/profiles.py - new file: .venv/lib/python3.10/site-packages/isort/py.typed - new file: .venv/lib/python3.10/site-packages/isort/sections.py - new file: .venv/lib/python3.10/site-packages/isort/settings.py - new file: .venv/lib/python3.10/site-packages/isort/setuptools_commands.py - new file: .venv/lib/python3.10/site-packages/isort/sorting.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/all.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py2.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py27.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py3.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py310.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py311.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py312.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py313.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py314.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py36.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py37.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py38.py - new file: .venv/lib/python3.10/site-packages/isort/stdlibs/py39.py - new file: .venv/lib/python3.10/site-packages/isort/utils.py - new file: .venv/lib/python3.10/site-packages/isort/wrap.py - new file: .venv/lib/python3.10/site-packages/isort/wrap_modes.py - new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER - new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE - new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA - new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD - new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL - new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt - new file: .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt - new file: .venv/lib/python3.10/site-packages/mccabe.py - new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER - new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA - new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD - new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL - new file: .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE - new file: .venv/lib/python3.10/site-packages/platformdirs/__init__.py - new file: .venv/lib/python3.10/site-packages/platformdirs/__main__.py - new file: .venv/lib/python3.10/site-packages/platformdirs/android.py - new file: .venv/lib/python3.10/site-packages/platformdirs/api.py - new file: .venv/lib/python3.10/site-packages/platformdirs/macos.py - new file: .venv/lib/python3.10/site-packages/platformdirs/py.typed - new file: .venv/lib/python3.10/site-packages/platformdirs/unix.py - new file: .venv/lib/python3.10/site-packages/platformdirs/version.py - new file: .venv/lib/python3.10/site-packages/platformdirs/windows.py - new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER - new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA - new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD - new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/REQUESTED - new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL - new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt - new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt - new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE - new file: .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt - new file: .venv/lib/python3.10/site-packages/pylint/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/__main__.py - new file: .venv/lib/python3.10/site-packages/pylint/__pkginfo__.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base/pass_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/base_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/classes/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/classes/class_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/classes/special_methods_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/clear_lru_cache.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/dataclass_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/deprecated.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/design_analysis.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/dunder_methods.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/ellipsis_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/exceptions.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/format.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/imports.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/lambda_expressions.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/logging.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/match_statements_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/method_args.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/misc.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/modified_iterating_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/nested_min_max.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/newstyle.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/non_ascii_names.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/raw_metrics.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/implicit_booleaness_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/not_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/recommendation_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/refactoring_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/spelling.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/stdlib.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/strings.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/symilar.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/threading_checker.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/typecheck.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/unicode.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/unsupported_version.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/utils.py - new file: .venv/lib/python3.10/site-packages/pylint/checkers/variables.py - new file: .venv/lib/python3.10/site-packages/pylint/config/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/config/_breaking_changes/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/generate_command.py - new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/help_message.py - new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/main.py - new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/setup.py - new file: .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/utils.py - new file: .venv/lib/python3.10/site-packages/pylint/config/argument.py - new file: .venv/lib/python3.10/site-packages/pylint/config/arguments_manager.py - new file: .venv/lib/python3.10/site-packages/pylint/config/arguments_provider.py - new file: .venv/lib/python3.10/site-packages/pylint/config/callback_actions.py - new file: .venv/lib/python3.10/site-packages/pylint/config/config_file_parser.py - new file: .venv/lib/python3.10/site-packages/pylint/config/config_initialization.py - new file: .venv/lib/python3.10/site-packages/pylint/config/deprecation_actions.py - new file: .venv/lib/python3.10/site-packages/pylint/config/exceptions.py - new file: .venv/lib/python3.10/site-packages/pylint/config/find_default_config_files.py - new file: .venv/lib/python3.10/site-packages/pylint/config/help_formatter.py - new file: .venv/lib/python3.10/site-packages/pylint/config/utils.py - new file: .venv/lib/python3.10/site-packages/pylint/constants.py - new file: .venv/lib/python3.10/site-packages/pylint/exceptions.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/_check_docs_utils.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/bad_builtin.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/broad_try_clause.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/check_elif.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/code_style.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/comparison_placement.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/confusing_elif.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/consider_refactoring_into_while_condition.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/consider_ternary_expression.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/dict_init_mutate.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/docparams.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/docstyle.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/dunder.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/empty_comment.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/eq_without_hash.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/for_any_all.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/magic_value.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/mccabe.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/no_self_use.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/overlapping_exceptions.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/private_import.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/redefined_loop_name.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/redefined_variable_type.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/set_membership.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/typing.py - new file: .venv/lib/python3.10/site-packages/pylint/extensions/while_used.py - new file: .venv/lib/python3.10/site-packages/pylint/graph.py - new file: .venv/lib/python3.10/site-packages/pylint/interfaces.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/base_options.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/caching.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/expand_modules.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/message_state_handler.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/parallel.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/pylinter.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/report_functions.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/run.py - new file: .venv/lib/python3.10/site-packages/pylint/lint/utils.py - new file: .venv/lib/python3.10/site-packages/pylint/message/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/message/_deleted_message_ids.py - new file: .venv/lib/python3.10/site-packages/pylint/message/message.py - new file: .venv/lib/python3.10/site-packages/pylint/message/message_definition.py - new file: .venv/lib/python3.10/site-packages/pylint/message/message_definition_store.py - new file: .venv/lib/python3.10/site-packages/pylint/message/message_id_store.py - new file: .venv/lib/python3.10/site-packages/pylint/py.typed - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/diadefslib.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/diagrams.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/dot_printer.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/inspector.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/main.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/mermaidjs_printer.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/plantuml_printer.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/printer.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/printer_factory.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/utils.py - new file: .venv/lib/python3.10/site-packages/pylint/pyreverse/writer.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/base_reporter.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/collecting_reporter.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/json_reporter.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/multi_reporter.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/progress_reporters.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/reports_handler_mix_in.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/text.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/ureports/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/ureports/base_writer.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/ureports/nodes.py - new file: .venv/lib/python3.10/site-packages/pylint/reporters/ureports/text_writer.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/package_to_lint.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_command.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_compare_command.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_prepare_command.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_run_command.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/_run.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/checker_test_case.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/configuration_test.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/constants.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/decorator.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/functional/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/functional/find_functional_tests.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/functional/lint_module_output_update.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/functional/test_file.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/get_test_info.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/global_test_linter.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/lint_module_test.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/output_line.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/pyreverse.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/reporter_for_tests.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/testing_pylintrc - new file: .venv/lib/python3.10/site-packages/pylint/testutils/tokenize_str.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/unittest_linter.py - new file: .venv/lib/python3.10/site-packages/pylint/testutils/utils.py - new file: .venv/lib/python3.10/site-packages/pylint/typing.py - new file: .venv/lib/python3.10/site-packages/pylint/utils/__init__.py - new file: .venv/lib/python3.10/site-packages/pylint/utils/ast_walker.py - new file: .venv/lib/python3.10/site-packages/pylint/utils/docs.py - new file: .venv/lib/python3.10/site-packages/pylint/utils/file_state.py - new file: .venv/lib/python3.10/site-packages/pylint/utils/linterstats.py - new file: .venv/lib/python3.10/site-packages/pylint/utils/pragma_parser.py - new file: .venv/lib/python3.10/site-packages/pylint/utils/utils.py - new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/INSTALLER - new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/METADATA - new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/RECORD - new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/WHEEL - new file: .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/licenses/LICENSE - new file: .venv/lib/python3.10/site-packages/tomli/__init__.py - new file: .venv/lib/python3.10/site-packages/tomli/_parser.py - new file: .venv/lib/python3.10/site-packages/tomli/_re.py - new file: .venv/lib/python3.10/site-packages/tomli/_types.py - new file: .venv/lib/python3.10/site-packages/tomli/py.typed - new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/INSTALLER - new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/LICENSE - new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/METADATA - new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/RECORD - new file: .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/WHEEL - new file: .venv/lib/python3.10/site-packages/tomlkit/__init__.py - new file: .venv/lib/python3.10/site-packages/tomlkit/_compat.py - new file: .venv/lib/python3.10/site-packages/tomlkit/_types.py - new file: .venv/lib/python3.10/site-packages/tomlkit/_utils.py - new file: .venv/lib/python3.10/site-packages/tomlkit/api.py - new file: .venv/lib/python3.10/site-packages/tomlkit/container.py - new file: .venv/lib/python3.10/site-packages/tomlkit/exceptions.py - new file: .venv/lib/python3.10/site-packages/tomlkit/items.py - new file: .venv/lib/python3.10/site-packages/tomlkit/parser.py - new file: .venv/lib/python3.10/site-packages/tomlkit/py.typed - new file: .venv/lib/python3.10/site-packages/tomlkit/source.py - new file: .venv/lib/python3.10/site-packages/tomlkit/toml_char.py - new file: .venv/lib/python3.10/site-packages/tomlkit/toml_document.py - new file: .venv/lib/python3.10/site-packages/tomlkit/toml_file.py - new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER - new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/METADATA - new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/RECORD - new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/WHEEL - new file: .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE - new file: .venv/lib/python3.10/site-packages/typing_extensions.py From 9b8697432891cebddb4778196f0f7c9ab54e7537 Mon Sep 17 00:00:00 2001 From: Clif Date: Sat, 6 Dec 2025 15:35:27 -0500 Subject: [PATCH 16/18] Remove .venv from repository and ignore it --- .gitignore | 3 +- .venv/bin/get_gprof | 75 - .venv/bin/get_objgraph | 54 - .venv/bin/isort | 7 - .venv/bin/isort-identify-imports | 7 - .venv/bin/pylint | 7 - .venv/bin/pylint-config | 7 - .venv/bin/pyreverse | 7 - .venv/bin/symilar | 7 - .venv/bin/undill | 22 - .../astroid-4.0.2.dist-info/INSTALLER | 1 - .../astroid-4.0.2.dist-info/METADATA | 122 - .../astroid-4.0.2.dist-info/RECORD | 197 - .../astroid-4.0.2.dist-info/WHEEL | 5 - .../licenses/CONTRIBUTORS.txt | 226 - .../astroid-4.0.2.dist-info/licenses/LICENSE | 508 -- .../astroid-4.0.2.dist-info/top_level.txt | 1 - .../site-packages/astroid/__init__.py | 242 - .../site-packages/astroid/__pkginfo__.py | 6 - .../python3.10/site-packages/astroid/_ast.py | 102 - .../site-packages/astroid/arguments.py | 309 - .../site-packages/astroid/astroid_manager.py | 20 - .../python3.10/site-packages/astroid/bases.py | 778 --- .../site-packages/astroid/brain/__init__.py | 0 .../astroid/brain/brain_argparse.py | 50 - .../astroid/brain/brain_attrs.py | 110 - .../astroid/brain/brain_boto3.py | 32 - .../astroid/brain/brain_builtin_inference.py | 1106 ---- .../astroid/brain/brain_collections.py | 138 - .../astroid/brain/brain_crypt.py | 27 - .../astroid/brain/brain_ctypes.py | 86 - .../astroid/brain/brain_curses.py | 185 - .../astroid/brain/brain_dataclasses.py | 635 -- .../astroid/brain/brain_datetime.py | 20 - .../astroid/brain/brain_dateutil.py | 28 - .../astroid/brain/brain_functools.py | 174 - .../site-packages/astroid/brain/brain_gi.py | 252 - .../astroid/brain/brain_hashlib.py | 96 - .../site-packages/astroid/brain/brain_http.py | 238 - .../astroid/brain/brain_hypothesis.py | 56 - .../site-packages/astroid/brain/brain_io.py | 44 - .../astroid/brain/brain_mechanize.py | 125 - .../astroid/brain/brain_multiprocessing.py | 106 - .../astroid/brain/brain_namedtuple_enum.py | 681 -- .../brain/brain_numpy_core_einsumfunc.py | 28 - .../brain/brain_numpy_core_fromnumeric.py | 24 - .../brain/brain_numpy_core_function_base.py | 35 - .../brain/brain_numpy_core_multiarray.py | 106 - .../astroid/brain/brain_numpy_core_numeric.py | 50 - .../brain/brain_numpy_core_numerictypes.py | 265 - .../astroid/brain/brain_numpy_core_umath.py | 154 - .../astroid/brain/brain_numpy_ma.py | 33 - .../astroid/brain/brain_numpy_ndarray.py | 163 - .../brain/brain_numpy_random_mtrand.py | 73 - .../astroid/brain/brain_numpy_utils.py | 94 - .../astroid/brain/brain_pathlib.py | 55 - .../astroid/brain/brain_pkg_resources.py | 72 - .../astroid/brain/brain_pytest.py | 85 - .../site-packages/astroid/brain/brain_qt.py | 89 - .../astroid/brain/brain_random.py | 94 - .../site-packages/astroid/brain/brain_re.py | 97 - .../astroid/brain/brain_regex.py | 95 - .../astroid/brain/brain_responses.py | 80 - .../astroid/brain/brain_scipy_signal.py | 90 - .../astroid/brain/brain_signal.py | 120 - .../site-packages/astroid/brain/brain_six.py | 244 - .../astroid/brain/brain_sqlalchemy.py | 41 - .../site-packages/astroid/brain/brain_ssl.py | 163 - .../astroid/brain/brain_statistics.py | 73 - .../astroid/brain/brain_subprocess.py | 100 - .../astroid/brain/brain_threading.py | 33 - .../site-packages/astroid/brain/brain_type.py | 70 - .../astroid/brain/brain_typing.py | 504 -- .../astroid/brain/brain_unittest.py | 31 - .../site-packages/astroid/brain/brain_uuid.py | 18 - .../site-packages/astroid/brain/helpers.py | 146 - .../site-packages/astroid/builder.py | 505 -- .../python3.10/site-packages/astroid/const.py | 26 - .../site-packages/astroid/constraint.py | 186 - .../site-packages/astroid/context.py | 204 - .../site-packages/astroid/decorators.py | 232 - .../site-packages/astroid/exceptions.py | 419 -- .../astroid/filter_statements.py | 240 - .../site-packages/astroid/helpers.py | 335 - .../site-packages/astroid/inference_tip.py | 130 - .../astroid/interpreter/__init__.py | 0 .../astroid/interpreter/_import/__init__.py | 0 .../astroid/interpreter/_import/spec.py | 496 -- .../astroid/interpreter/_import/util.py | 111 - .../astroid/interpreter/dunder_lookup.py | 75 - .../astroid/interpreter/objectmodel.py | 1013 --- .../site-packages/astroid/manager.py | 478 -- .../site-packages/astroid/modutils.py | 703 -- .../site-packages/astroid/nodes/__init__.py | 303 - .../astroid/nodes/_base_nodes.py | 672 -- .../site-packages/astroid/nodes/as_string.py | 740 --- .../site-packages/astroid/nodes/const.py | 27 - .../astroid/nodes/node_classes.py | 5701 ----------------- .../site-packages/astroid/nodes/node_ng.py | 771 --- .../astroid/nodes/scoped_nodes/__init__.py | 47 - .../astroid/nodes/scoped_nodes/mixin.py | 202 - .../nodes/scoped_nodes/scoped_nodes.py | 2900 --------- .../astroid/nodes/scoped_nodes/utils.py | 35 - .../site-packages/astroid/nodes/utils.py | 14 - .../site-packages/astroid/objects.py | 360 -- .../site-packages/astroid/protocols.py | 958 --- .../site-packages/astroid/raw_building.py | 735 --- .../site-packages/astroid/rebuilder.py | 1996 ------ .../site-packages/astroid/test_utils.py | 78 - .../site-packages/astroid/transforms.py | 163 - .../site-packages/astroid/typing.py | 98 - .../python3.10/site-packages/astroid/util.py | 159 - .../dill-0.4.0.dist-info/INSTALLER | 1 - .../dill-0.4.0.dist-info/LICENSE | 35 - .../dill-0.4.0.dist-info/METADATA | 281 - .../site-packages/dill-0.4.0.dist-info/RECORD | 101 - .../site-packages/dill-0.4.0.dist-info/WHEEL | 5 - .../dill-0.4.0.dist-info/top_level.txt | 1 - .../python3.10/site-packages/dill/__diff.py | 234 - .../python3.10/site-packages/dill/__info__.py | 291 - .../python3.10/site-packages/dill/__init__.py | 119 - .../python3.10/site-packages/dill/_dill.py | 2255 ------- .../python3.10/site-packages/dill/_objects.py | 541 -- .../python3.10/site-packages/dill/_shims.py | 193 - .../python3.10/site-packages/dill/detect.py | 287 - .../python3.10/site-packages/dill/logger.py | 285 - .../python3.10/site-packages/dill/objtypes.py | 24 - .../python3.10/site-packages/dill/pointers.py | 122 - .../python3.10/site-packages/dill/session.py | 612 -- .../python3.10/site-packages/dill/settings.py | 25 - .../python3.10/site-packages/dill/source.py | 1023 --- .../lib/python3.10/site-packages/dill/temp.py | 252 - .../site-packages/dill/tests/__init__.py | 22 - .../site-packages/dill/tests/__main__.py | 35 - .../site-packages/dill/tests/test_abc.py | 169 - .../site-packages/dill/tests/test_check.py | 62 - .../site-packages/dill/tests/test_classdef.py | 340 - .../dill/tests/test_dataclasses.py | 35 - .../site-packages/dill/tests/test_detect.py | 160 - .../dill/tests/test_dictviews.py | 39 - .../site-packages/dill/tests/test_diff.py | 107 - .../dill/tests/test_extendpickle.py | 53 - .../site-packages/dill/tests/test_fglobals.py | 55 - .../site-packages/dill/tests/test_file.py | 500 -- .../dill/tests/test_functions.py | 141 - .../site-packages/dill/tests/test_functors.py | 39 - .../site-packages/dill/tests/test_logger.py | 70 - .../site-packages/dill/tests/test_mixins.py | 121 - .../site-packages/dill/tests/test_module.py | 84 - .../dill/tests/test_moduledict.py | 54 - .../site-packages/dill/tests/test_nested.py | 135 - .../site-packages/dill/tests/test_objects.py | 63 - .../dill/tests/test_properties.py | 62 - .../dill/tests/test_pycapsule.py | 45 - .../dill/tests/test_recursive.py | 177 - .../dill/tests/test_registered.py | 64 - .../dill/tests/test_restricted.py | 27 - .../site-packages/dill/tests/test_selected.py | 126 - .../site-packages/dill/tests/test_session.py | 280 - .../site-packages/dill/tests/test_source.py | 173 - .../site-packages/dill/tests/test_sources.py | 190 - .../site-packages/dill/tests/test_temp.py | 103 - .../site-packages/dill/tests/test_threads.py | 46 - .../site-packages/dill/tests/test_weakref.py | 72 - .../isort-7.0.0.dist-info/INSTALLER | 1 - .../isort-7.0.0.dist-info/METADATA | 375 -- .../isort-7.0.0.dist-info/RECORD | 101 - .../site-packages/isort-7.0.0.dist-info/WHEEL | 4 - .../isort-7.0.0.dist-info/entry_points.txt | 6 - .../isort-7.0.0.dist-info/licenses/LICENSE | 21 - .../site-packages/isort/__init__.py | 39 - .../site-packages/isort/__main__.py | 3 - .../isort/_vendored/tomli/LICENSE | 21 - .../isort/_vendored/tomli/__init__.py | 6 - .../isort/_vendored/tomli/_parser.py | 650 -- .../isort/_vendored/tomli/_re.py | 100 - .../isort/_vendored/tomli/py.typed | 1 - .../site-packages/isort/_version.py | 3 - .../lib/python3.10/site-packages/isort/api.py | 660 -- .../site-packages/isort/comments.py | 29 - .../python3.10/site-packages/isort/core.py | 513 -- .../isort/deprecated/__init__.py | 0 .../site-packages/isort/deprecated/finders.py | 392 -- .../site-packages/isort/exceptions.py | 197 - .../python3.10/site-packages/isort/files.py | 41 - .../python3.10/site-packages/isort/format.py | 157 - .../python3.10/site-packages/isort/hooks.py | 93 - .../site-packages/isort/identify.py | 208 - .../lib/python3.10/site-packages/isort/io.py | 73 - .../python3.10/site-packages/isort/literal.py | 115 - .../python3.10/site-packages/isort/logo.py | 19 - .../python3.10/site-packages/isort/main.py | 1308 ---- .../python3.10/site-packages/isort/output.py | 686 -- .../python3.10/site-packages/isort/parse.py | 601 -- .../python3.10/site-packages/isort/place.py | 146 - .../site-packages/isort/profiles.py | 96 - .../python3.10/site-packages/isort/py.typed | 0 .../site-packages/isort/sections.py | 8 - .../site-packages/isort/settings.py | 933 --- .../isort/setuptools_commands.py | 63 - .../python3.10/site-packages/isort/sorting.py | 131 - .../site-packages/isort/stdlibs/__init__.py | 18 - .../site-packages/isort/stdlibs/all.py | 3 - .../site-packages/isort/stdlibs/py2.py | 3 - .../site-packages/isort/stdlibs/py27.py | 301 - .../site-packages/isort/stdlibs/py3.py | 13 - .../site-packages/isort/stdlibs/py310.py | 232 - .../site-packages/isort/stdlibs/py311.py | 232 - .../site-packages/isort/stdlibs/py312.py | 227 - .../site-packages/isort/stdlibs/py313.py | 207 - .../site-packages/isort/stdlibs/py314.py | 208 - .../site-packages/isort/stdlibs/py36.py | 224 - .../site-packages/isort/stdlibs/py37.py | 225 - .../site-packages/isort/stdlibs/py38.py | 233 - .../site-packages/isort/stdlibs/py39.py | 234 - .../python3.10/site-packages/isort/utils.py | 74 - .../python3.10/site-packages/isort/wrap.py | 147 - .../site-packages/isort/wrap_modes.py | 375 -- .../mccabe-0.7.0.dist-info/INSTALLER | 1 - .../mccabe-0.7.0.dist-info/LICENSE | 25 - .../mccabe-0.7.0.dist-info/METADATA | 199 - .../mccabe-0.7.0.dist-info/RECORD | 9 - .../mccabe-0.7.0.dist-info/WHEEL | 6 - .../mccabe-0.7.0.dist-info/entry_points.txt | 3 - .../mccabe-0.7.0.dist-info/top_level.txt | 1 - .venv/lib/python3.10/site-packages/mccabe.py | 346 - .../platformdirs-4.5.0.dist-info/INSTALLER | 1 - .../platformdirs-4.5.0.dist-info/METADATA | 350 - .../platformdirs-4.5.0.dist-info/RECORD | 22 - .../platformdirs-4.5.0.dist-info/WHEEL | 4 - .../licenses/LICENSE | 21 - .../site-packages/platformdirs/__init__.py | 631 -- .../site-packages/platformdirs/__main__.py | 55 - .../site-packages/platformdirs/android.py | 249 - .../site-packages/platformdirs/api.py | 299 - .../site-packages/platformdirs/macos.py | 146 - .../site-packages/platformdirs/py.typed | 0 .../site-packages/platformdirs/unix.py | 272 - .../site-packages/platformdirs/version.py | 34 - .../site-packages/platformdirs/windows.py | 272 - .../pylint-4.0.4.dist-info/INSTALLER | 1 - .../pylint-4.0.4.dist-info/METADATA | 277 - .../pylint-4.0.4.dist-info/RECORD | 371 -- .../pylint-4.0.4.dist-info/REQUESTED | 0 .../pylint-4.0.4.dist-info/WHEEL | 5 - .../pylint-4.0.4.dist-info/entry_points.txt | 5 - .../licenses/CONTRIBUTORS.txt | 699 -- .../pylint-4.0.4.dist-info/licenses/LICENSE | 340 - .../pylint-4.0.4.dist-info/top_level.txt | 1 - .../site-packages/pylint/__init__.py | 119 - .../site-packages/pylint/__main__.py | 10 - .../site-packages/pylint/__pkginfo__.py | 43 - .../site-packages/pylint/checkers/__init__.py | 140 - .../pylint/checkers/async_checker.py | 97 - .../pylint/checkers/bad_chained_comparison.py | 60 - .../pylint/checkers/base/__init__.py | 50 - .../pylint/checkers/base/basic_checker.py | 962 --- .../checkers/base/basic_error_checker.py | 647 -- .../checkers/base/comparison_checker.py | 352 - .../pylint/checkers/base/docstring_checker.py | 203 - .../pylint/checkers/base/function_checker.py | 149 - .../checkers/base/name_checker/__init__.py | 25 - .../checkers/base/name_checker/checker.py | 804 --- .../base/name_checker/naming_style.py | 187 - .../pylint/checkers/base/pass_checker.py | 29 - .../pylint/checkers/base_checker.py | 248 - .../pylint/checkers/classes/__init__.py | 18 - .../pylint/checkers/classes/class_checker.py | 2408 ------- .../classes/special_methods_checker.py | 403 -- .../pylint/checkers/clear_lru_cache.py | 37 - .../pylint/checkers/dataclass_checker.py | 129 - .../pylint/checkers/deprecated.py | 294 - .../pylint/checkers/design_analysis.py | 705 -- .../pylint/checkers/dunder_methods.py | 102 - .../pylint/checkers/ellipsis_checker.py | 58 - .../pylint/checkers/exceptions.py | 658 -- .../site-packages/pylint/checkers/format.py | 733 --- .../site-packages/pylint/checkers/imports.py | 1274 ---- .../pylint/checkers/lambda_expressions.py | 94 - .../site-packages/pylint/checkers/logging.py | 417 -- .../checkers/match_statements_checker.py | 230 - .../pylint/checkers/method_args.py | 129 - .../site-packages/pylint/checkers/misc.py | 192 - .../checkers/modified_iterating_checker.py | 198 - .../pylint/checkers/nested_min_max.py | 176 - .../site-packages/pylint/checkers/newstyle.py | 113 - .../pylint/checkers/non_ascii_names.py | 174 - .../pylint/checkers/raw_metrics.py | 109 - .../pylint/checkers/refactoring/__init__.py | 33 - .../implicit_booleaness_checker.py | 420 -- .../checkers/refactoring/not_checker.py | 84 - .../refactoring/recommendation_checker.py | 452 -- .../refactoring/refactoring_checker.py | 2454 ------- .../site-packages/pylint/checkers/spelling.py | 473 -- .../site-packages/pylint/checkers/stdlib.py | 1003 --- .../site-packages/pylint/checkers/strings.py | 1101 ---- .../site-packages/pylint/checkers/symilar.py | 932 --- .../pylint/checkers/threading_checker.py | 59 - .../pylint/checkers/typecheck.py | 2355 ------- .../site-packages/pylint/checkers/unicode.py | 537 -- .../pylint/checkers/unsupported_version.py | 196 - .../site-packages/pylint/checkers/utils.py | 2336 ------- .../pylint/checkers/variables.py | 3534 ---------- .../site-packages/pylint/config/__init__.py | 9 - .../config/_breaking_changes/__init__.py | 178 - .../pylint/config/_pylint_config/__init__.py | 13 - .../config/_pylint_config/generate_command.py | 49 - .../config/_pylint_config/help_message.py | 59 - .../pylint/config/_pylint_config/main.py | 25 - .../pylint/config/_pylint_config/setup.py | 49 - .../pylint/config/_pylint_config/utils.py | 110 - .../site-packages/pylint/config/argument.py | 503 -- .../pylint/config/arguments_manager.py | 402 -- .../pylint/config/arguments_provider.py | 65 - .../pylint/config/callback_actions.py | 468 -- .../pylint/config/config_file_parser.py | 129 - .../pylint/config/config_initialization.py | 206 - .../pylint/config/deprecation_actions.py | 108 - .../site-packages/pylint/config/exceptions.py | 25 - .../config/find_default_config_files.py | 150 - .../pylint/config/help_formatter.py | 64 - .../site-packages/pylint/config/utils.py | 259 - .../site-packages/pylint/constants.py | 284 - .../site-packages/pylint/exceptions.py | 53 - .../pylint/extensions/__init__.py | 20 - .../pylint/extensions/_check_docs_utils.py | 941 --- .../pylint/extensions/bad_builtin.py | 65 - .../pylint/extensions/broad_try_clause.py | 73 - .../pylint/extensions/check_elif.py | 64 - .../pylint/extensions/code_style.py | 361 -- .../pylint/extensions/comparison_placement.py | 69 - .../pylint/extensions/confusing_elif.py | 55 - ...nsider_refactoring_into_while_condition.py | 93 - .../extensions/consider_ternary_expression.py | 52 - .../pylint/extensions/dict_init_mutate.py | 54 - .../pylint/extensions/docparams.py | 697 -- .../pylint/extensions/docstyle.py | 89 - .../site-packages/pylint/extensions/dunder.py | 75 - .../pylint/extensions/empty_comment.py | 63 - .../pylint/extensions/eq_without_hash.py | 39 - .../pylint/extensions/for_any_all.py | 163 - .../pylint/extensions/magic_value.py | 119 - .../site-packages/pylint/extensions/mccabe.py | 226 - .../pylint/extensions/no_self_use.py | 111 - .../extensions/overlapping_exceptions.py | 88 - .../pylint/extensions/private_import.py | 266 - .../pylint/extensions/redefined_loop_name.py | 88 - .../extensions/redefined_variable_type.py | 108 - .../pylint/extensions/set_membership.py | 52 - .../site-packages/pylint/extensions/typing.py | 558 -- .../pylint/extensions/while_used.py | 37 - .../python3.10/site-packages/pylint/graph.py | 211 - .../site-packages/pylint/interfaces.py | 38 - .../site-packages/pylint/lint/__init__.py | 48 - .../site-packages/pylint/lint/base_options.py | 595 -- .../site-packages/pylint/lint/caching.py | 71 - .../pylint/lint/expand_modules.py | 185 - .../pylint/lint/message_state_handler.py | 444 -- .../site-packages/pylint/lint/parallel.py | 173 - .../site-packages/pylint/lint/pylinter.py | 1357 ---- .../pylint/lint/report_functions.py | 85 - .../site-packages/pylint/lint/run.py | 270 - .../site-packages/pylint/lint/utils.py | 135 - .../site-packages/pylint/message/__init__.py | 17 - .../pylint/message/_deleted_message_ids.py | 179 - .../site-packages/pylint/message/message.py | 75 - .../pylint/message/message_definition.py | 131 - .../message/message_definition_store.py | 118 - .../pylint/message/message_id_store.py | 160 - .../python3.10/site-packages/pylint/py.typed | 0 .../pylint/pyreverse/__init__.py | 7 - .../pylint/pyreverse/diadefslib.py | 295 - .../pylint/pyreverse/diagrams.py | 368 -- .../pylint/pyreverse/dot_printer.py | 190 - .../pylint/pyreverse/inspector.py | 569 -- .../site-packages/pylint/pyreverse/main.py | 367 -- .../pylint/pyreverse/mermaidjs_printer.py | 131 - .../pylint/pyreverse/plantuml_printer.py | 100 - .../site-packages/pylint/pyreverse/printer.py | 133 - .../pylint/pyreverse/printer_factory.py | 22 - .../site-packages/pylint/pyreverse/utils.py | 269 - .../site-packages/pylint/pyreverse/writer.py | 214 - .../pylint/reporters/__init__.py | 34 - .../pylint/reporters/base_reporter.py | 89 - .../pylint/reporters/collecting_reporter.py | 28 - .../pylint/reporters/json_reporter.py | 201 - .../pylint/reporters/multi_reporter.py | 111 - .../pylint/reporters/progress_reporters.py | 31 - .../reporters/reports_handler_mix_in.py | 83 - .../site-packages/pylint/reporters/text.py | 298 - .../pylint/reporters/ureports/__init__.py | 7 - .../pylint/reporters/ureports/base_writer.py | 107 - .../pylint/reporters/ureports/nodes.py | 194 - .../pylint/reporters/ureports/text_writer.py | 108 - .../pylint/testutils/__init__.py | 35 - .../pylint/testutils/_primer/__init__.py | 10 - .../testutils/_primer/package_to_lint.py | 137 - .../pylint/testutils/_primer/primer.py | 129 - .../testutils/_primer/primer_command.py | 39 - .../_primer/primer_compare_command.py | 174 - .../_primer/primer_prepare_command.py | 48 - .../testutils/_primer/primer_run_command.py | 109 - .../site-packages/pylint/testutils/_run.py | 41 - .../pylint/testutils/checker_test_case.py | 85 - .../pylint/testutils/configuration_test.py | 148 - .../pylint/testutils/constants.py | 31 - .../pylint/testutils/decorator.py | 37 - .../pylint/testutils/functional/__init__.py | 23 - .../functional/find_functional_tests.py | 144 - .../functional/lint_module_output_update.py | 43 - .../pylint/testutils/functional/test_file.py | 129 - .../pylint/testutils/get_test_info.py | 50 - .../pylint/testutils/global_test_linter.py | 20 - .../pylint/testutils/lint_module_test.py | 342 - .../pylint/testutils/output_line.py | 121 - .../pylint/testutils/pyreverse.py | 131 - .../pylint/testutils/reporter_for_tests.py | 79 - .../pylint/testutils/testing_pylintrc | 13 - .../pylint/testutils/tokenize_str.py | 13 - .../pylint/testutils/unittest_linter.py | 84 - .../site-packages/pylint/testutils/utils.py | 107 - .../python3.10/site-packages/pylint/typing.py | 138 - .../site-packages/pylint/utils/__init__.py | 49 - .../site-packages/pylint/utils/ast_walker.py | 102 - .../site-packages/pylint/utils/docs.py | 96 - .../site-packages/pylint/utils/file_state.py | 254 - .../site-packages/pylint/utils/linterstats.py | 408 -- .../pylint/utils/pragma_parser.py | 135 - .../site-packages/pylint/utils/utils.py | 335 - .../tomli-2.3.0.dist-info/INSTALLER | 1 - .../tomli-2.3.0.dist-info/METADATA | 269 - .../tomli-2.3.0.dist-info/RECORD | 14 - .../site-packages/tomli-2.3.0.dist-info/WHEEL | 4 - .../tomli-2.3.0.dist-info/licenses/LICENSE | 21 - .../site-packages/tomli/__init__.py | 8 - .../python3.10/site-packages/tomli/_parser.py | 777 --- .../lib/python3.10/site-packages/tomli/_re.py | 115 - .../python3.10/site-packages/tomli/_types.py | 10 - .../python3.10/site-packages/tomli/py.typed | 1 - .../tomlkit-0.13.3.dist-info/INSTALLER | 1 - .../tomlkit-0.13.3.dist-info/LICENSE | 20 - .../tomlkit-0.13.3.dist-info/METADATA | 76 - .../tomlkit-0.13.3.dist-info/RECORD | 32 - .../tomlkit-0.13.3.dist-info/WHEEL | 4 - .../site-packages/tomlkit/__init__.py | 59 - .../site-packages/tomlkit/_compat.py | 22 - .../site-packages/tomlkit/_types.py | 82 - .../site-packages/tomlkit/_utils.py | 158 - .../python3.10/site-packages/tomlkit/api.py | 312 - .../site-packages/tomlkit/container.py | 946 --- .../site-packages/tomlkit/exceptions.py | 234 - .../python3.10/site-packages/tomlkit/items.py | 2013 ------ .../site-packages/tomlkit/parser.py | 1140 ---- .../python3.10/site-packages/tomlkit/py.typed | 0 .../site-packages/tomlkit/source.py | 180 - .../site-packages/tomlkit/toml_char.py | 52 - .../site-packages/tomlkit/toml_document.py | 7 - .../site-packages/tomlkit/toml_file.py | 59 - .../INSTALLER | 1 - .../METADATA | 72 - .../typing_extensions-4.15.0.dist-info/RECORD | 7 - .../typing_extensions-4.15.0.dist-info/WHEEL | 4 - .../licenses/LICENSE | 279 - .../site-packages/typing_extensions.py | 4317 ------------- 4_data_analysis/model_artifacts/.gitignore | 9 + 4_data_analysis/model_artifacts/.ls-lint.yml | 23 + {notes => 4_data_analysis}/pyproject.toml | 0 467 files changed, 34 insertions(+), 117037 deletions(-) delete mode 100755 .venv/bin/get_gprof delete mode 100755 .venv/bin/get_objgraph delete mode 100755 .venv/bin/isort delete mode 100755 .venv/bin/isort-identify-imports delete mode 100755 .venv/bin/pylint delete mode 100755 .venv/bin/pylint-config delete mode 100755 .venv/bin/pyreverse delete mode 100755 .venv/bin/symilar delete mode 100755 .venv/bin/undill delete mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA delete mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD delete mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL delete mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt delete mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt delete mode 100644 .venv/lib/python3.10/site-packages/astroid/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/__pkginfo__.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/_ast.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/arguments.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/astroid_manager.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/bases.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_http.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_io.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_random.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_re.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_six.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_type.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/brain/helpers.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/builder.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/const.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/constraint.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/context.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/decorators.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/exceptions.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/filter_statements.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/helpers.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/inference_tip.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/manager.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/modutils.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/as_string.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/const.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/nodes/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/objects.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/protocols.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/raw_building.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/rebuilder.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/test_utils.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/transforms.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/typing.py delete mode 100644 .venv/lib/python3.10/site-packages/astroid/util.py delete mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA delete mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD delete mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL delete mode 100644 .venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt delete mode 100644 .venv/lib/python3.10/site-packages/dill/__diff.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/__info__.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/_dill.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/_objects.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/_shims.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/detect.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/logger.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/objtypes.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/pointers.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/session.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/settings.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/source.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/temp.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/__main__.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_abc.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_check.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_classdef.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_detect.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_diff.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_file.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_functions.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_functors.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_logger.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_mixins.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_module.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_nested.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_objects.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_properties.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_recursive.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_registered.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_restricted.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_selected.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_session.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_source.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_sources.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_temp.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_threads.py delete mode 100644 .venv/lib/python3.10/site-packages/dill/tests/test_weakref.py delete mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA delete mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD delete mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL delete mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt delete mode 100644 .venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/isort/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/__main__.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed delete mode 100644 .venv/lib/python3.10/site-packages/isort/_version.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/api.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/comments.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/core.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/deprecated/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/deprecated/finders.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/exceptions.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/files.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/format.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/hooks.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/identify.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/io.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/literal.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/logo.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/main.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/output.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/parse.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/place.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/profiles.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/py.typed delete mode 100644 .venv/lib/python3.10/site-packages/isort/sections.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/settings.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/setuptools_commands.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/sorting.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/all.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py2.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py27.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py3.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py310.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py311.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py312.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py313.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py314.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py36.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py37.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py38.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/stdlibs/py39.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/wrap.py delete mode 100644 .venv/lib/python3.10/site-packages/isort/wrap_modes.py delete mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA delete mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD delete mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL delete mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt delete mode 100644 .venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt delete mode 100644 .venv/lib/python3.10/site-packages/mccabe.py delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs/__main__.py delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs/android.py delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs/api.py delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs/macos.py delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs/py.typed delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs/unix.py delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs/version.py delete mode 100644 .venv/lib/python3.10/site-packages/platformdirs/windows.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA delete mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD delete mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/REQUESTED delete mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL delete mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt delete mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt delete mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt delete mode 100644 .venv/lib/python3.10/site-packages/pylint/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/__main__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/__pkginfo__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base/pass_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/base_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/classes/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/classes/class_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/classes/special_methods_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/clear_lru_cache.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/dataclass_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/deprecated.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/design_analysis.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/dunder_methods.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/ellipsis_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/exceptions.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/format.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/imports.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/lambda_expressions.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/logging.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/match_statements_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/method_args.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/misc.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/modified_iterating_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/nested_min_max.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/newstyle.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/non_ascii_names.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/raw_metrics.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/implicit_booleaness_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/not_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/recommendation_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/refactoring/refactoring_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/spelling.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/stdlib.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/strings.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/symilar.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/threading_checker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/typecheck.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/unicode.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/unsupported_version.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/checkers/variables.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_breaking_changes/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/generate_command.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/help_message.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/main.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/setup.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/_pylint_config/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/argument.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/arguments_manager.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/arguments_provider.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/callback_actions.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/config_file_parser.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/config_initialization.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/deprecation_actions.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/exceptions.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/find_default_config_files.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/help_formatter.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/config/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/constants.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/exceptions.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/_check_docs_utils.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/bad_builtin.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/broad_try_clause.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/check_elif.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/code_style.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/comparison_placement.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/confusing_elif.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/consider_refactoring_into_while_condition.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/consider_ternary_expression.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/dict_init_mutate.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/docparams.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/docstyle.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/dunder.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/empty_comment.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/eq_without_hash.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/for_any_all.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/magic_value.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/mccabe.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/no_self_use.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/overlapping_exceptions.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/private_import.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/redefined_loop_name.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/redefined_variable_type.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/set_membership.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/typing.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/extensions/while_used.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/graph.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/interfaces.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/base_options.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/caching.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/expand_modules.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/message_state_handler.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/parallel.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/pylinter.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/report_functions.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/run.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/lint/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/message/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/message/_deleted_message_ids.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/message/message.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/message/message_definition.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/message/message_definition_store.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/message/message_id_store.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/py.typed delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/diadefslib.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/diagrams.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/dot_printer.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/inspector.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/main.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/mermaidjs_printer.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/plantuml_printer.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/printer.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/printer_factory.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/pyreverse/writer.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/base_reporter.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/collecting_reporter.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/json_reporter.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/multi_reporter.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/progress_reporters.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/reports_handler_mix_in.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/text.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/ureports/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/ureports/base_writer.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/ureports/nodes.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/reporters/ureports/text_writer.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/package_to_lint.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_command.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_compare_command.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_prepare_command.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_primer/primer_run_command.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/_run.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/checker_test_case.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/configuration_test.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/constants.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/decorator.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/functional/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/functional/find_functional_tests.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/functional/lint_module_output_update.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/functional/test_file.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/get_test_info.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/global_test_linter.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/lint_module_test.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/output_line.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/pyreverse.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/reporter_for_tests.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/testing_pylintrc delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/tokenize_str.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/unittest_linter.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/testutils/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/typing.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/ast_walker.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/docs.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/file_state.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/linterstats.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/pragma_parser.py delete mode 100644 .venv/lib/python3.10/site-packages/pylint/utils/utils.py delete mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/METADATA delete mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/RECORD delete mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/WHEEL delete mode 100644 .venv/lib/python3.10/site-packages/tomli-2.3.0.dist-info/licenses/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/tomli/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/tomli/_parser.py delete mode 100644 .venv/lib/python3.10/site-packages/tomli/_re.py delete mode 100644 .venv/lib/python3.10/site-packages/tomli/_types.py delete mode 100644 .venv/lib/python3.10/site-packages/tomli/py.typed delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/METADATA delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/RECORD delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit-0.13.3.dist-info/WHEEL delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/__init__.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/_compat.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/_types.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/_utils.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/api.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/container.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/exceptions.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/items.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/parser.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/py.typed delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/source.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/toml_char.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/toml_document.py delete mode 100644 .venv/lib/python3.10/site-packages/tomlkit/toml_file.py delete mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/METADATA delete mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/RECORD delete mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/WHEEL delete mode 100644 .venv/lib/python3.10/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE delete mode 100644 .venv/lib/python3.10/site-packages/typing_extensions.py create mode 100644 4_data_analysis/model_artifacts/.gitignore create mode 100644 4_data_analysis/model_artifacts/.ls-lint.yml rename {notes => 4_data_analysis}/pyproject.toml (100%) diff --git a/.gitignore b/.gitignore index b1c6cec..9d2e60a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ __pycache__ *.pytest_cache -venv/ +.venv/ + # .env *.db *.idea diff --git a/.venv/bin/get_gprof b/.venv/bin/get_gprof deleted file mode 100755 index 749ade0..0000000 --- a/.venv/bin/get_gprof +++ /dev/null @@ -1,75 +0,0 @@ -#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -''' -build profile graph for the given instance - -running: - $ get_gprof - -executes: - gprof2dot -f pstats .prof | dot -Tpng -o .call.png - -where: - are arguments for gprof2dot, such as "-n 5 -e 5" - is code to create the instance to profile - is the class of the instance (i.e. type(instance)) - -For example: - $ get_gprof -n 5 -e 1 "import numpy; numpy.array([1,2])" - -will create 'ndarray.call.png' with the profile graph for numpy.array([1,2]), -where '-n 5' eliminates nodes below 5% threshold, similarly '-e 1' eliminates -edges below 1% threshold -''' - -if __name__ == "__main__": - import sys - if len(sys.argv) < 2: - print ("Please provide an object instance (e.g. 'import math; math.pi')") - sys.exit() - # grab args for gprof2dot - args = sys.argv[1:-1] - args = ' '.join(args) - # last arg builds the object - obj = sys.argv[-1] - obj = obj.split(';') - # multi-line prep for generating an instance - for line in obj[:-1]: - exec(line) - # one-line generation of an instance - try: - obj = eval(obj[-1]) - except Exception: - print ("Error processing object instance") - sys.exit() - - # get object 'name' - objtype = type(obj) - name = getattr(objtype, '__name__', getattr(objtype, '__class__', objtype)) - - # profile dumping an object - import dill - import os - import cProfile - #name = os.path.splitext(os.path.basename(__file__))[0] - cProfile.run("dill.dumps(obj)", filename="%s.prof" % name) - msg = "gprof2dot -f pstats %s %s.prof | dot -Tpng -o %s.call.png" % (args, name, name) - try: - res = os.system(msg) - except Exception: - print ("Please verify install of 'gprof2dot' to view profile graphs") - if res: - print ("Please verify install of 'gprof2dot' to view profile graphs") - - # get stats - f_prof = "%s.prof" % name - import pstats - stats = pstats.Stats(f_prof, stream=sys.stdout) - stats.strip_dirs().sort_stats('cumtime') - stats.print_stats(20) #XXX: save to file instead of print top 20? - os.remove(f_prof) diff --git a/.venv/bin/get_objgraph b/.venv/bin/get_objgraph deleted file mode 100755 index a9944aa..0000000 --- a/.venv/bin/get_objgraph +++ /dev/null @@ -1,54 +0,0 @@ -#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -display the reference paths for objects in ``dill.types`` or a .pkl file - -Notes: - the generated image is useful in showing the pointer references in - objects that are or can be pickled. Any object in ``dill.objects`` - listed in ``dill.load_types(picklable=True, unpicklable=True)`` works. - -Examples:: - - $ get_objgraph ArrayType - Image generated as ArrayType.png -""" - -import dill as pickle -#pickle.debug.trace(True) -#import pickle - -# get all objects for testing -from dill import load_types -load_types(pickleable=True,unpickleable=True) -from dill import objects - -if __name__ == "__main__": - import sys - if len(sys.argv) != 2: - print ("Please provide exactly one file or type name (e.g. 'IntType')") - msg = "\n" - for objtype in list(objects.keys())[:40]: - msg += objtype + ', ' - print (msg + "...") - else: - objtype = str(sys.argv[-1]) - try: - obj = objects[objtype] - except KeyError: - obj = pickle.load(open(objtype,'rb')) - import os - objtype = os.path.splitext(objtype)[0] - try: - import objgraph - objgraph.show_refs(obj, filename=objtype+'.png') - except ImportError: - print ("Please install 'objgraph' to view object graphs") - - -# EOF diff --git a/.venv/bin/isort b/.venv/bin/isort deleted file mode 100755 index a91b31e..0000000 --- a/.venv/bin/isort +++ /dev/null @@ -1,7 +0,0 @@ -#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 -import sys -from isort.main import main -if __name__ == '__main__': - if sys.argv[0].endswith('.exe'): - sys.argv[0] = sys.argv[0][:-4] - sys.exit(main()) diff --git a/.venv/bin/isort-identify-imports b/.venv/bin/isort-identify-imports deleted file mode 100755 index 6c012b8..0000000 --- a/.venv/bin/isort-identify-imports +++ /dev/null @@ -1,7 +0,0 @@ -#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 -import sys -from isort.main import identify_imports_main -if __name__ == '__main__': - if sys.argv[0].endswith('.exe'): - sys.argv[0] = sys.argv[0][:-4] - sys.exit(identify_imports_main()) diff --git a/.venv/bin/pylint b/.venv/bin/pylint deleted file mode 100755 index 949f9c5..0000000 --- a/.venv/bin/pylint +++ /dev/null @@ -1,7 +0,0 @@ -#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 -import sys -from pylint import run_pylint -if __name__ == '__main__': - if sys.argv[0].endswith('.exe'): - sys.argv[0] = sys.argv[0][:-4] - sys.exit(run_pylint()) diff --git a/.venv/bin/pylint-config b/.venv/bin/pylint-config deleted file mode 100755 index 1778e49..0000000 --- a/.venv/bin/pylint-config +++ /dev/null @@ -1,7 +0,0 @@ -#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 -import sys -from pylint import _run_pylint_config -if __name__ == '__main__': - if sys.argv[0].endswith('.exe'): - sys.argv[0] = sys.argv[0][:-4] - sys.exit(_run_pylint_config()) diff --git a/.venv/bin/pyreverse b/.venv/bin/pyreverse deleted file mode 100755 index d291842..0000000 --- a/.venv/bin/pyreverse +++ /dev/null @@ -1,7 +0,0 @@ -#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 -import sys -from pylint import run_pyreverse -if __name__ == '__main__': - if sys.argv[0].endswith('.exe'): - sys.argv[0] = sys.argv[0][:-4] - sys.exit(run_pyreverse()) diff --git a/.venv/bin/symilar b/.venv/bin/symilar deleted file mode 100755 index c4f2f0a..0000000 --- a/.venv/bin/symilar +++ /dev/null @@ -1,7 +0,0 @@ -#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 -import sys -from pylint import run_symilar -if __name__ == '__main__': - if sys.argv[0].endswith('.exe'): - sys.argv[0] = sys.argv[0][:-4] - sys.exit(run_symilar()) diff --git a/.venv/bin/undill b/.venv/bin/undill deleted file mode 100755 index 32e3531..0000000 --- a/.venv/bin/undill +++ /dev/null @@ -1,22 +0,0 @@ -#!/home/clif_lastrophysicien/ELO2_LAPERLE_HT/.venv/bin/python3 -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -unpickle the contents of a pickled object file - -Examples:: - - $ undill hello.pkl - ['hello', 'world'] -""" - -if __name__ == '__main__': - import sys - import dill - for file in sys.argv[1:]: - print (dill.load(open(file,'rb'))) - diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA deleted file mode 100644 index 33d9aa9..0000000 --- a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/METADATA +++ /dev/null @@ -1,122 +0,0 @@ -Metadata-Version: 2.4 -Name: astroid -Version: 4.0.2 -Summary: An abstract syntax tree for Python with inference support. -License-Expression: LGPL-2.1-or-later -Project-URL: Bug tracker, https://github.com/pylint-dev/astroid/issues -Project-URL: Discord server, https://discord.gg/Egy6P8AMB5 -Project-URL: Docs, https://pylint.readthedocs.io/projects/astroid/en/latest/ -Project-URL: Source Code, https://github.com/pylint-dev/astroid -Keywords: abstract syntax tree,python,static code analysis -Classifier: Development Status :: 6 - Mature -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Software Development :: Quality Assurance -Classifier: Topic :: Software Development :: Testing -Requires-Python: >=3.10.0 -Description-Content-Type: text/x-rst -License-File: LICENSE -License-File: CONTRIBUTORS.txt -Requires-Dist: typing-extensions>=4; python_version < "3.11" -Dynamic: license-file - -Astroid -======= - -.. image:: https://codecov.io/gh/pylint-dev/astroid/branch/main/graph/badge.svg?token=Buxy4WptLb - :target: https://codecov.io/gh/pylint-dev/astroid - :alt: Coverage badge from codecov - -.. image:: https://readthedocs.org/projects/astroid/badge/?version=latest - :target: http://astroid.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status - -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/ambv/black - -.. image:: https://results.pre-commit.ci/badge/github/pylint-dev/astroid/main.svg - :target: https://results.pre-commit.ci/latest/github/pylint-dev/astroid/main - :alt: pre-commit.ci status - -.. |tidelift_logo| image:: https://raw.githubusercontent.com/pylint-dev/astroid/main/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png - :width: 200 - :alt: Tidelift - -.. list-table:: - :widths: 10 100 - - * - |tidelift_logo| - - Professional support for astroid is available as part of the - `Tidelift Subscription`_. Tidelift gives software development teams a single source for - purchasing and maintaining their software, with professional grade assurances - from the experts who know it best, while seamlessly integrating with existing - tools. - -.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-astroid?utm_source=pypi-astroid&utm_medium=referral&utm_campaign=readme - - - -What's this? ------------- - -The aim of this module is to provide a common base representation of -python source code. It is currently the library powering pylint's capabilities. - -It provides a compatible representation which comes from the `_ast` -module. It rebuilds the tree generated by the builtin _ast module by -recursively walking down the AST and building an extended ast. The new -node classes have additional methods and attributes for different -usages. They include some support for static inference and local name -scopes. Furthermore, astroid can also build partial trees by inspecting living -objects. - - -Installation ------------- - -Extract the tarball, jump into the created directory and run:: - - pip install . - - -If you want to do an editable installation, you can run:: - - pip install -e . - - -If you have any questions, please mail the code-quality@python.org -mailing list for support. See -http://mail.python.org/mailman/listinfo/code-quality for subscription -information and archives. - -Documentation -------------- -http://astroid.readthedocs.io/en/latest/ - - -Python Versions ---------------- - -astroid 2.0 is currently available for Python 3 only. If you want Python 2 -support, use an older version of astroid (though note that these versions -are no longer supported). - -Test ----- - -Tests are in the 'test' subdirectory. To launch the whole tests suite, you can use -either `tox` or `pytest`:: - - tox - pytest diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD deleted file mode 100644 index 9c8ada9..0000000 --- a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/RECORD +++ /dev/null @@ -1,197 +0,0 @@ -astroid-4.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -astroid-4.0.2.dist-info/METADATA,sha256=agB87FwwkVcjxaOosr7YNv7hQ1ANIYy4sJncPUwX98s,4382 -astroid-4.0.2.dist-info/RECORD,, -astroid-4.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 -astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt,sha256=c0v8W544hmspwqUGRfzPIcVh6vzof-I35MHcoIl0JEs,9339 -astroid-4.0.2.dist-info/licenses/LICENSE,sha256=_qFr2p5zTeoNnI2fW5CYeO9BcWJjVDKWCf_889tCyyQ,26516 -astroid-4.0.2.dist-info/top_level.txt,sha256=HsdW4O2x7ZXRj6k-agi3RaQybGLobI3VSE-jt4vQUXM,8 -astroid/__init__.py,sha256=FA-4lFE-nkNgJ9nLhh6BDYb2_rIgpFxaS0UX_LFUqC4,7718 -astroid/__pkginfo__.py,sha256=SRnk6MFqpPf0RFnriaY_nHFCzBor39Im0vy9HyYvook,283 -astroid/__pycache__/__init__.cpython-310.pyc,, -astroid/__pycache__/__pkginfo__.cpython-310.pyc,, -astroid/__pycache__/_ast.cpython-310.pyc,, -astroid/__pycache__/arguments.cpython-310.pyc,, -astroid/__pycache__/astroid_manager.cpython-310.pyc,, -astroid/__pycache__/bases.cpython-310.pyc,, -astroid/__pycache__/builder.cpython-310.pyc,, -astroid/__pycache__/const.cpython-310.pyc,, -astroid/__pycache__/constraint.cpython-310.pyc,, -astroid/__pycache__/context.cpython-310.pyc,, -astroid/__pycache__/decorators.cpython-310.pyc,, -astroid/__pycache__/exceptions.cpython-310.pyc,, -astroid/__pycache__/filter_statements.cpython-310.pyc,, -astroid/__pycache__/helpers.cpython-310.pyc,, -astroid/__pycache__/inference_tip.cpython-310.pyc,, -astroid/__pycache__/manager.cpython-310.pyc,, -astroid/__pycache__/modutils.cpython-310.pyc,, -astroid/__pycache__/objects.cpython-310.pyc,, -astroid/__pycache__/protocols.cpython-310.pyc,, -astroid/__pycache__/raw_building.cpython-310.pyc,, -astroid/__pycache__/rebuilder.cpython-310.pyc,, -astroid/__pycache__/test_utils.cpython-310.pyc,, -astroid/__pycache__/transforms.cpython-310.pyc,, -astroid/__pycache__/typing.cpython-310.pyc,, -astroid/__pycache__/util.cpython-310.pyc,, -astroid/_ast.py,sha256=LLKPdNWj6Qk4u3yj9wid9zIOsKf_YV4Yfr8L4EA285U,3003 -astroid/arguments.py,sha256=UwoLKjjgYrmtTHWRTEXuqz4g4elodx_DJIR5vBkCfzo,13094 -astroid/astroid_manager.py,sha256=uGIFUKDTjCdy757OF60r3apRqVybugBZh1rudtOSGMA,729 -astroid/bases.py,sha256=IJyVeO6ssO3QmBZ_2C4_yJO2YvxhtolJPlNisqM2k6A,28007 -astroid/brain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -astroid/brain/__pycache__/__init__.cpython-310.pyc,, -astroid/brain/__pycache__/brain_argparse.cpython-310.pyc,, -astroid/brain/__pycache__/brain_attrs.cpython-310.pyc,, -astroid/brain/__pycache__/brain_boto3.cpython-310.pyc,, -astroid/brain/__pycache__/brain_builtin_inference.cpython-310.pyc,, -astroid/brain/__pycache__/brain_collections.cpython-310.pyc,, -astroid/brain/__pycache__/brain_crypt.cpython-310.pyc,, -astroid/brain/__pycache__/brain_ctypes.cpython-310.pyc,, -astroid/brain/__pycache__/brain_curses.cpython-310.pyc,, -astroid/brain/__pycache__/brain_dataclasses.cpython-310.pyc,, -astroid/brain/__pycache__/brain_datetime.cpython-310.pyc,, -astroid/brain/__pycache__/brain_dateutil.cpython-310.pyc,, -astroid/brain/__pycache__/brain_functools.cpython-310.pyc,, -astroid/brain/__pycache__/brain_gi.cpython-310.pyc,, -astroid/brain/__pycache__/brain_hashlib.cpython-310.pyc,, -astroid/brain/__pycache__/brain_http.cpython-310.pyc,, -astroid/brain/__pycache__/brain_hypothesis.cpython-310.pyc,, -astroid/brain/__pycache__/brain_io.cpython-310.pyc,, -astroid/brain/__pycache__/brain_mechanize.cpython-310.pyc,, -astroid/brain/__pycache__/brain_multiprocessing.cpython-310.pyc,, -astroid/brain/__pycache__/brain_namedtuple_enum.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_core_einsumfunc.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_core_umath.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_ma.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_ndarray.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-310.pyc,, -astroid/brain/__pycache__/brain_numpy_utils.cpython-310.pyc,, -astroid/brain/__pycache__/brain_pathlib.cpython-310.pyc,, -astroid/brain/__pycache__/brain_pkg_resources.cpython-310.pyc,, -astroid/brain/__pycache__/brain_pytest.cpython-310.pyc,, -astroid/brain/__pycache__/brain_qt.cpython-310.pyc,, -astroid/brain/__pycache__/brain_random.cpython-310.pyc,, -astroid/brain/__pycache__/brain_re.cpython-310.pyc,, -astroid/brain/__pycache__/brain_regex.cpython-310.pyc,, -astroid/brain/__pycache__/brain_responses.cpython-310.pyc,, -astroid/brain/__pycache__/brain_scipy_signal.cpython-310.pyc,, -astroid/brain/__pycache__/brain_signal.cpython-310.pyc,, -astroid/brain/__pycache__/brain_six.cpython-310.pyc,, -astroid/brain/__pycache__/brain_sqlalchemy.cpython-310.pyc,, -astroid/brain/__pycache__/brain_ssl.cpython-310.pyc,, -astroid/brain/__pycache__/brain_statistics.cpython-310.pyc,, -astroid/brain/__pycache__/brain_subprocess.cpython-310.pyc,, -astroid/brain/__pycache__/brain_threading.cpython-310.pyc,, -astroid/brain/__pycache__/brain_type.cpython-310.pyc,, -astroid/brain/__pycache__/brain_typing.cpython-310.pyc,, -astroid/brain/__pycache__/brain_unittest.cpython-310.pyc,, -astroid/brain/__pycache__/brain_uuid.cpython-310.pyc,, -astroid/brain/__pycache__/helpers.cpython-310.pyc,, -astroid/brain/brain_argparse.py,sha256=SxYW42PcxTv0YM4VGAS_K8iYnYDxZ5LQZeptooyIMo0,1725 -astroid/brain/brain_attrs.py,sha256=y6xnsBRIhXiJucZPjtBsGM-hNVMMOttBnxIHNE65YRU,3567 -astroid/brain/brain_boto3.py,sha256=efZTk72fPOe6wnEVCH9bIBFWxcQDlS5xrzuNakpR06Y,1112 -astroid/brain/brain_builtin_inference.py,sha256=WApCZIQF5FkFOO83PRdGt2I3vNCMEg8h5gGPwC3E6-U,37758 -astroid/brain/brain_collections.py,sha256=VetyHBQO9nWQmaDeujEOfZ92JANUpgIAWLH5-jT6uZ4,4787 -astroid/brain/brain_crypt.py,sha256=UKa9GpbK7b_N2fAx4-KNqr4Pvsg3EI1Mu1T3uuV3CcE,957 -astroid/brain/brain_ctypes.py,sha256=ZlOQHSIyvslgdYC_a0hOop3_I5uBh3afbQ8ZuaZxke0,2762 -astroid/brain/brain_curses.py,sha256=Ne_30je70tZpUyobl3b0Y5ATZhxolYCxfP-oLB4eciM,3571 -astroid/brain/brain_dataclasses.py,sha256=gzBGXp0cDeq-LV1spl-2kZtfKDltJdGni0c5SQg6suE,22119 -astroid/brain/brain_datetime.py,sha256=q9kjCi-Fz5S-u4Hyv88mJvCgXYvk2X8eEUTaw637lK4,813 -astroid/brain/brain_dateutil.py,sha256=y_GyQYVf_UR6zTOcgmVm3uQhkxVCVVzRD0Ow4Gg1jFI,861 -astroid/brain/brain_functools.py,sha256=I8nxIKGTcQKq5ZeGAiFbm3ErlZfLnpdUYy31pLXxwxc,6401 -astroid/brain/brain_gi.py,sha256=krGyADdWRjm7w-ZVTHbx77hylNgs8ziwZ1--RU6QhxM,7662 -astroid/brain/brain_hashlib.py,sha256=c76QAbD-LYkKMVrdAe6nRboJdG3l1LkK0UAZOlxfSFM,2799 -astroid/brain/brain_http.py,sha256=Vj7mN4uU8qROy-p9BjLOhF9_HTZ0wFbiNuDgi2uXKY4,11668 -astroid/brain/brain_hypothesis.py,sha256=8fwM59eqVgKEL3Dkz2Uva62v0-AZhBPyFxXyOs65E0g,1885 -astroid/brain/brain_io.py,sha256=YV_hxg1EGjV3Wprvp5M_YF8pWoC2MK1oyyH3io6hZpk,1589 -astroid/brain/brain_mechanize.py,sha256=-80L1FfddRUM-qENpAZ5sro9P9Kgz2uchwjjM5ErJJk,2740 -astroid/brain/brain_multiprocessing.py,sha256=ATm2DwP-SXt4LH6hhwN-7ova5Bt483efnuKzKkulmfQ,3260 -astroid/brain/brain_namedtuple_enum.py,sha256=KDWuKv3mAHE1IYXu7CMCb9bMM7vmQnxY8ON96jgUuVk,24278 -astroid/brain/brain_numpy_core_einsumfunc.py,sha256=YfPheziU5UKb80hXS0VZcWVHmw_lQ1t0-Q-w13dwQyM,885 -astroid/brain/brain_numpy_core_fromnumeric.py,sha256=Aum9F0w1An28elWtvBcTHzohV0YCkIOqg8nIqkm179c,834 -astroid/brain/brain_numpy_core_function_base.py,sha256=qSusSR1lilumpc6T8mrELunZS3zgUuuouh-qzNBiBcc,1356 -astroid/brain/brain_numpy_core_multiarray.py,sha256=s9adpaJ8SAk3WTgSEuNgCUwx8HOGt_vyZdXl3QJ1OmE,4408 -astroid/brain/brain_numpy_core_numeric.py,sha256=B95vP691ynDRXFqhg_VI1TKyWkxGbdCZji3M8qsyTmc,1692 -astroid/brain/brain_numpy_core_numerictypes.py,sha256=Bgc4_qguu3ypTIr74STkeUu79OhtLwrD0jVUFSBZ1wA,8648 -astroid/brain/brain_numpy_core_umath.py,sha256=M2_oE2HGcD5DEcU3W2_CvENjxjuOxfLPvnSgFjtmpU4,4981 -astroid/brain/brain_numpy_ma.py,sha256=3UnzHTkN8H6yxI8km6bBZoWyV80U3vLIzfFyD8gEOLo,990 -astroid/brain/brain_numpy_ndarray.py,sha256=qoNm2LSOkaerQus6wOj9Lxbjgh7SAg4bEbfPQbZKO6c,9034 -astroid/brain/brain_numpy_random_mtrand.py,sha256=d3g_v9X1e4Wr8I9EE8g7E0d-hqGQk_gYvByj4-1HC5w,3538 -astroid/brain/brain_numpy_utils.py,sha256=03ztDMdAFnQLFmTOiEeABcSBv_gL46Mpi9rO2rbw0M0,2893 -astroid/brain/brain_pathlib.py,sha256=jX_TTTpf4wp1lePKTSGFFjbQVSa13fXafAfdIqow9JM,1728 -astroid/brain/brain_pkg_resources.py,sha256=rfjLwAejZVqM_s721UdAPbuEMyM0fx3sb1yYkPDeDqE,2302 -astroid/brain/brain_pytest.py,sha256=tqisdV1Cw2vdYdjTNnF_jBMO2jO7lw86Fjh6XpZoJ1g,2312 -astroid/brain/brain_qt.py,sha256=yNle65lxFYmRJksiaqksF_FINvae732yvcOl6PVdIvM,2874 -astroid/brain/brain_random.py,sha256=VMVXkVHEhdQ8S5VaW-tHS3hQYmQj4XI66Y9tV1qDjTE,3110 -astroid/brain/brain_re.py,sha256=-Dh05FNgXrWxOS-mrajf7LFUgozA7qFQzfjs8SwNoxU,2999 -astroid/brain/brain_regex.py,sha256=GI-TCq6gGAE9OYwyl2Bi9auJINJn0uF8ADBYekyKZyU,3466 -astroid/brain/brain_responses.py,sha256=vC9JUtdjtN2W5ux8_6G7QG_nhbZOBV3lMpy1fG56nzc,1962 -astroid/brain/brain_scipy_signal.py,sha256=HPGStodLs9L9APFnc77RZBf-PIlaKm-COdyTSk2KVnM,2370 -astroid/brain/brain_signal.py,sha256=Qt8cFsEwYmwaI_PU5aBBfYV4TLkF0lBPdME-oeGWMYk,3932 -astroid/brain/brain_six.py,sha256=ps49tRfn6NRtTpjmcBOfrF8shznz0T1SB7Pn1teWTIs,7772 -astroid/brain/brain_sqlalchemy.py,sha256=8JlRq_TCEheDtbc2BIupibHRtEZ4WasqLEPkkZ5sziY,1103 -astroid/brain/brain_ssl.py,sha256=MOU0L1wh-ysKzACAKVTnpCca6awwwsF09OcJi5-NKfk,6712 -astroid/brain/brain_statistics.py,sha256=howkDIgRSFh6Zy20ZS2OCN4HQxfEStiIwsICMOZ5bi4,2786 -astroid/brain/brain_subprocess.py,sha256=dj-Y3S_QbWaIsDA6j8a74xEwPd5Fe5J1uj1YDFDrcrQ,2962 -astroid/brain/brain_threading.py,sha256=3XS9DNglTezqvBUlWTKbhjWLgkjXmQR9w-p5sNPJLO4,964 -astroid/brain/brain_type.py,sha256=sThdsmoQwUxIe04m5Wb6WnouGk1ZmZWtBo6AfXqWLsA,2497 -astroid/brain/brain_typing.py,sha256=b5Lvle7K9vhxzvFqH3BOwB8aYLPjiex-6FQCdupDvZk,17237 -astroid/brain/brain_unittest.py,sha256=ejWKg85j26dfKWHwycKztGs4_RSPTtqs9mTF3XKfsyA,1178 -astroid/brain/brain_uuid.py,sha256=K1so-CZChY4lqI-qWQbdrrJu_z0gJPPG4h4J3Iz9FoU,678 -astroid/brain/helpers.py,sha256=VIf9Yy6SVaXBQ0Lh1oLEa0szhJRXuyTR4qANXhAeJBs,4743 -astroid/builder.py,sha256=pk_U8QDLwQmwZMnDRK7Z8EnJjeCLHV_7msGIf1AgVGE,19169 -astroid/const.py,sha256=dcdtuOYk6xd_5K9kLZcijEp93kXYzpb_GrmHBGccs4Q,694 -astroid/constraint.py,sha256=BzI-DMG1Vp3eapvmVjahYEL9VYSRoluGkTscokNUipc,6483 -astroid/context.py,sha256=xseGQ6wscT5eOPiLl7nd6nS7Mfwi3KrQGSScPlBU6h4,6293 -astroid/decorators.py,sha256=aUEm2t4UM31X8vpGi5BJb3jOcK44LtZQRUl9H1Dlqh4,8531 -astroid/exceptions.py,sha256=9OrH8LliIRyv85CQEmm0br0R0XWuRJ74tmIDAv2QR_s,12935 -astroid/filter_statements.py,sha256=CJRqKTLsspIfHlLjHQsEpfLjscjPoOtLWdLuVhb7OoM,9451 -astroid/helpers.py,sha256=7r3xj2r2DriL9J8IbuIlmim-T7aC1Jog7GbJ8R6TM7U,11819 -astroid/inference_tip.py,sha256=5Dsfn-9qie1Wuzc2XaWzhlPQQxD3fGt64RR3nyptvHc,4586 -astroid/interpreter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -astroid/interpreter/__pycache__/__init__.cpython-310.pyc,, -astroid/interpreter/__pycache__/dunder_lookup.cpython-310.pyc,, -astroid/interpreter/__pycache__/objectmodel.cpython-310.pyc,, -astroid/interpreter/_import/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -astroid/interpreter/_import/__pycache__/__init__.cpython-310.pyc,, -astroid/interpreter/_import/__pycache__/spec.cpython-310.pyc,, -astroid/interpreter/_import/__pycache__/util.cpython-310.pyc,, -astroid/interpreter/_import/spec.py,sha256=uVQdyJ7OTJzTM50EVmyXmN2bMUR_D-EXI36CC-NTPBU,17613 -astroid/interpreter/_import/util.py,sha256=XCvSEZ_iTHUfvIX9YrPjp4LVgA0IuJSsOnLwlpuGN6A,4675 -astroid/interpreter/dunder_lookup.py,sha256=miAYo7UTVOhfMYpo44qdV3WJEk1P__JVTGWGPtT1wnk,2487 -astroid/interpreter/objectmodel.py,sha256=gP8LsWsw3uzXWh4QhecNom27Q8N7MKN4ipGMg_abSuo,34180 -astroid/manager.py,sha256=adI6Dc8b0HqScUoGet-FzV90CHguHpJ-s2Ne-fwgdOM,18331 -astroid/modutils.py,sha256=KAyhpORelvLrHnAVoiX5HDhDnMCuAHhoqaox2E6rSdw,23458 -astroid/nodes/__init__.py,sha256=X6V4GWkgUL8AuqfzK_Ns7jpCM_RK14UMyY8KMhEqZ8o,4862 -astroid/nodes/__pycache__/__init__.cpython-310.pyc,, -astroid/nodes/__pycache__/_base_nodes.cpython-310.pyc,, -astroid/nodes/__pycache__/as_string.cpython-310.pyc,, -astroid/nodes/__pycache__/const.cpython-310.pyc,, -astroid/nodes/__pycache__/node_classes.cpython-310.pyc,, -astroid/nodes/__pycache__/node_ng.cpython-310.pyc,, -astroid/nodes/__pycache__/utils.cpython-310.pyc,, -astroid/nodes/_base_nodes.py,sha256=IY3vZ4vEvsAepGTvrPf7zm11BPTlRQUndzyT9CmEsZ8,23927 -astroid/nodes/as_string.py,sha256=ap_dHTQTE9xyxb6GndRrd4TS4xdfhDadCW0NcGmktqQ,29106 -astroid/nodes/const.py,sha256=aD7rKF5kPM2UFJAR5pzZDAM-Zm7p9LG57E-9FjB1gTc,807 -astroid/nodes/node_classes.py,sha256=G4zKRdUNdp-u-wJWPdE72J5mBN5_bXkqKo6g5txIDwo,177422 -astroid/nodes/node_ng.py,sha256=SzcLmkZiuv2Pk4q6iPSfokPRdGaM9VqPLQqrJCaCKOE,26435 -astroid/nodes/scoped_nodes/__init__.py,sha256=YXf0Sc8m3HlzbG9IGtX2aHTSGbG4nHfxl9_kY_QLs1g,1271 -astroid/nodes/scoped_nodes/__pycache__/__init__.cpython-310.pyc,, -astroid/nodes/scoped_nodes/__pycache__/mixin.cpython-310.pyc,, -astroid/nodes/scoped_nodes/__pycache__/scoped_nodes.cpython-310.pyc,, -astroid/nodes/scoped_nodes/__pycache__/utils.cpython-310.pyc,, -astroid/nodes/scoped_nodes/mixin.py,sha256=kDdvUEZRSPmQ88sLD2edjalmU88-UZbUIaBERzCy2Ew,7151 -astroid/nodes/scoped_nodes/scoped_nodes.py,sha256=32D6acbmS94Ojdf5kljqOOCQ_roMKaV5x6Ff5ctnSOY,100704 -astroid/nodes/scoped_nodes/utils.py,sha256=rBKL_c6byvOWq7yzRSMNWsZQIe5smST8Dnpz40Nni7Q,1181 -astroid/nodes/utils.py,sha256=5strAqVk0zh9cOD4nH8EZiIsaDn5-gLIT2U1hAoY9wU,433 -astroid/objects.py,sha256=qEuJQ7WfBxxs2p1vpNGqJ2aunfXl5kUwVgZhdNu-n7c,12676 -astroid/protocols.py,sha256=tMR8XAboNiDOqlbdfoxjU66TNl0AD3OJ0Yll2zqCnsg,32562 -astroid/raw_building.py,sha256=hohV5ukMBC2xYJ9ehc6_QV3sop3i0YRkUY-hb6ckumg,25307 -astroid/rebuilder.py,sha256=Jv3xq3M2jdrJS1Y9n0wvYJbHB2ly9-UbXE2pelBbQsw,71520 -astroid/test_utils.py,sha256=gyLSvyMM7QfsfqZhmVW6nM1whn1ArMWTsiHi8_Jy060,2474 -astroid/transforms.py,sha256=AARfSAiYVQ6Ale33UOEkn0n-QjAgJ9xI3HZenReFisA,5946 -astroid/typing.py,sha256=fYk-NAnlC7_prv9jvfnXcL32278yJ65x-qimK32tW_o,2807 -astroid/util.py,sha256=H0hAr2TTxsK97137v9gp59-JmuCMWABmmI_0UOOM9N0,4846 diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL deleted file mode 100644 index e7fa31b..0000000 --- a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (80.9.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt deleted file mode 100644 index 67068c0..0000000 --- a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/CONTRIBUTORS.txt +++ /dev/null @@ -1,226 +0,0 @@ -# This file is autocompleted by 'contributors-txt', -# using the configuration in 'script/.contributors_aliases.json'. -# Do not add new persons manually and only add information without -# using '-' as the line first character. -# Please verify that your change are stable if you modify manually. - -Ex-maintainers --------------- -- Claudiu Popa -- Sylvain Thénault -- Torsten Marek - - -Maintainers ------------ -- Pierre Sassoulas -- Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> -- Jacob Walls -- Marc Mueller <30130371+cdce8p@users.noreply.github.com> -- Hippo91 -- Bryce Guinta -- Ceridwen -- Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> -- Łukasz Rogalski -- Florian Bruhin -- Ashley Whetter -- Dimitri Prybysh -- Areveny - - -Contributors ------------- -- Emile Anclin -- Nick Drozd -- correctmost <134317971+correctmost@users.noreply.github.com> -- Andrew Haigh -- Julien Cristau -- Artem Yurchenko <44875844+temyurchenko@users.noreply.github.com> -- David Liu -- Alexandre Fayolle -- Eevee (Alex Munroe) -- David Gilman -- Tushar Sadhwani -- Matus Valo -- Julien Jehannet -- Hugo van Kemenade -- Calen Pennington -- Antonio -- Akhil Kamat -- Zen Lee <53538590+zenlyj@users.noreply.github.com> -- Tim Martin -- Phil Schaf -- Alex Hall -- Raphael Gaschignard -- Radosław Ganczarek -- Paligot Gérard -- Ioana Tagirta -- Eric Vergnaud -- Derek Gustafson -- David Shea -- Daniel Harding -- Christian Clauss -- Ville Skyttä -- Synrom <30272537+Synrom@users.noreply.github.com> -- Rene Zhang -- Philip Lorenz -- Nicolas Chauvat -- Michael K -- Mario Corchero -- Marien Zwart -- Laura Médioni -- James Addison <55152140+jayaddison@users.noreply.github.com> -- FELD Boris -- Enji Cooper -- Dani Alcala <112832187+clavedeluna@users.noreply.github.com> -- Adrien Di Mascio -- tristanlatr <19967168+tristanlatr@users.noreply.github.com> -- grayjk -- emile@crater.logilab.fr -- doranid -- brendanator -- Tomas Gavenciak -- Tim Paine -- Thomas Hisch -- Stefan Scherfke -- Sergei Lebedev <185856+superbobry@users.noreply.github.com> -- Saugat Pachhai (सौगात) -- Robert Hofer <1058012+hofrob@users.noreply.github.com> -- Ram Rachum -- Pierre-Yves David -- Peter Pentchev -- Peter Kolbus -- Omer Katz -- Moises Lopez -- Mitch Harding -- Michal Vasilek -- Keichi Takahashi -- Kavins Singh -- Karthikeyan Singaravelan -- Joshua Cannon -- John Vandenberg -- Jacob Bogdanov -- Google, Inc. -- Emmanuel Ferdman -- David Euresti -- David Douard -- David Cain -- Anthony Truchet -- Anthony Sottile -- Alexander Shadchin -- wgehalo -- tejaschauhan36912 <59693377+tejaschauhan36912@users.noreply.github.com> -- rr- -- raylu -- plucury -- pavan-msys <149513767+pavan-msys@users.noreply.github.com> -- ostr00000 -- noah-weingarden <33741795+noah-weingarden@users.noreply.github.com> -- nathannaveen <42319948+nathannaveen@users.noreply.github.com> -- mathieui -- markmcclain -- ioanatia -- alm -- adam-grant-hendry <59346180+adam-grant-hendry@users.noreply.github.com> -- aatle <168398276+aatle@users.noreply.github.com> -- Zbigniew Jędrzejewski-Szmek -- Zac Hatfield-Dodds -- Vilnis Termanis -- Valentin Valls -- Uilian Ries -- Tomas Novak -- Thirumal Venkat -- SupImDos <62866982+SupImDos@users.noreply.github.com> -- Stéphane Brunner -- Stanislav Levin -- Simon Hewitt -- Serhiy Storchaka -- Roy Wright -- Robin Jarry -- René Fritze <47802+renefritze@users.noreply.github.com> -- Redoubts -- Philipp Hörist -- Peter de Blanc -- Peter Talley -- Ovidiu Sabou -- Oleh Prypin -- Nicolas Noirbent -- Neil Girdhar -- Miro Hrončok -- Michał Masłowski -- Mateusz Bysiek -- Matej Aleksandrov -- Marcelo Trylesinski -- Leandro T. C. Melo -- Konrad Weihmann -- Kian Meng, Ang -- Kai Mueller <15907922+kasium@users.noreply.github.com> -- Jörg Thalheim -- Jérome Perrin -- JulianJvn <128477611+JulianJvn@users.noreply.github.com> -- Josef Kemetmüller -- Jonathan Striebel -- John Belmonte -- Jeff Widman -- Jeff Quast -- Jarrad Hope -- Jared Garst -- Jamie Scott -- Jakub Wilk -- Iva Miholic -- Ionel Maries Cristian -- HoverHell -- Hashem Nasarat -- HQupgradeHQ <18361586+HQupgradeHQ@users.noreply.github.com> -- Gwyn Ciesla -- Grygorii Iermolenko -- Gregory P. Smith -- Giuseppe Scrivano -- Frédéric Chapoton -- Francis Charette Migneault -- Felix Mölder -- Federico Bond -- DudeNr33 <3929834+DudeNr33@users.noreply.github.com> -- Dmitry Shachnev -- Denis Laxalde -- Deepyaman Datta -- David Poirier -- Dave Hirschfeld -- Dave Baum -- Daniel Martin -- Daniel Colascione -- Damien Baty -- Craig Franklin -- Colin Kennedy -- Cole Robinson -- Christoph Reiter -- Chris Philip -- Charlie Ringström <34444482+Chasarr@users.noreply.github.com> -- BioGeek -- Bianca Power <30207144+biancapower@users.noreply.github.com> -- Benjamin Elven <25181435+S3ntinelX@users.noreply.github.com> -- Ben Elliston -- Becker Awqatty -- Batuhan Taskaya -- BasPH -- Azeem Bande-Ali -- Avram Lubkin -- Aru Sahni -- Artsiom Kaval -- Anubhav <35621759+anubh-v@users.noreply.github.com> -- Antoine Boellinger -- Alphadelta14 -- Alexander Scheel -- Alexander Presnyakov -- Ahmed Azzaoui - -Co-Author ---------- -The following persons were credited manually but did not commit themselves -under this name, or we did not manage to find their commits in the history. - -- François Mockers -- platings -- carl -- alain lefroy -- Mark Gius diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE deleted file mode 100644 index 182e0fb..0000000 --- a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/licenses/LICENSE +++ /dev/null @@ -1,508 +0,0 @@ - - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations -below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it -becomes a de-facto standard. To achieve this, non-free programs must -be allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control -compilation and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at least - three years, to give the same user the materials specified in - Subsection 6a, above, for a charge no more than the cost of - performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply, and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License -may add an explicit geographical distribution limitation excluding those -countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms -of the ordinary General Public License). - - To apply these terms, attach the following notices to the library. -It is safest to attach them to the start of each source file to most -effectively convey the exclusion of warranty; and each file should -have at least the "copyright" line and a pointer to where the full -notice is found. - - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or -your school, if any, to sign a "copyright disclaimer" for the library, -if necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James - Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt b/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt deleted file mode 100644 index 450d4fe..0000000 --- a/.venv/lib/python3.10/site-packages/astroid-4.0.2.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -astroid diff --git a/.venv/lib/python3.10/site-packages/astroid/__init__.py b/.venv/lib/python3.10/site-packages/astroid/__init__.py deleted file mode 100644 index abb45cf..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/__init__.py +++ /dev/null @@ -1,242 +0,0 @@ -# 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 -""" - -# 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 Context -from astroid.exceptions import ( - AstroidBuildingError, - 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 as _DEPRECATED_AnnAssign, - Arguments as _DEPRECATED_Arguments, - Assert as _DEPRECATED_Assert, - Assign as _DEPRECATED_Assign, - AssignAttr as _DEPRECATED_AssignAttr, - AssignName as _DEPRECATED_AssignName, - AsyncFor as _DEPRECATED_AsyncFor, - AsyncFunctionDef as _DEPRECATED_AsyncFunctionDef, - AsyncWith as _DEPRECATED_AsyncWith, - Attribute as _DEPRECATED_Attribute, - AugAssign as _DEPRECATED_AugAssign, - Await as _DEPRECATED_Await, - BinOp as _DEPRECATED_BinOp, - BoolOp as _DEPRECATED_BoolOp, - Break as _DEPRECATED_Break, - Call as _DEPRECATED_Call, - ClassDef as _DEPRECATED_ClassDef, - Compare as _DEPRECATED_Compare, - Comprehension as _DEPRECATED_Comprehension, - ComprehensionScope as _DEPRECATED_ComprehensionScope, - Const as _DEPRECATED_Const, - Continue as _DEPRECATED_Continue, - Decorators as _DEPRECATED_Decorators, - DelAttr as _DEPRECATED_DelAttr, - Delete as _DEPRECATED_Delete, - DelName as _DEPRECATED_DelName, - Dict as _DEPRECATED_Dict, - DictComp as _DEPRECATED_DictComp, - DictUnpack as _DEPRECATED_DictUnpack, - EmptyNode as _DEPRECATED_EmptyNode, - EvaluatedObject as _DEPRECATED_EvaluatedObject, - ExceptHandler as _DEPRECATED_ExceptHandler, - Expr as _DEPRECATED_Expr, - For as _DEPRECATED_For, - FormattedValue as _DEPRECATED_FormattedValue, - FunctionDef as _DEPRECATED_FunctionDef, - GeneratorExp as _DEPRECATED_GeneratorExp, - Global as _DEPRECATED_Global, - If as _DEPRECATED_If, - IfExp as _DEPRECATED_IfExp, - Import as _DEPRECATED_Import, - ImportFrom as _DEPRECATED_ImportFrom, - Interpolation as _DEPRECATED_Interpolation, - JoinedStr as _DEPRECATED_JoinedStr, - Keyword as _DEPRECATED_Keyword, - Lambda as _DEPRECATED_Lambda, - List as _DEPRECATED_List, - ListComp as _DEPRECATED_ListComp, - Match as _DEPRECATED_Match, - MatchAs as _DEPRECATED_MatchAs, - MatchCase as _DEPRECATED_MatchCase, - MatchClass as _DEPRECATED_MatchClass, - MatchMapping as _DEPRECATED_MatchMapping, - MatchOr as _DEPRECATED_MatchOr, - MatchSequence as _DEPRECATED_MatchSequence, - MatchSingleton as _DEPRECATED_MatchSingleton, - MatchStar as _DEPRECATED_MatchStar, - MatchValue as _DEPRECATED_MatchValue, - Module as _DEPRECATED_Module, - Name as _DEPRECATED_Name, - NamedExpr as _DEPRECATED_NamedExpr, - NodeNG as _DEPRECATED_NodeNG, - Nonlocal as _DEPRECATED_Nonlocal, - ParamSpec as _DEPRECATED_ParamSpec, - Pass as _DEPRECATED_Pass, - Raise as _DEPRECATED_Raise, - Return as _DEPRECATED_Return, - Set as _DEPRECATED_Set, - SetComp as _DEPRECATED_SetComp, - Slice as _DEPRECATED_Slice, - Starred as _DEPRECATED_Starred, - Subscript as _DEPRECATED_Subscript, - TemplateStr as _DEPRECATED_TemplateStr, - Try as _DEPRECATED_Try, - TryStar as _DEPRECATED_TryStar, - Tuple as _DEPRECATED_Tuple, - TypeAlias as _DEPRECATED_TypeAlias, - TypeVar as _DEPRECATED_TypeVar, - TypeVarTuple as _DEPRECATED_TypeVarTuple, - UnaryOp as _DEPRECATED_UnaryOp, - Unknown as _DEPRECATED_Unknown, - While as _DEPRECATED_While, - With as _DEPRECATED_With, - Yield as _DEPRECATED_Yield, - YieldFrom as _DEPRECATED_YieldFrom, - are_exclusive, - builtin_lookup, - unpack_infer, - function_to_method, -) - -# isort: on - -from astroid.util import Uninferable - -__all__ = [ - "CONST_CLS", - "MANAGER", - "AstroidBuildingError", - "AstroidError", - "AstroidImportError", - "AstroidIndexError", - "AstroidSyntaxError", - "AstroidTypeError", - "AstroidValueError", - "AttributeInferenceError", - "BaseInstance", - "BoundMethod", - "Context", - "DuplicateBasesError", - "ExceptionInstance", - "InconsistentMroError", - "InferenceError", - "InferenceOverwriteError", - "Instance", - "MroError", - "NameInferenceError", - "NoDefault", - "NotFoundError", - "ParentMissingError", - "ResolveError", - "StatementMissing", - "SuperArgumentTypeError", - "SuperError", - "TooManyLevelsError", - "UnboundMethod", - "Uninferable", - "UnresolvableName", - "UseInferenceDefault", - "__version__", - "_inference_tip_cached", - "are_exclusive", - "builtin_lookup", - "extract_node", - "function_to_method", - "inference_tip", - "node_classes", - "parse", - "raw_building", - "register_module_extender", - "scoped_nodes", - "unpack_infer", - "version", -] - - -def __getattr__(name: str): - if (val := globals().get(f"_DEPRECATED_{name}")) is None: - msg = f"module '{__name__}' has no attribute '{name}" - raise AttributeError(msg) - - # pylint: disable-next=import-outside-toplevel - import warnings - - msg = ( - f"importing '{name}' from 'astroid' is deprecated and will be removed in v5, " - "import it from 'astroid.nodes' instead" - ) - warnings.warn(msg, DeprecationWarning, stacklevel=2) - return val diff --git a/.venv/lib/python3.10/site-packages/astroid/__pkginfo__.py b/.venv/lib/python3.10/site-packages/astroid/__pkginfo__.py deleted file mode 100644 index 8ef2395..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/__pkginfo__.py +++ /dev/null @@ -1,6 +0,0 @@ -# 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__ = "4.0.2" -version = __version__ diff --git a/.venv/lib/python3.10/site-packages/astroid/_ast.py b/.venv/lib/python3.10/site-packages/astroid/_ast.py deleted file mode 100644 index e3ad97d..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/_ast.py +++ /dev/null @@ -1,102 +0,0 @@ -# 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, filename: str | None = None - ) -> ast.Module: - if filename: - return ast.parse(string, filename=filename, type_comments=type_comments) - 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, "", "func_type") - 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, - } diff --git a/.venv/lib/python3.10/site-packages/astroid/arguments.py b/.venv/lib/python3.10/site-packages/astroid/arguments.py deleted file mode 100644 index 3781889..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/arguments.py +++ /dev/null @@ -1,309 +0,0 @@ -# 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: nodes.Call, 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) -> bool: - """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, - ) -> dict[str | None, InferenceResult]: - 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.""" - # pylint: disable = too-many-branches - - 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, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/astroid_manager.py b/.venv/lib/python3.10/site-packages/astroid/astroid_manager.py deleted file mode 100644 index 3031057..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/astroid_manager.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -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) diff --git a/.venv/lib/python3.10/site-packages/astroid/bases.py b/.venv/lib/python3.10/site-packages/astroid/bases.py deleted file mode 100644 index a029da6..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/bases.py +++ /dev/null @@ -1,778 +0,0 @@ -# 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 PY311_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", "functools.cached_property"} -# enum.property was added in Python 3.11 -if PY311_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 - - if not meth.decorators: - return False - # Lookup for subclasses of *property* - 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): - # Check for a class which inherits from a standard property type - if any(inferred.is_subtype_of(pclass) for pclass in PROPERTIES): - return True - for base_class in inferred.bases: - # Check for a class which inherits from functools.cached_property - # and includes a subscripted type annotation - if isinstance(base_class, nodes.Subscript): - value = safe_infer(base_class.value, context=context) - if not isinstance(value, nodes.ClassDef): - continue - if value.name != "cached_property": - continue - module, _ = value.lookup(value.name) - if isinstance(module, nodes.Module) and module.name == "functools": - return True - continue - - 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]: - """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 - if isinstance(node, BaseInstance) and node._proxied is self._proxied: - inferred = True - yield node - # Prevent recursion. - 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]: - return method.infer_call_result(self, context) - - def __repr__(self) -> str: - return "".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]: - 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.GeneratorBaseModel - - 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"" - - def __str__(self) -> str: - return f"Generator({self._proxied.name})" - - -class AsyncGenerator(Generator): - """Special node representing an async generator.""" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - AsyncGenerator.special_attributes = objectmodel.AsyncGeneratorModel() - - def pytype(self) -> Literal["builtins.async_generator"]: - return "builtins.async_generator" - - def display_type(self) -> str: - return "AsyncGenerator" - - def __repr__(self) -> str: - return f"" - - 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"" - - def __str__(self) -> str: - return f"UnionType({self._proxied.name})" diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/__init__.py b/.venv/lib/python3.10/site-packages/astroid/brain/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py deleted file mode 100644 index 6bde22f..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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, nodes -from astroid.context import InferenceContext -from astroid.exceptions import UseInferenceDefault -from astroid.inference_tip import inference_tip -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.SYNTHETIC_ROOT, # this class is not real - end_lineno=node.end_lineno, - end_col_offset=node.end_col_offset, - ) - 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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py deleted file mode 100644 index b619bb3..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py +++ /dev/null @@ -1,110 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import is_class_var -from astroid.manager import AstroidManager -from astroid.util import safe_infer - -ATTRIB_NAMES = frozenset( - ( - "attr.Factory", - "attr.ib", - "attrib", - "attr.attrib", - "attr.field", - "attrs.field", - "field", - ) -) -NEW_ATTRS_NAMES = frozenset( - ( - "attrs.define", - "attrs.mutable", - "attrs.frozen", - ) -) -ATTRS_NAMES = frozenset( - ( - "attr.s", - "attrs", - "attr.attrs", - "attr.attributes", - "attr.define", - "attr.mutable", - "attr.frozen", - *NEW_ATTRS_NAMES, - ) -) - - -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, nodes.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: nodes.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__"] = [nodes.Unknown(parent=node)] - - use_bare_annotations = is_decorated_with_attrs(node, NEW_ATTRS_NAMES) - for cdef_body_node in node.body: - if not isinstance(cdef_body_node, (nodes.Assign, nodes.AnnAssign)): - continue - if isinstance(cdef_body_node.value, nodes.Call): - if cdef_body_node.value.func.as_string() not in ATTRIB_NAMES: - continue - elif not use_bare_annotations: - continue - - # Skip attributes that are explicitly annotated as class variables - if isinstance(cdef_body_node, nodes.AnnAssign) and is_class_var( - cdef_body_node.annotation - ): - continue - - targets = ( - cdef_body_node.targets - if hasattr(cdef_body_node, "targets") - else [cdef_body_node.target] - ) - for target in targets: - rhs_node = nodes.Unknown( - lineno=cdef_body_node.lineno, - col_offset=cdef_body_node.col_offset, - parent=cdef_body_node, - ) - if isinstance(target, nodes.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( - nodes.ClassDef, attr_attributes_transform, is_decorated_with_attrs - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py deleted file mode 100644 index 3a95feb..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py +++ /dev/null @@ -1,32 +0,0 @@ -# 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.builder 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: ClassDef) -> ClassDef: - """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: ClassDef) -> 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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py deleted file mode 100644 index e21d361..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py +++ /dev/null @@ -1,1106 +0,0 @@ -# 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, Iterator -from functools import partial -from typing import TYPE_CHECKING, Any, NoReturn, cast - -from astroid import arguments, helpers, nodes, objects, util -from astroid.builder import AstroidBuilder -from astroid.context import InferenceContext -from astroid.exceptions import ( - AstroidTypeError, - AttributeInferenceError, - InferenceError, - MroError, - UseInferenceDefault, -) -from astroid.inference_tip import inference_tip -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 = ( - objects.FrozenSet | objects.DictItems | objects.DictKeys | objects.DictValues -) - -BuiltContainers = type[tuple] | type[list] | type[set] | type[frozenset] - -CopyResult = 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: - # pylint: disable = too-many-boolean-expressions - 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): - return node.func.name == builtin_name - 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((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) -> bool: - 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="", - lineno=node.lineno, - col_offset=node.col_offset, - # ↓ semantically, the definition of the class of property isn't within - # node.frame. It's somewhere in the builtins module, but we are special - # casing it for each "property()" call, so we are making up the - # definition on the spot, ad-hoc. - parent=scoped_nodes.SYNTHETIC_ROOT, - ) - 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( - f"TypeError: arg 1 must be class, not {type(obj_type)!r}" - ) - - # 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) -> nodes.Const: - """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) -> nodes.Const: - """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: list) -> nodes.Dict: - 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) and 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)) - and 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, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py deleted file mode 100644 index 94944e6..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py +++ /dev/null @@ -1,138 +0,0 @@ -# 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 - -from astroid.brain.helpers import register_module_extender -from astroid.builder import AstroidBuilder, extract_node, parse -from astroid.const import PY313_PLUS -from astroid.context import InferenceContext -from astroid.exceptions import AttributeInferenceError -from astroid.manager import AstroidManager -from astroid.nodes.scoped_nodes import ClassDef - -if TYPE_CHECKING: - from astroid import nodes - - -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 _collections_abc_313_transform() -> nodes.Module: - """See https://github.com/python/cpython/pull/124735""" - return AstroidBuilder(AstroidManager()).string_build( - "from _collections_abc import *" - ) - - -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 - @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 - @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) - - # 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 - ) - - if PY313_PLUS: - register_module_extender( - manager, "collections.abc", _collections_abc_313_transform - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py deleted file mode 100644 index 71f9dfc..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py +++ /dev/null @@ -1,27 +0,0 @@ -# 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 nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def _re_transform() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py deleted file mode 100644 index 8ae10bc..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py +++ /dev/null @@ -1,86 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def enrich_ctypes_redefined_types() -> nodes.Module: - """ - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py deleted file mode 100644 index 5824fd7..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py +++ /dev/null @@ -1,185 +0,0 @@ -# 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 nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def _curses_transform() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py deleted file mode 100644 index bafd5f1..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py +++ /dev/null @@ -1,635 +0,0 @@ -# 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 - -from astroid import bases, context, nodes -from astroid.brain.helpers import is_class_var -from astroid.builder import parse -from astroid.const import PY313_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 = ( - 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) and 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 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() is True - - 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) - and 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 keyword.value.bool_value() is False # 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( - node: nodes.ClassDef, assigns: list[nodes.AnnAssign], kw_only_decorated: bool -) -> str: - """Return an init method for a dataclass given the targets.""" - # pylint: disable = too-many-locals, too-many-branches, too-many-statements - - 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 (keyword.value.bool_value() is False) - 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() is True: - 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 _looks_like_dataclasses(node: nodes.Module) -> bool: - return node.qname() == "dataclasses" - - -def _resolve_private_replace_to_public(node: nodes.Module) -> None: - """In python/cpython@6f3c138, a _replace() method was extracted from - replace(), and this indirection made replace() uninferable.""" - if "_replace" in node.locals: - node.locals["replace"] = node.locals["_replace"] - - -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_keyword_only_sentinel(node: nodes.NodeNG) -> bool: - """Return True if node is the KW_ONLY sentinel.""" - 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: - if PY313_PLUS: - manager.register_transform( - nodes.Module, - _resolve_private_replace_to_public, - _looks_like_dataclasses, - ) - - 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, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py deleted file mode 100644 index f4cb667..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_datetime.py +++ /dev/null @@ -1,20 +0,0 @@ -# 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 nodes -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() -> nodes.Module: - """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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py deleted file mode 100644 index c27343f..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py +++ /dev/null @@ -1,28 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import AstroidBuilder -from astroid.manager import AstroidManager - - -def dateutil_transform() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py deleted file mode 100644 index 1cb8442..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py +++ /dev/null @@ -1,174 +0,0 @@ -# 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, nodes, objects -from astroid.builder import extract_node -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.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, nodes.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, nodes.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, (nodes.Attribute, nodes.Call)): - continue - if _looks_like_functools_member(decorator, "lru_cache"): - return True - return False - - -def _looks_like_functools_member( - node: nodes.Attribute | nodes.Call, member: str -) -> bool: - """Check if the given Call node is the wanted member of functools.""" - if isinstance(node, nodes.Attribute): - return node.attrname == member - if isinstance(node.func, nodes.Name): - return node.func.name == member - if isinstance(node.func, nodes.Attribute): - return ( - node.func.attrname == member - and isinstance(node.func.expr, nodes.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( - nodes.FunctionDef, _transform_lru_cache, _looks_like_lru_cache - ) - - manager.register_transform( - nodes.Call, - inference_tip(_functools_partial_inference), - _looks_like_partial, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py deleted file mode 100644 index fa60077..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py +++ /dev/null @@ -1,252 +0,0 @@ -# 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. - """ - # pylint: disable = too-many-branches, too-many-statements - - 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(" bool: - # Return whether this looks like a call to gi.require_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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py deleted file mode 100644 index a17645a..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py +++ /dev/null @@ -1,96 +0,0 @@ -# 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 nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def _hashlib_transform() -> nodes.Module: - init_signature = "value='', usedforsecurity=True" - 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, " - "node_depth=0, inner_size=0, last_node=False, usedforsecurity=True" - ) - - blake2s_signature = ( - "data=b'', *, digest_size=32, key=b'', salt=b'', " - "person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, " - "node_depth=0, inner_size=0, last_node=False, usedforsecurity=True" - ) - - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_http.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_http.py deleted file mode 100644 index 9802c0f..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_http.py +++ /dev/null @@ -1,238 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import AstroidBuilder -from astroid.manager import AstroidManager - - -def _http_transform() -> nodes.Module: - code = textwrap.dedent( - """ - from enum import IntEnum, StrEnum - from collections import namedtuple - _HTTPStatus = namedtuple('_HTTPStatus', 'value phrase description') - - class HTTPMethod(StrEnum): - GET = "GET" - POST = "POST" - PUT = "PUT" - DELETE = "DELETE" - HEAD = "HEAD" - OPTIONS = "OPTIONS" - PATCH = "PATCH" - TRACE = "TRACE" - CONNECT = "CONNECT" - - 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', '') - EARLY_HINTS = _HTTPStatus(103, 'Early Hints') - 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') - CONTENT_TOO_LARGE = _HTTPStatus(413, 'Content Too Large', - 'Content is too large') - REQUEST_ENTITY_TOO_LARGE = CONTENT_TOO_LARGE - URI_TOO_LONG = _HTTPStatus(414, 'URI Too Long', 'URI is too long') - REQUEST_URI_TOO_LONG = URI_TOO_LONG - UNSUPPORTED_MEDIA_TYPE = _HTTPStatus(415, 'Unsupported Media Type', - 'Entity body in unsupported format') - RANGE_NOT_SATISFIABLE = (416, 'Range Not Satisfiable', - 'Cannot satisfy request range') - REQUESTED_RANGE_NOT_SATISFIABLE = RANGE_NOT_SATISFIABLE - EXPECTATION_FAILED = _HTTPStatus(417, 'Expectation Failed', - 'Expect condition could not be satisfied') - IM_A_TEAPOT = _HTTPStatus(418, 'I\\\'m a Teapot', - 'Server refuses to brew coffee because it is a teapot.') - MISDIRECTED_REQUEST = _HTTPStatus(421, 'Misdirected Request', - 'Server is not able to produce a response') - UNPROCESSABLE_CONTENT = _HTTPStatus(422, 'Unprocessable Content') - UNPROCESSABLE_ENTITY = UNPROCESSABLE_CONTENT - LOCKED = _HTTPStatus(423, 'Locked') - FAILED_DEPENDENCY = _HTTPStatus(424, 'Failed Dependency') - TOO_EARLY = _HTTPStatus(425, 'Too Early') - 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() -> nodes.Module: - return AstroidBuilder(AstroidManager()).string_build( - textwrap.dedent( - """ - from http import HTTPStatus - - CONTINUE = HTTPStatus.CONTINUE - SWITCHING_PROTOCOLS = HTTPStatus.SWITCHING_PROTOCOLS - PROCESSING = HTTPStatus.PROCESSING - EARLY_HINTS = HTTPStatus.EARLY_HINTS - 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 - CONTENT_TOO_LARGE = HTTPStatus.CONTENT_TOO_LARGE - REQUEST_ENTITY_TOO_LARGE = HTTPStatus.CONTENT_TOO_LARGE - URI_TOO_LONG = HTTPStatus.URI_TOO_LONG - REQUEST_URI_TOO_LONG = HTTPStatus.URI_TOO_LONG - UNSUPPORTED_MEDIA_TYPE = HTTPStatus.UNSUPPORTED_MEDIA_TYPE - RANGE_NOT_SATISFIABLE = HTTPStatus.RANGE_NOT_SATISFIABLE - REQUESTED_RANGE_NOT_SATISFIABLE = HTTPStatus.RANGE_NOT_SATISFIABLE - EXPECTATION_FAILED = HTTPStatus.EXPECTATION_FAILED - IM_A_TEAPOT = HTTPStatus.IM_A_TEAPOT - UNPROCESSABLE_CONTENT = HTTPStatus.UNPROCESSABLE_CONTENT - UNPROCESSABLE_ENTITY = HTTPStatus.UNPROCESSABLE_CONTENT - LOCKED = HTTPStatus.LOCKED - FAILED_DEPENDENCY = HTTPStatus.FAILED_DEPENDENCY - TOO_EARLY = HTTPStatus.TOO_EARLY - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py deleted file mode 100644 index ba20f06..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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: FunctionDef) -> 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: FunctionDef) -> FunctionDef: - """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. - """ - assert isinstance(node.args.args, list) - 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, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_io.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_io.py deleted file mode 100644 index ab6e607..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_io.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py deleted file mode 100644 index 62cc2d0..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py +++ /dev/null @@ -1,125 +0,0 @@ -# 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 nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import AstroidBuilder -from astroid.manager import AstroidManager - - -def mechanize_transform() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py deleted file mode 100644 index e6413b0..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py +++ /dev/null @@ -1,106 +0,0 @@ -# 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py deleted file mode 100644 index ff5b715..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py +++ /dev/null @@ -1,681 +0,0 @@ -# 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 - -from astroid import arguments, bases, 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.inference_tip import inference_tip -from astroid.manager import AstroidManager -from astroid.nodes.scoped_nodes.scoped_nodes import SYNTHETIC_ROOT - -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: nodes.NodeNG, - *, - parent: 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=parent, - ) - class_node.postinit( - 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: nodes.Name = _extract_single_node("tuple") - class_node, name, attributes = infer_func_form( - node, tuple_base, parent=SYNTHETIC_ROOT, context=context - ) - - call_site = arguments.CallSite.from_call(node, context=context) - func = util.safe_infer( - _extract_single_node("import collections; collections.namedtuple") - ) - assert isinstance(func, nodes.NodeNG) - try: - rename_arg_bool_value = next( - call_site.infer_argument(func, "rename", context or InferenceContext()) - ).bool_value() - rename = rename_arg_bool_value is True - 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): - # pylint: disable = too-many-boolean-expressions - 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 - # - for name in (typename, *attributes): - if not isinstance(name, str): - raise AstroidTypeError( - f"Type names and field names must be strings, not {type(name)!r}" - ) - 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) - # - - 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__ = [''] - """ - ) - - # FIXME arguably, the base here shouldn't be the EnumMeta class definition - # itself, but a reference (Name) to it. Otherwise, the invariant that all - # children of a node have that node as their parent is broken. - class_node = infer_func_form( - node, - enum_meta, - parent=SYNTHETIC_ROOT, - 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): - """The name of the Enum member. - - This is a reconstruction by astroid: enums are too dynamic to understand, but we at least - know 'name' should be a string, so this is astroid's best guess. - """ - 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: nodes.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, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py deleted file mode 100644 index b72369c..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py +++ /dev/null @@ -1,28 +0,0 @@ -# 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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py deleted file mode 100644 index ce4173c..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py +++ /dev/null @@ -1,24 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def numpy_core_fromnumeric_transform() -> nodes.Module: - 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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py deleted file mode 100644 index b66ba5f..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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 import nodes -from astroid.brain.brain_numpy_utils import ( - attribute_name_looks_like_numpy_member, - infer_numpy_attribute, -) -from astroid.inference_tip import inference_tip -from astroid.manager import AstroidManager - -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: - manager.register_transform( - nodes.Attribute, - inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)), - functools.partial( - attribute_name_looks_like_numpy_member, - frozenset(METHODS_TO_BE_INFERRED.keys()), - ), - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py deleted file mode 100644 index 19850d3..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py +++ /dev/null @@ -1,106 +0,0 @@ -# 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 import nodes -from astroid.brain.brain_numpy_utils import ( - attribute_name_looks_like_numpy_member, - infer_numpy_attribute, - infer_numpy_name, - 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 - - -def numpy_core_multiarray_transform() -> nodes.Module: - 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 - ) - - method_names = frozenset(METHODS_TO_BE_INFERRED.keys()) - - manager.register_transform( - nodes.Attribute, - inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)), - functools.partial(attribute_name_looks_like_numpy_member, method_names), - ) - manager.register_transform( - nodes.Name, - inference_tip(functools.partial(infer_numpy_name, METHODS_TO_BE_INFERRED)), - functools.partial(member_name_looks_like_numpy_member, method_names), - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py deleted file mode 100644 index ee08e02..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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 import nodes -from astroid.brain.brain_numpy_utils import ( - attribute_name_looks_like_numpy_member, - infer_numpy_attribute, -) -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 - - -def numpy_core_numeric_transform() -> nodes.Module: - 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 - ) - - manager.register_transform( - nodes.Attribute, - inference_tip(functools.partial(infer_numpy_attribute, METHODS_TO_BE_INFERRED)), - functools.partial( - attribute_name_looks_like_numpy_member, - frozenset(METHODS_TO_BE_INFERRED.keys()), - ), - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py deleted file mode 100644 index 7111c83..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py +++ /dev/null @@ -1,265 +0,0 @@ -# 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 import nodes -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() -> nodes.Module: - # 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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py deleted file mode 100644 index a048a1c..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py +++ /dev/null @@ -1,154 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def numpy_core_umath_transform() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py deleted file mode 100644 index e61acb5..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def numpy_ma_transform() -> nodes.Module: - """ - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py deleted file mode 100644 index c98adb1..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py +++ /dev/null @@ -1,163 +0,0 @@ -# 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 import nodes -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 - - -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: nodes.Attribute) -> bool: - return node.attrname == "ndarray" - - -def register(manager: AstroidManager) -> None: - manager.register_transform( - nodes.Attribute, - inference_tip(infer_numpy_ndarray), - _looks_like_numpy_ndarray, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py deleted file mode 100644 index be1c957..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py +++ /dev/null @@ -1,73 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def numpy_random_mtrand_transform() -> nodes.Module: - 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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py deleted file mode 100644 index 1a8f665..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py +++ /dev/null @@ -1,94 +0,0 @@ -# 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 import nodes -from astroid.builder import extract_node -from astroid.context import InferenceContext - -# 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_name( - sources: dict[str, str], node: nodes.Name, context: InferenceContext | None = None -): - extracted_node = extract_node(sources[node.name]) - return extracted_node.infer(context=context) - - -def infer_numpy_attribute( - sources: dict[str, str], - node: nodes.Attribute, - context: InferenceContext | None = None, -): - extracted_node = extract_node(sources[node.attrname]) - return extracted_node.infer(context=context) - - -def _is_a_numpy_module(node: nodes.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 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, nodes.Import) - ] - return any( - ("numpy", module_nickname) in target.names or ("numpy", None) in target.names - for target in potential_import_target - ) - - -def member_name_looks_like_numpy_member( - member_names: frozenset[str], node: nodes.Name -) -> bool: - """ - Returns True if the Name node's name matches a member name from numpy - """ - return node.name in member_names and node.root().name.startswith("numpy") - - -def attribute_name_looks_like_numpy_member( - member_names: frozenset[str], node: nodes.Attribute -) -> bool: - """ - Returns True if the Attribute node's name matches a member name from numpy - """ - return ( - node.attrname in member_names - and isinstance(node.expr, nodes.Name) - and _is_a_numpy_module(node.expr) - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py deleted file mode 100644 index d1d1bda..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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, nodes -from astroid.builder import _extract_single_node -from astroid.const import PY313 -from astroid.exceptions import InferenceError, UseInferenceDefault -from astroid.inference_tip import inference_tip -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 - parents = "builtins.tuple" if PY313 else "pathlib._PathParents" - return ( - isinstance(value, bases.Instance) - and isinstance(value._proxied, nodes.ClassDef) - and value.qname() == parents - ) - - -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, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py deleted file mode 100644 index e2bd669..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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 nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def pkg_resources_transform() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py deleted file mode 100644 index 6d06267..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py +++ /dev/null @@ -1,85 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import AstroidBuilder -from astroid.manager import AstroidManager - - -def pytest_transform() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py deleted file mode 100644 index 30581e0..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py +++ /dev/null @@ -1,89 +0,0 @@ -# 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 -from astroid.brain.helpers import register_module_extender -from astroid.builder import AstroidBuilder, parse -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"}, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_random.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_random.py deleted file mode 100644 index 84b4f4e..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_random.py +++ /dev/null @@ -1,94 +0,0 @@ -# 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 import nodes -from astroid.context import InferenceContext -from astroid.exceptions import UseInferenceDefault -from astroid.inference_tip import inference_tip -from astroid.manager import AstroidManager -from astroid.util import safe_infer - -ACCEPTED_ITERABLES_FOR_SAMPLE = (nodes.List, nodes.Set, nodes.Tuple) - - -def _clone_node_with_lineno(node, parent, lineno): - if isinstance(node, nodes.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, nodes.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 = nodes.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, nodes.Attribute): - return func.attrname == "sample" - if isinstance(func, nodes.Name): - return func.name == "sample" - return False - - -def register(manager: AstroidManager) -> None: - manager.register_transform( - nodes.Call, inference_tip(infer_random_sample), _looks_like_random_sample - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_re.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_re.py deleted file mode 100644 index 6464645..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_re.py +++ /dev/null @@ -1,97 +0,0 @@ -# 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, nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import _extract_single_node, parse -from astroid.const import PY311_PLUS -from astroid.inference_tip import inference_tip -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, - ) - 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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py deleted file mode 100644 index 70fb946..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py +++ /dev/null @@ -1,95 +0,0 @@ -# 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, nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import _extract_single_node, parse -from astroid.inference_tip import inference_tip -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, - ) - 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 - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py deleted file mode 100644 index f2e6069..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py +++ /dev/null @@ -1,80 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def responses_funcs() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py deleted file mode 100644 index a7a2576..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py +++ /dev/null @@ -1,90 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def scipy_signal() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py deleted file mode 100644 index 649e974..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py +++ /dev/null @@ -1,120 +0,0 @@ -# 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_six.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_six.py deleted file mode 100644 index 1218009..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_six.py +++ /dev/null @@ -1,244 +0,0 @@ -# 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 ( - isinstance(func, (nodes.FunctionDef, nodes.ClassDef)) - and 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, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py deleted file mode 100644 index 8410d9e..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py +++ /dev/null @@ -1,41 +0,0 @@ -# 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 nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def _session_transform() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py deleted file mode 100644 index 6b4fc5c..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py +++ /dev/null @@ -1,163 +0,0 @@ -# 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 nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.const import PY312_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 - 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 - """ - if PY312_PLUS: - enum += "OP_LEGACY_SERVER_CONNECT = 15" - return enum - - -def ssl_transform() -> nodes.Module: - return parse( - f""" - # 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 OP_LEGACY_SERVER_CONNECT" if PY312_PLUS else ""} - - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py deleted file mode 100644 index 5420ef9..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_statistics.py +++ /dev/null @@ -1,73 +0,0 @@ -# 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 statistics library module. - -Provides inference improvements for statistics module functions that have -complex runtime behavior difficult to analyze statically. -""" - -from __future__ import annotations - -from collections.abc import Iterator -from typing import TYPE_CHECKING - -from astroid import nodes -from astroid.context import InferenceContext -from astroid.inference_tip import inference_tip -from astroid.manager import AstroidManager -from astroid.util import Uninferable - -if TYPE_CHECKING: - from astroid.typing import InferenceResult - - -def _looks_like_statistics_quantiles(node: nodes.Call) -> bool: - """Check if this is a call to statistics.quantiles.""" - match node.func: - case nodes.Attribute(expr=nodes.Name(name="statistics"), attrname="quantiles"): - # Case 1: statistics.quantiles(...) - return True - case nodes.Name(name="quantiles"): - # Case 2: from statistics import quantiles; quantiles(...) - # Check if quantiles was imported from statistics - try: - frame = node.frame() - if "quantiles" in frame.locals: - # Look for import from statistics - for stmt in frame.body: - if ( - isinstance(stmt, nodes.ImportFrom) - and stmt.modname == "statistics" - and any(name[0] == "quantiles" for name in stmt.names or []) - ): - return True - except (AttributeError, TypeError): - # If we can't determine the import context, be conservative - pass - return False - - -def infer_statistics_quantiles( - node: nodes.Call, context: InferenceContext | None = None -) -> Iterator[InferenceResult]: - """Infer the result of statistics.quantiles() calls. - - Returns Uninferable because quantiles() has complex runtime behavior - that cannot be statically analyzed, preventing false positives in - pylint's unbalanced-tuple-unpacking checker. - - statistics.quantiles() returns a list with (n-1) elements, but static - analysis sees only the empty list initializations in the function body. - """ - yield Uninferable - - -def register(manager: AstroidManager) -> None: - """Register statistics-specific inference improvements.""" - manager.register_transform( - nodes.Call, - inference_tip(infer_statistics_quantiles), - _looks_like_statistics_quantiles, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py deleted file mode 100644 index 3a99802..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py +++ /dev/null @@ -1,100 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.const import PY311_PLUS -from astroid.manager import AstroidManager - - -def _subprocess_transform() -> nodes.Module: - 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, - user=None, group=None, extra_groups=None, umask=-1, 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} - @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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py deleted file mode 100644 index 95af2db..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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 nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def _thread_transform() -> nodes.Module: - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_type.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_type.py deleted file mode 100644 index 8391e59..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_type.py +++ /dev/null @@ -1,70 +0,0 @@ -# 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 nodes -from astroid.builder import extract_node -from astroid.context import InferenceContext -from astroid.exceptions import UseInferenceDefault -from astroid.inference_tip import inference_tip -from astroid.manager import AstroidManager - - -def _looks_like_type_subscript(node: nodes.Name) -> 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.NodeNG - :return: whether the node is a Name node inside a type related subscript - """ - if 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.NodeNG - :return: the inferred node - :rtype: nodes.NodeNG - """ - node_scope, _ = node.scope().lookup("type") - if not (isinstance(node_scope, nodes.Module) and 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: - manager.register_transform( - nodes.Name, inference_tip(infer_type_sub), _looks_like_type_subscript - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py deleted file mode 100644 index 217a803..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py +++ /dev/null @@ -1,504 +0,0 @@ -# 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, nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import AstroidBuilder, _extract_single_node, extract_node -from astroid.const import PY312_PLUS, PY313_PLUS, PY314_PLUS -from astroid.exceptions import ( - AstroidSyntaxError, - AttributeInferenceError, - InferenceError, - UseInferenceDefault, -) -from astroid.inference_tip import inference_tip -from astroid.manager import AstroidManager - -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", # scheduled for removal in 3.17 - "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, nodes.Attribute): - return func.attrname in TYPING_TYPEVARS - if isinstance(func, nodes.Name): - return func.name in TYPING_TYPEVARS - return False - - -def infer_typing_typevar_or_newtype( - node: nodes.Call, context_itton: context.InferenceContext | None = None -) -> Iterator[nodes.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], nodes.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, nodes.Name): - return node.name in TYPING_MEMBERS - if isinstance(node, nodes.Attribute): - return node.attrname in TYPING_MEMBERS - if isinstance(node, nodes.Subscript): - return _looks_like_typing_subscript(node.value) - return False - - -def infer_typing_attr( - node: nodes.Subscript, ctx: context.InferenceContext | None = None -) -> Iterator[nodes.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 ( - PY313_PLUS - and isinstance(value, nodes.FunctionDef) - and value.qname() == "typing.Annotated" - ): - # typing.Annotated is a FunctionDef on 3.13+ - node._explicit_inference = lambda node, context: iter([value]) - return iter([value]) - - if isinstance(value, nodes.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, nodes.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] - # Avoid re-instantiating this class every time it's seen - node._explicit_inference = lambda node, context: iter([value]) - return iter([value]) - - node = extract_node(TYPING_TYPE_TEMPLATE.format(value.qname().split(".")[-1])) - return node.infer(context=ctx) - - -def _looks_like_generic_class_pep695(node: nodes.ClassDef) -> bool: - """Check if class is using type parameter. Python 3.12+.""" - return len(node.type_params) > 0 - - -def infer_typing_generic_class_pep695( - node: nodes.ClassDef, ctx: context.InferenceContext | None = None -) -> Iterator[nodes.ClassDef]: - """Add __class_getitem__ for generic classes. Python 3.12+.""" - func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE) - node.locals["__class_getitem__"] = [func_to_add] - return iter([node]) - - -def _looks_like_typedDict( # pylint: disable=invalid-name - node: nodes.FunctionDef | nodes.ClassDef, -) -> bool: - """Check if node is TypedDict FunctionDef.""" - return node.qname() in TYPING_TYPEDDICT_QUALIFIED - - -def infer_typedDict( # pylint: disable=invalid-name - node: nodes.FunctionDef, ctx: context.InferenceContext | None = None -) -> Iterator[nodes.ClassDef]: - """Replace TypedDict FunctionDef with ClassDef.""" - class_def = nodes.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: nodes.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, nodes.Name) - # TODO: remove _DeprecatedGenericAlias when Py3.14 min - and node.func.name in {"_alias", "_DeprecatedGenericAlias"} - and len(node.args) == 2 - and ( - # _alias function works also for builtins object such as list and dict - isinstance(node.args[0], (nodes.Attribute, nodes.Name)) - ) - ) - - -def _forbid_class_getitem_access(node: nodes.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: nodes.Call, ctx: context.InferenceContext | None = None -) -> Iterator[nodes.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, nodes.Assign) - and len(node.parent.targets) == 1 - and isinstance(node.parent.targets[0], nodes.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 = nodes.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, nodes.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 isinstance(maybe_type_var, nodes.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: nodes.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, nodes.Name) - and node.args - and ( - ( - node.func.name == "_TupleType" - and isinstance(node.args[0], nodes.Name) - and node.args[0].name == "tuple" - ) - or ( - node.func.name == "_CallableType" - and isinstance(node.args[0], nodes.Attribute) - and node.args[0].as_string() == "collections.abc.Callable" - ) - ) - ) - - -def infer_special_alias( - node: nodes.Call, ctx: context.InferenceContext | None = None -) -> Iterator[nodes.ClassDef]: - """Infer call to tuple alias as new subscriptable class typing.Tuple.""" - if not ( - isinstance(node.parent, nodes.Assign) - and len(node.parent.targets) == 1 - and isinstance(node.parent.targets[0], nodes.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 = nodes.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] - # 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_typing_cast(node: nodes.Call) -> bool: - return (isinstance(node.func, nodes.Name) and node.func.name == "cast") or ( - isinstance(node.func, nodes.Attribute) and node.func.attrname == "cast" - ) - - -def infer_typing_cast( - node: nodes.Call, ctx: context.InferenceContext | None = None -) -> Iterator[nodes.NodeNG]: - """Infer call to cast() returning same type as casted-from var.""" - if not isinstance(node.func, (nodes.Name, nodes.Attribute)): - raise UseInferenceDefault - - try: - func = next(node.func.infer(context=ctx)) - except (InferenceError, StopIteration) as exc: - raise UseInferenceDefault from exc - if not ( - isinstance(func, nodes.FunctionDef) - and func.qname() == "typing.cast" - and len(node.args) == 2 - ): - raise UseInferenceDefault - - return node.args[1].infer(context=ctx) - - -def _typing_transform(): - code = textwrap.dedent( - """ - class Generic: - @classmethod - def __class_getitem__(cls, item): return cls - class ParamSpec: - @property - def args(self): - return ParamSpecArgs(self) - @property - def kwargs(self): - return ParamSpecKwargs(self) - 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: ... - class ContextManager: - @classmethod - def __class_getitem__(cls, item): return cls - class AsyncContextManager: - @classmethod - def __class_getitem__(cls, item): return cls - class Pattern: - @classmethod - def __class_getitem__(cls, item): return cls - class Match: - @classmethod - def __class_getitem__(cls, item): return cls - """ - ) - if PY314_PLUS: - code += textwrap.dedent( - """ - from annotationlib import ForwardRef - class Union: - @classmethod - def __class_getitem__(cls, item): return cls - """ - ) - return AstroidBuilder(AstroidManager()).string_build(code) - - -def register(manager: AstroidManager) -> None: - manager.register_transform( - nodes.Call, - inference_tip(infer_typing_typevar_or_newtype), - looks_like_typing_typevar_or_newtype, - ) - manager.register_transform( - nodes.Subscript, inference_tip(infer_typing_attr), _looks_like_typing_subscript - ) - manager.register_transform( - nodes.Call, inference_tip(infer_typing_cast), _looks_like_typing_cast - ) - - manager.register_transform( - nodes.FunctionDef, inference_tip(infer_typedDict), _looks_like_typedDict - ) - - manager.register_transform( - nodes.Call, inference_tip(infer_typing_alias), _looks_like_typing_alias - ) - manager.register_transform( - nodes.Call, inference_tip(infer_special_alias), _looks_like_special_alias - ) - - if PY312_PLUS: - register_module_extender(manager, "typing", _typing_transform) - manager.register_transform( - nodes.ClassDef, - inference_tip(infer_typing_generic_class_pep695), - _looks_like_generic_class_pep695, - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py deleted file mode 100644 index 4103ce0..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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 import nodes -from astroid.brain.helpers import register_module_extender -from astroid.builder import parse -from astroid.manager import AstroidManager - - -def IsolatedAsyncioTestCaseImport() -> nodes.Module: - """ - 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py b/.venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py deleted file mode 100644 index 4405a62..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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 import nodes -from astroid.manager import AstroidManager - - -def _patch_uuid_class(node: nodes.ClassDef) -> None: - # The .int member is patched using __dict__ - node.locals["int"] = [nodes.Const(0, parent=node)] - - -def register(manager: AstroidManager) -> None: - manager.register_transform( - nodes.ClassDef, _patch_uuid_class, lambda node: node.qname() == "uuid.UUID" - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/brain/helpers.py b/.venv/lib/python3.10/site-packages/astroid/brain/helpers.py deleted file mode 100644 index 0064a1f..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/brain/helpers.py +++ /dev/null @@ -1,146 +0,0 @@ -# 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 typing import TYPE_CHECKING - -from astroid.exceptions import InferenceError -from astroid.manager import AstroidManager -from astroid.nodes.scoped_nodes import Module - -if TYPE_CHECKING: - from astroid.nodes.node_ng import NodeNG - - -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_functools, - brain_gi, - brain_hashlib, - brain_http, - brain_hypothesis, - brain_io, - brain_mechanize, - brain_multiprocessing, - brain_namedtuple_enum, - 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_statistics, - 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_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_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_statistics.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) - - -def is_class_var(node: NodeNG) -> bool: - """Return True if node is a ClassVar, with or without subscripting.""" - try: - inferred = next(node.infer()) - except (InferenceError, StopIteration): - return False - - return getattr(inferred, "name", "") == "ClassVar" diff --git a/.venv/lib/python3.10/site-packages/astroid/builder.py b/.venv/lib/python3.10/site-packages/astroid/builder.py deleted file mode 100644 index f166ab4..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/builder.py +++ /dev/null @@ -1,505 +0,0 @@ -# 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 re -import textwrap -import types -import warnings -from collections.abc import Collection, Iterator, Sequence -from io import TextIOWrapper -from tokenize import detect_encoding -from typing import TYPE_CHECKING, cast - -from astroid import bases, modutils, nodes, raw_building, rebuilder, util -from astroid._ast import ParserModule, get_parser_module -from astroid.const import PY312_PLUS, PY314_PLUS -from astroid.exceptions import AstroidBuildingError, AstroidSyntaxError, InferenceError - -if TYPE_CHECKING: - 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 = "#@" - -if PY312_PLUS: - warnings.filterwarnings("ignore", ".*invalid escape sequence", SyntaxWarning) -if PY314_PLUS: - warnings.filterwarnings( - "ignore", "'(return|continue|break)' in a 'finally'", SyntaxWarning - ) - - -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. 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, apply_transforms: bool = True) -> None: - super().__init__(manager) - self._apply_transforms = apply_transforms - if not raw_building.InspectBuilder.bootstrapped: - manager.bootstrap() - - 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, global_names 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, global_names) - # 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, modname=modname - ) - except (TypeError, ValueError, SyntaxError, MemoryError) 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, global_name: Collection[str] - ) -> None: - """Store imported names to the locals. - - Resort the locals if coming from a delayed node - """ - - def add_local(parent_or_root: nodes.NodeNG, name: str) -> None: - parent_or_root.set_local(name, node) - my_list = parent_or_root.scope().locals[name] - if TYPE_CHECKING: - my_list = cast(list[nodes.NodeNG], my_list) - my_list.sort(key=lambda n: n.fromlineno or 0) - - assert node.parent # It should always default to the module - module = node.root() - for name, asname in node.names: - if name == "*": - try: - imported = node.do_import_module() - except AstroidBuildingError: - continue - for name in imported.public_names(): - if name in global_name: - add_local(module, name) - else: - add_local(node.parent, name) - else: - name = asname or name - if name in global_name: - add_local(module, name) - else: - add_local(node.parent, name) - - def delayed_assattr(self, node: nodes.AssignAttr) -> None: - """Visit an AssignAttr 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: - # We want a narrow check on the parent type, not all of its subclasses - if type(inferred) in {bases.Instance, 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. - """ - # pylint: disable-next=import-outside-toplevel - from astroid.manager import AstroidManager - - code = textwrap.dedent(code) - builder = AstroidBuilder(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 - and node.args - ): - 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, modname: str | None = None -) -> tuple[ast.Module, ParserModule]: - parser_module = get_parser_module(type_comments=type_comments) - try: - parsed = parser_module.parse( - data + "\n", type_comments=type_comments, filename=modname - ) - 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. We use a heuristic for - # determining if the error is due to type annotations. - type_annot_related = re.search(r"#\s+type:", exc.text or "") - if not (type_annot_related and type_comments): - raise - - parser_module = get_parser_module(type_comments=False) - parsed = parser_module.parse(data + "\n", type_comments=False) - return parsed, parser_module diff --git a/.venv/lib/python3.10/site-packages/astroid/const.py b/.venv/lib/python3.10/site-packages/astroid/const.py deleted file mode 100644 index dcce074..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/const.py +++ /dev/null @@ -1,26 +0,0 @@ -# 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 - -PY311_PLUS = sys.version_info >= (3, 11) -PY312_PLUS = sys.version_info >= (3, 12) -PY313 = sys.version_info[:2] == (3, 13) -PY313_PLUS = sys.version_info >= (3, 13) -PY314_PLUS = sys.version_info >= (3, 14) - -WIN32 = sys.platform == "win32" - -IS_PYPY = sys.implementation.name == "pypy" -IS_JYTHON = sys.implementation.name == "jython" - - -class Context(enum.Enum): - Load = 1 - Store = 2 - Del = 3 - - -_EMPTY_OBJECT_MARKER = object() diff --git a/.venv/lib/python3.10/site-packages/astroid/constraint.py b/.venv/lib/python3.10/site-packages/astroid/constraint.py deleted file mode 100644 index 692d22d..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/constraint.py +++ /dev/null @@ -1,186 +0,0 @@ -# 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 - -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 = 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) - - -class BooleanConstraint(Constraint): - """Represents an "x" or "not x" constraint.""" - - @classmethod - def match( - cls, node: _NameNodes, expr: nodes.NodeNG, negate: bool = False - ) -> Self | None: - """Return a new constraint for node if expr matches one of these patterns: - - - direct match (expr == node): use given negate value - - negated match (expr == `not node`): flip negate value - - Return None if no pattern matches. - """ - if _matches(expr, node): - return cls(node=node, negate=negate) - - if ( - isinstance(expr, nodes.UnaryOp) - and expr.op == "not" - and _matches(expr.operand, node) - ): - return cls(node=node, negate=not negate) - - return None - - def satisfied_by(self, inferred: InferenceResult) -> bool: - """Return True for uninferable results, or depending on negate flag: - - - negate=False: satisfied if boolean value is True - - negate=True: satisfied if boolean value is False - """ - inferred_booleaness = inferred.bool_value() - if isinstance(inferred, util.UninferableBase) or isinstance( - inferred_booleaness, util.UninferableBase - ): - return True - - return self.negate ^ inferred_booleaness - - -def get_constraints( - expr: _NameNodes, frame: nodes.LocalsDictNodeNG -) -> dict[nodes.If | nodes.IfExp, 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 | nodes.IfExp, set[Constraint]] = {} - while current_node is not None and current_node is not frame: - parent = current_node.parent - if isinstance(parent, (nodes.If, nodes.IfExp)): - 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, - BooleanConstraint, - ) -) -"""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 diff --git a/.venv/lib/python3.10/site-packages/astroid/context.py b/.venv/lib/python3.10/site-packages/astroid/context.py deleted file mode 100644 index fa9ed22..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/context.py +++ /dev/null @@ -1,204 +0,0 @@ -# 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, Sequence -from typing import TYPE_CHECKING - -from astroid.typing import InferenceResult, SuccessfulInferenceResult - -if TYPE_CHECKING: - from astroid import constraint, nodes - -_InferenceCache = dict[ - tuple["nodes.NodeNG", str | None, str | None, str | None], Sequence["nodes.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__ = ( - "_nodes_inferred", - "boundnode", - "callcontext", - "constraints", - "extra_context", - "lookupname", - "path", - ) - - 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 | nodes.IfExp, 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", "callee", "keywords") - - def __init__( - self, - args: list[nodes.NodeNG], - keywords: list[nodes.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 diff --git a/.venv/lib/python3.10/site-packages/astroid/decorators.py b/.venv/lib/python3.10/site-packages/astroid/decorators.py deleted file mode 100644 index 05d2dd3..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/decorators.py +++ /dev/null @@ -1,232 +0,0 @@ -# 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 ParamSpec, TypeVar - -from astroid import util -from astroid.context import InferenceContext -from astroid.exceptions import InferenceError -from astroid.typing import InferenceResult - -_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]], -) -> Callable[_P, Generator[InferenceResult]]: - def inner(*args: _P.args, **kwargs: _P.kwargs) -> Generator[InferenceResult]: - 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]], -) -> Callable[_P, Generator[InferenceResult]]: - def inner(*args: _P.args, **kwargs: _P.kwargs) -> Generator[InferenceResult]: - 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 - # pylint: disable = too-many-boolean-expressions - 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/exceptions.py b/.venv/lib/python3.10/site-packages/astroid/exceptions.py deleted file mode 100644 index e523b70..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/exceptions.py +++ /dev/null @@ -1,419 +0,0 @@ -# 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", - "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: - try: - return self.message.format(**vars(self)) - except ValueError: - return self.message # Return raw message if formatting fails - - -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, too-many-positional-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 diff --git a/.venv/lib/python3.10/site-packages/astroid/filter_statements.py b/.venv/lib/python3.10/site-packages/astroid/filter_statements.py deleted file mode 100644 index a48b6e7..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/filter_statements.py +++ /dev/null @@ -1,240 +0,0 @@ -# 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, _base_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. - """ - # pylint: disable = too-many-branches, too-many-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: _base_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 diff --git a/.venv/lib/python3.10/site-packages/astroid/helpers.py b/.venv/lib/python3.10/site-packages/astroid/helpers.py deleted file mode 100644 index 9c370aa..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/helpers.py +++ /dev/null @@ -1,335 +0,0 @@ -# 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, 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]: - 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): - 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: - 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( - f"arg 2 must be a type or tuple of types, not {type(klass)!r}" - ) - - 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, not {type(node)!r}") - 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 - - 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/inference_tip.py b/.venv/lib/python3.10/site-packages/astroid/inference_tip.py deleted file mode 100644 index c3187c0..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/inference_tip.py +++ /dev/null @@ -1,130 +0,0 @@ -# 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]: - 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py deleted file mode 100644 index af7c55b..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py +++ /dev/null @@ -1,496 +0,0 @@ -# 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 Iterable, Iterator, Sequence -from functools import lru_cache -from pathlib import Path -from typing import Literal, NamedTuple, Protocol - -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 - - @staticmethod - @abc.abstractmethod - def find_module( - modname: str, - module_parts: tuple[str, ...], - processed: tuple[str, ...], - submodule_path: tuple[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 tuple 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 tuple 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] - ) - - @staticmethod - @lru_cache(maxsize=1024) - def find_module( - modname: str, - module_parts: tuple[str, ...], - processed: tuple[str, ...], - submodule_path: tuple[str, ...] | None, - ) -> ModuleSpec | None: - # pylint: disable-next=import-outside-toplevel - from astroid.modutils import cached_os_path_isfile - - # Although we should be able to use `find_spec` this doesn't work on PyPy for builtins. - # Therefore, we use the `builtin_module_nams` heuristic for these. - if submodule_path is None and modname in sys.builtin_module_names: - return ModuleSpec( - name=modname, - location=None, - type=ModuleType.C_BUILTIN, - ) - - if submodule_path is not None: - search_paths = list(submodule_path) - else: - search_paths = sys.path - - suffixes = (".py", ".pyi", importlib.machinery.BYTECODE_SUFFIXES[0]) - for entry in search_paths: - package_directory = os.path.join(entry, modname) - for suffix in suffixes: - package_file_name = "__init__" + suffix - file_path = os.path.join(package_directory, package_file_name) - if cached_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 cached_os_path_isfile(file_path): - return ModuleSpec(name=modname, location=file_path, type=type_) - - # If the module name matches a stdlib module name, check whether this is a frozen - # module. Note that `find_spec` actually imports parent modules, so we want to make - # sure we only run this code for stuff that can be expected to be frozen. For now - # this is only stdlib. - if (modname in sys.stdlib_module_names and not processed) or ( - processed and processed[0] in sys.stdlib_module_names - ): - try: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=Warning) - spec = importlib.util.find_spec(".".join((*processed, modname))) - except ValueError: - spec = None - - if ( - spec - and spec.loader # type: ignore[comparison-overlap] # noqa: E501 - is importlib.machinery.FrozenImporter - ): - return ModuleSpec( - name=modname, - location=getattr(spec.loader_state, "filename", None), - type=ModuleType.PY_FROZEN, - ) - - return None - - def contribute_to_path( - self, spec: ModuleSpec, processed: list[str] - ) -> Sequence[str] | None: - if spec.location is None: - # Builtin. - return None - # pylint: disable-next=import-outside-toplevel - from astroid.modutils import EXT_LIB_DIRS - - 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.""" - - @staticmethod - @lru_cache(maxsize=1024) - def find_module( - modname: str, - module_parts: tuple[str, ...], - processed: tuple[str, ...], - submodule_path: tuple[str, ...] | None, - ) -> ModuleSpec | None: - if processed: - modname = ".".join([*processed, modname]) - if util.is_namespace(modname) and modname in sys.modules: - return ModuleSpec( - name=modname, - location="", - origin="namespace", - type=ModuleType.PY_NAMESPACE, - submodule_search_locations=sys.modules[modname].__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( - entry_path - ) - except zipimport.ZipImportError: - continue - - @staticmethod - @lru_cache(maxsize=1024) - def find_module( - modname: str, - module_parts: tuple[str, ...], - processed: tuple[str, ...], - submodule_path: tuple[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, - ) - - def contribute_to_path( - self, spec: ModuleSpec, processed: list[str] - ) -> Sequence[str] | None: - return spec.submodule_search_locations - - -class PathSpecFinder(Finder): - """Finder based on importlib.machinery.PathFinder.""" - - @staticmethod - @lru_cache(maxsize=1024) - def find_module( - modname: str, - module_parts: tuple[str, ...], - processed: tuple[str, ...], - submodule_path: tuple[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, -) - - -@lru_cache(maxsize=1024) -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 importer is not None and isinstance(importer, zipimport.zipimporter): - yield filepath, importer - - -def _search_zip( - modpath: tuple[str, ...], -) -> tuple[Literal[ModuleType.PY_ZIPMODULE], str, str]: - for filepath, importer in _get_zipimporters(): - found = importer.find_spec(modpath[0]) - if found: - if not importer.find_spec(os.path.sep.join(modpath)): - raise ImportError( - "No module named {} in {}/{}".format( - ".".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: tuple[str, ...], - processed: tuple[str, ...], - submodule_path: tuple[str, ...] | None, -) -> tuple[Finder | _MetaPathFinder, ModuleSpec]: - for finder in _SPEC_FINDERS: - finder_instance = finder(search_path) - mod_spec = finder.find_module(modname, module_parts, processed, submodule_path) - if mod_spec is None: - continue - return finder_instance, mod_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: Iterable[str], path: Iterable[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. - """ - return _find_spec(tuple(modpath), tuple(path) if path else None) - - -@lru_cache(maxsize=1024) -def _find_spec( - module_path: tuple[str, ...], path: tuple[str, ...] | None -) -> ModuleSpec: - _path = path or sys.path - - # Need a copy for not mutating the argument. - modpath = list(module_path) - - search_paths = None - processed: list[str] = [] - - while modpath: - modname = modpath.pop(0) - - submodule_path = search_paths or path - if submodule_path is not None: - submodule_path = tuple(submodule_path) - - finder, spec = _find_spec_with_path( - _path, modname, module_path, tuple(processed), submodule_path - ) - processed.append(modname) - if modpath: - if isinstance(finder, Finder): - search_paths = finder.contribute_to_path(spec, processed) - # If modname is a package from an editable install, update search_paths - # 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] - search_paths = spec.submodule_search_locations - - if spec.type == ModuleType.PKG_DIRECTORY: - spec = spec._replace(submodule_search_locations=search_paths) - - return spec diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py deleted file mode 100644 index 8b8725f..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py +++ /dev/null @@ -1,111 +0,0 @@ -# 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 # type: ignore[attr-defined] -from importlib.util import _find_spec_from_path # type: ignore[attr-defined] - -from astroid.const import IS_PYPY - -if sys.version_info >= (3, 11): - from importlib.machinery import NamespaceLoader -else: - from importlib._bootstrap_external import _NamespaceLoader as NamespaceLoader - - -@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: - 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 - and ( - found_spec.loader is None or isinstance(found_spec.loader, NamespaceLoader) - ) - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py deleted file mode 100644 index 8eab35c..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py +++ /dev/null @@ -1,75 +0,0 @@ -# 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 import nodes -from astroid.exceptions import AttributeInferenceError - -if TYPE_CHECKING: - 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, (nodes.List, nodes.Tuple, nodes.Const, nodes.Dict, nodes.Set)): - return _builtin_lookup(node, name) - if isinstance(node, astroid.Instance): - return _lookup_in_mro(node, name) - if isinstance(node, nodes.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 diff --git a/.venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py b/.venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py deleted file mode 100644 index eac9e43..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py +++ /dev/null @@ -1,1013 +0,0 @@ -# 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(parent=self._instance) - - @property - def attr___loader__(self): - # No handling for now. - return node_classes.Unknown(parent=self._instance) - - @property - def attr___cached__(self): - # No handling for now. - return node_classes.Unknown(parent=self._instance) - - -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 = nodes.Arguments( - parent=func.args.parent, vararg=None, kwarg=None - ) - - positional_or_keyword_params = func.args.args.copy() - positional_or_keyword_params.append( - nodes.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(parent=self._instance) - - 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___annotations__(self) -> node_classes.Unknown: - return node_classes.Unknown(parent=self._instance) - - @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): - mro = self._instance.mro() - obj = node_classes.Tuple(parent=self._instance) - obj.postinit(mro) - return obj - - @property - def attr_mro(self): - 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. - """ - - 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 GeneratorBaseModel(FunctionModel, ContextManagerModel): - def __init__(self, gen_module: nodes.Module): - super().__init__() - for name, values in gen_module.locals.items(): - method = values[0] - if isinstance(method, nodes.FunctionDef): - method = bases.BoundMethod(method, _get_bound_node(self)) - - def patched(cls, meth=method): - return meth - - setattr(type(self), IMPL_PREFIX + name, property(patched)) - - @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 GeneratorModel(GeneratorBaseModel): - def __init__(self): - super().__init__(AstroidManager().builtins_module["generator"]) - - -class AsyncGeneratorModel(GeneratorBaseModel): - def __init__(self): - super().__init__(AstroidManager().builtins_module["async_generator"]) - - -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 GroupExceptionInstanceModel(ExceptionInstanceModel): - @property - def attr_exceptions(self) -> nodes.Tuple: - return node_classes.Tuple(parent=self._instance) - - -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(b"") - - -BUILTIN_EXCEPTIONS = { - "builtins.SyntaxError": SyntaxErrorInstanceModel, - "builtins.ExceptionGroup": GroupExceptionInstanceModel, - "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) -> nodes.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 diff --git a/.venv/lib/python3.10/site-packages/astroid/manager.py b/.venv/lib/python3.10/site-packages/astroid/manager.py deleted file mode 100644 index e232886..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/manager.py +++ /dev/null @@ -1,478 +0,0 @@ -# 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.builder import AstroidBuilder, build_namespace_package_module -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_, - _has_init, - cached_os_path_isfile, - 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(), - "module_denylist": set(), - "_transform": TransformVisitor(), - "prefer_stubs": False, - } - - 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.module_denylist = AstroidManager.brain["module_denylist"] - self._transform = AstroidManager.brain["_transform"] - self.prefer_stubs = AstroidManager.brain["prefer_stubs"] - - @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"] - - @property - def prefer_stubs(self) -> bool: - return AstroidManager.brain["prefer_stubs"] - - @prefer_stubs.setter - def prefer_stubs(self, value: bool) -> None: - AstroidManager.brain["prefer_stubs"] = value - - 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, prefer_stubs=self.prefer_stubs - ) - 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: - 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. - """ - return AstroidBuilder(self).string_build(data, modname, filepath) - - def _build_stub_module(self, modname: str) -> nodes.Module: - return AstroidBuilder(self).string_build("", modname) - - def _build_namespace_module( - self, modname: str, path: Sequence[str] - ) -> nodes.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.module_denylist: - raise AstroidImportError(f"Skipping ignored module {modname!r}") - 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 - - 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: - 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 - - 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: - yield from modastroid.igetattr(name, context) - 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 `nodes.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._import.spec import ( - _find_spec, - _is_setuptools_namespace, - ) - 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() - self._mod_file_cache.clear() - - # NB: not a new TransformVisitor() - AstroidManager.brain["_transform"].transforms = collections.defaultdict(list) - - for lru_cache in ( - LookupMixIn.lookup, - _cache_normalize_path_, - _has_init, - cached_os_path_isfile, - util.is_namespace, - ObjectModel.attributes, - ClassDef._metaclass_lookup_attribute, - _find_spec, - _is_setuptools_namespace, - ): - lru_cache.cache_clear() # type: ignore[attr-defined] - - for finder in spec._SPEC_FINDERS: - finder.find_module.cache_clear() - - self.bootstrap() - - # Reload brain plugins. During initialisation this is done in astroid.manager.py - register_all_brains(self) diff --git a/.venv/lib/python3.10/site-packages/astroid/modutils.py b/.venv/lib/python3.10/site-packages/astroid/modutils.py deleted file mode 100644 index 0868c60..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/modutils.py +++ /dev/null @@ -1,703 +0,0 @@ -# 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 sys import stdlib_module_names - -from astroid.const import IS_JYTHON -from astroid.interpreter._import import spec, util - -logger = logging.getLogger(__name__) - - -if sys.platform.startswith("win"): - PY_SOURCE_EXTS = ("py", "pyw", "pyi") - PY_SOURCE_EXTS_STUBS_FIRST = ("pyi", "pyw", "py") - PY_COMPILED_EXTS = ("dll", "pyd") -else: - PY_SOURCE_EXTS = ("py", "pyi") - PY_SOURCE_EXTS_STUBS_FIRST = ("pyi", "py") - 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 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 _is_subpath(path: str, base: str) -> bool: - path = os.path.normcase(os.path.normpath(path)) - base = os.path.normcase(os.path.normpath(base)) - if not path.startswith(base): - return False - return (len(path) == len(base)) or (path[len(base)] == os.path.sep) - - -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 - """ - path_to_check = os.path.normcase(os.path.normpath(path_to_check)) - - abs_filename = os.path.abspath(filename) - if _is_subpath(abs_filename, path_to_check): - base_path = os.path.splitext(abs_filename)[0] - relative_base_path = base_path[len(path_to_check) :].lstrip(os.path.sep) - return [pkg for pkg in relative_base_path.split(os.sep) if pkg] - - real_filename = os.path.realpath(filename) - if _is_subpath(real_filename, path_to_check): - base_path = os.path.splitext(real_filename)[0] - relative_base_path = base_path[len(path_to_check) :].lstrip(os.path.sep) - return [pkg for pkg in relative_base_path.split(os.sep) if pkg] - - return None - - -def modpath_from_file_with_callback( - filename: str, - path: list[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 + paths_to_check - 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(paths_to_check) - ) - ) - - -def modpath_from_file(filename: str, path: list[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 paths where the module or package should be - searched, additionally to sys.path - - :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, prefer_stubs: 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) - orig_ext = orig_ext.lstrip(".") - if orig_ext not in PY_SOURCE_EXTS and os.path.exists(f"{base}.{orig_ext}"): - return f"{base}.{orig_ext}" - for ext in PY_SOURCE_EXTS_STUBS_FIRST if prefer_stubs else 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] - ) - ) - - -@lru_cache(maxsize=1024) -def cached_os_path_isfile(path: str | os.PathLike[str]) -> bool: - """A cached version of os.path.isfile that helps avoid repetitive I/O""" - return os.path.isfile(path) - - -# 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")) - - -@lru_cache(maxsize=1024) -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) - ) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/__init__.py b/.venv/lib/python3.10/site-packages/astroid/nodes/__init__.py deleted file mode 100644 index 6a67516..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/__init__.py +++ /dev/null @@ -1,303 +0,0 @@ -# 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 ` - -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, - Interpolation, - 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, - TemplateStr, - Try, - TryStar, - Tuple, - TypeAlias, - TypeVar, - TypeVarTuple, - UnaryOp, - Unknown, - While, - With, - Yield, - YieldFrom, - are_exclusive, - const_factory, - unpack_infer, -) -from astroid.nodes.scoped_nodes import ( - SYNTHETIC_ROOT, - 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__ = ( - "CONST_CLS", - "SYNTHETIC_ROOT", - "AnnAssign", - "Arguments", - "Assert", - "Assign", - "AssignAttr", - "AssignName", - "AsyncFor", - "AsyncFunctionDef", - "AsyncWith", - "Attribute", - "AugAssign", - "Await", - "BaseContainer", - "BinOp", - "BoolOp", - "Break", - "Call", - "ClassDef", - "Compare", - "Comprehension", - "ComprehensionScope", - "Const", - "Continue", - "Decorators", - "DelAttr", - "DelName", - "Delete", - "Dict", - "DictComp", - "DictUnpack", - "EmptyNode", - "EvaluatedObject", - "ExceptHandler", - "Expr", - "For", - "FormattedValue", - "FunctionDef", - "GeneratorExp", - "Global", - "If", - "IfExp", - "Import", - "ImportFrom", - "Interpolation", - "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", - "TemplateStr", - "Try", - "TryStar", - "Tuple", - "TypeAlias", - "TypeVar", - "TypeVarTuple", - "UnaryOp", - "Unknown", - "While", - "With", - "Yield", - "YieldFrom", - "are_exclusive", - "builtin_lookup", - "const_factory", - "function_to_method", - "get_wrapping_class", - "unpack_infer", -) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py b/.venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py deleted file mode 100644 index df452cb..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py +++ /dev/null @@ -1,672 +0,0 @@ -# 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 Callable, Generator, Iterator -from functools import cached_property, lru_cache, partial -from typing import TYPE_CHECKING, Any, ClassVar - -from astroid import bases, nodes, util -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, - InferenceResult | None, - nodes.AugAssign | nodes.BinOp, - InferenceResult, - InferenceResult | None, - InferenceContext, - InferenceContext, - ], - list[partial[Generator[InferenceResult]]], - ] - - -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 imported by .""" - 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], - ], - context: InferenceContext | None, - error: type[util.BadOperationMessage], - ) -> Generator[InferenceResult]: - 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]: - """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]]: - """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]]: - """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]: - """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]]]: - """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]]]: - """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 - ), - ] - - # pylint: disable = too-many-boolean-expressions - if ( - 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]: - """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) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/as_string.py b/.venv/lib/python3.10/site-packages/astroid/nodes/as_string.py deleted file mode 100644 index 01007b9..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/as_string.py +++ /dev/null @@ -1,740 +0,0 @@ -# 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 - -import warnings -from collections.abc import Iterator -from typing import TYPE_CHECKING - -from astroid import nodes - -if TYPE_CHECKING: - from astroid import objects - -# 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: nodes.NodeNG) -> str: - """Makes this visitor behave as a simple function""" - return node.accept(self).replace(DOC_NEWLINE, "\n") - - def _docs_dedent(self, doc_node: nodes.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: nodes.NodeNG, child: nodes.NodeNG, 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: nodes.NodeNG, child: nodes.NodeNG, 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_ methods ########################################### - - def visit_await(self, node: nodes.Await) -> str: - return f"await {node.value.accept(self)}" - - def visit_asyncwith(self, node: nodes.AsyncWith) -> str: - return f"async {self.visit_with(node)}" - - def visit_asyncfor(self, node: nodes.AsyncFor) -> str: - return f"async {self.visit_for(node)}" - - def visit_arguments(self, node: nodes.Arguments) -> str: - """return an nodes.Arguments node as string""" - return node.format_args() - - def visit_assignattr(self, node: nodes.AssignAttr) -> str: - """return an nodes.AssignAttr node as string""" - return self.visit_attribute(node) - - def visit_assert(self, node: nodes.Assert) -> str: - """return an nodes.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: nodes.AssignName) -> str: - """return an nodes.AssignName node as string""" - return node.name - - def visit_assign(self, node: nodes.Assign) -> str: - """return an nodes.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: nodes.AugAssign) -> str: - """return an nodes.AugAssign node as string""" - return f"{node.target.accept(self)} {node.op} {node.value.accept(self)}" - - def visit_annassign(self, node: nodes.AnnAssign) -> str: - """Return an nodes.AnnAssign 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: nodes.BinOp) -> str: - """return an nodes.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: nodes.BoolOp) -> str: - """return an nodes.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: nodes.Break) -> str: - """return an nodes.Break node as string""" - return "break" - - def visit_call(self, node: nodes.Call) -> str: - """return an nodes.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 _handle_type_params( - self, type_params: list[nodes.TypeVar | nodes.ParamSpec | nodes.TypeVarTuple] - ) -> str: - return ( - f"[{', '.join(tp.accept(self) for tp in type_params)}]" - if type_params - else "" - ) - - def visit_classdef(self, node: nodes.ClassDef) -> str: - """return an nodes.ClassDef node as string""" - decorate = node.decorators.accept(self) if node.decorators else "" - type_params = self._handle_type_params(node.type_params) - 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) - return "\n\n{}class {}{}{}:{}\n{}\n".format( - decorate, node.name, type_params, args_str, docs, self._stmt_list(node.body) - ) - - def visit_compare(self, node: nodes.Compare) -> str: - """return an nodes.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: nodes.Comprehension) -> str: - """return an nodes.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: nodes.Const) -> str: - """return an nodes.Const node as string""" - if node.value is Ellipsis: - return "..." - return repr(node.value) - - def visit_continue(self, node: nodes.Continue) -> str: - """return an nodes.Continue node as string""" - return "continue" - - def visit_delete(self, node: nodes.Delete) -> str: - """return an nodes.Delete node as string""" - return f"del {', '.join(child.accept(self) for child in node.targets)}" - - def visit_delattr(self, node: nodes.DelAttr) -> str: - """return an nodes.DelAttr node as string""" - return self.visit_attribute(node) - - def visit_delname(self, node: nodes.DelName) -> str: - """return an nodes.DelName node as string""" - return node.name - - def visit_decorators(self, node: nodes.Decorators) -> str: - """return an nodes.Decorators node as string""" - return "@%s\n" % "\n@".join(item.accept(self) for item in node.nodes) - - def visit_dict(self, node: nodes.Dict) -> str: - """return an nodes.Dict node as string""" - return "{%s}" % ", ".join(self._visit_dict(node)) - - def _visit_dict(self, node: nodes.Dict) -> 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: nodes.DictUnpack) -> str: - return "**" - - def visit_dictcomp(self, node: nodes.DictComp) -> str: - """return an nodes.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: nodes.Expr) -> str: - """return an nodes.Expr node as string""" - return node.value.accept(self) - - def visit_emptynode(self, node: nodes.EmptyNode) -> str: - """dummy method for visiting an EmptyNode""" - return "" - - def visit_excepthandler(self, node: nodes.ExceptHandler) -> 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: nodes.EmptyNode) -> str: - """return an EmptyNode as string""" - return "" - - def visit_for(self, node: nodes.For) -> str: - """return an nodes.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: nodes.ImportFrom) -> str: - """return an nodes.ImportFrom node as string""" - return "from {} import {}".format( - "." * (node.level or 0) + node.modname, _import_string(node.names) - ) - - def visit_joinedstr(self, node: nodes.JoinedStr) -> 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: nodes.FormattedValue) -> 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 "" - type_params = self._handle_type_params(node.type_params) - docs = self._docs_dedent(node.doc_node) - trailer = ":" - if node.returns: - return_annotation = " -> " + node.returns.as_string() - trailer = return_annotation + ":" - def_format = "\n%s%s %s%s(%s)%s%s\n%s" - return def_format % ( - decorate, - keyword, - node.name, - type_params, - node.args.accept(self), - trailer, - docs, - self._stmt_list(node.body), - ) - - def visit_functiondef(self, node: nodes.FunctionDef) -> str: - """return an nodes.FunctionDef node as string""" - return self.handle_functiondef(node, "def") - - def visit_asyncfunctiondef(self, node: nodes.AsyncFunctionDef) -> str: - """return an nodes.AsyncFunction node as string""" - return self.handle_functiondef(node, "async def") - - def visit_generatorexp(self, node: nodes.GeneratorExp) -> str: - """return an nodes.GeneratorExp node as string""" - return "({} {})".format( - node.elt.accept(self), " ".join(n.accept(self) for n in node.generators) - ) - - def visit_attribute( - self, node: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr - ) -> str: - """return an nodes.Attribute node as string""" - try: - left = self._precedence_parens(node, node.expr) - except RecursionError: - warnings.warn( - "Recursion limit exhausted; defaulting to adding parentheses.", - UserWarning, - stacklevel=2, - ) - left = f"({node.expr.accept(self)})" - if left.isdigit(): - left = f"({left})" - return f"{left}.{node.attrname}" - - def visit_global(self, node: nodes.Global) -> str: - """return an nodes.Global node as string""" - return f"global {', '.join(node.names)}" - - def visit_if(self, node: nodes.If) -> str: - """return an nodes.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: nodes.IfExp) -> str: - """return an nodes.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: nodes.Import) -> str: - """return an nodes.Import node as string""" - return f"import {_import_string(node.names)}" - - def visit_keyword(self, node: nodes.Keyword) -> str: - """return an nodes.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: nodes.Lambda) -> str: - """return an nodes.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: nodes.List) -> str: - """return an nodes.List node as string""" - return f"[{', '.join(child.accept(self) for child in node.elts)}]" - - def visit_listcomp(self, node: nodes.ListComp) -> str: - """return an nodes.ListComp node as string""" - return "[{} {}]".format( - node.elt.accept(self), " ".join(n.accept(self) for n in node.generators) - ) - - def visit_module(self, node: nodes.Module) -> str: - """return an nodes.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: nodes.Name) -> str: - """return an nodes.Name node as string""" - return node.name - - def visit_namedexpr(self, node: nodes.NamedExpr) -> 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: nodes.Nonlocal) -> str: - """return an nodes.Nonlocal node as string""" - return f"nonlocal {', '.join(node.names)}" - - def visit_paramspec(self, node: nodes.ParamSpec) -> str: - """return an nodes.ParamSpec node as string""" - default_value_str = ( - f" = {node.default_value.accept(self)}" if node.default_value else "" - ) - return f"**{node.name.accept(self)}{default_value_str}" - - def visit_pass(self, node: nodes.Pass) -> str: - """return an nodes.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: nodes.Raise) -> str: - """return an nodes.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: nodes.Return) -> str: - """return an nodes.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: nodes.Set) -> str: - """return an nodes.Set node as string""" - return "{%s}" % ", ".join(child.accept(self) for child in node.elts) - - def visit_setcomp(self, node: nodes.SetComp) -> str: - """return an nodes.SetComp node as string""" - return "{{{} {}}}".format( - node.elt.accept(self), " ".join(n.accept(self) for n in node.generators) - ) - - def visit_slice(self, node: nodes.Slice) -> str: - """return an nodes.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: nodes.Subscript) -> str: - """return an nodes.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: nodes.Try) -> str: - """return an nodes.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: nodes.TryStar) -> str: - """return an nodes.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: nodes.Tuple) -> str: - """return an nodes.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 nodes.TypeAlias node as string""" - type_params = self._handle_type_params(node.type_params) - return f"type {node.name.accept(self)}{type_params} = {node.value.accept(self)}" - - def visit_typevar(self, node: nodes.TypeVar) -> str: - """return an nodes.TypeVar node as string""" - bound_str = f": {node.bound.accept(self)}" if node.bound else "" - default_value_str = ( - f" = {node.default_value.accept(self)}" if node.default_value else "" - ) - return f"{node.name.accept(self)}{bound_str}{default_value_str}" - - def visit_typevartuple(self, node: nodes.TypeVarTuple) -> str: - """return an nodes.TypeVarTuple node as string""" - default_value_str = ( - f" = {node.default_value.accept(self)}" if node.default_value else "" - ) - return f"*{node.name.accept(self)}{default_value_str}" - - def visit_unaryop(self, node: nodes.UnaryOp) -> str: - """return an nodes.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: nodes.While) -> str: - """return an nodes.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: nodes.With) -> str: # 'with' without 'as' is possible - """return an nodes.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: nodes.Yield) -> 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: nodes.YieldFrom) -> str: - """Return an nodes.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: nodes.Starred) -> str: - """return Starred node as string""" - return "*" + node.value.accept(self) - - def visit_match(self, node: nodes.Match) -> str: - """Return an nodes.Match node as string.""" - return f"match {node.subject.accept(self)}:\n{self._stmt_list(node.cases)}" - - def visit_matchcase(self, node: nodes.MatchCase) -> str: - """Return an nodes.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: nodes.MatchValue) -> str: - """Return an nodes.MatchValue node as string.""" - return node.value.accept(self) - - @staticmethod - def visit_matchsingleton(node: nodes.MatchSingleton) -> str: - """Return an nodes.MatchSingleton node as string.""" - return str(node.value) - - def visit_matchsequence(self, node: nodes.MatchSequence) -> str: - """Return an nodes.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: nodes.MatchMapping) -> str: - """Return an nodes..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: nodes.MatchClass) -> str: - """Return an nodes..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: nodes.MatchStar) -> str: - """Return an nodes..MatchStar node as string.""" - return f"*{node.name.accept(self) if node.name else '_'}" - - def visit_matchas(self, node: nodes.MatchAs) -> str: - """Return an nodes..MatchAs node as string.""" - if isinstance( - node.parent, (nodes.MatchSequence, nodes.MatchMapping, nodes.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: nodes.MatchOr) -> str: - """Return an nodes.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) - - def visit_templatestr(self, node: nodes.TemplateStr) -> str: - """Return an nodes.TemplateStr node as string.""" - string = "" - for value in node.values: - match value: - case nodes.Interpolation(): - string += "{" + value.accept(self) + "}" - case _: - string += value.accept(self)[1:-1] - for quote in ("'", '"', '"""', "'''"): - if quote not in string: - break - return "t" + quote + string + quote - - def visit_interpolation(self, node: nodes.Interpolation) -> str: - """Return an nodes.Interpolation node as string.""" - result = f"{node.str}" - 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 result - - # These aren't for real AST nodes, but for inference objects. - - def visit_frozenset(self, node: objects.FrozenSet) -> str: - return node.parent.accept(self) - - def visit_super(self, node: objects.Super) -> str: - return node.parent.accept(self) - - def visit_uninferable(self, node) -> str: - return str(node) - - def visit_property(self, node: objects.Property) -> str: - return node.function.accept(self) - - def visit_evaluatedobject(self, node: nodes.EvaluatedObject) -> str: - return node.original.accept(self) - - def visit_unknown(self, node: nodes.Unknown) -> str: - return str(node) - - -def _import_string(names: list[tuple[str, str | None]]) -> 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(" ") diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/const.py b/.venv/lib/python3.10/site-packages/astroid/nodes/const.py deleted file mode 100644 index f66b633..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/const.py +++ /dev/null @@ -1,27 +0,0 @@ -# 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 -} diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py b/.venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py deleted file mode 100644 index 0d3a425..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py +++ /dev/null @@ -1,5701 +0,0 @@ -# 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 Callable, Generator, Iterable, Iterator, Mapping -from functools import cached_property -from typing import TYPE_CHECKING, Any, ClassVar, Literal, Union - -from astroid import decorators, protocols, util -from astroid.bases import Instance, _infer_stmts -from astroid.const import _EMPTY_OBJECT_MARKER, PY314_PLUS, 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.nodes.scoped_nodes import SYNTHETIC_ROOT -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) - -# pylint: disable-next=consider-alternative-union-syntax -AssignedStmtsPossibleNode = Union["List", "Tuple", "AssignName", "AssignAttr", None] -AssignedStmtsCall = Callable[ - [ - _NodesT, - AssignedStmtsPossibleNode, - InferenceContext | None, - list[int] | None, - ], - Any, -] -InferBinaryOperation = Callable[ - [_NodesT, InferenceContext | None], - Generator[InferenceResult | _BadOpMessageT], -] -InferLHS = Callable[ - [_NodesT, InferenceContext | None], - Generator[InferenceResult, None, InferenceErrorInfo | None], -] -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 - - >>> list(node.get_children()) - [, ] - >>> 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()) - [] - >>> 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 - - >>> list(node.get_children()) - [, ] - >>> 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 - - >>> node.args - - """ - - # 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, too-many-positional-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]: - # 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) - else: - default_offset = None - 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 default_offset 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 - - >>> list(node.get_children()) - [, ] - >>> 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 - - """ - - _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 - - """ - - 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 - - """ - - _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 - - """ - - _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 - ) -> list[util.BadBinaryOperationMessage]: - """Get a list of type errors which can occur during inference. - - Each TypeError is represented by a :class:`BadBinaryOperationMessage` , - which holds the original exception. - - If any inferred result is uninferable, an empty list is returned. - """ - bad = [] - try: - for result in self._infer_augassign(context=context): - if result is util.Uninferable: - raise InferenceError - if isinstance(result, util.BadBinaryOperationMessage): - bad.append(result) - except InferenceError: - return [] - return bad - - 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]: - """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]: - 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 - - """ - - _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 - ) -> list[util.BadBinaryOperationMessage]: - """Get a list of type errors which can occur during inference. - - Each TypeError is represented by a :class:`BadBinaryOperationMessage`, - which holds the original exception. - - If any inferred result is uninferable, an empty list is returned. - """ - bad = [] - try: - for result in self._infer_binop(context=context): - if result is util.Uninferable: - raise InferenceError - if isinstance(result, util.BadBinaryOperationMessage): - bad.append(result) - except InferenceError: - return [] - return bad - - def get_children(self): - yield self.left - yield self.right - - def op_precedence(self) -> int: - 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]: - """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]: - 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 - - """ - - _astroid_fields = ("values",) - _other_fields = ("op",) - - def __init__( - self, - op: 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 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 = 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) -> int: - 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 - - """ - - -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 - - """ - - _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 - - >>> node.ops - [('<=', ), ('<=', )] - """ - - _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, ValueError, or TypeError 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, TypeError): - 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]: - """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()) - [, ] - >>> 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 - - >>> list(node.get_children()) - [, - , - , - , - ] - """ - - _other_fields = ("value", "kind") - - def __init__( - self, - value: Any, - lineno: int | None = None, - col_offset: int | None = None, - parent: NodeNG = SYNTHETIC_ROOT, - 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. - """ - if getattr(value, "__name__", None) == "__doc__": - warnings.warn( # pragma: no cover - "You have most likely called a __doc__ field of some object " - "and it didn't return a string. " - "That happens to some symbols from the standard library. " - "Check for isinstance(.__doc__, str).", - RuntimeWarning, - stacklevel=0, - ) - self.value = 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 or Uninferable - """ - # bool(NotImplemented) is deprecated; it raises TypeError starting from Python 3.14 - # and returns True for versions under 3.14 - if self.value is NotImplemented: - return util.Uninferable if PY314_PLUS else True - 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 - - """ - - -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 - - >>> list(node.get_children())[0] - - """ - - _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 - - >>> list(node.get_children())[0] - - """ - - _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 - - """ - - _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 - - """ - - _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 - - >>> node.parent - - """ - - _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: NodeNG = SYNTHETIC_ROOT, - *, - 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]: - 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 - - >>> node.handlers - [] - """ - - _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 - - """ - - _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 - - >>> node.body[0] - - """ - - -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 - - >>> node.body[0] - - >>> list(node.body[0].get_children())[0] - - """ - - _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 - - """ - - _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]: - """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 - - """ - - _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]: - 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 - - """ - - _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) -> bool: - 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 - - """ - - _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]: - """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: - test_bool_value = test.bool_value() - if not isinstance(test, util.UninferableBase) and not isinstance( - test_bool_value, 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 - - """ - - _other_fields = ("names",) - - def __init__( - self, - names: list[tuple[str, str | 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 - """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]: - """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 - - >>> node.keywords - [] - """ - - _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 - - """ - - _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 - - >>> node.body[0] - - """ - - _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] - - """ - - _astroid_fields = ("name", "default_value") - name: AssignName - default_value: 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, default_value: NodeNG | None) -> None: - self.name = name - self.default_value = default_value - - def _infer( - self, context: InferenceContext | None = None, **kwargs: Any - ) -> Iterator[ParamSpec]: - yield self - - assigned_stmts = protocols.generic_type_assigned_stmts - """Returns the assigned statement (non inferred) according to the assignment type. - See astroid/protocols.py for actual implementation. - """ - - -class Pass(_base_nodes.NoChildrenNode, _base_nodes.Statement): - """Class representing an :class:`ast.Pass` node. - - >>> import astroid - >>> node = astroid.extract_node('pass') - >>> node - - """ - - -class Raise(_base_nodes.Statement): - """Class representing an :class:`ast.Raise` node. - - >>> import astroid - >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")') - >>> node - - """ - - _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 - - """ - - _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) -> bool: - 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 - - """ - - 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 - - >>> node.slice - - """ - - _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 display_type(self) -> Literal["Slice"]: - """A human readable type of this node. - - :returns: The type of this node. - """ - return "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 - - """ - - _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_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 - - """ - - _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 - - """ - - _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 - - """ - - _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], - ] - ] = 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] - - """ - - _astroid_fields = ("name", "bound", "default_value") - name: AssignName - bound: NodeNG | None - default_value: 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, - default_value: NodeNG | None = None, - ) -> None: - self.name = name - self.bound = bound - self.default_value = default_value - - def _infer( - self, context: InferenceContext | None = None, **kwargs: Any - ) -> Iterator[TypeVar]: - yield self - - assigned_stmts = protocols.generic_type_assigned_stmts - """Returns the assigned statement (non inferred) according to the assignment type. - See astroid/protocols.py for actual implementation. - """ - - -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] - - """ - - _astroid_fields = ("name", "default_value") - name: AssignName - default_value: 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, default_value: NodeNG | None = None - ) -> None: - self.name = name - self.default_value = default_value - - def _infer( - self, context: InferenceContext | None = None, **kwargs: Any - ) -> Iterator[TypeVarTuple]: - yield self - - assigned_stmts = protocols.generic_type_assigned_stmts - """Returns the assigned statement (non inferred) according to the assignment type. - See astroid/protocols.py for actual implementation. - """ - - -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 - - """ - - _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 - ) -> list[util.BadUnaryOperationMessage]: - """Get a list of type errors which can occur during inference. - - Each TypeError is represented by a :class:`BadUnaryOperationMessage`, - which holds the original exception. - - If any inferred result is uninferable, an empty list is returned. - """ - bad = [] - try: - for result in self._infer_unaryop(context=context): - if result is util.Uninferable: - raise InferenceError - if isinstance(result, util.BadUnaryOperationMessage): - bad.append(result) - except InferenceError: - return [] - return bad - - def get_children(self): - yield self.operand - - def op_precedence(self) -> int: - 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 - - """ - - _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 - - """ - - _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 - - """ - - _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 - - >>> node.values - [, ] - """ - - _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 - - def _infer( - self, context: InferenceContext | None = None, **kwargs: Any - ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: - format_specs = Const("") if self.format_spec is None else self.format_spec - uninferable_already_generated = False - for format_spec in format_specs.infer(context, **kwargs): - if not isinstance(format_spec, Const): - if not uninferable_already_generated: - yield util.Uninferable - uninferable_already_generated = True - continue - for value in self.value.infer(context, **kwargs): - value_to_format = value - if isinstance(value, Const): - value_to_format = value.value - try: - formatted = format(value_to_format, format_spec.value) - yield Const( - formatted, - lineno=self.lineno, - col_offset=self.col_offset, - end_lineno=self.end_lineno, - end_col_offset=self.end_col_offset, - ) - continue - except (ValueError, TypeError): - # happens when format_spec.value is invalid - yield util.Uninferable - uninferable_already_generated = True - continue - - -UNINFERABLE_VALUE = "{Uninferable}" - - -class JoinedStr(NodeNG): - """Represents a list of string expressions to be joined. - - >>> import astroid - >>> node = astroid.extract_node('f"Format {type_}"') - >>> node - - """ - - _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 - - def _infer( - self, context: InferenceContext | None = None, **kwargs: Any - ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: - if self.values: - yield from self._infer_with_values(context) - else: - yield Const("") - - def _infer_with_values( - self, context: InferenceContext | None = None, **kwargs: Any - ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: - uninferable_already_generated = False - for inferred in self._infer_from_values(self.values, context): - failed = inferred is util.Uninferable or ( - isinstance(inferred, Const) and UNINFERABLE_VALUE in inferred.value - ) - if failed: - if not uninferable_already_generated: - uninferable_already_generated = True - yield util.Uninferable - continue - yield inferred - - @classmethod - def _infer_from_values( - cls, nodes: list[NodeNG], context: InferenceContext | None = None, **kwargs: Any - ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: - if not nodes: - return - if len(nodes) == 1: - for node in cls._safe_infer_from_node(nodes[0], context, **kwargs): - if isinstance(node, Const): - yield node - continue - yield Const(UNINFERABLE_VALUE) - return - for prefix in cls._safe_infer_from_node(nodes[0], context, **kwargs): - for suffix in cls._infer_from_values(nodes[1:], context, **kwargs): - result = "" - for node in (prefix, suffix): - if isinstance(node, Const): - result += str(node.value) - continue - result += UNINFERABLE_VALUE - yield Const(result) - - @classmethod - def _safe_infer_from_node( - cls, node: NodeNG, context: InferenceContext | None = None, **kwargs: Any - ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: - try: - yield from node._infer(context, **kwargs) - except InferenceError: - yield util.Uninferable - - -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 - - """ - - _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) -> 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 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, - parent: NodeNG, - lineno: None = None, - col_offset: 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 - - -UNATTACHED_UNKNOWN = Unknown(parent=SYNTHETIC_ROOT) - - -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]: - 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 - - """ - - _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] - - """ - - _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 - - """ - - _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 - - >>> node.cases[1].pattern - - >>> node.cases[2].pattern - - """ - - _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 - - >>> node.cases[1].pattern - - """ - - _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 - - """ - - _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 - - >>> node.cases[1].pattern - - """ - - _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] - - """ - - _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] - - >>> node.cases[1].pattern.patterns[0] - - >>> node.cases[2].pattern - - >>> node.cases[3].pattern - - """ - - _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 - - """ - - _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 TemplateStr(NodeNG): - """Class representing an :class:`ast.TemplateStr` node. - - >>> import astroid - >>> node = astroid.extract_node('t"{name} finished {place!s}"') - >>> node - - """ - - _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: - self.values: 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, *, values: list[NodeNG]) -> None: - self.values = values - - def get_children(self) -> Iterator[NodeNG]: - yield from self.values - - -class Interpolation(NodeNG): - """Class representing an :class:`ast.Interpolation` node. - - >>> import astroid - >>> node = astroid.extract_node('t"{name} finished {place!s}"') - >>> node - - >>> node.values[0] - - >>> node.values[2] - - """ - - _astroid_fields = ("value", "format_spec") - _other_fields = ("str", "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: - self.value: NodeNG - """Any expression node.""" - - self.str: str - """Text of the interpolation expression.""" - - self.conversion: int - """The type of formatting to be applied to the value. - - .. seealso:: - :class:`ast.Interpolation` - """ - - self.format_spec: JoinedStr | None = None - """The formatting to be applied to the value. - - .. seealso:: - :class:`ast.Interpolation` - """ - - 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, - str: str, # pylint: disable=redefined-builtin - conversion: int = -1, - format_spec: JoinedStr | None = None, - ) -> None: - self.value = value - self.str = str - self.conversion = conversion - self.format_spec = format_spec - - def get_children(self) -> Iterator[NodeNG]: - yield self.value - if self.format_spec: - yield self.format_spec - - -# 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: - # NOTE: avoid accessing any attributes of element in the loop. - 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(): - # NOTE: avoid accessing any attributes of both key and value in the loop. - 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.""" - # NOTE: avoid accessing any attributes of value until it is known that value - # is of a const type, to avoid possibly triggering code for a live object. - # Accesses include value.__class__ and isinstance(value, ...), but not type(value). - # See: https://github.com/pylint-dev/astroid/issues/2686 - value_type = type(value) - assert not issubclass(value_type, 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_type not in CONST_CLS: - node = EmptyNode() - node.object = value - return node - - instance: List | Set | Tuple | Dict - initializer_cls = CONST_CLS[value_type] - if issubclass(initializer_cls, (List, Set, Tuple)): - instance = initializer_cls( - lineno=None, - col_offset=None, - parent=SYNTHETIC_ROOT, - 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=SYNTHETIC_ROOT, - end_lineno=None, - end_col_offset=None, - ) - instance.postinit(_create_dict_items(value, instance)) - return instance - return Const(value) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py b/.venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py deleted file mode 100644 index 1af39c2..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py +++ /dev/null @@ -1,771 +0,0 @@ -# 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 -from collections.abc import Generator, Iterator -from functools import cached_property -from functools import singledispatch as _singledispatch -from typing import ( - TYPE_CHECKING, - Any, - ClassVar, - TypeVar, - cast, - overload, -) - -from astroid import nodes, 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.nodes import _base_nodes - - -# Types for 'NodeNG.nodes_of_class()' -_NodesT = TypeVar("_NodesT", bound="NodeNG") -_NodesT2 = TypeVar("_NodesT2", bound="NodeNG") -_NodesT3 = TypeVar("_NodesT3", bound="NodeNG") -SkipKlassT = 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]: - """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) -> _base_nodes.Statement: - """The first parent node, including self, marked as statement node. - - :raises StatementMissing: If self has no parent attribute. - """ - if self.is_statement: - return cast("_base_nodes.Statement", self) - if not self.parent: - raise StatementMissing(target=self) - return self.parent.statement() - - def frame(self) -> 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 self.parent is None: - raise ParentMissingError(target=self) - return self.parent.frame() - - 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): - assert isinstance(self, nodes.Module) - return self - - while parent.parent: - parent = parent.parent - assert isinstance(parent, nodes.Module) - return parent - - 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 - """ - - # pylint: disable = too-many-statements - - @_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" 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) -> int: - # 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py deleted file mode 100644 index 01f99fa..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# 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 ( - SYNTHETIC_ROOT, - 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__ = ( - "SYNTHETIC_ROOT", - "AsyncFunctionDef", - "ClassDef", - "ComprehensionScope", - "DictComp", - "FunctionDef", - "GeneratorExp", - "Lambda", - "ListComp", - "LocalsDictNodeNG", - "Module", - "SetComp", - "_is_metaclass", - "builtin_lookup", - "function_to_method", - "get_wrapping_class", -) diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py deleted file mode 100644 index 8874c06..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py +++ /dev/null @@ -1,202 +0,0 @@ -# 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.""" diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py deleted file mode 100644 index f232db3..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py +++ /dev/null @@ -1,2900 +0,0 @@ -# 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 -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.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 _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 import Arguments, Const, NodeNG - 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 - - -def _infer_last( - arg: SuccessfulInferenceResult, context: InferenceContext -) -> InferenceResult: - res = util.Uninferable - for b in arg.infer(context=context.clone()): - res = b - return res - - -class Module(LocalsDictNodeNG): - """Class representing an :class:`ast.Module` node. - - >>> import astroid - >>> node = astroid.extract_node('import astroid') - >>> node - - >>> node.parent - - """ - - _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)] - if name == "__name__": - main_const = node_classes.const_factory("__main__") - main_const.parent = AstroidManager().builtins_module - result.append(main_const) - 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) -> NoReturn: - """The first parent node, including self, marked as statement node. - - When called on a :class:`Module` this raises a StatementMissing. - """ - 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]: - yield self - - -class __SyntheticRoot(Module): - def __init__(self): - super().__init__("__astroid_synthetic", pure_python=False) - - -SYNTHETIC_ROOT = __SyntheticRoot() - - -class GeneratorExp(ComprehensionScope): - """Class representing an :class:`ast.GeneratorExp` node. - - >>> import astroid - >>> node = astroid.extract_node('(thing for thing in things if thing)') - >>> node - - """ - - _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 - - """ - - _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 - - """ - - _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 - - """ - - _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 - l.1 at 0x7f23b2e41518> - """ - - _astroid_fields: ClassVar[tuple[str, ...]] = ("args", "body") - _other_other_fields: ClassVar[tuple[str, ...]] = ("locals",) - name = "" - 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]: - 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 - - """ - - _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 = "" - - 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, - ) - - 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 and 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 - """ - if self.returns: - return self.returns.tolineno - 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 ' 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 and bases._is_property(self)): - yield self - return InferenceErrorInfo(node=self, context=context) - - if not self.parent: - raise ParentMissingError(target=self) - prop_func = objects.Property( - function=self, - name=self.name, - lineno=self.lineno, - parent=self.parent, - col_offset=self.col_offset, - ) - 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 - """ - for yield_ in self.nodes_of_class(node_classes.Yield): - if yield_.value is None: - yield node_classes.Const(None, parent=yield_, lineno=yield_.lineno) - 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 context is None: - context = InferenceContext() - 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, node_classes.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): - class_bases = [_infer_last(x, context) for x in caller.args[1:]] - new_class = ClassDef( - name="temporary_class", - lineno=0, - col_offset=0, - end_lineno=0, - end_col_offset=0, - parent=SYNTHETIC_ROOT, - ) - 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.type_params - - 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 - - >>> node.body[0] - - """ - - -def _is_metaclass( - klass: ClassDef, - seen: set[str] | None = 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: ClassDef, - ancestors: set[str] | None = None, - context: InferenceContext | None = None, -) -> Literal["class", "exception", "metaclass"]: - """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 klass._type != "metaclass": - # 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( - _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 - - """ - - # 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: Literal["class", "exception", "metaclass"] | None = 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" - - 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, - ) - 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__), - ("__annotations__", self.special_attributes.attr___annotations__), - ) - 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._metaclass = metaclass - self.position = position - self.doc_node = doc_node - self.type_params = type_params or [] - - @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"]: - """Get the name of the type that this node represents. - - :returns: The name of the type. - """ - return "builtins.type" - - 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=caller.parent, - ) - - # 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] - - 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) and not self.type_params) - 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]: - """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)] - return result - - if class_context: - values += self._metaclass_lookup_attribute(name, context) - - result: list[InferenceResult] = [] - for value in values: - if isinstance(value, node_classes.AssignName): - stmt = value.statement() - # Ignore AnnAssigns without value, which are not attributes in the purest sense. - if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None: - continue - result.append(value) - - if not result: - raise AttributeInferenceError(target=self, attribute=name, context=context) - - return result - - @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(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.parent.scope() - attributes = [first_attr] + [ - attr - for attr in attributes - if attr.parent and attr.parent.scope() == first_scope - ] - functions = [attr for attr in attributes if isinstance(attr, FunctionDef)] - setter = None - for function in functions: - dec_names = function.decoratornames(context=context) - for dec_name in dec_names: - if dec_name is util.Uninferable: - continue - if dec_name.split(".")[-1] == "setter": - setter = function - if setter: - break - if functions: - # Prefer only the last function, unless a property is involved. - last_function = functions[-1] - attributes = [ - a - for a in attributes - if a not in functions or a is last_function or bases._is_property(a) - ] - - 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: - if not context.callcontext and not setter: - context.callcontext = CallContext( - args=function.args.arguments, callee=function - ) - # 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: - 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" - ): - 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. - - This will return an instance of builtins.type. - - :returns: The metaclass. - :rtype: builtins.type - """ - return builtin_lookup("type")[1][0] - - 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) -> bool: - 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) - and isinstance(inferred.value, str) - ): - continue - if not inferred.value: - continue - yield inferred - except InferenceError: - continue - - return None - - def _slots(self): - - 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 - - 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: - baseobj = _infer_last(stmt, context) - except InferenceError: - 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 - - mro = base._compute_mro(context=context) - bases_mro.append(mro) - - 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.type_params - - 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]: - yield self diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py b/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py deleted file mode 100644 index 8892008..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py +++ /dev/null @@ -1,35 +0,0 @@ -# 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/nodes/utils.py b/.venv/lib/python3.10/site-packages/astroid/nodes/utils.py deleted file mode 100644 index 6dc4828..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/nodes/utils.py +++ /dev/null @@ -1,14 +0,0 @@ -# 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 diff --git a/.venv/lib/python3.10/site-packages/astroid/objects.py b/.venv/lib/python3.10/site-packages/astroid/objects.py deleted file mode 100644 index 73670ec..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/objects.py +++ /dev/null @@ -1,360 +0,0 @@ -# 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, 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, - ) - - 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.""" - - def __init__(self, call, name=None, lineno=None, col_offset=None, parent=None): - # TODO: Pass end_lineno, end_col_offset as well - super().__init__( - name, - lineno=lineno, - col_offset=col_offset, - end_col_offset=0, - end_lineno=0, - 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.""" - - def __init__(self, function, name=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, - ) - - 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]: - yield self diff --git a/.venv/lib/python3.10/site-packages/astroid/protocols.py b/.venv/lib/python3.10/site-packages/astroid/protocols.py deleted file mode 100644 index 50e5cfa..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/protocols.py +++ /dev/null @@ -1,958 +0,0 @@ -# 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.builder import extract_node -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]: - 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 not (value > 0 and self.elts): - node.elts = [] - return node - if len(self.elts) * 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 node_classes.UNATTACHED_UNKNOWN - else: - for inferred in elt.infer(context): - if not isinstance(inferred, util.UninferableBase): - yield inferred - else: - yield node_classes.UNATTACHED_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]: - """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]: - 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]: - # 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 - - def _generate_assigned(): - for assigned in node_classes.unpack_infer(self.type): - if isinstance(assigned, nodes.ClassDef): - assigned = objects.ExceptionInstance(assigned) - - yield assigned - - if isinstance(self.parent, node_classes.TryStar): - # except * handler has assigned ExceptionGroup with caught - # exceptions under exceptions attribute - # pylint: disable-next=stop-iteration-return - eg = next( - node_classes.unpack_infer( - extract_node( - """ -from builtins import ExceptionGroup -ExceptionGroup -""" - ) - ) - ) - assigned = objects.ExceptionInstance(eg) - assigned.instance_attrs["exceptions"] = [ - nodes.List.from_elements(_generate_assigned()) - ] - yield assigned - else: - yield from _generate_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, too-many-branches - - 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( - f"Could not make sense of this, the target must be a tuple, not {type(target)!r}", - 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]: - """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]: - """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]: - """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 - - -@decorators.yes_if_nothing_inferred -def generic_type_assigned_stmts( - self: nodes.TypeVar | nodes.TypeVarTuple | nodes.ParamSpec, - node: nodes.AssignName, - context: InferenceContext | None = None, - assign_path: None = None, -) -> Generator[nodes.NodeNG]: - """Hack. Return any Node so inference doesn't fail - when evaluating __class_getitem__. Revert if it's causing issues. - """ - yield nodes.Const(None) diff --git a/.venv/lib/python3.10/site-packages/astroid/raw_building.py b/.venv/lib/python3.10/site-packages/astroid/raw_building.py deleted file mode 100644 index d1bbbd5..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/raw_building.py +++ /dev/null @@ -1,735 +0,0 @@ -# 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 TYPE_CHECKING, Any - -from astroid import bases, nodes -from astroid.const import _EMPTY_OBJECT_MARKER, IS_PYPY -from astroid.nodes import node_classes - -if TYPE_CHECKING: - from astroid.manager import AstroidManager - -logger = logging.getLogger(__name__) - - -_FunctionTypes = ( - types.FunctionType - | types.MethodType - | types.BuiltinFunctionType - | types.WrapperDescriptorType - | types.MethodDescriptorType - | types.ClassMethodDescriptorType -) - -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, parent: nodes.NodeNG, 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__] - doc = python_cls.__doc__ if isinstance(python_cls.__doc__, str) else None - ast_klass = build_class(cls_name, parent, cls_bases, doc) - func.instance_attrs["__class__"] = [ast_klass] - - -def build_dummy(runtime_object) -> nodes.EmptyNode: - enode = nodes.EmptyNode() - enode.object = runtime_object - return enode - - -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 - """ - _attach_local_node(node, build_dummy(runtime_object), 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, - parent: nodes.NodeNG, - 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=parent, - ) - 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, - parent: nodes.NodeNG, - 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=parent, - 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 -) -> 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) - - -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 -) -> nodes.FunctionDef: - """create astroid for a living function object""" - ( - args, - posonlyargs, - defaults, - kwonlyargs, - kwonly_defaults, - ) = _get_args_info_from_callable(member) - - return build_function( - getattr(member, "__name__", ""), - node, - args, - posonlyargs, - defaults, - member.__doc__ if isinstance(member.__doc__, str) else None, - kwonlyargs=kwonlyargs, - kwonlydefaults=kwonly_defaults, - ) - - -def object_build_datadescriptor( - node: nodes.Module | nodes.ClassDef, member: type -) -> nodes.ClassDef: - """create astroid for a living data descriptor object""" - return _base_class_object_build(node, member, []) - - -def object_build_methoddescriptor( - node: nodes.Module | nodes.ClassDef, - member: _FunctionTypes, -) -> nodes.FunctionDef: - """create astroid for a living method descriptor object""" - # FIXME get arguments ? - name = getattr(member, "__name__", "") - func = build_function(name, node, doc=member.__doc__) - _add_dunder_class(func, node, member) - return func - - -def _base_class_object_build( - node: nodes.Module | nodes.ClassDef, - member: type, - basenames: list[str], -) -> nodes.ClassDef: - """create astroid for a living class object, with a given set of base names - (e.g. ancestors) - """ - name = getattr(member, "__name__", "") - doc = member.__doc__ if isinstance(member.__doc__, str) else None - klass = build_class(name, node, basenames, doc) - klass._newstyle = isinstance(member, type) - 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): - member_object = member() - if hasattr(member_object, "__dict__"): - instdict = member_object.__dict__ - else: - raise TypeError - 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, - member: _FunctionTypes, - module: types.ModuleType, -) -> nodes.FunctionDef | nodes.EmptyNode: - # 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: - return object_build_methoddescriptor(node, member) - if filename == getattr(module, "__file__", None): - return object_build_function(node, member) - return build_dummy(member) - - -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: - self._manager = manager_instance - 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 alias 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 alias == "__class_getitem__" - try: - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - member = getattr(obj, alias) - except AttributeError: - # damned ExtensionClass.Base, I know you're there ! - attach_dummy_node(node, alias) - continue - if inspect.ismethod(member) and not pypy__class_getitem__: - member = member.__func__ - if inspect.isfunction(member): - child = _build_from_function(node, member, self._module) - elif inspect.isbuiltin(member) or pypy__class_getitem__: - if self.imported_member(node, member, alias): - continue - child = object_build_methoddescriptor(node, member) - elif inspect.isclass(member): - if self.imported_member(node, member, alias): - continue - if member in self._done: - child = self._done[member] - assert isinstance(child, nodes.ClassDef) - else: - child = object_build_class(node, member) - # recursion - self.object_build(child, member) - elif inspect.ismethoddescriptor(member): - child: nodes.NodeNG = object_build_methoddescriptor(node, member) - elif inspect.isdatadescriptor(member): - child = object_build_datadescriptor(node, member) - elif isinstance(member, tuple(node_classes.CONST_CLS)): - if alias in node.special_attributes: - continue - child = nodes.const_factory(member) - elif inspect.isroutine(member): - # This should be called for Jython, where some builtin - # methods aren't caught by isbuiltin branch. - child = _build_from_function(node, member, self._module) - elif _safe_has_attribute(member, "__all__"): - child: nodes.NodeNG = build_module(alias) - # recursion - self.object_build(child, member) - else: - # create an empty node so that the name is actually defined - child: nodes.NodeNG = build_dummy(member) - if child not in node.locals.get(alias, ()): - node.add_local_node(child, alias) - 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 - # pylint: disable-next=import-outside-toplevel - from astroid.manager import AstroidManager - - builder = InspectBuilder(AstroidManager()) - 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", astroid_builtin) - elif cls is TYPE_NOTIMPLEMENTED: - proxy = build_class("NotImplementedType", astroid_builtin) - elif cls is TYPE_ELLIPSIS: - proxy = build_class("Ellipsis", 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=astroid_builtin, - ) - astroid_builtin.set_local(_GeneratorType.name, _GeneratorType) - 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=astroid_builtin, - ) - astroid_builtin.set_local(_AsyncGeneratorType.name, _AsyncGeneratorType) - 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=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=astroid_builtin, - ) - doc = _type.__doc__ if isinstance(_type.__doc__, str) else None - klass.postinit( - bases=[], - body=[], - decorators=None, - doc_node=nodes.Const(doc) if 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() diff --git a/.venv/lib/python3.10/site-packages/astroid/rebuilder.py b/.venv/lib/python3.10/site-packages/astroid/rebuilder.py deleted file mode 100644 index 97f3a39..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/rebuilder.py +++ /dev/null @@ -1,1996 +0,0 @@ -# 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, Collection, Generator -from io import StringIO -from tokenize import TokenInfo, generate_tokens -from typing import TYPE_CHECKING, Final, TypeVar, cast, overload - -from astroid import nodes -from astroid._ast import ParserModule, get_parser_module, parse_function_type_comment -from astroid.const import PY312_PLUS, PY313_PLUS, Context -from astroid.nodes.utils import Position -from astroid.typing import InferenceResult - -if TYPE_CHECKING: - from astroid.manager import AstroidManager - - T_Doc = TypeVar( - "T_Doc", - ast.Module, - ast.ClassDef, - 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 = nodes.Module | nodes.ClassDef | nodes.FunctionDef - - -REDIRECT: Final[dict[str, str]] = { - "arguments": "Arguments", - "comprehension": "Comprehension", - "ListCompFor": "Comprehension", - "GenExprFor": "Comprehension", - "excepthandler": "ExceptHandler", - "keyword": "Keyword", - "match_case": "MatchCase", -} - - -# 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[tuple[nodes.ImportFrom, Collection[str]]] = [] - self._delayed_assattr: list[nodes.AssignAttr] = [] - self._visit_meths: dict[ - type[ast.AST], Callable[[ast.AST, nodes.NodeNG], nodes.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 | 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:] - 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 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), - ) - return newnode - - if TYPE_CHECKING: # noqa: C901 - - @overload - def visit(self, node: ast.arg, parent: nodes.NodeNG) -> nodes.AssignName: ... - - @overload - def visit( - self, node: ast.arguments, parent: nodes.NodeNG - ) -> nodes.Arguments: ... - - @overload - def visit(self, node: ast.Assert, parent: nodes.NodeNG) -> nodes.Assert: ... - - @overload - def visit( - self, node: ast.AsyncFunctionDef, parent: nodes.NodeNG - ) -> nodes.AsyncFunctionDef: ... - - @overload - def visit(self, node: ast.AsyncFor, parent: nodes.NodeNG) -> nodes.AsyncFor: ... - - @overload - def visit(self, node: ast.Await, parent: nodes.NodeNG) -> nodes.Await: ... - - @overload - def visit( - self, node: ast.AsyncWith, parent: nodes.NodeNG - ) -> nodes.AsyncWith: ... - - @overload - def visit(self, node: ast.Assign, parent: nodes.NodeNG) -> nodes.Assign: ... - - @overload - def visit( - self, node: ast.AnnAssign, parent: nodes.NodeNG - ) -> nodes.AnnAssign: ... - - @overload - def visit( - self, node: ast.AugAssign, parent: nodes.NodeNG - ) -> nodes.AugAssign: ... - - @overload - def visit(self, node: ast.BinOp, parent: nodes.NodeNG) -> nodes.BinOp: ... - - @overload - def visit(self, node: ast.BoolOp, parent: nodes.NodeNG) -> nodes.BoolOp: ... - - @overload - def visit(self, node: ast.Break, parent: nodes.NodeNG) -> nodes.Break: ... - - @overload - def visit(self, node: ast.Call, parent: nodes.NodeNG) -> nodes.Call: ... - - @overload - def visit(self, node: ast.ClassDef, parent: nodes.NodeNG) -> nodes.ClassDef: ... - - @overload - def visit(self, node: ast.Continue, parent: nodes.NodeNG) -> nodes.Continue: ... - - @overload - def visit(self, node: ast.Compare, parent: nodes.NodeNG) -> nodes.Compare: ... - - @overload - def visit( - self, node: ast.comprehension, parent: nodes.NodeNG - ) -> nodes.Comprehension: ... - - @overload - def visit(self, node: ast.Delete, parent: nodes.NodeNG) -> nodes.Delete: ... - - @overload - def visit(self, node: ast.Dict, parent: nodes.NodeNG) -> nodes.Dict: ... - - @overload - def visit(self, node: ast.DictComp, parent: nodes.NodeNG) -> nodes.DictComp: ... - - @overload - def visit(self, node: ast.Expr, parent: nodes.NodeNG) -> nodes.Expr: ... - - @overload - def visit( - self, node: ast.ExceptHandler, parent: nodes.NodeNG - ) -> nodes.ExceptHandler: ... - - @overload - def visit(self, node: ast.For, parent: nodes.NodeNG) -> nodes.For: ... - - @overload - def visit( - self, node: ast.ImportFrom, parent: nodes.NodeNG - ) -> nodes.ImportFrom: ... - - @overload - def visit( - self, node: ast.FunctionDef, parent: nodes.NodeNG - ) -> nodes.FunctionDef: ... - - @overload - def visit( - self, node: ast.GeneratorExp, parent: nodes.NodeNG - ) -> nodes.GeneratorExp: ... - - @overload - def visit( - self, node: ast.Attribute, parent: nodes.NodeNG - ) -> nodes.Attribute: ... - - @overload - def visit(self, node: ast.Global, parent: nodes.NodeNG) -> nodes.Global: ... - - @overload - def visit(self, node: ast.If, parent: nodes.NodeNG) -> nodes.If: ... - - @overload - def visit(self, node: ast.IfExp, parent: nodes.NodeNG) -> nodes.IfExp: ... - - @overload - def visit(self, node: ast.Import, parent: nodes.NodeNG) -> nodes.Import: ... - - @overload - def visit( - self, node: ast.JoinedStr, parent: nodes.NodeNG - ) -> nodes.JoinedStr: ... - - @overload - def visit( - self, node: ast.FormattedValue, parent: nodes.NodeNG - ) -> nodes.FormattedValue: ... - - @overload - def visit( - self, node: ast.NamedExpr, parent: nodes.NodeNG - ) -> nodes.NamedExpr: ... - - @overload - def visit(self, node: ast.keyword, parent: nodes.NodeNG) -> nodes.Keyword: ... - - @overload - def visit(self, node: ast.Lambda, parent: nodes.NodeNG) -> nodes.Lambda: ... - - @overload - def visit(self, node: ast.List, parent: nodes.NodeNG) -> nodes.List: ... - - @overload - def visit(self, node: ast.ListComp, parent: nodes.NodeNG) -> nodes.ListComp: ... - - @overload - def visit( - self, node: ast.Name, parent: nodes.NodeNG - ) -> nodes.Name | nodes.Const | nodes.AssignName | nodes.DelName: ... - - @overload - def visit(self, node: ast.Nonlocal, parent: nodes.NodeNG) -> nodes.Nonlocal: ... - - @overload - def visit(self, node: ast.Constant, parent: nodes.NodeNG) -> nodes.Const: ... - - if sys.version_info >= (3, 12): - - @overload - def visit( - self, node: ast.ParamSpec, parent: nodes.NodeNG - ) -> nodes.ParamSpec: ... - - @overload - def visit(self, node: ast.Pass, parent: nodes.NodeNG) -> nodes.Pass: ... - - @overload - def visit(self, node: ast.Raise, parent: nodes.NodeNG) -> nodes.Raise: ... - - @overload - def visit(self, node: ast.Return, parent: nodes.NodeNG) -> nodes.Return: ... - - @overload - def visit(self, node: ast.Set, parent: nodes.NodeNG) -> nodes.Set: ... - - @overload - def visit(self, node: ast.SetComp, parent: nodes.NodeNG) -> nodes.SetComp: ... - - @overload - def visit(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice: ... - - @overload - def visit( - self, node: ast.Subscript, parent: nodes.NodeNG - ) -> nodes.Subscript: ... - - @overload - def visit(self, node: ast.Starred, parent: nodes.NodeNG) -> nodes.Starred: ... - - @overload - def visit(self, node: ast.Try, parent: nodes.NodeNG) -> nodes.Try: ... - - if sys.version_info >= (3, 11): - - @overload - def visit( - self, node: ast.TryStar, parent: nodes.NodeNG - ) -> nodes.TryStar: ... - - @overload - def visit(self, node: ast.Tuple, parent: nodes.NodeNG) -> nodes.Tuple: ... - - if sys.version_info >= (3, 12): - - @overload - def visit( - self, node: ast.TypeAlias, parent: nodes.NodeNG - ) -> nodes.TypeAlias: ... - - @overload - def visit( - self, node: ast.TypeVar, parent: nodes.NodeNG - ) -> nodes.TypeVar: ... - - @overload - def visit( - self, node: ast.TypeVarTuple, parent: nodes.NodeNG - ) -> nodes.TypeVarTuple: ... - - @overload - def visit(self, node: ast.UnaryOp, parent: nodes.NodeNG) -> nodes.UnaryOp: ... - - @overload - def visit(self, node: ast.While, parent: nodes.NodeNG) -> nodes.While: ... - - @overload - def visit(self, node: ast.With, parent: nodes.NodeNG) -> nodes.With: ... - - @overload - def visit(self, node: ast.Yield, parent: nodes.NodeNG) -> nodes.Yield: ... - - @overload - def visit( - self, node: ast.YieldFrom, parent: nodes.NodeNG - ) -> nodes.YieldFrom: ... - - @overload - def visit(self, node: ast.Match, parent: nodes.NodeNG) -> nodes.Match: ... - - @overload - def visit( - self, node: ast.match_case, parent: nodes.NodeNG - ) -> nodes.MatchCase: ... - - @overload - def visit( - self, node: ast.MatchValue, parent: nodes.NodeNG - ) -> nodes.MatchValue: ... - - @overload - def visit( - self, node: ast.MatchSingleton, parent: nodes.NodeNG - ) -> nodes.MatchSingleton: ... - - @overload - def visit( - self, node: ast.MatchSequence, parent: nodes.NodeNG - ) -> nodes.MatchSequence: ... - - @overload - def visit( - self, node: ast.MatchMapping, parent: nodes.NodeNG - ) -> nodes.MatchMapping: ... - - @overload - def visit( - self, node: ast.MatchClass, parent: nodes.NodeNG - ) -> nodes.MatchClass: ... - - @overload - def visit( - self, node: ast.MatchStar, parent: nodes.NodeNG - ) -> nodes.MatchStar: ... - - @overload - def visit(self, node: ast.MatchAs, parent: nodes.NodeNG) -> nodes.MatchAs: ... - - @overload - def visit(self, node: ast.MatchOr, parent: nodes.NodeNG) -> nodes.MatchOr: ... - - @overload - def visit(self, node: ast.pattern, parent: nodes.NodeNG) -> nodes.Pattern: ... - - if sys.version_info >= (3, 14): - - @overload - def visit( - self, node: ast.TemplateStr, parent: nodes.NodeNG - ) -> nodes.TemplateStr: ... - - @overload - def visit( - self, node: ast.Interpolation, parent: nodes.NodeNG - ) -> nodes.Interpolation: ... - - @overload - def visit(self, node: ast.AST, parent: nodes.NodeNG) -> nodes.NodeNG: ... - - @overload - def visit(self, node: None, parent: nodes.NodeNG) -> None: ... - - def visit(self, node: ast.AST | None, parent: nodes.NodeNG) -> nodes.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: nodes.NodeNG) -> nodes.AssignName: - """Visit an arg node by returning a fresh AssignName instance.""" - return self.visit_assignname(node, parent, node.arg) - - def visit_arguments( - self, node: ast.arguments, parent: nodes.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, - ( - nodes.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 - ), - ( - nodes.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: nodes.NodeNG | None = None - kwargannotation: nodes.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) - - 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: nodes.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: nodes.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 - ), - ) -> nodes.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: nodes.NodeNG - ) -> tuple[nodes.NodeNG | None, list[nodes.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: nodes.NodeNG | None = None - argtypes: list[nodes.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: nodes.NodeNG - ) -> nodes.AsyncFunctionDef: - return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent) - - def visit_asyncfor( - self, node: ast.AsyncFor, parent: nodes.NodeNG - ) -> nodes.AsyncFor: - return self._visit_for(nodes.AsyncFor, node, parent) - - def visit_await(self, node: ast.Await, parent: nodes.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: nodes.NodeNG - ) -> nodes.AsyncWith: - return self._visit_with(nodes.AsyncWith, node, parent) - - def visit_assign(self, node: ast.Assign, parent: nodes.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: nodes.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: nodes.NodeNG, node_name: str - ) -> nodes.AssignName: ... - - @overload - def visit_assignname( - self, node: ast.AST, parent: nodes.NodeNG, node_name: None - ) -> None: ... - - def visit_assignname( - self, node: ast.AST, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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 [] - ), - ) - parent.set_local(newnode.name, newnode) - return newnode - - def visit_continue( - self, node: ast.Continue, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.NodeNG, newnode: nodes.Dict - ) -> Generator[tuple[nodes.NodeNG, nodes.NodeNG]]: - for key, value in zip(node.keys, node.values): - rebuilt_key: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.NodeNG - ) -> nodes.For: ... - - @overload - def _visit_for( - self, cls: type[nodes.AsyncFor], node: ast.AsyncFor, parent: nodes.NodeNG - ) -> nodes.AsyncFor: ... - - def _visit_for( - self, cls: type[_ForT], node: ast.For | ast.AsyncFor, parent: nodes.NodeNG - ) -> _ForT: - """Visit a For node by returning a fresh instance of it.""" - newnode = cls( - 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( - 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: nodes.NodeNG) -> nodes.For: - return self._visit_for(nodes.For, node, parent) - - def visit_importfrom( - self, node: ast.ImportFrom, parent: nodes.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, self._global_names[-1].keys() if self._global_names else ()) - ) - return newnode - - @overload - def _visit_functiondef( - self, cls: type[nodes.FunctionDef], node: ast.FunctionDef, parent: nodes.NodeNG - ) -> nodes.FunctionDef: ... - - @overload - def _visit_functiondef( - self, - cls: type[nodes.AsyncFunctionDef], - node: ast.AsyncFunctionDef, - parent: nodes.NodeNG, - ) -> nodes.AsyncFunctionDef: ... - - def _visit_functiondef( - self, - cls: type[_FunctionT], - node: ast.FunctionDef | ast.AsyncFunctionDef, - parent: nodes.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: nodes.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() - parent.set_local(newnode.name, newnode) - return newnode - - def visit_functiondef( - self, node: ast.FunctionDef, parent: nodes.NodeNG - ) -> nodes.FunctionDef: - return self._visit_functiondef(nodes.FunctionDef, node, parent) - - def visit_generatorexp( - self, node: ast.GeneratorExp, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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).split(".")[0] - if self._global_names and name in self._global_names[-1]: - parent.root().set_local(name, newnode) - else: - parent.set_local(name, newnode) - return newnode - - def visit_joinedstr( - self, node: ast.JoinedStr, parent: nodes.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: nodes.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: nodes.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 - - def visit_keyword(self, node: ast.keyword, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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((nodes.AssignName | nodes.DelName), newnode) - self._save_assignment(newnode) - return newnode - - def visit_nonlocal( - self, node: ast.Nonlocal, parent: nodes.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: nodes.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: nodes.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), - default_value=( - self.visit(node.default_value, newnode) if PY313_PLUS else None - ), - ) - return newnode - - def visit_pass(self, node: ast.Pass, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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), - default_value=( - self.visit(node.default_value, newnode) if PY313_PLUS else None - ), - ) - return newnode - - def visit_typevartuple( - self, node: ast.TypeVarTuple, parent: nodes.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), - default_value=( - self.visit(node.default_value, newnode) if PY313_PLUS else None - ), - ) - return newnode - - def visit_unaryop(self, node: ast.UnaryOp, parent: nodes.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: nodes.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: nodes.NodeNG - ) -> nodes.With: ... - - @overload - def _visit_with( - self, cls: type[nodes.AsyncWith], node: ast.AsyncWith, parent: nodes.NodeNG - ) -> nodes.AsyncWith: ... - - def _visit_with( - self, - cls: type[_WithT], - node: ast.With | ast.AsyncWith, - parent: nodes.NodeNG, - ) -> _WithT: - newnode = cls( - lineno=node.lineno, - col_offset=node.col_offset, - end_lineno=node.end_lineno, - end_col_offset=node.end_col_offset, - parent=parent, - ) - - def visit_child( - child: ast.withitem, - ) -> tuple[nodes.NodeNG, nodes.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: nodes.NodeNG) -> nodes.NodeNG: - return self._visit_with(nodes.With, node, parent) - - def visit_yield(self, node: ast.Yield, parent: nodes.NodeNG) -> nodes.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: nodes.NodeNG - ) -> nodes.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 - - def visit_match(self, node: ast.Match, parent: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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: nodes.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 - - if sys.version_info >= (3, 14): - - def visit_templatestr( - self, node: ast.TemplateStr, parent: nodes.NodeNG - ) -> nodes.TemplateStr: - newnode = nodes.TemplateStr( - lineno=node.lineno, - col_offset=node.col_offset, - end_lineno=node.end_lineno, - end_col_offset=node.end_col_offset, - parent=parent, - ) - newnode.postinit( - values=[self.visit(value, newnode) for value in node.values] - ) - return newnode - - def visit_interpolation( - self, node: ast.Interpolation, parent: nodes.NodeNG - ) -> nodes.Interpolation: - newnode = nodes.Interpolation( - 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, parent), - str=node.str, - conversion=node.conversion, - format_spec=self.visit(node.format_spec, parent), - ) - return newnode diff --git a/.venv/lib/python3.10/site-packages/astroid/test_utils.py b/.venv/lib/python3.10/site-packages/astroid/test_utils.py deleted file mode 100644 index afddb1a..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/test_utils.py +++ /dev/null @@ -1,78 +0,0 @@ -# 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() - m.module_denylist = set() - return m diff --git a/.venv/lib/python3.10/site-packages/astroid/transforms.py b/.venv/lib/python3.10/site-packages/astroid/transforms.py deleted file mode 100644 index d44ec3d..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/transforms.py +++ /dev/null @@ -1,163 +0,0 @@ -# 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 collections import defaultdict -from collections.abc import Callable -from typing import TYPE_CHECKING, 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 = Callable[[_SuccessfulInferenceResultT], bool] | None - -# pylint: disable-next=consider-alternative-union-syntax -_Vistables = Union[ - "nodes.NodeNG", list["nodes.NodeNG"], tuple["nodes.NodeNG", ...], str, None -] -_VisitReturns = ( - 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) - if TYPE_CHECKING: - 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 not node: - return node - 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 isinstance(node, str): - return node - - try: - return self._visit(node) - except RecursionError: - # Returning the node untransformed is better than giving up. - warnings.warn( - f"Astroid was unable to transform {node}.\n" - "Some functionality will be missing unless the system recursion limit is lifted.\n" - "From pylint, try: --init-hook='import sys; sys.setrecursionlimit(2000)' or higher.", - UserWarning, - stacklevel=0, - ) - return 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) diff --git a/.venv/lib/python3.10/site-packages/astroid/typing.py b/.venv/lib/python3.10/site-packages/astroid/typing.py deleted file mode 100644 index 37ea434..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/typing.py +++ /dev/null @@ -1,98 +0,0 @@ -# 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, Generator -from typing import ( - TYPE_CHECKING, - Any, - 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 - - -# pylint: disable=consider-alternative-union-syntax -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], -] - - -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 diff --git a/.venv/lib/python3.10/site-packages/astroid/util.py b/.venv/lib/python3.10/site-packages/astroid/util.py deleted file mode 100644 index 510b81c..0000000 --- a/.venv/lib/python3.10/site-packages/astroid/util.py +++ /dev/null @@ -1,159 +0,0 @@ -# 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 diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE deleted file mode 100644 index 648ba98..0000000 --- a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/LICENSE +++ /dev/null @@ -1,35 +0,0 @@ -Copyright (c) 2004-2016 California Institute of Technology. -Copyright (c) 2016-2025 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. - diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA deleted file mode 100644 index 6c3e255..0000000 --- a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/METADATA +++ /dev/null @@ -1,281 +0,0 @@ -Metadata-Version: 2.1 -Name: dill -Version: 0.4.0 -Summary: serialize all of Python -Home-page: https://github.com/uqfoundation/dill -Download-URL: https://pypi.org/project/dill/#files -Author: Mike McKerns -Author-email: mmckerns@uqfoundation.org -Maintainer: Mike McKerns -Maintainer-email: mmckerns@uqfoundation.org -License: BSD-3-Clause -Project-URL: Documentation, http://dill.rtfd.io -Project-URL: Source Code, https://github.com/uqfoundation/dill -Project-URL: Bug Tracker, https://github.com/uqfoundation/dill/issues -Platform: Linux -Platform: Windows -Platform: Mac -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: Science/Research -Classifier: License :: OSI Approved :: BSD License -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Scientific/Engineering -Classifier: Topic :: Software Development -Requires-Python: >=3.8 -License-File: LICENSE -Provides-Extra: graph -Requires-Dist: objgraph >=1.7.2 ; extra == 'graph' -Provides-Extra: profile -Requires-Dist: gprof2dot >=2022.7.29 ; extra == 'profile' -Provides-Extra: readline - ------------------------------ -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] - -To optionally include the ``gprof2dot`` diagnostic tool in the install:: - - $ pip install dill[profile] - -For windows users, to optionally install session history tools:: - - $ pip install dill[readline] - - -Requirements -============ - -``dill`` requires: - - - ``python`` (or ``pypy``), **>=3.8** - - ``setuptools``, **>=42** - -Optional requirements: - - - ``objgraph``, **>=1.7.2** - - ``gprof2dot``, **>=2022.7.29** - - ``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: at 0x7fe074f8c280> - ├┬ F2: - │└ # F2 [34 B] - ├┬ Co: at 0x7fe07501eb30, file "", line 1> - │├┬ F2: - ││└ # F2 [19 B] - │└ # Co [87 B] - ├┬ D1: - │└ # D1 [22 B] - ├┬ D2: - │└ # D2 [2 B] - ├┬ D2: - │├┬ D2: - ││└ # 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. diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD deleted file mode 100644 index 6a03692..0000000 --- a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/RECORD +++ /dev/null @@ -1,101 +0,0 @@ -../../../bin/get_gprof,sha256=u3jtVyBFM-MOV_hjjswKy-BCbTL8FHzFEyPG-R3uJ3M,2501 -../../../bin/get_objgraph,sha256=r5IbXHDLImzWN2a4SQd3IHQMl_gwnwYvSZdUoeLDPhM,1695 -../../../bin/undill,sha256=N92W9cF0G6aYodwZtn70tgEdYpX0YUd2D0GGBxfOLRQ,631 -dill-0.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -dill-0.4.0.dist-info/LICENSE,sha256=mYpIzbuubSrNbyOVUajkpFDpZ-udj9jiQsNOnmTkDiY,1790 -dill-0.4.0.dist-info/METADATA,sha256=Zzr1eKtuTvrriL-H_4DmN7fxjB9tENzGRz0DwAI7u5Q,10174 -dill-0.4.0.dist-info/RECORD,, -dill-0.4.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92 -dill-0.4.0.dist-info/top_level.txt,sha256=HLSIyYIjQzJiBvs3_-16ntezE3j6mWGTW0DT1xDd7X0,5 -dill/__diff.py,sha256=yLCKcd0GkDxR86wdE0SG2qw5Yxhon6qHeOeI1li8Vrc,7146 -dill/__info__.py,sha256=sD2tcjIAuRmMAIqBtF4-JWK7VTCIoQx3axUNBQb-qPw,10756 -dill/__init__.py,sha256=u5X9cMJo3zAtpGN4MUATU71s5wNvnIbXsZXi29DyvXE,3798 -dill/__pycache__/__diff.cpython-310.pyc,, -dill/__pycache__/__info__.cpython-310.pyc,, -dill/__pycache__/__init__.cpython-310.pyc,, -dill/__pycache__/_dill.cpython-310.pyc,, -dill/__pycache__/_objects.cpython-310.pyc,, -dill/__pycache__/_shims.cpython-310.pyc,, -dill/__pycache__/detect.cpython-310.pyc,, -dill/__pycache__/logger.cpython-310.pyc,, -dill/__pycache__/objtypes.cpython-310.pyc,, -dill/__pycache__/pointers.cpython-310.pyc,, -dill/__pycache__/session.cpython-310.pyc,, -dill/__pycache__/settings.cpython-310.pyc,, -dill/__pycache__/source.cpython-310.pyc,, -dill/__pycache__/temp.cpython-310.pyc,, -dill/_dill.py,sha256=DX51DqW1DXL_5VdZ-zF5w9avxiVOsNh6cNwMkd39Fwo,91313 -dill/_objects.py,sha256=wPETr0_Gtj__cFAIEO6FRSC_vpBYexmlDiHl_d8geJU,19740 -dill/_shims.py,sha256=mGN22u7TeK-keDvhGvWZVvCm-GW0TY04MXW-Hncbh7c,6635 -dill/detect.py,sha256=L0eWBEVzqMV6nf8gd5R4G-cytzJEjBNTKAlsVVkJRlY,11207 -dill/logger.py,sha256=WNX59lxDHuZnSMKZ294i0uCDTmbneWPLu79Be7mOa-Q,11143 -dill/objtypes.py,sha256=NvSqfHX6AY2-QOVUv0hEda_GEcx_uiuGPQvILJu8TyY,736 -dill/pointers.py,sha256=Q8AYqhdcvdq9X4HZ46lihM2bnZqGZG2vVoGpRxTHRNE,4467 -dill/session.py,sha256=qc41kfzXT9MIx1KGSEIVbUsDKBHErhptpeQzwcGspkU,23541 -dill/settings.py,sha256=hmtpAbplWE-HHxRHLPEkOxpEqWoCMTMn_FRa-1seao4,630 -dill/source.py,sha256=aaK9d3J7yNQuVw15ESRXxVqE8DxYHf1TfSOBEQYcZmU,45507 -dill/temp.py,sha256=QhkBKO5eNsEwSHdhiw5nCzALchAsl1yWQ1apZlVXY7w,8027 -dill/tests/__init__.py,sha256=5z3npvfFh6Xw5xa8ML-AQk3PDyTc3MIngJlUVVf35vk,479 -dill/tests/__main__.py,sha256=i9A86Q2NGoEhO-080sgnv2-TXxTRoH-gN2RTaPvpGvA,899 -dill/tests/__pycache__/__init__.cpython-310.pyc,, -dill/tests/__pycache__/__main__.cpython-310.pyc,, -dill/tests/__pycache__/test_abc.cpython-310.pyc,, -dill/tests/__pycache__/test_check.cpython-310.pyc,, -dill/tests/__pycache__/test_classdef.cpython-310.pyc,, -dill/tests/__pycache__/test_dataclasses.cpython-310.pyc,, -dill/tests/__pycache__/test_detect.cpython-310.pyc,, -dill/tests/__pycache__/test_dictviews.cpython-310.pyc,, -dill/tests/__pycache__/test_diff.cpython-310.pyc,, -dill/tests/__pycache__/test_extendpickle.cpython-310.pyc,, -dill/tests/__pycache__/test_fglobals.cpython-310.pyc,, -dill/tests/__pycache__/test_file.cpython-310.pyc,, -dill/tests/__pycache__/test_functions.cpython-310.pyc,, -dill/tests/__pycache__/test_functors.cpython-310.pyc,, -dill/tests/__pycache__/test_logger.cpython-310.pyc,, -dill/tests/__pycache__/test_mixins.cpython-310.pyc,, -dill/tests/__pycache__/test_module.cpython-310.pyc,, -dill/tests/__pycache__/test_moduledict.cpython-310.pyc,, -dill/tests/__pycache__/test_nested.cpython-310.pyc,, -dill/tests/__pycache__/test_objects.cpython-310.pyc,, -dill/tests/__pycache__/test_properties.cpython-310.pyc,, -dill/tests/__pycache__/test_pycapsule.cpython-310.pyc,, -dill/tests/__pycache__/test_recursive.cpython-310.pyc,, -dill/tests/__pycache__/test_registered.cpython-310.pyc,, -dill/tests/__pycache__/test_restricted.cpython-310.pyc,, -dill/tests/__pycache__/test_selected.cpython-310.pyc,, -dill/tests/__pycache__/test_session.cpython-310.pyc,, -dill/tests/__pycache__/test_source.cpython-310.pyc,, -dill/tests/__pycache__/test_sources.cpython-310.pyc,, -dill/tests/__pycache__/test_temp.cpython-310.pyc,, -dill/tests/__pycache__/test_threads.cpython-310.pyc,, -dill/tests/__pycache__/test_weakref.cpython-310.pyc,, -dill/tests/test_abc.py,sha256=OT50obWxTiqk2eZLrbfHGPU9_Wr30wFTQ1Z8CfsdClU,4227 -dill/tests/test_check.py,sha256=OT6iu8R1BzZFQdiR3YgPoKKV7KJ2AYLJP62zTgsThdo,1396 -dill/tests/test_classdef.py,sha256=M3zzqKruuqIFTZDUqKYbjMtJmVkI59N0DxWZe019-qM,8600 -dill/tests/test_dataclasses.py,sha256=MPkDH5pfvYlYbwlGm-uFMN7svhPUImqKUYpROWO_Wak,890 -dill/tests/test_detect.py,sha256=wjgVFO8UMDLaM7A1cu1bQNXgYQCOpLGYqMYxID5CNsk,4144 -dill/tests/test_dictviews.py,sha256=qPAuqmp1yApPsARB6EKjmIWIAigK6P0L2Q68my02D-I,1337 -dill/tests/test_diff.py,sha256=TXCpiSPVTwEVR9-cs_sr_MYP9T7R2Pw2N2PAvnOvWFI,2667 -dill/tests/test_extendpickle.py,sha256=mcnPB0_YIqx6Ct678LUzWER1FRA1kVSRxE6PKhaN2oQ,1315 -dill/tests/test_fglobals.py,sha256=tbdBdLzW_7rx5mR-BzeThzEhH0KbtDZfAazLBYC_h94,1676 -dill/tests/test_file.py,sha256=XjW_EFSPsH7udEijchzef-OunBiJGJ-ieLRm8Vcai4Q,13578 -dill/tests/test_functions.py,sha256=AVBbHCDffw4Jca3Kijjae4rQfX0ZFTgurUjPeP2qqoM,4267 -dill/tests/test_functors.py,sha256=GSHR6UGBuiq0iUk4mk04Ly9ohSVdt9fBCrEYwByzSxk,930 -dill/tests/test_logger.py,sha256=PiZHr3bGIh6rW_Rx3uAQlVl5oFl-n2BQjXkpPzbmjng,2385 -dill/tests/test_mixins.py,sha256=GHO-GXeYvWzZeysJnsIDR8sTj_RADBs0-z23r_SfoNA,4007 -dill/tests/test_module.py,sha256=BOJ1mU4qaK680gEERfFXAIElTTuOg1nY4Ne26UhcpvU,1943 -dill/tests/test_moduledict.py,sha256=SlosX5tkmdfGLuhYweASu-WKIsrgphXrEm20veZmFJc,1182 -dill/tests/test_nested.py,sha256=dLgI3ZKWWAyZxXfTF131EHtph8yTS-e9Wqb8Mj3HA6A,3146 -dill/tests/test_objects.py,sha256=QyYSMiBZDyjhsGwDkdUeJH65rHODQMrLFajy-fUqzLI,1931 -dill/tests/test_properties.py,sha256=PinOM_C2Fzfj_3FlQm8OdjAmJ8WQ38nHWKkfHI4rCeA,1346 -dill/tests/test_pycapsule.py,sha256=XgT8VQZAAY3q7oV3N8726PnqU4GjuxSoT_FuFsoNsso,1417 -dill/tests/test_recursive.py,sha256=Bq2Q--Ro3Mb4CppYm5K5v0__5mGcFvSEjzmZOC81C64,4182 -dill/tests/test_registered.py,sha256=jgUBl_msBiqQTgvCC1bwKTGK-jmAz2FSE4pcJgEDRBw,1573 -dill/tests/test_restricted.py,sha256=mk93WLtNAEQr03h4N-NZO7Wr9xPWvzgMPOPEdi4Aku4,783 -dill/tests/test_selected.py,sha256=LjhZNntQ9KDJf9lhEYTHqzbFpsoeEFK2wobO1JT0FRg,3258 -dill/tests/test_session.py,sha256=-xmj46QxC60MaRIljv2eVrqIQYZrIcL54PsPaTLpoXc,10161 -dill/tests/test_source.py,sha256=EOTG_Fh0cWlICTRq04KH0Y5_G9BgGigkAOhr3CD1-dk,7059 -dill/tests/test_sources.py,sha256=sQXVRsv_u9i1kTyJYRtX_Ykb3TgaPrQ3sI2az8bNxQQ,8672 -dill/tests/test_temp.py,sha256=8NvuImVkYM1dw-j_nhaeDAb8Oan9whCOoehXktqurtw,2619 -dill/tests/test_threads.py,sha256=PD8RVdtu_kCkM7AVV8a81ywVWRi1g3GyDRocx04ZRFg,1257 -dill/tests/test_weakref.py,sha256=TfQHVMqgzeBywBiTjHj6ep8E6GF90y5SFzLgWbRbc2Q,1602 diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL deleted file mode 100644 index bab98d6..0000000 --- a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.43.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt b/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt deleted file mode 100644 index 85eea70..0000000 --- a/.venv/lib/python3.10/site-packages/dill-0.4.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -dill diff --git a/.venv/lib/python3.10/site-packages/dill/__diff.py b/.venv/lib/python3.10/site-packages/dill/__diff.py deleted file mode 100644 index 63e8182..0000000 --- a/.venv/lib/python3.10/site-packages/dill/__diff.py +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -""" -Module to show if an object has changed since it was memorised -""" - -import builtins -import os -import sys -import types -try: - import numpy.ma - HAS_NUMPY = True -except ImportError: - HAS_NUMPY = False - -# pypy doesn't use reference counting -getrefcount = getattr(sys, 'getrefcount', lambda x:0) - -# memo of objects indexed by id to a tuple (attributes, sequence items) -# attributes is a dict indexed by attribute name to attribute id -# sequence items is either a list of ids, of a dictionary of keys to ids -memo = {} -id_to_obj = {} -# types that cannot have changing attributes -builtins_types = set((str, list, dict, set, frozenset, int)) -dont_memo = set(id(i) for i in (memo, sys.modules, sys.path_importer_cache, - os.environ, id_to_obj)) - - -def get_attrs(obj): - """ - Gets all the attributes of an object though its __dict__ or return None - """ - if type(obj) in builtins_types \ - or type(obj) is type and obj in builtins_types: - return - return getattr(obj, '__dict__', None) - - -def get_seq(obj, cache={str: False, frozenset: False, list: True, set: True, - dict: True, tuple: True, type: False, - types.ModuleType: False, types.FunctionType: False, - types.BuiltinFunctionType: False}): - """ - Gets all the items in a sequence or return None - """ - try: - o_type = obj.__class__ - except AttributeError: - o_type = type(obj) - hsattr = hasattr - if o_type in cache: - if cache[o_type]: - if hsattr(obj, "copy"): - return obj.copy() - return obj - elif HAS_NUMPY and o_type in (numpy.ndarray, numpy.ma.core.MaskedConstant): - if obj.shape and obj.size: - return obj - else: - return [] - elif hsattr(obj, "__contains__") and hsattr(obj, "__iter__") \ - and hsattr(obj, "__len__") and hsattr(o_type, "__contains__") \ - and hsattr(o_type, "__iter__") and hsattr(o_type, "__len__"): - cache[o_type] = True - if hsattr(obj, "copy"): - return obj.copy() - return obj - else: - cache[o_type] = False - return None - - -def memorise(obj, force=False): - """ - Adds an object to the memo, and recursively adds all the objects - attributes, and if it is a container, its items. Use force=True to update - an object already in the memo. Updating is not recursively done. - """ - obj_id = id(obj) - if obj_id in memo and not force or obj_id in dont_memo: - return - id_ = id - g = get_attrs(obj) - if g is None: - attrs_id = None - else: - attrs_id = dict((key,id_(value)) for key, value in g.items()) - - s = get_seq(obj) - if s is None: - seq_id = None - elif hasattr(s, "items"): - seq_id = dict((id_(key),id_(value)) for key, value in s.items()) - elif not hasattr(s, "__len__"): #XXX: avoid TypeError from unexpected case - seq_id = None - else: - seq_id = [id_(i) for i in s] - - memo[obj_id] = attrs_id, seq_id - id_to_obj[obj_id] = obj - mem = memorise - if g is not None: - [mem(value) for key, value in g.items()] - - if s is not None: - if hasattr(s, "items"): - [(mem(key), mem(item)) - for key, item in s.items()] - else: - if hasattr(s, '__len__'): - [mem(item) for item in s] - else: mem(s) - - -def release_gone(): - itop, mp, src = id_to_obj.pop, memo.pop, getrefcount - [(itop(id_), mp(id_)) for id_, obj in list(id_to_obj.items()) - if src(obj) < 4] #XXX: correct for pypy? - - -def whats_changed(obj, seen=None, simple=False, first=True): - """ - Check an object against the memo. Returns a list in the form - (attribute changes, container changed). Attribute changes is a dict of - attribute name to attribute value. container changed is a boolean. - If simple is true, just returns a boolean. None for either item means - that it has not been checked yet - """ - # Special cases - if first: - # ignore the _ variable, which only appears in interactive sessions - if "_" in builtins.__dict__: - del builtins._ - if seen is None: - seen = {} - - obj_id = id(obj) - - if obj_id in seen: - if simple: - return any(seen[obj_id]) - return seen[obj_id] - - # Safety checks - if obj_id in dont_memo: - seen[obj_id] = [{}, False] - if simple: - return False - return seen[obj_id] - elif obj_id not in memo: - if simple: - return True - else: - raise RuntimeError("Object not memorised " + str(obj)) - - seen[obj_id] = ({}, False) - - chngd = whats_changed - id_ = id - - # compare attributes - attrs = get_attrs(obj) - if attrs is None: - changed = {} - else: - obj_attrs = memo[obj_id][0] - obj_get = obj_attrs.get - changed = dict((key,None) for key in obj_attrs if key not in attrs) - for key, o in attrs.items(): - if id_(o) != obj_get(key, None) or chngd(o, seen, True, False): - changed[key] = o - - # compare sequence - items = get_seq(obj) - seq_diff = False - if (items is not None) and (hasattr(items, '__len__')): - obj_seq = memo[obj_id][1] - if (len(items) != len(obj_seq)): - seq_diff = True - elif hasattr(obj, "items"): # dict type obj - obj_get = obj_seq.get - for key, item in items.items(): - if id_(item) != obj_get(id_(key)) \ - or chngd(key, seen, True, False) \ - or chngd(item, seen, True, False): - seq_diff = True - break - else: - for i, j in zip(items, obj_seq): # list type obj - if id_(i) != j or chngd(i, seen, True, False): - seq_diff = True - break - seen[obj_id] = changed, seq_diff - if simple: - return changed or seq_diff - return changed, seq_diff - - -def has_changed(*args, **kwds): - kwds['simple'] = True # ignore simple if passed in - return whats_changed(*args, **kwds) - -__import__ = __import__ - - -def _imp(*args, **kwds): - """ - Replaces the default __import__, to allow a module to be memorised - before the user can change it - """ - before = set(sys.modules.keys()) - mod = __import__(*args, **kwds) - after = set(sys.modules.keys()).difference(before) - for m in after: - memorise(sys.modules[m]) - return mod - -builtins.__import__ = _imp -if hasattr(builtins, "_"): - del builtins._ - -# memorise all already imported modules. This implies that this must be -# imported first for any changes to be recorded -for mod in list(sys.modules.values()): - memorise(mod) -release_gone() diff --git a/.venv/lib/python3.10/site-packages/dill/__info__.py b/.venv/lib/python3.10/site-packages/dill/__info__.py deleted file mode 100644 index e71bdfa..0000000 --- a/.venv/lib/python3.10/site-packages/dill/__info__.py +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2025 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] - -To optionally include the ``gprof2dot`` diagnostic tool in the install:: - - $ pip install dill[profile] - -For windows users, to optionally install session history tools:: - - $ pip install dill[readline] - - -Requirements -============ - -``dill`` requires: - - - ``python`` (or ``pypy``), **>=3.8** - - ``setuptools``, **>=42** - -Optional requirements: - - - ``objgraph``, **>=1.7.2** - - ``gprof2dot``, **>=2022.7.29** - - ``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: at 0x7fe074f8c280> - ├┬ F2: - │└ # F2 [34 B] - ├┬ Co: at 0x7fe07501eb30, file "", line 1> - │├┬ F2: - ││└ # F2 [19 B] - │└ # Co [87 B] - ├┬ D1: - │└ # D1 [22 B] - ├┬ D2: - │└ # D2 [2 B] - ├┬ D2: - │├┬ D2: - ││└ # 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.4.0' -__author__ = 'Mike McKerns' - -__license__ = ''' -Copyright (c) 2004-2016 California Institute of Technology. -Copyright (c) 2016-2025 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. - -''' diff --git a/.venv/lib/python3.10/site-packages/dill/__init__.py b/.venv/lib/python3.10/site-packages/dill/__init__.py deleted file mode 100644 index 7fc930c..0000000 --- a/.venv/lib/python3.10/site-packages/dill/__init__.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -# author, version, license, and long description -try: # the package is installed - from .__info__ import __version__, __author__, __doc__, __license__ -except: # pragma: no cover - import os - import sys - parent = os.path.dirname(os.path.abspath(os.path.dirname(__file__))) - sys.path.append(parent) - # get distribution meta info - from version import (__version__, __author__, - get_license_text, get_readme_as_rst) - __license__ = get_license_text(os.path.join(parent, 'LICENSE')) - __license__ = "\n%s" % __license__ - __doc__ = get_readme_as_rst(os.path.join(parent, 'README.md')) - del os, sys, parent, get_license_text, get_readme_as_rst - - -from ._dill import ( - 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, -) -from .session import ( - dump_module, load_module, load_module_asdict, - dump_session, load_session # backward compatibility -) -from . import detect, logger, session, source, temp - -# get global settings -from .settings import settings - -# make sure "trace" is turned off -logger.trace(False) - -objects = {} -# local import of dill._objects -#from . import _objects -#objects.update(_objects.succeeds) -#del _objects - -# local import of dill.objtypes -from . import objtypes as types - -def load_types(pickleable=True, unpickleable=True): - """load pickleable and/or unpickleable types to ``dill.types`` - - ``dill.types`` is meant to mimic the ``types`` module, providing a - registry of object types. By default, the module is empty (for import - speed purposes). Use the ``load_types`` function to load selected object - types to the ``dill.types`` module. - - Args: - pickleable (bool, default=True): if True, load pickleable types. - unpickleable (bool, default=True): if True, load unpickleable types. - - Returns: - None - """ - from importlib import reload - # local import of dill.objects - from . import _objects - if pickleable: - objects.update(_objects.succeeds) - else: - [objects.pop(obj,None) for obj in _objects.succeeds] - if unpickleable: - objects.update(_objects.failures) - else: - [objects.pop(obj,None) for obj in _objects.failures] - objects.update(_objects.registered) - del _objects - # reset contents of types to 'empty' - [types.__dict__.pop(obj) for obj in list(types.__dict__.keys()) \ - if obj.find('Type') != -1] - # add corresponding types from objects to types - reload(types) - -def extend(use_dill=True): - '''add (or remove) dill types to/from the pickle registry - - by default, ``dill`` populates its types to ``pickle.Pickler.dispatch``. - Thus, all ``dill`` types are available upon calling ``'import pickle'``. - To drop all ``dill`` types from the ``pickle`` dispatch, *use_dill=False*. - - Args: - use_dill (bool, default=True): if True, extend the dispatch table. - - Returns: - None - ''' - from ._dill import _revert_extension, _extend - if use_dill: _extend() - else: _revert_extension() - return - -extend() - - -def license(): - """print license""" - print (__license__) - return - -def citation(): - """print citation""" - print (__doc__[-491:-118]) - return - -# end of file diff --git a/.venv/lib/python3.10/site-packages/dill/_dill.py b/.venv/lib/python3.10/site-packages/dill/_dill.py deleted file mode 100644 index aec297c..0000000 --- a/.venv/lib/python3.10/site-packages/dill/_dill.py +++ /dev/null @@ -1,2255 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2015 California Institute of Technology. -# Copyright (c) 2016-2025 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 . -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 -try: - from _thread import _ExceptHookArgs as ExceptHookArgsType -except ImportError: - ExceptHookArgsType = None -try: - from _thread import _ThreadHandle as ThreadHandleType -except ImportError: - ThreadHandleType = None -#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 - -IS_PYODIDE = sys.platform == 'emscripten' - -FileType = get_file_type('rb', buffering=0) -TextWrapperType = get_file_type('r', buffering=-1) -BufferedRandomType = None if IS_PYODIDE else 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 = None if IS_PYODIDE else 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 and not IS_PYPY: - 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,11,'p'), 'co_qualname', 'argcount posonlyargcount kwonlyargcount nlocals stacksize flags code consts names varnames filename name qualname firstlineno linetable 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 - # PyPy 3.11 7.3.19+ (17 members) - elif m.case(( - 'argcount', 'posonlyargcount', 'kwonlyargcount', 'nlocals', 'stacksize', 'flags', # args[0:6] - 'code', 'consts', 'names', 'varnames', 'filename', 'name', 'qualname', # args[6:13] - 'firstlineno', 'linetable', 'freevars', 'cellvars' # args[13:] - )): - if CODE_VERSION == (3,11,'p'): - 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], - args[16], - ) - 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] - -if ThreadHandleType: - def _create_thread_handle(ident, done, *args): #XXX: ignores 'blocking' - from threading import _make_thread_handle - handle = _make_thread_handle(ident) - if done: - handle._set_done() - return handle - -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 = {'':sys.__stdin__, '':sys.__stdout__, - '':sys.__stderr__} #XXX: better fileno=(0,1,2) ? - if name in list(names.keys()): - f = names[name] #XXX: safer "f=sys.stdin" - elif name == '': - f = os.tmpfile() - elif name == '': - 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 = '' # 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 == '': # 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] - submodule = getattr(__import__(module, None, None, [obj]), obj) - if isinstance(submodule, (ModuleType, type)): - return submodule - return __import__(import_name, None, None, [obj]) - else: - return __import__(import_name) - 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 == '': - 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])) - if sys.hexversion < 0x30e00a1: - pickler._batch_setitems(iter(source.items())) - else: - pickler._batch_setitems(iter(source.items()), obj=obj) - 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. `_. -# 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_qualname"): # pypy 3.11 7.3.19+ (17 args) - args = ( - obj.co_lnotab, 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_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. `_. - # 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 - -if ThreadHandleType: - @register(ThreadHandleType) - def save_thread_handle(pickler, obj): - logger.trace(pickler, "Th: %s", obj) - pickler.save_reduce(_create_thread_handle, (obj.ident, obj.is_done()), obj=obj) - logger.trace(pickler, "# Th") - return - -@register(LockType) #XXX: copied Thread will have new Event (due to new Lock) -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(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 BufferedRandomType: - @register(BufferedRandomType) - 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(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 - - if PyBufferedRandomType: - @register(PyBufferedRandomType) - 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. `_. -# 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 and hasattr(value, '__qualname__'): - 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 caes: NoneType, NotImplementedType, EllipsisType, EnumMeta, etc - 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") - elif obj is ExceptHookArgsType: #NOTE: must be after NoneType for pypy - logger.trace(pickler, "T7: %s", obj) - pickler.write(GLOBAL + b'threading\nExceptHookArgs\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) - #from _socket import CAPI as _testcapsule - _testcapsule_name = b'dill._dill._testcapsule' - _testcapsule = _PyCapsule_New( - ctypes.cast(_PyCapsule_New, ctypes.c_void_p), - ctypes.c_char_p(_testcapsule_name), - 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 diff --git a/.venv/lib/python3.10/site-packages/dill/_objects.py b/.venv/lib/python3.10/site-packages/dill/_objects.py deleted file mode 100644 index a37cd79..0000000 --- a/.venv/lib/python3.10/site-packages/dill/_objects.py +++ /dev/null @@ -1,541 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -all Python Standard Library objects (currently: CH 1-15 @ 2.7) -and some other common objects (i.e. numpy.ndarray) -""" - -__all__ = ['registered','failures','succeeds'] - -# helper imports -import warnings; warnings.filterwarnings("ignore", category=DeprecationWarning) -import sys -import queue as Queue -#import dbm as anydbm #XXX: delete foo -from io import BytesIO as StringIO -import re -import array -import collections -import codecs -import struct -import dataclasses -import datetime -import calendar -import weakref -import pprint -import decimal -import numbers -import functools -import itertools -import operator -import tempfile -import shelve -import zlib -import gzip -import zipfile -import tarfile -import csv -import hashlib -import hmac -import os -import logging -import logging.handlers -import optparse -#import __hello__ -import threading -import socket -import contextlib -try: - import bz2 - import sqlite3 - import dbm.ndbm as dbm - HAS_ALL = True -except ImportError: # Ubuntu - HAS_ALL = False -try: - #import curses - #from curses import textpad, panel - HAS_CURSES = True -except ImportError: # Windows - HAS_CURSES = False -try: - import ctypes - HAS_CTYPES = True - # if using `pypy`, pythonapi is not found - IS_PYPY = not hasattr(ctypes, 'pythonapi') -except ImportError: # MacPorts - HAS_CTYPES = False - IS_PYPY = False - -IS_PYODIDE = sys.platform == 'emscripten' - -# helper objects -class _class: - def _method(self): - pass -# @classmethod -# def _clsmethod(cls): #XXX: test me -# pass -# @staticmethod -# def _static(self): #XXX: test me -# pass -class _class2: - def __call__(self): - pass -_instance2 = _class2() -class _newclass(object): - def _method(self): - pass -# @classmethod -# def _clsmethod(cls): #XXX: test me -# pass -# @staticmethod -# def _static(self): #XXX: test me -# pass -class _newclass2(object): - __slots__ = ['descriptor'] -def _function(x): yield x -def _function2(): - try: raise - except Exception: - from sys import exc_info - e, er, tb = exc_info() - return er, tb -if HAS_CTYPES: - class _Struct(ctypes.Structure): - pass - _Struct._fields_ = [("_field", ctypes.c_int),("next", ctypes.POINTER(_Struct))] -_filedescrip, _tempfile = tempfile.mkstemp('r') # deleted in cleanup -if sys.hexversion < 0x30d00a1: - _tmpf = tempfile.TemporaryFile('w') # emits OSError 9 in python 3.13 -else: - _tmpf = tempfile.NamedTemporaryFile('w').file # for > python 3.9 - -# objects used by dill for type declaration -registered = d = {} -# objects dill fails to pickle -failures = x = {} -# all other type objects -succeeds = a = {} - -# types module (part of CH 8) -a['BooleanType'] = bool(1) -a['BuiltinFunctionType'] = len -a['BuiltinMethodType'] = a['BuiltinFunctionType'] -a['BytesType'] = _bytes = codecs.latin_1_encode('\x00')[0] # bytes(1) -a['ClassType'] = _class -a['ComplexType'] = complex(1) -a['DictType'] = _dict = {} -a['DictionaryType'] = a['DictType'] -a['FloatType'] = float(1) -a['FunctionType'] = _function -a['InstanceType'] = _instance = _class() -a['IntType'] = _int = int(1) -a['ListType'] = _list = [] -a['NoneType'] = None -a['ObjectType'] = object() -a['StringType'] = _str = str(1) -a['TupleType'] = _tuple = () -a['TypeType'] = type -a['LongType'] = _int -a['UnicodeType'] = _str -# built-in constants (CH 4) -a['CopyrightType'] = copyright -# built-in types (CH 5) -a['ClassObjectType'] = _newclass # -a['ClassInstanceType'] = _newclass() # -a['SetType'] = _set = set() -a['FrozenSetType'] = frozenset() -# built-in exceptions (CH 6) -a['ExceptionType'] = _exception = _function2()[0] -# string services (CH 7) -a['SREPatternType'] = _srepattern = re.compile('') -# data types (CH 8) -a['ArrayType'] = array.array("f") -a['DequeType'] = collections.deque([0]) -a['DefaultDictType'] = collections.defaultdict(_function, _dict) -a['TZInfoType'] = datetime.tzinfo() -a['DateTimeType'] = datetime.datetime.today() -a['CalendarType'] = calendar.Calendar() -# numeric and mathematical types (CH 9) -a['DecimalType'] = decimal.Decimal(1) -# data compression and archiving (CH 12) -a['TarInfoType'] = tarfile.TarInfo() -# generic operating system services (CH 15) -a['LoggerType'] = _logger = logging.getLogger() -a['FormatterType'] = logging.Formatter() # pickle ok -a['FilterType'] = logging.Filter() # pickle ok -a['LogRecordType'] = logging.makeLogRecord(_dict) # pickle ok -a['OptionParserType'] = _oparser = optparse.OptionParser() # pickle ok -a['OptionGroupType'] = optparse.OptionGroup(_oparser,"foo") # pickle ok -a['OptionType'] = optparse.Option('--foo') # pickle ok -if HAS_CTYPES: - z = x if IS_PYPY else a - z['CCharType'] = _cchar = ctypes.c_char() - z['CWCharType'] = ctypes.c_wchar() # fail == 2.6 - z['CByteType'] = ctypes.c_byte() - z['CUByteType'] = ctypes.c_ubyte() - z['CShortType'] = ctypes.c_short() - z['CUShortType'] = ctypes.c_ushort() - z['CIntType'] = ctypes.c_int() - z['CUIntType'] = ctypes.c_uint() - z['CLongType'] = ctypes.c_long() - z['CULongType'] = ctypes.c_ulong() - z['CLongLongType'] = ctypes.c_longlong() - z['CULongLongType'] = ctypes.c_ulonglong() - z['CFloatType'] = ctypes.c_float() - z['CDoubleType'] = ctypes.c_double() - z['CSizeTType'] = ctypes.c_size_t() - del z - a['CLibraryLoaderType'] = ctypes.cdll - a['StructureType'] = _Struct - # if not IS_PYPY: - # a['BigEndianStructureType'] = ctypes.BigEndianStructure() -#NOTE: also LittleEndianStructureType and UnionType... abstract classes -#NOTE: remember for ctypesobj.contents creates a new python object -#NOTE: ctypes.c_int._objects is memberdescriptor for object's __dict__ -#NOTE: base class of all ctypes data types is non-public _CData - -import fractions -import io -from io import StringIO as TextIO -# built-in functions (CH 2) -a['ByteArrayType'] = bytearray([1]) -# numeric and mathematical types (CH 9) -a['FractionType'] = fractions.Fraction() -a['NumberType'] = numbers.Number() -# generic operating system services (CH 15) -a['IOBaseType'] = io.IOBase() -a['RawIOBaseType'] = io.RawIOBase() -a['TextIOBaseType'] = io.TextIOBase() -a['BufferedIOBaseType'] = io.BufferedIOBase() -a['UnicodeIOType'] = TextIO() # the new StringIO -a['LoggerAdapterType'] = logging.LoggerAdapter(_logger,_dict) # pickle ok -if HAS_CTYPES: - z = x if IS_PYPY else a - z['CBoolType'] = ctypes.c_bool(1) - z['CLongDoubleType'] = ctypes.c_longdouble() - del z -import argparse -# data types (CH 8) -a['OrderedDictType'] = collections.OrderedDict(_dict) -a['CounterType'] = collections.Counter(_dict) -if HAS_CTYPES: - z = x if IS_PYPY else a - z['CSSizeTType'] = ctypes.c_ssize_t() - del z -# generic operating system services (CH 15) -a['NullHandlerType'] = logging.NullHandler() # pickle ok # new 2.7 -a['ArgParseFileType'] = argparse.FileType() # pickle ok - -# -- pickle fails on all below here ----------------------------------------- -# types module (part of CH 8) -a['CodeType'] = compile('','','exec') -a['DictProxyType'] = type.__dict__ -a['DictProxyType2'] = _newclass.__dict__ -a['EllipsisType'] = Ellipsis -a['ClosedFileType'] = open(os.devnull, 'wb', buffering=0).close() -a['GetSetDescriptorType'] = array.array.typecode -a['LambdaType'] = _lambda = lambda x: lambda y: x #XXX: works when not imported! -a['MemberDescriptorType'] = _newclass2.descriptor -if not IS_PYPY: - a['MemberDescriptorType2'] = datetime.timedelta.days -a['MethodType'] = _method = _class()._method #XXX: works when not imported! -a['ModuleType'] = datetime -a['NotImplementedType'] = NotImplemented -a['SliceType'] = slice(1) -a['UnboundMethodType'] = _class._method #XXX: works when not imported! -d['TextWrapperType'] = open(os.devnull, 'r') # same as mode='w','w+','r+' -if not IS_PYODIDE: - d['BufferedRandomType'] = open(os.devnull, 'r+b') # same as mode='w+b' -d['BufferedReaderType'] = open(os.devnull, 'rb') # (default: buffering=-1) -d['BufferedWriterType'] = open(os.devnull, 'wb') -try: # oddities: deprecated - from _pyio import open as _open - d['PyTextWrapperType'] = _open(os.devnull, 'r', buffering=-1) - if not IS_PYODIDE: - d['PyBufferedRandomType'] = _open(os.devnull, 'r+b', buffering=-1) - d['PyBufferedReaderType'] = _open(os.devnull, 'rb', buffering=-1) - d['PyBufferedWriterType'] = _open(os.devnull, 'wb', buffering=-1) -except ImportError: - pass -# other (concrete) object types -z = d if sys.hexversion < 0x30800a2 else a -z['CellType'] = (_lambda)(0).__closure__[0] -del z -a['XRangeType'] = _xrange = range(1) -a['MethodDescriptorType'] = type.__dict__['mro'] -a['WrapperDescriptorType'] = type.__repr__ -#a['WrapperDescriptorType2'] = type.__dict__['__module__']#XXX: GetSetDescriptor -a['ClassMethodDescriptorType'] = type.__dict__['__prepare__'] -# built-in functions (CH 2) -_methodwrap = (1).__lt__ -a['MethodWrapperType'] = _methodwrap -a['StaticMethodType'] = staticmethod(_method) -a['ClassMethodType'] = classmethod(_method) -a['PropertyType'] = property() -d['SuperType'] = super(Exception, _exception) -# string services (CH 7) -_in = _bytes -a['InputType'] = _cstrI = StringIO(_in) -a['OutputType'] = _cstrO = StringIO() -# data types (CH 8) -a['WeakKeyDictionaryType'] = weakref.WeakKeyDictionary() -a['WeakValueDictionaryType'] = weakref.WeakValueDictionary() -a['ReferenceType'] = weakref.ref(_instance) -a['DeadReferenceType'] = weakref.ref(_class()) -a['ProxyType'] = weakref.proxy(_instance) -a['DeadProxyType'] = weakref.proxy(_class()) -a['CallableProxyType'] = weakref.proxy(_instance2) -a['DeadCallableProxyType'] = weakref.proxy(_class2()) -a['QueueType'] = Queue.Queue() -# numeric and mathematical types (CH 9) -d['PartialType'] = functools.partial(int,base=2) -a['IzipType'] = zip('0','1') -d['ItemGetterType'] = operator.itemgetter(0) -d['AttrGetterType'] = operator.attrgetter('__repr__') -# file and directory access (CH 10) -_fileW = _cstrO -# data persistence (CH 11) -if HAS_ALL: - x['ConnectionType'] = _conn = sqlite3.connect(':memory:') - x['CursorType'] = _conn.cursor() -a['ShelveType'] = shelve.Shelf({}) -# data compression and archiving (CH 12) -if HAS_ALL: - x['BZ2FileType'] = bz2.BZ2File(os.devnull) - x['BZ2CompressorType'] = bz2.BZ2Compressor() - x['BZ2DecompressorType'] = bz2.BZ2Decompressor() -#x['ZipFileType'] = _zip = zipfile.ZipFile(os.devnull,'w') -#_zip.write(_tempfile,'x') [causes annoying warning/error printed on import] -#a['ZipInfoType'] = _zip.getinfo('x') -a['TarFileType'] = tarfile.open(fileobj=_fileW,mode='w') -# file formats (CH 13) -x['DialectType'] = csv.get_dialect('excel') -if sys.hexversion < 0x30d00a1: - import xdrlib - a['PackerType'] = xdrlib.Packer() -# optional operating system services (CH 16) -a['LockType'] = threading.Lock() -a['RLockType'] = threading.RLock() -# generic operating system services (CH 15) # also closed/open and r/w/etc... -a['NamedLoggerType'] = _logger = logging.getLogger(__name__) -#a['FrozenModuleType'] = __hello__ #FIXME: prints "Hello world..." -# interprocess communication (CH 17) -x['SocketType'] = _socket = socket.socket() -x['SocketPairType'] = socket.socketpair()[0] -# python runtime services (CH 27) -a['GeneratorContextManagerType'] = contextlib.contextmanager(max)([1]) - -try: # ipython - __IPYTHON__ is True # is ipython -except NameError: - # built-in constants (CH 4) - a['QuitterType'] = quit - d['ExitType'] = a['QuitterType'] -try: # numpy #FIXME: slow... 0.05 to 0.1 sec to import numpy - from numpy import ufunc as _numpy_ufunc - from numpy import array as _numpy_array - from numpy import int32 as _numpy_int32 - a['NumpyUfuncType'] = _numpy_ufunc - a['NumpyArrayType'] = _numpy_array - a['NumpyInt32Type'] = _numpy_int32 -except ImportError: - pass -# generic operating system services (CH 15) -a['FileHandlerType'] = logging.FileHandler(os.devnull) -a['RotatingFileHandlerType'] = logging.handlers.RotatingFileHandler(os.devnull) -a['SocketHandlerType'] = logging.handlers.SocketHandler('localhost',514) -a['MemoryHandlerType'] = logging.handlers.MemoryHandler(1) -# data types (CH 8) -a['WeakSetType'] = weakref.WeakSet() # 2.7 -# generic operating system services (CH 15) [errors when dill is imported] -#a['ArgumentParserType'] = _parser = argparse.ArgumentParser('PROG') -#a['NamespaceType'] = _parser.parse_args() # pickle ok -#a['SubParsersActionType'] = _parser.add_subparsers() -#a['MutuallyExclusiveGroupType'] = _parser.add_mutually_exclusive_group() -#a['ArgumentGroupType'] = _parser.add_argument_group() - -# -- dill fails in some versions below here --------------------------------- -# types module (part of CH 8) -d['FileType'] = open(os.devnull, 'rb', buffering=0) # same 'wb','wb+','rb+' -# built-in functions (CH 2) -# Iterators: -a['ListIteratorType'] = iter(_list) # empty vs non-empty -a['SetIteratorType'] = iter(_set) #XXX: empty vs non-empty #FIXME: list_iterator -a['TupleIteratorType']= iter(_tuple) # empty vs non-empty -a['XRangeIteratorType'] = iter(_xrange) # empty vs non-empty -a["BytesIteratorType"] = iter(b'') -a["BytearrayIteratorType"] = iter(bytearray(b'')) -z = x if IS_PYPY else a -z["CallableIteratorType"] = iter(iter, None) -del z -x["MemoryIteratorType"] = iter(memoryview(b'')) -a["ListReverseiteratorType"] = reversed([]) -X = a['OrderedDictType'] -d["OdictKeysType"] = X.keys() -d["OdictValuesType"] = X.values() -d["OdictItemsType"] = X.items() -a["OdictIteratorType"] = iter(X.keys()) #FIXME: list_iterator -del X -#FIXME: list_iterator -a['DictionaryItemIteratorType'] = iter(type.__dict__.items()) -a['DictionaryKeyIteratorType'] = iter(type.__dict__.keys()) -a['DictionaryValueIteratorType'] = iter(type.__dict__.values()) -if sys.hexversion >= 0x30800a0: - a["DictReversekeyiteratorType"] = reversed({}.keys()) - a["DictReversevalueiteratorType"] = reversed({}.values()) - a["DictReverseitemiteratorType"] = reversed({}.items()) - -try: - import symtable - #FIXME: fails to pickle - x["SymtableEntryType"] = symtable.symtable("", "string", "exec")._table -except ImportError: - pass - -if sys.hexversion >= 0x30a00a0 and not IS_PYPY: - x['LineIteratorType'] = compile('3', '', 'eval').co_lines() - -if sys.hexversion >= 0x30b00b0 and not IS_PYPY: - from types import GenericAlias - d["GenericAliasIteratorType"] = iter(GenericAlias(list, (int,))) - x['PositionsIteratorType'] = compile('3', '', 'eval').co_positions() - -# data types (CH 8) -a['PrettyPrinterType'] = pprint.PrettyPrinter() -# file and directory access (CH 10) -a['TemporaryFileType'] = _tmpf -# data compression and archiving (CH 12) -x['GzipFileType'] = gzip.GzipFile(fileobj=_fileW) -# generic operating system services (CH 15) -a['StreamHandlerType'] = logging.StreamHandler() -# numeric and mathematical types (CH 9) -z = a if sys.hexversion < 0x30e00a1 else x -z['CountType'] = itertools.count(0) #FIXME: __reduce__ removed in 3.14.0a1 -z['ChainType'] = itertools.chain('0','1') -z['ProductType'] = itertools.product('0','1') -z['CycleType'] = itertools.cycle('0') -z['PermutationsType'] = itertools.permutations('0') -z['CombinationsType'] = itertools.combinations('0',1) -z['RepeatType'] = itertools.repeat(0) -z['CompressType'] = itertools.compress('0',[1]) -del z -#XXX: ...and etc - -# -- dill fails on all below here ------------------------------------------- -# types module (part of CH 8) -x['GeneratorType'] = _generator = _function(1) #XXX: priority -x['FrameType'] = _generator.gi_frame #XXX: inspect.currentframe() -x['TracebackType'] = _function2()[1] #(see: inspect.getouterframes,getframeinfo) -# other (concrete) object types -# (also: Capsule / CObject ?) -# built-in functions (CH 2) -# built-in types (CH 5) -# string services (CH 7) -x['StructType'] = struct.Struct('c') -x['CallableIteratorType'] = _srepattern.finditer('') -x['SREMatchType'] = _srepattern.match('') -x['SREScannerType'] = _srepattern.scanner('') -x['StreamReader'] = codecs.StreamReader(_cstrI) #XXX: ... and etc -# python object persistence (CH 11) -# x['DbShelveType'] = shelve.open('foo','n')#,protocol=2) #XXX: delete foo -if HAS_ALL: - z = a if IS_PYPY else x - z['DbmType'] = dbm.open(_tempfile,'n') - del z -# x['DbCursorType'] = _dbcursor = anydbm.open('foo','n') #XXX: delete foo -# x['DbType'] = _dbcursor.db -# data compression and archiving (CH 12) -x['ZlibCompressType'] = zlib.compressobj() -x['ZlibDecompressType'] = zlib.decompressobj() -# file formats (CH 13) -x['CSVReaderType'] = csv.reader(_cstrI) -x['CSVWriterType'] = csv.writer(_cstrO) -x['CSVDictReaderType'] = csv.DictReader(_cstrI) -x['CSVDictWriterType'] = csv.DictWriter(_cstrO,{}) -# cryptographic services (CH 14) -x['HashType'] = hashlib.md5() -if (sys.hexversion < 0x30800a1): - x['HMACType'] = hmac.new(_in) -else: - x['HMACType'] = hmac.new(_in, digestmod='md5') -# generic operating system services (CH 15) -if HAS_CURSES: pass - #x['CursesWindowType'] = _curwin = curses.initscr() #FIXME: messes up tty - #x['CursesTextPadType'] = textpad.Textbox(_curwin) - #x['CursesPanelType'] = panel.new_panel(_curwin) -if HAS_CTYPES: - x['CCharPType'] = ctypes.c_char_p() - x['CWCharPType'] = ctypes.c_wchar_p() - x['CVoidPType'] = ctypes.c_void_p() - if sys.platform[:3] == 'win': - x['CDLLType'] = _cdll = ctypes.cdll.msvcrt - else: - x['CDLLType'] = _cdll = ctypes.CDLL(None) - if not IS_PYPY: - x['PyDLLType'] = _pydll = ctypes.pythonapi - x['FuncPtrType'] = _cdll._FuncPtr() - x['CCharArrayType'] = ctypes.create_string_buffer(1) - x['CWCharArrayType'] = ctypes.create_unicode_buffer(1) - x['CParamType'] = ctypes.byref(_cchar) - x['LPCCharType'] = ctypes.pointer(_cchar) - x['LPCCharObjType'] = _lpchar = ctypes.POINTER(ctypes.c_char) - x['NullPtrType'] = _lpchar() - x['NullPyObjectType'] = ctypes.py_object() - x['PyObjectType'] = ctypes.py_object(lambda :None) - z = a if IS_PYPY else x - z['FieldType'] = _field = _Struct._field - z['CFUNCTYPEType'] = _cfunc = ctypes.CFUNCTYPE(ctypes.c_char) - if sys.hexversion < 0x30c00b3: - x['CFunctionType'] = _cfunc(str) - del z -# numeric and mathematical types (CH 9) -a['MethodCallerType'] = operator.methodcaller('mro') # 2.6 -# built-in types (CH 5) -x['MemoryType'] = memoryview(_in) # 2.7 -x['MemoryType2'] = memoryview(bytearray(_in)) # 2.7 -d['DictItemsType'] = _dict.items() # 2.7 -d['DictKeysType'] = _dict.keys() # 2.7 -d['DictValuesType'] = _dict.values() # 2.7 -# generic operating system services (CH 15) -a['RawTextHelpFormatterType'] = argparse.RawTextHelpFormatter('PROG') -a['RawDescriptionHelpFormatterType'] = argparse.RawDescriptionHelpFormatter('PROG') -a['ArgDefaultsHelpFormatterType'] = argparse.ArgumentDefaultsHelpFormatter('PROG') -z = a if IS_PYPY else x -z['CmpKeyType'] = _cmpkey = functools.cmp_to_key(_methodwrap) # 2.7, >=3.2 -z['CmpKeyObjType'] = _cmpkey('0') #2.7, >=3.2 -del z -# oddities: removed, etc -x['BufferType'] = x['MemoryType'] - -from dill._dill import _testcapsule -if _testcapsule is not None: - d['PyCapsuleType'] = _testcapsule -del _testcapsule - -if hasattr(dataclasses, '_HAS_DEFAULT_FACTORY'): - a['DataclassesHasDefaultFactoryType'] = dataclasses._HAS_DEFAULT_FACTORY - -if hasattr(dataclasses, 'MISSING'): - a['DataclassesMissingType'] = dataclasses.MISSING - -if hasattr(dataclasses, 'KW_ONLY'): - a['DataclassesKWOnlyType'] = dataclasses.KW_ONLY - -if hasattr(dataclasses, '_FIELD_BASE'): - a['DataclassesFieldBaseType'] = dataclasses._FIELD - -# -- cleanup ---------------------------------------------------------------- -a.update(d) # registered also succeed -if sys.platform[:3] == 'win': - os.close(_filedescrip) # required on win32 -os.remove(_tempfile) - - -# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/_shims.py b/.venv/lib/python3.10/site-packages/dill/_shims.py deleted file mode 100644 index bb0a9dc..0000000 --- a/.venv/lib/python3.10/site-packages/dill/_shims.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) -# Copyright (c) 2021-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -Provides shims for compatibility between versions of dill and Python. - -Compatibility shims should be provided in this file. Here are two simple example -use cases. - -Deprecation of constructor function: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Assume that we were transitioning _import_module in _dill.py to -the builtin function importlib.import_module when present. - -@move_to(_dill) -def _import_module(import_name): - ... # code already in _dill.py - -_import_module = Getattr(importlib, 'import_module', Getattr(_dill, '_import_module', None)) - -The code will attempt to find import_module in the importlib module. If not -present, it will use the _import_module function in _dill. - -Emulate new Python behavior in older Python versions: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -CellType.cell_contents behaves differently in Python 3.6 and 3.7. It is -read-only in Python 3.6 and writable and deletable in 3.7. - -if _dill.OLD37 and _dill.HAS_CTYPES and ...: - @move_to(_dill) - def _setattr(object, name, value): - if type(object) is _dill.CellType and name == 'cell_contents': - _PyCell_Set.argtypes = (ctypes.py_object, ctypes.py_object) - _PyCell_Set(object, value) - else: - setattr(object, name, value) -... # more cases below - -_setattr = Getattr(_dill, '_setattr', setattr) - -_dill._setattr will be used when present to emulate Python 3.7 functionality in -older versions of Python while defaulting to the standard setattr in 3.7+. - -See this PR for the discussion that lead to this system: -https://github.com/uqfoundation/dill/pull/443 -""" - -import inspect -import sys - -_dill = sys.modules['dill._dill'] - - -class Reduce(object): - """ - Reduce objects are wrappers used for compatibility enforcement during - unpickle-time. They should only be used in calls to pickler.save and - other Reduce objects. They are only evaluated within unpickler.load. - - Pickling a Reduce object makes the two implementations equivalent: - - pickler.save(Reduce(*reduction)) - - pickler.save_reduce(*reduction, obj=reduction) - """ - __slots__ = ['reduction'] - def __new__(cls, *reduction, **kwargs): - """ - Args: - *reduction: a tuple that matches the format given here: - https://docs.python.org/3/library/pickle.html#object.__reduce__ - is_callable: a bool to indicate that the object created by - unpickling `reduction` is callable. If true, the current Reduce - is allowed to be used as the function in further save_reduce calls - or Reduce objects. - """ - is_callable = kwargs.get('is_callable', False) # Pleases Py2. Can be removed later - if is_callable: - self = object.__new__(_CallableReduce) - else: - self = object.__new__(Reduce) - self.reduction = reduction - return self - def __repr__(self): - return 'Reduce%s' % (self.reduction,) - def __copy__(self): - return self # pragma: no cover - def __deepcopy__(self, memo): - return self # pragma: no cover - def __reduce__(self): - return self.reduction - def __reduce_ex__(self, protocol): - return self.__reduce__() - -class _CallableReduce(Reduce): - # A version of Reduce for functions. Used to trick pickler.save_reduce into - # thinking that Reduce objects of functions are themselves meaningful functions. - def __call__(self, *args, **kwargs): - reduction = self.__reduce__() - func = reduction[0] - f_args = reduction[1] - obj = func(*f_args) - return obj(*args, **kwargs) - -__NO_DEFAULT = _dill.Sentinel('Getattr.NO_DEFAULT') - -def Getattr(object, name, default=__NO_DEFAULT): - """ - A Reduce object that represents the getattr operation. When unpickled, the - Getattr will access an attribute 'name' of 'object' and return the value - stored there. If the attribute doesn't exist, the default value will be - returned if present. - - The following statements are equivalent: - - Getattr(collections, 'OrderedDict') - Getattr(collections, 'spam', None) - Getattr(*args) - - Reduce(getattr, (collections, 'OrderedDict')) - Reduce(getattr, (collections, 'spam', None)) - Reduce(getattr, args) - - During unpickling, the first two will result in collections.OrderedDict and - None respectively because the first attribute exists and the second one does - not, forcing it to use the default value given in the third argument. - """ - - if default is Getattr.NO_DEFAULT: - reduction = (getattr, (object, name)) - else: - reduction = (getattr, (object, name, default)) - - return Reduce(*reduction, is_callable=callable(default)) - -Getattr.NO_DEFAULT = __NO_DEFAULT -del __NO_DEFAULT - -def move_to(module, name=None): - def decorator(func): - if name is None: - fname = func.__name__ - else: - fname = name - module.__dict__[fname] = func - func.__module__ = module.__name__ - return func - return decorator - -def register_shim(name, default): - """ - A easier to understand and more compact way of "softly" defining a function. - These two pieces of code are equivalent: - - if _dill.OLD3X: - def _create_class(): - ... - _create_class = register_shim('_create_class', types.new_class) - - if _dill.OLD3X: - @move_to(_dill) - def _create_class(): - ... - _create_class = Getattr(_dill, '_create_class', types.new_class) - - Intuitively, it creates a function or object in the versions of dill/python - that require special reimplementations, and use a core library or default - implementation if that function or object does not exist. - """ - func = globals().get(name) - if func is not None: - _dill.__dict__[name] = func - func.__module__ = _dill.__name__ - - if default is Getattr.NO_DEFAULT: - reduction = (getattr, (_dill, name)) - else: - reduction = (getattr, (_dill, name, default)) - - return Reduce(*reduction, is_callable=callable(default)) - -###################### -## Compatibility Shims are defined below -###################### - -_CELL_EMPTY = register_shim('_CELL_EMPTY', None) - -_setattr = register_shim('_setattr', setattr) -_delattr = register_shim('_delattr', delattr) diff --git a/.venv/lib/python3.10/site-packages/dill/detect.py b/.venv/lib/python3.10/site-packages/dill/detect.py deleted file mode 100644 index 2f0bea1..0000000 --- a/.venv/lib/python3.10/site-packages/dill/detect.py +++ /dev/null @@ -1,287 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -Methods for detecting objects leading to pickling failures. -""" - -import dis -from inspect import ismethod, isfunction, istraceback, isframe, iscode - -from .pointers import parent, reference, at, parents, children -from .logger import trace - -__all__ = ['baditems','badobjects','badtypes','code','errors','freevars', - 'getmodule','globalvars','nestedcode','nestedglobals','outermost', - 'referredglobals','referrednested','trace','varnames'] - -def getmodule(object, _filename=None, force=False): - """get the module of the object""" - from inspect import getmodule as getmod - module = getmod(object, _filename) - if module or not force: return module - import builtins - from .source import getname - name = getname(object, force=True) - return builtins if name in vars(builtins).keys() else None - -def outermost(func): # is analogous to getsource(func,enclosing=True) - """get outermost enclosing object (i.e. the outer function in a closure) - - NOTE: this is the object-equivalent of getsource(func, enclosing=True) - """ - if ismethod(func): - _globals = func.__func__.__globals__ or {} - elif isfunction(func): - _globals = func.__globals__ or {} - else: - return #XXX: or raise? no matches - _globals = _globals.items() - # get the enclosing source - from .source import getsourcelines - try: lines,lnum = getsourcelines(func, enclosing=True) - except Exception: #TypeError, IOError - lines,lnum = [],None - code = ''.join(lines) - # get all possible names,objects that are named in the enclosing source - _locals = ((name,obj) for (name,obj) in _globals if name in code) - # now only save the objects that generate the enclosing block - for name,obj in _locals: #XXX: don't really need 'name' - try: - if getsourcelines(obj) == (lines,lnum): return obj - except Exception: #TypeError, IOError - pass - return #XXX: or raise? no matches - -def nestedcode(func, recurse=True): #XXX: or return dict of {co_name: co} ? - """get the code objects for any nested functions (e.g. in a closure)""" - func = code(func) - if not iscode(func): return [] #XXX: or raise? no matches - nested = set() - for co in func.co_consts: - if co is None: continue - co = code(co) - if co: - nested.add(co) - if recurse: nested |= set(nestedcode(co, recurse=True)) - return list(nested) - -def code(func): - """get the code object for the given function or method - - NOTE: use dill.source.getsource(CODEOBJ) to get the source code - """ - if ismethod(func): func = func.__func__ - if isfunction(func): func = func.__code__ - if istraceback(func): func = func.tb_frame - if isframe(func): func = func.f_code - if iscode(func): return func - return - -#XXX: ugly: parse dis.dis for name after " len(referrednested(func)), try calling func(). - If possible, python builds code objects, but delays building functions - until func() is called. - """ - import gc - funcs = set() - # get the code objects, and try to track down by referrence - for co in nestedcode(func, recurse): - # look for function objects that refer to the code object - for obj in gc.get_referrers(co): - # get methods - _ = getattr(obj, '__func__', None) # ismethod - if getattr(_, '__code__', None) is co: funcs.add(obj) - # get functions - elif getattr(obj, '__code__', None) is co: funcs.add(obj) - # get frame objects - elif getattr(obj, 'f_code', None) is co: funcs.add(obj) - # get code objects - elif hasattr(obj, 'co_code') and obj is co: funcs.add(obj) -# frameobjs => func.__code__.co_varnames not in func.__code__.co_cellvars -# funcobjs => func.__code__.co_cellvars not in func.__code__.co_varnames -# frameobjs are not found, however funcobjs are... -# (see: test_mixins.quad ... and test_mixins.wtf) -# after execution, code objects get compiled, and then may be found by gc - return list(funcs) - - -def freevars(func): - """get objects defined in enclosing code that are referred to by func - - returns a dict of {name:object}""" - if ismethod(func): func = func.__func__ - if isfunction(func): - closures = func.__closure__ or () - func = func.__code__.co_freevars # get freevars - else: - return {} - - def get_cell_contents(): - for name, c in zip(func, closures): - try: - cell_contents = c.cell_contents - except ValueError: # cell is empty - continue - yield name, c.cell_contents - - return dict(get_cell_contents()) - -# thanks to Davies Liu for recursion of globals -def nestedglobals(func, recurse=True): - """get the names of any globals found within func""" - func = code(func) - if func is None: return list() - import sys - from .temp import capture - CAN_NULL = sys.hexversion >= 0x30b00a7 # NULL may be prepended >= 3.11a7 - names = set() - with capture('stdout') as out: - try: - dis.dis(func) #XXX: dis.dis(None) disassembles last traceback - except IndexError: - pass #FIXME: HACK for IS_PYPY (3.11) - for line in out.getvalue().splitlines(): - if '_GLOBAL' in line: - name = line.split('(')[-1].split(')')[0] - if CAN_NULL: - names.add(name.replace('NULL + ', '').replace(' + NULL', '')) - else: - names.add(name) - for co in getattr(func, 'co_consts', tuple()): - if co and recurse and iscode(co): - names.update(nestedglobals(co, recurse=True)) - return list(names) - -def referredglobals(func, recurse=True, builtin=False): - """get the names of objects in the global scope referred to by func""" - return globalvars(func, recurse, builtin).keys() - -def globalvars(func, recurse=True, builtin=False): - """get objects defined in global scope that are referred to by func - - return a dict of {name:object}""" - if ismethod(func): func = func.__func__ - if isfunction(func): - globs = vars(getmodule(sum)).copy() if builtin else {} - # get references from within closure - orig_func, func = func, set() - for obj in orig_func.__closure__ or {}: - try: - cell_contents = obj.cell_contents - except ValueError: # cell is empty - pass - else: - _vars = globalvars(cell_contents, recurse, builtin) or {} - func.update(_vars) #XXX: (above) be wary of infinte recursion? - globs.update(_vars) - # get globals - globs.update(orig_func.__globals__ or {}) - # get names of references - if not recurse: - func.update(orig_func.__code__.co_names) - else: - func.update(nestedglobals(orig_func.__code__)) - # find globals for all entries of func - for key in func.copy(): #XXX: unnecessary...? - nested_func = globs.get(key) - if nested_func is orig_func: - #func.remove(key) if key in func else None - continue #XXX: globalvars(func, False)? - func.update(globalvars(nested_func, True, builtin)) - elif iscode(func): - globs = vars(getmodule(sum)).copy() if builtin else {} - #globs.update(globals()) - if not recurse: - func = func.co_names # get names - else: - orig_func = func.co_name # to stop infinite recursion - func = set(nestedglobals(func)) - # find globals for all entries of func - for key in func.copy(): #XXX: unnecessary...? - if key is orig_func: - #func.remove(key) if key in func else None - continue #XXX: globalvars(func, False)? - nested_func = globs.get(key) - func.update(globalvars(nested_func, True, builtin)) - else: - return {} - #NOTE: if name not in __globals__, then we skip it... - return dict((name,globs[name]) for name in func if name in globs) - - -def varnames(func): - """get names of variables defined by func - - returns a tuple (local vars, local vars referrenced by nested functions)""" - func = code(func) - if not iscode(func): - return () #XXX: better ((),())? or None? - return func.co_varnames, func.co_cellvars - - -def baditems(obj, exact=False, safe=False): #XXX: obj=globals() ? - """get items in object that fail to pickle""" - if not hasattr(obj,'__iter__'): # is not iterable - return [j for j in (badobjects(obj,0,exact,safe),) if j is not None] - obj = obj.values() if getattr(obj,'values',None) else obj - _obj = [] # can't use a set, as items may be unhashable - [_obj.append(badobjects(i,0,exact,safe)) for i in obj if i not in _obj] - return [j for j in _obj if j is not None] - - -def badobjects(obj, depth=0, exact=False, safe=False): - """get objects that fail to pickle""" - from dill import pickles - if not depth: - if pickles(obj,exact,safe): return None - return obj - return dict(((attr, badobjects(getattr(obj,attr),depth-1,exact,safe)) \ - for attr in dir(obj) if not pickles(getattr(obj,attr),exact,safe))) - -def badtypes(obj, depth=0, exact=False, safe=False): - """get types for objects that fail to pickle""" - from dill import pickles - if not depth: - if pickles(obj,exact,safe): return None - return type(obj) - return dict(((attr, badtypes(getattr(obj,attr),depth-1,exact,safe)) \ - for attr in dir(obj) if not pickles(getattr(obj,attr),exact,safe))) - -def errors(obj, depth=0, exact=False, safe=False): - """get errors for objects that fail to pickle""" - from dill import pickles, copy - if not depth: - try: - pik = copy(obj) - if exact: - assert pik == obj, \ - "Unpickling produces %s instead of %s" % (pik,obj) - assert type(pik) == type(obj), \ - "Unpickling produces %s instead of %s" % (type(pik),type(obj)) - return None - except Exception: - import sys - return sys.exc_info()[1] - _dict = {} - for attr in dir(obj): - try: - _attr = getattr(obj,attr) - except Exception: - import sys - _dict[attr] = sys.exc_info()[1] - continue - if not pickles(_attr,exact,safe): - _dict[attr] = errors(_attr,depth-1,exact,safe) - return _dict - - -# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/logger.py b/.venv/lib/python3.10/site-packages/dill/logger.py deleted file mode 100644 index 435f82b..0000000 --- a/.venv/lib/python3.10/site-packages/dill/logger.py +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Author: Leonardo Gama (@leogama) -# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -Logging utilities for dill. - -The 'logger' object is dill's top-level logger. - -The 'adapter' object wraps the logger and implements a 'trace()' method that -generates a detailed tree-style trace for the pickling call at log level INFO. - -The 'trace()' function sets and resets dill's logger log level, enabling and -disabling the pickling trace. - -The trace shows a tree structure depicting the depth of each object serialized -*with dill save functions*, but not the ones that use save functions from -'pickle._Pickler.dispatch'. If the information is available, it also displays -the size in bytes that the object contributed to the pickle stream (including -its child objects). Sample trace output: - - >>> import dill, dill.tests - >>> dill.detect.trace(True) - >>> dill.dump_session(main=dill.tests) - ┬ M1: - ├┬ F2: - │└ # F2 [32 B] - ├┬ D2: - │├┬ T4: - ││└ # T4 [35 B] - │├┬ D2: - ││├┬ T4: - │││└ # T4 [50 B] - ││├┬ D2: - │││└ # D2 [84 B] - ││└ # D2 [413 B] - │└ # D2 [763 B] - └ # M1 [813 B] -""" - -__all__ = ['adapter', 'logger', 'trace'] - -import codecs -import contextlib -import locale -import logging -import math -import os -from functools import partial -from typing import TextIO, Union - -import dill - -# Tree drawing characters: Unicode to ASCII map. -ASCII_MAP = str.maketrans({"│": "|", "├": "|", "┬": "+", "└": "`"}) - -## Notes about the design choices ## - -# Here is some domumentation of the Standard Library's logging internals that -# can't be found completely in the official documentation. dill's logger is -# obtained by calling logging.getLogger('dill') and therefore is an instance of -# logging.getLoggerClass() at the call time. As this is controlled by the user, -# in order to add some functionality to it it's necessary to use a LoggerAdapter -# to wrap it, overriding some of the adapter's methods and creating new ones. -# -# Basic calling sequence -# ====================== -# -# Python's logging functionality can be conceptually divided into five steps: -# 0. Check logging level -> abort if call level is greater than logger level -# 1. Gather information -> construct a LogRecord from passed arguments and context -# 2. Filter (optional) -> discard message if the record matches a filter -# 3. Format -> format message with args, then format output string with message plus record -# 4. Handle -> write the formatted string to output as defined in the handler -# -# dill.logging.logger.log -> # or logger.info, etc. -# Logger.log -> \ -# Logger._log -> }- accept 'extra' parameter for custom record entries -# Logger.makeRecord -> / -# LogRecord.__init__ -# Logger.handle -> -# Logger.callHandlers -> -# Handler.handle -> -# Filterer.filter -> -# Filter.filter -# StreamHandler.emit -> -# Handler.format -> -# Formatter.format -> -# LogRecord.getMessage # does: record.message = msg % args -# Formatter.formatMessage -> -# PercentStyle.format # does: self._fmt % vars(record) -# -# NOTE: All methods from the second line on are from logging.__init__.py - -class TraceAdapter(logging.LoggerAdapter): - """ - Tracks object tree depth and calculates pickled object size. - - A single instance of this wraps the module's logger, as the logging API - doesn't allow setting it directly with a custom Logger subclass. The added - 'trace()' method receives a pickle instance as the first argument and - creates extra values to be added in the LogRecord from it, then calls - 'info()'. - - Usage of logger with 'trace()' method: - - >>> from dill.logger import adapter as logger #NOTE: not dill.logger.logger - >>> ... - >>> def save_atype(pickler, obj): - >>> logger.trace(pickler, "Message with %s and %r etc. placeholders", 'text', obj) - >>> ... - """ - def __init__(self, logger): - self.logger = logger - def addHandler(self, handler): - formatter = TraceFormatter("%(prefix)s%(message)s%(suffix)s", handler=handler) - handler.setFormatter(formatter) - self.logger.addHandler(handler) - def removeHandler(self, handler): - self.logger.removeHandler(handler) - def process(self, msg, kwargs): - # A no-op override, as we don't have self.extra. - return msg, kwargs - def trace_setup(self, pickler): - # Called by Pickler.dump(). - if not dill._dill.is_dill(pickler, child=False): - return - if self.isEnabledFor(logging.INFO): - pickler._trace_depth = 1 - pickler._size_stack = [] - else: - pickler._trace_depth = None - def trace(self, pickler, msg, *args, **kwargs): - if not hasattr(pickler, '_trace_depth'): - logger.info(msg, *args, **kwargs) - return - if pickler._trace_depth is None: - return - extra = kwargs.get('extra', {}) - pushed_obj = msg.startswith('#') - size = None - try: - # Streams are not required to be tellable. - size = pickler._file.tell() - frame = pickler.framer.current_frame - try: - size += frame.tell() - except AttributeError: - # PyPy may use a BytesBuilder as frame - size += len(frame) - except (AttributeError, TypeError): - pass - if size is not None: - if not pushed_obj: - pickler._size_stack.append(size) - else: - size -= pickler._size_stack.pop() - extra['size'] = size - if pushed_obj: - pickler._trace_depth -= 1 - extra['depth'] = pickler._trace_depth - kwargs['extra'] = extra - self.info(msg, *args, **kwargs) - if not pushed_obj: - pickler._trace_depth += 1 - -class TraceFormatter(logging.Formatter): - """ - Generates message prefix and suffix from record. - - This Formatter adds prefix and suffix strings to the log message in trace - mode (an also provides empty string defaults for normal logs). - """ - def __init__(self, *args, handler=None, **kwargs): - super().__init__(*args, **kwargs) - try: - encoding = handler.stream.encoding - if encoding is None: - raise AttributeError - except AttributeError: - encoding = locale.getpreferredencoding() - try: - encoding = codecs.lookup(encoding).name - except LookupError: - self.is_utf8 = False - else: - self.is_utf8 = (encoding == codecs.lookup('utf-8').name) - def format(self, record): - fields = {'prefix': "", 'suffix': ""} - if getattr(record, 'depth', 0) > 0: - if record.msg.startswith("#"): - prefix = (record.depth - 1)*"│" + "└" - elif record.depth == 1: - prefix = "┬" - else: - prefix = (record.depth - 2)*"│" + "├┬" - if not self.is_utf8: - prefix = prefix.translate(ASCII_MAP) + "-" - fields['prefix'] = prefix + " " - if hasattr(record, 'size') and record.size is not None and record.size >= 1: - # Show object size in human-readable form. - power = int(math.log(record.size, 2)) // 10 - size = record.size >> power*10 - fields['suffix'] = " [%d %sB]" % (size, "KMGTP"[power] + "i" if power else "") - vars(record).update(fields) - return super().format(record) - -logger = logging.getLogger('dill') -logger.propagate = False -adapter = TraceAdapter(logger) -stderr_handler = logging._StderrHandler() -adapter.addHandler(stderr_handler) - -def trace(arg: Union[bool, TextIO, str, os.PathLike] = None, *, mode: str = 'a') -> None: - """print a trace through the stack when pickling; useful for debugging - - With a single boolean argument, enable or disable the tracing. - - Example usage: - - >>> import dill - >>> dill.detect.trace(True) - >>> dill.dump_session() - - Alternatively, ``trace()`` can be used as a context manager. With no - arguments, it just takes care of restoring the tracing state on exit. - Either a file handle, or a file name and (optionally) a file mode may be - specitfied to redirect the tracing output in the ``with`` block context. A - log function is yielded by the manager so the user can write extra - information to the file. - - Example usage: - - >>> from dill import detect - >>> D = {'a': 42, 'b': {'x': None}} - >>> with detect.trace(): - >>> dumps(D) - ┬ D2: - ├┬ D2: - │└ # D2 [8 B] - └ # D2 [22 B] - >>> squared = lambda x: x**2 - >>> with detect.trace('output.txt', mode='w') as log: - >>> log("> D = %r", D) - >>> dumps(D) - >>> log("> squared = %r", squared) - >>> dumps(squared) - - Arguments: - arg: a boolean value, or an optional file-like or path-like object for the context manager - mode: mode string for ``open()`` if a file name is passed as the first argument - """ - if repr(arg) not in ('False', 'True'): - return TraceManager(file=arg, mode=mode) - logger.setLevel(logging.INFO if arg else logging.WARNING) - -class TraceManager(contextlib.AbstractContextManager): - """context manager version of trace(); can redirect the trace to a file""" - def __init__(self, file, mode): - self.file = file - self.mode = mode - self.redirect = file is not None - self.file_is_stream = hasattr(file, 'write') - def __enter__(self): - if self.redirect: - stderr_handler.flush() - if self.file_is_stream: - self.handler = logging.StreamHandler(self.file) - else: - self.handler = logging.FileHandler(self.file, self.mode) - adapter.removeHandler(stderr_handler) - adapter.addHandler(self.handler) - self.old_level = adapter.getEffectiveLevel() - adapter.setLevel(logging.INFO) - return adapter.info - def __exit__(self, *exc_info): - adapter.setLevel(self.old_level) - if self.redirect: - adapter.removeHandler(self.handler) - adapter.addHandler(stderr_handler) - if not self.file_is_stream: - self.handler.close() diff --git a/.venv/lib/python3.10/site-packages/dill/objtypes.py b/.venv/lib/python3.10/site-packages/dill/objtypes.py deleted file mode 100644 index 0453fe3..0000000 --- a/.venv/lib/python3.10/site-packages/dill/objtypes.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -all Python Standard Library object types (currently: CH 1-15 @ 2.7) -and some other common object types (i.e. numpy.ndarray) - -to load more objects and types, use dill.load_types() -""" - -# non-local import of dill.objects -from dill import objects -for _type in objects.keys(): - exec("%s = type(objects['%s'])" % (_type,_type)) - -del objects -try: - del _type -except NameError: - pass diff --git a/.venv/lib/python3.10/site-packages/dill/pointers.py b/.venv/lib/python3.10/site-packages/dill/pointers.py deleted file mode 100644 index d3e2e31..0000000 --- a/.venv/lib/python3.10/site-packages/dill/pointers.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -__all__ = ['parent', 'reference', 'at', 'parents', 'children'] - -import gc -import sys - -from ._dill import _proxy_helper as reference -from ._dill import _locate_object as at - -def parent(obj, objtype, ignore=()): - """ ->>> listiter = iter([4,5,6,7]) ->>> obj = parent(listiter, list) ->>> obj == [4,5,6,7] # actually 'is', but don't have handle any longer -True - -NOTE: objtype can be a single type (e.g. int or list) or a tuple of types. - -WARNING: if obj is a sequence (e.g. list), may produce unexpected results. -Parent finds *one* parent (e.g. the last member of the sequence). - """ - depth = 1 #XXX: always looking for the parent (only, right?) - chain = parents(obj, objtype, depth, ignore) - parent = chain.pop() - if parent is obj: - return None - return parent - - -def parents(obj, objtype, depth=1, ignore=()): #XXX: objtype=object ? - """Find the chain of referents for obj. Chain will end with obj. - - objtype: an object type or tuple of types to search for - depth: search depth (e.g. depth=2 is 'grandparents') - ignore: an object or tuple of objects to ignore in the search - """ - edge_func = gc.get_referents # looking for refs, not back_refs - predicate = lambda x: isinstance(x, objtype) # looking for parent type - #if objtype is None: predicate = lambda x: True #XXX: in obj.mro() ? - ignore = (ignore,) if not hasattr(ignore, '__len__') else ignore - ignore = (id(obj) for obj in ignore) - chain = find_chain(obj, predicate, edge_func, depth)[::-1] - #XXX: should pop off obj... ? - return chain - - -def children(obj, objtype, depth=1, ignore=()): #XXX: objtype=object ? - """Find the chain of referrers for obj. Chain will start with obj. - - objtype: an object type or tuple of types to search for - depth: search depth (e.g. depth=2 is 'grandchildren') - ignore: an object or tuple of objects to ignore in the search - - NOTE: a common thing to ignore is all globals, 'ignore=(globals(),)' - - NOTE: repeated calls may yield different results, as python stores - the last value in the special variable '_'; thus, it is often good - to execute something to replace '_' (e.g. >>> 1+1). - """ - edge_func = gc.get_referrers # looking for back_refs, not refs - predicate = lambda x: isinstance(x, objtype) # looking for child type - #if objtype is None: predicate = lambda x: True #XXX: in obj.mro() ? - ignore = (ignore,) if not hasattr(ignore, '__len__') else ignore - ignore = (id(obj) for obj in ignore) - chain = find_chain(obj, predicate, edge_func, depth, ignore) - #XXX: should pop off obj... ? - return chain - - -# more generic helper function (cut-n-paste from objgraph) -# Source at http://mg.pov.lt/objgraph/ -# Copyright (c) 2008-2010 Marius Gedminas -# Copyright (c) 2010 Stefano Rivera -# Released under the MIT licence (see objgraph/objgrah.py) - -def find_chain(obj, predicate, edge_func, max_depth=20, extra_ignore=()): - queue = [obj] - depth = {id(obj): 0} - parent = {id(obj): None} - ignore = set(extra_ignore) - ignore.add(id(extra_ignore)) - ignore.add(id(queue)) - ignore.add(id(depth)) - ignore.add(id(parent)) - ignore.add(id(ignore)) - ignore.add(id(sys._getframe())) # this function - ignore.add(id(sys._getframe(1))) # find_chain/find_backref_chain, likely - gc.collect() - while queue: - target = queue.pop(0) - if predicate(target): - chain = [target] - while parent[id(target)] is not None: - target = parent[id(target)] - chain.append(target) - return chain - tdepth = depth[id(target)] - if tdepth < max_depth: - referrers = edge_func(target) - ignore.add(id(referrers)) - for source in referrers: - if id(source) in ignore: - continue - if id(source) not in depth: - depth[id(source)] = tdepth + 1 - parent[id(source)] = target - queue.append(source) - return [obj] # not found - - -# backward compatibility -refobject = at - - -# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/session.py b/.venv/lib/python3.10/site-packages/dill/session.py deleted file mode 100644 index 8278ccd..0000000 --- a/.venv/lib/python3.10/site-packages/dill/session.py +++ /dev/null @@ -1,612 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Author: Leonardo Gama (@leogama) -# Copyright (c) 2008-2015 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -Pickle and restore the intepreter session. -""" - -__all__ = [ - 'dump_module', 'load_module', 'load_module_asdict', - 'dump_session', 'load_session' # backward compatibility -] - -import re -import os -import sys -import warnings -import pathlib -import tempfile - -TEMPDIR = pathlib.PurePath(tempfile.gettempdir()) - -# Type hints. -from typing import Optional, Union - -from dill import _dill, Pickler, Unpickler -from ._dill import ( - BuiltinMethodType, FunctionType, MethodType, ModuleType, TypeType, - _import_module, _is_builtin_module, _is_imported_module, _main_module, - _reverse_typemap, __builtin__, UnpicklingError, -) - -def _module_map(): - """get map of imported modules""" - from collections import defaultdict - from types import SimpleNamespace - modmap = SimpleNamespace( - by_name=defaultdict(list), - by_id=defaultdict(list), - top_level={}, - ) - for modname, module in sys.modules.items(): - if modname in ('__main__', '__mp_main__') or not isinstance(module, ModuleType): - continue - if '.' not in modname: - modmap.top_level[id(module)] = modname - for objname, modobj in module.__dict__.items(): - modmap.by_name[objname].append((modobj, modname)) - modmap.by_id[id(modobj)].append((modobj, objname, modname)) - return modmap - -IMPORTED_AS_TYPES = (ModuleType, TypeType, FunctionType, MethodType, BuiltinMethodType) -if 'PyCapsuleType' in _reverse_typemap: - IMPORTED_AS_TYPES += (_reverse_typemap['PyCapsuleType'],) -IMPORTED_AS_MODULES = ('ctypes', 'typing', 'subprocess', 'threading', - r'concurrent\.futures(\.\w+)?', r'multiprocessing(\.\w+)?') -IMPORTED_AS_MODULES = tuple(re.compile(x) for x in IMPORTED_AS_MODULES) - -def _lookup_module(modmap, name, obj, main_module): - """lookup name or id of obj if module is imported""" - for modobj, modname in modmap.by_name[name]: - if modobj is obj and sys.modules[modname] is not main_module: - return modname, name - __module__ = getattr(obj, '__module__', None) - if isinstance(obj, IMPORTED_AS_TYPES) or (__module__ is not None - and any(regex.fullmatch(__module__) for regex in IMPORTED_AS_MODULES)): - for modobj, objname, modname in modmap.by_id[id(obj)]: - if sys.modules[modname] is not main_module: - return modname, objname - return None, None - -def _stash_modules(main_module): - modmap = _module_map() - newmod = ModuleType(main_module.__name__) - - imported = [] - imported_as = [] - imported_top_level = [] # keep separated for backward compatibility - original = {} - for name, obj in main_module.__dict__.items(): - if obj is main_module: - original[name] = newmod # self-reference - elif obj is main_module.__dict__: - original[name] = newmod.__dict__ - # Avoid incorrectly matching a singleton value in another package (ex.: __doc__). - elif any(obj is singleton for singleton in (None, False, True)) \ - or isinstance(obj, ModuleType) and _is_builtin_module(obj): # always saved by ref - original[name] = obj - else: - source_module, objname = _lookup_module(modmap, name, obj, main_module) - if source_module is not None: - if objname == name: - imported.append((source_module, name)) - else: - imported_as.append((source_module, objname, name)) - else: - try: - imported_top_level.append((modmap.top_level[id(obj)], name)) - except KeyError: - original[name] = obj - - if len(original) < len(main_module.__dict__): - newmod.__dict__.update(original) - newmod.__dill_imported = imported - newmod.__dill_imported_as = imported_as - newmod.__dill_imported_top_level = imported_top_level - if getattr(newmod, '__loader__', None) is None and _is_imported_module(main_module): - # Trick _is_imported_module() to force saving as an imported module. - newmod.__loader__ = True # will be discarded by save_module() - return newmod - else: - return main_module - -def _restore_modules(unpickler, main_module): - try: - for modname, name in main_module.__dict__.pop('__dill_imported'): - main_module.__dict__[name] = unpickler.find_class(modname, name) - for modname, objname, name in main_module.__dict__.pop('__dill_imported_as'): - main_module.__dict__[name] = unpickler.find_class(modname, objname) - for modname, name in main_module.__dict__.pop('__dill_imported_top_level'): - main_module.__dict__[name] = __import__(modname) - except KeyError: - pass - -#NOTE: 06/03/15 renamed main_module to main -def dump_module( - filename: Union[str, os.PathLike] = None, - module: Optional[Union[ModuleType, str]] = None, - refimported: bool = False, - **kwds -) -> None: - """Pickle the current state of :py:mod:`__main__` or another module to a file. - - Save the contents of :py:mod:`__main__` (e.g. from an interactive - interpreter session), an imported module, or a module-type object (e.g. - built with :py:class:`~types.ModuleType`), to a file. The pickled - module can then be restored with the function :py:func:`load_module`. - - Args: - filename: a path-like object or a writable stream. If `None` - (the default), write to a named file in a temporary directory. - module: a module object or the name of an importable module. If `None` - (the default), :py:mod:`__main__` is saved. - refimported: if `True`, all objects identified as having been imported - into the module's namespace are saved by reference. *Note:* this is - similar but independent from ``dill.settings[`byref`]``, as - ``refimported`` refers to virtually all imported objects, while - ``byref`` only affects select objects. - **kwds: extra keyword arguments passed to :py:class:`Pickler()`. - - Raises: - :py:exc:`PicklingError`: if pickling fails. - - Examples: - - - Save current interpreter session state: - - >>> import dill - >>> squared = lambda x: x*x - >>> dill.dump_module() # save state of __main__ to /tmp/session.pkl - - - Save the state of an imported/importable module: - - >>> import dill - >>> import pox - >>> pox.plus_one = lambda x: x+1 - >>> dill.dump_module('pox_session.pkl', module=pox) - - - Save the state of a non-importable, module-type object: - - >>> import dill - >>> from types import ModuleType - >>> foo = ModuleType('foo') - >>> foo.values = [1,2,3] - >>> import math - >>> foo.sin = math.sin - >>> dill.dump_module('foo_session.pkl', module=foo, refimported=True) - - - Restore the state of the saved modules: - - >>> import dill - >>> dill.load_module() - >>> squared(2) - 4 - >>> pox = dill.load_module('pox_session.pkl') - >>> pox.plus_one(1) - 2 - >>> foo = dill.load_module('foo_session.pkl') - >>> [foo.sin(x) for x in foo.values] - [0.8414709848078965, 0.9092974268256817, 0.1411200080598672] - - - Use `refimported` to save imported objects by reference: - - >>> import dill - >>> from html.entities import html5 - >>> type(html5), len(html5) - (dict, 2231) - >>> import io - >>> buf = io.BytesIO() - >>> dill.dump_module(buf) # saves __main__, with html5 saved by value - >>> len(buf.getvalue()) # pickle size in bytes - 71665 - >>> buf = io.BytesIO() - >>> dill.dump_module(buf, refimported=True) # html5 saved by reference - >>> len(buf.getvalue()) - 438 - - *Changed in version 0.3.6:* Function ``dump_session()`` was renamed to - ``dump_module()``. Parameters ``main`` and ``byref`` were renamed to - ``module`` and ``refimported``, respectively. - - Note: - Currently, ``dill.settings['byref']`` and ``dill.settings['recurse']`` - don't apply to this function. - """ - for old_par, par in [('main', 'module'), ('byref', 'refimported')]: - if old_par in kwds: - message = "The argument %r has been renamed %r" % (old_par, par) - if old_par == 'byref': - message += " to distinguish it from dill.settings['byref']" - warnings.warn(message + ".", PendingDeprecationWarning) - if locals()[par]: # the defaults are None and False - raise TypeError("both %r and %r arguments were used" % (par, old_par)) - refimported = kwds.pop('byref', refimported) - module = kwds.pop('main', module) - - from .settings import settings - protocol = settings['protocol'] - main = module - if main is None: - main = _main_module - elif isinstance(main, str): - main = _import_module(main) - if not isinstance(main, ModuleType): - raise TypeError("%r is not a module" % main) - if hasattr(filename, 'write'): - file = filename - else: - if filename is None: - filename = str(TEMPDIR/'session.pkl') - file = open(filename, 'wb') - try: - pickler = Pickler(file, protocol, **kwds) - pickler._original_main = main - if refimported: - main = _stash_modules(main) - pickler._main = main #FIXME: dill.settings are disabled - pickler._byref = False # disable pickling by name reference - pickler._recurse = False # disable pickling recursion for globals - pickler._session = True # is best indicator of when pickling a session - pickler._first_pass = True - pickler._main_modified = main is not pickler._original_main - pickler.dump(main) - finally: - if file is not filename: # if newly opened file - file.close() - return - -# Backward compatibility. -def dump_session(filename=None, main=None, byref=False, **kwds): - warnings.warn("dump_session() has been renamed dump_module()", PendingDeprecationWarning) - dump_module(filename, module=main, refimported=byref, **kwds) -dump_session.__doc__ = dump_module.__doc__ - -class _PeekableReader: - """lightweight stream wrapper that implements peek()""" - def __init__(self, stream): - self.stream = stream - def read(self, n): - return self.stream.read(n) - def readline(self): - return self.stream.readline() - def tell(self): - return self.stream.tell() - def close(self): - return self.stream.close() - def peek(self, n): - stream = self.stream - try: - if hasattr(stream, 'flush'): stream.flush() - position = stream.tell() - stream.seek(position) # assert seek() works before reading - chunk = stream.read(n) - stream.seek(position) - return chunk - except (AttributeError, OSError): - raise NotImplementedError("stream is not peekable: %r", stream) from None - -def _make_peekable(stream): - """return stream as an object with a peek() method""" - import io - if hasattr(stream, 'peek'): - return stream - if not (hasattr(stream, 'tell') and hasattr(stream, 'seek')): - try: - return io.BufferedReader(stream) - except Exception: - pass - return _PeekableReader(stream) - -def _identify_module(file, main=None): - """identify the name of the module stored in the given file-type object""" - from pickletools import genops - UNICODE = {'UNICODE', 'BINUNICODE', 'SHORT_BINUNICODE'} - found_import = False - try: - for opcode, arg, pos in genops(file.peek(256)): - if not found_import: - if opcode.name in ('GLOBAL', 'SHORT_BINUNICODE') and \ - arg.endswith('_import_module'): - found_import = True - else: - if opcode.name in UNICODE: - return arg - else: - raise UnpicklingError("reached STOP without finding main module") - except (NotImplementedError, ValueError) as error: - # ValueError occours when the end of the chunk is reached (without a STOP). - if isinstance(error, NotImplementedError) and main is not None: - # file is not peekable, but we have main. - return None - raise UnpicklingError("unable to identify main module") from error - -def load_module( - filename: Union[str, os.PathLike] = None, - module: Optional[Union[ModuleType, str]] = None, - **kwds -) -> Optional[ModuleType]: - """Update the selected module (default is :py:mod:`__main__`) with - the state saved at ``filename``. - - Restore a module to the state saved with :py:func:`dump_module`. The - saved module can be :py:mod:`__main__` (e.g. an interpreter session), - an imported module, or a module-type object (e.g. created with - :py:class:`~types.ModuleType`). - - When restoring the state of a non-importable module-type object, the - current instance of this module may be passed as the argument ``main``. - Otherwise, a new instance is created with :py:class:`~types.ModuleType` - and returned. - - Args: - filename: a path-like object or a readable stream. If `None` - (the default), read from a named file in a temporary directory. - module: a module object or the name of an importable module; - the module name and kind (i.e. imported or non-imported) must - match the name and kind of the module stored at ``filename``. - **kwds: extra keyword arguments passed to :py:class:`Unpickler()`. - - Raises: - :py:exc:`UnpicklingError`: if unpickling fails. - :py:exc:`ValueError`: if the argument ``main`` and module saved - at ``filename`` are incompatible. - - Returns: - A module object, if the saved module is not :py:mod:`__main__` or - a module instance wasn't provided with the argument ``main``. - - Examples: - - - Save the state of some modules: - - >>> import dill - >>> squared = lambda x: x*x - >>> dill.dump_module() # save state of __main__ to /tmp/session.pkl - >>> - >>> import pox # an imported module - >>> pox.plus_one = lambda x: x+1 - >>> dill.dump_module('pox_session.pkl', module=pox) - >>> - >>> from types import ModuleType - >>> foo = ModuleType('foo') # a module-type object - >>> foo.values = [1,2,3] - >>> import math - >>> foo.sin = math.sin - >>> dill.dump_module('foo_session.pkl', module=foo, refimported=True) - - - Restore the state of the interpreter: - - >>> import dill - >>> dill.load_module() # updates __main__ from /tmp/session.pkl - >>> squared(2) - 4 - - - Load the saved state of an importable module: - - >>> import dill - >>> pox = dill.load_module('pox_session.pkl') - >>> pox.plus_one(1) - 2 - >>> import sys - >>> pox in sys.modules.values() - True - - - Load the saved state of a non-importable module-type object: - - >>> import dill - >>> foo = dill.load_module('foo_session.pkl') - >>> [foo.sin(x) for x in foo.values] - [0.8414709848078965, 0.9092974268256817, 0.1411200080598672] - >>> import math - >>> foo.sin is math.sin # foo.sin was saved by reference - True - >>> import sys - >>> foo in sys.modules.values() - False - - - Update the state of a non-importable module-type object: - - >>> import dill - >>> from types import ModuleType - >>> foo = ModuleType('foo') - >>> foo.values = ['a','b'] - >>> foo.sin = lambda x: x*x - >>> dill.load_module('foo_session.pkl', module=foo) - >>> [foo.sin(x) for x in foo.values] - [0.8414709848078965, 0.9092974268256817, 0.1411200080598672] - - *Changed in version 0.3.6:* Function ``load_session()`` was renamed to - ``load_module()``. Parameter ``main`` was renamed to ``module``. - - See also: - :py:func:`load_module_asdict` to load the contents of module saved - with :py:func:`dump_module` into a dictionary. - """ - if 'main' in kwds: - warnings.warn( - "The argument 'main' has been renamed 'module'.", - PendingDeprecationWarning - ) - if module is not None: - raise TypeError("both 'module' and 'main' arguments were used") - module = kwds.pop('main') - main = module - if hasattr(filename, 'read'): - file = filename - else: - if filename is None: - filename = str(TEMPDIR/'session.pkl') - file = open(filename, 'rb') - try: - file = _make_peekable(file) - #FIXME: dill.settings are disabled - unpickler = Unpickler(file, **kwds) - unpickler._session = True - - # Resolve unpickler._main - pickle_main = _identify_module(file, main) - if main is None and pickle_main is not None: - main = pickle_main - if isinstance(main, str): - if main.startswith('__runtime__.'): - # Create runtime module to load the session into. - main = ModuleType(main.partition('.')[-1]) - else: - main = _import_module(main) - if main is not None: - if not isinstance(main, ModuleType): - raise TypeError("%r is not a module" % main) - unpickler._main = main - else: - main = unpickler._main - - # Check against the pickle's main. - is_main_imported = _is_imported_module(main) - if pickle_main is not None: - is_runtime_mod = pickle_main.startswith('__runtime__.') - if is_runtime_mod: - pickle_main = pickle_main.partition('.')[-1] - error_msg = "can't update{} module{} %r with the saved state of{} module{} %r" - if is_runtime_mod and is_main_imported: - raise ValueError( - error_msg.format(" imported", "", "", "-type object") - % (main.__name__, pickle_main) - ) - if not is_runtime_mod and not is_main_imported: - raise ValueError( - error_msg.format("", "-type object", " imported", "") - % (pickle_main, main.__name__) - ) - if main.__name__ != pickle_main: - raise ValueError(error_msg.format("", "", "", "") % (main.__name__, pickle_main)) - - # This is for find_class() to be able to locate it. - if not is_main_imported: - runtime_main = '__runtime__.%s' % main.__name__ - sys.modules[runtime_main] = main - - loaded = unpickler.load() - finally: - if not hasattr(filename, 'read'): # if newly opened file - file.close() - try: - del sys.modules[runtime_main] - except (KeyError, NameError): - pass - assert loaded is main - _restore_modules(unpickler, main) - if main is _main_module or main is module: - return None - else: - return main - -# Backward compatibility. -def load_session(filename=None, main=None, **kwds): - warnings.warn("load_session() has been renamed load_module().", PendingDeprecationWarning) - load_module(filename, module=main, **kwds) -load_session.__doc__ = load_module.__doc__ - -def load_module_asdict( - filename: Union[str, os.PathLike] = None, - update: bool = False, - **kwds -) -> dict: - """ - Load the contents of a saved module into a dictionary. - - ``load_module_asdict()`` is the near-equivalent of:: - - lambda filename: vars(dill.load_module(filename)).copy() - - however, does not alter the original module. Also, the path of - the loaded module is stored in the ``__session__`` attribute. - - Args: - filename: a path-like object or a readable stream. If `None` - (the default), read from a named file in a temporary directory. - update: if `True`, initialize the dictionary with the current state - of the module prior to loading the state stored at filename. - **kwds: extra keyword arguments passed to :py:class:`Unpickler()` - - Raises: - :py:exc:`UnpicklingError`: if unpickling fails - - Returns: - A copy of the restored module's dictionary. - - Note: - If ``update`` is True, the corresponding module may first be imported - into the current namespace before the saved state is loaded from - filename to the dictionary. Note that any module that is imported into - the current namespace as a side-effect of using ``update`` will not be - modified by loading the saved module in filename to a dictionary. - - Example: - >>> import dill - >>> alist = [1, 2, 3] - >>> anum = 42 - >>> dill.dump_module() - >>> anum = 0 - >>> new_var = 'spam' - >>> main = dill.load_module_asdict() - >>> main['__name__'], main['__session__'] - ('__main__', '/tmp/session.pkl') - >>> main is globals() # loaded objects don't reference globals - False - >>> main['alist'] == alist - True - >>> main['alist'] is alist # was saved by value - False - >>> main['anum'] == anum # changed after the session was saved - False - >>> new_var in main # would be True if the option 'update' was set - False - """ - if 'module' in kwds: - raise TypeError("'module' is an invalid keyword argument for load_module_asdict()") - if hasattr(filename, 'read'): - file = filename - else: - if filename is None: - filename = str(TEMPDIR/'session.pkl') - file = open(filename, 'rb') - try: - file = _make_peekable(file) - main_name = _identify_module(file) - old_main = sys.modules.get(main_name) - main = ModuleType(main_name) - if update: - if old_main is None: - old_main = _import_module(main_name) - main.__dict__.update(old_main.__dict__) - else: - main.__builtins__ = __builtin__ - sys.modules[main_name] = main - load_module(file, **kwds) - finally: - if not hasattr(filename, 'read'): # if newly opened file - file.close() - try: - if old_main is None: - del sys.modules[main_name] - else: - sys.modules[main_name] = old_main - except NameError: # failed before setting old_main - pass - main.__session__ = str(filename) - return main.__dict__ - - -# Internal exports for backward compatibility with dill v0.3.5.1 -# Can't be placed in dill._dill because of circular import problems. -for name in ( - '_lookup_module', '_module_map', '_restore_modules', '_stash_modules', - 'dump_session', 'load_session' # backward compatibility functions -): - setattr(_dill, name, globals()[name]) -del name diff --git a/.venv/lib/python3.10/site-packages/dill/settings.py b/.venv/lib/python3.10/site-packages/dill/settings.py deleted file mode 100644 index bba1ab9..0000000 --- a/.venv/lib/python3.10/site-packages/dill/settings.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -global settings for Pickler -""" - -from pickle import DEFAULT_PROTOCOL - -settings = { - #'main' : None, - 'protocol' : DEFAULT_PROTOCOL, - 'byref' : False, - #'strictio' : False, - 'fmode' : 0, #HANDLE_FMODE - 'recurse' : False, - 'ignore' : False, -} - -del DEFAULT_PROTOCOL - diff --git a/.venv/lib/python3.10/site-packages/dill/source.py b/.venv/lib/python3.10/site-packages/dill/source.py deleted file mode 100644 index 4b538fa..0000000 --- a/.venv/lib/python3.10/site-packages/dill/source.py +++ /dev/null @@ -1,1023 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -# -# inspired by inspect.py from Python-2.7.6 -# inspect.py author: 'Ka-Ping Yee ' -# inspect.py merged into original dill.source by Mike McKerns 4/13/14 -""" -Extensions to python's 'inspect' module, which can be used -to retrieve information from live python objects. The methods -defined in this module are augmented to facilitate access to -source code of interactively defined functions and classes, -as well as provide access to source code for objects defined -in a file. -""" - -__all__ = ['findsource', 'getsourcelines', 'getsource', 'indent', 'outdent', \ - '_wrap', 'dumpsource', 'getname', '_namespace', 'getimport', \ - '_importable', 'importable','isdynamic', 'isfrommain'] - -import linecache -import re -from inspect import (getblock, getfile, getmodule, getsourcefile, indentsize, - isbuiltin, isclass, iscode, isframe, isfunction, ismethod, - ismodule, istraceback) -from tokenize import TokenError - -from ._dill import IS_IPYTHON - - -def isfrommain(obj): - "check if object was built in __main__" - module = getmodule(obj) - if module and module.__name__ == '__main__': - return True - return False - - -def isdynamic(obj): - "check if object was built in the interpreter" - try: file = getfile(obj) - except TypeError: file = None - if file == '' and isfrommain(obj): - return True - return False - - -def _matchlambda(func, line): - """check if lambda object 'func' matches raw line of code 'line'""" - from .detect import code as getcode - from .detect import freevars, globalvars, varnames - dummy = lambda : '__this_is_a_big_dummy_function__' - # process the line (removing leading whitespace, etc) - lhs,rhs = line.split('lambda ',1)[-1].split(":", 1) #FIXME: if !1 inputs - try: #FIXME: unsafe - _ = eval("lambda %s : %s" % (lhs,rhs), globals(),locals()) - except Exception: _ = dummy - # get code objects, for comparison - _, code = getcode(_).co_code, getcode(func).co_code - # check if func is in closure - _f = [line.count(i) for i in freevars(func).keys()] - if not _f: # not in closure - # check if code matches - if _ == code: return True - return False - # weak check on freevars - if not all(_f): return False #XXX: VERY WEAK - # weak check on varnames and globalvars - _f = varnames(func) - _f = [line.count(i) for i in _f[0]+_f[1]] - if _f and not all(_f): return False #XXX: VERY WEAK - _f = [line.count(i) for i in globalvars(func).keys()] - if _f and not all(_f): return False #XXX: VERY WEAK - # check if func is a double lambda - if (line.count('lambda ') > 1) and (lhs in freevars(func).keys()): - _lhs,_rhs = rhs.split('lambda ',1)[-1].split(":",1) #FIXME: if !1 inputs - try: #FIXME: unsafe - _f = eval("lambda %s : %s" % (_lhs,_rhs), globals(),locals()) - except Exception: _f = dummy - # get code objects, for comparison - _, code = getcode(_f).co_code, getcode(func).co_code - if len(_) != len(code): return False - #NOTE: should be same code same order, but except for 't' and '\x88' - _ = set((i,j) for (i,j) in zip(_,code) if i != j) - if len(_) != 1: return False #('t','\x88') - return True - # check indentsize - if not indentsize(line): return False #FIXME: is this a good check??? - # check if code 'pattern' matches - #XXX: or pattern match against dis.dis(code)? (or use uncompyle2?) - _ = _.split(_[0]) # 't' #XXX: remove matching values if starts the same? - _f = code.split(code[0]) # '\x88' - #NOTE: should be same code different order, with different first element - _ = dict(re.match(r'([\W\D\S])(.*)', _[i]).groups() for i in range(1,len(_))) - _f = dict(re.match(r'([\W\D\S])(.*)', _f[i]).groups() for i in range(1,len(_f))) - if (_.keys() == _f.keys()) and (sorted(_.values()) == sorted(_f.values())): - return True - return False - - -def findsource(object): - """Return the entire source file and starting line number for an object. - For interactively-defined objects, the 'file' is the interpreter's history. - - The argument may be a module, class, method, function, traceback, frame, - or code object. The source code is returned as a list of all the lines - in the file and the line number indexes a line in that list. An IOError - is raised if the source code cannot be retrieved, while a TypeError is - raised for objects where the source code is unavailable (e.g. builtins).""" - - module = getmodule(object) - try: file = getfile(module) - except TypeError: file = None - is_module_main = (module and module.__name__ == '__main__' and not file) - if IS_IPYTHON and is_module_main: - #FIXME: quick fix for functions and classes in IPython interpreter - try: - file = getfile(object) - sourcefile = getsourcefile(object) - except TypeError: - if isclass(object): - for object_method in filter(isfunction, object.__dict__.values()): - # look for a method of the class - file_candidate = getfile(object_method) - if not file_candidate.startswith('': pat1 = r'(.*(?': - pat1 = r'(.*(?' - if stdin: - lnum = len(lines) - 1 # can't get lnum easily, so leverage pat - if not pat1: pat1 = r'^(\s*def\s)|(.*(? 0: #XXX: won't find decorators in ? - line = lines[lnum] - if pat1.match(line): - if not stdin: break # co_firstlineno does the job - if name == '': # hackery needed to confirm a match - if _matchlambda(obj, line): break - else: # not a lambda, just look for the name - if name in line: # need to check for decorator... - hats = 0 - for _lnum in range(lnum-1,-1,-1): - if pat2.match(lines[_lnum]): hats += 1 - else: break - lnum = lnum - hats - break - lnum = lnum - 1 - return lines, lnum - - try: # turn instances into classes - if not isclass(object) and isclass(type(object)): # __class__ - object = object.__class__ #XXX: sometimes type(class) is better? - #XXX: we don't find how the instance was built - except AttributeError: pass - if isclass(object): - name = object.__name__ - pat = re.compile(r'^(\s*)class\s*' + name + r'\b') - # make some effort to find the best matching class definition: - # use the one with the least indentation, which is the one - # that's most probably not inside a function definition. - candidates = [] - for i in range(len(lines)-1,-1,-1): - match = pat.match(lines[i]) - if match: - # if it's at toplevel, it's already the best one - if lines[i][0] == 'c': - return lines, i - # else add whitespace to candidate list - candidates.append((match.group(1), i)) - if candidates: - # this will sort by whitespace, and by line number, - # less whitespace first #XXX: should sort high lnum before low - candidates.sort() - return lines, candidates[0][1] - else: - raise IOError('could not find class definition') - raise IOError('could not find code object') - - -def getblocks(object, lstrip=False, enclosing=False, locate=False): - """Return a list of source lines and starting line number for an object. - Interactively-defined objects refer to lines in the interpreter's history. - - If enclosing=True, then also return any enclosing code. - If lstrip=True, ensure there is no indentation in the first line of code. - If locate=True, then also return the line number for the block of code. - - DEPRECATED: use 'getsourcelines' instead - """ - lines, lnum = findsource(object) - - if ismodule(object): - if lstrip: lines = _outdent(lines) - return ([lines], [0]) if locate is True else [lines] - - #XXX: 'enclosing' means: closures only? or classes and files? - indent = indentsize(lines[lnum]) - block = getblock(lines[lnum:]) #XXX: catch any TokenError here? - - if not enclosing or not indent: - if lstrip: block = _outdent(block) - return ([block], [lnum]) if locate is True else [block] - - pat1 = r'^(\s*def\s)|(.*(? indent: #XXX: should be >= ? - line += len(code) - skip - elif target in ''.join(code): - blocks.append(code) # save code block as the potential winner - _lnum.append(line - skip) # save the line number for the match - line += len(code) - skip - else: - line += 1 - skip = 0 - # find skip: the number of consecutive decorators - elif pat2.match(lines[line]): - try: code = getblock(lines[line:]) - except TokenError: code = [lines[line]] - skip = 1 - for _line in code[1:]: # skip lines that are decorators - if not pat2.match(_line): break - skip += 1 - line += skip - # no match: reset skip and go to the next line - else: - line +=1 - skip = 0 - - if not blocks: - blocks = [block] - _lnum = [lnum] - if lstrip: blocks = [_outdent(block) for block in blocks] - # return last match - return (blocks, _lnum) if locate is True else blocks - - -def getsourcelines(object, lstrip=False, enclosing=False): - """Return a list of source lines and starting line number for an object. - Interactively-defined objects refer to lines in the interpreter's history. - - The argument may be a module, class, method, function, traceback, frame, - or code object. The source code is returned as a list of the lines - corresponding to the object and the line number indicates where in the - original source file the first line of code was found. An IOError is - raised if the source code cannot be retrieved, while a TypeError is - raised for objects where the source code is unavailable (e.g. builtins). - - If lstrip=True, ensure there is no indentation in the first line of code. - If enclosing=True, then also return any enclosing code.""" - code, n = getblocks(object, lstrip=lstrip, enclosing=enclosing, locate=True) - return code[-1], n[-1] - - -#NOTE: broke backward compatibility 4/16/14 (was lstrip=True, force=True) -def getsource(object, alias='', lstrip=False, enclosing=False, \ - force=False, builtin=False): - """Return the text of the source code for an object. The source code for - interactively-defined objects are extracted from the interpreter's history. - - The argument may be a module, class, method, function, traceback, frame, - or code object. The source code is returned as a single string. An - IOError is raised if the source code cannot be retrieved, while a - TypeError is raised for objects where the source code is unavailable - (e.g. builtins). - - If alias is provided, then add a line of code that renames the object. - If lstrip=True, ensure there is no indentation in the first line of code. - If enclosing=True, then also return any enclosing code. - If force=True, catch (TypeError,IOError) and try to use import hooks. - If builtin=True, force an import for any builtins - """ - # hascode denotes a callable - hascode = _hascode(object) - # is a class instance type (and not in builtins) - instance = _isinstance(object) - - # get source lines; if fail, try to 'force' an import - try: # fails for builtins, and other assorted object types - lines, lnum = getsourcelines(object, enclosing=enclosing) - except (TypeError, IOError): # failed to get source, resort to import hooks - if not force: # don't try to get types that findsource can't get - raise - if not getmodule(object): # get things like 'None' and '1' - if not instance: return getimport(object, alias, builtin=builtin) - # special handling (numpy arrays, ...) - _import = getimport(object, builtin=builtin) - name = getname(object, force=True) - _alias = "%s = " % alias if alias else "" - if alias == name: _alias = "" - return _import+_alias+"%s\n" % name - else: #FIXME: could use a good bit of cleanup, since using getimport... - if not instance: return getimport(object, alias, builtin=builtin) - # now we are dealing with an instance... - name = object.__class__.__name__ - module = object.__module__ - if module in ['builtins','__builtin__']: - return getimport(object, alias, builtin=builtin) - else: #FIXME: leverage getimport? use 'from module import name'? - lines, lnum = ["%s = __import__('%s', fromlist=['%s']).%s\n" % (name,module,name,name)], 0 - obj = eval(lines[0].lstrip(name + ' = ')) - lines, lnum = getsourcelines(obj, enclosing=enclosing) - - # strip leading indent (helps ensure can be imported) - if lstrip or alias: - lines = _outdent(lines) - - # instantiate, if there's a nice repr #XXX: BAD IDEA??? - if instance: #and force: #XXX: move into findsource or getsourcelines ? - if '(' in repr(object): lines.append('%r\n' % object) - #else: #XXX: better to somehow to leverage __reduce__ ? - # reconstructor,args = object.__reduce__() - # _ = reconstructor(*args) - else: # fall back to serialization #XXX: bad idea? - #XXX: better not duplicate work? #XXX: better new/enclose=True? - lines = dumpsource(object, alias='', new=force, enclose=False) - lines, lnum = [line+'\n' for line in lines.split('\n')][:-1], 0 - #else: object.__code__ # raise AttributeError - - # add an alias to the source code - if alias: - if hascode: - skip = 0 - for line in lines: # skip lines that are decorators - if not line.startswith('@'): break - skip += 1 - #XXX: use regex from findsource / getsourcelines ? - if lines[skip].lstrip().startswith('def '): # we have a function - if alias != object.__name__: - lines.append('\n%s = %s\n' % (alias, object.__name__)) - elif 'lambda ' in lines[skip]: # we have a lambda - if alias != lines[skip].split('=')[0].strip(): - lines[skip] = '%s = %s' % (alias, lines[skip]) - else: # ...try to use the object's name - if alias != object.__name__: - lines.append('\n%s = %s\n' % (alias, object.__name__)) - else: # class or class instance - if instance: - if alias != lines[-1].split('=')[0].strip(): - lines[-1] = ('%s = ' % alias) + lines[-1] - else: - name = getname(object, force=True) or object.__name__ - if alias != name: - lines.append('\n%s = %s\n' % (alias, name)) - return ''.join(lines) - - -def _hascode(object): - '''True if object has an attribute that stores it's __code__''' - return getattr(object,'__code__',None) or getattr(object,'func_code',None) - -def _isinstance(object): - '''True if object is a class instance type (and is not a builtin)''' - if _hascode(object) or isclass(object) or ismodule(object): - return False - if istraceback(object) or isframe(object) or iscode(object): - return False - # special handling (numpy arrays, ...) - if not getmodule(object) and getmodule(type(object)).__name__ in ['numpy']: - return True -# # check if is instance of a builtin -# if not getmodule(object) and getmodule(type(object)).__name__ in ['__builtin__','builtins']: -# return False - _types = ('") - if not repr(type(object)).startswith(_types): #FIXME: weak hack - return False - if not getmodule(object) or object.__module__ in ['builtins','__builtin__'] or getname(object, force=True) in ['array']: - return False - return True # by process of elimination... it's what we want - - -def _intypes(object): - '''check if object is in the 'types' module''' - import types - # allow user to pass in object or object.__name__ - if type(object) is not type(''): - object = getname(object, force=True) - if object == 'ellipsis': object = 'EllipsisType' - return True if hasattr(types, object) else False - - -def _isstring(object): #XXX: isstringlike better? - '''check if object is a string-like type''' - return isinstance(object, (str, bytes)) - - -def indent(code, spaces=4): - '''indent a block of code with whitespace (default is 4 spaces)''' - indent = indentsize(code) - from numbers import Integral - if isinstance(spaces, Integral): spaces = ' '*spaces - # if '\t' is provided, will indent with a tab - nspaces = indentsize(spaces) - # blank lines (etc) need to be ignored - lines = code.split('\n') -## stq = "'''"; dtq = '"""' -## in_stq = in_dtq = False - for i in range(len(lines)): - #FIXME: works... but shouldn't indent 2nd+ lines of multiline doc - _indent = indentsize(lines[i]) - if indent > _indent: continue - lines[i] = spaces+lines[i] -## #FIXME: may fail when stq and dtq in same line (depends on ordering) -## nstq, ndtq = lines[i].count(stq), lines[i].count(dtq) -## if not in_dtq and not in_stq: -## lines[i] = spaces+lines[i] # we indent -## # entering a comment block -## if nstq%2: in_stq = not in_stq -## if ndtq%2: in_dtq = not in_dtq -## # leaving a comment block -## elif in_dtq and ndtq%2: in_dtq = not in_dtq -## elif in_stq and nstq%2: in_stq = not in_stq -## else: pass - if lines[-1].strip() == '': lines[-1] = '' - return '\n'.join(lines) - - -def _outdent(lines, spaces=None, all=True): - '''outdent lines of code, accounting for docs and line continuations''' - indent = indentsize(lines[0]) - if spaces is None or spaces > indent or spaces < 0: spaces = indent - for i in range(len(lines) if all else 1): - #FIXME: works... but shouldn't outdent 2nd+ lines of multiline doc - _indent = indentsize(lines[i]) - if spaces > _indent: _spaces = _indent - else: _spaces = spaces - lines[i] = lines[i][_spaces:] - return lines - -def outdent(code, spaces=None, all=True): - '''outdent a block of code (default is to strip all leading whitespace)''' - indent = indentsize(code) - if spaces is None or spaces > indent or spaces < 0: spaces = indent - #XXX: will this delete '\n' in some cases? - if not all: return code[spaces:] - return '\n'.join(_outdent(code.split('\n'), spaces=spaces, all=all)) - - -# _wrap provides an wrapper to correctly exec and load into locals -__globals__ = globals() -__locals__ = locals() -def _wrap(f): - """ encapsulate a function and it's __import__ """ - def func(*args, **kwds): - try: - # _ = eval(getsource(f, force=True)) #XXX: safer but less robust - exec(getimportable(f, alias='_'), __globals__, __locals__) - except Exception: - raise ImportError('cannot import name ' + f.__name__) - return _(*args, **kwds) - func.__name__ = f.__name__ - func.__doc__ = f.__doc__ - return func - - -def _enclose(object, alias=''): #FIXME: needs alias to hold returned object - """create a function enclosure around the source of some object""" - #XXX: dummy and stub should append a random string - dummy = '__this_is_a_big_dummy_enclosing_function__' - stub = '__this_is_a_stub_variable__' - code = 'def %s():\n' % dummy - code += indent(getsource(object, alias=stub, lstrip=True, force=True)) - code += indent('return %s\n' % stub) - if alias: code += '%s = ' % alias - code += '%s(); del %s\n' % (dummy, dummy) - #code += "globals().pop('%s',lambda :None)()\n" % dummy - return code - - -def dumpsource(object, alias='', new=False, enclose=True): - """'dump to source', where the code includes a pickled object. - - If new=True and object is a class instance, then create a new - instance using the unpacked class source code. If enclose, then - create the object inside a function enclosure (thus minimizing - any global namespace pollution). - """ - from dill import dumps - pik = repr(dumps(object)) - code = 'import dill\n' - if enclose: - stub = '__this_is_a_stub_variable__' #XXX: *must* be same _enclose.stub - pre = '%s = ' % stub - new = False #FIXME: new=True doesn't work with enclose=True - else: - stub = alias - pre = '%s = ' % stub if alias else alias - - # if a 'new' instance is not needed, then just dump and load - if not new or not _isinstance(object): - code += pre + 'dill.loads(%s)\n' % pik - else: #XXX: other cases where source code is needed??? - code += getsource(object.__class__, alias='', lstrip=True, force=True) - mod = repr(object.__module__) # should have a module (no builtins here) - code += pre + 'dill.loads(%s.replace(b%s,bytes(__name__,"UTF-8")))\n' % (pik,mod) - #code += 'del %s' % object.__class__.__name__ #NOTE: kills any existing! - - if enclose: - # generation of the 'enclosure' - dummy = '__this_is_a_big_dummy_object__' - dummy = _enclose(dummy, alias=alias) - # hack to replace the 'dummy' with the 'real' code - dummy = dummy.split('\n') - code = dummy[0]+'\n' + indent(code) + '\n'.join(dummy[-3:]) - - return code #XXX: better 'dumpsourcelines', returning list of lines? - - -def getname(obj, force=False, fqn=False): #XXX: throw(?) to raise error on fail? - """get the name of the object. for lambdas, get the name of the pointer """ - if fqn: return '.'.join(_namespace(obj)) #NOTE: returns 'type' - module = getmodule(obj) - if not module: # things like "None" and "1" - if not force: return None #NOTE: returns 'instance' NOT 'type' #FIXME? - # handle some special cases - if hasattr(obj, 'dtype') and not obj.shape: - return getname(obj.__class__) + "(" + repr(obj.tolist()) + ")" - return repr(obj) - try: - #XXX: 'wrong' for decorators and curried functions ? - # if obj.func_closure: ...use logic from getimportable, etc ? - name = obj.__name__ - if name == '': - return getsource(obj).split('=',1)[0].strip() - # handle some special cases - if module.__name__ in ['builtins','__builtin__']: - if name == 'ellipsis': name = 'EllipsisType' - return name - except AttributeError: #XXX: better to just throw AttributeError ? - if not force: return None - name = repr(obj) - if name.startswith('<'): # or name.split('('): - return None - return name - - -def _namespace(obj): - """_namespace(obj); return namespace hierarchy (as a list of names) - for the given object. For an instance, find the class hierarchy. - - For example: - - >>> from functools import partial - >>> p = partial(int, base=2) - >>> _namespace(p) - [\'functools\', \'partial\'] - """ - # mostly for functions and modules and such - #FIXME: 'wrong' for decorators and curried functions - try: #XXX: needs some work and testing on different types - module = qual = str(getmodule(obj)).split()[1].strip('>').strip('"').strip("'") - qual = qual.split('.') - if ismodule(obj): - return qual - # get name of a lambda, function, etc - name = getname(obj) or obj.__name__ # failing, raise AttributeError - # check special cases (NoneType, ...) - if module in ['builtins','__builtin__']: # BuiltinFunctionType - if _intypes(name): return ['types'] + [name] - return qual + [name] #XXX: can be wrong for some aliased objects - except Exception: pass - # special case: numpy.inf and numpy.nan (we don't want them as floats) - if str(obj) in ['inf','nan','Inf','NaN']: # is more, but are they needed? - return ['numpy'] + [str(obj)] - # mostly for classes and class instances and such - module = getattr(obj.__class__, '__module__', None) - qual = str(obj.__class__) - try: qual = qual[qual.index("'")+1:-2] - except ValueError: pass # str(obj.__class__) made the 'try' unnecessary - qual = qual.split(".") - if module in ['builtins','__builtin__']: - # check special cases (NoneType, Ellipsis, ...) - if qual[-1] == 'ellipsis': qual[-1] = 'EllipsisType' - if _intypes(qual[-1]): module = 'types' #XXX: BuiltinFunctionType - qual = [module] + qual - return qual - - -#NOTE: 05/25/14 broke backward compatibility: added 'alias' as 3rd argument -def _getimport(head, tail, alias='', verify=True, builtin=False): - """helper to build a likely import string from head and tail of namespace. - ('head','tail') are used in the following context: "from head import tail" - - If verify=True, then test the import string before returning it. - If builtin=True, then force an import for builtins where possible. - If alias is provided, then rename the object on import. - """ - # special handling for a few common types - if tail in ['Ellipsis', 'NotImplemented'] and head in ['types']: - head = len.__module__ - elif tail in ['None'] and head in ['types']: - _alias = '%s = ' % alias if alias else '' - if alias == tail: _alias = '' - return _alias+'%s\n' % tail - # we don't need to import from builtins, so return '' -# elif tail in ['NoneType','int','float','long','complex']: return '' #XXX: ? - if head in ['builtins','__builtin__']: - # special cases (NoneType, Ellipsis, ...) #XXX: BuiltinFunctionType - if tail == 'ellipsis': tail = 'EllipsisType' - if _intypes(tail): head = 'types' - elif not builtin: - _alias = '%s = ' % alias if alias else '' - if alias == tail: _alias = '' - return _alias+'%s\n' % tail - else: pass # handle builtins below - # get likely import string - if not head: _str = "import %s" % tail - else: _str = "from %s import %s" % (head, tail) - _alias = " as %s\n" % alias if alias else "\n" - if alias == tail: _alias = "\n" - _str += _alias - # FIXME: fails on most decorators, currying, and such... - # (could look for magic __wrapped__ or __func__ attr) - # (could fix in 'namespace' to check obj for closure) - if verify and not head.startswith('dill.'):# weird behavior for dill - #print(_str) - try: exec(_str) #XXX: check if == obj? (name collision) - except ImportError: #XXX: better top-down or bottom-up recursion? - _head = head.rsplit(".",1)[0] #(or get all, then compare == obj?) - if not _head: raise - if _head != head: - _str = _getimport(_head, tail, alias, verify) - return _str - - -#XXX: rename builtin to force? vice versa? verify to force? (as in getsource) -#NOTE: 05/25/14 broke backward compatibility: added 'alias' as 2nd argument -def getimport(obj, alias='', verify=True, builtin=False, enclosing=False): - """get the likely import string for the given object - - obj is the object to inspect - If verify=True, then test the import string before returning it. - If builtin=True, then force an import for builtins where possible. - If enclosing=True, get the import for the outermost enclosing callable. - If alias is provided, then rename the object on import. - """ - if enclosing: - from .detect import outermost - _obj = outermost(obj) - obj = _obj if _obj else obj - # get the namespace - qual = _namespace(obj) - head = '.'.join(qual[:-1]) - tail = qual[-1] - # for named things... with a nice repr #XXX: move into _namespace? - try: # look for '<...>' and be mindful it might be in lists, dicts, etc... - name = repr(obj).split('<',1)[1].split('>',1)[1] - name = None # we have a 'object'-style repr - except Exception: # it's probably something 'importable' - if head in ['builtins','__builtin__']: - name = repr(obj) #XXX: catch [1,2], (1,2), set([1,2])... others? - elif _isinstance(obj): - name = getname(obj, force=True).split('(')[0] - else: - name = repr(obj).split('(')[0] - #if not repr(obj).startswith('<'): name = repr(obj).split('(')[0] - #else: name = None - if name: # try using name instead of tail - try: return _getimport(head, name, alias, verify, builtin) - except ImportError: pass - except SyntaxError: - if head in ['builtins','__builtin__']: - _alias = '%s = ' % alias if alias else '' - if alias == name: _alias = '' - return _alias+'%s\n' % name - else: pass - try: - #if type(obj) is type(abs): _builtin = builtin # BuiltinFunctionType - #else: _builtin = False - return _getimport(head, tail, alias, verify, builtin) - except ImportError: - raise # could do some checking against obj - except SyntaxError: - if head in ['builtins','__builtin__']: - _alias = '%s = ' % alias if alias else '' - if alias == tail: _alias = '' - return _alias+'%s\n' % tail - raise # could do some checking against obj - - -def _importable(obj, alias='', source=None, enclosing=False, force=True, \ - builtin=True, lstrip=True): - """get an import string (or the source code) for the given object - - This function will attempt to discover the name of the object, or the repr - of the object, or the source code for the object. To attempt to force - discovery of the source code, use source=True, to attempt to force the - use of an import, use source=False; otherwise an import will be sought - for objects not defined in __main__. The intent is to build a string - that can be imported from a python file. obj is the object to inspect. - If alias is provided, then rename the object with the given alias. - - If source=True, use these options: - If enclosing=True, then also return any enclosing code. - If force=True, catch (TypeError,IOError) and try to use import hooks. - If lstrip=True, ensure there is no indentation in the first line of code. - - If source=False, use these options: - If enclosing=True, get the import for the outermost enclosing callable. - If force=True, then don't test the import string before returning it. - If builtin=True, then force an import for builtins where possible. - """ - if source is None: - source = True if isfrommain(obj) else False - if source: # first try to get the source - try: - return getsource(obj, alias, enclosing=enclosing, \ - force=force, lstrip=lstrip, builtin=builtin) - except Exception: pass - try: - if not _isinstance(obj): - return getimport(obj, alias, enclosing=enclosing, \ - verify=(not force), builtin=builtin) - # first 'get the import', then 'get the instance' - _import = getimport(obj, enclosing=enclosing, \ - verify=(not force), builtin=builtin) - name = getname(obj, force=True) - if not name: - raise AttributeError("object has no atribute '__name__'") - _alias = "%s = " % alias if alias else "" - if alias == name: _alias = "" - return _import+_alias+"%s\n" % name - - except Exception: pass - if not source: # try getsource, only if it hasn't been tried yet - try: - return getsource(obj, alias, enclosing=enclosing, \ - force=force, lstrip=lstrip, builtin=builtin) - except Exception: pass - # get the name (of functions, lambdas, and classes) - # or hope that obj can be built from the __repr__ - #XXX: what to do about class instances and such? - obj = getname(obj, force=force) - # we either have __repr__ or __name__ (or None) - if not obj or obj.startswith('<'): - raise AttributeError("object has no atribute '__name__'") - _alias = '%s = ' % alias if alias else '' - if alias == obj: _alias = '' - return _alias+'%s\n' % obj - #XXX: possible failsafe... (for example, for instances when source=False) - # "import dill; result = dill.loads(); # repr()" - -def _closuredimport(func, alias='', builtin=False): - """get import for closured objects; return a dict of 'name' and 'import'""" - import re - from .detect import freevars, outermost - free_vars = freevars(func) - func_vars = {} - # split into 'funcs' and 'non-funcs' - for name,obj in list(free_vars.items()): - if not isfunction(obj): continue - # get import for 'funcs' - fobj = free_vars.pop(name) - src = getsource(fobj) - if src.lstrip().startswith('@'): # we have a decorator - src = getimport(fobj, alias=alias, builtin=builtin) - else: # we have to "hack" a bit... and maybe be lucky - encl = outermost(func) - # pattern: 'func = enclosing(fobj' - pat = r'.*[\w\s]=\s*'+getname(encl)+r'\('+getname(fobj) - mod = getname(getmodule(encl)) - #HACK: get file containing 'outer' function; is func there? - lines,_ = findsource(encl) - candidate = [line for line in lines if getname(encl) in line and \ - re.match(pat, line)] - if not candidate: - mod = getname(getmodule(fobj)) - #HACK: get file containing 'inner' function; is func there? - lines,_ = findsource(fobj) - candidate = [line for line in lines \ - if getname(fobj) in line and re.match(pat, line)] - if not len(candidate): raise TypeError('import could not be found') - candidate = candidate[-1] - name = candidate.split('=',1)[0].split()[-1].strip() - src = _getimport(mod, name, alias=alias, builtin=builtin) - func_vars[name] = src - if not func_vars: - name = outermost(func) - mod = getname(getmodule(name)) - if not mod or name is func: # then it can be handled by getimport - name = getname(func, force=True) #XXX: better key? - src = getimport(func, alias=alias, builtin=builtin) - else: - lines,_ = findsource(name) - # pattern: 'func = enclosing(' - candidate = [line for line in lines if getname(name) in line and \ - re.match(r'.*[\w\s]=\s*'+getname(name)+r'\(', line)] - if not len(candidate): raise TypeError('import could not be found') - candidate = candidate[-1] - name = candidate.split('=',1)[0].split()[-1].strip() - src = _getimport(mod, name, alias=alias, builtin=builtin) - func_vars[name] = src - return func_vars - -#XXX: should be able to use __qualname__ -def _closuredsource(func, alias=''): - """get source code for closured objects; return a dict of 'name' - and 'code blocks'""" - #FIXME: this entire function is a messy messy HACK - # - pollutes global namespace - # - fails if name of freevars are reused - # - can unnecessarily duplicate function code - from .detect import freevars - free_vars = freevars(func) - func_vars = {} - # split into 'funcs' and 'non-funcs' - for name,obj in list(free_vars.items()): - if not isfunction(obj): - # get source for 'non-funcs' - free_vars[name] = getsource(obj, force=True, alias=name) - continue - # get source for 'funcs' - fobj = free_vars.pop(name) - src = getsource(fobj, alias) # DO NOT include dependencies - # if source doesn't start with '@', use name as the alias - if not src.lstrip().startswith('@'): #FIXME: 'enclose' in dummy; - src = importable(fobj,alias=name)# wrong ref 'name' - org = getsource(func, alias, enclosing=False, lstrip=True) - src = (src, org) # undecorated first, then target - else: #NOTE: reproduces the code! - org = getsource(func, enclosing=True, lstrip=False) - src = importable(fobj, alias, source=True) # include dependencies - src = (org, src) # target first, then decorated - func_vars[name] = src - src = ''.join(free_vars.values()) - if not func_vars: #FIXME: 'enclose' in dummy; wrong ref 'name' - org = getsource(func, alias, force=True, enclosing=False, lstrip=True) - src = (src, org) # variables first, then target - else: - src = (src, None) # just variables (better '' instead of None?) - func_vars[None] = src - # FIXME: remove duplicates (however, order is important...) - return func_vars - -def importable(obj, alias='', source=None, builtin=True): - """get an importable string (i.e. source code or the import string) - for the given object, including any required objects from the enclosing - and global scope - - This function will attempt to discover the name of the object, or the repr - of the object, or the source code for the object. To attempt to force - discovery of the source code, use source=True, to attempt to force the - use of an import, use source=False; otherwise an import will be sought - for objects not defined in __main__. The intent is to build a string - that can be imported from a python file. - - obj is the object to inspect. If alias is provided, then rename the - object with the given alias. If builtin=True, then force an import for - builtins where possible. - """ - #NOTE: we always 'force', and 'lstrip' as necessary - #NOTE: for 'enclosing', use importable(outermost(obj)) - if source is None: - source = True if isfrommain(obj) else False - elif builtin and isbuiltin(obj): - source = False - tried_source = tried_import = False - while True: - if not source: # we want an import - try: - if _isinstance(obj): # for instances, punt to _importable - return _importable(obj, alias, source=False, builtin=builtin) - src = _closuredimport(obj, alias=alias, builtin=builtin) - if len(src) == 0: - raise NotImplementedError('not implemented') - if len(src) > 1: - raise NotImplementedError('not implemented') - return list(src.values())[0] - except Exception: - if tried_source: raise - tried_import = True - # we want the source - try: - src = _closuredsource(obj, alias=alias) - if len(src) == 0: - raise NotImplementedError('not implemented') - # groan... an inline code stitcher - def _code_stitcher(block): - "stitch together the strings in tuple 'block'" - if block[0] and block[-1]: block = '\n'.join(block) - elif block[0]: block = block[0] - elif block[-1]: block = block[-1] - else: block = '' - return block - # get free_vars first - _src = _code_stitcher(src.pop(None)) - _src = [_src] if _src else [] - # get func_vars - for xxx in src.values(): - xxx = _code_stitcher(xxx) - if xxx: _src.append(xxx) - # make a single source string - if not len(_src): - src = '' - elif len(_src) == 1: - src = _src[0] - else: - src = '\n'.join(_src) - # get source code of objects referred to by obj in global scope - from .detect import globalvars - obj = globalvars(obj) #XXX: don't worry about alias? recurse? etc? - obj = list(getsource(_obj,name,force=True) for (name,_obj) in obj.items() if not isbuiltin(_obj)) - obj = '\n'.join(obj) if obj else '' - # combine all referred-to source (global then enclosing) - if not obj: return src - if not src: return obj - return obj + src - except Exception: - if tried_import: raise - tried_source = True - source = not source - # should never get here - return - - -# backward compatibility -def getimportable(obj, alias='', byname=True, explicit=False): - return importable(obj,alias,source=(not byname),builtin=explicit) - #return outdent(_importable(obj,alias,source=(not byname),builtin=explicit)) -def likely_import(obj, passive=False, explicit=False): - return getimport(obj, verify=(not passive), builtin=explicit) -def _likely_import(first, last, passive=False, explicit=True): - return _getimport(first, last, verify=(not passive), builtin=explicit) -_get_name = getname -getblocks_from_history = getblocks - - - -# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/temp.py b/.venv/lib/python3.10/site-packages/dill/temp.py deleted file mode 100644 index c272877..0000000 --- a/.venv/lib/python3.10/site-packages/dill/temp.py +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -Methods for serialized objects (or source code) stored in temporary files -and file-like objects. -""" -#XXX: better instead to have functions write to any given file-like object ? -#XXX: currently, all file-like objects are created by the function... - -__all__ = ['dump_source', 'dump', 'dumpIO_source', 'dumpIO',\ - 'load_source', 'load', 'loadIO_source', 'loadIO',\ - 'capture'] - -import contextlib - - -@contextlib.contextmanager -def capture(stream='stdout'): - """builds a context that temporarily replaces the given stream name - - >>> with capture('stdout') as out: - ... print ("foo!") - ... - >>> print (out.getvalue()) - foo! - - """ - import sys - from io import StringIO - orig = getattr(sys, stream) - setattr(sys, stream, StringIO()) - try: - yield getattr(sys, stream) - finally: - setattr(sys, stream, orig) - - -def b(x): # deal with b'foo' versus 'foo' - import codecs - return codecs.latin_1_encode(x)[0] - -def load_source(file, **kwds): - """load an object that was stored with dill.temp.dump_source - - file: filehandle - alias: string name of stored object - mode: mode to open the file, one of: {'r', 'rb'} - - >>> f = lambda x: x**2 - >>> pyfile = dill.temp.dump_source(f, alias='_f') - >>> _f = dill.temp.load_source(pyfile) - >>> _f(4) - 16 - """ - alias = kwds.pop('alias', None) - mode = kwds.pop('mode', 'r') - fname = getattr(file, 'name', file) # fname=file.name or fname=file (if str) - source = open(fname, mode=mode, **kwds).read() - if not alias: - tag = source.strip().splitlines()[-1].split() - if tag[0] != '#NAME:': - stub = source.splitlines()[0] - raise IOError("unknown name for code: %s" % stub) - alias = tag[-1] - local = {} - exec(source, local) - _ = eval("%s" % alias, local) - return _ - -def dump_source(object, **kwds): - """write object source to a NamedTemporaryFile (instead of dill.dump) -Loads with "import" or "dill.temp.load_source". Returns the filehandle. - - >>> f = lambda x: x**2 - >>> pyfile = dill.temp.dump_source(f, alias='_f') - >>> _f = dill.temp.load_source(pyfile) - >>> _f(4) - 16 - - >>> f = lambda x: x**2 - >>> pyfile = dill.temp.dump_source(f, dir='.') - >>> modulename = os.path.basename(pyfile.name).split('.py')[0] - >>> exec('from %s import f as _f' % modulename) - >>> _f(4) - 16 - -Optional kwds: - If 'alias' is specified, the object will be renamed to the given string. - - If 'prefix' is specified, the file name will begin with that prefix, - otherwise a default prefix is used. - - If 'dir' is specified, the file will be created in that directory, - otherwise a default directory is used. - - If 'text' is specified and true, the file is opened in text - mode. Else (the default) the file is opened in binary mode. On - some operating systems, this makes no difference. - -NOTE: Keep the return value for as long as you want your file to exist ! - """ #XXX: write a "load_source"? - from .source import importable, getname - import tempfile - kwds.setdefault('delete', True) - kwds.pop('suffix', '') # this is *always* '.py' - alias = kwds.pop('alias', '') #XXX: include an alias so a name is known - name = str(alias) or getname(object) - name = "\n#NAME: %s\n" % name - #XXX: assumes kwds['dir'] is writable and on $PYTHONPATH - file = tempfile.NamedTemporaryFile(suffix='.py', **kwds) - file.write(b(''.join([importable(object, alias=alias),name]))) - file.flush() - return file - -def load(file, **kwds): - """load an object that was stored with dill.temp.dump - - file: filehandle - mode: mode to open the file, one of: {'r', 'rb'} - - >>> dumpfile = dill.temp.dump([1, 2, 3, 4, 5]) - >>> dill.temp.load(dumpfile) - [1, 2, 3, 4, 5] - """ - import dill as pickle - mode = kwds.pop('mode', 'rb') - name = getattr(file, 'name', file) # name=file.name or name=file (if str) - return pickle.load(open(name, mode=mode, **kwds)) - -def dump(object, **kwds): - """dill.dump of object to a NamedTemporaryFile. -Loads with "dill.temp.load". Returns the filehandle. - - >>> dumpfile = dill.temp.dump([1, 2, 3, 4, 5]) - >>> dill.temp.load(dumpfile) - [1, 2, 3, 4, 5] - -Optional kwds: - If 'suffix' is specified, the file name will end with that suffix, - otherwise there will be no suffix. - - If 'prefix' is specified, the file name will begin with that prefix, - otherwise a default prefix is used. - - If 'dir' is specified, the file will be created in that directory, - otherwise a default directory is used. - - If 'text' is specified and true, the file is opened in text - mode. Else (the default) the file is opened in binary mode. On - some operating systems, this makes no difference. - -NOTE: Keep the return value for as long as you want your file to exist ! - """ - import dill as pickle - import tempfile - kwds.setdefault('delete', True) - file = tempfile.NamedTemporaryFile(**kwds) - pickle.dump(object, file) - file.flush() - return file - -def loadIO(buffer, **kwds): - """load an object that was stored with dill.temp.dumpIO - - buffer: buffer object - - >>> dumpfile = dill.temp.dumpIO([1, 2, 3, 4, 5]) - >>> dill.temp.loadIO(dumpfile) - [1, 2, 3, 4, 5] - """ - import dill as pickle - from io import BytesIO as StringIO - value = getattr(buffer, 'getvalue', buffer) # value or buffer.getvalue - if value != buffer: value = value() # buffer.getvalue() - return pickle.load(StringIO(value)) - -def dumpIO(object, **kwds): - """dill.dump of object to a buffer. -Loads with "dill.temp.loadIO". Returns the buffer object. - - >>> dumpfile = dill.temp.dumpIO([1, 2, 3, 4, 5]) - >>> dill.temp.loadIO(dumpfile) - [1, 2, 3, 4, 5] - """ - import dill as pickle - from io import BytesIO as StringIO - file = StringIO() - pickle.dump(object, file) - file.flush() - return file - -def loadIO_source(buffer, **kwds): - """load an object that was stored with dill.temp.dumpIO_source - - buffer: buffer object - alias: string name of stored object - - >>> f = lambda x:x**2 - >>> pyfile = dill.temp.dumpIO_source(f, alias='_f') - >>> _f = dill.temp.loadIO_source(pyfile) - >>> _f(4) - 16 - """ - alias = kwds.pop('alias', None) - source = getattr(buffer, 'getvalue', buffer) # source or buffer.getvalue - if source != buffer: source = source() # buffer.getvalue() - source = source.decode() # buffer to string - if not alias: - tag = source.strip().splitlines()[-1].split() - if tag[0] != '#NAME:': - stub = source.splitlines()[0] - raise IOError("unknown name for code: %s" % stub) - alias = tag[-1] - local = {} - exec(source, local) - _ = eval("%s" % alias, local) - return _ - -def dumpIO_source(object, **kwds): - """write object source to a buffer (instead of dill.dump) -Loads by with dill.temp.loadIO_source. Returns the buffer object. - - >>> f = lambda x:x**2 - >>> pyfile = dill.temp.dumpIO_source(f, alias='_f') - >>> _f = dill.temp.loadIO_source(pyfile) - >>> _f(4) - 16 - -Optional kwds: - If 'alias' is specified, the object will be renamed to the given string. - """ - from .source import importable, getname - from io import BytesIO as StringIO - alias = kwds.pop('alias', '') #XXX: include an alias so a name is known - name = str(alias) or getname(object) - name = "\n#NAME: %s\n" % name - #XXX: assumes kwds['dir'] is writable and on $PYTHONPATH - file = StringIO() - file.write(b(''.join([importable(object, alias=alias),name]))) - file.flush() - return file - - -del contextlib - - -# EOF diff --git a/.venv/lib/python3.10/site-packages/dill/tests/__init__.py b/.venv/lib/python3.10/site-packages/dill/tests/__init__.py deleted file mode 100644 index 809bc6d..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2018-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -to run this test suite, first build and install `dill`. - - $ python -m pip install ../.. - - -then run the tests with: - - $ python -m dill.tests - - -or, if `nose` is installed: - - $ nosetests - -""" diff --git a/.venv/lib/python3.10/site-packages/dill/tests/__main__.py b/.venv/lib/python3.10/site-packages/dill/tests/__main__.py deleted file mode 100644 index 17b3fab..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/__main__.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2018-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -import glob -import os -import sys -import subprocess as sp -python = sys.executable -try: - import pox - python = pox.which_python(version=True) or python -except ImportError: - pass -shell = sys.platform[:3] == 'win' - -suite = os.path.dirname(__file__) or os.path.curdir -tests = glob.glob(suite + os.path.sep + 'test_*.py') - - -if __name__ == '__main__': - - failed = 0 - for test in tests: - p = sp.Popen([python, test], shell=shell).wait() - if p: - print('F', end='', flush=True) - failed = 1 - else: - print('.', end='', flush=True) - print('') - exit(failed) diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_abc.py b/.venv/lib/python3.10/site-packages/dill/tests/test_abc.py deleted file mode 100644 index 37d2acc..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_abc.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2023-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -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 type(instance) #NOTE: issue #612, test_abc_local - #NOTE: dill.copy of local (or non-local) classes should (not) be the same? - 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) - #NOTE: dill.copy of local (or non-local) classes should (not) be the same? - # - # .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() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_check.py b/.venv/lib/python3.10/site-packages/dill/tests/test_check.py deleted file mode 100644 index 9daef72..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_check.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -from dill import check -import sys - -from dill.temp import capture - - -#FIXME: this doesn't catch output... it's from the internal call -def raise_check(func, **kwds): - try: - with capture('stdout') as out: - check(func, **kwds) - except Exception: - e = sys.exc_info()[1] - raise AssertionError(str(e)) - else: - assert 'Traceback' not in out.getvalue() - finally: - out.close() - - -f = lambda x:x**2 - - -def test_simple(verbose=None): - raise_check(f, verbose=verbose) - - -def test_recurse(verbose=None): - raise_check(f, recurse=True, verbose=verbose) - - -def test_byref(verbose=None): - raise_check(f, byref=True, verbose=verbose) - - -def test_protocol(verbose=None): - raise_check(f, protocol=True, verbose=verbose) - - -def test_python(verbose=None): - raise_check(f, python=None, verbose=verbose) - - -#TODO: test incompatible versions -#TODO: test dump failure -#TODO: test load failure - - -if __name__ == '__main__': - test_simple() - test_recurse() - test_byref() - test_protocol() - test_python() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_classdef.py b/.venv/lib/python3.10/site-packages/dill/tests/test_classdef.py deleted file mode 100644 index e3110cd..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_classdef.py +++ /dev/null @@ -1,340 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 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('..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 - -def test_inherit(): #NOTE: see issue #612 - class Foo: - w = 0 - x = 1 - y = 1.1 - a = () - b = (1,) - n = None - - class Bar(Foo): - w = 2 - x = 1 - y = 1.1 - z = 0.2 - a = () - b = (1,) - c = (2,) - n = None - - Baz = dill.copy(Bar) - - import platform - is_pypy = platform.python_implementation() == 'PyPy' - assert Bar.__dict__ == Baz.__dict__ - # ints - assert 'w' in Bar.__dict__ and 'w' in Baz.__dict__ - assert Bar.__dict__['w'] is Baz.__dict__['w'] - assert 'x' in Bar.__dict__ and 'x' in Baz.__dict__ - assert Bar.__dict__['x'] is Baz.__dict__['x'] - # floats - assert 'y' in Bar.__dict__ and 'y' in Baz.__dict__ - same = Bar.__dict__['y'] is Baz.__dict__['y'] - assert same if is_pypy else not same - assert 'z' in Bar.__dict__ and 'z' in Baz.__dict__ - same = Bar.__dict__['z'] is Baz.__dict__['z'] - assert same if is_pypy else not same - # tuples - assert 'a' in Bar.__dict__ and 'a' in Baz.__dict__ - assert Bar.__dict__['a'] is Baz.__dict__['a'] - assert 'b' in Bar.__dict__ and 'b' in Baz.__dict__ - assert Bar.__dict__['b'] is not Baz.__dict__['b'] - assert 'c' in Bar.__dict__ and 'c' in Baz.__dict__ - assert Bar.__dict__['c'] is not Baz.__dict__['c'] - # None - assert 'n' in Bar.__dict__ and 'n' in Baz.__dict__ - assert Bar.__dict__['n'] is Baz.__dict__['n'] - - -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() - test_inherit() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py b/.venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py deleted file mode 100644 index 0da145c..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) -# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -test pickling a dataclass -""" - -import dill -import dataclasses - -def test_dataclasses(): - # Issue #500 - @dataclasses.dataclass - class A: - x: int - y: str - - @dataclasses.dataclass - class B: - a: A - - a = A(1, "test") - before = B(a) - save = dill.dumps(before) - after = dill.loads(save) - assert before != after # classes don't match - assert before == B(A(**dataclasses.asdict(after.a))) - assert dataclasses.asdict(before) == dataclasses.asdict(after) - -if __name__ == '__main__': - test_dataclasses() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_detect.py b/.venv/lib/python3.10/site-packages/dill/tests/test_detect.py deleted file mode 100644 index b0a71c2..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_detect.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -from dill.detect import baditems, badobjects, badtypes, errors, parent, at, globalvars -from dill import settings -from dill._dill import IS_PYPY -from pickle import PicklingError - -import inspect -import sys -import os - -def test_bad_things(): - f = inspect.currentframe() - assert baditems(f) == [f] - #assert baditems(globals()) == [f] #XXX - assert badobjects(f) is f - assert badtypes(f) == type(f) - assert type(errors(f)) is TypeError - d = badtypes(f, 1) - assert isinstance(d, dict) - assert list(badobjects(f, 1).keys()) == list(d.keys()) - assert list(errors(f, 1).keys()) == list(d.keys()) - s = set([(err.__class__.__name__,err.args[0]) for err in list(errors(f, 1).values())]) - a = dict(s) - if not os.environ.get('COVERAGE'): #XXX: travis-ci - proxy = 0 if type(f.f_locals) is dict else 1 - assert len(s) == len(a) + proxy # TypeError (and possibly PicklingError) - n = 2 - assert len(a) is n if 'PicklingError' in a.keys() else n-1 - -def test_parent(): - x = [4,5,6,7] - listiter = iter(x) - obj = parent(listiter, list) - assert obj is x - - if IS_PYPY: assert parent(obj, int) is None - else: assert parent(obj, int) is x[-1] # python oddly? finds last int - assert at(id(at)) is at - -a, b, c = 1, 2, 3 - -def squared(x): - return a+x**2 - -def foo(x): - def bar(y): - return squared(x)+y - return bar - -class _class: - def _method(self): - pass - def ok(self): - return True - -def test_globals(): - def f(): - a - def g(): - b - def h(): - c - assert globalvars(f) == dict(a=1, b=2, c=3) - - res = globalvars(foo, recurse=True) - assert set(res) == set(['squared', 'a']) - res = globalvars(foo, recurse=False) - assert res == {} - zap = foo(2) - res = globalvars(zap, recurse=True) - assert set(res) == set(['squared', 'a']) - res = globalvars(zap, recurse=False) - assert set(res) == set(['squared']) - del zap - res = globalvars(squared) - assert set(res) == set(['a']) - # FIXME: should find referenced __builtins__ - #res = globalvars(_class, recurse=True) - #assert set(res) == set(['True']) - #res = globalvars(_class, recurse=False) - #assert res == {} - #res = globalvars(_class.ok, recurse=True) - #assert set(res) == set(['True']) - #res = globalvars(_class.ok, recurse=False) - #assert set(res) == set(['True']) - - -#98 dill ignores __getstate__ in interactive lambdas -bar = [0] - -class Foo(object): - def __init__(self): - pass - def __getstate__(self): - bar[0] = bar[0]+1 - return {} - def __setstate__(self, data): - pass - -f = Foo() - -def test_getstate(): - from dill import dumps, loads - dumps(f) - b = bar[0] - dumps(lambda: f, recurse=False) # doesn't call __getstate__ - assert bar[0] == b - dumps(lambda: f, recurse=True) # calls __getstate__ - assert bar[0] == b + 1 - -#97 serialize lambdas in test files -def test_deleted(): - global sin - from dill import dumps, loads - from math import sin, pi - - def sinc(x): - return sin(x)/x - - settings['recurse'] = True - _sinc = dumps(sinc) - sin = globals().pop('sin') - sin = 1 - del sin - sinc_ = loads(_sinc) # no NameError... pickling preserves 'sin' - res = sinc_(1) - from math import sin - assert sinc(1) == res - - -def test_lambdify(): - try: - from sympy import symbols, lambdify - except ImportError: - return - settings['recurse'] = True - x = symbols("x") - y = x**2 - f = lambdify([x], y) - z = min - d = globals() - globalvars(f, recurse=True, builtin=True) - assert z is min - assert d is globals() - - -if __name__ == '__main__': - test_bad_things() - test_parent() - test_globals() - test_getstate() - test_deleted() - test_lambdify() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py b/.venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py deleted file mode 100644 index bb904b1..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) -# Copyright (c) 2021-2025 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 dill._dill import OLD310, MAPPING_PROXY_TRICK, DictProxyType - -def test_dictproxy(): - assert dill.copy(DictProxyType({'a': 2})) - -def test_dictviews(): - x = {'a': 1} - assert dill.copy(x.keys()) - assert dill.copy(x.values()) - assert dill.copy(x.items()) - -def test_dictproxy_trick(): - if not OLD310 and MAPPING_PROXY_TRICK: - x = {'a': 1} - all_views = (x.values(), x.items(), x.keys(), x) - seperate_views = dill.copy(all_views) - new_x = seperate_views[-1] - new_x['b'] = 2 - new_x['c'] = 1 - assert len(new_x) == 3 and len(x) == 1 - assert len(seperate_views[0]) == 3 and len(all_views[0]) == 1 - assert len(seperate_views[1]) == 3 and len(all_views[1]) == 1 - assert len(seperate_views[2]) == 3 and len(all_views[2]) == 1 - assert dict(all_views[1]) == x - assert dict(seperate_views[1]) == new_x - -if __name__ == '__main__': - test_dictproxy() - test_dictviews() - test_dictproxy_trick() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_diff.py b/.venv/lib/python3.10/site-packages/dill/tests/test_diff.py deleted file mode 100644 index 16d821d..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_diff.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -from dill import __diff as diff - -import sys -IS_PYPY = not hasattr(sys, 'getrefcount') - -class A: - pass - -def test_diff(): - a = A() - b = A() - c = A() - a.a = b - b.a = c - diff.memorise(a) - assert not diff.has_changed(a) - c.a = 1 - assert diff.has_changed(a) - diff.memorise(c, force=True) - assert not diff.has_changed(a) - c.a = 2 - assert diff.has_changed(a) - changed = diff.whats_changed(a) - assert list(changed[0].keys()) == ["a"] - assert not changed[1] - - a2 = [] - b2 = [a2] - c2 = [b2] - diff.memorise(c2) - assert not diff.has_changed(c2) - a2.append(1) - assert diff.has_changed(c2) - changed = diff.whats_changed(c2) - assert changed[0] == {} - assert changed[1] - - a3 = {} - b3 = {1: a3} - c3 = {1: b3} - diff.memorise(c3) - assert not diff.has_changed(c3) - a3[1] = 1 - assert diff.has_changed(c3) - changed = diff.whats_changed(c3) - assert changed[0] == {} - assert changed[1] - - if not IS_PYPY: - import abc - # make sure the "_abc_invaldation_counter" doesn't make test fail - diff.memorise(abc.ABCMeta, force=True) - assert not diff.has_changed(abc) - abc.ABCMeta.zzz = 1 - assert diff.has_changed(abc) - changed = diff.whats_changed(abc) - assert list(changed[0].keys()) == ["ABCMeta"] - assert not changed[1] - - ''' - import Queue - diff.memorise(Queue, force=True) - assert not diff.has_changed(Queue) - Queue.Queue.zzz = 1 - assert diff.has_changed(Queue) - changed = diff.whats_changed(Queue) - assert list(changed[0].keys()) == ["Queue"] - assert not changed[1] - - import math - diff.memorise(math, force=True) - assert not diff.has_changed(math) - math.zzz = 1 - assert diff.has_changed(math) - changed = diff.whats_changed(math) - assert list(changed[0].keys()) == ["zzz"] - assert not changed[1] - ''' - - a = A() - b = A() - c = A() - a.a = b - b.a = c - diff.memorise(a) - assert not diff.has_changed(a) - c.a = 1 - assert diff.has_changed(a) - diff.memorise(c, force=True) - assert not diff.has_changed(a) - del c.a - assert diff.has_changed(a) - changed = diff.whats_changed(a) - assert list(changed[0].keys()) == ["a"] - assert not changed[1] - - -if __name__ == '__main__': - test_diff() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py b/.venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py deleted file mode 100644 index 2d66e54..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 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 as pickle -from io import BytesIO as StringIO - - -def my_fn(x): - return x * 17 - - -def test_extend(): - obj = lambda : my_fn(34) - assert obj() == 578 - - obj_io = StringIO() - pickler = pickle.Pickler(obj_io) - pickler.dump(obj) - - obj_str = obj_io.getvalue() - - obj2_io = StringIO(obj_str) - unpickler = pickle.Unpickler(obj2_io) - obj2 = unpickler.load() - - assert obj2() == 578 - - -def test_isdill(): - obj_io = StringIO() - pickler = pickle.Pickler(obj_io) - assert pickle._dill.is_dill(pickler) is True - - pickler = pickle._dill.StockPickler(obj_io) - assert pickle._dill.is_dill(pickler) is False - - try: - import multiprocess as mp - pickler = mp.reduction.ForkingPickler(obj_io) - assert pickle._dill.is_dill(pickler, child=True) is True - assert pickle._dill.is_dill(pickler, child=False) is False - except Exception: - pass - - -if __name__ == '__main__': - test_extend() - test_isdill() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py b/.venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py deleted file mode 100644 index 45e4166..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2021-2025 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 -dill.settings['recurse'] = True - -def get_fun_with_strftime(): - def fun_with_strftime(): - import datetime - return datetime.datetime.strptime("04-01-1943", "%d-%m-%Y").strftime( - "%Y-%m-%d %H:%M:%S" - ) - return fun_with_strftime - - -def get_fun_with_strftime2(): - import datetime - return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') - - -def test_doc_dill_issue_219(): - back_fn = dill.loads(dill.dumps(get_fun_with_strftime())) - assert back_fn() == "1943-01-04 00:00:00" - dupl = dill.loads(dill.dumps(get_fun_with_strftime2)) - assert dupl() == get_fun_with_strftime2() - - -def get_fun_with_internal_import(): - def fun_with_import(): - import re - return re.compile("$") - return fun_with_import - - -def test_method_with_internal_import_should_work(): - import re - back_fn = dill.loads(dill.dumps(get_fun_with_internal_import())) - import inspect - if hasattr(inspect, 'getclosurevars'): - vars = inspect.getclosurevars(back_fn) - assert vars.globals == {} - assert vars.nonlocals == {} - assert back_fn() == re.compile("$") - assert "__builtins__" in back_fn.__globals__ - - -if __name__ == "__main__": - import sys - if (sys.version_info[:3] != (3,10,0) or sys.version_info[3] != 'alpha'): - test_doc_dill_issue_219() - test_method_with_internal_import_should_work() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_file.py b/.venv/lib/python3.10/site-packages/dill/tests/test_file.py deleted file mode 100644 index 4a9bac3..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_file.py +++ /dev/null @@ -1,500 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -import os -import sys -import string -import random - -import dill - - -dill.settings['recurse'] = True - -fname = "_test_file.txt" -rand_chars = list(string.ascii_letters) + ["\n"] * 40 # bias newline - -buffer_error = ValueError("invalid buffer size") -dne_error = FileNotFoundError("[Errno 2] No such file or directory: '%s'" % fname) - - -def write_randomness(number=200): - f = open(fname, "w") - for i in range(number): - f.write(random.choice(rand_chars)) - f.close() - f = open(fname, "r") - contents = f.read() - f.close() - return contents - - -def trunc_file(): - open(fname, "w").close() - - -def throws(op, args, exc): - try: - op(*args) - except type(exc): - return sys.exc_info()[1].args == exc.args - else: - return False - - -def teardown_module(): - if os.path.exists(fname): - os.remove(fname) - - -def bench(strictio, fmode, skippypy): - import platform - if skippypy and platform.python_implementation() == 'PyPy': - # Skip for PyPy... - return - - # file exists, with same contents - # read - - write_randomness() - - f = open(fname, "r") - _f = dill.loads(dill.dumps(f, fmode=fmode))#, strictio=strictio)) - assert _f.mode == f.mode - assert _f.tell() == f.tell() - assert _f.read() == f.read() - f.close() - _f.close() - - # write - - f = open(fname, "w") - f.write("hello") - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - f1mode = f.mode - ftell = f.tell() - f.close() - f2 = dill.loads(f_dumped) #FIXME: fails due to pypy/issues/1233 - # TypeError: expected py_object instance instead of str - f2mode = f2.mode - f2tell = f2.tell() - f2name = f2.name - f2.write(" world!") - f2.close() - - if fmode == dill.HANDLE_FMODE: - assert open(fname).read() == " world!" - assert f2mode == f1mode - assert f2tell == 0 - elif fmode == dill.CONTENTS_FMODE: - assert open(fname).read() == "hello world!" - assert f2mode == f1mode - assert f2tell == ftell - assert f2name == fname - elif fmode == dill.FILE_FMODE: - assert open(fname).read() == "hello world!" - assert f2mode == f1mode - assert f2tell == ftell - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - - # append - - trunc_file() - - f = open(fname, "a") - f.write("hello") - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - f1mode = f.mode - ftell = f.tell() - f.close() - f2 = dill.loads(f_dumped) - f2mode = f2.mode - f2tell = f2.tell() - f2.write(" world!") - f2.close() - - assert f2mode == f1mode - if fmode == dill.CONTENTS_FMODE: - assert open(fname).read() == "hello world!" - assert f2tell == ftell - elif fmode == dill.HANDLE_FMODE: - assert open(fname).read() == "hello world!" - assert f2tell == ftell - elif fmode == dill.FILE_FMODE: - assert open(fname).read() == "hello world!" - assert f2tell == ftell - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - - # file exists, with different contents (smaller size) - # read - - write_randomness() - - f = open(fname, "r") - fstr = f.read() - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - f1mode = f.mode - ftell = f.tell() - f.close() - _flen = 150 - _fstr = write_randomness(number=_flen) - - if strictio: # throw error if ftell > EOF - assert throws(dill.loads, (f_dumped,), buffer_error) - else: - f2 = dill.loads(f_dumped) - assert f2.mode == f1mode - if fmode == dill.CONTENTS_FMODE: - assert f2.tell() == _flen - assert f2.read() == "" - f2.seek(0) - assert f2.read() == _fstr - assert f2.tell() == _flen # 150 - elif fmode == dill.HANDLE_FMODE: - assert f2.tell() == 0 - assert f2.read() == _fstr - assert f2.tell() == _flen # 150 - elif fmode == dill.FILE_FMODE: - assert f2.tell() == ftell # 200 - assert f2.read() == "" - f2.seek(0) - assert f2.read() == fstr - assert f2.tell() == ftell # 200 - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - f2.close() - - # write - - write_randomness() - - f = open(fname, "w") - f.write("hello") - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - f1mode = f.mode - ftell = f.tell() - f.close() - fstr = open(fname).read() - - f = open(fname, "w") - f.write("h") - _ftell = f.tell() - f.close() - - if strictio: # throw error if ftell > EOF - assert throws(dill.loads, (f_dumped,), buffer_error) - else: - f2 = dill.loads(f_dumped) - f2mode = f2.mode - f2tell = f2.tell() - f2.write(" world!") - f2.close() - if fmode == dill.CONTENTS_FMODE: - assert open(fname).read() == "h world!" - assert f2mode == f1mode - assert f2tell == _ftell - elif fmode == dill.HANDLE_FMODE: - assert open(fname).read() == " world!" - assert f2mode == f1mode - assert f2tell == 0 - elif fmode == dill.FILE_FMODE: - assert open(fname).read() == "hello world!" - assert f2mode == f1mode - assert f2tell == ftell - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - f2.close() - - # append - - trunc_file() - - f = open(fname, "a") - f.write("hello") - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - f1mode = f.mode - ftell = f.tell() - f.close() - fstr = open(fname).read() - - f = open(fname, "w") - f.write("h") - _ftell = f.tell() - f.close() - - if strictio: # throw error if ftell > EOF - assert throws(dill.loads, (f_dumped,), buffer_error) - else: - f2 = dill.loads(f_dumped) - f2mode = f2.mode - f2tell = f2.tell() - f2.write(" world!") - f2.close() - assert f2mode == f1mode - if fmode == dill.CONTENTS_FMODE: - # position of writes cannot be changed on some OSs - assert open(fname).read() == "h world!" - assert f2tell == _ftell - elif fmode == dill.HANDLE_FMODE: - assert open(fname).read() == "h world!" - assert f2tell == _ftell - elif fmode == dill.FILE_FMODE: - assert open(fname).read() == "hello world!" - assert f2tell == ftell - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - f2.close() - - # file does not exist - # read - - write_randomness() - - f = open(fname, "r") - fstr = f.read() - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - f1mode = f.mode - ftell = f.tell() - f.close() - - os.remove(fname) - - if strictio: # throw error if file DNE - assert throws(dill.loads, (f_dumped,), dne_error) - else: - f2 = dill.loads(f_dumped) - assert f2.mode == f1mode - if fmode == dill.CONTENTS_FMODE: - # FIXME: this fails on systems where f2.tell() always returns 0 - # assert f2.tell() == ftell # 200 - assert f2.read() == "" - f2.seek(0) - assert f2.read() == "" - assert f2.tell() == 0 - elif fmode == dill.FILE_FMODE: - assert f2.tell() == ftell # 200 - assert f2.read() == "" - f2.seek(0) - assert f2.read() == fstr - assert f2.tell() == ftell # 200 - elif fmode == dill.HANDLE_FMODE: - assert f2.tell() == 0 - assert f2.read() == "" - assert f2.tell() == 0 - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - f2.close() - - # write - - write_randomness() - - f = open(fname, "w+") - f.write("hello") - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - ftell = f.tell() - f1mode = f.mode - f.close() - - os.remove(fname) - - if strictio: # throw error if file DNE - assert throws(dill.loads, (f_dumped,), dne_error) - else: - f2 = dill.loads(f_dumped) - f2mode = f2.mode - f2tell = f2.tell() - f2.write(" world!") - f2.close() - if fmode == dill.CONTENTS_FMODE: - assert open(fname).read() == " world!" - assert f2mode == 'w+' - assert f2tell == 0 - elif fmode == dill.HANDLE_FMODE: - assert open(fname).read() == " world!" - assert f2mode == f1mode - assert f2tell == 0 - elif fmode == dill.FILE_FMODE: - assert open(fname).read() == "hello world!" - assert f2mode == f1mode - assert f2tell == ftell - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - - # append - - trunc_file() - - f = open(fname, "a") - f.write("hello") - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - ftell = f.tell() - f1mode = f.mode - f.close() - - os.remove(fname) - - if strictio: # throw error if file DNE - assert throws(dill.loads, (f_dumped,), dne_error) - else: - f2 = dill.loads(f_dumped) - f2mode = f2.mode - f2tell = f2.tell() - f2.write(" world!") - f2.close() - assert f2mode == f1mode - if fmode == dill.CONTENTS_FMODE: - assert open(fname).read() == " world!" - assert f2tell == 0 - elif fmode == dill.HANDLE_FMODE: - assert open(fname).read() == " world!" - assert f2tell == 0 - elif fmode == dill.FILE_FMODE: - assert open(fname).read() == "hello world!" - assert f2tell == ftell - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - - # file exists, with different contents (larger size) - # read - - write_randomness() - - f = open(fname, "r") - fstr = f.read() - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - f1mode = f.mode - ftell = f.tell() - f.close() - _flen = 250 - _fstr = write_randomness(number=_flen) - - # XXX: no safe_file: no way to be 'safe'? - - f2 = dill.loads(f_dumped) - assert f2.mode == f1mode - if fmode == dill.CONTENTS_FMODE: - assert f2.tell() == ftell # 200 - assert f2.read() == _fstr[ftell:] - f2.seek(0) - assert f2.read() == _fstr - assert f2.tell() == _flen # 250 - elif fmode == dill.HANDLE_FMODE: - assert f2.tell() == 0 - assert f2.read() == _fstr - assert f2.tell() == _flen # 250 - elif fmode == dill.FILE_FMODE: - assert f2.tell() == ftell # 200 - assert f2.read() == "" - f2.seek(0) - assert f2.read() == fstr - assert f2.tell() == ftell # 200 - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - f2.close() # XXX: other alternatives? - - # write - - f = open(fname, "w") - f.write("hello") - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - f1mode = f.mode - ftell = f.tell() - - fstr = open(fname).read() - - f.write(" and goodbye!") - _ftell = f.tell() - f.close() - - # XXX: no safe_file: no way to be 'safe'? - - f2 = dill.loads(f_dumped) - f2mode = f2.mode - f2tell = f2.tell() - f2.write(" world!") - f2.close() - if fmode == dill.CONTENTS_FMODE: - assert open(fname).read() == "hello world!odbye!" - assert f2mode == f1mode - assert f2tell == ftell - elif fmode == dill.HANDLE_FMODE: - assert open(fname).read() == " world!" - assert f2mode == f1mode - assert f2tell == 0 - elif fmode == dill.FILE_FMODE: - assert open(fname).read() == "hello world!" - assert f2mode == f1mode - assert f2tell == ftell - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - f2.close() - - # append - - trunc_file() - - f = open(fname, "a") - f.write("hello") - f_dumped = dill.dumps(f, fmode=fmode)#, strictio=strictio) - f1mode = f.mode - ftell = f.tell() - fstr = open(fname).read() - - f.write(" and goodbye!") - _ftell = f.tell() - f.close() - - # XXX: no safe_file: no way to be 'safe'? - - f2 = dill.loads(f_dumped) - f2mode = f2.mode - f2tell = f2.tell() - f2.write(" world!") - f2.close() - assert f2mode == f1mode - if fmode == dill.CONTENTS_FMODE: - assert open(fname).read() == "hello and goodbye! world!" - assert f2tell == ftell - elif fmode == dill.HANDLE_FMODE: - assert open(fname).read() == "hello and goodbye! world!" - assert f2tell == _ftell - elif fmode == dill.FILE_FMODE: - assert open(fname).read() == "hello world!" - assert f2tell == ftell - else: - raise RuntimeError("Unknown file mode '%s'" % fmode) - f2.close() - - -def test_nostrictio_handlefmode(): - bench(False, dill.HANDLE_FMODE, False) - teardown_module() - - -def test_nostrictio_filefmode(): - bench(False, dill.FILE_FMODE, False) - teardown_module() - - -def test_nostrictio_contentsfmode(): - bench(False, dill.CONTENTS_FMODE, True) - teardown_module() - - -#bench(True, dill.HANDLE_FMODE, False) -#bench(True, dill.FILE_FMODE, False) -#bench(True, dill.CONTENTS_FMODE, True) - - -if __name__ == '__main__': - test_nostrictio_handlefmode() - test_nostrictio_filefmode() - test_nostrictio_contentsfmode() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_functions.py b/.venv/lib/python3.10/site-packages/dill/tests/test_functions.py deleted file mode 100644 index 463b135..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_functions.py +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2019-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -import functools -import dill -import sys -dill.settings['recurse'] = True - - -def function_a(a): - return a - - -def function_b(b, b1): - return b + b1 - - -def function_c(c, c1=1): - return c + c1 - - -def function_d(d, d1, d2=1): - """doc string""" - return d + d1 + d2 - -function_d.__module__ = 'a module' - - -exec(''' -def function_e(e, *e1, e2=1, e3=2): - return e + sum(e1) + e2 + e3''') - -globalvar = 0 - -@functools.lru_cache(None) -def function_with_cache(x): - global globalvar - globalvar += x - return globalvar - - -def function_with_unassigned_variable(): - if False: - value = None - return (lambda: value) - - -def test_issue_510(): - # A very bizzare use of functions and methods that pickle doesn't get - # correctly for odd reasons. - class Foo: - def __init__(self): - def f2(self): - return self - self.f2 = f2.__get__(self) - - import dill, pickletools - f = Foo() - f1 = dill.copy(f) - assert f1.f2() is f1 - - -def test_functions(): - dumped_func_a = dill.dumps(function_a) - assert dill.loads(dumped_func_a)(0) == 0 - - dumped_func_b = dill.dumps(function_b) - assert dill.loads(dumped_func_b)(1,2) == 3 - - dumped_func_c = dill.dumps(function_c) - assert dill.loads(dumped_func_c)(1) == 2 - assert dill.loads(dumped_func_c)(1, 2) == 3 - - dumped_func_d = dill.dumps(function_d) - assert dill.loads(dumped_func_d).__doc__ == function_d.__doc__ - assert dill.loads(dumped_func_d).__module__ == function_d.__module__ - assert dill.loads(dumped_func_d)(1, 2) == 4 - assert dill.loads(dumped_func_d)(1, 2, 3) == 6 - assert dill.loads(dumped_func_d)(1, 2, d2=3) == 6 - - function_with_cache(1) - globalvar = 0 - dumped_func_cache = dill.dumps(function_with_cache) - assert function_with_cache(2) == 3 - assert function_with_cache(1) == 1 - assert function_with_cache(3) == 6 - assert function_with_cache(2) == 3 - - empty_cell = function_with_unassigned_variable() - cell_copy = dill.loads(dill.dumps(empty_cell)) - assert 'empty' in str(cell_copy.__closure__[0]) - try: - cell_copy() - except Exception: - # this is good - pass - else: - raise AssertionError('cell_copy() did not read an empty cell') - - exec(''' -dumped_func_e = dill.dumps(function_e) -assert dill.loads(dumped_func_e)(1, 2) == 6 -assert dill.loads(dumped_func_e)(1, 2, 3) == 9 -assert dill.loads(dumped_func_e)(1, 2, e2=3) == 8 -assert dill.loads(dumped_func_e)(1, 2, e2=3, e3=4) == 10 -assert dill.loads(dumped_func_e)(1, 2, 3, e2=4) == 12 -assert dill.loads(dumped_func_e)(1, 2, 3, e2=4, e3=5) == 15''') - -def test_code_object(): - import warnings - from dill._dill import ALL_CODE_PARAMS, CODE_PARAMS, CODE_VERSION, _create_code - code = function_c.__code__ - warnings.filterwarnings('ignore', category=DeprecationWarning) # issue 597 - LNOTAB = getattr(code, 'co_lnotab', b'') - if warnings.filters: del warnings.filters[0] - fields = {f: getattr(code, 'co_'+f) for f in CODE_PARAMS} - fields.setdefault('posonlyargcount', 0) # python >= 3.8 - fields.setdefault('lnotab', LNOTAB) # python <= 3.9 - fields.setdefault('linetable', b'') # python >= 3.10 - fields.setdefault('qualname', fields['name']) # python >= 3.11 - fields.setdefault('exceptiontable', b'') # python >= 3.11 - fields.setdefault('endlinetable', None) # python == 3.11a - fields.setdefault('columntable', None) # python == 3.11a - - for version, _, params in ALL_CODE_PARAMS: - args = tuple(fields[p] for p in params.split()) - try: - _create_code(*args) - if version >= (3,10): - _create_code(fields['lnotab'], *args) - except Exception as error: - raise Exception("failed to construct code object with format version {}".format(version)) from error - -if __name__ == '__main__': - test_functions() - test_issue_510() - test_code_object() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_functors.py b/.venv/lib/python3.10/site-packages/dill/tests/test_functors.py deleted file mode 100644 index 8bc4124..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_functors.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -import functools -import dill -dill.settings['recurse'] = True - - -def f(a, b, c): # without keywords - pass - - -def g(a, b, c=2): # with keywords - pass - - -def h(a=1, b=2, c=3): # without args - pass - - -def test_functools(): - fp = functools.partial(f, 1, 2) - gp = functools.partial(g, 1, c=2) - hp = functools.partial(h, 1, c=2) - bp = functools.partial(int, base=2) - - assert dill.pickles(fp, safe=True) - assert dill.pickles(gp, safe=True) - assert dill.pickles(hp, safe=True) - assert dill.pickles(bp, safe=True) - - -if __name__ == '__main__': - test_functools() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_logger.py b/.venv/lib/python3.10/site-packages/dill/tests/test_logger.py deleted file mode 100644 index b46a96a..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_logger.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python - -# Author: Leonardo Gama (@leogama) -# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -import logging -import re -import tempfile - -import dill -from dill import detect -from dill.logger import stderr_handler, adapter as logger - -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - -test_obj = {'a': (1, 2), 'b': object(), 'f': lambda x: x**2, 'big': list(range(10))} - -def test_logging(should_trace): - buffer = StringIO() - handler = logging.StreamHandler(buffer) - logger.addHandler(handler) - try: - dill.dumps(test_obj) - if should_trace: - regex = re.compile(r'(\S*┬ \w.*[^)]' # begin pickling object - r'|│*└ # \w.* \[\d+ (\wi)?B])' # object written (with size) - ) - for line in buffer.getvalue().splitlines(): - assert regex.fullmatch(line) - return buffer.getvalue() - else: - assert buffer.getvalue() == "" - finally: - logger.removeHandler(handler) - buffer.close() - -def test_trace_to_file(stream_trace): - file = tempfile.NamedTemporaryFile(mode='r') - with detect.trace(file.name, mode='w'): - dill.dumps(test_obj) - file_trace = file.read() - file.close() - # Apparently, objects can change location in memory... - reghex = re.compile(r'0x[0-9A-Za-z]+') - file_trace, stream_trace = reghex.sub('0x', file_trace), reghex.sub('0x', stream_trace) - # PyPy prints dictionary contents with repr(dict)... - regdict = re.compile(r'(dict\.__repr__ of ).*') - file_trace, stream_trace = regdict.sub(r'\1{}>', file_trace), regdict.sub(r'\1{}>', stream_trace) - assert file_trace == stream_trace - -if __name__ == '__main__': - logger.removeHandler(stderr_handler) - test_logging(should_trace=False) - detect.trace(True) - test_logging(should_trace=True) - detect.trace(False) - test_logging(should_trace=False) - - loglevel = logging.ERROR - logger.setLevel(loglevel) - with detect.trace(): - stream_trace = test_logging(should_trace=True) - test_logging(should_trace=False) - assert logger.getEffectiveLevel() == loglevel - test_trace_to_file(stream_trace) diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_mixins.py b/.venv/lib/python3.10/site-packages/dill/tests/test_mixins.py deleted file mode 100644 index 9a54bad..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_mixins.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 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 -dill.settings['recurse'] = True - - -def wtf(x,y,z): - def zzz(): - return x - def yyy(): - return y - def xxx(): - return z - return zzz,yyy - - -def quad(a=1, b=1, c=0): - inverted = [False] - def invert(): - inverted[0] = not inverted[0] - def dec(f): - def func(*args, **kwds): - x = f(*args, **kwds) - if inverted[0]: x = -x - return a*x**2 + b*x + c - func.__wrapped__ = f - func.invert = invert - func.inverted = inverted - return func - return dec - - -@quad(a=0,b=2) -def double_add(*args): - return sum(args) - - -fx = sum([1,2,3]) - - -### to make it interesting... -def quad_factory(a=1,b=1,c=0): - def dec(f): - def func(*args,**kwds): - fx = f(*args,**kwds) - return a*fx**2 + b*fx + c - return func - return dec - - -@quad_factory(a=0,b=4,c=0) -def quadish(x): - return x+1 - - -quadratic = quad_factory() - - -def doubler(f): - def inner(*args, **kwds): - fx = f(*args, **kwds) - return 2*fx - return inner - - -@doubler -def quadruple(x): - return 2*x - - -def test_mixins(): - # test mixins - assert double_add(1,2,3) == 2*fx - double_add.invert() - assert double_add(1,2,3) == -2*fx - - _d = dill.copy(double_add) - assert _d(1,2,3) == -2*fx - #_d.invert() #FIXME: fails seemingly randomly - #assert _d(1,2,3) == 2*fx - - assert _d.__wrapped__(1,2,3) == fx - - # XXX: issue or feature? in python3.4, inverted is linked through copy - if not double_add.inverted[0]: - double_add.invert() - - # test some stuff from source and pointers - ds = dill.source - dd = dill.detect - assert ds.getsource(dd.freevars(quadish)['f']) == '@quad_factory(a=0,b=4,c=0)\ndef quadish(x):\n return x+1\n' - assert ds.getsource(dd.freevars(quadruple)['f']) == '@doubler\ndef quadruple(x):\n return 2*x\n' - assert ds.importable(quadish, source=False) == 'from %s import quadish\n' % __name__ - assert ds.importable(quadruple, source=False) == 'from %s import quadruple\n' % __name__ - assert ds.importable(quadratic, source=False) == 'from %s import quadratic\n' % __name__ - assert ds.importable(double_add, source=False) == 'from %s import double_add\n' % __name__ - assert ds.importable(quadruple, source=True) == 'def doubler(f):\n def inner(*args, **kwds):\n fx = f(*args, **kwds)\n return 2*fx\n return inner\n\n@doubler\ndef quadruple(x):\n return 2*x\n' - #***** #FIXME: this needs work - result = ds.importable(quadish, source=True) - a,b,c,_,result = result.split('\n',4) - assert result == 'def quad_factory(a=1,b=1,c=0):\n def dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n return dec\n\n@quad_factory(a=0,b=4,c=0)\ndef quadish(x):\n return x+1\n' - assert set([a,b,c]) == set(['a = 0', 'c = 0', 'b = 4']) - result = ds.importable(quadratic, source=True) - a,b,c,result = result.split('\n',3) - assert result == '\ndef dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n' - assert set([a,b,c]) == set(['a = 1', 'c = 0', 'b = 1']) - result = ds.importable(double_add, source=True) - a,b,c,d,_,result = result.split('\n',5) - assert result == 'def quad(a=1, b=1, c=0):\n inverted = [False]\n def invert():\n inverted[0] = not inverted[0]\n def dec(f):\n def func(*args, **kwds):\n x = f(*args, **kwds)\n if inverted[0]: x = -x\n return a*x**2 + b*x + c\n func.__wrapped__ = f\n func.invert = invert\n func.inverted = inverted\n return func\n return dec\n\n@quad(a=0,b=2)\ndef double_add(*args):\n return sum(args)\n' - assert set([a,b,c,d]) == set(['a = 0', 'c = 0', 'b = 2', 'inverted = [True]']) - #***** - - -if __name__ == '__main__': - test_mixins() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_module.py b/.venv/lib/python3.10/site-packages/dill/tests/test_module.py deleted file mode 100644 index beec0c6..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_module.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -import sys -import dill -import test_mixins as module -from importlib import reload -dill.settings['recurse'] = True - -cached = (module.__cached__ if hasattr(module, "__cached__") - else module.__file__.split(".", 1)[0] + ".pyc") - -module.a = 1234 - -pik_mod = dill.dumps(module) - -module.a = 0 - -# remove module -del sys.modules[module.__name__] -del module - -module = dill.loads(pik_mod) -def test_attributes(): - #assert hasattr(module, "a") and module.a == 1234 #FIXME: -m dill.tests - assert module.double_add(1, 2, 3) == 2 * module.fx - -# Restart, and test use_diff - -reload(module) - -try: - dill.use_diff() - - module.a = 1234 - - pik_mod = dill.dumps(module) - - module.a = 0 - - # remove module - del sys.modules[module.__name__] - del module - - module = dill.loads(pik_mod) - def test_diff_attributes(): - assert hasattr(module, "a") and module.a == 1234 - assert module.double_add(1, 2, 3) == 2 * module.fx - -except AttributeError: - def test_diff_attributes(): - pass - -# clean up -import os -if os.path.exists(cached): - os.remove(cached) -pycache = os.path.join(os.path.dirname(module.__file__), "__pycache__") -if os.path.exists(pycache) and not os.listdir(pycache): - os.removedirs(pycache) - - -# test when module is None -import math - -def get_lambda(str, **kwarg): - return eval(str, kwarg, None) - -obj = get_lambda('lambda x: math.exp(x)', math=math) - -def test_module_is_none(): - assert obj.__module__ is None - assert dill.copy(obj)(3) == obj(3) - - -if __name__ == '__main__': - test_attributes() - test_diff_attributes() - test_module_is_none() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py b/.venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py deleted file mode 100644 index 477ba1b..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 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 -dill.settings['recurse'] = True - -def f(func): - def w(*args): - return f(*args) - return w - -@f -def f2(): pass - -# check when __main__ and on import -def test_decorated(): - assert dill.pickles(f2) - - -import doctest -import logging -logging.basicConfig(level=logging.DEBUG) - -class SomeUnreferencedUnpicklableClass(object): - def __reduce__(self): - raise Exception - -unpicklable = SomeUnreferencedUnpicklableClass() - -# This works fine outside of Doctest: -def test_normal(): - serialized = dill.dumps(lambda x: x) - -# should not try to pickle unpicklable object in __globals__ -def tests(): - """ - >>> serialized = dill.dumps(lambda x: x) - """ - return - -#print("\n\nRunning Doctest:") -def test_doctest(): - doctest.testmod() - - -if __name__ == '__main__': - test_decorated() - test_normal() - test_doctest() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_nested.py b/.venv/lib/python3.10/site-packages/dill/tests/test_nested.py deleted file mode 100644 index 6fd435c..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_nested.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -test dill's ability to handle nested functions -""" - -import os -import math - -import dill as pickle -pickle.settings['recurse'] = True - - -# the nested function: pickle should fail here, but dill is ok. -def adder(augend): - zero = [0] - - def inner(addend): - return addend + augend + zero[0] - return inner - - -# rewrite the nested function using a class: standard pickle should work here. -class cadder(object): - def __init__(self, augend): - self.augend = augend - self.zero = [0] - - def __call__(self, addend): - return addend + self.augend + self.zero[0] - - -# rewrite again, but as an old-style class -class c2adder: - def __init__(self, augend): - self.augend = augend - self.zero = [0] - - def __call__(self, addend): - return addend + self.augend + self.zero[0] - - -# some basic class stuff -class basic(object): - pass - - -class basic2: - pass - - -x = 5 -y = 1 - - -def test_basic(): - a = [0, 1, 2] - pa = pickle.dumps(a) - pmath = pickle.dumps(math) #XXX: FAILS in pickle - pmap = pickle.dumps(map) - # ... - la = pickle.loads(pa) - lmath = pickle.loads(pmath) - lmap = pickle.loads(pmap) - assert list(map(math.sin, a)) == list(lmap(lmath.sin, la)) - - -def test_basic_class(): - pbasic2 = pickle.dumps(basic2) - _pbasic2 = pickle.loads(pbasic2)() - pbasic = pickle.dumps(basic) - _pbasic = pickle.loads(pbasic)() - - -def test_c2adder(): - pc2adder = pickle.dumps(c2adder) - pc2add5 = pickle.loads(pc2adder)(x) - assert pc2add5(y) == x+y - - -def test_pickled_cadder(): - pcadder = pickle.dumps(cadder) - pcadd5 = pickle.loads(pcadder)(x) - assert pcadd5(y) == x+y - - -def test_raw_adder_and_inner(): - add5 = adder(x) - assert add5(y) == x+y - - -def test_pickled_adder(): - padder = pickle.dumps(adder) - padd5 = pickle.loads(padder)(x) - assert padd5(y) == x+y - - -def test_pickled_inner(): - add5 = adder(x) - pinner = pickle.dumps(add5) #XXX: FAILS in pickle - p5add = pickle.loads(pinner) - assert p5add(y) == x+y - - -def test_moduledict_where_not_main(): - try: - from . import test_moduledict - except ImportError: - import test_moduledict - name = 'test_moduledict.py' - if os.path.exists(name) and os.path.exists(name+'c'): - os.remove(name+'c') - - if os.path.exists(name) and hasattr(test_moduledict, "__cached__") \ - and os.path.exists(test_moduledict.__cached__): - os.remove(getattr(test_moduledict, "__cached__")) - - if os.path.exists("__pycache__") and not os.listdir("__pycache__"): - os.removedirs("__pycache__") - - -if __name__ == '__main__': - test_basic() - test_basic_class() - test_c2adder() - test_pickled_cadder() - test_raw_adder_and_inner() - test_pickled_adder() - test_pickled_inner() - test_moduledict_where_not_main() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_objects.py b/.venv/lib/python3.10/site-packages/dill/tests/test_objects.py deleted file mode 100644 index d26a7e8..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_objects.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -demonstrate dill's ability to pickle different python types -test pickling of all Python Standard Library objects (currently: CH 1-14 @ 2.7) -""" - -import dill as pickle -pickle.settings['recurse'] = True -#pickle.detect.trace(True) -#import pickle - -# get all objects for testing -from dill import load_types, objects, extend -load_types(pickleable=True,unpickleable=False) - -# uncomment the next two lines to test cloudpickle -#extend(False) -#import cloudpickle as pickle - -# helper objects -class _class: - def _method(self): - pass - -# objects that *fail* if imported -special = {} -special['LambdaType'] = _lambda = lambda x: lambda y: x -special['MethodType'] = _method = _class()._method -special['UnboundMethodType'] = _class._method -objects.update(special) - -def pickles(name, exact=False, verbose=True): - """quick check if object pickles with dill""" - obj = objects[name] - try: - pik = pickle.loads(pickle.dumps(obj)) - if exact: - try: - assert pik == obj - except AssertionError: - assert type(obj) == type(pik) - if verbose: print ("weak: %s %s" % (name, type(obj))) - else: - assert type(obj) == type(pik) - except Exception: - if verbose: print ("fails: %s %s" % (name, type(obj))) - - -def test_objects(verbose=True): - for member in objects.keys(): - #pickles(member, exact=True, verbose=verbose) - pickles(member, exact=False, verbose=verbose) - -if __name__ == '__main__': - import warnings - warnings.simplefilter('ignore') - test_objects(verbose=False) diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_properties.py b/.venv/lib/python3.10/site-packages/dill/tests/test_properties.py deleted file mode 100644 index b19bce5..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_properties.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -import sys - -import dill -dill.settings['recurse'] = True - - -class Foo(object): - def __init__(self): - self._data = 1 - - def _get_data(self): - return self._data - - def _set_data(self, x): - self._data = x - - data = property(_get_data, _set_data) - - -def test_data_not_none(): - FooS = dill.copy(Foo) - assert FooS.data.fget is not None - assert FooS.data.fset is not None - assert FooS.data.fdel is None - - -def test_data_unchanged(): - FooS = dill.copy(Foo) - try: - res = FooS().data - except Exception: - e = sys.exc_info()[1] - raise AssertionError(str(e)) - else: - assert res == 1 - - -def test_data_changed(): - FooS = dill.copy(Foo) - try: - f = FooS() - f.data = 1024 - res = f.data - except Exception: - e = sys.exc_info()[1] - raise AssertionError(str(e)) - else: - assert res == 1024 - - -if __name__ == '__main__': - test_data_not_none() - test_data_unchanged() - test_data_changed() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py b/.venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py deleted file mode 100644 index aa315ba..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) -# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -test pickling a PyCapsule object -""" - -import dill -import warnings - -test_pycapsule = None - -if dill._dill._testcapsule is not None: - import ctypes - def test_pycapsule(): - name = ctypes.create_string_buffer(b'dill._testcapsule') - capsule = dill._dill._PyCapsule_New( - ctypes.cast(dill._dill._PyCapsule_New, ctypes.c_void_p), - name, - None - ) - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - dill.copy(capsule) - dill._testcapsule = capsule - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - dill.copy(capsule) - dill._testcapsule = None - try: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", dill.PicklingWarning) - dill.copy(capsule) - except dill.UnpicklingError: - pass - else: - raise AssertionError("Expected a different error") - -if __name__ == '__main__': - if test_pycapsule is not None: - test_pycapsule() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_recursive.py b/.venv/lib/python3.10/site-packages/dill/tests/test_recursive.py deleted file mode 100644 index d7542ff..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_recursive.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2019-2025 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 functools import partial -import warnings - - -def copy(obj, byref=False, recurse=False): - if byref: - try: - return dill.copy(obj, byref=byref, recurse=recurse) - except Exception: - pass - else: - raise AssertionError('Copy of %s with byref=True should have given a warning!' % (obj,)) - - warnings.simplefilter('ignore') - val = dill.copy(obj, byref=byref, recurse=recurse) - warnings.simplefilter('error') - return val - else: - return dill.copy(obj, byref=byref, recurse=recurse) - - -class obj1(object): - def __init__(self): - super(obj1, self).__init__() - -class obj2(object): - def __init__(self): - super(obj2, self).__init__() - -class obj3(object): - super_ = super - def __init__(self): - obj3.super_(obj3, self).__init__() - - -def test_super(): - assert copy(obj1(), byref=True) - assert copy(obj1(), byref=True, recurse=True) - assert copy(obj1(), recurse=True) - assert copy(obj1()) - - assert copy(obj2(), byref=True) - assert copy(obj2(), byref=True, recurse=True) - assert copy(obj2(), recurse=True) - assert copy(obj2()) - - assert copy(obj3(), byref=True) - assert copy(obj3(), byref=True, recurse=True) - assert copy(obj3(), recurse=True) - assert copy(obj3()) - - -def get_trigger(model): - pass - -class Machine(object): - def __init__(self): - self.child = Model() - self.trigger = partial(get_trigger, self) - self.child.trigger = partial(get_trigger, self.child) - -class Model(object): - pass - - - -def test_partial(): - assert copy(Machine(), byref=True) - assert copy(Machine(), byref=True, recurse=True) - assert copy(Machine(), recurse=True) - assert copy(Machine()) - - -class Machine2(object): - def __init__(self): - self.go = partial(self.member, self) - def member(self, model): - pass - - -class SubMachine(Machine2): - def __init__(self): - super(SubMachine, self).__init__() - - -def test_partials(): - assert copy(SubMachine(), byref=True) - assert copy(SubMachine(), byref=True, recurse=True) - assert copy(SubMachine(), recurse=True) - assert copy(SubMachine()) - - -class obj4(object): - def __init__(self): - super(obj4, self).__init__() - a = self - class obj5(object): - def __init__(self): - super(obj5, self).__init__() - self.a = a - self.b = obj5() - - -def test_circular_reference(): - assert copy(obj4()) - obj4_copy = dill.loads(dill.dumps(obj4())) - assert type(obj4_copy) is type(obj4_copy).__init__.__closure__[0].cell_contents - assert type(obj4_copy.b) is type(obj4_copy.b).__init__.__closure__[0].cell_contents - - -def f(): - def g(): - return g - return g - - -def test_function_cells(): - assert copy(f()) - - -def fib(n): - assert n >= 0 - if n <= 1: - return n - else: - return fib(n-1) + fib(n-2) - - -def test_recursive_function(): - global fib - fib2 = copy(fib, recurse=True) - fib3 = copy(fib) - fib4 = fib - del fib - assert fib2(5) == 5 - for _fib in (fib3, fib4): - try: - _fib(5) - except Exception: - # This is expected to fail because fib no longer exists - pass - else: - raise AssertionError("Function fib shouldn't have been found") - fib = fib4 - - -def collection_function_recursion(): - d = {} - def g(): - return d - d['g'] = g - return g - - -def test_collection_function_recursion(): - g = copy(collection_function_recursion()) - assert g()['g'] is g - - -if __name__ == '__main__': - with warnings.catch_warnings(): - warnings.simplefilter('error') - test_super() - test_partial() - test_partials() - test_circular_reference() - test_function_cells() - test_recursive_function() - test_collection_function_recursion() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_registered.py b/.venv/lib/python3.10/site-packages/dill/tests/test_registered.py deleted file mode 100644 index 92c3703..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_registered.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -test pickling registered objects -""" - -import dill -from dill._objects import failures, registered, succeeds -import warnings -warnings.filterwarnings('ignore') - -def check(d, ok=True): - res = [] - for k,v in d.items(): - try: - z = dill.copy(v) - if ok: res.append(k) - except: - if not ok: res.append(k) - return res - -fails = check(failures) -try: - assert not bool(fails) -except AssertionError as e: - print("FAILS: %s" % fails) - raise e from None - -register = check(registered, ok=False) -try: - assert not bool(register) -except AssertionError as e: - print("REGISTER: %s" % register) - raise e from None - -success = check(succeeds, ok=False) -try: - assert not bool(success) -except AssertionError as e: - print("SUCCESS: %s" % success) - raise e from None - -import builtins -import types -q = dill._dill._reverse_typemap -p = {k:v for k,v in q.items() if k not in vars(builtins) and k not in vars(types)} - -diff = set(p.keys()).difference(registered.keys()) -try: - assert not bool(diff) -except AssertionError as e: - print("DIFF: %s" % diff) - raise e from None - -miss = set(registered.keys()).difference(p.keys()) -try: - assert not bool(miss) -except AssertionError as e: - print("MISS: %s" % miss) - raise e from None diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_restricted.py b/.venv/lib/python3.10/site-packages/dill/tests/test_restricted.py deleted file mode 100644 index ea4857b..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_restricted.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# -# Author: Kirill Makhonin (@kirillmakhonin) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 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 - -class RestrictedType: - def __bool__(*args, **kwargs): - raise Exception('Restricted function') - - __eq__ = __lt__ = __le__ = __ne__ = __gt__ = __ge__ = __hash__ = __bool__ - -glob_obj = RestrictedType() - -def restricted_func(): - a = glob_obj - -def test_function_with_restricted_object(): - deserialized = dill.loads(dill.dumps(restricted_func, recurse=True)) - - -if __name__ == '__main__': - test_function_with_restricted_object() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_selected.py b/.venv/lib/python3.10/site-packages/dill/tests/test_selected.py deleted file mode 100644 index ea0fafa..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_selected.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -testing some selected object types -""" - -import dill -dill.settings['recurse'] = True - -verbose = False - -def test_dict_contents(): - c = type.__dict__ - for i,j in c.items(): - #try: - ok = dill.pickles(j) - #except Exception: - # print ("FAIL: %s with %s" % (i, dill.detect.errors(j))) - if verbose: print ("%s: %s, %s" % (ok, type(j), j)) - assert ok - if verbose: print ("") - -def _g(x): yield x; - -def _f(): - try: raise - except Exception: - from sys import exc_info - e, er, tb = exc_info() - return er, tb - -class _d(object): - def _method(self): - pass - -from dill import objects -from dill import load_types -load_types(pickleable=True,unpickleable=False) -_newclass = objects['ClassObjectType'] -# some clean-up #FIXME: should happen internal to dill -objects['TemporaryFileType'].close() -objects['TextWrapperType'].close() -if 'BufferedRandomType' in objects: - objects['BufferedRandomType'].close() -objects['BufferedReaderType'].close() -objects['BufferedWriterType'].close() -objects['FileType'].close() -del objects - -# getset_descriptor for new-style classes (fails on '_method', if not __main__) -def test_class_descriptors(): - d = _d.__dict__ - for i in d.values(): - ok = dill.pickles(i) - if verbose: print ("%s: %s, %s" % (ok, type(i), i)) - assert ok - if verbose: print ("") - od = _newclass.__dict__ - for i in od.values(): - ok = dill.pickles(i) - if verbose: print ("%s: %s, %s" % (ok, type(i), i)) - assert ok - if verbose: print ("") - -# (__main__) class instance for new-style classes -def test_class(): - o = _d() - oo = _newclass() - ok = dill.pickles(o) - if verbose: print ("%s: %s, %s" % (ok, type(o), o)) - assert ok - ok = dill.pickles(oo) - if verbose: print ("%s: %s, %s" % (ok, type(oo), oo)) - assert ok - if verbose: print ("") - -# frames, generators, and tracebacks (all depend on frame) -def test_frame_related(): - g = _g(1) - f = g.gi_frame - e,t = _f() - _is = lambda ok: ok - ok = dill.pickles(f) - if verbose: print ("%s: %s, %s" % (ok, type(f), f)) - assert not ok - ok = dill.pickles(g) - if verbose: print ("%s: %s, %s" % (ok, type(g), g)) - assert _is(not ok) #XXX: dill fails - ok = dill.pickles(t) - if verbose: print ("%s: %s, %s" % (ok, type(t), t)) - assert not ok #XXX: dill fails - ok = dill.pickles(e) - if verbose: print ("%s: %s, %s" % (ok, type(e), e)) - assert ok - if verbose: print ("") - -def test_typing(): - import typing - x = typing.Any - assert x == dill.copy(x) - x = typing.Dict[int, str] - assert x == dill.copy(x) - x = typing.List[int] - assert x == dill.copy(x) - x = typing.Tuple[int, str] - assert x == dill.copy(x) - x = typing.Tuple[int] - assert x == dill.copy(x) - x = typing.Tuple[()] - assert x == dill.copy(x) - x = typing.Tuple[()].copy_with(()) - assert x == dill.copy(x) - return - - -if __name__ == '__main__': - test_frame_related() - test_dict_contents() - test_class() - test_class_descriptors() - test_typing() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_session.py b/.venv/lib/python3.10/site-packages/dill/tests/test_session.py deleted file mode 100644 index 891eaf8..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_session.py +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/bin/env python - -# Author: Leonardo Gama (@leogama) -# Copyright (c) 2022-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -import atexit -import os -import sys -import __main__ -from contextlib import suppress -from io import BytesIO - -import dill - -session_file = os.path.join(os.path.dirname(__file__), 'session-refimported-%s.pkl') - -################### -# Child process # -################### - -def _error_line(error, obj, refimported): - import traceback - line = traceback.format_exc().splitlines()[-2].replace('[obj]', '['+repr(obj)+']') - return "while testing (with refimported=%s): %s" % (refimported, line.lstrip()) - -if __name__ == '__main__' and len(sys.argv) >= 3 and sys.argv[1] == '--child': - # Test session loading in a fresh interpreter session. - refimported = (sys.argv[2] == 'True') - dill.load_module(session_file % refimported, module='__main__') - - def test_modules(refimported): - # FIXME: In this test setting with CPython 3.7, 'calendar' is not included - # in sys.modules, independent of the value of refimported. Tried to - # run garbage collection just before loading the session with no luck. It - # fails even when preceding them with 'import calendar'. Needed to run - # these kinds of tests in a supbrocess. Failing test sample: - # assert globals()['day_name'] is sys.modules['calendar'].__dict__['day_name'] - try: - for obj in ('json', 'url', 'local_mod', 'sax', 'dom'): - assert globals()[obj].__name__ in sys.modules - assert 'calendar' in sys.modules and 'cmath' in sys.modules - import calendar, cmath - - for obj in ('Calendar', 'isleap'): - assert globals()[obj] is sys.modules['calendar'].__dict__[obj] - assert __main__.day_name.__module__ == 'calendar' - if refimported: - assert __main__.day_name is calendar.day_name - - assert __main__.complex_log is cmath.log - - except AssertionError as error: - error.args = (_error_line(error, obj, refimported),) - raise - - test_modules(refimported) - sys.exit() - -#################### -# Parent process # -#################### - -# Create various kinds of objects to test different internal logics. - -## Modules. -import json # top-level module -import urllib as url # top-level module under alias -from xml import sax # submodule -import xml.dom.minidom as dom # submodule under alias -import test_dictviews as local_mod # non-builtin top-level module - -## Imported objects. -from calendar import Calendar, isleap, day_name # class, function, other object -from cmath import log as complex_log # imported with alias - -## Local objects. -x = 17 -empty = None -names = ['Alice', 'Bob', 'Carol'] -def squared(x): return x**2 -cubed = lambda x: x**3 -class Person: - def __init__(self, name, age): - self.name = name - self.age = age -person = Person(names[0], x) -class CalendarSubclass(Calendar): - def weekdays(self): - return [day_name[i] for i in self.iterweekdays()] -cal = CalendarSubclass() -selfref = __main__ - -# Setup global namespace for session saving tests. -class TestNamespace: - test_globals = globals().copy() - def __init__(self, **extra): - self.extra = extra - def __enter__(self): - self.backup = globals().copy() - globals().clear() - globals().update(self.test_globals) - globals().update(self.extra) - return self - def __exit__(self, *exc_info): - globals().clear() - globals().update(self.backup) - -def _clean_up_cache(module): - cached = module.__file__.split('.', 1)[0] + '.pyc' - cached = module.__cached__ if hasattr(module, '__cached__') else cached - pycache = os.path.join(os.path.dirname(module.__file__), '__pycache__') - for remove, file in [(os.remove, cached), (os.removedirs, pycache)]: - with suppress(OSError): - remove(file) - -atexit.register(_clean_up_cache, local_mod) - -def _test_objects(main, globals_copy, refimported): - try: - main_dict = __main__.__dict__ - global Person, person, Calendar, CalendarSubclass, cal, selfref - - for obj in ('json', 'url', 'local_mod', 'sax', 'dom'): - assert globals()[obj].__name__ == globals_copy[obj].__name__ - - for obj in ('x', 'empty', 'names'): - assert main_dict[obj] == globals_copy[obj] - - for obj in ['squared', 'cubed']: - assert main_dict[obj].__globals__ is main_dict - assert main_dict[obj](3) == globals_copy[obj](3) - - assert Person.__module__ == __main__.__name__ - assert isinstance(person, Person) - assert person.age == globals_copy['person'].age - - assert issubclass(CalendarSubclass, Calendar) - assert isinstance(cal, CalendarSubclass) - assert cal.weekdays() == globals_copy['cal'].weekdays() - - assert selfref is __main__ - - except AssertionError as error: - error.args = (_error_line(error, obj, refimported),) - raise - -def test_session_main(refimported): - """test dump/load_module() for __main__, both in this process and in a subprocess""" - extra_objects = {} - if refimported: - # Test unpickleable imported object in main. - from sys import flags - extra_objects['flags'] = flags - - with TestNamespace(**extra_objects) as ns: - try: - # Test session loading in a new session. - dill.dump_module(session_file % refimported, refimported=refimported) - from dill.tests.__main__ import python, shell, sp - error = sp.call([python, __file__, '--child', str(refimported)], shell=shell) - if error: sys.exit(error) - finally: - with suppress(OSError): - os.remove(session_file % refimported) - - # Test session loading in the same session. - session_buffer = BytesIO() - dill.dump_module(session_buffer, refimported=refimported) - session_buffer.seek(0) - dill.load_module(session_buffer, module='__main__') - ns.backup['_test_objects'](__main__, ns.backup, refimported) - -def test_session_other(): - """test dump/load_module() for a module other than __main__""" - import test_classdef as module - atexit.register(_clean_up_cache, module) - module.selfref = module - dict_objects = [obj for obj in module.__dict__.keys() if not obj.startswith('__')] - - session_buffer = BytesIO() - dill.dump_module(session_buffer, module) - - for obj in dict_objects: - del module.__dict__[obj] - - session_buffer.seek(0) - dill.load_module(session_buffer, module) - - assert all(obj in module.__dict__ for obj in dict_objects) - assert module.selfref is module - -def test_runtime_module(): - from types import ModuleType - modname = '__runtime__' - runtime = ModuleType(modname) - runtime.x = 42 - - mod = dill.session._stash_modules(runtime) - if mod is not runtime: - print("There are objects to save by referenece that shouldn't be:", - mod.__dill_imported, mod.__dill_imported_as, mod.__dill_imported_top_level, - file=sys.stderr) - - # This is also for code coverage, tests the use case of dump_module(refimported=True) - # without imported objects in the namespace. It's a contrived example because - # even dill can't be in it. This should work after fixing #462. - session_buffer = BytesIO() - dill.dump_module(session_buffer, module=runtime, refimported=True) - session_dump = session_buffer.getvalue() - - # Pass a new runtime created module with the same name. - runtime = ModuleType(modname) # empty - return_val = dill.load_module(BytesIO(session_dump), module=runtime) - assert return_val is None - assert runtime.__name__ == modname - assert runtime.x == 42 - assert runtime not in sys.modules.values() - - # Pass nothing as main. load_module() must create it. - session_buffer.seek(0) - runtime = dill.load_module(BytesIO(session_dump)) - assert runtime.__name__ == modname - assert runtime.x == 42 - assert runtime not in sys.modules.values() - -def test_refimported_imported_as(): - import collections - import concurrent.futures - import types - import typing - mod = sys.modules['__test__'] = types.ModuleType('__test__') - dill.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) - mod.Dict = collections.UserDict # select by type - mod.AsyncCM = typing.AsyncContextManager # select by __module__ - mod.thread_exec = dill.executor # select by __module__ with regex - - session_buffer = BytesIO() - dill.dump_module(session_buffer, mod, refimported=True) - session_buffer.seek(0) - mod = dill.load(session_buffer) - del sys.modules['__test__'] - - assert set(mod.__dill_imported_as) == { - ('collections', 'UserDict', 'Dict'), - ('typing', 'AsyncContextManager', 'AsyncCM'), - ('dill', 'executor', 'thread_exec'), - } - -def test_load_module_asdict(): - with TestNamespace(): - session_buffer = BytesIO() - dill.dump_module(session_buffer) - - global empty, names, x, y - x = y = 0 # change x and create y - del empty - globals_state = globals().copy() - - session_buffer.seek(0) - main_vars = dill.load_module_asdict(session_buffer) - - assert main_vars is not globals() - assert globals() == globals_state - - assert main_vars['__name__'] == '__main__' - assert main_vars['names'] == names - assert main_vars['names'] is not names - assert main_vars['x'] != x - assert 'y' not in main_vars - assert 'empty' in main_vars - -if __name__ == '__main__': - test_session_main(refimported=False) - test_session_main(refimported=True) - test_session_other() - test_runtime_module() - test_refimported_imported_as() - test_load_module_asdict() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_source.py b/.venv/lib/python3.10/site-packages/dill/tests/test_source.py deleted file mode 100644 index 12b4519..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_source.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -from dill.source import getsource, getname, _wrap, getimport -from dill.source import importable -from dill._dill import IS_PYPY - -import sys -PY310b = 0x30a00b1 - -f = lambda x: x**2 -def g(x): return f(x) - x - -def h(x): - def g(x): return x - return g(x) - x - -class Foo(object): - def bar(self, x): - return x*x+x -_foo = Foo() - -def add(x,y): - return x+y - -# yes, same as 'f', but things are tricky when it comes to pointers -squared = lambda x:x**2 - -class Bar: - pass -_bar = Bar() - - # inspect.getsourcelines # dill.source.getblocks -def test_getsource(): - assert getsource(f) == 'f = lambda x: x**2\n' - assert getsource(g) == 'def g(x): return f(x) - x\n' - assert getsource(h) == 'def h(x):\n def g(x): return x\n return g(x) - x\n' - assert getname(f) == 'f' - assert getname(g) == 'g' - assert getname(h) == 'h' - assert _wrap(f)(4) == 16 - assert _wrap(g)(4) == 12 - assert _wrap(h)(4) == 0 - - assert getname(Foo) == 'Foo' - assert getname(Bar) == 'Bar' - assert getsource(Bar) == 'class Bar:\n pass\n' - assert getsource(Foo) == 'class Foo(object):\n def bar(self, x):\n return x*x+x\n' - #XXX: add getsource for _foo, _bar - -# test itself -def test_itself(): - assert getimport(getimport)=='from dill.source import getimport\n' - -# builtin functions and objects -def test_builtin(): - assert getimport(pow) == 'pow\n' - assert getimport(100) == '100\n' - assert getimport(True) == 'True\n' - assert getimport(pow, builtin=True) == 'from builtins import pow\n' - assert getimport(100, builtin=True) == '100\n' - assert getimport(True, builtin=True) == 'True\n' - # this is kinda BS... you can't import a None - assert getimport(None) == 'None\n' - assert getimport(None, builtin=True) == 'None\n' - - -# other imported functions -def test_imported(): - from math import sin - assert getimport(sin) == 'from math import sin\n' - -# interactively defined functions -def test_dynamic(): - assert getimport(add) == 'from %s import add\n' % __name__ - # interactive lambdas - assert getimport(squared) == 'from %s import squared\n' % __name__ - -# classes and class instances -def test_classes(): - from io import BytesIO as StringIO - y = "from _io import BytesIO\n" - x = y if (IS_PYPY or sys.hexversion >= PY310b) else "from io import BytesIO\n" - s = StringIO() - - assert getimport(StringIO) == x - assert getimport(s) == y - # interactively defined classes and class instances - assert getimport(Foo) == 'from %s import Foo\n' % __name__ - assert getimport(_foo) == 'from %s import Foo\n' % __name__ - - -# test importable -def test_importable(): - assert importable(add, source=False) == 'from %s import add\n' % __name__ - assert importable(squared, source=False) == 'from %s import squared\n' % __name__ - assert importable(Foo, source=False) == 'from %s import Foo\n' % __name__ - assert importable(Foo.bar, source=False) == 'from %s import bar\n' % __name__ - assert importable(_foo.bar, source=False) == 'from %s import bar\n' % __name__ - assert importable(None, source=False) == 'None\n' - assert importable(100, source=False) == '100\n' - - assert importable(add, source=True) == 'def add(x,y):\n return x+y\n' - assert importable(squared, source=True) == 'squared = lambda x:x**2\n' - assert importable(None, source=True) == 'None\n' - assert importable(Bar, source=True) == 'class Bar:\n pass\n' - assert importable(Foo, source=True) == 'class Foo(object):\n def bar(self, x):\n return x*x+x\n' - assert importable(Foo.bar, source=True) == 'def bar(self, x):\n return x*x+x\n' - assert importable(Foo.bar, source=False) == 'from %s import bar\n' % __name__ - assert importable(Foo.bar, alias='memo', source=False) == 'from %s import bar as memo\n' % __name__ - assert importable(Foo, alias='memo', source=False) == 'from %s import Foo as memo\n' % __name__ - assert importable(squared, alias='memo', source=False) == 'from %s import squared as memo\n' % __name__ - assert importable(squared, alias='memo', source=True) == 'memo = squared = lambda x:x**2\n' - assert importable(add, alias='memo', source=True) == 'def add(x,y):\n return x+y\n\nmemo = add\n' - assert importable(None, alias='memo', source=True) == 'memo = None\n' - assert importable(100, alias='memo', source=True) == 'memo = 100\n' - assert importable(add, builtin=True, source=False) == 'from %s import add\n' % __name__ - assert importable(squared, builtin=True, source=False) == 'from %s import squared\n' % __name__ - assert importable(Foo, builtin=True, source=False) == 'from %s import Foo\n' % __name__ - assert importable(Foo.bar, builtin=True, source=False) == 'from %s import bar\n' % __name__ - assert importable(_foo.bar, builtin=True, source=False) == 'from %s import bar\n' % __name__ - assert importable(None, builtin=True, source=False) == 'None\n' - assert importable(100, builtin=True, source=False) == '100\n' - - -def test_numpy(): - try: - import numpy as np - y = np.array - x = y([1,2,3]) - assert importable(x, source=False) == 'from numpy import array\narray([1, 2, 3])\n' - assert importable(y, source=False) == 'from %s import array\n' % y.__module__ - assert importable(x, source=True) == 'from numpy import array\narray([1, 2, 3])\n' - assert importable(y, source=True) == 'from %s import array\n' % y.__module__ - y = np.int64 - x = y(0) - assert importable(x, source=False) == 'from numpy import int64\nint64(0)\n' - assert importable(y, source=False) == 'from %s import int64\n' % y.__module__ - assert importable(x, source=True) == 'from numpy import int64\nint64(0)\n' - assert importable(y, source=True) == 'from %s import int64\n' % y.__module__ - y = np.bool_ - x = y(0) - import warnings - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', category=FutureWarning) - warnings.filterwarnings('ignore', category=DeprecationWarning) - if hasattr(np, 'bool'): b = 'bool_' if np.bool is bool else 'bool' - else: b = 'bool_' - assert importable(x, source=False) == 'from numpy import %s\n%s(False)\n' % (b,b) - assert importable(y, source=False) == 'from %s import %s\n' % (y.__module__,b) - assert importable(x, source=True) == 'from numpy import %s\n%s(False)\n' % (b,b) - assert importable(y, source=True) == 'from %s import %s\n' % (y.__module__,b) - except ImportError: pass - -#NOTE: if before getimport(pow), will cause pow to throw AssertionError -def test_foo(): - assert importable(_foo, source=True).startswith("import dill\nclass Foo(object):\n def bar(self, x):\n return x*x+x\ndill.loads(") - -if __name__ == '__main__': - test_getsource() - test_itself() - test_builtin() - test_imported() - test_dynamic() - test_classes() - test_importable() - test_numpy() - test_foo() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_sources.py b/.venv/lib/python3.10/site-packages/dill/tests/test_sources.py deleted file mode 100644 index 478b967..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_sources.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @uqfoundation) -# Copyright (c) 2024-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE -""" -check that dill.source performs as expected with changes to locals in 3.13.0b1 -see: https://github.com/python/cpython/issues/118888 -""" -# repeat functions from test_source.py -f = lambda x: x**2 -def g(x): return f(x) - x - -def h(x): - def g(x): return x - return g(x) - x - -class Foo(object): - def bar(self, x): - return x*x+x -_foo = Foo() - -def add(x,y): - return x+y - -squared = lambda x:x**2 - -class Bar: - pass -_bar = Bar() - -# repeat, but from test_source.py -import test_source as ts - -# test objects created in other test modules -import test_mixins as tm - -import dill.source as ds - - -def test_isfrommain(): - assert ds.isfrommain(add) == True - assert ds.isfrommain(squared) == True - assert ds.isfrommain(Bar) == True - assert ds.isfrommain(_bar) == True - assert ds.isfrommain(ts.add) == False - assert ds.isfrommain(ts.squared) == False - assert ds.isfrommain(ts.Bar) == False - assert ds.isfrommain(ts._bar) == False - assert ds.isfrommain(tm.quad) == False - assert ds.isfrommain(tm.double_add) == False - assert ds.isfrommain(tm.quadratic) == False - assert ds.isdynamic(add) == False - assert ds.isdynamic(squared) == False - assert ds.isdynamic(ts.add) == False - assert ds.isdynamic(ts.squared) == False - assert ds.isdynamic(tm.double_add) == False - assert ds.isdynamic(tm.quadratic) == False - - -def test_matchlambda(): - assert ds._matchlambda(f, 'f = lambda x: x**2\n') - assert ds._matchlambda(squared, 'squared = lambda x:x**2\n') - assert ds._matchlambda(ts.f, 'f = lambda x: x**2\n') - assert ds._matchlambda(ts.squared, 'squared = lambda x:x**2\n') - - -def test_findsource(): - lines, lineno = ds.findsource(add) - assert lines[lineno] == 'def add(x,y):\n' - lines, lineno = ds.findsource(ts.add) - assert lines[lineno] == 'def add(x,y):\n' - lines, lineno = ds.findsource(squared) - assert lines[lineno] == 'squared = lambda x:x**2\n' - lines, lineno = ds.findsource(ts.squared) - assert lines[lineno] == 'squared = lambda x:x**2\n' - lines, lineno = ds.findsource(Bar) - assert lines[lineno] == 'class Bar:\n' - lines, lineno = ds.findsource(ts.Bar) - assert lines[lineno] == 'class Bar:\n' - lines, lineno = ds.findsource(_bar) - assert lines[lineno] == 'class Bar:\n' - lines, lineno = ds.findsource(ts._bar) - assert lines[lineno] == 'class Bar:\n' - lines, lineno = ds.findsource(tm.quad) - assert lines[lineno] == 'def quad(a=1, b=1, c=0):\n' - lines, lineno = ds.findsource(tm.double_add) - assert lines[lineno] == ' def func(*args, **kwds):\n' - lines, lineno = ds.findsource(tm.quadratic) - assert lines[lineno] == ' def dec(f):\n' - - -def test_getsourcelines(): - assert ''.join(ds.getsourcelines(add)[0]) == 'def add(x,y):\n return x+y\n' - assert ''.join(ds.getsourcelines(ts.add)[0]) == 'def add(x,y):\n return x+y\n' - assert ''.join(ds.getsourcelines(squared)[0]) == 'squared = lambda x:x**2\n' - assert ''.join(ds.getsourcelines(ts.squared)[0]) == 'squared = lambda x:x**2\n' - assert ''.join(ds.getsourcelines(Bar)[0]) == 'class Bar:\n pass\n' - assert ''.join(ds.getsourcelines(ts.Bar)[0]) == 'class Bar:\n pass\n' - assert ''.join(ds.getsourcelines(_bar)[0]) == 'class Bar:\n pass\n' #XXX: ? - assert ''.join(ds.getsourcelines(ts._bar)[0]) == 'class Bar:\n pass\n' #XXX: ? - assert ''.join(ds.getsourcelines(tm.quad)[0]) == 'def quad(a=1, b=1, c=0):\n inverted = [False]\n def invert():\n inverted[0] = not inverted[0]\n def dec(f):\n def func(*args, **kwds):\n x = f(*args, **kwds)\n if inverted[0]: x = -x\n return a*x**2 + b*x + c\n func.__wrapped__ = f\n func.invert = invert\n func.inverted = inverted\n return func\n return dec\n' - assert ''.join(ds.getsourcelines(tm.quadratic)[0]) == ' def dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n' - assert ''.join(ds.getsourcelines(tm.quadratic, lstrip=True)[0]) == 'def dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n' - assert ''.join(ds.getsourcelines(tm.quadratic, enclosing=True)[0]) == 'def quad_factory(a=1,b=1,c=0):\n def dec(f):\n def func(*args,**kwds):\n fx = f(*args,**kwds)\n return a*fx**2 + b*fx + c\n return func\n return dec\n' - assert ''.join(ds.getsourcelines(tm.double_add)[0]) == ' def func(*args, **kwds):\n x = f(*args, **kwds)\n if inverted[0]: x = -x\n return a*x**2 + b*x + c\n' - assert ''.join(ds.getsourcelines(tm.double_add, enclosing=True)[0]) == 'def quad(a=1, b=1, c=0):\n inverted = [False]\n def invert():\n inverted[0] = not inverted[0]\n def dec(f):\n def func(*args, **kwds):\n x = f(*args, **kwds)\n if inverted[0]: x = -x\n return a*x**2 + b*x + c\n func.__wrapped__ = f\n func.invert = invert\n func.inverted = inverted\n return func\n return dec\n' - - -def test_indent(): - assert ds.outdent(''.join(ds.getsourcelines(tm.quadratic)[0])) == ''.join(ds.getsourcelines(tm.quadratic, lstrip=True)[0]) - assert ds.indent(''.join(ds.getsourcelines(tm.quadratic, lstrip=True)[0]), 2) == ''.join(ds.getsourcelines(tm.quadratic)[0]) - - -def test_dumpsource(): - local = {} - exec(ds.dumpsource(add, alias='raw'), {}, local) - exec(ds.dumpsource(ts.add, alias='mod'), {}, local) - assert local['raw'](1,2) == local['mod'](1,2) - exec(ds.dumpsource(squared, alias='raw'), {}, local) - exec(ds.dumpsource(ts.squared, alias='mod'), {}, local) - assert local['raw'](3) == local['mod'](3) - assert ds._wrap(add)(1,2) == ds._wrap(ts.add)(1,2) - assert ds._wrap(squared)(3) == ds._wrap(ts.squared)(3) - - -def test_name(): - assert ds._namespace(add) == ds.getname(add, fqn=True).split('.') - assert ds._namespace(ts.add) == ds.getname(ts.add, fqn=True).split('.') - assert ds._namespace(squared) == ds.getname(squared, fqn=True).split('.') - assert ds._namespace(ts.squared) == ds.getname(ts.squared, fqn=True).split('.') - assert ds._namespace(Bar) == ds.getname(Bar, fqn=True).split('.') - assert ds._namespace(ts.Bar) == ds.getname(ts.Bar, fqn=True).split('.') - assert ds._namespace(tm.quad) == ds.getname(tm.quad, fqn=True).split('.') - #XXX: the following also works, however behavior may be wrong for nested functions - #assert ds._namespace(tm.double_add) == ds.getname(tm.double_add, fqn=True).split('.') - #assert ds._namespace(tm.quadratic) == ds.getname(tm.quadratic, fqn=True).split('.') - assert ds.getname(add) == 'add' - assert ds.getname(ts.add) == 'add' - assert ds.getname(squared) == 'squared' - assert ds.getname(ts.squared) == 'squared' - assert ds.getname(Bar) == 'Bar' - assert ds.getname(ts.Bar) == 'Bar' - assert ds.getname(tm.quad) == 'quad' - assert ds.getname(tm.double_add) == 'func' #XXX: ? - assert ds.getname(tm.quadratic) == 'dec' #XXX: ? - - -def test_getimport(): - local = {} - exec(ds.getimport(add, alias='raw'), {}, local) - exec(ds.getimport(ts.add, alias='mod'), {}, local) - assert local['raw'](1,2) == local['mod'](1,2) - exec(ds.getimport(squared, alias='raw'), {}, local) - exec(ds.getimport(ts.squared, alias='mod'), {}, local) - assert local['raw'](3) == local['mod'](3) - exec(ds.getimport(Bar, alias='raw'), {}, local) - exec(ds.getimport(ts.Bar, alias='mod'), {}, local) - assert ds.getname(local['raw']) == ds.getname(local['mod']) - exec(ds.getimport(tm.quad, alias='mod'), {}, local) - assert local['mod']()(sum)([1,2,3]) == tm.quad()(sum)([1,2,3]) - #FIXME: wrong results for nested functions (e.g. tm.double_add, tm.quadratic) - - -def test_importable(): - assert ds.importable(add, source=False) == ds.getimport(add) - assert ds.importable(add) == ds.getsource(add) - assert ds.importable(squared, source=False) == ds.getimport(squared) - assert ds.importable(squared) == ds.getsource(squared) - assert ds.importable(Bar, source=False) == ds.getimport(Bar) - assert ds.importable(Bar) == ds.getsource(Bar) - assert ds.importable(ts.add) == ds.getimport(ts.add) - assert ds.importable(ts.add, source=True) == ds.getsource(ts.add) - assert ds.importable(ts.squared) == ds.getimport(ts.squared) - assert ds.importable(ts.squared, source=True) == ds.getsource(ts.squared) - assert ds.importable(ts.Bar) == ds.getimport(ts.Bar) - assert ds.importable(ts.Bar, source=True) == ds.getsource(ts.Bar) - - -if __name__ == '__main__': - test_isfrommain() - test_matchlambda() - test_findsource() - test_getsourcelines() - test_indent() - test_dumpsource() - test_name() - test_getimport() - test_importable() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_temp.py b/.venv/lib/python3.10/site-packages/dill/tests/test_temp.py deleted file mode 100644 index e9201f4..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_temp.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 The Uncertainty Quantification Foundation. -# License: 3-clause BSD. The full license text is available at: -# - https://github.com/uqfoundation/dill/blob/master/LICENSE - -import sys -from dill.temp import dump, dump_source, dumpIO, dumpIO_source -from dill.temp import load, load_source, loadIO, loadIO_source -WINDOWS = sys.platform[:3] == 'win' - - -f = lambda x: x**2 -x = [1,2,3,4,5] - -# source code to tempfile -def test_code_to_tempfile(): - if not WINDOWS: #see: https://bugs.python.org/issue14243 - pyfile = dump_source(f, alias='_f') - _f = load_source(pyfile) - assert _f(4) == f(4) - -# source code to stream -def test_code_to_stream(): - pyfile = dumpIO_source(f, alias='_f') - _f = loadIO_source(pyfile) - assert _f(4) == f(4) - -# pickle to tempfile -def test_pickle_to_tempfile(): - if not WINDOWS: #see: https://bugs.python.org/issue14243 - dumpfile = dump(x) - _x = load(dumpfile) - assert _x == x - -# pickle to stream -def test_pickle_to_stream(): - dumpfile = dumpIO(x) - _x = loadIO(dumpfile) - assert _x == x - -### now testing the objects ### -f = lambda x: x**2 -def g(x): return f(x) - x - -def h(x): - def g(x): return x - return g(x) - x - -class Foo(object): - def bar(self, x): - return x*x+x -_foo = Foo() - -def add(x,y): - return x+y - -# yes, same as 'f', but things are tricky when it comes to pointers -squared = lambda x:x**2 - -class Bar: - pass -_bar = Bar() - - -# test function-type objects that take 2 args -def test_two_arg_functions(): - for obj in [add]: - pyfile = dumpIO_source(obj, alias='_obj') - _obj = loadIO_source(pyfile) - assert _obj(4,2) == obj(4,2) - -# test function-type objects that take 1 arg -def test_one_arg_functions(): - for obj in [g, h, squared]: - pyfile = dumpIO_source(obj, alias='_obj') - _obj = loadIO_source(pyfile) - assert _obj(4) == obj(4) - -# test instance-type objects -#for obj in [_bar, _foo]: -# pyfile = dumpIO_source(obj, alias='_obj') -# _obj = loadIO_source(pyfile) -# assert type(_obj) == type(obj) - -# test the rest of the objects -def test_the_rest(): - for obj in [Bar, Foo, Foo.bar, _foo.bar]: - pyfile = dumpIO_source(obj, alias='_obj') - _obj = loadIO_source(pyfile) - assert _obj.__name__ == obj.__name__ - - -if __name__ == '__main__': - test_code_to_tempfile() - test_code_to_stream() - test_pickle_to_tempfile() - test_pickle_to_stream() - test_two_arg_functions() - test_one_arg_functions() - test_the_rest() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_threads.py b/.venv/lib/python3.10/site-packages/dill/tests/test_threads.py deleted file mode 100644 index debc5e1..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_threads.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2024-2025 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 -dill.settings['recurse'] = True - - -def test_new_thread(): - import threading - t = threading.Thread() - t_ = dill.copy(t) - assert t.is_alive() == t_.is_alive() - for i in ['daemon','name','ident','native_id']: - if hasattr(t, i): - assert getattr(t, i) == getattr(t_, i) - -def test_run_thread(): - import threading - t = threading.Thread() - t.start() - t_ = dill.copy(t) - assert t.is_alive() == t_.is_alive() - for i in ['daemon','name','ident','native_id']: - if hasattr(t, i): - assert getattr(t, i) == getattr(t_, i) - -def test_join_thread(): - import threading - t = threading.Thread() - t.start() - t.join() - t_ = dill.copy(t) - assert t.is_alive() == t_.is_alive() - for i in ['daemon','name','ident','native_id']: - if hasattr(t, i): - assert getattr(t, i) == getattr(t_, i) - - -if __name__ == '__main__': - test_new_thread() - test_run_thread() - test_join_thread() diff --git a/.venv/lib/python3.10/site-packages/dill/tests/test_weakref.py b/.venv/lib/python3.10/site-packages/dill/tests/test_weakref.py deleted file mode 100644 index 378a21a..0000000 --- a/.venv/lib/python3.10/site-packages/dill/tests/test_weakref.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# -# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) -# Copyright (c) 2008-2016 California Institute of Technology. -# Copyright (c) 2016-2025 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 -dill.settings['recurse'] = True -import weakref - -class _class: - def _method(self): - pass - -class _callable_class: - def __call__(self): - pass - -def _function(): - pass - - -def test_weakref(): - o = _class() - oc = _callable_class() - f = _function - x = _class - - # ReferenceType - r = weakref.ref(o) - d_r = weakref.ref(_class()) - fr = weakref.ref(f) - xr = weakref.ref(x) - - # ProxyType - p = weakref.proxy(o) - d_p = weakref.proxy(_class()) - - # CallableProxyType - cp = weakref.proxy(oc) - d_cp = weakref.proxy(_callable_class()) - fp = weakref.proxy(f) - xp = weakref.proxy(x) - - objlist = [r,d_r,fr,xr, p,d_p, cp,d_cp,fp,xp] - #dill.detect.trace(True) - - for obj in objlist: - res = dill.detect.errors(obj) - if res: - print ("%r:\n %s" % (obj, res)) - # else: - # print ("PASS: %s" % obj) - assert not res - -def test_dictproxy(): - from dill._dill import DictProxyType - try: - m = DictProxyType({"foo": "bar"}) - except Exception: - m = type.__dict__ - mp = dill.copy(m) - assert mp.items() == m.items() - - -if __name__ == '__main__': - test_weakref() - from dill._dill import IS_PYPY - if not IS_PYPY: - test_dictproxy() diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA deleted file mode 100644 index 1785ba1..0000000 --- a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/METADATA +++ /dev/null @@ -1,375 +0,0 @@ -Metadata-Version: 2.4 -Name: isort -Version: 7.0.0 -Summary: A Python utility / library to sort Python imports. -Project-URL: Homepage, https://pycqa.github.io/isort/index.html -Project-URL: Documentation, https://pycqa.github.io/isort/index.html -Project-URL: Repository, https://github.com/PyCQA/isort -Project-URL: Changelog, https://github.com/PyCQA/isort/releases -Author-email: Timothy Crosley , staticdev -License-Expression: MIT -License-File: LICENSE -Keywords: Clean,Imports,Lint,Refactor,Sort -Classifier: Development Status :: 6 - Mature -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Natural Language :: English -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Utilities -Requires-Python: >=3.10.0 -Provides-Extra: colors -Requires-Dist: colorama; extra == 'colors' -Provides-Extra: plugins -Requires-Dist: setuptools; extra == 'plugins' -Description-Content-Type: text/markdown - -[![isort - isort your imports, so you don't have to.](https://raw.githubusercontent.com/pycqa/isort/main/art/logo_large.png)](https://pycqa.github.io/isort/) - ------------------------------------------------------------------------- - -[![PyPI version](https://badge.fury.io/py/isort.svg)](https://badge.fury.io/py/isort) -[![Python Version](https://img.shields.io/pypi/pyversions/isort)][pypi status] -[![Test](https://github.com/PyCQA/isort/actions/workflows/test.yml/badge.svg)](https://github.com/PyCQA/isort/actions/workflows/test.yml) -[![Lint](https://github.com/PyCQA/isort/actions/workflows/lint.yml/badge.svg)](https://github.com/PyCQA/isort/actions/workflows/lint.yml) -[![Code coverage Status](https://codecov.io/gh/pycqa/isort/branch/main/graph/badge.svg)](https://codecov.io/gh/pycqa/isort) -[![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://pypi.org/project/isort/) -[![Downloads](https://pepy.tech/badge/isort)](https://pepy.tech/project/isort) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) -[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) -[![DeepSource](https://static.deepsource.io/deepsource-badge-light-mini.svg)](https://deepsource.io/gh/pycqa/isort/?ref=repository-badge) - -[pypi status]: https://pypi.org/project/isort/ -_________________ - -[Read Latest Documentation](https://pycqa.github.io/isort/) - [Browse GitHub Code Repository](https://github.com/pycqa/isort/) -_________________ - -isort your imports, so you don't have to. - -isort is a Python utility / library to sort imports alphabetically and -automatically separate into sections and by type. It provides a command line -utility, Python library and [plugins for various -editors](https://github.com/pycqa/isort/wiki/isort-Plugins) to -quickly sort all your imports. It requires Python 3.9+ to run but -supports formatting Python 2 code too. - -- [Try isort now from your browser!](https://pycqa.github.io/isort/docs/quick_start/0.-try.html) -- [Using black? See the isort and black compatibility guide.](https://pycqa.github.io/isort/docs/configuration/black_compatibility.html) -- [isort has official support for pre-commit!](https://pycqa.github.io/isort/docs/configuration/pre-commit.html) - -![Example Usage](https://raw.github.com/pycqa/isort/main/example.gif) - -Before isort: - -```python -from my_lib import Object - -import os - -from my_lib import Object3 - -from my_lib import Object2 - -import sys - -from third_party import lib15, lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, lib9, lib10, lib11, lib12, lib13, lib14 - -import sys - -from __future__ import absolute_import - -from third_party import lib3 - -print("Hey") -print("yo") -``` - -After isort: - -```python -from __future__ import absolute_import - -import os -import sys - -from third_party import (lib1, lib2, lib3, lib4, lib5, lib6, lib7, lib8, - lib9, lib10, lib11, lib12, lib13, lib14, lib15) - -from my_lib import Object, Object2, Object3 - -print("Hey") -print("yo") -``` - -## Installing isort - -Installing isort is as simple as: - -```bash -pip install isort -``` - -## Using isort - -**From the command line**: - -To run on specific files: - -```bash -isort mypythonfile.py mypythonfile2.py -``` - -To apply recursively: - -```bash -isort . -``` - -If [globstar](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html) -is enabled, `isort .` is equivalent to: - -```bash -isort **/*.py -``` - -To view proposed changes without applying them: - -```bash -isort mypythonfile.py --diff -``` - -Finally, to atomically run isort against a project, only applying -changes if they don't introduce syntax errors: - -```bash -isort --atomic . -``` - -(Note: this is disabled by default, as it prevents isort from -running against code written using a different version of Python.) - -**From within Python**: - -```python -import isort - -isort.file("pythonfile.py") -``` - -or: - -```python -import isort - -sorted_code = isort.code("import b\nimport a\n") -``` - -## Installing isort's for your preferred text editor - -Several plugins have been written that enable to use isort from within a -variety of text-editors. You can find a full list of them [on the isort -wiki](https://github.com/pycqa/isort/wiki/isort-Plugins). -Additionally, I will enthusiastically accept pull requests that include -plugins for other text editors and add documentation for them as I am -notified. - -## Multi line output modes - -You will notice above the \"multi\_line\_output\" setting. This setting -defines how from imports wrap when they extend past the line\_length -limit and has [12 possible settings](https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes.html). - -## Indentation - -To change the how constant indents appear - simply change the -indent property with the following accepted formats: - -- Number of spaces you would like. For example: 4 would cause standard - 4 space indentation. -- Tab -- A verbatim string with quotes around it. - -For example: - -```python -" " -``` - -is equivalent to 4. - -For the import styles that use parentheses, you can control whether or -not to include a trailing comma after the last import with the -`include_trailing_comma` option (defaults to `False`). - -## Intelligently Balanced Multi-line Imports - -As of isort 3.1.0 support for balanced multi-line imports has been -added. With this enabled isort will dynamically change the import length -to the one that produces the most balanced grid, while staying below the -maximum import length defined. - -Example: - -```python -from __future__ import (absolute_import, division, - print_function, unicode_literals) -``` - -Will be produced instead of: - -```python -from __future__ import (absolute_import, division, print_function, - unicode_literals) -``` - -To enable this set `balanced_wrapping` to `True` in your config or pass -the `-e` option into the command line utility. - -## Custom Sections and Ordering - -isort provides configuration options to change almost every aspect of how -imports are organized, ordered, or grouped together in sections. - -[Click here](https://pycqa.github.io/isort/docs/configuration/custom_sections_and_ordering.html) for an overview of all these options. - -## Skip processing of imports (outside of configuration) - -To make isort ignore a single import simply add a comment at the end of -the import line containing the text `isort:skip`: - -```python -import module # isort:skip -``` - -or: - -```python -from xyz import (abc, # isort:skip - yo, - hey) -``` - -To make isort skip an entire file simply add `isort:skip_file` to the -module's doc string: - -```python -""" my_module.py - Best module ever - - isort:skip_file -""" - -import b -import a -``` - -## Adding or removing an import from multiple files - -isort can be ran or configured to add / remove imports automatically. - -[See a complete guide here.](https://pycqa.github.io/isort/docs/configuration/add_or_remove_imports.html) - -## Using isort to verify code - -The `--check-only` option -------------------------- - -isort can also be used to verify that code is correctly formatted -by running it with `-c`. Any files that contain incorrectly sorted -and/or formatted imports will be outputted to `stderr`. - -```bash -isort **/*.py -c -v - -SUCCESS: /home/timothy/Projects/Open_Source/isort/isort_kate_plugin.py Everything Looks Good! -ERROR: /home/timothy/Projects/Open_Source/isort/isort/isort.py Imports are incorrectly sorted. -``` - -One great place this can be used is with a pre-commit git hook, such as -this one by \@acdha: - - - -This can help to ensure a certain level of code quality throughout a -project. - -## Git hook - -isort provides a hook function that can be integrated into your Git -pre-commit script to check Python code before committing. - -[More info here.](https://pycqa.github.io/isort/docs/configuration/git_hook.html) - -## Setuptools integration - -Upon installation, isort enables a `setuptools` command that checks -Python files declared by your project. - -[More info here.](https://pycqa.github.io/isort/docs/configuration/setuptools_integration.html) - -## Spread the word - -[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) - -Place this badge at the top of your repository to let others know your project uses isort. - -For README.md: - -```markdown -[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) -``` - -Or README.rst: - -```rst -.. image:: https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336 - :target: https://pycqa.github.io/isort/ -``` - -## Security contact information - -To report a security vulnerability, please use the [Tidelift security -contact](https://tidelift.com/security). Tidelift will coordinate the -fix and disclosure. - -## Why isort? - -isort simply stands for import sort. It was originally called -"sortImports" however I got tired of typing the extra characters and -came to the realization camelCase is not pythonic. - -I wrote isort because in an organization I used to work in the manager -came in one day and decided all code must have alphabetically sorted -imports. The code base was huge - and he meant for us to do it by hand. -However, being a programmer - I\'m too lazy to spend 8 hours mindlessly -performing a function, but not too lazy to spend 16 hours automating it. -I was given permission to open source sortImports and here we are :) - ------------------------------------------------------------------------- - -[Get professionally supported isort with the Tidelift -Subscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme) - -Professional support for isort is available as part of the [Tidelift -Subscription](https://tidelift.com/subscription/pkg/pypi-isort?utm_source=pypi-isort&utm_medium=referral&utm_campaign=readme). -Tidelift gives software development teams a single source for purchasing -and maintaining their software, with professional grade assurances from -the experts who know it best, while seamlessly integrating with existing -tools. - ------------------------------------------------------------------------- - -Thanks and I hope you find isort useful! - -~Timothy Crosley diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD deleted file mode 100644 index 22b6e9d..0000000 --- a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/RECORD +++ /dev/null @@ -1,101 +0,0 @@ -../../../bin/isort,sha256=wt-xKEp2aF5_QNZjWj3hn4izHD2Cu-kLJ_5kHN8LDmY,226 -../../../bin/isort-identify-imports,sha256=EZhp_yvp4eekQ76zoD9_yhWd8VUVpWA8Qx1XZoiX0lU,260 -isort-7.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -isort-7.0.0.dist-info/METADATA,sha256=nm4sPm5TimI06E1lXwKPjPWDHNTzJ1LVFn93VYM6NbQ,11986 -isort-7.0.0.dist-info/RECORD,, -isort-7.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 -isort-7.0.0.dist-info/entry_points.txt,sha256=3EUwrc2fZP9efifU_6TBNChjhWtdYNaSBga6R5VfiSs,169 -isort-7.0.0.dist-info/licenses/LICENSE,sha256=BjKUABw9Uj26y6ud1UrCKZgnVsyvWSylMkCysM3YIGU,1089 -isort/__init__.py,sha256=izMCmePBol7NDXEMXZvMEXCvZ_Rfzli-kt6dOilU1N0,872 -isort/__main__.py,sha256=iK0trzN9CCXpQX-XPZDZ9JVkm2Lc0q0oiAgsa6FkJb4,36 -isort/__pycache__/__init__.cpython-310.pyc,, -isort/__pycache__/__main__.cpython-310.pyc,, -isort/__pycache__/_version.cpython-310.pyc,, -isort/__pycache__/api.cpython-310.pyc,, -isort/__pycache__/comments.cpython-310.pyc,, -isort/__pycache__/core.cpython-310.pyc,, -isort/__pycache__/exceptions.cpython-310.pyc,, -isort/__pycache__/files.cpython-310.pyc,, -isort/__pycache__/format.cpython-310.pyc,, -isort/__pycache__/hooks.cpython-310.pyc,, -isort/__pycache__/identify.cpython-310.pyc,, -isort/__pycache__/io.cpython-310.pyc,, -isort/__pycache__/literal.cpython-310.pyc,, -isort/__pycache__/logo.cpython-310.pyc,, -isort/__pycache__/main.cpython-310.pyc,, -isort/__pycache__/output.cpython-310.pyc,, -isort/__pycache__/parse.cpython-310.pyc,, -isort/__pycache__/place.cpython-310.pyc,, -isort/__pycache__/profiles.cpython-310.pyc,, -isort/__pycache__/sections.cpython-310.pyc,, -isort/__pycache__/settings.cpython-310.pyc,, -isort/__pycache__/setuptools_commands.cpython-310.pyc,, -isort/__pycache__/sorting.cpython-310.pyc,, -isort/__pycache__/utils.cpython-310.pyc,, -isort/__pycache__/wrap.cpython-310.pyc,, -isort/__pycache__/wrap_modes.cpython-310.pyc,, -isort/_vendored/tomli/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072 -isort/_vendored/tomli/__init__.py,sha256=Y3N65pvphV_EF4k2qKiq_vYcohIUHhT05GzdRc0TOy8,213 -isort/_vendored/tomli/__pycache__/__init__.cpython-310.pyc,, -isort/_vendored/tomli/__pycache__/_parser.cpython-310.pyc,, -isort/_vendored/tomli/__pycache__/_re.cpython-310.pyc,, -isort/_vendored/tomli/_parser.py,sha256=ZbnCfybF5Assq8i1hPmIhP0ey1_u9BT5UloqDdCTyew,21397 -isort/_vendored/tomli/_re.py,sha256=3r6TD3gNGFjgOsfpy8aLpxgvasL__pvaN2m1R5DTxeQ,2833 -isort/_vendored/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26 -isort/_version.py,sha256=pXTtYi-S-p8e00o2Ad-PNREL9wAQaPgQzk_c_jndLOw,72 -isort/api.py,sha256=RkB3sLfVhSEiBEsFkztUTuwOdaegqlgHT83Ips17vFY,26280 -isort/comments.py,sha256=cFvf-vofTEz3neRY5mQa9jwVb3bY_QRlU8G8f9vTvw4,887 -isort/core.py,sha256=mcHPy9ds8ijyL1sX02CktPJ1-PnPaiCgumrCoMrwDRI,22684 -isort/deprecated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -isort/deprecated/__pycache__/__init__.cpython-310.pyc,, -isort/deprecated/__pycache__/finders.cpython-310.pyc,, -isort/deprecated/finders.py,sha256=tbDwxTz-xbUS5C1lEi6KB7lif9IkXfNUf8VEY7dlRHQ,14176 -isort/exceptions.py,sha256=v3S4LVEQ0QOQK054fEnrUcJyJBTRPmkrrjrWMh1sHBY,7008 -isort/files.py,sha256=EJm_fmPxOqX8-ZFvvXR7Vas6BQ3Tzd-u1LnXuKlhrKg,1611 -isort/format.py,sha256=pY8zyf2Eh_o9h26_re-f8NxoSd00sKjbrjyNaO5yWsg,5455 -isort/hooks.py,sha256=9KJoPNALP1MIRrvj-oxRsksl04Ezam93Yec0FAJP5QM,3268 -isort/identify.py,sha256=6_SAcXcWVcOueuC1Yr0wV5OeKlbyg-GAlqpLLpG0bBE,8342 -isort/io.py,sha256=oG_-VRTBJn54kmlmtxBE_ix6wloWDnN5rnlLoj4WnpE,2219 -isort/literal.py,sha256=B48IZqL5gZWtkuN6gEb__kw_mP3-GUrC2auqSOiOoIg,3699 -isort/logo.py,sha256=cL3al79O7O0G2viqRMRfBPp0qtRZmJw2nHSCZw8XWdQ,388 -isort/main.py,sha256=zqAFDpJ7RlFVNjS9d9QgQRFiEEE7sID724F0dO4uJZQ,47137 -isort/output.py,sha256=IrxpyGnzQYYsFh1cMBAb6-2dMpfb7Q-EOQkh_MsFWvg,28631 -isort/parse.py,sha256=rKLrGg4D1UKoS1svk3ARWhKnqbAnq-qGuM2ZeFLLo-E,25519 -isort/place.py,sha256=7sQ2k12AI3MXebW1cDDOghC0QEh1BZXmXGWNxjcFoIg,5137 -isort/profiles.py,sha256=7vCdA-KVBLL2dr0o2ZMMV-yZbJYhMlwv9QKfCpLoFiw,2292 -isort/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -isort/sections.py,sha256=e3fmTIPb5xzeOoiaHEoKNcSEx1qNja_Z2beuJ_0cjVE,272 -isort/settings.py,sha256=Uq38k0caWCKzJaaSr3Jof7KB97oJ8nU3D_igD7al-8Y,35637 -isort/setuptools_commands.py,sha256=Haxx62_HNqZONZyc4pS5VixILKaME4mhioBh--1CPqs,2383 -isort/sorting.py,sha256=2TiXU3uHhPCIeXlDf9LslN9AaUnQFOQwsY4s9nlmE-U,4495 -isort/stdlibs/__init__.py,sha256=qOZAOAgHwqSKmUe87NzCZc9xI9GWPhRUE_Ot1RidpVw,288 -isort/stdlibs/__pycache__/__init__.cpython-310.pyc,, -isort/stdlibs/__pycache__/all.cpython-310.pyc,, -isort/stdlibs/__pycache__/py2.cpython-310.pyc,, -isort/stdlibs/__pycache__/py27.cpython-310.pyc,, -isort/stdlibs/__pycache__/py3.cpython-310.pyc,, -isort/stdlibs/__pycache__/py310.cpython-310.pyc,, -isort/stdlibs/__pycache__/py311.cpython-310.pyc,, -isort/stdlibs/__pycache__/py312.cpython-310.pyc,, -isort/stdlibs/__pycache__/py313.cpython-310.pyc,, -isort/stdlibs/__pycache__/py314.cpython-310.pyc,, -isort/stdlibs/__pycache__/py36.cpython-310.pyc,, -isort/stdlibs/__pycache__/py37.cpython-310.pyc,, -isort/stdlibs/__pycache__/py38.cpython-310.pyc,, -isort/stdlibs/__pycache__/py39.cpython-310.pyc,, -isort/stdlibs/all.py,sha256=n8Es1WK6UlupYyVvf1PDjGbionqix-afC3LkY8nzTcw,57 -isort/stdlibs/py2.py,sha256=dTgWTa7ggz1cwN8fuI9eIs9-5nTmkRxG_uO61CGwfXI,41 -isort/stdlibs/py27.py,sha256=QriKfttNSHsjaRtDfR5WXytjzf7Xi7p9lxiOOcmA2JM,4504 -isort/stdlibs/py3.py,sha256=e1Y2e7UjCe8NVJWSN75hr3KsG2DhFI2bvVGrL-Hqqm0,251 -isort/stdlibs/py310.py,sha256=eSmafU9DNrMhXpzgnJQs9DHqxjXU6bKWCSodw4H7GXM,3440 -isort/stdlibs/py311.py,sha256=tOI3W9oHIaelXuXhHHYmPP7Put83R0s4FDFyq-_Y4vU,3441 -isort/stdlibs/py312.py,sha256=gTInIvuBpNzWXsrXAuOwzib0BKumEPP6AsF9ed9AYdM,3368 -isort/stdlibs/py313.py,sha256=qCQF8fqOVwemGdssKq5dZ3P_SLqKjBrlF4VvRCcKUgo,3095 -isort/stdlibs/py314.py,sha256=XLtbO3Egu-DqJXXo47TAUWeD7LRihxula_nfVV1gVeE,3116 -isort/stdlibs/py36.py,sha256=iuXIDLcFrSviMMSOP4PoKWCG5BveMnZbFravpduSUss,3310 -isort/stdlibs/py37.py,sha256=dLxxRerCvb4O9vrifTg5KWgO0L3a6AQB13haK_tSBRw,3334 -isort/stdlibs/py38.py,sha256=kGTxrw7fgCwgnaSdQNcuUVgOQL3A0EOiNpjPvm6QCvI,3455 -isort/stdlibs/py39.py,sha256=z5gwSoKVw6i9G5Pib8SRN0XSZjyPsecdhhKpTUtGXxU,3464 -isort/utils.py,sha256=_gc-gWRk4TIAfrlHT0XoL8TP7BJ8mKofwkbVBLfCTnE,2441 -isort/wrap.py,sha256=W7WUga954p9gao4Je2lgrkHALTyKek1nGlx4RRiawnU,6381 -isort/wrap_modes.py,sha256=KFwm6Xo48iy88Tzufu6HdzDtsZjSi9_wvi7oRN7iKHY,13462 diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL deleted file mode 100644 index 12228d4..0000000 --- a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.27.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt deleted file mode 100644 index fb42967..0000000 --- a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/entry_points.txt +++ /dev/null @@ -1,6 +0,0 @@ -[console_scripts] -isort = isort.main:main -isort-identify-imports = isort.main:identify_imports_main - -[distutils.commands] -isort = isort.setuptools_commands:ISortCommand diff --git a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE b/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE deleted file mode 100644 index b5083a5..0000000 --- a/.venv/lib/python3.10/site-packages/isort-7.0.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Timothy Edmund Crosley - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/.venv/lib/python3.10/site-packages/isort/__init__.py b/.venv/lib/python3.10/site-packages/isort/__init__.py deleted file mode 100644 index ba2bef8..0000000 --- a/.venv/lib/python3.10/site-packages/isort/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Defines the public isort interface""" - -__all__ = ( - "Config", - "ImportKey", - "__version__", - "check_code", - "check_file", - "check_stream", - "code", - "file", - "find_imports_in_code", - "find_imports_in_file", - "find_imports_in_paths", - "find_imports_in_stream", - "place_module", - "place_module_with_reason", - "settings", - "stream", -) - -from . import settings -from ._version import __version__ -from .api import ImportKey -from .api import check_code_string as check_code -from .api import ( - check_file, - check_stream, - find_imports_in_code, - find_imports_in_file, - find_imports_in_paths, - find_imports_in_stream, - place_module, - place_module_with_reason, -) -from .api import sort_code_string as code -from .api import sort_file as file -from .api import sort_stream as stream -from .settings import Config diff --git a/.venv/lib/python3.10/site-packages/isort/__main__.py b/.venv/lib/python3.10/site-packages/isort/__main__.py deleted file mode 100644 index 94b1d05..0000000 --- a/.venv/lib/python3.10/site-packages/isort/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -from isort.main import main - -main() diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE deleted file mode 100644 index e859590..0000000 --- a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 Taneli Hukkinen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py deleted file mode 100644 index 5b9f247..0000000 --- a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""A lil' TOML parser.""" - -__all__ = ("loads", "load", "TOMLDecodeError") -__version__ = "1.2.0" # DO NOT EDIT THIS LINE MANUALLY. LET bump2version UTILITY DO IT - -from ._parser import TOMLDecodeError, load, loads diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py deleted file mode 100644 index ab36adc..0000000 --- a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py +++ /dev/null @@ -1,650 +0,0 @@ -import string -import warnings -from types import MappingProxyType -from typing import IO, Any, Callable, Dict, FrozenSet, Iterable, NamedTuple, Optional, Tuple - -from ._re import ( - RE_DATETIME, - RE_LOCALTIME, - RE_NUMBER, - match_to_datetime, - match_to_localtime, - match_to_number, -) - -ASCII_CTRL = frozenset(chr(i) for i in range(32)) | frozenset(chr(127)) - -# Neither of these sets include quotation mark or backslash. They are -# currently handled as separate cases in the parser functions. -ILLEGAL_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t") -ILLEGAL_MULTILINE_BASIC_STR_CHARS = ASCII_CTRL - frozenset("\t\n\r") - -ILLEGAL_LITERAL_STR_CHARS = ILLEGAL_BASIC_STR_CHARS -ILLEGAL_MULTILINE_LITERAL_STR_CHARS = ASCII_CTRL - frozenset("\t\n") - -ILLEGAL_COMMENT_CHARS = ILLEGAL_BASIC_STR_CHARS - -TOML_WS = frozenset(" \t") -TOML_WS_AND_NEWLINE = TOML_WS | frozenset("\n") -BARE_KEY_CHARS = frozenset(string.ascii_letters + string.digits + "-_") -KEY_INITIAL_CHARS = BARE_KEY_CHARS | frozenset("\"'") -HEXDIGIT_CHARS = frozenset(string.hexdigits) - -BASIC_STR_ESCAPE_REPLACEMENTS = MappingProxyType( - { - "\\b": "\u0008", # backspace - "\\t": "\u0009", # tab - "\\n": "\u000a", # linefeed - "\\f": "\u000c", # form feed - "\\r": "\u000d", # carriage return - '\\"': "\u0022", # quote - "\\\\": "\u005c", # backslash - } -) - -# Type annotations -ParseFloat = Callable[[str], Any] -Key = Tuple[str, ...] -Pos = int - - -class TOMLDecodeError(ValueError): - """An error raised if a document is not valid TOML.""" - - -def load(fp: IO, *, parse_float: ParseFloat = float) -> Dict[str, Any]: - """Parse TOML from a file object.""" - s = fp.read() - if isinstance(s, bytes): - s = s.decode() - else: - warnings.warn( - "Text file object support is deprecated in favor of binary file objects." - ' Use `open("foo.toml", "rb")` to open the file in binary mode.', - DeprecationWarning, - ) - return loads(s, parse_float=parse_float) - - -def loads(s: str, *, parse_float: ParseFloat = float) -> Dict[str, Any]: # noqa: C901 - """Parse TOML from a string.""" - - # The spec allows converting "\r\n" to "\n", even in string - # literals. Let's do so to simplify parsing. - src = s.replace("\r\n", "\n") - pos = 0 - out = Output(NestedDict(), Flags()) - header: Key = () - - # Parse one statement at a time - # (typically means one line in TOML source) - while True: - # 1. Skip line leading whitespace - pos = skip_chars(src, pos, TOML_WS) - - # 2. Parse rules. Expect one of the following: - # - end of file - # - end of line - # - comment - # - key/value pair - # - append dict to list (and move to its namespace) - # - create dict (and move to its namespace) - # Skip trailing whitespace when applicable. - try: - char = src[pos] - except IndexError: - break - if char == "\n": - pos += 1 - continue - if char in KEY_INITIAL_CHARS: - pos = key_value_rule(src, pos, out, header, parse_float) - pos = skip_chars(src, pos, TOML_WS) - elif char == "[": - try: - second_char: Optional[str] = src[pos + 1] - except IndexError: - second_char = None - if second_char == "[": - pos, header = create_list_rule(src, pos, out) - else: - pos, header = create_dict_rule(src, pos, out) - pos = skip_chars(src, pos, TOML_WS) - elif char != "#": - raise suffixed_err(src, pos, "Invalid statement") - - # 3. Skip comment - pos = skip_comment(src, pos) - - # 4. Expect end of line or end of file - try: - char = src[pos] - except IndexError: - break - if char != "\n": - raise suffixed_err(src, pos, "Expected newline or end of document after a statement") - pos += 1 - - return out.data.dict - - -class Flags: - """Flags that map to parsed keys/namespaces.""" - - # Marks an immutable namespace (inline array or inline table). - FROZEN = 0 - # Marks a nest that has been explicitly created and can no longer - # be opened using the "[table]" syntax. - EXPLICIT_NEST = 1 - - def __init__(self) -> None: - self._flags: Dict[str, dict] = {} - - def unset_all(self, key: Key) -> None: - cont = self._flags - for k in key[:-1]: - if k not in cont: - return - cont = cont[k]["nested"] - cont.pop(key[-1], None) - - def set_for_relative_key(self, head_key: Key, rel_key: Key, flag: int) -> None: - cont = self._flags - for k in head_key: - if k not in cont: - cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}} - cont = cont[k]["nested"] - for k in rel_key: - if k in cont: - cont[k]["flags"].add(flag) - else: - cont[k] = {"flags": {flag}, "recursive_flags": set(), "nested": {}} - cont = cont[k]["nested"] - - def set(self, key: Key, flag: int, *, recursive: bool) -> None: # noqa: A003 - cont = self._flags - key_parent, key_stem = key[:-1], key[-1] - for k in key_parent: - if k not in cont: - cont[k] = {"flags": set(), "recursive_flags": set(), "nested": {}} - cont = cont[k]["nested"] - if key_stem not in cont: - cont[key_stem] = {"flags": set(), "recursive_flags": set(), "nested": {}} - cont[key_stem]["recursive_flags" if recursive else "flags"].add(flag) - - def is_(self, key: Key, flag: int) -> bool: - if not key: - return False # document root has no flags - cont = self._flags - for k in key[:-1]: - if k not in cont: - return False - inner_cont = cont[k] - if flag in inner_cont["recursive_flags"]: - return True - cont = inner_cont["nested"] - key_stem = key[-1] - if key_stem in cont: - cont = cont[key_stem] - return flag in cont["flags"] or flag in cont["recursive_flags"] - return False - - -class NestedDict: - def __init__(self) -> None: - # The parsed content of the TOML document - self.dict: Dict[str, Any] = {} - - def get_or_create_nest( - self, - key: Key, - *, - access_lists: bool = True, - ) -> dict: - cont: Any = self.dict - for k in key: - if k not in cont: - cont[k] = {} - cont = cont[k] - if access_lists and isinstance(cont, list): - cont = cont[-1] - if not isinstance(cont, dict): - raise KeyError("There is no nest behind this key") - return cont - - def append_nest_to_list(self, key: Key) -> None: - cont = self.get_or_create_nest(key[:-1]) - last_key = key[-1] - if last_key in cont: - list_ = cont[last_key] - if not isinstance(list_, list): - raise KeyError("An object other than list found behind this key") - list_.append({}) - else: - cont[last_key] = [{}] - - -class Output(NamedTuple): - data: NestedDict - flags: Flags - - -def skip_chars(src: str, pos: Pos, chars: Iterable[str]) -> Pos: - try: - while src[pos] in chars: - pos += 1 - except IndexError: - pass - return pos - - -def skip_until( - src: str, - pos: Pos, - expect: str, - *, - error_on: FrozenSet[str], - error_on_eof: bool, -) -> Pos: - try: - new_pos = src.index(expect, pos) - except ValueError: - new_pos = len(src) - if error_on_eof: - raise suffixed_err(src, new_pos, f'Expected "{expect!r}"') - - if not error_on.isdisjoint(src[pos:new_pos]): - while src[pos] not in error_on: - pos += 1 - raise suffixed_err(src, pos, f'Found invalid character "{src[pos]!r}"') - return new_pos - - -def skip_comment(src: str, pos: Pos) -> Pos: - try: - char: Optional[str] = src[pos] - except IndexError: - char = None - if char == "#": - return skip_until(src, pos + 1, "\n", error_on=ILLEGAL_COMMENT_CHARS, error_on_eof=False) - return pos - - -def skip_comments_and_array_ws(src: str, pos: Pos) -> Pos: - while True: - pos_before_skip = pos - pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) - pos = skip_comment(src, pos) - if pos == pos_before_skip: - return pos - - -def create_dict_rule(src: str, pos: Pos, out: Output) -> Tuple[Pos, Key]: - pos += 1 # Skip "[" - pos = skip_chars(src, pos, TOML_WS) - pos, key = parse_key(src, pos) - - if out.flags.is_(key, Flags.EXPLICIT_NEST) or out.flags.is_(key, Flags.FROZEN): - raise suffixed_err(src, pos, f"Can not declare {key} twice") - out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) - try: - out.data.get_or_create_nest(key) - except KeyError: - raise suffixed_err(src, pos, "Can not overwrite a value") - - if not src.startswith("]", pos): - raise suffixed_err(src, pos, 'Expected "]" at the end of a table declaration') - return pos + 1, key - - -def create_list_rule(src: str, pos: Pos, out: Output) -> Tuple[Pos, Key]: - pos += 2 # Skip "[[" - pos = skip_chars(src, pos, TOML_WS) - pos, key = parse_key(src, pos) - - if out.flags.is_(key, Flags.FROZEN): - raise suffixed_err(src, pos, f"Can not mutate immutable namespace {key}") - # Free the namespace now that it points to another empty list item... - out.flags.unset_all(key) - # ...but this key precisely is still prohibited from table declaration - out.flags.set(key, Flags.EXPLICIT_NEST, recursive=False) - try: - out.data.append_nest_to_list(key) - except KeyError: - raise suffixed_err(src, pos, "Can not overwrite a value") - - if not src.startswith("]]", pos): - raise suffixed_err(src, pos, 'Expected "]]" at the end of an array declaration') - return pos + 2, key - - -def key_value_rule(src: str, pos: Pos, out: Output, header: Key, parse_float: ParseFloat) -> Pos: - pos, key, value = parse_key_value_pair(src, pos, parse_float) - key_parent, key_stem = key[:-1], key[-1] - abs_key_parent = header + key_parent - - if out.flags.is_(abs_key_parent, Flags.FROZEN): - raise suffixed_err(src, pos, f"Can not mutate immutable namespace {abs_key_parent}") - # Containers in the relative path can't be opened with the table syntax after this - out.flags.set_for_relative_key(header, key, Flags.EXPLICIT_NEST) - try: - nest = out.data.get_or_create_nest(abs_key_parent) - except KeyError: - raise suffixed_err(src, pos, "Can not overwrite a value") - if key_stem in nest: - raise suffixed_err(src, pos, "Can not overwrite a value") - # Mark inline table and array namespaces recursively immutable - if isinstance(value, (dict, list)): - out.flags.set(header + key, Flags.FROZEN, recursive=True) - nest[key_stem] = value - return pos - - -def parse_key_value_pair(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, Key, Any]: - pos, key = parse_key(src, pos) - try: - char: Optional[str] = src[pos] - except IndexError: - char = None - if char != "=": - raise suffixed_err(src, pos, 'Expected "=" after a key in a key/value pair') - pos += 1 - pos = skip_chars(src, pos, TOML_WS) - pos, value = parse_value(src, pos, parse_float) - return pos, key, value - - -def parse_key(src: str, pos: Pos) -> Tuple[Pos, Key]: - pos, key_part = parse_key_part(src, pos) - key: Key = (key_part,) - pos = skip_chars(src, pos, TOML_WS) - while True: - try: - char: Optional[str] = src[pos] - except IndexError: - char = None - if char != ".": - return pos, key - pos += 1 - pos = skip_chars(src, pos, TOML_WS) - pos, key_part = parse_key_part(src, pos) - key += (key_part,) - pos = skip_chars(src, pos, TOML_WS) - - -def parse_key_part(src: str, pos: Pos) -> Tuple[Pos, str]: - try: - char: Optional[str] = src[pos] - except IndexError: - char = None - if char in BARE_KEY_CHARS: - start_pos = pos - pos = skip_chars(src, pos, BARE_KEY_CHARS) - return pos, src[start_pos:pos] - if char == "'": - return parse_literal_str(src, pos) - if char == '"': - return parse_one_line_basic_str(src, pos) - raise suffixed_err(src, pos, "Invalid initial character for a key part") - - -def parse_one_line_basic_str(src: str, pos: Pos) -> Tuple[Pos, str]: - pos += 1 - return parse_basic_str(src, pos, multiline=False) - - -def parse_array(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, list]: - pos += 1 - array: list = [] - - pos = skip_comments_and_array_ws(src, pos) - if src.startswith("]", pos): - return pos + 1, array - while True: - pos, val = parse_value(src, pos, parse_float) - array.append(val) - pos = skip_comments_and_array_ws(src, pos) - - c = src[pos : pos + 1] - if c == "]": - return pos + 1, array - if c != ",": - raise suffixed_err(src, pos, "Unclosed array") - pos += 1 - - pos = skip_comments_and_array_ws(src, pos) - if src.startswith("]", pos): - return pos + 1, array - - -def parse_inline_table(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, dict]: - pos += 1 - nested_dict = NestedDict() - flags = Flags() - - pos = skip_chars(src, pos, TOML_WS) - if src.startswith("}", pos): - return pos + 1, nested_dict.dict - while True: - pos, key, value = parse_key_value_pair(src, pos, parse_float) - key_parent, key_stem = key[:-1], key[-1] - if flags.is_(key, Flags.FROZEN): - raise suffixed_err(src, pos, f"Can not mutate immutable namespace {key}") - try: - nest = nested_dict.get_or_create_nest(key_parent, access_lists=False) - except KeyError: - raise suffixed_err(src, pos, "Can not overwrite a value") - if key_stem in nest: - raise suffixed_err(src, pos, f'Duplicate inline table key "{key_stem}"') - nest[key_stem] = value - pos = skip_chars(src, pos, TOML_WS) - c = src[pos : pos + 1] - if c == "}": - return pos + 1, nested_dict.dict - if c != ",": - raise suffixed_err(src, pos, "Unclosed inline table") - if isinstance(value, (dict, list)): - flags.set(key, Flags.FROZEN, recursive=True) - pos += 1 - pos = skip_chars(src, pos, TOML_WS) - - -def parse_basic_str_escape( # noqa: C901 - src: str, pos: Pos, *, multiline: bool = False -) -> Tuple[Pos, str]: - escape_id = src[pos : pos + 2] - pos += 2 - if multiline and escape_id in {"\\ ", "\\\t", "\\\n"}: - # Skip whitespace until next non-whitespace character or end of - # the doc. Error if non-whitespace is found before newline. - if escape_id != "\\\n": - pos = skip_chars(src, pos, TOML_WS) - try: - char = src[pos] - except IndexError: - return pos, "" - if char != "\n": - raise suffixed_err(src, pos, 'Unescaped "\\" in a string') - pos += 1 - pos = skip_chars(src, pos, TOML_WS_AND_NEWLINE) - return pos, "" - if escape_id == "\\u": - return parse_hex_char(src, pos, 4) - if escape_id == "\\U": - return parse_hex_char(src, pos, 8) - try: - return pos, BASIC_STR_ESCAPE_REPLACEMENTS[escape_id] - except KeyError: - if len(escape_id) != 2: - raise suffixed_err(src, pos, "Unterminated string") - raise suffixed_err(src, pos, 'Unescaped "\\" in a string') - - -def parse_basic_str_escape_multiline(src: str, pos: Pos) -> Tuple[Pos, str]: - return parse_basic_str_escape(src, pos, multiline=True) - - -def parse_hex_char(src: str, pos: Pos, hex_len: int) -> Tuple[Pos, str]: - hex_str = src[pos : pos + hex_len] - if len(hex_str) != hex_len or not HEXDIGIT_CHARS.issuperset(hex_str): - raise suffixed_err(src, pos, "Invalid hex value") - pos += hex_len - hex_int = int(hex_str, 16) - if not is_unicode_scalar_value(hex_int): - raise suffixed_err(src, pos, "Escaped character is not a Unicode scalar value") - return pos, chr(hex_int) - - -def parse_literal_str(src: str, pos: Pos) -> Tuple[Pos, str]: - pos += 1 # Skip starting apostrophe - start_pos = pos - pos = skip_until(src, pos, "'", error_on=ILLEGAL_LITERAL_STR_CHARS, error_on_eof=True) - return pos + 1, src[start_pos:pos] # Skip ending apostrophe - - -def parse_multiline_str(src: str, pos: Pos, *, literal: bool) -> Tuple[Pos, str]: - pos += 3 - if src.startswith("\n", pos): - pos += 1 - - if literal: - delim = "'" - end_pos = skip_until( - src, - pos, - "'''", - error_on=ILLEGAL_MULTILINE_LITERAL_STR_CHARS, - error_on_eof=True, - ) - result = src[pos:end_pos] - pos = end_pos + 3 - else: - delim = '"' - pos, result = parse_basic_str(src, pos, multiline=True) - - # Add at maximum two extra apostrophes/quotes if the end sequence - # is 4 or 5 chars long instead of just 3. - if not src.startswith(delim, pos): - return pos, result - pos += 1 - if not src.startswith(delim, pos): - return pos, result + delim - pos += 1 - return pos, result + (delim * 2) - - -def parse_basic_str(src: str, pos: Pos, *, multiline: bool) -> Tuple[Pos, str]: - if multiline: - error_on = ILLEGAL_MULTILINE_BASIC_STR_CHARS - parse_escapes = parse_basic_str_escape_multiline - else: - error_on = ILLEGAL_BASIC_STR_CHARS - parse_escapes = parse_basic_str_escape - result = "" - start_pos = pos - while True: - try: - char = src[pos] - except IndexError: - raise suffixed_err(src, pos, "Unterminated string") - if char == '"': - if not multiline: - return pos + 1, result + src[start_pos:pos] - if src.startswith('"""', pos): - return pos + 3, result + src[start_pos:pos] - pos += 1 - continue - if char == "\\": - result += src[start_pos:pos] - pos, parsed_escape = parse_escapes(src, pos) - result += parsed_escape - start_pos = pos - continue - if char in error_on: - raise suffixed_err(src, pos, f'Illegal character "{char!r}"') - pos += 1 - - -def parse_value(src: str, pos: Pos, parse_float: ParseFloat) -> Tuple[Pos, Any]: # noqa: C901 - try: - char: Optional[str] = src[pos] - except IndexError: - char = None - - # Basic strings - if char == '"': - if src.startswith('"""', pos): - return parse_multiline_str(src, pos, literal=False) - return parse_one_line_basic_str(src, pos) - - # Literal strings - if char == "'": - if src.startswith("'''", pos): - return parse_multiline_str(src, pos, literal=True) - return parse_literal_str(src, pos) - - # Booleans - if char == "t": - if src.startswith("true", pos): - return pos + 4, True - if char == "f": - if src.startswith("false", pos): - return pos + 5, False - - # Dates and times - datetime_match = RE_DATETIME.match(src, pos) - if datetime_match: - try: - datetime_obj = match_to_datetime(datetime_match) - except ValueError: - raise suffixed_err(src, pos, "Invalid date or datetime") - return datetime_match.end(), datetime_obj - localtime_match = RE_LOCALTIME.match(src, pos) - if localtime_match: - return localtime_match.end(), match_to_localtime(localtime_match) - - # Integers and "normal" floats. - # The regex will greedily match any type starting with a decimal - # char, so needs to be located after handling of dates and times. - number_match = RE_NUMBER.match(src, pos) - if number_match: - return number_match.end(), match_to_number(number_match, parse_float) - - # Arrays - if char == "[": - return parse_array(src, pos, parse_float) - - # Inline tables - if char == "{": - return parse_inline_table(src, pos, parse_float) - - # Special floats - first_three = src[pos : pos + 3] - if first_three in {"inf", "nan"}: - return pos + 3, parse_float(first_three) - first_four = src[pos : pos + 4] - if first_four in {"-inf", "+inf", "-nan", "+nan"}: - return pos + 4, parse_float(first_four) - - raise suffixed_err(src, pos, "Invalid value") - - -def suffixed_err(src: str, pos: Pos, msg: str) -> TOMLDecodeError: - """Return a `TOMLDecodeError` where error message is suffixed with - coordinates in source.""" - - def coord_repr(src: str, pos: Pos) -> str: - if pos >= len(src): - return "end of document" - line = src.count("\n", 0, pos) + 1 - if line == 1: - column = pos + 1 - else: - column = pos - src.rindex("\n", 0, pos) - return f"line {line}, column {column}" - - return TOMLDecodeError(f"{msg} (at {coord_repr(src, pos)})") - - -def is_unicode_scalar_value(codepoint: int) -> bool: - return (0 <= codepoint <= 55295) or (57344 <= codepoint <= 1114111) diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py deleted file mode 100644 index 8238aa1..0000000 --- a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py +++ /dev/null @@ -1,100 +0,0 @@ -import re -from datetime import date, datetime, time, timedelta, timezone, tzinfo -from functools import lru_cache -from typing import TYPE_CHECKING, Any, Optional, Union - -if TYPE_CHECKING: - from tomli._parser import ParseFloat - -# E.g. -# - 00:32:00.999999 -# - 00:32:00 -_TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?" - -RE_NUMBER = re.compile( - r""" -0 -(?: - x[0-9A-Fa-f](?:_?[0-9A-Fa-f])* # hex - | - b[01](?:_?[01])* # bin - | - o[0-7](?:_?[0-7])* # oct -) -| -[+-]?(?:0|[1-9](?:_?[0-9])*) # dec, integer part -(?P - (?:\.[0-9](?:_?[0-9])*)? # optional fractional part - (?:[eE][+-]?[0-9](?:_?[0-9])*)? # optional exponent part -) -""", - flags=re.VERBOSE, -) -RE_LOCALTIME = re.compile(_TIME_RE_STR) -RE_DATETIME = re.compile( - rf""" -([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27 -(?: - [T ] - {_TIME_RE_STR} - (?:(Z)|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))? # optional time offset -)? -""", - flags=re.VERBOSE, -) - - -def match_to_datetime(match: "re.Match") -> Union[datetime, date]: - """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`. - - Raises ValueError if the match does not correspond to a valid date - or datetime. - """ - ( - year_str, - month_str, - day_str, - hour_str, - minute_str, - sec_str, - micros_str, - zulu_time, - offset_sign_str, - offset_hour_str, - offset_minute_str, - ) = match.groups() - year, month, day = int(year_str), int(month_str), int(day_str) - if hour_str is None: - return date(year, month, day) - hour, minute, sec = int(hour_str), int(minute_str), int(sec_str) - micros = int(micros_str.ljust(6, "0")) if micros_str else 0 - if offset_sign_str: - tz: Optional[tzinfo] = cached_tz(offset_hour_str, offset_minute_str, offset_sign_str) - elif zulu_time: - tz = timezone.utc - else: # local date-time - tz = None - return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz) - - -@lru_cache(maxsize=None) -def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone: - sign = 1 if sign_str == "+" else -1 - return timezone( - timedelta( - hours=sign * int(hour_str), - minutes=sign * int(minute_str), - ) - ) - - -def match_to_localtime(match: "re.Match") -> time: - hour_str, minute_str, sec_str, micros_str = match.groups() - micros = int(micros_str.ljust(6, "0")) if micros_str else 0 - return time(int(hour_str), int(minute_str), int(sec_str), micros) - - -def match_to_number(match: "re.Match", parse_float: "ParseFloat") -> Any: - if match.group("floatpart"): - return parse_float(match.group()) - return int(match.group(), 0) diff --git a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed b/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed deleted file mode 100644 index 7632ecf..0000000 --- a/.venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed +++ /dev/null @@ -1 +0,0 @@ -# Marker file for PEP 561 diff --git a/.venv/lib/python3.10/site-packages/isort/_version.py b/.venv/lib/python3.10/site-packages/isort/_version.py deleted file mode 100644 index 4a8ec67..0000000 --- a/.venv/lib/python3.10/site-packages/isort/_version.py +++ /dev/null @@ -1,3 +0,0 @@ -from importlib import metadata - -__version__ = metadata.version("isort") diff --git a/.venv/lib/python3.10/site-packages/isort/api.py b/.venv/lib/python3.10/site-packages/isort/api.py deleted file mode 100644 index 706ecd8..0000000 --- a/.venv/lib/python3.10/site-packages/isort/api.py +++ /dev/null @@ -1,660 +0,0 @@ -__all__ = ( - "ImportKey", - "check_code_string", - "check_file", - "check_stream", - "find_imports_in_code", - "find_imports_in_file", - "find_imports_in_paths", - "find_imports_in_stream", - "place_module", - "place_module_with_reason", - "sort_code_string", - "sort_file", - "sort_stream", -) - -import contextlib -import shutil -import sys -from collections.abc import Iterator -from enum import Enum -from io import StringIO -from itertools import chain -from pathlib import Path -from typing import Any, TextIO, cast -from warnings import warn - -from isort import core - -from . import files, identify, io -from .exceptions import ( - ExistingSyntaxErrors, - FileSkipComment, - FileSkipSetting, - IntroducedSyntaxErrors, -) -from .format import ask_whether_to_apply_changes_to_file, create_terminal_printer, show_unified_diff -from .io import Empty, File -from .place import module as place_module # noqa: F401 -from .place import module_with_reason as place_module_with_reason # noqa: F401 -from .settings import CYTHON_EXTENSIONS, DEFAULT_CONFIG, Config - - -class ImportKey(Enum): - """Defines how to key an individual import, generally for deduping. - - Import keys are defined from less to more specific: - - from x.y import z as a - ______| | | | - | | | | - PACKAGE | | | - ________| | | - | | | - MODULE | | - _________________| | - | | - ATTRIBUTE | - ______________________| - | - ALIAS - """ - - PACKAGE = 1 - MODULE = 2 - ATTRIBUTE = 3 - ALIAS = 4 - - -def sort_code_string( - code: str, - extension: str | None = None, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - disregard_skip: bool = False, - show_diff: bool | TextIO = False, - **config_kwargs: Any, -) -> str: - """Sorts any imports within the provided code string, returning a new string with them sorted. - - - **code**: The string of code with imports that need to be sorted. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a - TextIO stream is provided results will be written to it, otherwise no diff will be computed. - - ****config_kwargs**: Any config modifications. - """ - input_stream = StringIO(code) - output_stream = StringIO() - config = _config(path=file_path, config=config, **config_kwargs) - sort_stream( - input_stream, - output_stream, - extension=extension, - config=config, - file_path=file_path, - disregard_skip=disregard_skip, - show_diff=show_diff, - ) - output_stream.seek(0) - return output_stream.read() - - -def check_code_string( - code: str, - show_diff: bool | TextIO = False, - extension: str | None = None, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - disregard_skip: bool = False, - **config_kwargs: Any, -) -> bool: - """Checks the order, format, and categorization of imports within the provided code string. - Returns `True` if everything is correct, otherwise `False`. - - - **code**: The string of code with imports that need to be sorted. - - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a - TextIO stream is provided results will be written to it, otherwise no diff will be computed. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - - ****config_kwargs**: Any config modifications. - """ - config = _config(path=file_path, config=config, **config_kwargs) - return check_stream( - StringIO(code), - show_diff=show_diff, - extension=extension, - config=config, - file_path=file_path, - disregard_skip=disregard_skip, - ) - - -def sort_stream( - input_stream: TextIO, - output_stream: TextIO, - extension: str | None = None, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - disregard_skip: bool = False, - show_diff: bool | TextIO = False, - raise_on_skip: bool = True, - **config_kwargs: Any, -) -> bool: - """Sorts any imports within the provided code stream, outputs to the provided output stream. - Returns `True` if anything is modified from the original input stream, otherwise `False`. - - - **input_stream**: The stream of code with imports that need to be sorted. - - **output_stream**: The stream where sorted imports should be written to. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a - TextIO stream is provided results will be written to it, otherwise no diff will be computed. - - ****config_kwargs**: Any config modifications. - """ - extension = extension or (file_path and file_path.suffix.lstrip(".")) or "py" - if show_diff: - _output_stream = StringIO() - _input_stream = StringIO(input_stream.read()) - changed = sort_stream( - input_stream=_input_stream, - output_stream=_output_stream, - extension=extension, - config=config, - file_path=file_path, - disregard_skip=disregard_skip, - raise_on_skip=raise_on_skip, - **config_kwargs, - ) - _output_stream.seek(0) - _input_stream.seek(0) - show_unified_diff( - file_input=_input_stream.read(), - file_output=_output_stream.read(), - file_path=file_path, - output=output_stream if show_diff is True else show_diff, - color_output=config.color_output, - ) - return changed - - config = _config(path=file_path, config=config, **config_kwargs) - content_source = str(file_path or "Passed in content") - if not disregard_skip and file_path and config.is_skipped(file_path): - raise FileSkipSetting(content_source) - - _internal_output = output_stream - - if config.atomic: - try: - file_content = input_stream.read() - compile(file_content, content_source, "exec", flags=0, dont_inherit=True) - except SyntaxError: - if extension not in CYTHON_EXTENSIONS: - raise ExistingSyntaxErrors(content_source) - if config.verbose: - warn( - f"{content_source} Python AST errors found but ignored due to Cython extension", - stacklevel=2, - ) - input_stream = StringIO(file_content) - - if not output_stream.readable(): - _internal_output = StringIO() - - try: - changed = core.process( - input_stream, - _internal_output, - extension=extension, - config=config, - raise_on_skip=raise_on_skip, - ) - except FileSkipComment: - raise FileSkipComment(content_source) - - if config.atomic: - _internal_output.seek(0) - try: - compile(_internal_output.read(), content_source, "exec", flags=0, dont_inherit=True) - _internal_output.seek(0) - except SyntaxError: # pragma: no cover - if extension not in CYTHON_EXTENSIONS: - raise IntroducedSyntaxErrors(content_source) - if config.verbose: - warn( - f"{content_source} Python AST errors found but ignored due to Cython extension", - stacklevel=2, - ) - if _internal_output != output_stream: - output_stream.write(_internal_output.read()) - - return changed - - -def check_stream( - input_stream: TextIO, - show_diff: bool | TextIO = False, - extension: str | None = None, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - disregard_skip: bool = False, - **config_kwargs: Any, -) -> bool: - """Checks any imports within the provided code stream, returning `False` if any unsorted or - incorrectly imports are found or `True` if no problems are identified. - - - **input_stream**: The stream of code with imports that need to be sorted. - - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a - TextIO stream is provided results will be written to it, otherwise no diff will be computed. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - - ****config_kwargs**: Any config modifications. - """ - config = _config(path=file_path, config=config, **config_kwargs) - - if show_diff: - input_stream = StringIO(input_stream.read()) - - changed: bool = sort_stream( - input_stream=input_stream, - output_stream=Empty, - extension=extension, - config=config, - file_path=file_path, - disregard_skip=disregard_skip, - ) - printer = create_terminal_printer( - color=config.color_output, error=config.format_error, success=config.format_success - ) - if not changed: - if config.verbose and not config.only_modified: - printer.success(f"{file_path or ''} Everything Looks Good!") - return True - - printer.error(f"{file_path or ''} Imports are incorrectly sorted and/or formatted.") - if show_diff: - output_stream = StringIO() - input_stream.seek(0) - file_contents = input_stream.read() - sort_stream( - input_stream=StringIO(file_contents), - output_stream=output_stream, - extension=extension, - config=config, - file_path=file_path, - disregard_skip=disregard_skip, - ) - output_stream.seek(0) - - show_unified_diff( - file_input=file_contents, - file_output=output_stream.read(), - file_path=file_path, - output=None if show_diff is True else show_diff, - color_output=config.color_output, - ) - return False - - -def check_file( - filename: str | Path, - show_diff: bool | TextIO = False, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - disregard_skip: bool = True, - extension: str | None = None, - **config_kwargs: Any, -) -> bool: - """Checks any imports within the provided file, returning `False` if any unsorted or - incorrectly imports are found or `True` if no problems are identified. - - - **filename**: The name or Path of the file to check. - - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a - TextIO stream is provided results will be written to it, otherwise no diff will be computed. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - - ****config_kwargs**: Any config modifications. - """ - file_config: Config = config - - if "config_trie" in config_kwargs: - config_trie = config_kwargs.pop("config_trie", None) - if config_trie: - config_info = config_trie.search(filename) - if config.verbose: - print(f"{config_info[0]} used for file {filename}") - - file_config = Config(**config_info[1]) - - with io.File.read(filename) as source_file: - return check_stream( - source_file.stream, - show_diff=show_diff, - extension=extension, - config=file_config, - file_path=file_path or source_file.path, - disregard_skip=disregard_skip, - **config_kwargs, - ) - - -def _tmp_file(source_file: File) -> Path: - return source_file.path.with_suffix(source_file.path.suffix + ".isorted") - - -@contextlib.contextmanager -def _in_memory_output_stream_context() -> Iterator[TextIO]: - yield StringIO(newline=None) - - -@contextlib.contextmanager -def _file_output_stream_context(filename: str | Path, source_file: File) -> Iterator[TextIO]: - tmp_file = _tmp_file(source_file) - with tmp_file.open("w+", encoding=source_file.encoding, newline="") as output_stream: - shutil.copymode(filename, tmp_file) - yield output_stream - - -# Ignore DeepSource cyclomatic complexity check for this function. It is one -# the main entrypoints so sort of expected to be complex. -# skipcq: PY-R1000 -def sort_file( - filename: str | Path, - extension: str | None = None, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - disregard_skip: bool = True, - ask_to_apply: bool = False, - show_diff: bool | TextIO = False, - write_to_stdout: bool = False, - output: TextIO | None = None, - **config_kwargs: Any, -) -> bool: - """Sorts and formats any groups of imports within the provided file or Path. - Returns `True` if the file has been changed, otherwise `False`. - - - **filename**: The name or Path of the file to format. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **disregard_skip**: set to `True` if you want to ignore a skip set in config for this file. - - **ask_to_apply**: If `True`, prompt before applying any changes. - - **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a - TextIO stream is provided results will be written to it, otherwise no diff will be computed. - - **write_to_stdout**: If `True`, write to stdout instead of the input file. - - **output**: If a TextIO is provided, results will be written there rather than replacing - the original file content. - - ****config_kwargs**: Any config modifications. - """ - file_config: Config = config - - if "config_trie" in config_kwargs: - config_trie = config_kwargs.pop("config_trie", None) - if config_trie: - config_info = config_trie.search(filename) - if config.verbose: - print(f"{config_info[0]} used for file {filename}") - - file_config = Config(**config_info[1]) - - with io.File.read(filename) as source_file: - actual_file_path = file_path or source_file.path - config = _config(path=actual_file_path, config=file_config, **config_kwargs) - changed: bool = False - try: - if write_to_stdout: - changed = sort_stream( - input_stream=source_file.stream, - output_stream=sys.stdout, - config=config, - file_path=actual_file_path, - disregard_skip=disregard_skip, - extension=extension, - ) - else: - if output is None: - try: - if config.overwrite_in_place: - output_stream_context = _in_memory_output_stream_context() - else: - output_stream_context = _file_output_stream_context( - filename, source_file - ) - with output_stream_context as output_stream: - changed = sort_stream( - input_stream=source_file.stream, - output_stream=output_stream, - config=config, - file_path=actual_file_path, - disregard_skip=disregard_skip, - extension=extension, - ) - output_stream.seek(0) - if changed: - if show_diff or ask_to_apply: - source_file.stream.seek(0) - show_unified_diff( - file_input=source_file.stream.read(), - file_output=output_stream.read(), - file_path=actual_file_path, - output=( - None if show_diff is True else cast(TextIO, show_diff) - ), - color_output=config.color_output, - ) - if show_diff or ( - ask_to_apply - and not ask_whether_to_apply_changes_to_file( - str(source_file.path) - ) - ): - return False - source_file.stream.close() - if config.overwrite_in_place: - output_stream.seek(0) - with source_file.path.open("w") as fs: - shutil.copyfileobj(output_stream, fs) - if changed: - if not config.overwrite_in_place: - tmp_file = _tmp_file(source_file) - tmp_file.replace(source_file.path) - if not config.quiet: - print(f"Fixing {source_file.path}") - finally: - if not config.overwrite_in_place: # pragma: no branch - tmp_file = _tmp_file(source_file) - tmp_file.unlink(missing_ok=True) - else: - changed = sort_stream( - input_stream=source_file.stream, - output_stream=output, - config=config, - file_path=actual_file_path, - disregard_skip=disregard_skip, - extension=extension, - ) - if changed and show_diff: - source_file.stream.seek(0) - output.seek(0) - show_unified_diff( - file_input=source_file.stream.read(), - file_output=output.read(), - file_path=actual_file_path, - output=None if show_diff is True else show_diff, - color_output=config.color_output, - ) - source_file.stream.close() - - except ExistingSyntaxErrors: - warn(f"{actual_file_path} unable to sort due to existing syntax errors", stacklevel=2) - except IntroducedSyntaxErrors: # pragma: no cover - warn( - f"{actual_file_path} unable to sort as isort introduces new syntax errors", - stacklevel=2, - ) - - return changed - - -def find_imports_in_code( - code: str, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - unique: bool | ImportKey = False, - top_only: bool = False, - **config_kwargs: Any, -) -> Iterator[identify.Import]: - """Finds and returns all imports within the provided code string. - - - **code**: The string of code with imports that need to be sorted. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **unique**: If True, only the first instance of an import is returned. - - **top_only**: If True, only return imports that occur before the first function or class. - - ****config_kwargs**: Any config modifications. - """ - yield from find_imports_in_stream( - input_stream=StringIO(code), - config=config, - file_path=file_path, - unique=unique, - top_only=top_only, - **config_kwargs, - ) - - -def find_imports_in_stream( - input_stream: TextIO, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - unique: bool | ImportKey = False, - top_only: bool = False, - _seen: set[str] | None = None, - **config_kwargs: Any, -) -> Iterator[identify.Import]: - """Finds and returns all imports within the provided code stream. - - - **input_stream**: The stream of code with imports that need to be sorted. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **unique**: If True, only the first instance of an import is returned. - - **top_only**: If True, only return imports that occur before the first function or class. - - **_seen**: An optional set of imports already seen. Generally meant only for internal use. - - ****config_kwargs**: Any config modifications. - """ - config = _config(config=config, **config_kwargs) - identified_imports = identify.imports( - input_stream, config=config, file_path=file_path, top_only=top_only - ) - if not unique: - yield from identified_imports - - seen: set[str] = set() if _seen is None else _seen - for identified_import in identified_imports: - if unique in (True, ImportKey.ALIAS): - key = identified_import.statement() - elif unique == ImportKey.ATTRIBUTE: - key = f"{identified_import.module}.{identified_import.attribute}" - elif unique == ImportKey.MODULE: - key = identified_import.module - elif unique == ImportKey.PACKAGE: # pragma: no branch # type checking ensures this - key = identified_import.module.split(".")[0] - - if key and key not in seen: - seen.add(key) - yield identified_import - - -def find_imports_in_file( - filename: str | Path, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - unique: bool | ImportKey = False, - top_only: bool = False, - **config_kwargs: Any, -) -> Iterator[identify.Import]: - """Finds and returns all imports within the provided source file. - - - **filename**: The name or Path of the file to look for imports in. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **unique**: If True, only the first instance of an import is returned. - - **top_only**: If True, only return imports that occur before the first function or class. - - ****config_kwargs**: Any config modifications. - """ - try: - with io.File.read(filename) as source_file: - yield from find_imports_in_stream( - input_stream=source_file.stream, - config=config, - file_path=file_path or source_file.path, - unique=unique, - top_only=top_only, - **config_kwargs, - ) - except OSError as error: - warn(f"Unable to parse file {filename} due to {error}", stacklevel=2) - - -def find_imports_in_paths( - paths: Iterator[str | Path], - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - unique: bool | ImportKey = False, - top_only: bool = False, - **config_kwargs: Any, -) -> Iterator[identify.Import]: - """Finds and returns all imports within the provided source paths. - - - **paths**: A collection of paths to recursively look for imports within. - - **extension**: The file extension that contains imports. Defaults to filename extension or py. - - **config**: The config object to use when sorting imports. - - **file_path**: The disk location where the code string was pulled from. - - **unique**: If True, only the first instance of an import is returned. - - **top_only**: If True, only return imports that occur before the first function or class. - - ****config_kwargs**: Any config modifications. - """ - config = _config(config=config, **config_kwargs) - seen: set[str] | None = set() if unique else None - yield from chain( - *( - find_imports_in_file( - file_name, unique=unique, config=config, top_only=top_only, _seen=seen - ) - for file_name in files.find(map(str, paths), config, [], []) - ) - ) - - -def _config( - path: Path | None = None, config: Config = DEFAULT_CONFIG, **config_kwargs: Any -) -> Config: - if path and ( - config is DEFAULT_CONFIG - and "settings_path" not in config_kwargs - and "settings_file" not in config_kwargs - ): - config_kwargs["settings_path"] = path - - if config_kwargs: - if config is not DEFAULT_CONFIG: - raise ValueError( - "You can either specify custom configuration options using kwargs or " - "passing in a Config object. Not Both!" - ) - - config = Config(**config_kwargs) - - return config diff --git a/.venv/lib/python3.10/site-packages/isort/comments.py b/.venv/lib/python3.10/site-packages/isort/comments.py deleted file mode 100644 index 0fdab29..0000000 --- a/.venv/lib/python3.10/site-packages/isort/comments.py +++ /dev/null @@ -1,29 +0,0 @@ -def parse(line: str) -> tuple[str, str]: - """Parses import lines for comments and returns back the - import statement and the associated comment. - """ - comment_start = line.find("#") - if comment_start != -1: - return (line[:comment_start], line[comment_start + 1 :].strip()) - - return (line, "") - - -def add_to_line( - comments: list[str] | None, - original_string: str = "", - removed: bool = False, - comment_prefix: str = "", -) -> str: - """Returns a string with comments added if removed is not set.""" - if removed: - return parse(original_string)[0] - - if not comments: - return original_string - - unique_comments: list[str] = [] - for comment in comments: - if comment not in unique_comments: - unique_comments.append(comment) - return f"{parse(original_string)[0]}{comment_prefix} {'; '.join(unique_comments)}" diff --git a/.venv/lib/python3.10/site-packages/isort/core.py b/.venv/lib/python3.10/site-packages/isort/core.py deleted file mode 100644 index f3c2e52..0000000 --- a/.venv/lib/python3.10/site-packages/isort/core.py +++ /dev/null @@ -1,513 +0,0 @@ -import textwrap -from io import StringIO -from itertools import chain -from typing import TextIO - -import isort.literal -from isort.settings import DEFAULT_CONFIG, Config - -from . import output, parse -from .exceptions import ExistingSyntaxErrors, FileSkipComment -from .format import format_natural, remove_whitespace -from .settings import FILE_SKIP_COMMENTS - -CIMPORT_IDENTIFIERS = ("cimport ", "cimport*", "from.cimport") -IMPORT_START_IDENTIFIERS = ("from ", "from.import", "import ", "import*", *CIMPORT_IDENTIFIERS) -DOCSTRING_INDICATORS = ('"""', "'''") -COMMENT_INDICATORS = (*DOCSTRING_INDICATORS, "'", '"', "#") -CODE_SORT_COMMENTS = ( - "# isort: list", - "# isort: dict", - "# isort: set", - "# isort: unique-list", - "# isort: tuple", - "# isort: unique-tuple", - "# isort: assignments", -) -LITERAL_TYPE_MAPPING = {"(": "tuple", "[": "list", "{": "set"} - - -# Ignore DeepSource cyclomatic complexity check for this function. -# skipcq: PY-R1000 -def process( - input_stream: TextIO, - output_stream: TextIO, - extension: str = "py", - raise_on_skip: bool = True, - config: Config = DEFAULT_CONFIG, -) -> bool: - """Parses stream identifying sections of contiguous imports and sorting them - - Code with unsorted imports is read from the provided `input_stream`, sorted and then - outputted to the specified `output_stream`. - - - `input_stream`: Text stream with unsorted import sections. - - `output_stream`: Text stream to output sorted inputs into. - - `config`: Config settings to use when sorting imports. Defaults settings. - - *Default*: `isort.settings.DEFAULT_CONFIG`. - - `extension`: The file extension or file extension rules that should be used. - - *Default*: `"py"`. - - *Choices*: `["py", "pyi", "pyx"]`. - - Returns `True` if there were changes that needed to be made (errors present) from what - was provided in the input_stream, otherwise `False`. - """ - line_separator: str = config.line_ending - add_imports: list[str] = [format_natural(addition) for addition in config.add_imports] - import_section: str = "" - next_import_section: str = "" - next_cimports: bool = False - in_quote: str = "" - was_in_quote: bool = False - first_comment_index_start: int = -1 - first_comment_index_end: int = -1 - contains_imports: bool = False - in_top_comment: bool = False - first_import_section: bool = True - indent: str = "" - isort_off: bool = False - skip_file: bool = False - code_sorting: bool | str = False - code_sorting_section: str = "" - code_sorting_indent: str = "" - cimports: bool = False - made_changes: bool = False - stripped_line: str = "" - end_of_file: bool = False - verbose_output: list[str] = [] - lines_before: list[str] = [] - is_reexport: bool = False - reexport_rollback: int = 0 - - if config.float_to_top: - new_input = "" - current = "" - isort_off = False - for line in chain(input_stream, (None,)): - if isort_off and line is not None: - if line == "# isort: on\n": - isort_off = False - new_input += line - elif line in ("# isort: split\n", "# isort: off\n", None) or str(line).endswith( - "# isort: split\n" - ): - if line == "# isort: off\n": - isort_off = True - if current: - if add_imports: - add_line_separator = line_separator or "\n" - current += add_line_separator + add_line_separator.join(add_imports) - add_imports = [] - parsed = parse.file_contents(current, config=config) - verbose_output += parsed.verbose_output - extra_space = "" - while current and current[-1] == "\n": - extra_space += "\n" - current = current[:-1] - extra_space = extra_space.replace("\n", "", 1) - sorted_output = output.sorted_imports( - parsed, config, extension, import_type="import" - ) - made_changes = made_changes or _has_changed( - before=current, - after=sorted_output, - line_separator=parsed.line_separator, - ignore_whitespace=config.ignore_whitespace, - ) - new_input += sorted_output - new_input += extra_space - current = "" - new_input += line or "" - else: - current += line or "" - - input_stream = StringIO(new_input) - - for index, line in enumerate(chain(input_stream, (None,))): - if line is None: - if index == 0 and not config.force_adds: - return False - - not_imports = True - end_of_file = True - line = "" - if not line_separator: - line_separator = "\n" - - if code_sorting and code_sorting_section: - if is_reexport: - output_stream.seek(output_stream.tell() - reexport_rollback) - reexport_rollback = 0 - sorted_code = textwrap.indent( - isort.literal.assignment( - code_sorting_section, - str(code_sorting), - extension, - config=_indented_config(config, indent), - ), - code_sorting_indent, - ) - made_changes = made_changes or _has_changed( - before=code_sorting_section, - after=sorted_code, - line_separator=line_separator, - ignore_whitespace=config.ignore_whitespace, - ) - output_stream.write(sorted_code) - if is_reexport: - output_stream.truncate() - else: - stripped_line = line.strip() - if stripped_line and not line_separator: - line_separator = line[len(line.rstrip()) :].replace(" ", "").replace("\t", "") - - for file_skip_comment in FILE_SKIP_COMMENTS: - if file_skip_comment in line: - if raise_on_skip: - raise FileSkipComment("Passed in content") - isort_off = True - skip_file = True - - if not in_quote: - if stripped_line == "# isort: off": - isort_off = True - elif stripped_line.startswith("# isort: dont-add-imports"): - add_imports = [] - elif stripped_line.startswith("# isort: dont-add-import:"): - import_not_to_add = stripped_line.split("# isort: dont-add-import:", 1)[ - 1 - ].strip() - add_imports = [ - import_to_add - for import_to_add in add_imports - if import_to_add != import_not_to_add - ] - - if ( - (index == 0 or (index in {1, 2} and not contains_imports)) - and stripped_line.startswith("#") - and stripped_line not in config.section_comments - and stripped_line not in CODE_SORT_COMMENTS - ): - in_top_comment = True - elif in_top_comment and ( - not line.startswith("#") - or stripped_line in config.section_comments - or stripped_line in CODE_SORT_COMMENTS - ): - in_top_comment = False - first_comment_index_end = index - 1 - - was_in_quote = bool(in_quote) - if ((not stripped_line.startswith("#") or in_quote) and '"' in line) or "'" in line: - char_index = 0 - if first_comment_index_start == -1 and line.startswith(('"', "'")): - first_comment_index_start = index - while char_index < len(line): - if line[char_index] == "\\": - char_index += 1 - elif in_quote: - if line[char_index : char_index + len(in_quote)] == in_quote: - in_quote = "" - if first_comment_index_end < first_comment_index_start: - first_comment_index_end = index - elif line[char_index] in ("'", '"'): - long_quote = line[char_index : char_index + 3] - if long_quote in ('"""', "'''"): - in_quote = long_quote - char_index += 2 - else: - in_quote = line[char_index] - elif line[char_index] == "#": - break - char_index += 1 - - not_imports = bool(in_quote) or was_in_quote or in_top_comment or isort_off - if not (in_quote or was_in_quote or in_top_comment): - if isort_off: - if not skip_file and stripped_line == "# isort: on": - isort_off = False - elif stripped_line.endswith("# isort: split"): - not_imports = True - elif stripped_line in CODE_SORT_COMMENTS: - code_sorting = stripped_line.split("isort: ")[1].strip() - code_sorting_indent = line[: -len(line.lstrip())] - not_imports = True - elif config.sort_reexports and stripped_line.startswith("__all__"): - _, rhs = stripped_line.split("=") - code_sorting = LITERAL_TYPE_MAPPING.get(rhs.lstrip()[0], "tuple") - code_sorting_indent = line[: -len(line.lstrip())] - not_imports = True - code_sorting_section += line - reexport_rollback = len(line) - is_reexport = True - elif code_sorting: - if not stripped_line: - sorted_code = textwrap.indent( - isort.literal.assignment( - code_sorting_section, - str(code_sorting), - extension, - config=_indented_config(config, indent), - ), - code_sorting_indent, - ) - made_changes = made_changes or _has_changed( - before=code_sorting_section, - after=sorted_code, - line_separator=line_separator, - ignore_whitespace=config.ignore_whitespace, - ) - if is_reexport: - output_stream.seek(output_stream.tell() - reexport_rollback) - reexport_rollback = 0 - output_stream.write(sorted_code) - if is_reexport: - output_stream.truncate() - not_imports = True - code_sorting = False - code_sorting_section = "" - code_sorting_indent = "" - is_reexport = False - else: - code_sorting_section += line - line = "" - elif ( - stripped_line in config.section_comments - or stripped_line in config.section_comments_end - ): - if import_section and not contains_imports: - output_stream.write(import_section) - import_section = line - not_imports = False - else: - import_section += line - indent = line[: -len(line.lstrip())] - elif not (stripped_line or contains_imports): - not_imports = True - elif not stripped_line or ( - stripped_line.startswith("#") - and (not indent or indent + line.lstrip() == line) - and not config.treat_all_comments_as_code - and stripped_line not in config.treat_comments_as_code - ): - import_section += line - elif stripped_line.startswith(IMPORT_START_IDENTIFIERS): - new_indent = line[: -len(line.lstrip())] - import_statement = line - stripped_line = line.strip().split("#")[0] - while stripped_line.endswith("\\") or ( - "(" in stripped_line and ")" not in stripped_line - ): - if stripped_line.endswith("\\"): - while stripped_line and stripped_line.endswith("\\"): - line = input_stream.readline() - stripped_line = line.strip().split("#")[0] - import_statement += line - else: - while ")" not in stripped_line: - line = input_stream.readline() - - if not line: # end of file without closing parenthesis - raise ExistingSyntaxErrors("Parenthesis is not closed") - - stripped_line = line.strip().split("#")[0] - import_statement += line - - if ( - import_statement.lstrip().startswith("from") - and "import" not in import_statement - ): - line = import_statement - not_imports = True - else: - did_contain_imports = contains_imports - contains_imports = True - - cimport_statement: bool = False - if ( - import_statement.lstrip().startswith(CIMPORT_IDENTIFIERS) - or " cimport " in import_statement - or " cimport*" in import_statement - or " cimport(" in import_statement - or ( - ".cimport" in import_statement - and "cython.cimports" not in import_statement - ) # Allow pure python imports. See #2062 - ): - cimport_statement = True - - if cimport_statement != cimports or ( - new_indent != indent - and import_section - and (not did_contain_imports or len(new_indent) < len(indent)) - ): - indent = new_indent - if import_section: - next_cimports = cimport_statement - next_import_section = import_statement - import_statement = "" - not_imports = True - line = "" - else: - cimports = cimport_statement - else: - if new_indent != indent: - if import_section and did_contain_imports: - import_statement = indent + import_statement.lstrip() - else: - indent = new_indent - import_section += import_statement - else: - not_imports = True - - if not_imports: - if not was_in_quote and config.lines_before_imports > -1: - if line.strip() == "": - lines_before += line - continue - if not import_section: - output_stream.write("".join(lines_before)) - lines_before = [] - - raw_import_section: str = import_section - if ( - add_imports - and (stripped_line or end_of_file) - and not config.append_only - and not in_top_comment - and not was_in_quote - and not import_section - and not line.lstrip().startswith(COMMENT_INDICATORS) - and not (line.rstrip().endswith(DOCSTRING_INDICATORS) and "=" not in line) - ): - add_line_separator = line_separator or "\n" - import_section = add_line_separator.join(add_imports) + add_line_separator - if end_of_file and index != 0: - output_stream.write(add_line_separator) - contains_imports = True - add_imports = [] - - if next_import_section and not import_section: # pragma: no cover - raw_import_section = import_section = next_import_section - next_import_section = "" - - if import_section: - if add_imports and (contains_imports or not config.append_only) and not indent: - import_section = ( - line_separator.join(add_imports) + line_separator + import_section - ) - contains_imports = True - add_imports = [] - - if not indent: - import_section += line - raw_import_section += line - if not contains_imports: - output_stream.write(import_section) - - else: - leading_whitespace = import_section[: -len(import_section.lstrip())] - trailing_whitespace = import_section[len(import_section.rstrip()) :] - if first_import_section and not import_section.lstrip( - line_separator - ).startswith(COMMENT_INDICATORS): - import_section = import_section.lstrip(line_separator) - raw_import_section = raw_import_section.lstrip(line_separator) - first_import_section = False - - if indent: - import_section = "".join( - line[len(indent) :] for line in import_section.splitlines(keepends=True) - ) - - parsed_content = parse.file_contents(import_section, config=config) - verbose_output += parsed_content.verbose_output - - sorted_import_section = output.sorted_imports( - parsed_content, - _indented_config(config, indent), - extension, - import_type="cimport" if cimports else "import", - ) - if not (import_section.strip() and not sorted_import_section): - if indent: - sorted_import_section = ( - leading_whitespace - + textwrap.indent(sorted_import_section, indent).strip() - + trailing_whitespace - ) - - made_changes = made_changes or _has_changed( - before=raw_import_section, - after=sorted_import_section, - line_separator=line_separator, - ignore_whitespace=config.ignore_whitespace, - ) - output_stream.write(sorted_import_section) - if not line and not indent and next_import_section: - output_stream.write(line_separator) - - if indent: - output_stream.write(line) - if not next_import_section: - indent = "" - - if next_import_section: - cimports = next_cimports - contains_imports = True - else: - contains_imports = False - import_section = next_import_section - next_import_section = "" - else: - output_stream.write(line) - not_imports = False - - if stripped_line and not in_quote and not import_section and not next_import_section: - if stripped_line == "yield": - while not stripped_line or stripped_line == "yield": - new_line = input_stream.readline() - if not new_line: - break - - output_stream.write(new_line) - stripped_line = new_line.strip().split("#")[0] - - if stripped_line.startswith(("raise", "yield")): - while stripped_line.endswith("\\"): - new_line = input_stream.readline() - if not new_line: - break - - output_stream.write(new_line) - stripped_line = new_line.strip().split("#")[0] - - if made_changes and config.only_modified: - for output_str in verbose_output: - print(output_str) - - return made_changes - - -def _indented_config(config: Config, indent: str) -> Config: - if not indent: - return config - - return Config( - config=config, - line_length=max(config.line_length - len(indent), 0), - wrap_length=max(config.wrap_length - len(indent), 0), - lines_after_imports=1, - import_headings=config.import_headings if config.indented_import_headings else {}, - import_footers=config.import_footers if config.indented_import_headings else {}, - ) - - -def _has_changed(before: str, after: str, line_separator: str, ignore_whitespace: bool) -> bool: - if ignore_whitespace: - return ( - remove_whitespace(before, line_separator=line_separator).strip() - != remove_whitespace(after, line_separator=line_separator).strip() - ) - return before.strip() != after.strip() diff --git a/.venv/lib/python3.10/site-packages/isort/deprecated/__init__.py b/.venv/lib/python3.10/site-packages/isort/deprecated/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.10/site-packages/isort/deprecated/finders.py b/.venv/lib/python3.10/site-packages/isort/deprecated/finders.py deleted file mode 100644 index 1bfb300..0000000 --- a/.venv/lib/python3.10/site-packages/isort/deprecated/finders.py +++ /dev/null @@ -1,392 +0,0 @@ -"""Finders try to find right section for passed module name""" - -import importlib.machinery -import inspect -import os -import os.path -import re -import sys -import sysconfig -from abc import ABCMeta, abstractmethod -from collections.abc import Iterable, Iterator, Sequence -from contextlib import contextmanager -from fnmatch import fnmatch -from functools import lru_cache -from glob import glob -from pathlib import Path -from re import Pattern - -from isort import sections -from isort.settings import KNOWN_SECTION_MAPPING, Config -from isort.utils import exists_case_sensitive - -try: - from pipreqs import pipreqs # type: ignore - -except ImportError: - pipreqs = None - -try: - from pip_api import parse_requirements # type: ignore - -except ImportError: - parse_requirements = None # type: ignore[assignment] - - -@contextmanager -def chdir(path: str) -> Iterator[None]: - """Context manager for changing dir and restoring previous workdir after exit.""" - curdir = os.getcwd() - os.chdir(path) - try: - yield - finally: - os.chdir(curdir) - - -class BaseFinder(metaclass=ABCMeta): - def __init__(self, config: Config) -> None: - self.config = config - - @abstractmethod - def find(self, module_name: str) -> str | None: - raise NotImplementedError - - -class ForcedSeparateFinder(BaseFinder): - def find(self, module_name: str) -> str | None: - for forced_separate in self.config.forced_separate: - # Ensure all forced_separate patterns will match to end of string - path_glob = forced_separate - if not forced_separate.endswith("*"): - path_glob = f"{forced_separate}*" - - if fnmatch(module_name, path_glob) or fnmatch(module_name, "." + path_glob): - return forced_separate - return None - - -class LocalFinder(BaseFinder): - def find(self, module_name: str) -> str | None: - if module_name.startswith("."): - return "LOCALFOLDER" - return None - - -class KnownPatternFinder(BaseFinder): - def __init__(self, config: Config) -> None: - super().__init__(config) - - self.known_patterns: list[tuple[Pattern[str], str]] = [] - for placement in reversed(config.sections): - known_placement = KNOWN_SECTION_MAPPING.get(placement, placement).lower() - config_key = f"known_{known_placement}" - known_patterns = list( - getattr(self.config, config_key, self.config.known_other.get(known_placement, [])) - ) - known_patterns = [ - pattern - for known_pattern in known_patterns - for pattern in self._parse_known_pattern(known_pattern) - ] - for known_pattern in known_patterns: - regexp = "^" + known_pattern.replace("*", ".*").replace("?", ".?") + "$" - self.known_patterns.append((re.compile(regexp), placement)) - - def _parse_known_pattern(self, pattern: str) -> list[str]: - """Expand pattern if identified as a directory and return found sub packages""" - if pattern.endswith(os.path.sep): - patterns = [ - filename - for filename in os.listdir(os.path.join(self.config.directory, pattern)) - if os.path.isdir(os.path.join(self.config.directory, pattern, filename)) - ] - else: - patterns = [pattern] - - return patterns - - def find(self, module_name: str) -> str | None: - # Try to find most specific placement instruction match (if any) - parts = module_name.split(".") - module_names_to_check = (".".join(parts[:first_k]) for first_k in range(len(parts), 0, -1)) - for module_name_to_check in module_names_to_check: - for pattern, placement in self.known_patterns: - if pattern.match(module_name_to_check): - return placement - return None - - -class PathFinder(BaseFinder): - def __init__(self, config: Config, path: str = ".") -> None: - super().__init__(config) - - # restore the original import path (i.e. not the path to bin/isort) - root_dir = os.path.abspath(path) - src_dir = f"{root_dir}/src" - self.paths = [root_dir, src_dir] - - # virtual env - self.virtual_env = self.config.virtual_env or os.environ.get("VIRTUAL_ENV") - if self.virtual_env: - self.virtual_env = os.path.realpath(self.virtual_env) - self.virtual_env_src = "" - if self.virtual_env: - self.virtual_env_src = f"{self.virtual_env}/src/" - for venv_path in glob(f"{self.virtual_env}/lib/python*/site-packages"): - if venv_path not in self.paths: - self.paths.append(venv_path) - for nested_venv_path in glob(f"{self.virtual_env}/lib/python*/*/site-packages"): - if nested_venv_path not in self.paths: - self.paths.append(nested_venv_path) - for venv_src_path in glob(f"{self.virtual_env}/src/*"): - if os.path.isdir(venv_src_path): - self.paths.append(venv_src_path) - - # conda - self.conda_env = self.config.conda_env or os.environ.get("CONDA_PREFIX") or "" - if self.conda_env: - self.conda_env = os.path.realpath(self.conda_env) - for conda_path in glob(f"{self.conda_env}/lib/python*/site-packages"): - if conda_path not in self.paths: - self.paths.append(conda_path) - for nested_conda_path in glob(f"{self.conda_env}/lib/python*/*/site-packages"): - if nested_conda_path not in self.paths: - self.paths.append(nested_conda_path) - - # handle case-insensitive paths on windows - self.stdlib_lib_prefix = os.path.normcase(sysconfig.get_paths()["stdlib"]) - if self.stdlib_lib_prefix not in self.paths: - self.paths.append(self.stdlib_lib_prefix) - - # add system paths - for system_path in sys.path[1:]: - if system_path not in self.paths: - self.paths.append(system_path) - - def find(self, module_name: str) -> str | None: - for prefix in self.paths: - package_path = "/".join((prefix, module_name.split(".")[0])) - path_obj = Path(package_path).resolve() - is_module = ( - exists_case_sensitive(package_path + ".py") - or any( - exists_case_sensitive(package_path + ext_suffix) - for ext_suffix in importlib.machinery.EXTENSION_SUFFIXES - ) - or exists_case_sensitive(package_path + "/__init__.py") - ) - is_package = exists_case_sensitive(package_path) and os.path.isdir(package_path) - if is_module or is_package: - if ( - "site-packages" in prefix - or "dist-packages" in prefix - or (self.virtual_env and self.virtual_env_src in prefix) - ): - return sections.THIRDPARTY - if os.path.normcase(prefix) == self.stdlib_lib_prefix: - return sections.STDLIB - if self.conda_env and self.conda_env in prefix: - return sections.THIRDPARTY - for src_path in self.config.src_paths: - if src_path in path_obj.parents and not self.config.is_skipped(path_obj): - return sections.FIRSTPARTY - - if os.path.normcase(prefix).startswith(self.stdlib_lib_prefix): - return sections.STDLIB # pragma: no cover - edge case for one OS. Hard to test. - - return self.config.default_section - return None - - -class ReqsBaseFinder(BaseFinder): - enabled = False - - def __init__(self, config: Config, path: str = ".") -> None: - super().__init__(config) - self.path = path - if self.enabled: - self.mapping = self._load_mapping() - self.names = self._load_names() - - @abstractmethod - def _get_names(self, path: str) -> Iterator[str]: - raise NotImplementedError - - @abstractmethod - def _get_files_from_dir(self, path: str) -> Iterator[str]: - raise NotImplementedError - - @staticmethod - def _load_mapping() -> dict[str, str] | None: - """Return list of mappings `package_name -> module_name` - - Example: - django-haystack -> haystack - """ - if not pipreqs: - return None - path = os.path.dirname(inspect.getfile(pipreqs)) - path = os.path.join(path, "mapping") - with open(path) as f: - mappings: dict[str, str] = {} # pypi_name: import_name - for line in f: - import_name, _, pypi_name = line.strip().partition(":") - mappings[pypi_name] = import_name - return mappings - - def _load_names(self) -> list[str]: - """Return list of thirdparty modules from requirements""" - names: list[str] = [] - for path in self._get_files(): - names.extend(self._normalize_name(name) for name in self._get_names(path)) - return names - - @staticmethod - def _get_parents(path: str) -> Iterator[str]: - prev = "" - while path != prev: - prev = path - yield path - path = os.path.dirname(path) - - def _get_files(self) -> Iterator[str]: - """Return paths to all requirements files""" - path = os.path.abspath(self.path) - if os.path.isfile(path): - path = os.path.dirname(path) - - for path in self._get_parents(path): # noqa - yield from self._get_files_from_dir(path) - - def _normalize_name(self, name: str) -> str: - """Convert package name to module name - - Examples: - Django -> django - django-haystack -> django_haystack - Flask-RESTFul -> flask_restful - """ - if self.mapping: - name = self.mapping.get(name.replace("-", "_"), name) - return name.lower().replace("-", "_") - - def find(self, module_name: str) -> str | None: - # required lib not installed yet - if not self.enabled: - return None - - module_name, _sep, _submodules = module_name.partition(".") - module_name = module_name.lower() - if not module_name: - return None - - for name in self.names: - if module_name == name: - return sections.THIRDPARTY - return None - - -class RequirementsFinder(ReqsBaseFinder): - exts = (".txt", ".in") - enabled = bool(parse_requirements) - - def _get_files_from_dir(self, path: str) -> Iterator[str]: - """Return paths to requirements files from passed dir.""" - yield from self._get_files_from_dir_cached(path) - - @classmethod - @lru_cache(maxsize=16) - def _get_files_from_dir_cached(cls, path: str) -> list[str]: - results: list[str] = [] - - for fname in os.listdir(path): - if "requirements" not in fname: - continue - full_path = os.path.join(path, fname) - - # *requirements*/*.{txt,in} - if os.path.isdir(full_path): - for subfile_name in os.listdir(full_path): - results.extend( - os.path.join(full_path, subfile_name) - for ext in cls.exts - if subfile_name.endswith(ext) - ) - continue - - # *requirements*.{txt,in} - if os.path.isfile(full_path): - for ext in cls.exts: - if fname.endswith(ext): - results.append(full_path) - break - - return results - - def _get_names(self, path: str) -> Iterator[str]: - """Load required packages from path to requirements file""" - yield from self._get_names_cached(path) - - @classmethod - @lru_cache(maxsize=16) - def _get_names_cached(cls, path: str) -> list[str]: - result: list[str] = [] - - with chdir(os.path.dirname(path)): - requirements = parse_requirements(Path(path)) - result.extend(req.name for req in requirements.values() if req.name) - - return result - - -class DefaultFinder(BaseFinder): - def find(self, module_name: str) -> str | None: - return self.config.default_section - - -class FindersManager: - _default_finders_classes: Sequence[type[BaseFinder]] = ( - ForcedSeparateFinder, - LocalFinder, - KnownPatternFinder, - PathFinder, - RequirementsFinder, - DefaultFinder, - ) - - def __init__( - self, config: Config, finder_classes: Iterable[type[BaseFinder]] | None = None - ) -> None: - self.verbose: bool = config.verbose - - if finder_classes is None: - finder_classes = self._default_finders_classes - finders: list[BaseFinder] = [] - for finder_cls in finder_classes: - try: - finders.append(finder_cls(config)) - except Exception as exception: - # if one finder fails to instantiate isort can continue using the rest - if self.verbose: - print( - f"{finder_cls.__name__} encountered an error ({exception}) during " - "instantiation and cannot be used" - ) - self.finders: tuple[BaseFinder, ...] = tuple(finders) - - def find(self, module_name: str) -> str | None: - for finder in self.finders: - try: - section = finder.find(module_name) - if section is not None: - return section - except Exception as exception: - # isort has to be able to keep trying to identify the correct - # import section even if one approach fails - if self.verbose: - print( - f"{finder.__class__.__name__} encountered an error ({exception}) while " - f"trying to identify the {module_name} module" - ) - return None diff --git a/.venv/lib/python3.10/site-packages/isort/exceptions.py b/.venv/lib/python3.10/site-packages/isort/exceptions.py deleted file mode 100644 index 34d4e30..0000000 --- a/.venv/lib/python3.10/site-packages/isort/exceptions.py +++ /dev/null @@ -1,197 +0,0 @@ -"""All isort specific exception classes should be defined here""" - -from functools import partial -from pathlib import Path -from typing import Any - -from .profiles import profiles - - -class ISortError(Exception): - """Base isort exception object from which all isort sourced exceptions should inherit""" - - def __reduce__(self): # type: ignore - return (partial(type(self), **self.__dict__), ()) - - -class InvalidSettingsPath(ISortError): - """Raised when a settings path is provided that is neither a valid file or directory""" - - def __init__(self, settings_path: str): - super().__init__( - f"isort was told to use the settings_path: {settings_path} as the base directory or " - "file that represents the starting point of config file discovery, but it does not " - "exist." - ) - self.settings_path = settings_path - - -class ExistingSyntaxErrors(ISortError): - """Raised when isort is told to sort imports within code that has existing syntax errors""" - - def __init__(self, file_path: str): - super().__init__( - f"isort was told to sort imports within code that contains syntax errors: {file_path}." - ) - self.file_path = file_path - - -class IntroducedSyntaxErrors(ISortError): - """Raised when isort has introduced a syntax error in the process of sorting imports""" - - def __init__(self, file_path: str): - super().__init__( - f"isort introduced syntax errors when attempting to sort the imports contained within " - f"{file_path}." - ) - self.file_path = file_path - - -class FileSkipped(ISortError): - """Should be raised when a file is skipped for any reason""" - - def __init__(self, message: str, file_path: str): - super().__init__(message) - self.message = message - self.file_path = file_path - - -class FileSkipComment(FileSkipped): - """Raised when an entire file is skipped due to a isort skip file comment""" - - def __init__(self, file_path: str, **kwargs: str): - super().__init__( - f"{file_path} contains a file skip comment and was skipped.", file_path=file_path - ) - - -class FileSkipSetting(FileSkipped): - """Raised when an entire file is skipped due to provided isort settings""" - - def __init__(self, file_path: str, **kwargs: str): - super().__init__( - f"{file_path} was skipped as it's listed in 'skip' setting" - " or matches a glob in 'skip_glob' setting", - file_path=file_path, - ) - - -class ProfileDoesNotExist(ISortError): - """Raised when a profile is set by the user that doesn't exist""" - - def __init__(self, profile: str): - super().__init__( - f"Specified profile of {profile} does not exist. " - f"Available profiles: {','.join(profiles)}." - ) - self.profile = profile - - -class SortingFunctionDoesNotExist(ISortError): - """Raised when the specified sorting function isn't available""" - - def __init__(self, sort_order: str, available_sort_orders: list[str]): - super().__init__( - f"Specified sort_order of {sort_order} does not exist. " - f"Available sort_orders: {','.join(available_sort_orders)}." - ) - self.sort_order = sort_order - self.available_sort_orders = available_sort_orders - - -class FormattingPluginDoesNotExist(ISortError): - """Raised when a formatting plugin is set by the user that doesn't exist""" - - def __init__(self, formatter: str): - super().__init__(f"Specified formatting plugin of {formatter} does not exist. ") - self.formatter = formatter - - -class LiteralParsingFailure(ISortError): - """Raised when one of isorts literal sorting comments is used but isort can't parse the - the given data structure. - """ - - def __init__(self, code: str, original_error: Exception | type[Exception]): - super().__init__( - f"isort failed to parse the given literal {code}. It's important to note " - "that isort literal sorting only supports simple literals parsable by " - f"ast.literal_eval which gave the exception of {original_error}." - ) - self.code = code - self.original_error = original_error - - -class LiteralSortTypeMismatch(ISortError): - """Raised when an isort literal sorting comment is used, with a type that doesn't match the - supplied data structure's type. - """ - - def __init__(self, kind: type, expected_kind: type): - super().__init__( - f"isort was told to sort a literal of type {expected_kind} but was given " - f"a literal of type {kind}." - ) - self.kind = kind - self.expected_kind = expected_kind - - -class AssignmentsFormatMismatch(ISortError): - """Raised when isort is told to sort assignments but the format of the assignment section - doesn't match isort's expectation. - """ - - def __init__(self, code: str): - super().__init__( - "isort was told to sort a section of assignments, however the given code:\n\n" - f"{code}\n\n" - "Does not match isort's strict single line formatting requirement for assignment " - "sorting:\n\n" - "{variable_name} = {value}\n" - "{variable_name2} = {value2}\n" - "...\n\n" - ) - self.code = code - - -class UnsupportedSettings(ISortError): - """Raised when settings are passed into isort (either from config, CLI, or runtime) - that it doesn't support. - """ - - @staticmethod - def _format_option(name: str, value: Any, source: str) -> str: - return f"\t- {name} = {value} (source: '{source}')" - - def __init__(self, unsupported_settings: dict[str, dict[str, str]]): - errors = "\n".join( - self._format_option(name, **option) for name, option in unsupported_settings.items() - ) - - super().__init__( - "isort was provided settings that it doesn't support:\n\n" - f"{errors}\n\n" - "For a complete and up-to-date listing of supported settings see: " - "https://pycqa.github.io/isort/docs/configuration/options.\n" - ) - self.unsupported_settings = unsupported_settings - - -class UnsupportedEncoding(ISortError): - """Raised when isort encounters an encoding error while trying to read a file""" - - def __init__(self, filename: str | Path): - super().__init__(f"Unknown or unsupported encoding in {filename}") - self.filename = filename - - -class MissingSection(ISortError): - """Raised when isort encounters an import that matches a section that is not defined""" - - def __init__(self, import_module: str, section: str): - super().__init__( - f"Found {import_module} import while parsing, but {section} was not included " - "in the `sections` setting of your config. Please add it before continuing\n" - "See https://pycqa.github.io/isort/#custom-sections-and-ordering " - "for more info." - ) diff --git a/.venv/lib/python3.10/site-packages/isort/files.py b/.venv/lib/python3.10/site-packages/isort/files.py deleted file mode 100644 index c23674a..0000000 --- a/.venv/lib/python3.10/site-packages/isort/files.py +++ /dev/null @@ -1,41 +0,0 @@ -import os -from collections.abc import Iterable, Iterator -from pathlib import Path - -from isort.settings import Config - - -def find( - paths: Iterable[str], config: Config, skipped: list[str], broken: list[str] -) -> Iterator[str]: - """Fines and provides an iterator for all Python source files defined in paths.""" - visited_dirs: set[Path] = set() - - for path in paths: - if os.path.isdir(path): - for dirpath, dirnames, filenames in os.walk( - path, topdown=True, followlinks=config.follow_links - ): - base_path = Path(dirpath) - for dirname in list(dirnames): - full_path = base_path / dirname - resolved_path = full_path.resolve() - if config.is_skipped(full_path): - skipped.append(str(full_path)) - dirnames.remove(dirname) - else: - if resolved_path in visited_dirs: # pragma: no cover - dirnames.remove(dirname) - visited_dirs.add(resolved_path) - - for filename in filenames: - filepath = os.path.join(dirpath, filename) - if config.is_supported_filetype(filepath): - if config.is_skipped(Path(os.path.abspath(filepath))): - skipped.append(os.path.abspath(filepath)) - else: - yield filepath - elif not os.path.exists(path): - broken.append(path) - else: - yield path diff --git a/.venv/lib/python3.10/site-packages/isort/format.py b/.venv/lib/python3.10/site-packages/isort/format.py deleted file mode 100644 index 5ad9f47..0000000 --- a/.venv/lib/python3.10/site-packages/isort/format.py +++ /dev/null @@ -1,157 +0,0 @@ -import re -import sys -from datetime import datetime -from difflib import unified_diff -from pathlib import Path -from typing import TextIO - -try: - import colorama -except ImportError: - colorama_unavailable = True -else: - colorama_unavailable = False - - -ADDED_LINE_PATTERN = re.compile(r"\+[^+]") -REMOVED_LINE_PATTERN = re.compile(r"-[^-]") - - -def format_simplified(import_line: str) -> str: - import_line = import_line.strip() - if import_line.startswith("from "): - import_line = import_line.replace("from ", "") - import_line = import_line.replace(" import ", ".") - elif import_line.startswith("import "): - import_line = import_line.replace("import ", "") - - return import_line - - -def format_natural(import_line: str) -> str: - import_line = import_line.strip() - if not import_line.startswith("from ") and not import_line.startswith("import "): - if "." not in import_line: - return f"import {import_line}" - parts = import_line.split(".") - end = parts.pop(-1) - return f"from {'.'.join(parts)} import {end}" - - return import_line - - -def show_unified_diff( - *, - file_input: str, - file_output: str, - file_path: Path | None, - output: TextIO | None = None, - color_output: bool = False, -) -> None: - """Shows a unified_diff for the provided input and output against the provided file path. - - - **file_input**: A string that represents the contents of a file before changes. - - **file_output**: A string that represents the contents of a file after changes. - - **file_path**: A Path object that represents the file path of the file being changed. - - **output**: A stream to output the diff to. If non is provided uses sys.stdout. - - **color_output**: Use color in output if True. - """ - printer = create_terminal_printer(color_output, output) - file_name = "" if file_path is None else str(file_path) - file_mtime = str( - datetime.now() if file_path is None else datetime.fromtimestamp(file_path.stat().st_mtime) - ) - unified_diff_lines = unified_diff( - file_input.splitlines(keepends=True), - file_output.splitlines(keepends=True), - fromfile=file_name + ":before", - tofile=file_name + ":after", - fromfiledate=file_mtime, - tofiledate=str(datetime.now()), - ) - for line in unified_diff_lines: - printer.diff_line(line) - - -def ask_whether_to_apply_changes_to_file(file_path: str) -> bool: - answer = None - while answer not in ("yes", "y", "no", "n", "quit", "q"): - answer = input(f"Apply suggested changes to '{file_path}' [y/n/q]? ") # nosec - answer = answer.lower() - if answer in ("no", "n"): - return False - if answer in ("quit", "q"): - sys.exit(1) - return True - - -def remove_whitespace(content: str, line_separator: str = "\n") -> str: - content = content.replace(line_separator, "").replace(" ", "").replace("\x0c", "") - return content - - -class BasicPrinter: - ERROR = "ERROR" - SUCCESS = "SUCCESS" - - def __init__(self, error: str, success: str, output: TextIO | None = None): - self.output = output or sys.stdout - self.success_message = success - self.error_message = error - - def success(self, message: str) -> None: - print(self.success_message.format(success=self.SUCCESS, message=message), file=self.output) - - def error(self, message: str) -> None: - print(self.error_message.format(error=self.ERROR, message=message), file=sys.stderr) - - def diff_line(self, line: str) -> None: - self.output.write(line) - - -class ColoramaPrinter(BasicPrinter): - def __init__(self, error: str, success: str, output: TextIO | None): - super().__init__(error, success, output=output) - - # Note: this constants are instance variables instead ofs class variables - # because they refer to colorama which might not be installed. - self.ERROR = self.style_text("ERROR", colorama.Fore.RED) - self.SUCCESS = self.style_text("SUCCESS", colorama.Fore.GREEN) - self.ADDED_LINE = colorama.Fore.GREEN - self.REMOVED_LINE = colorama.Fore.RED - - @staticmethod - def style_text(text: str, style: str | None = None) -> str: - if style is None: - return text - return style + text + str(colorama.Style.RESET_ALL) - - def diff_line(self, line: str) -> None: - style = None - if re.match(ADDED_LINE_PATTERN, line): - style = self.ADDED_LINE - elif re.match(REMOVED_LINE_PATTERN, line): - style = self.REMOVED_LINE - self.output.write(self.style_text(line, style)) - - -def create_terminal_printer( - color: bool, output: TextIO | None = None, error: str = "", success: str = "" -) -> BasicPrinter: - if color and colorama_unavailable: - no_colorama_message = ( - "\n" - "Sorry, but to use --color (color_output) the colorama python package is required.\n\n" - "Reference: https://pypi.org/project/colorama/\n\n" - "You can either install it separately on your system or as the colors extra " - "for isort. Ex: \n\n" - "$ pip install isort[colors]\n" - ) - print(no_colorama_message, file=sys.stderr) - sys.exit(1) - - if not colorama_unavailable: - colorama.init(strip=False) - return ( - ColoramaPrinter(error, success, output) if color else BasicPrinter(error, success, output) - ) diff --git a/.venv/lib/python3.10/site-packages/isort/hooks.py b/.venv/lib/python3.10/site-packages/isort/hooks.py deleted file mode 100644 index af9020f..0000000 --- a/.venv/lib/python3.10/site-packages/isort/hooks.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Defines a git hook to allow pre-commit warnings and errors about import order. - -usage: - exit_code = git_hook(strict=True|False, modify=True|False) -""" - -import os -import subprocess # nosec -from pathlib import Path - -from isort import Config, api, exceptions - - -def get_output(command: list[str]) -> str: - """Run a command and return raw output - - :param str command: the command to run - :returns: the stdout output of the command - """ - result = subprocess.run(command, stdout=subprocess.PIPE, check=True) # nosec - return result.stdout.decode() - - -def get_lines(command: list[str]) -> list[str]: - """Run a command and return lines of output - - :param str command: the command to run - :returns: list of whitespace-stripped lines output by command - """ - stdout = get_output(command) - return [line.strip() for line in stdout.splitlines()] - - -def git_hook( - strict: bool = False, - modify: bool = False, - lazy: bool = False, - settings_file: str = "", - directories: list[str] | None = None, -) -> int: - """Git pre-commit hook to check staged files for isort errors - - :param bool strict - if True, return number of errors on exit, - causing the hook to fail. If False, return zero so it will - just act as a warning. - :param bool modify - if True, fix the sources if they are not - sorted properly. If False, only report result without - modifying anything. - :param bool lazy - if True, also check/fix unstaged files. - This is useful if you frequently use ``git commit -a`` for example. - If False, only check/fix the staged files for isort errors. - :param str settings_file - A path to a file to be used as - the configuration file for this run. - When settings_file is the empty string, the configuration file - will be searched starting at the directory containing the first - staged file, if any, and going upward in the directory structure. - :param list[str] directories - A list of directories to restrict the hook to. - - :return number of errors if in strict mode, 0 otherwise. - """ - # Get list of files modified and staged - diff_cmd = ["git", "diff-index", "--cached", "--name-only", "--diff-filter=ACMRTUXB", "HEAD"] - if lazy: - diff_cmd.remove("--cached") - if directories: - diff_cmd.extend(directories) - - files_modified = get_lines(diff_cmd) - if not files_modified: - return 0 - - errors = 0 - config = Config( - settings_file=settings_file, - settings_path=os.path.dirname(os.path.abspath(files_modified[0])), - ) - for filename in files_modified: - if filename.endswith(".py"): - # Get the staged contents of the file - staged_cmd = ["git", "show", f":{filename}"] - staged_contents = get_output(staged_cmd) - - try: - if not api.check_code_string( - staged_contents, file_path=Path(filename), config=config - ): - errors += 1 - if modify: - api.sort_file(filename, config=config) - except exceptions.FileSkipped: # pragma: no cover - pass - - return errors if strict else 0 diff --git a/.venv/lib/python3.10/site-packages/isort/identify.py b/.venv/lib/python3.10/site-packages/isort/identify.py deleted file mode 100644 index fad5330..0000000 --- a/.venv/lib/python3.10/site-packages/isort/identify.py +++ /dev/null @@ -1,208 +0,0 @@ -"""Fast stream based import identification. -Eventually this will likely replace parse.py -""" - -from collections.abc import Iterator -from functools import partial -from pathlib import Path -from typing import NamedTuple, TextIO - -from isort.parse import normalize_line, skip_line, strip_syntax - -from .comments import parse as parse_comments -from .settings import DEFAULT_CONFIG, Config - -STATEMENT_DECLARATIONS: tuple[str, ...] = ("def ", "cdef ", "cpdef ", "class ", "@", "async def") - - -class Import(NamedTuple): - line_number: int - indented: bool - module: str - attribute: str | None = None - alias: str | None = None - cimport: bool = False - file_path: Path | None = None - - def statement(self) -> str: - import_cmd = "cimport" if self.cimport else "import" - if self.attribute: - import_string = f"from {self.module} {import_cmd} {self.attribute}" - else: - import_string = f"{import_cmd} {self.module}" - if self.alias: - import_string += f" as {self.alias}" - return import_string - - def __str__(self) -> str: - return ( - f"{self.file_path or ''}:{self.line_number} " - f"{'indented ' if self.indented else ''}{self.statement()}" - ) - - -def imports( - input_stream: TextIO, - config: Config = DEFAULT_CONFIG, - file_path: Path | None = None, - top_only: bool = False, -) -> Iterator[Import]: - """Parses a python file taking out and categorizing imports.""" - in_quote = "" - - indexed_input = enumerate(input_stream) - for index, raw_line in indexed_input: - (skipping_line, in_quote) = skip_line( - raw_line, in_quote=in_quote, index=index, section_comments=config.section_comments - ) - - if top_only and not in_quote and raw_line.startswith(STATEMENT_DECLARATIONS): - break - if skipping_line: - continue - - stripped_line = raw_line.strip().split("#")[0] - if stripped_line.startswith(("raise", "yield")): - if stripped_line == "yield": - while not stripped_line or stripped_line == "yield": - try: - index, next_line = next(indexed_input) - except StopIteration: - break - - stripped_line = next_line.strip().split("#")[0] - while stripped_line.endswith("\\"): - try: - index, next_line = next(indexed_input) - except StopIteration: - break - - stripped_line = next_line.strip().split("#")[0] - continue # pragma: no cover - - line, *end_of_line_comment = raw_line.split("#", 1) - statements = [line.strip() for line in line.split(";")] - if end_of_line_comment: - statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" - - for statement in statements: - line, _raw_line = normalize_line(statement) - if line.startswith(("import ", "cimport ")): - type_of_import = "straight" - elif line.startswith("from "): - type_of_import = "from" - else: - continue # pragma: no cover - - import_string, _ = parse_comments(line) - normalized_import_string = ( - import_string.replace("import(", "import (").replace("\\", " ").replace("\n", " ") - ) - cimports: bool = ( - " cimport " in normalized_import_string - or normalized_import_string.startswith("cimport") - ) - identified_import = partial( - Import, - index + 1, # line numbers use 1 based indexing - raw_line.startswith((" ", "\t")), - cimport=cimports, - file_path=file_path, - ) - - if "(" in line.split("#", 1)[0]: - while not line.split("#")[0].strip().endswith(")"): - try: - index, next_line = next(indexed_input) - except StopIteration: - break - - line, _ = parse_comments(next_line) - import_string += "\n" + line - else: - while line.strip().endswith("\\"): - try: - index, next_line = next(indexed_input) - except StopIteration: - break - - line, _ = parse_comments(next_line) - - # Still need to check for parentheses after an escaped line - if "(" in line.split("#")[0] and ")" not in line.split("#")[0]: - import_string += "\n" + line - - while not line.split("#")[0].strip().endswith(")"): - try: - index, next_line = next(indexed_input) - except StopIteration: - break - line, _ = parse_comments(next_line) - import_string += "\n" + line - else: - if import_string.strip().endswith( - (" import", " cimport") - ) or line.strip().startswith(("import ", "cimport ")): - import_string += "\n" + line - else: - import_string = ( - import_string.rstrip().rstrip("\\") + " " + line.lstrip() - ) - - if type_of_import == "from": - import_string = ( - import_string.replace("import(", "import (") - .replace("\\", " ") - .replace("\n", " ") - ) - parts = import_string.split(" cimport " if cimports else " import ") - - from_import = parts[0].split(" ") - import_string = (" cimport " if cimports else " import ").join( - [from_import[0] + " " + "".join(from_import[1:]), *parts[1:]] - ) - - just_imports = [ - item.replace("{|", "{ ").replace("|}", " }") - for item in strip_syntax(import_string).split() - ] - - direct_imports = just_imports[1:] - top_level_module = "" - if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): - while "as" in just_imports: - attribute = None - as_index = just_imports.index("as") - if type_of_import == "from": - attribute = just_imports[as_index - 1] - top_level_module = just_imports[0] - module = top_level_module + "." + attribute - alias = just_imports[as_index + 1] - direct_imports.remove(attribute) - direct_imports.remove(alias) - direct_imports.remove("as") - just_imports[1:] = direct_imports - if attribute == alias and config.remove_redundant_aliases: - yield identified_import(top_level_module, attribute) - else: - yield identified_import(top_level_module, attribute, alias=alias) - - else: - module = just_imports[as_index - 1] - alias = just_imports[as_index + 1] - just_imports.remove(alias) - just_imports.remove("as") - just_imports.remove(module) - if module == alias and config.remove_redundant_aliases: - yield identified_import(module) - else: - yield identified_import(module, alias=alias) - - if just_imports: - if type_of_import == "from": - module = just_imports.pop(0) - for attribute in just_imports: - yield identified_import(module, attribute) - else: - for module in just_imports: - yield identified_import(module) diff --git a/.venv/lib/python3.10/site-packages/isort/io.py b/.venv/lib/python3.10/site-packages/isort/io.py deleted file mode 100644 index deeda1e..0000000 --- a/.venv/lib/python3.10/site-packages/isort/io.py +++ /dev/null @@ -1,73 +0,0 @@ -"""Defines any IO utilities used by isort""" - -import dataclasses -import re -import tokenize -from collections.abc import Callable, Iterator -from contextlib import contextmanager -from io import BytesIO, StringIO, TextIOWrapper -from pathlib import Path -from typing import Any, TextIO - -from isort.exceptions import UnsupportedEncoding - -_ENCODING_PATTERN = re.compile(rb"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)") - - -@dataclasses.dataclass(frozen=True) -class File: - stream: TextIO - path: Path - encoding: str - - @staticmethod - def detect_encoding(filename: str | Path, readline: Callable[[], bytes]) -> str: - try: - return tokenize.detect_encoding(readline)[0] - except Exception: - raise UnsupportedEncoding(filename) - - @staticmethod - def from_contents(contents: str, filename: str) -> "File": - encoding = File.detect_encoding(filename, BytesIO(contents.encode("utf-8")).readline) - return File(stream=StringIO(contents), path=Path(filename).resolve(), encoding=encoding) - - @property - def extension(self) -> str: - return self.path.suffix.lstrip(".") - - @staticmethod - def _open(filename: str | Path) -> TextIOWrapper: - """Open a file in read only mode using the encoding detected by - detect_encoding(). - """ - buffer = open(filename, "rb") - try: - encoding = File.detect_encoding(filename, buffer.readline) - buffer.seek(0) - text = TextIOWrapper(buffer, encoding, line_buffering=True, newline="") - text.mode = "r" # type: ignore - return text - except Exception: - buffer.close() - raise - - @staticmethod - @contextmanager - def read(filename: str | Path) -> Iterator["File"]: - file_path = Path(filename).resolve() - stream = None - try: - stream = File._open(file_path) - yield File(stream=stream, path=file_path, encoding=stream.encoding) - finally: - if stream is not None: - stream.close() - - -class _EmptyIO(StringIO): - def write(self, *args: Any, **kwargs: Any) -> None: # type: ignore # skipcq: PTC-W0049 - pass - - -Empty = _EmptyIO() diff --git a/.venv/lib/python3.10/site-packages/isort/literal.py b/.venv/lib/python3.10/site-packages/isort/literal.py deleted file mode 100644 index 16ddcba..0000000 --- a/.venv/lib/python3.10/site-packages/isort/literal.py +++ /dev/null @@ -1,115 +0,0 @@ -import ast -from collections.abc import Callable -from pprint import PrettyPrinter -from typing import Any - -from isort.exceptions import ( - AssignmentsFormatMismatch, - LiteralParsingFailure, - LiteralSortTypeMismatch, -) -from isort.settings import DEFAULT_CONFIG, Config - - -class ISortPrettyPrinter(PrettyPrinter): - """an isort customized pretty printer for sorted literals""" - - def __init__(self, config: Config): - super().__init__(width=config.line_length, compact=True) - - -type_mapping: dict[str, tuple[type, Callable[[Any, ISortPrettyPrinter], str]]] = {} - - -def assignments(code: str) -> str: - values = {} - for line in code.splitlines(keepends=True): - if not line.strip(): - continue - if " = " not in line: - raise AssignmentsFormatMismatch(code) - variable_name, value = line.split(" = ", 1) - values[variable_name] = value - - return "".join( - f"{variable_name} = {values[variable_name]}" for variable_name in sorted(values.keys()) - ) - - -def assignment(code: str, sort_type: str, extension: str, config: Config = DEFAULT_CONFIG) -> str: - """Sorts the literal present within the provided code against the provided sort type, - returning the sorted representation of the source code. - """ - if sort_type == "assignments": - return assignments(code) - if sort_type not in type_mapping: - raise ValueError( - "Trying to sort using an undefined sort_type. " - f"Defined sort types are {', '.join(type_mapping.keys())}." - ) - - variable_name, literal = code.split("=") - variable_name = variable_name.strip() - literal = literal.lstrip() - try: - value = ast.literal_eval(literal) - except Exception as error: - raise LiteralParsingFailure(code, error) - - expected_type, sort_function = type_mapping[sort_type] - if type(value) is not expected_type: - raise LiteralSortTypeMismatch(type(value), expected_type) - - printer = ISortPrettyPrinter(config) - sorted_value_code = f"{variable_name} = {sort_function(value, printer)}" - if config.formatting_function: - sorted_value_code = config.formatting_function( - sorted_value_code, extension, config - ).rstrip() - - sorted_value_code += code[len(code.rstrip()) :] - return sorted_value_code - - -def register_type( - name: str, kind: type -) -> Callable[[Callable[[Any, ISortPrettyPrinter], str]], Callable[[Any, ISortPrettyPrinter], str]]: - """Registers a new literal sort type.""" - - def wrap( - function: Callable[[Any, ISortPrettyPrinter], str], - ) -> Callable[[Any, ISortPrettyPrinter], str]: - type_mapping[name] = (kind, function) - return function - - return wrap - - -@register_type("dict", dict) -def _dict(value: dict[Any, Any], printer: ISortPrettyPrinter) -> str: - return printer.pformat(dict(sorted(value.items(), key=lambda item: item[1]))) - - -@register_type("list", list) -def _list(value: list[Any], printer: ISortPrettyPrinter) -> str: - return printer.pformat(sorted(value)) - - -@register_type("unique-list", list) -def _unique_list(value: list[Any], printer: ISortPrettyPrinter) -> str: - return printer.pformat(sorted(set(value))) - - -@register_type("set", set) -def _set(value: set[Any], printer: ISortPrettyPrinter) -> str: - return "{" + printer.pformat(tuple(sorted(value)))[1:-1] + "}" - - -@register_type("tuple", tuple) -def _tuple(value: tuple[Any, ...], printer: ISortPrettyPrinter) -> str: - return printer.pformat(tuple(sorted(value))) - - -@register_type("unique-tuple", tuple) -def _unique_tuple(value: tuple[Any, ...], printer: ISortPrettyPrinter) -> str: - return printer.pformat(tuple(sorted(set(value)))) diff --git a/.venv/lib/python3.10/site-packages/isort/logo.py b/.venv/lib/python3.10/site-packages/isort/logo.py deleted file mode 100644 index 6377d86..0000000 --- a/.venv/lib/python3.10/site-packages/isort/logo.py +++ /dev/null @@ -1,19 +0,0 @@ -from ._version import __version__ - -ASCII_ART = rf""" - _ _ - (_) ___ ___ _ __| |_ - | |/ _/ / _ \/ '__ _/ - | |\__ \/\_\/| | | |_ - |_|\___/\___/\_/ \_/ - - isort your imports, so you don't have to. - - VERSION {__version__} -""" - -__doc__ = f""" -```python -{ASCII_ART} -``` -""" diff --git a/.venv/lib/python3.10/site-packages/isort/main.py b/.venv/lib/python3.10/site-packages/isort/main.py deleted file mode 100644 index f5408f6..0000000 --- a/.venv/lib/python3.10/site-packages/isort/main.py +++ /dev/null @@ -1,1308 +0,0 @@ -"""Tool for sorting imports alphabetically, and automatically separated into sections.""" - -import argparse -import functools -import json -import os -import sys -from collections.abc import Sequence -from gettext import gettext as _ -from io import TextIOWrapper -from pathlib import Path -from typing import Any -from warnings import warn - -from . import __version__, api, files, sections -from .exceptions import FileSkipped, ISortError, UnsupportedEncoding -from .format import create_terminal_printer -from .logo import ASCII_ART -from .profiles import profiles -from .settings import VALID_PY_TARGETS, Config, find_all_configs -from .utils import Trie -from .wrap_modes import WrapModes - -DEPRECATED_SINGLE_DASH_ARGS = { - "-ac", - "-af", - "-ca", - "-cs", - "-df", - "-ds", - "-dt", - "-fas", - "-fass", - "-ff", - "-fgw", - "-fss", - "-lai", - "-lbt", - "-le", - "-ls", - "-nis", - "-nlb", - "-ot", - "-rr", - "-sd", - "-sg", - "-sl", - "-sp", - "-tc", - "-wl", - "-ws", -} -QUICK_GUIDE = f""" -{ASCII_ART} - -Nothing to do: no files or paths have been passed in! - -Try one of the following: - - `isort .` - sort all Python files, starting from the current directory, recursively. - `isort . --interactive` - Do the same, but ask before making any changes. - `isort . --check --diff` - Check to see if imports are correctly sorted within this project. - `isort --help` - In-depth information about isort's available command-line options. - -Visit https://pycqa.github.io/isort/ for complete information about how to use isort. -""" - - -class SortAttempt: - def __init__(self, incorrectly_sorted: bool, skipped: bool, supported_encoding: bool) -> None: - self.incorrectly_sorted = incorrectly_sorted - self.skipped = skipped - self.supported_encoding = supported_encoding - - -def sort_imports( - file_name: str, - config: Config, - check: bool = False, - ask_to_apply: bool = False, - write_to_stdout: bool = False, - **kwargs: Any, -) -> SortAttempt | None: - incorrectly_sorted: bool = False - skipped: bool = False - try: - if check: - try: - incorrectly_sorted = not api.check_file(file_name, config=config, **kwargs) - except FileSkipped: - skipped = True - return SortAttempt(incorrectly_sorted, skipped, True) - - try: - incorrectly_sorted = not api.sort_file( - file_name, - config=config, - ask_to_apply=ask_to_apply, - write_to_stdout=write_to_stdout, - **kwargs, - ) - except FileSkipped: - skipped = True - return SortAttempt(incorrectly_sorted, skipped, True) - except (OSError, ValueError) as error: - warn(f"Unable to parse file {file_name} due to {error}", stacklevel=2) - return None - except UnsupportedEncoding: - if config.verbose: - warn(f"Encoding not supported for {file_name}", stacklevel=2) - return SortAttempt(incorrectly_sorted, skipped, False) - except ISortError as error: - _print_hard_fail(config, message=str(error)) - sys.exit(1) - except Exception: - _print_hard_fail(config, offending_file=file_name) - raise - - -def _print_hard_fail( - config: Config, offending_file: str | None = None, message: str | None = None -) -> None: - """Fail on unrecoverable exception with custom message.""" - message = message or ( - f"Unrecoverable exception thrown when parsing {offending_file or ''}! " - "This should NEVER happen.\n" - "If encountered, please open an issue: https://github.com/PyCQA/isort/issues/new" - ) - printer = create_terminal_printer( - color=config.color_output, error=config.format_error, success=config.format_success - ) - printer.error(message) - - -def _build_arg_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser( - description="Sort Python import definitions alphabetically " - "within logical sections. Run with no arguments to see a quick " - "start guide, otherwise, one or more files/directories/stdin must be provided. " - "Use `-` as the first argument to represent stdin. Use --interactive to use the pre 5.0.0 " - "interactive behavior." - " " - "If you've used isort 4 but are new to isort 5, see the upgrading guide: " - "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html", - add_help=False, # prevent help option from appearing in "optional arguments" group - ) - - general_group = parser.add_argument_group("general options") - target_group = parser.add_argument_group("target options") - output_group = parser.add_argument_group("general output options") - inline_args_group = output_group.add_mutually_exclusive_group() - section_group = parser.add_argument_group("section output options") - deprecated_group = parser.add_argument_group("deprecated options") - - general_group.add_argument( - "-h", - "--help", - action="help", - default=argparse.SUPPRESS, - help=_("show this help message and exit"), - ) - general_group.add_argument( - "-V", - "--version", - action="store_true", - dest="show_version", - help="Displays the currently installed version of isort.", - ) - general_group.add_argument( - "--vn", - "--version-number", - action="version", - version=__version__, - help="Returns just the current version number without the logo", - ) - general_group.add_argument( - "-v", - "--verbose", - action="store_true", - dest="verbose", - help="Shows verbose output, such as when files are skipped or when a check is successful.", - ) - general_group.add_argument( - "--only-modified", - "--om", - dest="only_modified", - action="store_true", - help="Suppresses verbose output for non-modified files.", - ) - general_group.add_argument( - "--dedup-headings", - dest="dedup_headings", - action="store_true", - help="Tells isort to only show an identical custom import heading comment once, even if" - " there are multiple sections with the comment set.", - ) - general_group.add_argument( - "-q", - "--quiet", - action="store_true", - dest="quiet", - help="Shows extra quiet output, only errors are outputted.", - ) - general_group.add_argument( - "-d", - "--stdout", - help="Force resulting output to stdout, instead of in-place.", - dest="write_to_stdout", - action="store_true", - ) - general_group.add_argument( - "--overwrite-in-place", - help="Tells isort to overwrite in place using the same file handle. " - "Comes at a performance and memory usage penalty over its standard " - "approach but ensures all file flags and modes stay unchanged.", - dest="overwrite_in_place", - action="store_true", - ) - general_group.add_argument( - "--show-config", - dest="show_config", - action="store_true", - help="See isort's determined config, as well as sources of config options.", - ) - general_group.add_argument( - "--show-files", - dest="show_files", - action="store_true", - help="See the files isort will be run against with the current config options.", - ) - general_group.add_argument( - "--df", - "--diff", - dest="show_diff", - action="store_true", - help="Prints a diff of all the changes isort would make to a file, instead of " - "changing it in place", - ) - general_group.add_argument( - "-c", - "--check-only", - "--check", - action="store_true", - dest="check", - help="Checks the file for unsorted / unformatted imports and prints them to the " - "command line without modifying the file. Returns 0 when nothing would change and " - "returns 1 when the file would be reformatted.", - ) - general_group.add_argument( - "--ws", - "--ignore-whitespace", - action="store_true", - dest="ignore_whitespace", - help="Tells isort to ignore whitespace differences when --check-only is being used.", - ) - general_group.add_argument( - "--sp", - "--settings-path", - "--settings-file", - "--settings", - dest="settings_path", - help="Explicitly set the settings path or file instead of auto determining " - "based on file location.", - ) - general_group.add_argument( - "--cr", - "--config-root", - dest="config_root", - help="Explicitly set the config root for resolving all configs. When used " - "with the --resolve-all-configs flag, isort will look at all sub-folders " - "in this config root to resolve config files and sort files based on the " - "closest available config(if any)", - ) - general_group.add_argument( - "--resolve-all-configs", - dest="resolve_all_configs", - action="store_true", - help="Tells isort to resolve the configs for all sub-directories " - "and sort files in terms of its closest config files.", - ) - general_group.add_argument( - "--profile", - dest="profile", - type=str, - help="Base profile type to use for configuration. " - f"Profiles include: {', '.join(profiles.keys())}. As well as any shared profiles.", - ) - general_group.add_argument( - "--old-finders", - "--magic-placement", - dest="old_finders", - action="store_true", - help="Use the old deprecated finder logic that relies on environment introspection magic.", - ) - general_group.add_argument( - "-j", - "--jobs", - help="Number of files to process in parallel. Negative value means use number of CPUs.", - dest="jobs", - type=int, - nargs="?", - const=-1, - ) - general_group.add_argument( - "--ac", - "--atomic", - dest="atomic", - action="store_true", - help="Ensures the output doesn't save if the resulting file contains syntax errors.", - ) - general_group.add_argument( - "--interactive", - dest="ask_to_apply", - action="store_true", - help="Tells isort to apply changes interactively.", - ) - general_group.add_argument( - "--format-error", - dest="format_error", - help="Override the format used to print errors.", - ) - general_group.add_argument( - "--format-success", - dest="format_success", - help="Override the format used to print success.", - ) - general_group.add_argument( - "--srx", - "--sort-reexports", - dest="sort_reexports", - action="store_true", - help="Automatically sort all re-exports (module level __all__ collections)", - ) - - target_group.add_argument( - "files", nargs="*", help="One or more Python source files that need their imports sorted." - ) - target_group.add_argument( - "--filter-files", - dest="filter_files", - action="store_true", - help="Tells isort to filter files even when they are explicitly passed in as " - "part of the CLI command.", - ) - target_group.add_argument( - "-s", - "--skip", - help="Files that isort should skip over. If you want to skip multiple " - "files you should specify twice: --skip file1 --skip file2. Values can be " - "file names, directory names or file paths. To skip all files in a nested path " - "use --skip-glob.", - dest="skip", - action="append", - ) - target_group.add_argument( - "--extend-skip", - help="Extends --skip to add additional files that isort should skip over. " - "If you want to skip multiple " - "files you should specify twice: --skip file1 --skip file2. Values can be " - "file names, directory names or file paths. To skip all files in a nested path " - "use --skip-glob.", - dest="extend_skip", - action="append", - ) - target_group.add_argument( - "--sg", - "--skip-glob", - help="Files that isort should skip over.", - dest="skip_glob", - action="append", - ) - target_group.add_argument( - "--extend-skip-glob", - help="Additional files that isort should skip over (extending --skip-glob).", - dest="extend_skip_glob", - action="append", - ) - target_group.add_argument( - "--gitignore", - "--skip-gitignore", - action="store_true", - dest="skip_gitignore", - help="Treat project as a git repository and ignore files listed in .gitignore." - "\nNOTE: This requires git to be installed and accessible from the same shell as isort.", - ) - target_group.add_argument( - "--ext", - "--extension", - "--supported-extension", - dest="supported_extensions", - action="append", - help="Specifies what extensions isort can be run against.", - ) - target_group.add_argument( - "--blocked-extension", - dest="blocked_extensions", - action="append", - help="Specifies what extensions isort can never be run against.", - ) - target_group.add_argument( - "--dont-follow-links", - dest="dont_follow_links", - action="store_true", - help="Tells isort not to follow symlinks that are encountered when running recursively.", - ) - target_group.add_argument( - "--filename", - dest="filename", - help="Provide the filename associated with a stream.", - ) - target_group.add_argument( - "--allow-root", - action="store_true", - default=False, - help="Tells isort not to treat / specially, allowing it to be run against the root dir.", - ) - - output_group.add_argument( - "-a", - "--add-import", - dest="add_imports", - action="append", - help="Adds the specified import line to all files, " - "automatically determining correct placement.", - ) - output_group.add_argument( - "--append", - "--append-only", - dest="append_only", - action="store_true", - help="Only adds the imports specified in --add-import if the file" - " contains existing imports.", - ) - output_group.add_argument( - "--af", - "--force-adds", - dest="force_adds", - action="store_true", - help="Forces import adds even if the original file is empty.", - ) - output_group.add_argument( - "--rm", - "--remove-import", - dest="remove_imports", - action="append", - help="Removes the specified import from all files.", - ) - output_group.add_argument( - "--float-to-top", - dest="float_to_top", - action="store_true", - help="Causes all non-indented imports to float to the top of the file having its imports " - "sorted (immediately below the top of file comment).\n" - "This can be an excellent shortcut for collecting imports every once in a while " - "when you place them in the middle of a file to avoid context switching.\n\n" - "*NOTE*: It currently doesn't work with cimports and introduces some extra over-head " - "and a performance penalty.", - ) - output_group.add_argument( - "--dont-float-to-top", - dest="dont_float_to_top", - action="store_true", - help="Forces --float-to-top setting off. See --float-to-top for more information.", - ) - output_group.add_argument( - "--ca", - "--combine-as", - dest="combine_as_imports", - action="store_true", - help="Combines as imports on the same line.", - ) - output_group.add_argument( - "--cs", - "--combine-star", - dest="combine_star", - action="store_true", - help="Ensures that if a star import is present, " - "nothing else is imported from that namespace.", - ) - output_group.add_argument( - "-e", - "--balanced", - dest="balanced_wrapping", - action="store_true", - help="Balances wrapping to produce the most consistent line length possible", - ) - output_group.add_argument( - "--ff", - "--from-first", - dest="from_first", - action="store_true", - help="Switches the typical ordering preference, " - "showing from imports first then straight ones.", - ) - output_group.add_argument( - "--fgw", - "--force-grid-wrap", - nargs="?", - const=2, - type=int, - dest="force_grid_wrap", - help="Force number of from imports (defaults to 2 when passed as CLI flag without value) " - "to be grid wrapped regardless of line " - "length. If 0 is passed in (the global default) only line length is considered.", - ) - output_group.add_argument( - "-i", - "--indent", - help='String to place for indents defaults to " " (4 spaces).', - dest="indent", - type=str, - ) - output_group.add_argument( - "--lbi", "--lines-before-imports", dest="lines_before_imports", type=int - ) - output_group.add_argument( - "--lai", "--lines-after-imports", dest="lines_after_imports", type=int - ) - output_group.add_argument( - "--lbt", "--lines-between-types", dest="lines_between_types", type=int - ) - output_group.add_argument( - "--le", - "--line-ending", - dest="line_ending", - help="Forces line endings to the specified value. " - "If not set, values will be guessed per-file.", - ) - output_group.add_argument( - "--ls", - "--length-sort", - help="Sort imports by their string length.", - dest="length_sort", - action="store_true", - ) - output_group.add_argument( - "--lss", - "--length-sort-straight", - help="Sort straight imports by their string length. Similar to `length_sort` " - "but applies only to straight imports and doesn't affect from imports.", - dest="length_sort_straight", - action="store_true", - ) - output_group.add_argument( - "-m", - "--multi-line", - dest="multi_line_output", - choices=list(WrapModes.__members__.keys()) - + [str(mode.value) for mode in WrapModes.__members__.values()], - type=str, - help="Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, " - "5-vert-grid-grouped, 6-deprecated-alias-for-5, 7-noqa, " - "8-vertical-hanging-indent-bracket, 9-vertical-prefix-from-module-import, " - "10-hanging-indent-with-parentheses).", - ) - output_group.add_argument( - "-n", - "--ensure-newline-before-comments", - dest="ensure_newline_before_comments", - action="store_true", - help="Inserts a blank line before a comment following an import.", - ) - inline_args_group.add_argument( - "--nis", - "--no-inline-sort", - dest="no_inline_sort", - action="store_true", - help="Leaves `from` imports with multiple imports 'as-is' " - "(e.g. `from foo import a, c ,b`).", - ) - output_group.add_argument( - "--ot", - "--order-by-type", - dest="order_by_type", - action="store_true", - help="Order imports by type, which is determined by case, in addition to alphabetically.\n" - "\n**NOTE**: type here refers to the implied type from the import name capitalization.\n" - ' isort does not do type introspection for the imports. These "types" are simply: ' - "CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8" - " or a related coding standard and has many imports this is a good default, otherwise you " - "likely will want to turn it off. From the CLI the `--dont-order-by-type` option will turn " - "this off.", - ) - output_group.add_argument( - "--dt", - "--dont-order-by-type", - dest="dont_order_by_type", - action="store_true", - help="Don't order imports by type, which is determined by case, in addition to " - "alphabetically.\n\n" - "**NOTE**: type here refers to the implied type from the import name capitalization.\n" - ' isort does not do type introspection for the imports. These "types" are simply: ' - "CONSTANT_VARIABLE, CamelCaseClass, variable_or_function. If your project follows PEP8" - " or a related coding standard and has many imports this is a good default. You can turn " - "this on from the CLI using `--order-by-type`.", - ) - output_group.add_argument( - "--rr", - "--reverse-relative", - dest="reverse_relative", - action="store_true", - help="Reverse order of relative imports.", - ) - output_group.add_argument( - "--reverse-sort", - dest="reverse_sort", - action="store_true", - help="Reverses the ordering of imports.", - ) - output_group.add_argument( - "--sort-order", - dest="sort_order", - help="Specify sorting function. Can be built in (natural[default] = force numbers " - "to be sequential, native = Python's built-in sorted function) or an installable plugin.", - ) - inline_args_group.add_argument( - "--sl", - "--force-single-line-imports", - dest="force_single_line", - action="store_true", - help="Forces all from imports to appear on their own line", - ) - output_group.add_argument( - "--nsl", - "--single-line-exclusions", - help="One or more modules to exclude from the single line rule.", - dest="single_line_exclusions", - action="append", - ) - output_group.add_argument( - "--tc", - "--trailing-comma", - dest="include_trailing_comma", - action="store_true", - help="Includes a trailing comma on multi line imports that include parentheses.", - ) - output_group.add_argument( - "--up", - "--use-parentheses", - dest="use_parentheses", - action="store_true", - help="Use parentheses for line continuation on length limit instead of slashes." - " **NOTE**: This is separate from wrap modes, and only affects how individual lines that " - " are too long get continued, not sections of multiple imports.", - ) - output_group.add_argument( - "-l", - "-w", - "--line-length", - "--line-width", - help="The max length of an import line (used for wrapping long imports).", - dest="line_length", - type=int, - ) - output_group.add_argument( - "--wl", - "--wrap-length", - dest="wrap_length", - type=int, - help="Specifies how long lines that are wrapped should be, if not set line_length is used." - "\nNOTE: wrap_length must be LOWER than or equal to line_length.", - ) - output_group.add_argument( - "--case-sensitive", - dest="case_sensitive", - action="store_true", - help="Tells isort to include casing when sorting module names", - ) - output_group.add_argument( - "--remove-redundant-aliases", - dest="remove_redundant_aliases", - action="store_true", - help=( - "Tells isort to remove redundant aliases from imports, such as `import os as os`." - " This defaults to `False` simply because some projects use these seemingly useless " - " aliases to signify intent and change behaviour." - ), - ) - output_group.add_argument( - "--honor-noqa", - dest="honor_noqa", - action="store_true", - help="Tells isort to honor noqa comments to enforce skipping those comments.", - ) - output_group.add_argument( - "--treat-comment-as-code", - dest="treat_comments_as_code", - action="append", - help="Tells isort to treat the specified single line comment(s) as if they are code.", - ) - output_group.add_argument( - "--treat-all-comment-as-code", - dest="treat_all_comments_as_code", - action="store_true", - help="Tells isort to treat all single line comments as if they are code.", - ) - output_group.add_argument( - "--formatter", - dest="formatter", - type=str, - help="Specifies the name of a formatting plugin to use when producing output.", - ) - output_group.add_argument( - "--color", - dest="color_output", - action="store_true", - help="Tells isort to use color in terminal output.", - ) - output_group.add_argument( - "--ext-format", - dest="ext_format", - help="Tells isort to format the given files according to an extensions formatting rules.", - ) - output_group.add_argument( - "--star-first", - help="Forces star imports above others to avoid overriding directly imported variables.", - dest="star_first", - action="store_true", - ) - output_group.add_argument( - "--split-on-trailing-comma", - help="Split imports list followed by a trailing comma into VERTICAL_HANGING_INDENT mode", - dest="split_on_trailing_comma", - action="store_true", - ) - - section_group.add_argument( - "--sd", - "--section-default", - dest="default_section", - help="Sets the default section for import options: " + str(sections.DEFAULT), - ) - section_group.add_argument( - "--only-sections", - "--os", - dest="only_sections", - action="store_true", - help="Causes imports to be sorted based on their sections like STDLIB, THIRDPARTY, etc. " - "Within sections, the imports are ordered by their import style and the imports with " - "the same style maintain their relative positions.", - ) - section_group.add_argument( - "--ds", - "--no-sections", - help="Put all imports into the same section bucket", - dest="no_sections", - action="store_true", - ) - section_group.add_argument( - "--fas", - "--force-alphabetical-sort", - action="store_true", - dest="force_alphabetical_sort", - help="Force all imports to be sorted as a single section", - ) - section_group.add_argument( - "--fss", - "--force-sort-within-sections", - action="store_true", - dest="force_sort_within_sections", - help="Don't sort straight-style imports (like import sys) before from-style imports " - "(like from itertools import groupby). Instead, sort the imports by module, " - "independent of import style.", - ) - section_group.add_argument( - "--hcss", - "--honor-case-in-force-sorted-sections", - action="store_true", - dest="honor_case_in_force_sorted_sections", - help="Honor `--case-sensitive` when `--force-sort-within-sections` is being used. " - "Without this option set, `--order-by-type` decides module name ordering too.", - ) - section_group.add_argument( - "--srss", - "--sort-relative-in-force-sorted-sections", - action="store_true", - dest="sort_relative_in_force_sorted_sections", - help="When using `--force-sort-within-sections`, sort relative imports the same " - "way as they are sorted when not using that setting.", - ) - section_group.add_argument( - "--fass", - "--force-alphabetical-sort-within-sections", - action="store_true", - dest="force_alphabetical_sort_within_sections", - help="Force all imports to be sorted alphabetically within a section", - ) - section_group.add_argument( - "-t", - "--top", - help="Force specific imports to the top of their appropriate section.", - dest="force_to_top", - action="append", - ) - section_group.add_argument( - "--combine-straight-imports", - "--csi", - dest="combine_straight_imports", - action="store_true", - help="Combines all the bare straight imports of the same section in a single line. " - "Won't work with sections which have 'as' imports", - ) - section_group.add_argument( - "--nlb", - "--no-lines-before", - help="Sections which should not be split with previous by empty lines", - dest="no_lines_before", - action="append", - ) - section_group.add_argument( - "--src", - "--src-path", - dest="src_paths", - action="append", - help="Add an explicitly defined source path " - "(modules within src paths have their imports automatically categorized as first_party)." - " Glob expansion (`*` and `**`) is supported for this option.", - ) - section_group.add_argument( - "-b", - "--builtin", - dest="known_standard_library", - action="append", - help="Force isort to recognize a module as part of Python's standard library.", - ) - section_group.add_argument( - "--extra-builtin", - dest="extra_standard_library", - action="append", - help="Extra modules to be included in the list of ones in Python's standard library.", - ) - section_group.add_argument( - "-f", - "--future", - dest="known_future_library", - action="append", - help="Force isort to recognize a module as part of Python's internal future compatibility " - "libraries. WARNING: this overrides the behavior of __future__ handling and therefore" - " can result in code that can't execute. If you're looking to add dependencies such " - "as six, a better option is to create another section below --future using custom " - "sections. See: https://github.com/PyCQA/isort#custom-sections-and-ordering and the " - "discussion here: https://github.com/PyCQA/isort/issues/1463.", - ) - section_group.add_argument( - "-o", - "--thirdparty", - dest="known_third_party", - action="append", - help="Force isort to recognize a module as being part of a third party library.", - ) - section_group.add_argument( - "-p", - "--project", - dest="known_first_party", - action="append", - help="Force isort to recognize a module as being part of the current python project.", - ) - section_group.add_argument( - "--known-local-folder", - dest="known_local_folder", - action="append", - help="Force isort to recognize a module as being a local folder. " - "Generally, this is reserved for relative imports (from . import module).", - ) - section_group.add_argument( - "--virtual-env", - dest="virtual_env", - help="Virtual environment to use for determining whether a package is third-party", - ) - section_group.add_argument( - "--conda-env", - dest="conda_env", - help="Conda environment to use for determining whether a package is third-party", - ) - section_group.add_argument( - "--py", - "--python-version", - action="store", - dest="py_version", - choices=(*tuple(VALID_PY_TARGETS), "auto"), - help="Tells isort to set the known standard library based on the specified Python " - "version. Default is to assume any Python 3 version could be the target, and use a union " - "of all stdlib modules across versions. If auto is specified, the version of the " - "interpreter used to run isort " - f"(currently: {sys.version_info.major}{sys.version_info.minor}) will be used.", - ) - - # deprecated options - deprecated_group.add_argument( - "--recursive", - dest="deprecated_flags", - action="append_const", - const="--recursive", - help=argparse.SUPPRESS, - ) - deprecated_group.add_argument( - "-rc", dest="deprecated_flags", action="append_const", const="-rc", help=argparse.SUPPRESS - ) - deprecated_group.add_argument( - "--dont-skip", - dest="deprecated_flags", - action="append_const", - const="--dont-skip", - help=argparse.SUPPRESS, - ) - deprecated_group.add_argument( - "-ns", dest="deprecated_flags", action="append_const", const="-ns", help=argparse.SUPPRESS - ) - deprecated_group.add_argument( - "--apply", - dest="deprecated_flags", - action="append_const", - const="--apply", - help=argparse.SUPPRESS, - ) - deprecated_group.add_argument( - "-k", - "--keep-direct-and-as", - dest="deprecated_flags", - action="append_const", - const="--keep-direct-and-as", - help=argparse.SUPPRESS, - ) - - return parser - - -def parse_args(argv: Sequence[str] | None = None) -> dict[str, Any]: - argv = sys.argv[1:] if argv is None else list(argv) - remapped_deprecated_args = [] - for index, arg in enumerate(argv): - if arg in DEPRECATED_SINGLE_DASH_ARGS: - remapped_deprecated_args.append(arg) - argv[index] = f"-{arg}" - - parser = _build_arg_parser() - arguments = {key: value for key, value in vars(parser.parse_args(argv)).items() if value} - if remapped_deprecated_args: - arguments["remapped_deprecated_args"] = remapped_deprecated_args - if "dont_order_by_type" in arguments: - arguments["order_by_type"] = False - del arguments["dont_order_by_type"] - if "dont_follow_links" in arguments: - arguments["follow_links"] = False - del arguments["dont_follow_links"] - if "dont_float_to_top" in arguments: - del arguments["dont_float_to_top"] - if arguments.get("float_to_top", False): - sys.exit("Can't set both --float-to-top and --dont-float-to-top.") - else: - arguments["float_to_top"] = False - multi_line_output = arguments.get("multi_line_output", None) - if multi_line_output: - if multi_line_output.isdigit(): - arguments["multi_line_output"] = WrapModes(int(multi_line_output)) - else: - arguments["multi_line_output"] = WrapModes[multi_line_output] - - return arguments - - -def _preconvert(item: Any) -> str | list[Any]: - """Preconverts objects from native types into JSONifyiable types""" - if isinstance(item, (set, frozenset)): - return list(item) - if isinstance(item, WrapModes): - return str(item.name) - if isinstance(item, Path): - return str(item) - if callable(item) and hasattr(item, "__name__"): - return str(item.__name__) - raise TypeError(f"Unserializable object {item} of type {type(item)}") - - -def identify_imports_main( - argv: Sequence[str] | None = None, stdin: TextIOWrapper | None = None -) -> None: - parser = argparse.ArgumentParser( - description="Get all import definitions from a given file." - "Use `-` as the first argument to represent stdin." - ) - parser.add_argument( - "files", nargs="+", help="One or more Python source files that need their imports sorted." - ) - parser.add_argument( - "--top-only", - action="store_true", - default=False, - help="Only identify imports that occur in before functions or classes.", - ) - - target_group = parser.add_argument_group("target options") - target_group.add_argument( - "--follow-links", - action="store_true", - default=False, - help="Tells isort to follow symlinks that are encountered when running recursively.", - ) - - uniqueness = parser.add_mutually_exclusive_group() - uniqueness.add_argument( - "--unique", - action="store_true", - default=False, - help="If true, isort will only identify unique imports.", - ) - uniqueness.add_argument( - "--packages", - dest="unique", - action="store_const", - const=api.ImportKey.PACKAGE, - default=False, - help="If true, isort will only identify the unique top level modules imported.", - ) - uniqueness.add_argument( - "--modules", - dest="unique", - action="store_const", - const=api.ImportKey.MODULE, - default=False, - help="If true, isort will only identify the unique modules imported.", - ) - uniqueness.add_argument( - "--attributes", - dest="unique", - action="store_const", - const=api.ImportKey.ATTRIBUTE, - default=False, - help="If true, isort will only identify the unique attributes imported.", - ) - - arguments = parser.parse_args(argv) - - file_names = arguments.files - if file_names == ["-"]: - identified_imports = api.find_imports_in_stream( - sys.stdin if stdin is None else stdin, - unique=arguments.unique, - top_only=arguments.top_only, - follow_links=arguments.follow_links, - ) - else: - identified_imports = api.find_imports_in_paths( - file_names, - unique=arguments.unique, - top_only=arguments.top_only, - follow_links=arguments.follow_links, - ) - - for identified_import in identified_imports: - if arguments.unique == api.ImportKey.PACKAGE: - print(identified_import.module.split(".")[0]) - elif arguments.unique == api.ImportKey.MODULE: - print(identified_import.module) - elif arguments.unique == api.ImportKey.ATTRIBUTE: - print(f"{identified_import.module}.{identified_import.attribute}") - else: - print(str(identified_import)) - - -def main(argv: Sequence[str] | None = None, stdin: TextIOWrapper | None = None) -> None: - arguments = parse_args(argv) - if arguments.get("show_version"): - print(ASCII_ART) - return - - show_config: bool = arguments.pop("show_config", False) - show_files: bool = arguments.pop("show_files", False) - if show_config and show_files: - sys.exit("Error: either specify show-config or show-files not both.") - - if "settings_path" in arguments: - if os.path.isfile(arguments["settings_path"]): - arguments["settings_file"] = os.path.abspath(arguments["settings_path"]) - arguments["settings_path"] = os.path.dirname(arguments["settings_file"]) - else: - arguments["settings_path"] = os.path.abspath(arguments["settings_path"]) - - if "virtual_env" in arguments: - venv = arguments["virtual_env"] - arguments["virtual_env"] = os.path.abspath(venv) - if not os.path.isdir(arguments["virtual_env"]): - warn(f"virtual_env dir does not exist: {arguments['virtual_env']}", stacklevel=2) - - file_names = arguments.pop("files", []) - if not file_names and not show_config: - print(QUICK_GUIDE) - if arguments: - sys.exit("Error: arguments passed in without any paths or content.") - return - if "settings_path" not in arguments: - arguments["settings_path"] = ( - arguments.get("filename", None) or os.getcwd() - if file_names == ["-"] - else os.path.abspath(file_names[0] if file_names else ".") - ) - if not os.path.isdir(arguments["settings_path"]): - arguments["settings_path"] = os.path.dirname(arguments["settings_path"]) - - config_dict = arguments.copy() - ask_to_apply = config_dict.pop("ask_to_apply", False) - jobs = config_dict.pop("jobs", None) - check = config_dict.pop("check", False) - show_diff = config_dict.pop("show_diff", False) - write_to_stdout = config_dict.pop("write_to_stdout", False) - deprecated_flags = config_dict.pop("deprecated_flags", False) - remapped_deprecated_args = config_dict.pop("remapped_deprecated_args", False) - stream_filename = config_dict.pop("filename", None) - ext_format = config_dict.pop("ext_format", None) - allow_root = config_dict.pop("allow_root", None) - resolve_all_configs = config_dict.pop("resolve_all_configs", False) - wrong_sorted_files = False - all_attempt_broken = False - no_valid_encodings = False - - config_trie: Trie | None = None - if resolve_all_configs: - config_trie = find_all_configs(config_dict.pop("config_root", ".")) - - if "src_paths" in config_dict: - config_dict["src_paths"] = { - Path(src_path).resolve() for src_path in config_dict.get("src_paths", ()) - } - - config = Config(**config_dict) - if show_config: - print(json.dumps(config.__dict__, indent=4, separators=(",", ": "), default=_preconvert)) - return - if file_names == ["-"]: - file_path = Path(stream_filename) if stream_filename else None - if show_files: - sys.exit("Error: can't show files for streaming input.") - - input_stream = sys.stdin if stdin is None else stdin - if check: - incorrectly_sorted = not api.check_stream( - input_stream=input_stream, - config=config, - show_diff=show_diff, - file_path=file_path, - extension=ext_format, - ) - - wrong_sorted_files = incorrectly_sorted - else: - try: - api.sort_stream( - input_stream=input_stream, - output_stream=sys.stdout, - config=config, - show_diff=show_diff, - file_path=file_path, - extension=ext_format, - raise_on_skip=False, - ) - except FileSkipped: - sys.stdout.write(input_stream.read()) - elif "/" in file_names and not allow_root: - printer = create_terminal_printer( - color=config.color_output, error=config.format_error, success=config.format_success - ) - printer.error("it is dangerous to operate recursively on '/'") - printer.error("use --allow-root to override this failsafe") - sys.exit(1) - else: - if stream_filename: - printer = create_terminal_printer( - color=config.color_output, error=config.format_error, success=config.format_success - ) - printer.error("Filename override is intended only for stream (-) sorting.") - sys.exit(1) - skipped: list[str] = [] - broken: list[str] = [] - - if config.filter_files: - filtered_files = [] - for file_name in file_names: - if config.is_skipped(Path(file_name)): - skipped.append(str(Path(file_name).resolve())) - else: - filtered_files.append(file_name) - file_names = filtered_files - - file_names = files.find(file_names, config, skipped, broken) - if show_files: - for file_name in file_names: - print(file_name) - return - num_skipped = 0 - num_broken = 0 - num_invalid_encoding = 0 - if config.verbose: - print(ASCII_ART) - - if jobs: - import multiprocessing # noqa: PLC0415 - - executor = multiprocessing.Pool(jobs if jobs > 0 else multiprocessing.cpu_count()) - attempt_iterator = executor.imap( - functools.partial( - sort_imports, - config=config, - check=check, - ask_to_apply=ask_to_apply, - show_diff=show_diff, - write_to_stdout=write_to_stdout, - extension=ext_format, - config_trie=config_trie, - ), - file_names, - ) - else: - # https://github.com/python/typeshed/pull/2814 - attempt_iterator = ( - sort_imports( # type: ignore - file_name, - config=config, - check=check, - ask_to_apply=ask_to_apply, - show_diff=show_diff, - write_to_stdout=write_to_stdout, - extension=ext_format, - config_trie=config_trie, - ) - for file_name in file_names - ) - - # If any files passed in are missing considered as error, should be removed - is_no_attempt = True - any_encoding_valid = False - for sort_attempt in attempt_iterator: - if not sort_attempt: - continue # pragma: no cover - shouldn't happen, satisfies type constraint - incorrectly_sorted = sort_attempt.incorrectly_sorted - if arguments.get("check", False) and incorrectly_sorted: - wrong_sorted_files = True - if sort_attempt.skipped: - num_skipped += ( - 1 # pragma: no cover - shouldn't happen, due to skip in iter_source_code - ) - - if not sort_attempt.supported_encoding: - num_invalid_encoding += 1 - else: - any_encoding_valid = True - - is_no_attempt = False - - num_skipped += len(skipped) - if num_skipped and not config.quiet: - if config.verbose: - for was_skipped in skipped: - print( - f"{was_skipped} was skipped as it's listed in 'skip' setting, " - "matches a glob in 'skip_glob' setting, or is in a .gitignore file with " - "--skip-gitignore enabled." - ) - print(f"Skipped {num_skipped} files") - - num_broken += len(broken) - if num_broken and not config.quiet: - if config.verbose: - for was_broken in broken: - warn( - f"{was_broken} was broken path, make sure it exists correctly", stacklevel=2 - ) - print(f"Broken {num_broken} paths") - - if num_broken > 0 and is_no_attempt: - all_attempt_broken = True - if num_invalid_encoding > 0 and not any_encoding_valid: - no_valid_encodings = True - - if not config.quiet and (remapped_deprecated_args or deprecated_flags): - if remapped_deprecated_args: - warn( - "W0502: The following deprecated single dash CLI flags were used and translated: " - f"{', '.join(remapped_deprecated_args)}!", - stacklevel=2, - ) - if deprecated_flags: - warn( - "W0501: The following deprecated CLI flags were used and ignored: " - f"{', '.join(deprecated_flags)}!", - stacklevel=2, - ) - warn( - "W0500: Please see the 5.0.0 Upgrade guide: " - "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html", - stacklevel=2, - ) - - if wrong_sorted_files: - sys.exit(1) - - if all_attempt_broken: - sys.exit(1) - - if no_valid_encodings: - printer = create_terminal_printer( - color=config.color_output, error=config.format_error, success=config.format_success - ) - printer.error("No valid encodings.") - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/.venv/lib/python3.10/site-packages/isort/output.py b/.venv/lib/python3.10/site-packages/isort/output.py deleted file mode 100644 index 9b72e16..0000000 --- a/.venv/lib/python3.10/site-packages/isort/output.py +++ /dev/null @@ -1,686 +0,0 @@ -import copy -import itertools -from collections.abc import Iterable -from functools import partial -from typing import Any - -from isort.format import format_simplified - -from . import parse, sorting, wrap -from .comments import add_to_line as with_comments -from .identify import STATEMENT_DECLARATIONS -from .settings import DEFAULT_CONFIG, Config - - -def sorted_imports( - parsed: parse.ParsedContent, - config: Config = DEFAULT_CONFIG, - extension: str = "py", - import_type: str = "import", -) -> str: - """Adds the imports back to the file. - - (at the index of the first import) sorted alphabetically and split between groups - - """ - if parsed.import_index == -1: - return _output_as_string(parsed.lines_without_imports, parsed.line_separator) - - formatted_output: list[str] = parsed.lines_without_imports.copy() - remove_imports = [format_simplified(removal) for removal in config.remove_imports] - - sections: Iterable[str] = itertools.chain(parsed.sections, config.forced_separate) - - if config.no_sections: - parsed.imports["no_sections"] = {"straight": {}, "from": {}} - base_sections: tuple[str, ...] = () - for section in sections: - if section == "FUTURE": - base_sections = ("FUTURE",) - continue - parsed.imports["no_sections"]["straight"].update( - parsed.imports[section].get("straight", {}) - ) - parsed.imports["no_sections"]["from"].update(parsed.imports[section].get("from", {})) - sections = (*base_sections, "no_sections") - - output: list[str] = [] - seen_headings: set[str] = set() - pending_lines_before = False - for section in sections: - straight_modules = parsed.imports[section]["straight"] - if not config.only_sections: - straight_modules = sorting.sort( - config, - straight_modules, - key=lambda key: sorting.module_key( - key, config, section_name=section, straight_import=True - ), - reverse=config.reverse_sort, - ) - - from_modules = parsed.imports[section]["from"] - if not config.only_sections: - from_modules = sorting.sort( - config, - from_modules, - key=lambda key: sorting.module_key(key, config, section_name=section), - reverse=config.reverse_sort, - ) - - if config.star_first: - star_modules = [] - other_modules = [] - for module in from_modules: - if "*" in parsed.imports[section]["from"][module]: - star_modules.append(module) - else: - other_modules.append(module) - from_modules = star_modules + other_modules - - straight_imports = _with_straight_imports( - parsed, config, straight_modules, section, remove_imports, import_type - ) - from_imports = _with_from_imports( - parsed, config, from_modules, section, remove_imports, import_type - ) - - lines_between = [""] * ( - config.lines_between_types if from_modules and straight_modules else 0 - ) - if config.from_first: - section_output = from_imports + lines_between + straight_imports - else: - section_output = straight_imports + lines_between + from_imports - - if config.force_sort_within_sections: - # collapse comments - comments_above = [] - new_section_output: list[str] = [] - for line in section_output: - if not line: - continue - if line.startswith("#"): - comments_above.append(line) - elif comments_above: - new_section_output.append(_LineWithComments(line, comments_above)) - comments_above = [] - else: - new_section_output.append(line) - # only_sections options is not imposed if force_sort_within_sections is True - new_section_output = sorting.sort( - config, - new_section_output, - key=partial(sorting.section_key, config=config), - reverse=config.reverse_sort, - ) - - # uncollapse comments - section_output = [] - for line in new_section_output: - comments = getattr(line, "comments", ()) - if comments: - section_output.extend(comments) - section_output.append(str(line)) - - section_name = section - no_lines_before = section_name in config.no_lines_before - - if section_output: - if section_name in parsed.place_imports: - parsed.place_imports[section_name] = section_output - continue - - section_title = config.import_headings.get(section_name.lower(), "") - if section_title and section_title not in seen_headings: - if config.dedup_headings: - seen_headings.add(section_title) - section_comment = f"# {section_title}" - if section_comment not in parsed.lines_without_imports[0:1]: # pragma: no branch - section_output.insert(0, section_comment) - - section_footer = config.import_footers.get(section_name.lower(), "") - if section_footer and section_footer not in seen_headings: - if config.dedup_headings: - seen_headings.add(section_footer) - section_comment_end = f"# {section_footer}" - if ( - section_comment_end not in parsed.lines_without_imports[-1:] - ): # pragma: no branch - section_output.append("") # Empty line for black compatibility - section_output.append(section_comment_end) - - if pending_lines_before or not no_lines_before: - output += [""] * config.lines_between_sections - - output += section_output - - pending_lines_before = False - else: - pending_lines_before = pending_lines_before or not no_lines_before - - if config.ensure_newline_before_comments: - output = _ensure_newline_before_comment(output) - - while output and output[-1].strip() == "": - output.pop() # pragma: no cover - while output and output[0].strip() == "": - output.pop(0) - - if config.formatting_function: - output = config.formatting_function( - parsed.line_separator.join(output), extension, config - ).splitlines() - - output_at = 0 - if parsed.import_index < parsed.original_line_count: - output_at = parsed.import_index - formatted_output[output_at:0] = output - - if output: - imports_tail = output_at + len(output) - while [ - character.strip() for character in formatted_output[imports_tail : imports_tail + 1] - ] == [""]: - formatted_output.pop(imports_tail) - - if len(formatted_output) > imports_tail: - next_construct = "" - tail = formatted_output[imports_tail:] - - for index, line in enumerate(tail): # pragma: no branch - should_skip, in_quote, *_ = parse.skip_line( - line, - in_quote="", - index=len(formatted_output), - section_comments=config.section_comments, - needs_import=False, - ) - if not should_skip and line.strip(): - if ( - line.strip().startswith("#") - and len(tail) > (index + 1) - and tail[index + 1].strip() - ): - continue - next_construct = line - break - if in_quote: # pragma: no branch - next_construct = line - break - - if config.lines_after_imports != -1: - lines_after_imports = config.lines_after_imports - if config.profile == "black" and extension == "pyi": # special case for black - lines_after_imports = 1 - formatted_output[imports_tail:0] = ["" for line in range(lines_after_imports)] - elif extension != "pyi" and next_construct.startswith(STATEMENT_DECLARATIONS): - formatted_output[imports_tail:0] = ["", ""] - else: - formatted_output[imports_tail:0] = [""] - - if config.lines_before_imports != -1: - lines_before_imports = config.lines_before_imports - if config.profile == "black" and extension == "pyi": # special case for black - lines_before_imports = 1 - formatted_output[:0] = ["" for line in range(lines_before_imports)] - - if parsed.place_imports: - new_out_lines = [] - for index, line in enumerate(formatted_output): - new_out_lines.append(line) - if line in parsed.import_placements: - new_out_lines.extend(parsed.place_imports[parsed.import_placements[line]]) - if ( - len(formatted_output) <= (index + 1) - or formatted_output[index + 1].strip() != "" - ): - new_out_lines.append("") - formatted_output = new_out_lines - - return _output_as_string(formatted_output, parsed.line_separator) - - -# Ignore DeepSource cyclomatic complexity check for this function. It was -# already complex when this check was enabled. -# skipcq: PY-R1000 -def _with_from_imports( - parsed: parse.ParsedContent, - config: Config, - from_modules: Iterable[str], - section: str, - remove_imports: list[str], - import_type: str, -) -> list[str]: - output: list[str] = [] - for module in from_modules: - if module in remove_imports: - continue - - import_start = f"from {module} {import_type} " - from_imports = list(parsed.imports[section]["from"][module]) - if ( - not config.no_inline_sort - or (config.force_single_line and module not in config.single_line_exclusions) - ) and not config.only_sections: - from_imports = sorting.sort( - config, - from_imports, - key=lambda key: sorting.module_key( - key, - config, - True, - config.force_alphabetical_sort_within_sections, - section_name=section, - ), - reverse=config.reverse_sort, - ) - if remove_imports: - from_imports = [ - line for line in from_imports if f"{module}.{line}" not in remove_imports - ] - - sub_modules = [f"{module}.{from_import}" for from_import in from_imports] - as_imports = { - from_import: [ - f"{from_import} as {as_module}" for as_module in parsed.as_map["from"][sub_module] - ] - for from_import, sub_module in zip(from_imports, sub_modules, strict=False) - if sub_module in parsed.as_map["from"] - } - if config.combine_as_imports and not ("*" in from_imports and config.combine_star): - if not config.no_inline_sort: - for as_import in as_imports: - if not config.only_sections: - as_imports[as_import] = sorting.sort(config, as_imports[as_import]) - for from_import in copy.copy(from_imports): - if from_import in as_imports: - idx = from_imports.index(from_import) - if parsed.imports[section]["from"][module][from_import]: - from_imports[(idx + 1) : (idx + 1)] = as_imports.pop(from_import) - else: - from_imports[idx : (idx + 1)] = as_imports.pop(from_import) - - only_show_as_imports = False - comments = parsed.categorized_comments["from"].pop(module, ()) - above_comments = parsed.categorized_comments["above"]["from"].pop(module, None) - while from_imports: - if above_comments: - output.extend(above_comments) - above_comments = None - - if "*" in from_imports and config.combine_star: - import_statement = wrap.line( - with_comments( - _with_star_comments(parsed, module, list(comments or ())), - f"{import_start}*", - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ), - parsed.line_separator, - config, - ) - from_imports = [ - from_import for from_import in from_imports if from_import in as_imports - ] - only_show_as_imports = True - elif config.force_single_line and module not in config.single_line_exclusions: - import_statement = "" - while from_imports: - from_import = from_imports.pop(0) - single_import_line = with_comments( - comments, - import_start + from_import, - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ) - comment = ( - parsed.categorized_comments["nested"].get(module, {}).pop(from_import, None) - ) - if comment: - single_import_line += ( - f"{(comments and ';') or config.comment_prefix} {comment}" - ) - if from_import in as_imports: - if ( - parsed.imports[section]["from"][module][from_import] - and not only_show_as_imports - ): - output.append( - wrap.line(single_import_line, parsed.line_separator, config) - ) - from_comments = parsed.categorized_comments["straight"].get( - f"{module}.{from_import}" - ) - - if not config.only_sections: - output.extend( - with_comments( - from_comments, - wrap.line( - import_start + as_import, parsed.line_separator, config - ), - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ) - for as_import in sorting.sort(config, as_imports[from_import]) - ) - - else: - output.extend( - with_comments( - from_comments, - wrap.line( - import_start + as_import, parsed.line_separator, config - ), - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ) - for as_import in as_imports[from_import] - ) - else: - output.append(wrap.line(single_import_line, parsed.line_separator, config)) - comments = None - else: - while from_imports and from_imports[0] in as_imports: - from_import = from_imports.pop(0) - - if not config.only_sections: - as_imports[from_import] = sorting.sort(config, as_imports[from_import]) - from_comments = ( - parsed.categorized_comments["straight"].get(f"{module}.{from_import}") or [] - ) - if ( - parsed.imports[section]["from"][module][from_import] - and not only_show_as_imports - ): - specific_comment = ( - parsed.categorized_comments["nested"] - .get(module, {}) - .pop(from_import, None) - ) - if specific_comment: - from_comments.append(specific_comment) - output.append( - wrap.line( - with_comments( - from_comments, - import_start + from_import, - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ), - parsed.line_separator, - config, - ) - ) - from_comments = [] - - for as_import in as_imports[from_import]: - specific_comment = ( - parsed.categorized_comments["nested"] - .get(module, {}) - .pop(as_import, None) - ) - if specific_comment: - from_comments.append(specific_comment) - - output.append( - wrap.line( - with_comments( - from_comments, - import_start + as_import, - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ), - parsed.line_separator, - config, - ) - ) - - from_comments = [] - - if "*" in from_imports: - output.append( - with_comments( - _with_star_comments(parsed, module, []), - f"{import_start}*", - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ) - ) - from_imports.remove("*") - - for from_import in copy.copy(from_imports): - comment = ( - parsed.categorized_comments["nested"].get(module, {}).pop(from_import, None) - ) - if comment: - # If the comment is a noqa and hanging indent wrapping is used, - # keep the name in the main list and hoist the comment to the statement. - if ( - comment.lower().startswith("noqa") - and config.multi_line_output == wrap.Modes.HANGING_INDENT # type: ignore[attr-defined] # noqa: E501 - ): - comments = list(comments) if comments else [] - comments.append(comment) - continue - - from_imports.remove(from_import) - if from_imports: - use_comments = [] - else: - use_comments = comments - comments = None - single_import_line = with_comments( - use_comments, - import_start + from_import, - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ) - single_import_line += ( - f"{(use_comments and ';') or config.comment_prefix} {comment}" - ) - output.append(wrap.line(single_import_line, parsed.line_separator, config)) - - from_import_section = [] - while from_imports and ( - from_imports[0] not in as_imports - or ( - config.combine_as_imports - and parsed.imports[section]["from"][module][from_import] - ) - ): - from_import_section.append(from_imports.pop(0)) - if config.combine_as_imports: - comments = (comments or []) + list( - parsed.categorized_comments["from"].pop(f"{module}.__combined_as__", ()) - ) - import_statement = with_comments( - comments, - import_start + (", ").join(from_import_section), - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ) - if not from_import_section: - import_statement = "" - - do_multiline_reformat = False - - force_grid_wrap = config.force_grid_wrap - if force_grid_wrap and len(from_import_section) >= force_grid_wrap: - do_multiline_reformat = True - - if len(import_statement) > config.line_length and len(from_import_section) > 1: - do_multiline_reformat = True - - # If line too long AND have imports AND we are - # NOT using GRID or VERTICAL wrap modes - if ( - len(import_statement) > config.line_length - and len(from_import_section) > 0 - and config.multi_line_output not in (wrap.Modes.GRID, wrap.Modes.VERTICAL) # type: ignore # noqa: E501 - ): - do_multiline_reformat = True - - if ( - import_statement - and config.split_on_trailing_comma - and module in parsed.trailing_commas - ): - import_statement = wrap.import_statement( - import_start=import_start, - from_imports=from_import_section, - comments=comments, - line_separator=parsed.line_separator, - config=config, - explode=True, - ) - - elif do_multiline_reformat: - import_statement = wrap.import_statement( - import_start=import_start, - from_imports=from_import_section, - comments=comments, - line_separator=parsed.line_separator, - config=config, - ) - if config.multi_line_output == wrap.Modes.GRID: # type: ignore - other_import_statement = wrap.import_statement( - import_start=import_start, - from_imports=from_import_section, - comments=comments, - line_separator=parsed.line_separator, - config=config, - multi_line_output=wrap.Modes.VERTICAL_GRID, # type: ignore - ) - if ( - max( - len(import_line) - for import_line in import_statement.split(parsed.line_separator) - ) - > config.line_length - ): - import_statement = other_import_statement - elif len(import_statement) > config.line_length: - import_statement = wrap.line(import_statement, parsed.line_separator, config) - - if import_statement: - output.append(import_statement) - return output - - -def _with_straight_imports( - parsed: parse.ParsedContent, - config: Config, - straight_modules: Iterable[str], - section: str, - remove_imports: list[str], - import_type: str, -) -> list[str]: - output: list[str] = [] - - as_imports = any(module in parsed.as_map["straight"] for module in straight_modules) - - # combine_straight_imports only works for bare imports, 'as' imports not included - if config.combine_straight_imports and not as_imports: - if not straight_modules: - return [] - - above_comments: list[str] = [] - inline_comments: list[str] = [] - - for module in straight_modules: - if module in parsed.categorized_comments["above"]["straight"]: - above_comments.extend(parsed.categorized_comments["above"]["straight"].pop(module)) - if module in parsed.categorized_comments["straight"]: - inline_comments.extend(parsed.categorized_comments["straight"][module]) - - combined_straight_imports = ", ".join(straight_modules) - if inline_comments: - combined_inline_comments = " ".join(inline_comments) - else: - combined_inline_comments = "" - - output.extend(above_comments) - - if combined_inline_comments: - output.append( - f"{import_type} {combined_straight_imports} # {combined_inline_comments}" - ) - else: - output.append(f"{import_type} {combined_straight_imports}") - - return output - - for module in straight_modules: - if module in remove_imports: - continue - - import_definition = [] - if module in parsed.as_map["straight"]: - if parsed.imports[section]["straight"][module]: - import_definition.append((f"{import_type} {module}", module)) - import_definition.extend( - (f"{import_type} {module} as {as_import}", f"{module} as {as_import}") - for as_import in parsed.as_map["straight"][module] - ) - else: - import_definition.append((f"{import_type} {module}", module)) - - comments_above = parsed.categorized_comments["above"]["straight"].pop(module, None) - if comments_above: - output.extend(comments_above) - output.extend( - with_comments( - parsed.categorized_comments["straight"].get(imodule), - idef, - removed=config.ignore_comments, - comment_prefix=config.comment_prefix, - ) - for idef, imodule in import_definition - ) - - return output - - -def _output_as_string(lines: list[str], line_separator: str) -> str: - return line_separator.join(_normalize_empty_lines(lines)) - - -def _normalize_empty_lines(lines: list[str]) -> list[str]: - while lines and lines[-1].strip() == "": - lines.pop(-1) - - lines.append("") - return lines - - -class _LineWithComments(str): - comments: list[str] - - def __new__( - cls: type["_LineWithComments"], value: Any, comments: list[str] - ) -> "_LineWithComments": - instance = super().__new__(cls, value) - instance.comments = comments - return instance - - -def _ensure_newline_before_comment(output: list[str]) -> list[str]: - new_output: list[str] = [] - - def is_comment(line: str | None) -> bool: - return line.startswith("#") if line else False - - for line, prev_line in zip(output, [None, *output], strict=False): - if is_comment(line) and prev_line != "" and not is_comment(prev_line): - new_output.append("") - new_output.append(line) - return new_output - - -def _with_star_comments(parsed: parse.ParsedContent, module: str, comments: list[str]) -> list[str]: - star_comment = parsed.categorized_comments["nested"].get(module, {}).pop("*", None) - if star_comment: - return [*comments, star_comment] - return comments diff --git a/.venv/lib/python3.10/site-packages/isort/parse.py b/.venv/lib/python3.10/site-packages/isort/parse.py deleted file mode 100644 index 2311c06..0000000 --- a/.venv/lib/python3.10/site-packages/isort/parse.py +++ /dev/null @@ -1,601 +0,0 @@ -"""Defines parsing functions used by isort for parsing import definitions""" - -import re -from collections import OrderedDict, defaultdict -from functools import partial -from itertools import chain -from typing import TYPE_CHECKING, Any, NamedTuple, TypedDict -from warnings import warn - -from . import place -from .comments import parse as parse_comments -from .exceptions import MissingSection -from .settings import DEFAULT_CONFIG, Config - -if TYPE_CHECKING: - CommentsAboveDict = TypedDict( - "CommentsAboveDict", {"straight": dict[str, Any], "from": dict[str, Any]} - ) - - CommentsDict = TypedDict( - "CommentsDict", - { - "from": dict[str, Any], - "straight": dict[str, Any], - "nested": dict[str, Any], - "above": CommentsAboveDict, - }, - ) - - -def _infer_line_separator(contents: str) -> str: - if "\r\n" in contents: - return "\r\n" - if "\r" in contents: - return "\r" - return "\n" - - -def normalize_line(raw_line: str) -> tuple[str, str]: - """Normalizes import related statements in the provided line. - - Returns (normalized_line: str, raw_line: str) - """ - line = re.sub(r"from(\.+)cimport ", r"from \g<1> cimport ", raw_line) - line = re.sub(r"from(\.+)import ", r"from \g<1> import ", line) - line = line.replace("import*", "import *") - line = re.sub(r" (\.+)import ", r" \g<1> import ", line) - line = re.sub(r" (\.+)cimport ", r" \g<1> cimport ", line) - line = line.replace("\t", " ") - return line, raw_line - - -def import_type(line: str, config: Config = DEFAULT_CONFIG) -> str | None: - """If the current line is an import line it will return its type (from or straight)""" - if config.honor_noqa and line.lower().rstrip().endswith("noqa"): - return None - if "isort:skip" in line or "isort: skip" in line or "isort: split" in line: - return None - if line.startswith(("import ", "cimport ")): - return "straight" - if line.startswith("from "): - return "from" - return None - - -def strip_syntax(import_string: str) -> str: - import_string = import_string.replace("_import", "[[i]]") - import_string = import_string.replace("_cimport", "[[ci]]") - for remove_syntax in ["\\", "(", ")", ","]: - import_string = import_string.replace(remove_syntax, " ") - import_list = import_string.split() - for key in ("from", "import", "cimport"): - if key in import_list: - import_list.remove(key) - import_string = " ".join(import_list) - import_string = import_string.replace("[[i]]", "_import") - import_string = import_string.replace("[[ci]]", "_cimport") - return import_string.replace("{ ", "{|").replace(" }", "|}") - - -def skip_line( - line: str, - in_quote: str, - index: int, - section_comments: tuple[str, ...], - needs_import: bool = True, -) -> tuple[bool, str]: - """Determine if a given line should be skipped. - - Returns back a tuple containing: - - (skip_line: bool, - in_quote: str,) - """ - should_skip = bool(in_quote) - if '"' in line or "'" in line: - char_index = 0 - while char_index < len(line): - if line[char_index] == "\\": - char_index += 1 - elif in_quote: - if line[char_index : char_index + len(in_quote)] == in_quote: - in_quote = "" - elif line[char_index] in ("'", '"'): - long_quote = line[char_index : char_index + 3] - if long_quote in ('"""', "'''"): - in_quote = long_quote - char_index += 2 - else: - in_quote = line[char_index] - elif line[char_index] == "#": - break - char_index += 1 - - if ";" in line.split("#")[0] and needs_import: - for part in (part.strip() for part in line.split(";")): - if ( - part - and not part.startswith("from ") - and not part.startswith(("import ", "cimport ")) - ): - should_skip = True - - return (bool(should_skip or in_quote), in_quote) - - -class ParsedContent(NamedTuple): - in_lines: list[str] - lines_without_imports: list[str] - import_index: int - place_imports: dict[str, list[str]] - import_placements: dict[str, str] - as_map: dict[str, dict[str, list[str]]] - imports: dict[str, dict[str, Any]] - categorized_comments: "CommentsDict" - change_count: int - original_line_count: int - line_separator: str - sections: Any - verbose_output: list[str] - trailing_commas: set[str] - - -def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedContent: - """Parses a python file taking out and categorizing imports.""" - line_separator: str = config.line_ending or _infer_line_separator(contents) - in_lines = contents.splitlines() - if contents and contents[-1] in ("\n", "\r"): - in_lines.append("") - - out_lines = [] - original_line_count = len(in_lines) - if config.old_finders: - from .deprecated.finders import FindersManager # noqa: PLC0415 - - finder = FindersManager(config=config).find - else: - finder = partial(place.module, config=config) - - line_count = len(in_lines) - - place_imports: dict[str, list[str]] = {} - import_placements: dict[str, str] = {} - as_map: dict[str, dict[str, list[str]]] = { - "straight": defaultdict(list), - "from": defaultdict(list), - } - imports: OrderedDict[str, dict[str, Any]] = OrderedDict() - verbose_output: list[str] = [] - - for section in chain(config.sections, config.forced_separate): - imports[section] = {"straight": OrderedDict(), "from": OrderedDict()} - categorized_comments: CommentsDict = { - "from": {}, - "straight": {}, - "nested": {}, - "above": {"straight": {}, "from": {}}, - } - - trailing_commas: set[str] = set() - - index = 0 - import_index = -1 - in_quote = "" - while index < line_count: - line = in_lines[index] - index += 1 - statement_index = index - (skipping_line, in_quote) = skip_line( - line, in_quote=in_quote, index=index, section_comments=config.section_comments - ) - - if ( - line in config.section_comments or line in config.section_comments_end - ) and not skipping_line: - if import_index == -1: # pragma: no branch - import_index = index - 1 - continue - - if "isort:imports-" in line and line.startswith("#"): - section = line.split("isort:imports-")[-1].split()[0].upper() - place_imports[section] = [] - import_placements[line] = section - elif "isort: imports-" in line and line.startswith("#"): - section = line.split("isort: imports-")[-1].split()[0].upper() - place_imports[section] = [] - import_placements[line] = section - - if skipping_line: - out_lines.append(line) - continue - - lstripped_line = line.lstrip() - if ( - config.float_to_top - and import_index == -1 - and line - and not in_quote - and not lstripped_line.startswith("#") - and not lstripped_line.startswith("'''") - and not lstripped_line.startswith('"""') - ): - if not lstripped_line.startswith("import") and not lstripped_line.startswith("from"): - import_index = index - 1 - while import_index and not in_lines[import_index - 1]: - import_index -= 1 - else: - commentless = line.split("#", 1)[0].strip() - if ( - ("isort:skip" in line or "isort: skip" in line) - and "(" in commentless - and ")" not in commentless - ): - import_index = index - - starting_line = line - while "isort:skip" in starting_line or "isort: skip" in starting_line: - commentless = starting_line.split("#", 1)[0] - if ( - "(" in commentless - and not commentless.rstrip().endswith(")") - and import_index < line_count - ): - while import_index < line_count and not commentless.rstrip().endswith( - ")" - ): - commentless = in_lines[import_index].split("#", 1)[0] - import_index += 1 - else: - import_index += 1 - - if import_index >= line_count: - break - - starting_line = in_lines[import_index] - - line, *end_of_line_comment = line.split("#", 1) - if ";" in line: - statements = [line.strip() for line in line.split(";")] - else: - statements = [line] - if end_of_line_comment: - statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}" - - for statement in statements: - line, raw_line = normalize_line(statement) - type_of_import = import_type(line, config) or "" - raw_lines = [raw_line] - if not type_of_import: - out_lines.append(raw_line) - continue - - if import_index == -1: - import_index = index - 1 - nested_comments = {} - import_string, comment = parse_comments(line) - comments = [comment] if comment else [] - line_parts = [part for part in strip_syntax(import_string).strip().split(" ") if part] - if type_of_import == "from" and len(line_parts) == 2 and comments: - nested_comments[line_parts[-1]] = comments[0] - - if "(" in line.split("#", 1)[0] and index < line_count: - while not line.split("#")[0].strip().endswith(")") and index < line_count: - line, new_comment = parse_comments(in_lines[index]) - index += 1 - if new_comment: - comments.append(new_comment) - stripped_line = strip_syntax(line).strip() - if ( - type_of_import == "from" - and stripped_line - and " " not in stripped_line.replace(" as ", "") - and new_comment - ): - nested_comments[stripped_line] = comments[-1] - import_string += line_separator + line - raw_lines.append(line) - else: - while line.strip().endswith("\\"): - line, new_comment = parse_comments(in_lines[index]) - line = line.lstrip() - index += 1 - if new_comment: - comments.append(new_comment) - - # Still need to check for parentheses after an escaped line - if ( - "(" in line.split("#")[0] - and ")" not in line.split("#")[0] - and index < line_count - ): - stripped_line = strip_syntax(line).strip() - if ( - type_of_import == "from" - and stripped_line - and " " not in stripped_line.replace(" as ", "") - and new_comment - ): - nested_comments[stripped_line] = comments[-1] - import_string += line_separator + line - raw_lines.append(line) - - while not line.split("#")[0].strip().endswith(")") and index < line_count: - line, new_comment = parse_comments(in_lines[index]) - index += 1 - if new_comment: - comments.append(new_comment) - stripped_line = strip_syntax(line).strip() - if ( - type_of_import == "from" - and stripped_line - and " " not in stripped_line.replace(" as ", "") - and new_comment - ): - nested_comments[stripped_line] = comments[-1] - import_string += line_separator + line - raw_lines.append(line) - - stripped_line = strip_syntax(line).strip() - if ( - type_of_import == "from" - and stripped_line - and " " not in stripped_line.replace(" as ", "") - and new_comment - ): - nested_comments[stripped_line] = comments[-1] - if import_string.strip().endswith( - (" import", " cimport") - ) or line.strip().startswith(("import ", "cimport ")): - import_string += line_separator + line - else: - import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip() - - if type_of_import == "from": - cimports: bool - import_string = ( - import_string.replace("import(", "import (") - .replace("\\", " ") - .replace("\n", " ") - ) - if "import " not in import_string: - out_lines.extend(raw_lines) - continue - - if " cimport " in import_string: - parts = import_string.split(" cimport ") - cimports = True - - else: - parts = import_string.split(" import ") - cimports = False - - from_import = parts[0].split(" ") - import_string = (" cimport " if cimports else " import ").join( - [from_import[0] + " " + "".join(from_import[1:]), *parts[1:]] - ) - - just_imports = [ - item.replace("{|", "{ ").replace("|}", " }") - for item in strip_syntax(import_string).split() - ] - - attach_comments_to: list[Any] | None = None - direct_imports = just_imports[1:] - straight_import = True - top_level_module = "" - if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports): - straight_import = False - while "as" in just_imports: - nested_module = None - as_index = just_imports.index("as") - if type_of_import == "from": - nested_module = just_imports[as_index - 1] - top_level_module = just_imports[0] - module = top_level_module + "." + nested_module - as_name = just_imports[as_index + 1] - direct_imports.remove(nested_module) - direct_imports.remove(as_name) - direct_imports.remove("as") - if nested_module == as_name and config.remove_redundant_aliases: - pass - elif as_name not in as_map["from"][module]: # pragma: no branch - as_map["from"][module].append(as_name) - - full_name = f"{nested_module} as {as_name}" - associated_comment = nested_comments.get(full_name) - if associated_comment: - categorized_comments["nested"].setdefault(top_level_module, {})[ - full_name - ] = associated_comment - if associated_comment in comments: # pragma: no branch - comments.pop(comments.index(associated_comment)) - else: - module = just_imports[as_index - 1] - as_name = just_imports[as_index + 1] - if module == as_name and config.remove_redundant_aliases: - pass - elif as_name not in as_map["straight"][module]: - as_map["straight"][module].append(as_name) - - if comments and attach_comments_to is None: - if nested_module and config.combine_as_imports: - attach_comments_to = categorized_comments["from"].setdefault( - f"{top_level_module}.__combined_as__", [] - ) - else: - if type_of_import == "from" or ( - config.remove_redundant_aliases and as_name == module.split(".")[-1] - ): - attach_comments_to = categorized_comments["straight"].setdefault( - module, [] - ) - else: - attach_comments_to = categorized_comments["straight"].setdefault( - f"{module} as {as_name}", [] - ) - del just_imports[as_index : as_index + 2] - - if type_of_import == "from": - import_from = just_imports.pop(0) - placed_module = finder(import_from) - if config.verbose and not config.only_modified: - print(f"from-type place_module for {import_from} returned {placed_module}") - - elif config.verbose: - verbose_output.append( - f"from-type place_module for {import_from} returned {placed_module}" - ) - if placed_module == "": - warn( - f"could not place module {import_from} of line {line} --" - " Do you need to define a default section?", - stacklevel=2, - ) - - if placed_module and placed_module not in imports: - raise MissingSection(import_module=import_from, section=placed_module) - - root = imports[placed_module][type_of_import] # type: ignore - for import_name in just_imports: - associated_comment = nested_comments.get(import_name) - if associated_comment: - categorized_comments["nested"].setdefault(import_from, {})[import_name] = ( - associated_comment - ) - if associated_comment in comments: # pragma: no branch - comments.pop(comments.index(associated_comment)) - if ( - config.force_single_line - and comments - and attach_comments_to is None - and len(just_imports) == 1 - ): - nested_from_comments = categorized_comments["nested"].setdefault( - import_from, {} - ) - existing_comment = nested_from_comments.get(just_imports[0], "") - nested_from_comments[just_imports[0]] = ( - f"{existing_comment}{'; ' if existing_comment else ''}{'; '.join(comments)}" - ) - comments = [] - - if comments and attach_comments_to is None: - attach_comments_to = categorized_comments["from"].setdefault(import_from, []) - - if len(out_lines) > max(import_index, 1) - 1: - last = out_lines[-1].rstrip() if out_lines else "" - while ( - last.startswith("#") - and not last.endswith('"""') - and not last.endswith("'''") - and "isort:imports-" not in last - and "isort: imports-" not in last - and not config.treat_all_comments_as_code - and last.strip() not in config.treat_comments_as_code - ): - categorized_comments["above"]["from"].setdefault(import_from, []).insert( - 0, out_lines.pop(-1) - ) - if out_lines: - last = out_lines[-1].rstrip() - else: - last = "" - if statement_index - 1 == import_index: # pragma: no cover - import_index -= len( - categorized_comments["above"]["from"].get(import_from, []) - ) - - if import_from not in root: - root[import_from] = OrderedDict( - (module, module in direct_imports) for module in just_imports - ) - else: - root[import_from].update( - (module, root[import_from].get(module, False) or module in direct_imports) - for module in just_imports - ) - - if comments and attach_comments_to is not None: - attach_comments_to.extend(comments) - - if ( - just_imports - and just_imports[-1] - and "," in import_string.split(just_imports[-1])[-1] - ): - trailing_commas.add(import_from) - else: - if comments and attach_comments_to is not None: - attach_comments_to.extend(comments) - comments = [] - - for module in just_imports: - if comments: - categorized_comments["straight"][module] = comments - comments = [] - - if len(out_lines) > max(import_index, +1, 1) - 1: - last = out_lines[-1].rstrip() if out_lines else "" - while ( - last.startswith("#") - and not last.endswith('"""') - and not last.endswith("'''") - and "isort:imports-" not in last - and "isort: imports-" not in last - and not config.treat_all_comments_as_code - and last.strip() not in config.treat_comments_as_code - ): - categorized_comments["above"]["straight"].setdefault(module, []).insert( - 0, out_lines.pop(-1) - ) - if out_lines: - last = out_lines[-1].rstrip() - else: - last = "" - if index - 1 == import_index: - import_index -= len( - categorized_comments["above"]["straight"].get(module, []) - ) - placed_module = finder(module) - if config.verbose and not config.only_modified: - print(f"else-type place_module for {module} returned {placed_module}") - - elif config.verbose: - verbose_output.append( - f"else-type place_module for {module} returned {placed_module}" - ) - if placed_module == "": - warn( - f"could not place module {module} of line {line} --" - " Do you need to define a default section?", - stacklevel=2, - ) - imports.setdefault("", {"straight": OrderedDict(), "from": OrderedDict()}) - - if placed_module and placed_module not in imports: - raise MissingSection(import_module=module, section=placed_module) - - straight_import |= imports[placed_module][type_of_import].get( # type: ignore - module, False - ) - imports[placed_module][type_of_import][module] = straight_import # type: ignore - - change_count = len(out_lines) - original_line_count - - return ParsedContent( - in_lines=in_lines, - lines_without_imports=out_lines, - import_index=import_index, - place_imports=place_imports, - import_placements=import_placements, - as_map=as_map, - imports=imports, - categorized_comments=categorized_comments, - change_count=change_count, - original_line_count=original_line_count, - line_separator=line_separator, - sections=config.sections, - verbose_output=verbose_output, - trailing_commas=trailing_commas, - ) diff --git a/.venv/lib/python3.10/site-packages/isort/place.py b/.venv/lib/python3.10/site-packages/isort/place.py deleted file mode 100644 index 2f863b3..0000000 --- a/.venv/lib/python3.10/site-packages/isort/place.py +++ /dev/null @@ -1,146 +0,0 @@ -"""Contains all logic related to placing an import within a certain section.""" - -import importlib -from collections.abc import Iterable -from fnmatch import fnmatch -from functools import lru_cache -from pathlib import Path - -from isort import sections -from isort.settings import DEFAULT_CONFIG, Config -from isort.utils import exists_case_sensitive - -LOCAL = "LOCALFOLDER" - - -def module(name: str, config: Config = DEFAULT_CONFIG) -> str: - """Returns the section placement for the given module name.""" - return module_with_reason(name, config)[0] - - -@lru_cache(maxsize=1000) -def module_with_reason(name: str, config: Config = DEFAULT_CONFIG) -> tuple[str, str]: - """Returns the section placement for the given module name alongside the reasoning.""" - return ( - _forced_separate(name, config) - or _local(name, config) - or _known_pattern(name, config) - or _src_path(name, config) - or (config.default_section, "Default option in Config or universal default.") - ) - - -def _forced_separate(name: str, config: Config) -> tuple[str, str] | None: - for forced_separate in config.forced_separate: - # Ensure all forced_separate patterns will match to end of string - path_glob = forced_separate - if not forced_separate.endswith("*"): - path_glob = f"{forced_separate}*" - - if fnmatch(name, path_glob) or fnmatch(name, "." + path_glob): - return (forced_separate, f"Matched forced_separate ({forced_separate}) config value.") - - return None - - -def _local(name: str, config: Config) -> tuple[str, str] | None: - if name.startswith("."): - return (LOCAL, "Module name started with a dot.") - - return None - - -def _known_pattern(name: str, config: Config) -> tuple[str, str] | None: - parts = name.split(".") - module_names_to_check = (".".join(parts[:first_k]) for first_k in range(len(parts), 0, -1)) - for module_name_to_check in module_names_to_check: - for pattern, placement in config.known_patterns: - if placement in config.sections and pattern.match(module_name_to_check): - return (placement, f"Matched configured known pattern {pattern}") - - return None - - -def _src_path( - name: str, - config: Config, - src_paths: Iterable[Path] | None = None, - prefix: tuple[str, ...] = (), -) -> tuple[str, str] | None: - if src_paths is None: - src_paths = config.src_paths - - root_module_name, *nested_module = name.split(".", 1) - new_prefix = (*prefix, root_module_name) - namespace = ".".join(new_prefix) - - for src_path in src_paths: - module_path = (src_path / root_module_name).resolve() - if not prefix and not module_path.is_dir() and src_path.name == root_module_name: - module_path = src_path.resolve() - if nested_module and ( - namespace in config.namespace_packages - or ( - config.auto_identify_namespace_packages - and _is_namespace_package(module_path, config.supported_extensions) - ) - ): - return _src_path(nested_module[0], config, (module_path,), new_prefix) - if ( - _is_module(module_path) - or _is_package(module_path) - or _src_path_is_module(src_path, root_module_name) - ): - return (sections.FIRSTPARTY, f"Found in one of the configured src_paths: {src_path}.") - - return None - - -def _is_module(path: Path) -> bool: - return ( - exists_case_sensitive(str(path.with_suffix(".py"))) - or any( - exists_case_sensitive(str(path.with_suffix(ext_suffix))) - for ext_suffix in importlib.machinery.EXTENSION_SUFFIXES - ) - or exists_case_sensitive(str(path / "__init__.py")) - ) - - -def _is_package(path: Path) -> bool: - return exists_case_sensitive(str(path)) and path.is_dir() - - -def _is_namespace_package(path: Path, src_extensions: frozenset[str]) -> bool: - if not _is_package(path): - return False - - init_file = path / "__init__.py" - if not init_file.exists(): - filenames = [ - filepath - for filepath in path.iterdir() - if filepath.suffix.lstrip(".") in src_extensions - or filepath.name.lower() in ("setup.cfg", "pyproject.toml") - ] - if filenames: - return False - else: - with init_file.open("rb") as open_init_file: - file_start = open_init_file.read(4096) - if ( - b"__import__('pkg_resources').declare_namespace(__name__)" not in file_start - and b'__import__("pkg_resources").declare_namespace(__name__)' not in file_start - and b"__path__ = __import__('pkgutil').extend_path(__path__, __name__)" - not in file_start - and b'__path__ = __import__("pkgutil").extend_path(__path__, __name__)' - not in file_start - ): - return False - return True - - -def _src_path_is_module(src_path: Path, module_name: str) -> bool: - return ( - module_name == src_path.name and src_path.is_dir() and exists_case_sensitive(str(src_path)) - ) diff --git a/.venv/lib/python3.10/site-packages/isort/profiles.py b/.venv/lib/python3.10/site-packages/isort/profiles.py deleted file mode 100644 index f2f0243..0000000 --- a/.venv/lib/python3.10/site-packages/isort/profiles.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Common profiles are defined here to be easily used within a project using --profile {name}""" - -from typing import Any - -black = { - "multi_line_output": 3, - "include_trailing_comma": True, - "split_on_trailing_comma": True, - "force_grid_wrap": 0, - "use_parentheses": True, - "ensure_newline_before_comments": True, - "line_length": 88, -} -django = { - "combine_as_imports": True, - "include_trailing_comma": True, - "multi_line_output": 5, - "line_length": 79, -} -pycharm = { - "multi_line_output": 3, - "force_grid_wrap": 2, - "lines_after_imports": 2, -} -google = { - "force_single_line": True, - "force_sort_within_sections": True, - "lexicographical": True, - "line_length": 1000, - "single_line_exclusions": ( - "collections.abc", - "six.moves", - "typing", - "typing_extensions", - ), - "order_by_type": False, - "group_by_package": True, -} -open_stack = { - "force_single_line": True, - "force_sort_within_sections": True, - "lexicographical": True, -} -plone = black.copy() -plone.update( - { - "force_alphabetical_sort": True, - "force_single_line": True, - "lines_after_imports": 2, - } -) -attrs = { - "atomic": True, - "force_grid_wrap": 0, - "include_trailing_comma": True, - "lines_after_imports": 2, - "lines_between_types": 1, - "multi_line_output": 3, - "use_parentheses": True, -} -hug = { - "multi_line_output": 3, - "include_trailing_comma": True, - "force_grid_wrap": 0, - "use_parentheses": True, - "line_length": 100, -} -wemake = { - "multi_line_output": 3, - "include_trailing_comma": True, - "use_parentheses": True, - "line_length": 80, -} -appnexus = { - **black, - "force_sort_within_sections": True, - "order_by_type": False, - "case_sensitive": False, - "reverse_relative": True, - "sort_relative_in_force_sorted_sections": True, - "sections": ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "APPLICATION", "LOCALFOLDER"], - "no_lines_before": "LOCALFOLDER", -} - -profiles: dict[str, dict[str, Any]] = { - "black": black, - "django": django, - "pycharm": pycharm, - "google": google, - "open_stack": open_stack, - "plone": plone, - "attrs": attrs, - "hug": hug, - "wemake": wemake, - "appnexus": appnexus, -} diff --git a/.venv/lib/python3.10/site-packages/isort/py.typed b/.venv/lib/python3.10/site-packages/isort/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.10/site-packages/isort/sections.py b/.venv/lib/python3.10/site-packages/isort/sections.py deleted file mode 100644 index 84671cb..0000000 --- a/.venv/lib/python3.10/site-packages/isort/sections.py +++ /dev/null @@ -1,8 +0,0 @@ -"""Defines all sections isort uses by default""" - -FUTURE: str = "FUTURE" -STDLIB: str = "STDLIB" -THIRDPARTY: str = "THIRDPARTY" -FIRSTPARTY: str = "FIRSTPARTY" -LOCALFOLDER: str = "LOCALFOLDER" -DEFAULT: tuple[str, ...] = (FUTURE, STDLIB, THIRDPARTY, FIRSTPARTY, LOCALFOLDER) diff --git a/.venv/lib/python3.10/site-packages/isort/settings.py b/.venv/lib/python3.10/site-packages/isort/settings.py deleted file mode 100644 index f40d48a..0000000 --- a/.venv/lib/python3.10/site-packages/isort/settings.py +++ /dev/null @@ -1,933 +0,0 @@ -"""isort/settings.py. - -Defines how the default settings for isort should be loaded -""" - -import configparser -import fnmatch -import os -import posixpath -import re -import stat -import subprocess # nosec # Needed for gitignore support. -import sys -from collections.abc import Callable, Iterable -from dataclasses import dataclass, field -from pathlib import Path -from re import Pattern -from typing import TYPE_CHECKING, Any -from warnings import warn - -from . import sorting, stdlibs -from .exceptions import ( - FormattingPluginDoesNotExist, - InvalidSettingsPath, - ProfileDoesNotExist, - SortingFunctionDoesNotExist, - UnsupportedSettings, -) -from .profiles import profiles as profiles -from .sections import DEFAULT as SECTION_DEFAULTS -from .sections import FIRSTPARTY, FUTURE, LOCALFOLDER, STDLIB, THIRDPARTY -from .utils import Trie -from .wrap_modes import WrapModes -from .wrap_modes import from_string as wrap_mode_from_string - -if TYPE_CHECKING: - from importlib.metadata import EntryPoints - - tomllib: Any -else: - if sys.version_info >= (3, 11): - import tomllib - else: - from ._vendored import tomli as tomllib - -_SHEBANG_RE = re.compile(rb"^#!.*\bpython[23w]?\b") -CYTHON_EXTENSIONS = frozenset({"pyx", "pxd"}) -SUPPORTED_EXTENSIONS = frozenset({"py", "pyi", *CYTHON_EXTENSIONS}) -BLOCKED_EXTENSIONS = frozenset({"pex"}) -FILE_SKIP_COMMENTS: tuple[str, ...] = ( - "isort:" + "skip_file", - "isort: " + "skip_file", -) # Concatenated to avoid this file being skipped -MAX_CONFIG_SEARCH_DEPTH: int = 25 # The number of parent directories to for a config file within -STOP_CONFIG_SEARCH_ON_DIRS: tuple[str, ...] = (".git", ".hg") -VALID_PY_TARGETS: tuple[str, ...] = tuple( - target.replace("py", "") for target in dir(stdlibs) if not target.startswith("_") -) -CONFIG_SOURCES: tuple[str, ...] = ( - ".isort.cfg", - "pyproject.toml", - "setup.cfg", - "tox.ini", - ".editorconfig", -) -DEFAULT_SKIP: frozenset[str] = frozenset( - { - ".venv", - "venv", - ".tox", - ".eggs", - ".git", - ".hg", - ".mypy_cache", - ".nox", - ".svn", - ".bzr", - "_build", - "buck-out", - "build", - "dist", - ".pants.d", - ".direnv", - "node_modules", - "__pypackages__", - ".pytype", - } -) - -CONFIG_SECTIONS: dict[str, tuple[str, ...]] = { - ".isort.cfg": ("settings", "isort"), - "pyproject.toml": ("tool.isort",), - "setup.cfg": ("isort", "tool:isort"), - "tox.ini": ("isort", "tool:isort"), - ".editorconfig": ("*", "*.py", "**.py", "*.{py}"), -} -FALLBACK_CONFIG_SECTIONS: tuple[str, ...] = ("isort", "tool:isort", "tool.isort") - -IMPORT_HEADING_PREFIX = "import_heading_" -IMPORT_FOOTER_PREFIX = "import_footer_" -KNOWN_PREFIX = "known_" -KNOWN_SECTION_MAPPING: dict[str, str] = { - STDLIB: "STANDARD_LIBRARY", - FUTURE: "FUTURE_LIBRARY", - FIRSTPARTY: "FIRST_PARTY", - THIRDPARTY: "THIRD_PARTY", - LOCALFOLDER: "LOCAL_FOLDER", -} - -RUNTIME_SOURCE = "runtime" - -DEPRECATED_SETTINGS = ("not_skip", "keep_direct_and_as_imports") - -_STR_BOOLEAN_MAPPING = { - "y": True, - "yes": True, - "t": True, - "on": True, - "1": True, - "true": True, - "n": False, - "no": False, - "f": False, - "off": False, - "0": False, - "false": False, -} - - -@dataclass(frozen=True) -class _Config: - """Defines the data schema and defaults used for isort configuration. - - NOTE: known lists, such as known_standard_library, are intentionally not complete as they are - dynamically determined later on. - """ - - py_version: str = "3" - force_to_top: frozenset[str] = frozenset() - skip: frozenset[str] = DEFAULT_SKIP - extend_skip: frozenset[str] = frozenset() - skip_glob: frozenset[str] = frozenset() - extend_skip_glob: frozenset[str] = frozenset() - skip_gitignore: bool = False - line_length: int = 79 - wrap_length: int = 0 - line_ending: str = "" - sections: tuple[str, ...] = SECTION_DEFAULTS - no_sections: bool = False - known_future_library: frozenset[str] = frozenset(("__future__",)) - known_third_party: frozenset[str] = frozenset() - known_first_party: frozenset[str] = frozenset() - known_local_folder: frozenset[str] = frozenset() - known_standard_library: frozenset[str] = frozenset() - extra_standard_library: frozenset[str] = frozenset() - known_other: dict[str, frozenset[str]] = field(default_factory=dict) - multi_line_output: WrapModes = WrapModes.GRID # type: ignore - forced_separate: tuple[str, ...] = () - indent: str = " " * 4 - comment_prefix: str = " #" - length_sort: bool = False - length_sort_straight: bool = False - length_sort_sections: frozenset[str] = frozenset() - add_imports: frozenset[str] = frozenset() - remove_imports: frozenset[str] = frozenset() - append_only: bool = False - reverse_relative: bool = False - force_single_line: bool = False - single_line_exclusions: tuple[str, ...] = () - default_section: str = THIRDPARTY - import_headings: dict[str, str] = field(default_factory=dict) - import_footers: dict[str, str] = field(default_factory=dict) - balanced_wrapping: bool = False - use_parentheses: bool = False - order_by_type: bool = True - atomic: bool = False - lines_before_imports: int = -1 - lines_after_imports: int = -1 - lines_between_sections: int = 1 - lines_between_types: int = 0 - combine_as_imports: bool = False - combine_star: bool = False - include_trailing_comma: bool = False - from_first: bool = False - verbose: bool = False - quiet: bool = False - force_adds: bool = False - force_alphabetical_sort_within_sections: bool = False - force_alphabetical_sort: bool = False - force_grid_wrap: int = 0 - force_sort_within_sections: bool = False - lexicographical: bool = False - group_by_package: bool = False - ignore_whitespace: bool = False - no_lines_before: frozenset[str] = frozenset() - no_inline_sort: bool = False - ignore_comments: bool = False - case_sensitive: bool = False - sources: tuple[dict[str, Any], ...] = () - virtual_env: str = "" - conda_env: str = "" - ensure_newline_before_comments: bool = False - directory: str = "" - profile: str = "" - honor_noqa: bool = False - src_paths: tuple[Path, ...] = () - old_finders: bool = False - remove_redundant_aliases: bool = False - float_to_top: bool = False - filter_files: bool = False - formatter: str = "" - formatting_function: Callable[[str, str, object], str] | None = None - color_output: bool = False - treat_comments_as_code: frozenset[str] = frozenset() - treat_all_comments_as_code: bool = False - supported_extensions: frozenset[str] = SUPPORTED_EXTENSIONS - blocked_extensions: frozenset[str] = BLOCKED_EXTENSIONS - constants: frozenset[str] = frozenset() - classes: frozenset[str] = frozenset() - variables: frozenset[str] = frozenset() - dedup_headings: bool = False - only_sections: bool = False - only_modified: bool = False - combine_straight_imports: bool = False - auto_identify_namespace_packages: bool = True - namespace_packages: frozenset[str] = frozenset() - follow_links: bool = True - indented_import_headings: bool = True - honor_case_in_force_sorted_sections: bool = False - sort_relative_in_force_sorted_sections: bool = False - overwrite_in_place: bool = False - reverse_sort: bool = False - star_first: bool = False - import_dependencies = dict[str, str] - git_ls_files: dict[Path, set[str]] = field(default_factory=dict) - format_error: str = "{error}: {message}" - format_success: str = "{success}: {message}" - sort_order: str = "natural" - sort_reexports: bool = False - split_on_trailing_comma: bool = False - - def __post_init__(self) -> None: - py_version = self.py_version - if py_version == "auto": # pragma: no cover - py_version = f"{sys.version_info.major}{sys.version_info.minor}" - - if py_version not in VALID_PY_TARGETS: - raise ValueError( - f"The python version {py_version} is not supported. " - "You can set a python version with the -py or --python-version flag. " - f"The following versions are supported: {VALID_PY_TARGETS}" - ) - - if py_version != "all": - object.__setattr__(self, "py_version", f"py{py_version}") - - if not self.known_standard_library: - object.__setattr__( - self, "known_standard_library", frozenset(getattr(stdlibs, self.py_version).stdlib) - ) - - if self.multi_line_output == WrapModes.VERTICAL_GRID_GROUPED_NO_COMMA: # type: ignore - vertical_grid_grouped = WrapModes.VERTICAL_GRID_GROUPED # type: ignore - object.__setattr__(self, "multi_line_output", vertical_grid_grouped) - if self.force_alphabetical_sort: - object.__setattr__(self, "force_alphabetical_sort_within_sections", True) - object.__setattr__(self, "no_sections", True) - object.__setattr__(self, "lines_between_types", 1) - object.__setattr__(self, "from_first", True) - if self.wrap_length > self.line_length: - raise ValueError( - "wrap_length must be set lower than or equal to line_length: " - f"{self.wrap_length} > {self.line_length}." - ) - - def __hash__(self) -> int: - return id(self) - - -_DEFAULT_SETTINGS = {**vars(_Config()), "source": "defaults"} - - -class Config(_Config): - def __init__( - self, - settings_file: str = "", - settings_path: str = "", - config: _Config | None = None, - **config_overrides: Any, - ): - self._known_patterns: list[tuple[Pattern[str], str]] | None = None - self._section_comments: tuple[str, ...] | None = None - self._section_comments_end: tuple[str, ...] | None = None - self._skips: frozenset[str] | None = None - self._skip_globs: frozenset[str] | None = None - self._sorting_function: Callable[..., list[str]] | None = None - - if config: - config_vars = vars(config).copy() - config_vars.update(config_overrides) - config_vars["py_version"] = config_vars["py_version"].replace("py", "") - config_vars.pop("_known_patterns") - config_vars.pop("_section_comments") - config_vars.pop("_section_comments_end") - config_vars.pop("_skips") - config_vars.pop("_skip_globs") - config_vars.pop("_sorting_function") - super().__init__(**config_vars) - return - - # We can't use self.quiet to conditionally show warnings before super.__init__() is called - # at the end of this method. _Config is also frozen so setting self.quiet isn't possible. - # Therefore we extract quiet early here in a variable and use that in warning conditions. - quiet = config_overrides.get("quiet", False) - - sources: list[dict[str, Any]] = [_DEFAULT_SETTINGS] - - config_settings: dict[str, Any] - project_root: str - if settings_file: - config_settings = _get_config_data( - settings_file, - CONFIG_SECTIONS.get(os.path.basename(settings_file), FALLBACK_CONFIG_SECTIONS), - ) - project_root = os.path.dirname(settings_file) - if not config_settings and not quiet: - warn( - f"A custom settings file was specified: {settings_file} but no configuration " - "was found inside. This can happen when [settings] is used as the config " - "header instead of [isort]. " - "See: https://pycqa.github.io/isort/docs/configuration/config_files" - "#custom-config-files for more information.", - stacklevel=2, - ) - elif settings_path: - if not os.path.exists(settings_path): - raise InvalidSettingsPath(settings_path) - - settings_path = os.path.abspath(settings_path) - project_root, config_settings = _find_config(settings_path) - else: - config_settings = {} - project_root = os.getcwd() - - profile_name = config_overrides.get("profile", config_settings.get("profile", "")) - profile: dict[str, Any] = {} - if profile_name: - if profile_name not in profiles: - for plugin in entry_points(group="isort.profiles"): - profiles.setdefault(plugin.name, plugin.load()) - - if profile_name not in profiles: - raise ProfileDoesNotExist(profile_name) - - profile = profiles[profile_name].copy() - profile["source"] = f"{profile_name} profile" - sources.append(profile) - - if config_settings: - sources.append(config_settings) - if config_overrides: - config_overrides["source"] = RUNTIME_SOURCE - sources.append(config_overrides) - - combined_config = {**profile, **config_settings, **config_overrides} - if "indent" in combined_config: - indent = str(combined_config["indent"]) - if indent.isdigit(): - indent = " " * int(indent) - else: - indent = indent.strip("'").strip('"') - if indent.lower() == "tab": - indent = "\t" - combined_config["indent"] = indent - - known_other = {} - import_headings = {} - import_footers = {} - for key, value in tuple(combined_config.items()): - # Collect all known sections beyond those that have direct entries - if key.startswith(KNOWN_PREFIX) and key not in ( - "known_standard_library", - "known_future_library", - "known_third_party", - "known_first_party", - "known_local_folder", - ): - import_heading = key[len(KNOWN_PREFIX) :].lower() - maps_to_section = import_heading.upper() - combined_config.pop(key) - if maps_to_section in KNOWN_SECTION_MAPPING: - section_name = f"known_{KNOWN_SECTION_MAPPING[maps_to_section].lower()}" - if section_name in combined_config and not quiet: - warn( - f"Can't set both {key} and {section_name} in the same config file.\n" - f"Default to {section_name} if unsure." - "\n\n" - "See: https://pycqa.github.io/isort/" - "#custom-sections-and-ordering.", - stacklevel=2, - ) - else: - combined_config[section_name] = frozenset(value) - else: - known_other[import_heading] = frozenset(value) - if maps_to_section not in combined_config.get("sections", ()) and not quiet: - warn( - f"`{key}` setting is defined, but {maps_to_section} is not" - " included in `sections` config option:" - f" {combined_config.get('sections', SECTION_DEFAULTS)}.\n\n" - "See: https://pycqa.github.io/isort/" - "#custom-sections-and-ordering.", - stacklevel=2, - ) - if key.startswith(IMPORT_HEADING_PREFIX): - import_headings[key[len(IMPORT_HEADING_PREFIX) :].lower()] = str(value) - if key.startswith(IMPORT_FOOTER_PREFIX): - import_footers[key[len(IMPORT_FOOTER_PREFIX) :].lower()] = str(value) - - # Coerce all provided config values into their correct type - default_value = _DEFAULT_SETTINGS.get(key, None) - if default_value is None: - continue - - combined_config[key] = type(default_value)(value) - - for section in combined_config.get("sections", ()): - if section in SECTION_DEFAULTS: - continue - - if section.lower() not in known_other: - config_keys = ", ".join(known_other.keys()) - warn( - f"`sections` setting includes {section}, but no known_{section.lower()} " - "is defined. " - f"The following known_SECTION config options are defined: {config_keys}.", - stacklevel=2, - ) - - if "directory" not in combined_config: - combined_config["directory"] = ( - os.path.dirname(config_settings["source"]) - if config_settings.get("source", None) - else os.getcwd() - ) - - path_root = Path(combined_config.get("directory", project_root)).resolve() - path_root = path_root if path_root.is_dir() else path_root.parent - if "src_paths" not in combined_config: - combined_config["src_paths"] = (path_root / "src", path_root) - else: - src_paths: list[Path] = [] - for src_path in combined_config.get("src_paths", ()): - full_paths = ( - path_root.glob(src_path) if "*" in str(src_path) else [path_root / src_path] - ) - for path in full_paths: - if path not in src_paths: - src_paths.append(path) - - combined_config["src_paths"] = tuple(src_paths) - - if "formatter" in combined_config: - for plugin in entry_points(group="isort.formatters"): - if plugin.name == combined_config["formatter"]: - combined_config["formatting_function"] = plugin.load() - break - else: - raise FormattingPluginDoesNotExist(combined_config["formatter"]) - - # Remove any config values that are used for creating config object but - # aren't defined in dataclass - combined_config.pop("source", None) - combined_config.pop("sources", None) - combined_config.pop("runtime_src_paths", None) - - deprecated_options_used = [ - option for option in combined_config if option in DEPRECATED_SETTINGS - ] - if deprecated_options_used: - for deprecated_option in deprecated_options_used: - combined_config.pop(deprecated_option) - if not quiet: - warn( - "W0503: Deprecated config options were used: " - f"{', '.join(deprecated_options_used)}." - "Please see the 5.0.0 upgrade guide: " - "https://pycqa.github.io/isort/docs/upgrade_guides/5.0.0.html", - stacklevel=2, - ) - - if known_other: - combined_config["known_other"] = known_other - if import_headings: - for import_heading_key in import_headings: - combined_config.pop(f"{IMPORT_HEADING_PREFIX}{import_heading_key}") - combined_config["import_headings"] = import_headings - if import_footers: - for import_footer_key in import_footers: - combined_config.pop(f"{IMPORT_FOOTER_PREFIX}{import_footer_key}") - combined_config["import_footers"] = import_footers - - unsupported_config_errors = {} - for option in set(combined_config.keys()).difference( - getattr(_Config, "__dataclass_fields__", {}).keys() - ): - for source in reversed(sources): - if option in source: - unsupported_config_errors[option] = { - "value": source[option], - "source": source["source"], - } - if unsupported_config_errors: - raise UnsupportedSettings(unsupported_config_errors) - - super().__init__(sources=tuple(sources), **combined_config) - - def is_supported_filetype(self, file_name: str) -> bool: - _root, ext = os.path.splitext(file_name) - ext = ext.lstrip(".") - if ext in self.supported_extensions: - return True - if ext in self.blocked_extensions: - return False - - # Skip editor backup files. - if file_name.endswith("~"): - return False - - try: - if stat.S_ISFIFO(os.stat(file_name).st_mode): - return False - except OSError: - pass - - try: - with open(file_name, "rb") as fp: - line = fp.readline(100) - except OSError: - return False - return bool(_SHEBANG_RE.match(line)) - - def _check_folder_git_ls_files(self, folder: str) -> Path | None: - env = {**os.environ, "LANG": "C.UTF-8"} - try: - topfolder_result = subprocess.check_output( # nosec # skipcq: PYL-W1510 - ["git", "-C", folder, "rev-parse", "--show-toplevel"], encoding="utf-8", env=env - ) - except subprocess.CalledProcessError: - return None - - git_folder = Path(topfolder_result.rstrip()).resolve() - - # files committed to git - tracked_files = ( - subprocess.check_output( # nosec # skipcq: PYL-W1510 - ["git", "-C", str(git_folder), "ls-files", "-z"], - encoding="utf-8", - env=env, - ) - .rstrip("\0") - .split("\0") - ) - # files that haven't been committed yet, but aren't ignored - tracked_files_others = ( - subprocess.check_output( # nosec # skipcq: PYL-W1510 - ["git", "-C", str(git_folder), "ls-files", "-z", "--others", "--exclude-standard"], - encoding="utf-8", - env=env, - ) - .rstrip("\0") - .split("\0") - ) - - self.git_ls_files[git_folder] = { - str(git_folder / Path(f)) for f in tracked_files + tracked_files_others - } - return git_folder - - def is_skipped(self, file_path: Path) -> bool: - """Returns True if the file and/or folder should be skipped based on current settings.""" - if self.directory and Path(self.directory) in file_path.resolve().parents: - file_name = os.path.relpath(file_path.resolve(), self.directory) - else: - file_name = str(file_path) - - os_path = str(file_path) - - normalized_path = os_path.replace("\\", "/") - if normalized_path[1:2] == ":": - normalized_path = normalized_path[2:] - - for skip_path in self.skips: - if posixpath.abspath(normalized_path) == posixpath.abspath( - skip_path.replace("\\", "/") - ): - return True - - position = os.path.split(file_name) - while position[1]: - if position[1] in self.skips: - return True - position = os.path.split(position[0]) - - for sglob in self.skip_globs: - if fnmatch.fnmatch(file_name, sglob) or fnmatch.fnmatch("/" + file_name, sglob): - return True - - if not (os.path.isfile(os_path) or os.path.isdir(os_path) or os.path.islink(os_path)): - return True - - if self.skip_gitignore: - if file_path.name == ".git": # pragma: no cover - return True - - git_folder = None - - file_paths = [file_path, file_path.resolve()] - for folder in self.git_ls_files: - if any(folder in path.parents for path in file_paths): - git_folder = folder - break - else: - git_folder = self._check_folder_git_ls_files(str(file_path.parent)) - - # git_ls_files are good files you should parse. If you're not in the allow list, skip. - - if ( - git_folder - and not file_path.is_dir() - and str(file_path.resolve()) not in self.git_ls_files[git_folder] - ): - return True - - return False - - @property - def known_patterns(self) -> list[tuple[Pattern[str], str]]: - if self._known_patterns is not None: - return self._known_patterns - - self._known_patterns = [] - pattern_sections = [STDLIB] + [section for section in self.sections if section != STDLIB] - for placement in reversed(pattern_sections): - known_placement = KNOWN_SECTION_MAPPING.get(placement, placement).lower() - config_key = f"{KNOWN_PREFIX}{known_placement}" - known_modules = getattr(self, config_key, self.known_other.get(known_placement, ())) - extra_modules = getattr(self, f"extra_{known_placement}", ()) - all_modules = set(extra_modules).union(known_modules) - known_patterns = [ - pattern - for known_pattern in all_modules - for pattern in self._parse_known_pattern(known_pattern) - ] - for known_pattern in known_patterns: - regexp = "^" + known_pattern.replace("*", ".*").replace("?", ".?") + "$" - self._known_patterns.append((re.compile(regexp), placement)) - - return self._known_patterns - - @property - def section_comments(self) -> tuple[str, ...]: - if self._section_comments is not None: - return self._section_comments - - self._section_comments = tuple(f"# {heading}" for heading in self.import_headings.values()) - return self._section_comments - - @property - def section_comments_end(self) -> tuple[str, ...]: - if self._section_comments_end is not None: - return self._section_comments_end - - self._section_comments_end = tuple(f"# {footer}" for footer in self.import_footers.values()) - return self._section_comments_end - - @property - def skips(self) -> frozenset[str]: - if self._skips is not None: - return self._skips - - self._skips = self.skip.union(self.extend_skip) - return self._skips - - @property - def skip_globs(self) -> frozenset[str]: - if self._skip_globs is not None: - return self._skip_globs - - self._skip_globs = self.skip_glob.union(self.extend_skip_glob) - return self._skip_globs - - @property - def sorting_function(self) -> Callable[..., list[str]]: - if self._sorting_function is not None: - return self._sorting_function - - if self.sort_order == "natural": - self._sorting_function = sorting.naturally - elif self.sort_order == "native": - self._sorting_function = sorted - else: - available_sort_orders = ["natural", "native"] - for sort_plugin in entry_points(group="isort.sort_function"): - available_sort_orders.append(sort_plugin.name) - if sort_plugin.name == self.sort_order: - self._sorting_function = sort_plugin.load() - break - else: - raise SortingFunctionDoesNotExist(self.sort_order, available_sort_orders) - - return self._sorting_function - - def _parse_known_pattern(self, pattern: str) -> list[str]: - """Expand pattern if identified as a directory and return found sub packages""" - if pattern.endswith(os.path.sep): - patterns = [ - filename - for filename in os.listdir(os.path.join(self.directory, pattern)) - if os.path.isdir(os.path.join(self.directory, pattern, filename)) - ] - else: - patterns = [pattern] - - return patterns - - -def _get_str_to_type_converter(setting_name: str) -> Callable[[str], Any] | type[Any]: - type_converter: Callable[[str], Any] | type[Any] = type(_DEFAULT_SETTINGS.get(setting_name, "")) - if type_converter == WrapModes: - type_converter = wrap_mode_from_string - return type_converter - - -def _as_list(value: str) -> list[str]: - if isinstance(value, list): - return [item.strip() for item in value] - filtered = [item.strip() for item in value.replace("\n", ",").split(",") if item.strip()] - return filtered - - -def _abspaths(cwd: str, values: Iterable[str]) -> set[str]: - paths = { - ( - os.path.join(cwd, value) - if not value.startswith(os.path.sep) and value.endswith(os.path.sep) - else value - ) - for value in values - } - return paths - - -def _find_config(path: str) -> tuple[str, dict[str, Any]]: - current_directory = path - tries = 0 - while current_directory and tries < MAX_CONFIG_SEARCH_DEPTH: - for config_file_name in CONFIG_SOURCES: - potential_config_file = os.path.join(current_directory, config_file_name) - if os.path.isfile(potential_config_file): - config_data: dict[str, Any] - try: - config_data = _get_config_data( - potential_config_file, CONFIG_SECTIONS[config_file_name] - ) - except Exception: - warn( - f"Failed to pull configuration information from {potential_config_file}", - stacklevel=2, - ) - config_data = {} - if config_data: - return (current_directory, config_data) - - for stop_dir in STOP_CONFIG_SEARCH_ON_DIRS: - if os.path.isdir(os.path.join(current_directory, stop_dir)): - return (current_directory, {}) - - new_directory = os.path.split(current_directory)[0] - if new_directory == current_directory: - break - - current_directory = new_directory - tries += 1 - - return (path, {}) - - -def find_all_configs(path: str) -> Trie: - """ - Looks for config files in the path provided and in all of its sub-directories. - Parses and stores any config file encountered in a trie and returns the root of - the trie - """ - trie_root = Trie("default", {}) - - for dirpath, _, _ in os.walk(path): - for config_file_name in CONFIG_SOURCES: - potential_config_file = os.path.join(dirpath, config_file_name) - if os.path.isfile(potential_config_file): - config_data: dict[str, Any] - try: - config_data = _get_config_data( - potential_config_file, CONFIG_SECTIONS[config_file_name] - ) - except Exception: - warn( - f"Failed to pull configuration information from {potential_config_file}", - stacklevel=2, - ) - config_data = {} - - if config_data: - trie_root.insert(potential_config_file, config_data) - break - - return trie_root - - -def _get_config_data(file_path: str, sections: tuple[str, ...]) -> dict[str, Any]: - settings: dict[str, Any] = {} - - if file_path.endswith(".toml"): - with open(file_path, "rb") as bin_config_file: - config = tomllib.load(bin_config_file) - for section in sections: - config_section = config - for key in section.split("."): - config_section = config_section.get(key, {}) - settings.update(config_section) - else: - with open(file_path, encoding="utf-8") as config_file: - if file_path.endswith(".editorconfig"): - line = "\n" - last_position = config_file.tell() - while line: - line = config_file.readline() - if "[" in line: - config_file.seek(last_position) - break - last_position = config_file.tell() - - config = configparser.ConfigParser(strict=False) - config.read_file(config_file) - for section in sections: - if section.startswith("*.{") and section.endswith("}"): - extension = section[len("*.{") : -1] - for config_key in config: - if ( - config_key.startswith("*.{") - and config_key.endswith("}") - and extension - in (text.strip() for text in config_key[len("*.{") : -1].split(",")) - ): - settings.update(config.items(config_key)) - - elif config.has_section(section): - settings.update(config.items(section)) - - if settings: - settings["source"] = file_path - - if file_path.endswith(".editorconfig"): - indent_style = settings.pop("indent_style", "").strip() - indent_size = settings.pop("indent_size", "").strip() - if indent_size == "tab": - indent_size = settings.pop("tab_width", "").strip() - - if indent_style == "space": - settings["indent"] = " " * ((indent_size and int(indent_size)) or 4) - - elif indent_style == "tab": - settings["indent"] = "\t" * ((indent_size and int(indent_size)) or 1) - - max_line_length = settings.pop("max_line_length", "").strip() - if max_line_length and (max_line_length == "off" or max_line_length.isdigit()): - settings["line_length"] = ( - float("inf") if max_line_length == "off" else int(max_line_length) - ) - settings = { - key: value - for key, value in settings.items() - if key in _DEFAULT_SETTINGS or key.startswith(KNOWN_PREFIX) - } - - for key, value in settings.items(): - existing_value_type = _get_str_to_type_converter(key) - if existing_value_type is tuple: - settings[key] = tuple(_as_list(value)) - elif existing_value_type is frozenset: - settings[key] = frozenset(_as_list(settings.get(key))) # type: ignore - elif existing_value_type is bool: - # Only some configuration formats support native boolean values. - if not isinstance(value, bool): - value = _as_bool(value) - settings[key] = value - elif key.startswith(KNOWN_PREFIX): - settings[key] = _abspaths(os.path.dirname(file_path), _as_list(value)) - elif key == "force_grid_wrap": - try: - result = existing_value_type(value) - except ValueError: # backwards compatibility for true / false force grid wrap - result = 0 if value.lower().strip() == "false" else 2 - settings[key] = result - elif key == "comment_prefix": - settings[key] = str(value).strip("'").strip('"') - else: - settings[key] = existing_value_type(value) - - return settings - - -def _as_bool(value: str) -> bool: - """Given a string value that represents True or False, returns the Boolean equivalent. - Heavily inspired from distutils strtobool. - """ - try: - return _STR_BOOLEAN_MAPPING[value.lower()] - except KeyError: - raise ValueError(f"invalid truth value {value}") - - -def entry_points(group: str) -> "EntryPoints": - """Call entry_point after lazy loading it. - - TODO: The reason for lazy loading here are unknown. - """ - from importlib.metadata import entry_points as ep # noqa: PLC0415 - - return ep(group=group) - - -DEFAULT_CONFIG = Config() diff --git a/.venv/lib/python3.10/site-packages/isort/setuptools_commands.py b/.venv/lib/python3.10/site-packages/isort/setuptools_commands.py deleted file mode 100644 index 3e6cc2e..0000000 --- a/.venv/lib/python3.10/site-packages/isort/setuptools_commands.py +++ /dev/null @@ -1,63 +0,0 @@ -import glob -import os -import sys -from collections.abc import Iterator -from typing import Any -from warnings import warn - -import setuptools - -from . import api -from .settings import DEFAULT_CONFIG - - -class ISortCommand(setuptools.Command): - """The :class:`ISortCommand` class is used by setuptools to perform - imports checks on registered modules. - """ - - description = "Run isort on modules registered in setuptools" - # Potentially unused variable - check if can be safely removed - user_options: list[Any] = [] # type: ignore[misc] - - def initialize_options(self) -> None: - default_settings = vars(DEFAULT_CONFIG).copy() - for key, value in default_settings.items(): - setattr(self, key, value) - - def finalize_options(self) -> None: - """Get options from config files.""" - self.arguments: dict[str, Any] = {} # skipcq: PYL-W0201 - self.arguments["settings_path"] = os.getcwd() - - def distribution_files(self) -> Iterator[str]: - """Find distribution packages.""" - # This is verbatim from flake8 - if self.distribution.packages: # pragma: no cover - package_dirs = self.distribution.package_dir or {} - for package in self.distribution.packages: - pkg_dir = package - if package in package_dirs: - pkg_dir = package_dirs[package] - elif "" in package_dirs: # pragma: no cover - pkg_dir = package_dirs[""] + os.path.sep + pkg_dir - yield pkg_dir.replace(".", os.path.sep) - - if self.distribution.py_modules: - for filename in self.distribution.py_modules: - yield f"{filename}.py" - # Don't miss the setup.py file itself - yield "setup.py" - - def run(self) -> None: - arguments = self.arguments - wrong_sorted_files = False - for path in self.distribution_files(): - for python_file in glob.iglob(os.path.join(path, "*.py")): - try: - if not api.check_file(python_file, **arguments): - wrong_sorted_files = True # pragma: no cover - except OSError as error: # pragma: no cover - warn(f"Unable to parse file {python_file} due to {error}", stacklevel=2) - if wrong_sorted_files: - sys.exit(1) # pragma: no cover diff --git a/.venv/lib/python3.10/site-packages/isort/sorting.py b/.venv/lib/python3.10/site-packages/isort/sorting.py deleted file mode 100644 index cb42b15..0000000 --- a/.venv/lib/python3.10/site-packages/isort/sorting.py +++ /dev/null @@ -1,131 +0,0 @@ -import re -from collections.abc import Callable, Iterable -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from .settings import Config -else: - Config = Any - -_import_line_intro_re = re.compile("^(?:from|import) ") -_import_line_midline_import_re = re.compile(" import ") - - -def module_key( - module_name: str, - config: Config, - sub_imports: bool = False, - ignore_case: bool = False, - section_name: Any | None = None, - straight_import: bool | None = False, -) -> str: - match = re.match(r"^(\.+)\s*(.*)", module_name) - if match: - sep = " " if config.reverse_relative else "_" - module_name = sep.join(match.groups()) - - prefix = "" - if ignore_case: - module_name = str(module_name).lower() - else: - module_name = str(module_name) - - if sub_imports and config.order_by_type: - if module_name in config.constants: - prefix = "A" - elif module_name in config.classes: - prefix = "B" - elif module_name in config.variables: - prefix = "C" - elif module_name.isupper() and len(module_name) > 1: # see issue #376 - prefix = "A" - elif module_name in config.classes or module_name[0:1].isupper(): - prefix = "B" - else: - prefix = "C" - if not config.case_sensitive: - module_name = module_name.lower() - - length_sort = ( - config.length_sort - or (config.length_sort_straight and straight_import) - or str(section_name).lower() in config.length_sort_sections - ) - _length_sort_maybe = (str(len(module_name)) + ":" + module_name) if length_sort else module_name - return f"{(module_name in config.force_to_top and 'A') or 'B'}{prefix}{_length_sort_maybe}" - - -def section_key(line: str, config: Config) -> str: - section = "B" - - if ( - not config.sort_relative_in_force_sorted_sections - and config.reverse_relative - and line.startswith("from .") - ): - match = re.match(r"^from (\.+)\s*(.*)", line) - if match: # pragma: no cover - regex always matches if line starts with "from ." - line = f"from {' '.join(match.groups())}" - if config.group_by_package and line.strip().startswith("from"): - line = line.split(" import ", 1)[0] - - if config.lexicographical: - line = _import_line_intro_re.sub("", _import_line_midline_import_re.sub(".", line)) - else: - line = re.sub("^from ", "", line) - line = re.sub("^import ", "", line) - if config.sort_relative_in_force_sorted_sections: - sep = " " if config.reverse_relative else "_" - line = re.sub(r"^(\.+)", rf"\1{sep}", line) - if line.split(" ")[0] in config.force_to_top: - section = "A" - # * If honor_case_in_force_sorted_sections is true, and case_sensitive and - # order_by_type are different, only ignore case in part of the line. - # * Otherwise, let order_by_type decide the sorting of the whole line. This - # is only "correct" if case_sensitive and order_by_type have the same value. - if config.honor_case_in_force_sorted_sections and config.case_sensitive != config.order_by_type: - split_module = line.split(" import ", 1) - if len(split_module) > 1: - module_name, names = split_module - if not config.case_sensitive: - module_name = module_name.lower() - if not config.order_by_type: - names = names.lower() - line = f"{module_name} import {names}" - elif not config.case_sensitive: - line = line.lower() - elif not config.order_by_type: - line = line.lower() - - return f"{section}{len(line) if config.length_sort else ''}{line}" - - -def sort( - config: Config, - to_sort: Iterable[str], - key: Callable[[str], Any] | None = None, - reverse: bool = False, -) -> list[str]: - return config.sorting_function(to_sort, key=key, reverse=reverse) - - -def naturally( - to_sort: Iterable[str], key: Callable[[str], Any] | None = None, reverse: bool = False -) -> list[str]: - """Returns a naturally sorted list""" - if key is None: - key_callback = _natural_keys - else: - - def key_callback(text: str) -> list[Any]: - return _natural_keys(key(text)) - - return sorted(to_sort, key=key_callback, reverse=reverse) - - -def _atoi(text: str) -> Any: - return int(text) if text.isdigit() else text - - -def _natural_keys(text: str) -> list[Any]: - return [_atoi(c) for c in re.split(r"(\d+)", text)] diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py deleted file mode 100644 index 3c80af8..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from . import all as _all -from . import py2, py3, py27, py36, py37, py38, py39, py310, py311, py312, py313, py314 - -__all__ = ( - "_all", - "py2", - "py3", - "py27", - "py36", - "py37", - "py38", - "py39", - "py310", - "py311", - "py312", - "py313", - "py314", -) diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/all.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/all.py deleted file mode 100644 index 08a365e..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/all.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import py2, py3 - -stdlib = py2.stdlib | py3.stdlib diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py2.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py2.py deleted file mode 100644 index 74af019..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py2.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import py27 - -stdlib = py27.stdlib diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py27.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py27.py deleted file mode 100644 index a9bc99d..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py27.py +++ /dev/null @@ -1,301 +0,0 @@ -""" -File contains the standard library of Python 2.7. - -DO NOT EDIT. If the standard library changes, a new list should be created -using the mkstdlibs.py script. -""" - -stdlib = { - "AL", - "BaseHTTPServer", - "Bastion", - "CGIHTTPServer", - "Carbon", - "ColorPicker", - "ConfigParser", - "Cookie", - "DEVICE", - "DocXMLRPCServer", - "EasyDialogs", - "FL", - "FrameWork", - "GL", - "HTMLParser", - "MacOS", - "MimeWriter", - "MiniAEFrame", - "Nav", - "PixMapWrapper", - "Queue", - "SUNAUDIODEV", - "ScrolledText", - "SimpleHTTPServer", - "SimpleXMLRPCServer", - "SocketServer", - "StringIO", - "Tix", - "Tkinter", - "UserDict", - "UserList", - "UserString", - "W", - "__builtin__", - "_ast", - "_winreg", - "abc", - "aepack", - "aetools", - "aetypes", - "aifc", - "al", - "anydbm", - "applesingle", - "argparse", - "array", - "ast", - "asynchat", - "asyncore", - "atexit", - "audioop", - "autoGIL", - "base64", - "bdb", - "binascii", - "binhex", - "bisect", - "bsddb", - "buildtools", - "bz2", - "cPickle", - "cProfile", - "cStringIO", - "calendar", - "cd", - "cfmfile", - "cgi", - "cgitb", - "chunk", - "cmath", - "cmd", - "code", - "codecs", - "codeop", - "collections", - "colorsys", - "commands", - "compileall", - "compiler", - "contextlib", - "cookielib", - "copy", - "copy_reg", - "crypt", - "csv", - "ctypes", - "curses", - "datetime", - "dbhash", - "dbm", - "decimal", - "difflib", - "dircache", - "dis", - "distutils", - "dl", - "doctest", - "dumbdbm", - "dummy_thread", - "dummy_threading", - "email", - "encodings", - "ensurepip", - "errno", - "exceptions", - "fcntl", - "filecmp", - "fileinput", - "findertools", - "fl", - "flp", - "fm", - "fnmatch", - "formatter", - "fpectl", - "fpformat", - "fractions", - "ftplib", - "functools", - "future_builtins", - "gc", - "gdbm", - "gensuitemodule", - "getopt", - "getpass", - "gettext", - "gl", - "glob", - "grp", - "gzip", - "hashlib", - "heapq", - "hmac", - "hotshot", - "htmlentitydefs", - "htmllib", - "httplib", - "ic", - "icopen", - "imageop", - "imaplib", - "imgfile", - "imghdr", - "imp", - "importlib", - "imputil", - "inspect", - "io", - "itertools", - "jpeg", - "json", - "keyword", - "lib2to3", - "linecache", - "locale", - "logging", - "macerrors", - "macostools", - "macpath", - "macresource", - "mailbox", - "mailcap", - "marshal", - "math", - "md5", - "mhlib", - "mimetools", - "mimetypes", - "mimify", - "mmap", - "modulefinder", - "msilib", - "msvcrt", - "multifile", - "multiprocessing", - "mutex", - "netrc", - "new", - "nis", - "nntplib", - "ntpath", - "numbers", - "operator", - "optparse", - "os", - "ossaudiodev", - "parser", - "pdb", - "pickle", - "pickletools", - "pipes", - "pkgutil", - "platform", - "plistlib", - "popen2", - "poplib", - "posix", - "posixfile", - "posixpath", - "pprint", - "profile", - "pstats", - "pty", - "pwd", - "py_compile", - "pyclbr", - "pydoc", - "quopri", - "random", - "re", - "readline", - "resource", - "rexec", - "rfc822", - "rlcompleter", - "robotparser", - "runpy", - "sched", - "select", - "sets", - "sgmllib", - "sha", - "shelve", - "shlex", - "shutil", - "signal", - "site", - "smtpd", - "smtplib", - "sndhdr", - "socket", - "spwd", - "sqlite3", - "sre", - "sre_compile", - "sre_constants", - "sre_parse", - "ssl", - "stat", - "statvfs", - "string", - "stringprep", - "struct", - "subprocess", - "sunau", - "sunaudiodev", - "symbol", - "symtable", - "sys", - "sysconfig", - "syslog", - "tabnanny", - "tarfile", - "telnetlib", - "tempfile", - "termios", - "test", - "textwrap", - "thread", - "threading", - "time", - "timeit", - "token", - "tokenize", - "trace", - "traceback", - "ttk", - "tty", - "turtle", - "types", - "unicodedata", - "unittest", - "urllib", - "urllib2", - "urlparse", - "user", - "uu", - "uuid", - "videoreader", - "warnings", - "wave", - "weakref", - "webbrowser", - "whichdb", - "winsound", - "wsgiref", - "xdrlib", - "xml", - "xmlrpclib", - "zipfile", - "zipimport", - "zlib", -} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py3.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py3.py deleted file mode 100644 index 9d99ce0..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py3.py +++ /dev/null @@ -1,13 +0,0 @@ -from . import py36, py37, py38, py39, py310, py311, py312, py313, py314 - -stdlib = ( - py36.stdlib - | py37.stdlib - | py38.stdlib - | py39.stdlib - | py310.stdlib - | py311.stdlib - | py312.stdlib - | py313.stdlib - | py314.stdlib -) diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py310.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py310.py deleted file mode 100644 index f676996..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py310.py +++ /dev/null @@ -1,232 +0,0 @@ -""" -File contains the standard library of Python 3.10. - -DO NOT EDIT. If the standard library changes, a new list should be created -using the mkstdlibs.py script. -""" - -stdlib = { - "_ast", - "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", - "email", - "encodings", - "ensurepip", - "enum", - "errno", - "faulthandler", - "fcntl", - "filecmp", - "fileinput", - "fnmatch", - "fractions", - "ftplib", - "functools", - "gc", - "genericpath", - "getopt", - "getpass", - "gettext", - "glob", - "graphlib", - "grp", - "gzip", - "hashlib", - "heapq", - "hmac", - "html", - "http", - "idlelib", - "imaplib", - "imghdr", - "imp", - "importlib", - "inspect", - "io", - "ipaddress", - "itertools", - "json", - "keyword", - "lib2to3", - "linecache", - "locale", - "logging", - "lzma", - "mailbox", - "mailcap", - "marshal", - "math", - "mimetypes", - "mmap", - "modulefinder", - "msilib", - "msvcrt", - "multiprocessing", - "netrc", - "nis", - "nntplib", - "nt", - "ntpath", - "nturl2path", - "numbers", - "opcode", - "operator", - "optparse", - "os", - "ossaudiodev", - "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", - "sre_compile", - "sre_constants", - "sre_parse", - "ssl", - "stat", - "statistics", - "string", - "stringprep", - "struct", - "subprocess", - "sunau", - "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", - "xx", - "xxlimited", - "xxlimited_35", - "xxsubtype", - "zipapp", - "zipfile", - "zipimport", - "zlib", - "zoneinfo", -} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py311.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py311.py deleted file mode 100644 index 9f50ec8..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py311.py +++ /dev/null @@ -1,232 +0,0 @@ -""" -File contains the standard library of Python 3.11. - -DO NOT EDIT. If the standard library changes, a new list should be created -using the mkstdlibs.py script. -""" - -stdlib = { - "_ast", - "abc", - "aifc", - "antigravity", - "argparse", - "array", - "ast", - "asynchat", - "asyncio", - "asyncore", - "atexit", - "audioop", - "base64", - "bdb", - "binascii", - "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", - "email", - "encodings", - "ensurepip", - "enum", - "errno", - "faulthandler", - "fcntl", - "filecmp", - "fileinput", - "fnmatch", - "fractions", - "ftplib", - "functools", - "gc", - "genericpath", - "getopt", - "getpass", - "gettext", - "glob", - "graphlib", - "grp", - "gzip", - "hashlib", - "heapq", - "hmac", - "html", - "http", - "idlelib", - "imaplib", - "imghdr", - "imp", - "importlib", - "inspect", - "io", - "ipaddress", - "itertools", - "json", - "keyword", - "lib2to3", - "linecache", - "locale", - "logging", - "lzma", - "mailbox", - "mailcap", - "marshal", - "math", - "mimetypes", - "mmap", - "modulefinder", - "msilib", - "msvcrt", - "multiprocessing", - "netrc", - "nis", - "nntplib", - "nt", - "ntpath", - "nturl2path", - "numbers", - "opcode", - "operator", - "optparse", - "os", - "ossaudiodev", - "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", - "sre_compile", - "sre_constants", - "sre_parse", - "ssl", - "stat", - "statistics", - "string", - "stringprep", - "struct", - "subprocess", - "sunau", - "symtable", - "sys", - "sysconfig", - "syslog", - "tabnanny", - "tarfile", - "telnetlib", - "tempfile", - "termios", - "textwrap", - "this", - "threading", - "time", - "timeit", - "tkinter", - "token", - "tokenize", - "tomllib", - "trace", - "traceback", - "tracemalloc", - "tty", - "turtle", - "turtledemo", - "types", - "typing", - "unicodedata", - "unittest", - "urllib", - "uu", - "uuid", - "venv", - "warnings", - "wave", - "weakref", - "webbrowser", - "winreg", - "winsound", - "wsgiref", - "xdrlib", - "xml", - "xmlrpc", - "xx", - "xxlimited", - "xxlimited_35", - "xxsubtype", - "zipapp", - "zipfile", - "zipimport", - "zlib", - "zoneinfo", -} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py312.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py312.py deleted file mode 100644 index 7feac7f..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py312.py +++ /dev/null @@ -1,227 +0,0 @@ -""" -File contains the standard library of Python 3.12. - -DO NOT EDIT. If the standard library changes, a new list should be created -using the mkstdlibs.py script. -""" - -stdlib = { - "_ast", - "abc", - "aifc", - "antigravity", - "argparse", - "array", - "ast", - "asyncio", - "atexit", - "audioop", - "base64", - "bdb", - "binascii", - "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", - "doctest", - "email", - "encodings", - "ensurepip", - "enum", - "errno", - "faulthandler", - "fcntl", - "filecmp", - "fileinput", - "fnmatch", - "fractions", - "ftplib", - "functools", - "gc", - "genericpath", - "getopt", - "getpass", - "gettext", - "glob", - "graphlib", - "grp", - "gzip", - "hashlib", - "heapq", - "hmac", - "html", - "http", - "idlelib", - "imaplib", - "imghdr", - "importlib", - "inspect", - "io", - "ipaddress", - "itertools", - "json", - "keyword", - "lib2to3", - "linecache", - "locale", - "logging", - "lzma", - "mailbox", - "mailcap", - "marshal", - "math", - "mimetypes", - "mmap", - "modulefinder", - "msilib", - "msvcrt", - "multiprocessing", - "netrc", - "nis", - "nntplib", - "nt", - "ntpath", - "nturl2path", - "numbers", - "opcode", - "operator", - "optparse", - "os", - "ossaudiodev", - "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", - "smtplib", - "sndhdr", - "socket", - "socketserver", - "spwd", - "sqlite3", - "sre", - "sre_compile", - "sre_constants", - "sre_parse", - "ssl", - "stat", - "statistics", - "string", - "stringprep", - "struct", - "subprocess", - "sunau", - "symtable", - "sys", - "sysconfig", - "syslog", - "tabnanny", - "tarfile", - "telnetlib", - "tempfile", - "termios", - "textwrap", - "this", - "threading", - "time", - "timeit", - "tkinter", - "token", - "tokenize", - "tomllib", - "trace", - "traceback", - "tracemalloc", - "tty", - "turtle", - "turtledemo", - "types", - "typing", - "unicodedata", - "unittest", - "urllib", - "uu", - "uuid", - "venv", - "warnings", - "wave", - "weakref", - "webbrowser", - "winreg", - "winsound", - "wsgiref", - "xdrlib", - "xml", - "xmlrpc", - "xx", - "xxlimited", - "xxlimited_35", - "xxsubtype", - "zipapp", - "zipfile", - "zipimport", - "zlib", - "zoneinfo", -} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py313.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py313.py deleted file mode 100644 index 9015a18..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py313.py +++ /dev/null @@ -1,207 +0,0 @@ -""" -File contains the standard library of Python 3.13. - -DO NOT EDIT. If the standard library changes, a new list should be created -using the mkstdlibs.py script. -""" - -stdlib = { - "_ast", - "abc", - "antigravity", - "argparse", - "array", - "ast", - "asyncio", - "atexit", - "base64", - "bdb", - "binascii", - "bisect", - "builtins", - "bz2", - "cProfile", - "calendar", - "cmath", - "cmd", - "code", - "codecs", - "codeop", - "collections", - "colorsys", - "compileall", - "concurrent", - "configparser", - "contextlib", - "contextvars", - "copy", - "copyreg", - "csv", - "ctypes", - "curses", - "dataclasses", - "datetime", - "dbm", - "decimal", - "difflib", - "dis", - "doctest", - "email", - "encodings", - "ensurepip", - "enum", - "errno", - "faulthandler", - "fcntl", - "filecmp", - "fileinput", - "fnmatch", - "fractions", - "ftplib", - "functools", - "gc", - "genericpath", - "getopt", - "getpass", - "gettext", - "glob", - "graphlib", - "grp", - "gzip", - "hashlib", - "heapq", - "hmac", - "html", - "http", - "idlelib", - "imaplib", - "importlib", - "inspect", - "io", - "ipaddress", - "itertools", - "json", - "keyword", - "linecache", - "locale", - "logging", - "lzma", - "mailbox", - "marshal", - "math", - "mimetypes", - "mmap", - "modulefinder", - "msvcrt", - "multiprocessing", - "netrc", - "nt", - "ntpath", - "nturl2path", - "numbers", - "opcode", - "operator", - "optparse", - "os", - "pathlib", - "pdb", - "pickle", - "pickletools", - "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", - "smtplib", - "socket", - "socketserver", - "sqlite3", - "sre", - "sre_compile", - "sre_constants", - "sre_parse", - "ssl", - "stat", - "statistics", - "string", - "stringprep", - "struct", - "subprocess", - "symtable", - "sys", - "sysconfig", - "syslog", - "tabnanny", - "tarfile", - "tempfile", - "termios", - "textwrap", - "this", - "threading", - "time", - "timeit", - "tkinter", - "token", - "tokenize", - "tomllib", - "trace", - "traceback", - "tracemalloc", - "tty", - "turtle", - "turtledemo", - "types", - "typing", - "unicodedata", - "unittest", - "urllib", - "uuid", - "venv", - "warnings", - "wave", - "weakref", - "webbrowser", - "winreg", - "winsound", - "wsgiref", - "xml", - "xmlrpc", - "xx", - "xxlimited", - "xxlimited_35", - "xxsubtype", - "zipapp", - "zipfile", - "zipimport", - "zlib", - "zoneinfo", -} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py314.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py314.py deleted file mode 100644 index 66b9bd4..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py314.py +++ /dev/null @@ -1,208 +0,0 @@ -""" -File contains the standard library of Python 3.14. - -DO NOT EDIT. If the standard library changes, a new list should be created -using the mkstdlibs.py script. -""" - -stdlib = { - "_ast", - "abc", - "annotationlib", - "antigravity", - "argparse", - "array", - "ast", - "asyncio", - "atexit", - "base64", - "bdb", - "binascii", - "bisect", - "builtins", - "bz2", - "cProfile", - "calendar", - "cmath", - "cmd", - "code", - "codecs", - "codeop", - "collections", - "colorsys", - "compileall", - "concurrent", - "configparser", - "contextlib", - "contextvars", - "copy", - "copyreg", - "csv", - "ctypes", - "curses", - "dataclasses", - "datetime", - "dbm", - "decimal", - "difflib", - "dis", - "doctest", - "email", - "encodings", - "ensurepip", - "enum", - "errno", - "faulthandler", - "fcntl", - "filecmp", - "fileinput", - "fnmatch", - "fractions", - "ftplib", - "functools", - "gc", - "genericpath", - "getopt", - "getpass", - "gettext", - "glob", - "graphlib", - "grp", - "gzip", - "hashlib", - "heapq", - "hmac", - "html", - "http", - "idlelib", - "imaplib", - "importlib", - "inspect", - "io", - "ipaddress", - "itertools", - "json", - "keyword", - "linecache", - "locale", - "logging", - "lzma", - "mailbox", - "marshal", - "math", - "mimetypes", - "mmap", - "modulefinder", - "msvcrt", - "multiprocessing", - "netrc", - "nt", - "ntpath", - "nturl2path", - "numbers", - "opcode", - "operator", - "optparse", - "os", - "pathlib", - "pdb", - "pickle", - "pickletools", - "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", - "smtplib", - "socket", - "socketserver", - "sqlite3", - "sre", - "sre_compile", - "sre_constants", - "sre_parse", - "ssl", - "stat", - "statistics", - "string", - "stringprep", - "struct", - "subprocess", - "symtable", - "sys", - "sysconfig", - "syslog", - "tabnanny", - "tarfile", - "tempfile", - "termios", - "textwrap", - "this", - "threading", - "time", - "timeit", - "tkinter", - "token", - "tokenize", - "tomllib", - "trace", - "traceback", - "tracemalloc", - "tty", - "turtle", - "turtledemo", - "types", - "typing", - "unicodedata", - "unittest", - "urllib", - "uuid", - "venv", - "warnings", - "wave", - "weakref", - "webbrowser", - "winreg", - "winsound", - "wsgiref", - "xml", - "xmlrpc", - "xx", - "xxlimited", - "xxlimited_35", - "xxsubtype", - "zipapp", - "zipfile", - "zipimport", - "zlib", - "zoneinfo", -} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py36.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py36.py deleted file mode 100644 index 59ebd24..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py36.py +++ /dev/null @@ -1,224 +0,0 @@ -""" -File contains the standard library of Python 3.6. - -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", - "copy", - "copyreg", - "crypt", - "csv", - "ctypes", - "curses", - "datetime", - "dbm", - "decimal", - "difflib", - "dis", - "distutils", - "doctest", - "dummy_threading", - "email", - "encodings", - "ensurepip", - "enum", - "errno", - "faulthandler", - "fcntl", - "filecmp", - "fileinput", - "fnmatch", - "formatter", - "fpectl", - "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", -} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py37.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py37.py deleted file mode 100644 index e0ad122..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py37.py +++ /dev/null @@ -1,225 +0,0 @@ -""" -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", -} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py38.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py38.py deleted file mode 100644 index bf2cdf2..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py38.py +++ /dev/null @@ -1,233 +0,0 @@ -""" -File contains the standard library of Python 3.8. - -DO NOT EDIT. If the standard library changes, a new list should be created -using the mkstdlibs.py script. -""" - -stdlib = { - "_ast", - "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", - "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", - "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", - "xx", - "xxlimited", - "xxsubtype", - "zipapp", - "zipfile", - "zipimport", - "zlib", -} diff --git a/.venv/lib/python3.10/site-packages/isort/stdlibs/py39.py b/.venv/lib/python3.10/site-packages/isort/stdlibs/py39.py deleted file mode 100644 index 7615952..0000000 --- a/.venv/lib/python3.10/site-packages/isort/stdlibs/py39.py +++ /dev/null @@ -1,234 +0,0 @@ -""" -File contains the standard library of Python 3.9. - -DO NOT EDIT. If the standard library changes, a new list should be created -using the mkstdlibs.py script. -""" - -stdlib = { - "_ast", - "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", - "email", - "encodings", - "ensurepip", - "enum", - "errno", - "faulthandler", - "fcntl", - "filecmp", - "fileinput", - "fnmatch", - "formatter", - "fractions", - "ftplib", - "functools", - "gc", - "genericpath", - "getopt", - "getpass", - "gettext", - "glob", - "graphlib", - "grp", - "gzip", - "hashlib", - "heapq", - "hmac", - "html", - "http", - "idlelib", - "imaplib", - "imghdr", - "imp", - "importlib", - "inspect", - "io", - "ipaddress", - "itertools", - "json", - "keyword", - "lib2to3", - "linecache", - "locale", - "logging", - "lzma", - "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", - "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", - "xx", - "xxlimited", - "xxsubtype", - "zipapp", - "zipfile", - "zipimport", - "zlib", - "zoneinfo", -} diff --git a/.venv/lib/python3.10/site-packages/isort/utils.py b/.venv/lib/python3.10/site-packages/isort/utils.py deleted file mode 100644 index 2c4016d..0000000 --- a/.venv/lib/python3.10/site-packages/isort/utils.py +++ /dev/null @@ -1,74 +0,0 @@ -import os -import sys -from functools import lru_cache -from pathlib import Path -from typing import Any - - -class TrieNode: - def __init__(self, config_file: str = "", config_data: dict[str, Any] | None = None) -> None: - if not config_data: - config_data = {} - - self.nodes: dict[str, TrieNode] = {} - self.config_info: tuple[str, dict[str, Any]] = (config_file, config_data) - - -class Trie: - """ - A prefix tree to store the paths of all config files and to search the nearest config - associated with each file - """ - - def __init__(self, config_file: str = "", config_data: dict[str, Any] | None = None) -> None: - self.root: TrieNode = TrieNode(config_file, config_data) - - def insert(self, config_file: str, config_data: dict[str, Any]) -> None: - resolved_config_path_as_tuple = Path(config_file).parent.resolve().parts - - temp = self.root - - for path in resolved_config_path_as_tuple: - if path not in temp.nodes: - temp.nodes[path] = TrieNode() - - temp = temp.nodes[path] - - temp.config_info = (config_file, config_data) - - def search(self, filename: str) -> tuple[str, dict[str, Any]]: - """ - Returns the closest config relative to filename by doing a depth - first search on the prefix tree. - """ - resolved_file_path_as_tuple = Path(filename).resolve().parts - - temp = self.root - - last_stored_config: tuple[str, dict[str, Any]] = ("", {}) - - for path in resolved_file_path_as_tuple: - if temp.config_info[0]: - last_stored_config = temp.config_info - - if path not in temp.nodes: - break - - temp = temp.nodes[path] - - return last_stored_config - - -@lru_cache(maxsize=1000) -def exists_case_sensitive(path: str) -> bool: - """Returns if the given path exists and also matches the case on Windows. - - When finding files that can be imported, it is important for the cases to match because while - file os.path.exists("module.py") and os.path.exists("MODULE.py") both return True on Windows, - Python can only import using the case of the real file. - """ - result = os.path.exists(path) - if result and (sys.platform.startswith("win") or sys.platform == "darwin"): # pragma: no cover - directory, basename = os.path.split(path) - result = basename in os.listdir(directory) - return result diff --git a/.venv/lib/python3.10/site-packages/isort/wrap.py b/.venv/lib/python3.10/site-packages/isort/wrap.py deleted file mode 100644 index 5f0813d..0000000 --- a/.venv/lib/python3.10/site-packages/isort/wrap.py +++ /dev/null @@ -1,147 +0,0 @@ -import copy -import re -from collections.abc import Sequence - -from .settings import DEFAULT_CONFIG, Config -from .wrap_modes import WrapModes as Modes -from .wrap_modes import formatter_from_string, vertical_hanging_indent - - -def import_statement( - import_start: str, - from_imports: list[str], - comments: Sequence[str] = (), - line_separator: str = "\n", - config: Config = DEFAULT_CONFIG, - multi_line_output: Modes | None = None, - explode: bool = False, -) -> str: - """Returns a multi-line wrapped form of the provided from import statement.""" - if explode: - formatter = vertical_hanging_indent - line_length = 1 - include_trailing_comma = True - else: - formatter = formatter_from_string((multi_line_output or config.multi_line_output).name) - line_length = config.wrap_length or config.line_length - include_trailing_comma = config.include_trailing_comma - dynamic_indent = " " * (len(import_start) + 1) - indent = config.indent - statement = formatter( - statement=import_start, - imports=copy.copy(from_imports), - white_space=dynamic_indent, - indent=indent, - line_length=line_length, - comments=comments, - line_separator=line_separator, - comment_prefix=config.comment_prefix, - include_trailing_comma=include_trailing_comma, - remove_comments=config.ignore_comments, - ) - if config.balanced_wrapping: - lines = statement.split(line_separator) - line_count = len(lines) - if len(lines) > 1: - minimum_length = min(len(line) for line in lines[:-1]) - else: - minimum_length = 0 - new_import_statement = statement - while len(lines[-1]) < minimum_length and len(lines) == line_count and line_length > 10: - statement = new_import_statement - line_length -= 1 - new_import_statement = formatter( - statement=import_start, - imports=copy.copy(from_imports), - white_space=dynamic_indent, - indent=indent, - line_length=line_length, - comments=comments, - line_separator=line_separator, - comment_prefix=config.comment_prefix, - include_trailing_comma=include_trailing_comma, - remove_comments=config.ignore_comments, - ) - lines = new_import_statement.split(line_separator) - if statement.count(line_separator) == 0: - return _wrap_line(statement, line_separator, config) - return statement - - -def line(content: str, line_separator: str, config: Config = DEFAULT_CONFIG) -> str: - """Returns a line wrapped to the specified line-length, if possible.""" - wrap_mode = config.multi_line_output - if len(content) > config.line_length and wrap_mode != Modes.NOQA: # type: ignore - line_without_comment = content - comment = None - if "#" in content: - line_without_comment, comment = content.split("#", 1) - for splitter in ("import ", "cimport ", ".", "as "): - exp = r"\b" + re.escape(splitter) + r"\b" - if re.search(exp, line_without_comment) and not line_without_comment.strip().startswith( - splitter - ): - line_parts = re.split(exp, line_without_comment) - if comment and not (config.use_parentheses and "noqa" in comment): - _comma_maybe = ( - "," - if ( - config.include_trailing_comma - and config.use_parentheses - and not line_without_comment.rstrip().endswith(",") - ) - else "" - ) - line_parts[-1] = ( - f"{line_parts[-1].strip()}{_comma_maybe}{config.comment_prefix}{comment}" - ) - next_line = [] - while (len(content) + 2) > ( - config.wrap_length or config.line_length - ) and line_parts: - next_line.append(line_parts.pop()) - content = splitter.join(line_parts) - if not content: - content = next_line.pop() - - cont_line = _wrap_line( - config.indent + splitter.join(next_line).lstrip(), - line_separator, - config, - ) - if config.use_parentheses: - if splitter == "as ": - output = f"{content}{splitter}{cont_line.lstrip()}" - else: - _comma = "," if config.include_trailing_comma and not comment else "" - - if wrap_mode in ( - Modes.VERTICAL_HANGING_INDENT, # type: ignore - Modes.VERTICAL_GRID_GROUPED, # type: ignore - ): - _separator = line_separator - else: - _separator = "" - noqa_comment = "" - if comment and "noqa" in comment: - noqa_comment = f"{config.comment_prefix}{comment}" - cont_line = cont_line.rstrip() - _comma = "," if config.include_trailing_comma else "" - output = ( - f"{content}{splitter}({noqa_comment}" - f"{line_separator}{cont_line}{_comma}{_separator})" - ) - lines = output.split(line_separator) - if config.comment_prefix in lines[-1] and lines[-1].endswith(")"): - content, comment = lines[-1].split(config.comment_prefix, 1) - lines[-1] = content + ")" + config.comment_prefix + comment[:-1] - output = line_separator.join(lines) - return output - return f"{content}{splitter}\\{line_separator}{cont_line}" - elif len(content) > config.line_length and wrap_mode == Modes.NOQA and "# NOQA" not in content: # type: ignore - return f"{content}{config.comment_prefix} NOQA" - - return content - - -_wrap_line = line diff --git a/.venv/lib/python3.10/site-packages/isort/wrap_modes.py b/.venv/lib/python3.10/site-packages/isort/wrap_modes.py deleted file mode 100644 index cc4a97f..0000000 --- a/.venv/lib/python3.10/site-packages/isort/wrap_modes.py +++ /dev/null @@ -1,375 +0,0 @@ -"""Defines all wrap modes that can be used when outputting formatted imports""" - -import enum -from collections.abc import Callable -from inspect import signature -from typing import Any - -import isort.comments - -_wrap_modes: dict[str, Callable[..., str]] = {} - - -def from_string(value: str) -> "WrapModes": - return getattr(WrapModes, str(value), None) or WrapModes(int(value)) - - -def formatter_from_string(name: str) -> Callable[..., str]: - return _wrap_modes.get(name.upper(), grid) - - -def _wrap_mode_interface( - statement: str, - imports: list[str], - white_space: str, - indent: str, - line_length: int, - comments: list[str], - line_separator: str, - comment_prefix: str, - include_trailing_comma: bool, - remove_comments: bool, -) -> str: - """Defines the common interface used by all wrap mode functions""" - return "" - - -def _wrap_mode(function: Callable[..., str]) -> Callable[..., str]: - """Registers an individual wrap mode. Function name and order are significant and used for - creating enum. - """ - _wrap_modes[function.__name__.upper()] = function - function.__signature__ = signature(_wrap_mode_interface) # type: ignore - function.__annotations__ = _wrap_mode_interface.__annotations__ - return function - - -@_wrap_mode -def grid(**interface: Any) -> str: - if not interface["imports"]: - return "" - - interface["statement"] += "(" + interface["imports"].pop(0) - while interface["imports"]: - next_import = interface["imports"].pop(0) - next_statement = isort.comments.add_to_line( - interface["comments"], - interface["statement"] + ", " + next_import, - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - if ( - len(next_statement.split(interface["line_separator"])[-1]) + 1 - > interface["line_length"] - ): - lines = [f"{interface['white_space']}{next_import.split(' ')[0]}"] - for part in next_import.split(" ")[1:]: - new_line = f"{lines[-1]} {part}" - if len(new_line) + 1 > interface["line_length"]: - lines.append(f"{interface['white_space']}{part}") - else: - lines[-1] = new_line - next_import = interface["line_separator"].join(lines) - interface["statement"] = ( - isort.comments.add_to_line( - interface["comments"], - f"{interface['statement']},", - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - + f"{interface['line_separator']}{next_import}" - ) - interface["comments"] = [] - else: - interface["statement"] += ", " + next_import - return f"{interface['statement']}{',' if interface['include_trailing_comma'] else ''})" - - -@_wrap_mode -def vertical(**interface: Any) -> str: - if not interface["imports"]: - return "" - - first_import = ( - isort.comments.add_to_line( - interface["comments"], - interface["imports"].pop(0) + ",", - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - + interface["line_separator"] - + interface["white_space"] - ) - - _imports = ("," + interface["line_separator"] + interface["white_space"]).join( - interface["imports"] - ) - _comma_maybe = "," if interface["include_trailing_comma"] else "" - return f"{interface['statement']}({first_import}{_imports}{_comma_maybe})" - - -def _hanging_indent_end_line(line: str) -> str: - if not line.endswith(" "): - line += " " - return line + "\\" - - -@_wrap_mode -def hanging_indent(**interface: Any) -> str: - if not interface["imports"]: - return "" - - line_length_limit = interface["line_length"] - 3 - - next_import = interface["imports"].pop(0) - next_statement = interface["statement"] + next_import - # Check for first import - if len(next_statement) > line_length_limit: - next_statement = ( - _hanging_indent_end_line(interface["statement"]) - + interface["line_separator"] - + interface["indent"] - + next_import - ) - - interface["statement"] = next_statement - while interface["imports"]: - next_import = interface["imports"].pop(0) - next_statement = interface["statement"] + ", " + next_import - if len(next_statement.split(interface["line_separator"])[-1]) > line_length_limit: - next_statement = ( - _hanging_indent_end_line(interface["statement"] + ",") - + f"{interface['line_separator']}{interface['indent']}{next_import}" - ) - interface["statement"] = next_statement - - if interface["comments"]: - statement_with_comments = isort.comments.add_to_line( - interface["comments"], - interface["statement"], - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - if len(statement_with_comments.split(interface["line_separator"])[-1]) <= ( - line_length_limit + 2 - ): - return statement_with_comments - return ( - _hanging_indent_end_line(interface["statement"]) - + str(interface["line_separator"]) - + isort.comments.add_to_line( - interface["comments"], - interface["indent"], - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"].lstrip(), - ) - ) - return str(interface["statement"]) - - -@_wrap_mode -def vertical_hanging_indent(**interface: Any) -> str: - _line_with_comments = isort.comments.add_to_line( - interface["comments"], - "", - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - _imports = ("," + interface["line_separator"] + interface["indent"]).join(interface["imports"]) - _comma_maybe = "," if interface["include_trailing_comma"] else "" - return ( - f"{interface['statement']}({_line_with_comments}{interface['line_separator']}" - f"{interface['indent']}{_imports}{_comma_maybe}{interface['line_separator']})" - ) - - -def _vertical_grid_common(need_trailing_char: bool, **interface: Any) -> str: - if not interface["imports"]: - return "" - - interface["statement"] += ( - isort.comments.add_to_line( - interface["comments"], - "(", - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - + interface["line_separator"] - + interface["indent"] - + interface["imports"].pop(0) - ) - while interface["imports"]: - next_import = interface["imports"].pop(0) - next_statement = f"{interface['statement']}, {next_import}" - current_line_length = len(next_statement.split(interface["line_separator"])[-1]) - if interface["imports"] or interface["include_trailing_comma"]: - # We need to account for a comma after this import. - current_line_length += 1 - if not interface["imports"] and need_trailing_char: - # We need to account for a closing ) we're going to add. - current_line_length += 1 - if current_line_length > interface["line_length"]: - next_statement = ( - f"{interface['statement']},{interface['line_separator']}" - f"{interface['indent']}{next_import}" - ) - interface["statement"] = next_statement - if interface["include_trailing_comma"]: - interface["statement"] += "," - return str(interface["statement"]) - - -@_wrap_mode -def vertical_grid(**interface: Any) -> str: - return _vertical_grid_common(need_trailing_char=True, **interface) + ")" - - -@_wrap_mode -def vertical_grid_grouped(**interface: Any) -> str: - return ( - _vertical_grid_common(need_trailing_char=False, **interface) - + str(interface["line_separator"]) - + ")" - ) - - -@_wrap_mode -def vertical_grid_grouped_no_comma(**interface: Any) -> str: - # This is a deprecated alias for vertical_grid_grouped above. This function - # needs to exist for backwards compatibility but should never get called. - raise NotImplementedError - - -@_wrap_mode -def noqa(**interface: Any) -> str: - _imports = ", ".join(interface["imports"]) - retval = f"{interface['statement']}{_imports}" - comment_str = " ".join(interface["comments"]) - if interface["comments"]: - if ( - len(retval) + len(interface["comment_prefix"]) + 1 + len(comment_str) - <= interface["line_length"] - ): - return f"{retval}{interface['comment_prefix']} {comment_str}" - if "NOQA" in interface["comments"]: - return f"{retval}{interface['comment_prefix']} {comment_str}" - return f"{retval}{interface['comment_prefix']} NOQA {comment_str}" - - if len(retval) <= interface["line_length"]: - return retval - return f"{retval}{interface['comment_prefix']} NOQA" - - -@_wrap_mode -def vertical_hanging_indent_bracket(**interface: Any) -> str: - if not interface["imports"]: - return "" - statement = vertical_hanging_indent(**interface) - return f"{statement[:-1]}{interface['indent']})" - - -@_wrap_mode -def vertical_prefix_from_module_import(**interface: Any) -> str: - if not interface["imports"]: - return "" - - prefix_statement = interface["statement"] - output_statement = prefix_statement + interface["imports"].pop(0) - comments = interface["comments"] - - statement = output_statement - statement_with_comments = "" - for next_import in interface["imports"]: - statement = statement + ", " + next_import - statement_with_comments = isort.comments.add_to_line( - comments, - statement, - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - if ( - len(statement_with_comments.split(interface["line_separator"])[-1]) + 1 - > interface["line_length"] - ): - statement = ( - isort.comments.add_to_line( - comments, - output_statement, - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - + f"{interface['line_separator']}{prefix_statement}{next_import}" - ) - comments = [] - output_statement = statement - - if comments and statement_with_comments: - output_statement = statement_with_comments - return str(output_statement) - - -@_wrap_mode -def hanging_indent_with_parentheses(**interface: Any) -> str: - if not interface["imports"]: - return "" - - line_length_limit = interface["line_length"] - 1 - - interface["statement"] += "(" - next_import = interface["imports"].pop(0) - next_statement = interface["statement"] + next_import - # Check for first import - if len(next_statement) > line_length_limit: - next_statement = ( - isort.comments.add_to_line( - interface["comments"], - interface["statement"], - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - + f"{interface['line_separator']}{interface['indent']}{next_import}" - ) - interface["comments"] = [] - interface["statement"] = next_statement - while interface["imports"]: - next_import = interface["imports"].pop(0) - if ( - interface["line_separator"] not in interface["statement"] - and "#" in interface["statement"] - ): # pragma: no cover # TODO: fix, this is because of test run inconsistency. - line, comments = interface["statement"].split("#", 1) - next_statement = ( - f"{line.rstrip()}, {next_import}{interface['comment_prefix']}{comments}" - ) - else: - next_statement = isort.comments.add_to_line( - interface["comments"], - interface["statement"] + ", " + next_import, - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - current_line = next_statement.split(interface["line_separator"])[-1] - if len(current_line) > line_length_limit: - next_statement = ( - isort.comments.add_to_line( - interface["comments"], - interface["statement"] + ",", - removed=interface["remove_comments"], - comment_prefix=interface["comment_prefix"], - ) - + f"{interface['line_separator']}{interface['indent']}{next_import}" - ) - interface["comments"] = [] - interface["statement"] = next_statement - return f"{interface['statement']}{',' if interface['include_trailing_comma'] else ''})" - - -@_wrap_mode -def backslash_grid(**interface: Any) -> str: - interface["indent"] = interface["white_space"][:-1] - return hanging_indent(**interface) - - -WrapModes = enum.Enum( # type: ignore - "WrapModes", {wrap_mode: index for index, wrap_mode in enumerate(_wrap_modes.keys())} -) diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE deleted file mode 100644 index 8fd356e..0000000 --- a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright © Ned Batchelder -Copyright © 2011-2013 Tarek Ziade -Copyright © 2013 Florent Xicluna - -Licensed under the terms of the Expat License - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation files -(the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA deleted file mode 100644 index e25facd..0000000 --- a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA +++ /dev/null @@ -1,199 +0,0 @@ -Metadata-Version: 2.1 -Name: mccabe -Version: 0.7.0 -Summary: McCabe checker, plugin for flake8 -Home-page: https://github.com/pycqa/mccabe -Author: Tarek Ziade -Author-email: tarek@ziade.org -Maintainer: Ian Stapleton Cordasco -Maintainer-email: graffatcolmingov@gmail.com -License: Expat license -Keywords: flake8 mccabe -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Software Development :: Quality Assurance -Requires-Python: >=3.6 -License-File: LICENSE - -McCabe complexity checker -========================= - -Ned's script to check McCabe complexity. - -This module provides a plugin for ``flake8``, the Python code checker. - - -Installation ------------- - -You can install, upgrade, or uninstall ``mccabe`` with these commands:: - - $ pip install mccabe - $ pip install --upgrade mccabe - $ pip uninstall mccabe - - -Standalone script ------------------ - -The complexity checker can be used directly:: - - $ python -m mccabe --min 5 mccabe.py - ("185:1: 'PathGraphingAstVisitor.visitIf'", 5) - ("71:1: 'PathGraph.to_dot'", 5) - ("245:1: 'McCabeChecker.run'", 5) - ("283:1: 'main'", 7) - ("203:1: 'PathGraphingAstVisitor.visitTryExcept'", 5) - ("257:1: 'get_code_complexity'", 5) - - -Plugin for Flake8 ------------------ - -When both ``flake8 2+`` and ``mccabe`` are installed, the plugin is -available in ``flake8``:: - - $ flake8 --version - 2.0 (pep8: 1.4.2, pyflakes: 0.6.1, mccabe: 0.2) - -By default the plugin is disabled. Use the ``--max-complexity`` switch to -enable it. It will emit a warning if the McCabe complexity of a function is -higher than the provided value:: - - $ flake8 --max-complexity 10 coolproject - ... - coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14) - -This feature is quite useful for detecting over-complex code. According to McCabe, -anything that goes beyond 10 is too complex. - -Flake8 has many features that mccabe does not provide. Flake8 allows users to -ignore violations reported by plugins with ``# noqa``. Read more about this in -`their documentation -`__. -To silence violations reported by ``mccabe``, place your ``# noqa: C901`` on -the function definition line, where the error is reported for (possibly a -decorator). - - -Links ------ - -* Feedback and ideas: http://mail.python.org/mailman/listinfo/code-quality - -* Cyclomatic complexity: http://en.wikipedia.org/wiki/Cyclomatic_complexity - -* Ned Batchelder's script: - http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html - -* McCabe complexity: http://en.wikipedia.org/wiki/Cyclomatic_complexity - - -Changes -------- - -0.7.0 - 2021-01-23 -`````````````````` - -* Drop support for all versions of Python lower than 3.6 - -* Add support for Python 3.8, 3.9, and 3.10 - -* Fix option declaration for Flake8 - -0.6.1 - 2017-01-26 -`````````````````` - -* Fix signature for ``PathGraphingAstVisitor.default`` to match the signature - for ``ASTVisitor`` - -0.6.0 - 2017-01-23 -`````````````````` - -* Add support for Python 3.6 - -* Fix handling for missing statement types - -0.5.3 - 2016-12-14 -`````````````````` - -* Report actual column number of violation instead of the start of the line - -0.5.2 - 2016-07-31 -`````````````````` - -* When opening files ourselves, make sure we always name the file variable - -0.5.1 - 2016-07-28 -`````````````````` - -* Set default maximum complexity to -1 on the class itself - -0.5.0 - 2016-05-30 -`````````````````` - -* PyCon 2016 PDX release - -* Add support for Flake8 3.0 - -0.4.0 - 2016-01-27 -`````````````````` - -* Stop testing on Python 3.2 - -* Add support for async/await keywords on Python 3.5 from PEP 0492 - -0.3.1 - 2015-06-14 -`````````````````` - -* Include ``test_mccabe.py`` in releases. - -* Always coerce the ``max_complexity`` value from Flake8's entry-point to an - integer. - -0.3 - 2014-12-17 -```````````````` - -* Computation was wrong: the mccabe complexity starts at 1, not 2. - -* The ``max-complexity`` value is now inclusive. E.g.: if the - value is 10 and the reported complexity is 10, then it passes. - -* Add tests. - - -0.2.1 - 2013-04-03 -`````````````````` - -* Do not require ``setuptools`` in setup.py. It works around an issue - with ``pip`` and Python 3. - - -0.2 - 2013-02-22 -```````````````` - -* Rename project to ``mccabe``. - -* Provide ``flake8.extension`` setuptools entry point. - -* Read ``max-complexity`` from the configuration file. - -* Rename argument ``min_complexity`` to ``threshold``. - - -0.1 - 2013-02-11 -```````````````` -* First release - - diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD deleted file mode 100644 index 69c4dbd..0000000 --- a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD +++ /dev/null @@ -1,9 +0,0 @@ -__pycache__/mccabe.cpython-310.pyc,, -mccabe-0.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -mccabe-0.7.0.dist-info/LICENSE,sha256=EPvAA8uvims89xlbgNrJbIba85ADmyq_bntuc1r9fXQ,1221 -mccabe-0.7.0.dist-info/METADATA,sha256=oMxU_cw4ev2Q23YTL3NRg4pebHSqlrbF_DSSs-cpfBE,5035 -mccabe-0.7.0.dist-info/RECORD,, -mccabe-0.7.0.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110 -mccabe-0.7.0.dist-info/entry_points.txt,sha256=N2NH182GXTUyTm8r8XMgadb9C-CRa5dUr1k8OC91uGE,47 -mccabe-0.7.0.dist-info/top_level.txt,sha256=21cXuqZE-lpcfAqqANvX9EjI1ED1p8zcViv064u3RKA,7 -mccabe.py,sha256=g_kB8oPilNLemdOirPaZymQyyjqAH0kowrncUQaaw00,10654 diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL deleted file mode 100644 index 0b18a28..0000000 --- a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.37.1) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt deleted file mode 100644 index cc6645b..0000000 --- a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[flake8.extension] -C90 = mccabe:McCabeChecker - diff --git a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt b/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt deleted file mode 100644 index 8831b36..0000000 --- a/.venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -mccabe diff --git a/.venv/lib/python3.10/site-packages/mccabe.py b/.venv/lib/python3.10/site-packages/mccabe.py deleted file mode 100644 index 5746504..0000000 --- a/.venv/lib/python3.10/site-packages/mccabe.py +++ /dev/null @@ -1,346 +0,0 @@ -""" Meager code path measurement tool. - Ned Batchelder - http://nedbatchelder.com/blog/200803/python_code_complexity_microtool.html - MIT License. -""" -from __future__ import with_statement - -import optparse -import sys -import tokenize - -from collections import defaultdict -try: - import ast - from ast import iter_child_nodes -except ImportError: # Python 2.5 - from flake8.util import ast, iter_child_nodes - -__version__ = '0.7.0' - - -class ASTVisitor(object): - """Performs a depth-first walk of the AST.""" - - def __init__(self): - self.node = None - self._cache = {} - - def default(self, node, *args): - for child in iter_child_nodes(node): - self.dispatch(child, *args) - - def dispatch(self, node, *args): - self.node = node - klass = node.__class__ - meth = self._cache.get(klass) - if meth is None: - className = klass.__name__ - meth = getattr(self.visitor, 'visit' + className, self.default) - self._cache[klass] = meth - return meth(node, *args) - - def preorder(self, tree, visitor, *args): - """Do preorder walk of tree using visitor""" - self.visitor = visitor - visitor.visit = self.dispatch - self.dispatch(tree, *args) # XXX *args make sense? - - -class PathNode(object): - def __init__(self, name, look="circle"): - self.name = name - self.look = look - - def to_dot(self): - print('node [shape=%s,label="%s"] %d;' % ( - self.look, self.name, self.dot_id())) - - def dot_id(self): - return id(self) - - -class PathGraph(object): - def __init__(self, name, entity, lineno, column=0): - self.name = name - self.entity = entity - self.lineno = lineno - self.column = column - self.nodes = defaultdict(list) - - def connect(self, n1, n2): - self.nodes[n1].append(n2) - # Ensure that the destination node is always counted. - self.nodes[n2] = [] - - def to_dot(self): - print('subgraph {') - for node in self.nodes: - node.to_dot() - for node, nexts in self.nodes.items(): - for next in nexts: - print('%s -- %s;' % (node.dot_id(), next.dot_id())) - print('}') - - def complexity(self): - """ Return the McCabe complexity for the graph. - V-E+2 - """ - num_edges = sum([len(n) for n in self.nodes.values()]) - num_nodes = len(self.nodes) - return num_edges - num_nodes + 2 - - -class PathGraphingAstVisitor(ASTVisitor): - """ A visitor for a parsed Abstract Syntax Tree which finds executable - statements. - """ - - def __init__(self): - super(PathGraphingAstVisitor, self).__init__() - self.classname = "" - self.graphs = {} - self.reset() - - def reset(self): - self.graph = None - self.tail = None - - def dispatch_list(self, node_list): - for node in node_list: - self.dispatch(node) - - def visitFunctionDef(self, node): - - if self.classname: - entity = '%s%s' % (self.classname, node.name) - else: - entity = node.name - - name = '%d:%d: %r' % (node.lineno, node.col_offset, entity) - - if self.graph is not None: - # closure - pathnode = self.appendPathNode(name) - self.tail = pathnode - self.dispatch_list(node.body) - bottom = PathNode("", look='point') - self.graph.connect(self.tail, bottom) - self.graph.connect(pathnode, bottom) - self.tail = bottom - else: - self.graph = PathGraph(name, entity, node.lineno, node.col_offset) - pathnode = PathNode(name) - self.tail = pathnode - self.dispatch_list(node.body) - self.graphs["%s%s" % (self.classname, node.name)] = self.graph - self.reset() - - visitAsyncFunctionDef = visitFunctionDef - - def visitClassDef(self, node): - old_classname = self.classname - self.classname += node.name + "." - self.dispatch_list(node.body) - self.classname = old_classname - - def appendPathNode(self, name): - if not self.tail: - return - pathnode = PathNode(name) - self.graph.connect(self.tail, pathnode) - self.tail = pathnode - return pathnode - - def visitSimpleStatement(self, node): - if node.lineno is None: - lineno = 0 - else: - lineno = node.lineno - name = "Stmt %d" % lineno - self.appendPathNode(name) - - def default(self, node, *args): - if isinstance(node, ast.stmt): - self.visitSimpleStatement(node) - else: - super(PathGraphingAstVisitor, self).default(node, *args) - - def visitLoop(self, node): - name = "Loop %d" % node.lineno - self._subgraph(node, name) - - visitAsyncFor = visitFor = visitWhile = visitLoop - - def visitIf(self, node): - name = "If %d" % node.lineno - self._subgraph(node, name) - - def _subgraph(self, node, name, extra_blocks=()): - """create the subgraphs representing any `if` and `for` statements""" - if self.graph is None: - # global loop - self.graph = PathGraph(name, name, node.lineno, node.col_offset) - pathnode = PathNode(name) - self._subgraph_parse(node, pathnode, extra_blocks) - self.graphs["%s%s" % (self.classname, name)] = self.graph - self.reset() - else: - pathnode = self.appendPathNode(name) - self._subgraph_parse(node, pathnode, extra_blocks) - - def _subgraph_parse(self, node, pathnode, extra_blocks): - """parse the body and any `else` block of `if` and `for` statements""" - loose_ends = [] - self.tail = pathnode - self.dispatch_list(node.body) - loose_ends.append(self.tail) - for extra in extra_blocks: - self.tail = pathnode - self.dispatch_list(extra.body) - loose_ends.append(self.tail) - if node.orelse: - self.tail = pathnode - self.dispatch_list(node.orelse) - loose_ends.append(self.tail) - else: - loose_ends.append(pathnode) - if pathnode: - bottom = PathNode("", look='point') - for le in loose_ends: - self.graph.connect(le, bottom) - self.tail = bottom - - def visitTryExcept(self, node): - name = "TryExcept %d" % node.lineno - self._subgraph(node, name, extra_blocks=node.handlers) - - visitTry = visitTryExcept - - def visitWith(self, node): - name = "With %d" % node.lineno - self.appendPathNode(name) - self.dispatch_list(node.body) - - visitAsyncWith = visitWith - - -class McCabeChecker(object): - """McCabe cyclomatic complexity checker.""" - name = 'mccabe' - version = __version__ - _code = 'C901' - _error_tmpl = "C901 %r is too complex (%d)" - max_complexity = -1 - - def __init__(self, tree, filename): - self.tree = tree - - @classmethod - def add_options(cls, parser): - flag = '--max-complexity' - kwargs = { - 'default': -1, - 'action': 'store', - 'type': int, - 'help': 'McCabe complexity threshold', - 'parse_from_config': 'True', - } - config_opts = getattr(parser, 'config_options', None) - if isinstance(config_opts, list): - # Flake8 2.x - kwargs.pop('parse_from_config') - parser.add_option(flag, **kwargs) - parser.config_options.append('max-complexity') - else: - parser.add_option(flag, **kwargs) - - @classmethod - def parse_options(cls, options): - cls.max_complexity = int(options.max_complexity) - - def run(self): - if self.max_complexity < 0: - return - visitor = PathGraphingAstVisitor() - visitor.preorder(self.tree, visitor) - for graph in visitor.graphs.values(): - if graph.complexity() > self.max_complexity: - text = self._error_tmpl % (graph.entity, graph.complexity()) - yield graph.lineno, graph.column, text, type(self) - - -def get_code_complexity(code, threshold=7, filename='stdin'): - try: - tree = compile(code, filename, "exec", ast.PyCF_ONLY_AST) - except SyntaxError: - e = sys.exc_info()[1] - sys.stderr.write("Unable to parse %s: %s\n" % (filename, e)) - return 0 - - complx = [] - McCabeChecker.max_complexity = threshold - for lineno, offset, text, check in McCabeChecker(tree, filename).run(): - complx.append('%s:%d:1: %s' % (filename, lineno, text)) - - if len(complx) == 0: - return 0 - print('\n'.join(complx)) - return len(complx) - - -def get_module_complexity(module_path, threshold=7): - """Returns the complexity of a module""" - code = _read(module_path) - return get_code_complexity(code, threshold, filename=module_path) - - -def _read(filename): - if (2, 5) < sys.version_info < (3, 0): - with open(filename, 'rU') as f: - return f.read() - elif (3, 0) <= sys.version_info < (4, 0): - """Read the source code.""" - try: - with open(filename, 'rb') as f: - (encoding, _) = tokenize.detect_encoding(f.readline) - except (LookupError, SyntaxError, UnicodeError): - # Fall back if file encoding is improperly declared - with open(filename, encoding='latin-1') as f: - return f.read() - with open(filename, 'r', encoding=encoding) as f: - return f.read() - - -def main(argv=None): - if argv is None: - argv = sys.argv[1:] - opar = optparse.OptionParser() - opar.add_option("-d", "--dot", dest="dot", - help="output a graphviz dot file", action="store_true") - opar.add_option("-m", "--min", dest="threshold", - help="minimum complexity for output", type="int", - default=1) - - options, args = opar.parse_args(argv) - - code = _read(args[0]) - tree = compile(code, args[0], "exec", ast.PyCF_ONLY_AST) - visitor = PathGraphingAstVisitor() - visitor.preorder(tree, visitor) - - if options.dot: - print('graph {') - for graph in visitor.graphs.values(): - if (not options.threshold or - graph.complexity() >= options.threshold): - graph.to_dot() - print('}') - else: - for graph in visitor.graphs.values(): - if graph.complexity() >= options.threshold: - print(graph.name, graph.complexity()) - - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA deleted file mode 100644 index f39386d..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/METADATA +++ /dev/null @@ -1,350 +0,0 @@ -Metadata-Version: 2.4 -Name: platformdirs -Version: 4.5.0 -Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`. -Project-URL: Changelog, https://github.com/tox-dev/platformdirs/releases -Project-URL: Documentation, https://platformdirs.readthedocs.io -Project-URL: Homepage, https://github.com/tox-dev/platformdirs -Project-URL: Source, https://github.com/tox-dev/platformdirs -Project-URL: Tracker, https://github.com/tox-dev/platformdirs/issues -Maintainer-email: Bernát Gábor , Julian Berman , Ofek Lev , Ronny Pfannschmidt -License-Expression: MIT -License-File: LICENSE -Keywords: appdirs,application,cache,directory,log,user -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Python: >=3.10 -Provides-Extra: docs -Requires-Dist: furo>=2025.9.25; extra == 'docs' -Requires-Dist: proselint>=0.14; extra == 'docs' -Requires-Dist: sphinx-autodoc-typehints>=3.2; extra == 'docs' -Requires-Dist: sphinx>=8.2.3; extra == 'docs' -Provides-Extra: test -Requires-Dist: appdirs==1.4.4; extra == 'test' -Requires-Dist: covdefaults>=2.3; extra == 'test' -Requires-Dist: pytest-cov>=7; extra == 'test' -Requires-Dist: pytest-mock>=3.15.1; extra == 'test' -Requires-Dist: pytest>=8.4.2; extra == 'test' -Provides-Extra: type -Requires-Dist: mypy>=1.18.2; extra == 'type' -Description-Content-Type: text/x-rst - -The problem -=========== - -.. image:: https://badge.fury.io/py/platformdirs.svg - :target: https://badge.fury.io/py/platformdirs -.. image:: https://img.shields.io/pypi/pyversions/platformdirs.svg - :target: https://pypi.python.org/pypi/platformdirs/ -.. image:: https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg - :target: https://github.com/platformdirs/platformdirs/actions -.. image:: https://static.pepy.tech/badge/platformdirs/month - :target: https://pepy.tech/project/platformdirs - -When writing desktop application, finding the right location to store user data -and configuration varies per platform. Even for single-platform apps, there -may by plenty of nuances in figuring out the right location. - -For example, if running on macOS, you should use:: - - ~/Library/Application Support/ - -If on Windows (at least English Win) that should be:: - - C:\Users\\Application Data\Local Settings\\ - -or possibly:: - - C:\Users\\Application Data\\ - -for `roaming profiles `_ but that is another story. - -On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be:: - - ~/.local/share/ - -.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html - -``platformdirs`` to the rescue -============================== - -This kind of thing is what the ``platformdirs`` package is for. -``platformdirs`` will help you choose an appropriate: - -- user data dir (``user_data_dir``) -- user config dir (``user_config_dir``) -- user cache dir (``user_cache_dir``) -- site data dir (``site_data_dir``) -- site config dir (``site_config_dir``) -- user log dir (``user_log_dir``) -- user documents dir (``user_documents_dir``) -- user downloads dir (``user_downloads_dir``) -- user pictures dir (``user_pictures_dir``) -- user videos dir (``user_videos_dir``) -- user music dir (``user_music_dir``) -- user desktop dir (``user_desktop_dir``) -- user runtime dir (``user_runtime_dir``) - -And also: - -- Is slightly opinionated on the directory names used. Look for "OPINION" in - documentation and code for when an opinion is being applied. - -Example output -============== - -On macOS: - -.. code-block:: pycon - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/Users/trentm/Library/Application Support/SuperApp' - >>> user_config_dir(appname, appauthor) - '/Users/trentm/Library/Application Support/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/Users/trentm/Library/Caches/SuperApp' - >>> site_data_dir(appname, appauthor) - '/Library/Application Support/SuperApp' - >>> site_config_dir(appname, appauthor) - '/Library/Application Support/SuperApp' - >>> user_log_dir(appname, appauthor) - '/Users/trentm/Library/Logs/SuperApp' - >>> user_documents_dir() - '/Users/trentm/Documents' - >>> user_downloads_dir() - '/Users/trentm/Downloads' - >>> user_pictures_dir() - '/Users/trentm/Pictures' - >>> user_videos_dir() - '/Users/trentm/Movies' - >>> user_music_dir() - '/Users/trentm/Music' - >>> user_desktop_dir() - '/Users/trentm/Desktop' - >>> user_runtime_dir(appname, appauthor) - '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' - -On Windows: - -.. code-block:: pycon - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' - >>> user_data_dir(appname, appauthor, roaming=True) - 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' - >>> user_config_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' - >>> user_cache_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' - >>> site_data_dir(appname, appauthor) - 'C:\\ProgramData\\Acme\\SuperApp' - >>> site_config_dir(appname, appauthor) - 'C:\\ProgramData\\Acme\\SuperApp' - >>> user_log_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' - >>> user_documents_dir() - 'C:\\Users\\trentm\\Documents' - >>> user_downloads_dir() - 'C:\\Users\\trentm\\Downloads' - >>> user_pictures_dir() - 'C:\\Users\\trentm\\Pictures' - >>> user_videos_dir() - 'C:\\Users\\trentm\\Videos' - >>> user_music_dir() - 'C:\\Users\\trentm\\Music' - >>> user_desktop_dir() - 'C:\\Users\\trentm\\Desktop' - >>> user_runtime_dir(appname, appauthor) - 'C:\\Users\\trentm\\AppData\\Local\\Temp\\Acme\\SuperApp' - -On Linux: - -.. code-block:: pycon - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/home/trentm/.local/share/SuperApp' - >>> user_config_dir(appname) - '/home/trentm/.config/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/home/trentm/.cache/SuperApp' - >>> site_data_dir(appname, appauthor) - '/usr/local/share/SuperApp' - >>> site_data_dir(appname, appauthor, multipath=True) - '/usr/local/share/SuperApp:/usr/share/SuperApp' - >>> site_config_dir(appname) - '/etc/xdg/SuperApp' - >>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc" - >>> site_config_dir(appname, multipath=True) - '/etc/SuperApp:/usr/local/etc/SuperApp' - >>> user_log_dir(appname, appauthor) - '/home/trentm/.local/state/SuperApp/log' - >>> user_documents_dir() - '/home/trentm/Documents' - >>> user_downloads_dir() - '/home/trentm/Downloads' - >>> user_pictures_dir() - '/home/trentm/Pictures' - >>> user_videos_dir() - '/home/trentm/Videos' - >>> user_music_dir() - '/home/trentm/Music' - >>> user_desktop_dir() - '/home/trentm/Desktop' - >>> user_runtime_dir(appname, appauthor) - '/run/user/{os.getuid()}/SuperApp' - -On Android:: - - >>> from platformdirs import * - >>> appname = "SuperApp" - >>> appauthor = "Acme" - >>> user_data_dir(appname, appauthor) - '/data/data/com.myApp/files/SuperApp' - >>> user_config_dir(appname) - '/data/data/com.myApp/shared_prefs/SuperApp' - >>> user_cache_dir(appname, appauthor) - '/data/data/com.myApp/cache/SuperApp' - >>> site_data_dir(appname, appauthor) - '/data/data/com.myApp/files/SuperApp' - >>> site_config_dir(appname) - '/data/data/com.myApp/shared_prefs/SuperApp' - >>> user_log_dir(appname, appauthor) - '/data/data/com.myApp/cache/SuperApp/log' - >>> user_documents_dir() - '/storage/emulated/0/Documents' - >>> user_downloads_dir() - '/storage/emulated/0/Downloads' - >>> user_pictures_dir() - '/storage/emulated/0/Pictures' - >>> user_videos_dir() - '/storage/emulated/0/DCIM/Camera' - >>> user_music_dir() - '/storage/emulated/0/Music' - >>> user_desktop_dir() - '/storage/emulated/0/Desktop' - >>> user_runtime_dir(appname, appauthor) - '/data/data/com.myApp/cache/SuperApp/tmp' - -Note: Some android apps like Termux and Pydroid are used as shells. These -apps are used by the end user to emulate Linux environment. Presence of -``SHELL`` environment variable is used by Platformdirs to differentiate -between general android apps and android apps used as shells. Shell android -apps also support ``XDG_*`` environment variables. - - -``PlatformDirs`` for convenience -================================ - -.. code-block:: pycon - - >>> from platformdirs import PlatformDirs - >>> dirs = PlatformDirs("SuperApp", "Acme") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp' - >>> dirs.user_config_dir - '/Users/trentm/Library/Application Support/SuperApp' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp' - >>> dirs.site_config_dir - '/Library/Application Support/SuperApp' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp' - >>> dirs.user_documents_dir - '/Users/trentm/Documents' - >>> dirs.user_downloads_dir - '/Users/trentm/Downloads' - >>> dirs.user_pictures_dir - '/Users/trentm/Pictures' - >>> dirs.user_videos_dir - '/Users/trentm/Movies' - >>> dirs.user_music_dir - '/Users/trentm/Music' - >>> dirs.user_desktop_dir - '/Users/trentm/Desktop' - >>> dirs.user_runtime_dir - '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' - -Per-version isolation -===================== - -If you have multiple versions of your app in use that you want to be -able to run side-by-side, then you may want version-isolation for these -dirs:: - - >>> from platformdirs import PlatformDirs - >>> dirs = PlatformDirs("SuperApp", "Acme", version="1.0") - >>> dirs.user_data_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - >>> dirs.user_config_dir - '/Users/trentm/Library/Application Support/SuperApp/1.0' - >>> dirs.user_cache_dir - '/Users/trentm/Library/Caches/SuperApp/1.0' - >>> dirs.site_data_dir - '/Library/Application Support/SuperApp/1.0' - >>> dirs.site_config_dir - '/Library/Application Support/SuperApp/1.0' - >>> dirs.user_log_dir - '/Users/trentm/Library/Logs/SuperApp/1.0' - >>> dirs.user_documents_dir - '/Users/trentm/Documents' - >>> dirs.user_downloads_dir - '/Users/trentm/Downloads' - >>> dirs.user_pictures_dir - '/Users/trentm/Pictures' - >>> dirs.user_videos_dir - '/Users/trentm/Movies' - >>> dirs.user_music_dir - '/Users/trentm/Music' - >>> dirs.user_desktop_dir - '/Users/trentm/Desktop' - >>> dirs.user_runtime_dir - '/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0' - -Be wary of using this for configuration files though; you'll need to handle -migrating configuration files manually. - -Why this Fork? -============== - -This repository is a friendly fork of the wonderful work started by -`ActiveState `_ who created -``appdirs``, this package's ancestor. - -Maintaining an open source project is no easy task, particularly -from within an organization, and the Python community is indebted -to ``appdirs`` (and to Trent Mick and Jeff Rouse in particular) for -creating an incredibly useful simple module, as evidenced by the wide -number of users it has attracted over the years. - -Nonetheless, given the number of long-standing open issues -and pull requests, and no clear path towards `ensuring -that maintenance of the package would continue or grow -`_, this fork was -created. - -Contributions are most welcome. diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD deleted file mode 100644 index 1ac011f..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/RECORD +++ /dev/null @@ -1,22 +0,0 @@ -platformdirs-4.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -platformdirs-4.5.0.dist-info/METADATA,sha256=mFxZl6Q-fO2nCdWWCJT4WOr4p7U12jZX4lk26MqGy1o,12804 -platformdirs-4.5.0.dist-info/RECORD,, -platformdirs-4.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 -platformdirs-4.5.0.dist-info/licenses/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089 -platformdirs/__init__.py,sha256=iORRy6_lZ9tXLvO0W6fJPn8QV7F532ivl-f2WGmabBc,22284 -platformdirs/__main__.py,sha256=HnsUQHpiBaiTxwcmwVw-nFaPdVNZtQIdi1eWDtI-MzI,1493 -platformdirs/__pycache__/__init__.cpython-310.pyc,, -platformdirs/__pycache__/__main__.cpython-310.pyc,, -platformdirs/__pycache__/android.cpython-310.pyc,, -platformdirs/__pycache__/api.cpython-310.pyc,, -platformdirs/__pycache__/macos.cpython-310.pyc,, -platformdirs/__pycache__/unix.cpython-310.pyc,, -platformdirs/__pycache__/version.cpython-310.pyc,, -platformdirs/__pycache__/windows.cpython-310.pyc,, -platformdirs/android.py,sha256=r0DshVBf-RO1jXJGX8C4Til7F1XWt-bkdWMgmvEiaYg,9013 -platformdirs/api.py,sha256=wPHOlwOsfz2oqQZ6A2FcCu5kEAj-JondzoNOHYFQ0h8,9281 -platformdirs/macos.py,sha256=0XoOgin1NK7Qki7iskD-oS8xKxw6bXgoKEgdqpCRAFQ,6322 -platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -platformdirs/unix.py,sha256=WZmkUA--L3JNRGmz32s35YfoD3ica6xKIPdCV_HhLcs,10458 -platformdirs/version.py,sha256=sved76l3nstESjZInsYGzPryR4cPIaf3QHTJuTDYXNM,704 -platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125 diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL deleted file mode 100644 index 12228d4..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.27.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE b/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE deleted file mode 100644 index f35fed9..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs-4.5.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2010-202x The platformdirs developers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.venv/lib/python3.10/site-packages/platformdirs/__init__.py b/.venv/lib/python3.10/site-packages/platformdirs/__init__.py deleted file mode 100644 index 02daa59..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs/__init__.py +++ /dev/null @@ -1,631 +0,0 @@ -""" -Utilities for determining application-specific dirs. - -See for details and usage. - -""" - -from __future__ import annotations - -import os -import sys -from typing import TYPE_CHECKING - -from .api import PlatformDirsABC -from .version import __version__ -from .version import __version_tuple__ as __version_info__ - -if TYPE_CHECKING: - from pathlib import Path - from typing import Literal - -if sys.platform == "win32": - from platformdirs.windows import Windows as _Result -elif sys.platform == "darwin": - from platformdirs.macos import MacOS as _Result -else: - from platformdirs.unix import Unix as _Result - - -def _set_platform_dir_class() -> type[PlatformDirsABC]: - if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": - if os.getenv("SHELL") or os.getenv("PREFIX"): - return _Result - - from platformdirs.android import _android_folder # noqa: PLC0415 - - if _android_folder() is not None: - from platformdirs.android import Android # noqa: PLC0415 - - return Android # return to avoid redefinition of a result - - return _Result - - -if TYPE_CHECKING: - # Work around mypy issue: https://github.com/python/mypy/issues/10962 - PlatformDirs = _Result -else: - PlatformDirs = _set_platform_dir_class() #: Currently active platform -AppDirs = PlatformDirs #: Backwards compatibility with appdirs - - -def user_data_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - roaming: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param roaming: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: data directory tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - roaming=roaming, - ensure_exists=ensure_exists, - ).user_data_dir - - -def site_data_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - multipath: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param multipath: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: data directory shared by users - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - multipath=multipath, - ensure_exists=ensure_exists, - ).site_data_dir - - -def user_config_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - roaming: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param roaming: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: config directory tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - roaming=roaming, - ensure_exists=ensure_exists, - ).user_config_dir - - -def site_config_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - multipath: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param multipath: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: config directory shared by the users - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - multipath=multipath, - ensure_exists=ensure_exists, - ).site_config_dir - - -def user_cache_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: cache directory tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).user_cache_dir - - -def site_cache_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `opinion `. - :param ensure_exists: See `ensure_exists `. - :returns: cache directory tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).site_cache_dir - - -def user_state_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - roaming: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param roaming: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: state directory tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - roaming=roaming, - ensure_exists=ensure_exists, - ).user_state_dir - - -def user_log_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: log directory tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).user_log_dir - - -def user_documents_dir() -> str: - """:returns: documents directory tied to the user""" - return PlatformDirs().user_documents_dir - - -def user_downloads_dir() -> str: - """:returns: downloads directory tied to the user""" - return PlatformDirs().user_downloads_dir - - -def user_pictures_dir() -> str: - """:returns: pictures directory tied to the user""" - return PlatformDirs().user_pictures_dir - - -def user_videos_dir() -> str: - """:returns: videos directory tied to the user""" - return PlatformDirs().user_videos_dir - - -def user_music_dir() -> str: - """:returns: music directory tied to the user""" - return PlatformDirs().user_music_dir - - -def user_desktop_dir() -> str: - """:returns: desktop directory tied to the user""" - return PlatformDirs().user_desktop_dir - - -def user_runtime_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `opinion `. - :param ensure_exists: See `ensure_exists `. - :returns: runtime directory tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).user_runtime_dir - - -def site_runtime_dir( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> str: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `opinion `. - :param ensure_exists: See `ensure_exists `. - :returns: runtime directory shared by users - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).site_runtime_dir - - -def user_data_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - roaming: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param roaming: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: data path tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - roaming=roaming, - ensure_exists=ensure_exists, - ).user_data_path - - -def site_data_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - multipath: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param multipath: See `multipath `. - :param ensure_exists: See `ensure_exists `. - :returns: data path shared by users - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - multipath=multipath, - ensure_exists=ensure_exists, - ).site_data_path - - -def user_config_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - roaming: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param roaming: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: config path tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - roaming=roaming, - ensure_exists=ensure_exists, - ).user_config_path - - -def site_config_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - multipath: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param multipath: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: config path shared by the users - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - multipath=multipath, - ensure_exists=ensure_exists, - ).site_config_path - - -def site_cache_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `opinion `. - :param ensure_exists: See `ensure_exists `. - :returns: cache directory tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).site_cache_path - - -def user_cache_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: cache path tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).user_cache_path - - -def user_state_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - roaming: bool = False, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param roaming: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: state path tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - roaming=roaming, - ensure_exists=ensure_exists, - ).user_state_path - - -def user_log_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `roaming `. - :param ensure_exists: See `ensure_exists `. - :returns: log path tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).user_log_path - - -def user_documents_path() -> Path: - """:returns: documents a path tied to the user""" - return PlatformDirs().user_documents_path - - -def user_downloads_path() -> Path: - """:returns: downloads path tied to the user""" - return PlatformDirs().user_downloads_path - - -def user_pictures_path() -> Path: - """:returns: pictures path tied to the user""" - return PlatformDirs().user_pictures_path - - -def user_videos_path() -> Path: - """:returns: videos path tied to the user""" - return PlatformDirs().user_videos_path - - -def user_music_path() -> Path: - """:returns: music path tied to the user""" - return PlatformDirs().user_music_path - - -def user_desktop_path() -> Path: - """:returns: desktop path tied to the user""" - return PlatformDirs().user_desktop_path - - -def user_runtime_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `opinion `. - :param ensure_exists: See `ensure_exists `. - :returns: runtime path tied to the user - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).user_runtime_path - - -def site_runtime_path( - appname: str | None = None, - appauthor: str | Literal[False] | None = None, - version: str | None = None, - opinion: bool = True, # noqa: FBT001, FBT002 - ensure_exists: bool = False, # noqa: FBT001, FBT002 -) -> Path: - """ - :param appname: See `appname `. - :param appauthor: See `appauthor `. - :param version: See `version `. - :param opinion: See `opinion `. - :param ensure_exists: See `ensure_exists `. - :returns: runtime path shared by users - """ - return PlatformDirs( - appname=appname, - appauthor=appauthor, - version=version, - opinion=opinion, - ensure_exists=ensure_exists, - ).site_runtime_path - - -__all__ = [ - "AppDirs", - "PlatformDirs", - "PlatformDirsABC", - "__version__", - "__version_info__", - "site_cache_dir", - "site_cache_path", - "site_config_dir", - "site_config_path", - "site_data_dir", - "site_data_path", - "site_runtime_dir", - "site_runtime_path", - "user_cache_dir", - "user_cache_path", - "user_config_dir", - "user_config_path", - "user_data_dir", - "user_data_path", - "user_desktop_dir", - "user_desktop_path", - "user_documents_dir", - "user_documents_path", - "user_downloads_dir", - "user_downloads_path", - "user_log_dir", - "user_log_path", - "user_music_dir", - "user_music_path", - "user_pictures_dir", - "user_pictures_path", - "user_runtime_dir", - "user_runtime_path", - "user_state_dir", - "user_state_path", - "user_videos_dir", - "user_videos_path", -] diff --git a/.venv/lib/python3.10/site-packages/platformdirs/__main__.py b/.venv/lib/python3.10/site-packages/platformdirs/__main__.py deleted file mode 100644 index 922c521..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs/__main__.py +++ /dev/null @@ -1,55 +0,0 @@ -"""Main entry point.""" - -from __future__ import annotations - -from platformdirs import PlatformDirs, __version__ - -PROPS = ( - "user_data_dir", - "user_config_dir", - "user_cache_dir", - "user_state_dir", - "user_log_dir", - "user_documents_dir", - "user_downloads_dir", - "user_pictures_dir", - "user_videos_dir", - "user_music_dir", - "user_runtime_dir", - "site_data_dir", - "site_config_dir", - "site_cache_dir", - "site_runtime_dir", -) - - -def main() -> None: - """Run the main entry point.""" - app_name = "MyApp" - app_author = "MyCompany" - - print(f"-- platformdirs {__version__} --") # noqa: T201 - - print("-- app dirs (with optional 'version')") # noqa: T201 - dirs = PlatformDirs(app_name, app_author, version="1.0") - for prop in PROPS: - print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 - - print("\n-- app dirs (without optional 'version')") # noqa: T201 - dirs = PlatformDirs(app_name, app_author) - for prop in PROPS: - print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 - - print("\n-- app dirs (without optional 'appauthor')") # noqa: T201 - dirs = PlatformDirs(app_name) - for prop in PROPS: - print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 - - print("\n-- app dirs (with disabled 'appauthor')") # noqa: T201 - dirs = PlatformDirs(app_name, appauthor=False) - for prop in PROPS: - print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 - - -if __name__ == "__main__": - main() diff --git a/.venv/lib/python3.10/site-packages/platformdirs/android.py b/.venv/lib/python3.10/site-packages/platformdirs/android.py deleted file mode 100644 index 92efc85..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs/android.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Android.""" - -from __future__ import annotations - -import os -import re -import sys -from functools import lru_cache -from typing import TYPE_CHECKING, cast - -from .api import PlatformDirsABC - - -class Android(PlatformDirsABC): - """ - Follows the guidance `from here `_. - - Makes use of the `appname `, `version - `, `ensure_exists `. - - """ - - @property - def user_data_dir(self) -> str: - """:return: data directory tied to the user, e.g. ``/data/user///files/``""" - return self._append_app_name_and_version(cast("str", _android_folder()), "files") - - @property - def site_data_dir(self) -> str: - """:return: data directory shared by users, same as `user_data_dir`""" - return self.user_data_dir - - @property - def user_config_dir(self) -> str: - """ - :return: config directory tied to the user, e.g. \ - ``/data/user///shared_prefs/`` - """ - return self._append_app_name_and_version(cast("str", _android_folder()), "shared_prefs") - - @property - def site_config_dir(self) -> str: - """:return: config directory shared by the users, same as `user_config_dir`""" - return self.user_config_dir - - @property - def user_cache_dir(self) -> str: - """:return: cache directory tied to the user, e.g.,``/data/user///cache/``""" - return self._append_app_name_and_version(cast("str", _android_folder()), "cache") - - @property - def site_cache_dir(self) -> str: - """:return: cache directory shared by users, same as `user_cache_dir`""" - return self.user_cache_dir - - @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, same as `user_cache_dir` if not opinionated else ``log`` in it, - e.g. ``/data/user///cache//log`` - """ - path = self.user_cache_dir - if self.opinion: - path = os.path.join(path, "log") # noqa: PTH118 - return path - - @property - def user_documents_dir(self) -> str: - """:return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``""" - return _android_documents_folder() - - @property - def user_downloads_dir(self) -> str: - """:return: downloads directory tied to the user e.g. ``/storage/emulated/0/Downloads``""" - return _android_downloads_folder() - - @property - def user_pictures_dir(self) -> str: - """:return: pictures directory tied to the user e.g. ``/storage/emulated/0/Pictures``""" - return _android_pictures_folder() - - @property - def user_videos_dir(self) -> str: - """:return: videos directory tied to the user e.g. ``/storage/emulated/0/DCIM/Camera``""" - return _android_videos_folder() - - @property - def user_music_dir(self) -> str: - """:return: music directory tied to the user e.g. ``/storage/emulated/0/Music``""" - return _android_music_folder() - - @property - def user_desktop_dir(self) -> str: - """:return: desktop directory tied to the user e.g. ``/storage/emulated/0/Desktop``""" - return "/storage/emulated/0/Desktop" - - @property - def user_runtime_dir(self) -> str: - """ - :return: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it, - e.g. ``/data/user///cache//tmp`` - """ - path = self.user_cache_dir - if self.opinion: - path = os.path.join(path, "tmp") # noqa: PTH118 - return path - - @property - def site_runtime_dir(self) -> str: - """:return: runtime directory shared by users, same as `user_runtime_dir`""" - return self.user_runtime_dir - - -@lru_cache(maxsize=1) -def _android_folder() -> str | None: # noqa: C901 - """:return: base folder for the Android OS or None if it cannot be found""" - result: str | None = None - # type checker isn't happy with our "import android", just don't do this when type checking see - # https://stackoverflow.com/a/61394121 - if not TYPE_CHECKING: - try: - # First try to get a path to android app using python4android (if available)... - from android import mActivity # noqa: PLC0415 - - context = cast("android.content.Context", mActivity.getApplicationContext()) # noqa: F821 - result = context.getFilesDir().getParentFile().getAbsolutePath() - except Exception: # noqa: BLE001 - result = None - if result is None: - try: - # ...and fall back to using plain pyjnius, if python4android isn't available or doesn't deliver any useful - # result... - from jnius import autoclass # noqa: PLC0415 - - context = autoclass("android.content.Context") - result = context.getFilesDir().getParentFile().getAbsolutePath() - except Exception: # noqa: BLE001 - result = None - if result is None: - # and if that fails, too, find an android folder looking at path on the sys.path - # warning: only works for apps installed under /data, not adopted storage etc. - pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files") - for path in sys.path: - if pattern.match(path): - result = path.split("/files")[0] - break - else: - result = None - if result is None: - # one last try: find an android folder looking at path on the sys.path taking adopted storage paths into - # account - pattern = re.compile(r"/mnt/expand/[a-fA-F0-9-]{36}/(data|user/\d+)/(.+)/files") - for path in sys.path: - if pattern.match(path): - result = path.split("/files")[0] - break - else: - result = None - return result - - -@lru_cache(maxsize=1) -def _android_documents_folder() -> str: - """:return: documents folder for the Android OS""" - # Get directories with pyjnius - try: - from jnius import autoclass # noqa: PLC0415 - - context = autoclass("android.content.Context") - environment = autoclass("android.os.Environment") - documents_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOCUMENTS).getAbsolutePath() - except Exception: # noqa: BLE001 - documents_dir = "/storage/emulated/0/Documents" - - return documents_dir - - -@lru_cache(maxsize=1) -def _android_downloads_folder() -> str: - """:return: downloads folder for the Android OS""" - # Get directories with pyjnius - try: - from jnius import autoclass # noqa: PLC0415 - - context = autoclass("android.content.Context") - environment = autoclass("android.os.Environment") - downloads_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOWNLOADS).getAbsolutePath() - except Exception: # noqa: BLE001 - downloads_dir = "/storage/emulated/0/Downloads" - - return downloads_dir - - -@lru_cache(maxsize=1) -def _android_pictures_folder() -> str: - """:return: pictures folder for the Android OS""" - # Get directories with pyjnius - try: - from jnius import autoclass # noqa: PLC0415 - - context = autoclass("android.content.Context") - environment = autoclass("android.os.Environment") - pictures_dir: str = context.getExternalFilesDir(environment.DIRECTORY_PICTURES).getAbsolutePath() - except Exception: # noqa: BLE001 - pictures_dir = "/storage/emulated/0/Pictures" - - return pictures_dir - - -@lru_cache(maxsize=1) -def _android_videos_folder() -> str: - """:return: videos folder for the Android OS""" - # Get directories with pyjnius - try: - from jnius import autoclass # noqa: PLC0415 - - context = autoclass("android.content.Context") - environment = autoclass("android.os.Environment") - videos_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DCIM).getAbsolutePath() - except Exception: # noqa: BLE001 - videos_dir = "/storage/emulated/0/DCIM/Camera" - - return videos_dir - - -@lru_cache(maxsize=1) -def _android_music_folder() -> str: - """:return: music folder for the Android OS""" - # Get directories with pyjnius - try: - from jnius import autoclass # noqa: PLC0415 - - context = autoclass("android.content.Context") - environment = autoclass("android.os.Environment") - music_dir: str = context.getExternalFilesDir(environment.DIRECTORY_MUSIC).getAbsolutePath() - except Exception: # noqa: BLE001 - music_dir = "/storage/emulated/0/Music" - - return music_dir - - -__all__ = [ - "Android", -] diff --git a/.venv/lib/python3.10/site-packages/platformdirs/api.py b/.venv/lib/python3.10/site-packages/platformdirs/api.py deleted file mode 100644 index 251600e..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs/api.py +++ /dev/null @@ -1,299 +0,0 @@ -"""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: - from collections.abc import Iterator - from typing import Literal - - -class PlatformDirsABC(ABC): # noqa: PLR0904 - """Abstract base class for platform directories.""" - - def __init__( # noqa: PLR0913, PLR0917 - self, - appname: str | None = None, - appauthor: str | Literal[False] | None = 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 ``.``. - - """ - 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 `_). - - """ - 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) - - def _first_item_as_path_if_multipath(self, directory: str) -> Path: - if self.multipath: - # If multipath is True, the first path is returned. - directory = directory.partition(os.pathsep)[0] - return Path(directory) - - @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 a 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) - - def iter_config_dirs(self) -> Iterator[str]: - """:yield: all user and site configuration directories.""" - yield self.user_config_dir - yield self.site_config_dir - - def iter_data_dirs(self) -> Iterator[str]: - """:yield: all user and site data directories.""" - yield self.user_data_dir - yield self.site_data_dir - - def iter_cache_dirs(self) -> Iterator[str]: - """:yield: all user and site cache directories.""" - yield self.user_cache_dir - yield self.site_cache_dir - - def iter_runtime_dirs(self) -> Iterator[str]: - """:yield: all user and site runtime directories.""" - yield self.user_runtime_dir - yield self.site_runtime_dir - - def iter_config_paths(self) -> Iterator[Path]: - """:yield: all user and site configuration paths.""" - for path in self.iter_config_dirs(): - yield Path(path) - - def iter_data_paths(self) -> Iterator[Path]: - """:yield: all user and site data paths.""" - for path in self.iter_data_dirs(): - yield Path(path) - - def iter_cache_paths(self) -> Iterator[Path]: - """:yield: all user and site cache paths.""" - for path in self.iter_cache_dirs(): - yield Path(path) - - def iter_runtime_paths(self) -> Iterator[Path]: - """:yield: all user and site runtime paths.""" - for path in self.iter_runtime_dirs(): - yield Path(path) diff --git a/.venv/lib/python3.10/site-packages/platformdirs/macos.py b/.venv/lib/python3.10/site-packages/platformdirs/macos.py deleted file mode 100644 index 30ab368..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs/macos.py +++ /dev/null @@ -1,146 +0,0 @@ -"""macOS.""" - -from __future__ import annotations - -import os.path -import sys -from typing import TYPE_CHECKING - -from .api import PlatformDirsABC - -if TYPE_CHECKING: - from pathlib import Path - - -class MacOS(PlatformDirsABC): - """ - Platform directories for the macOS operating system. - - Follows the guidance from - `Apple documentation `_. - Makes use of the `appname `, - `version `, - `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 `_, the directory - will be under the Homebrew prefix, e.g. ``$homebrew_prefix/share/$appname/$version``. - If `multipath ` is enabled, and we're in Homebrew, - the response is a multi-path string separated by ":", e.g. - ``$homebrew_prefix/share/$appname/$version:/Library/Application Support/$appname/$version`` - """ - is_homebrew = "/opt/python" in sys.prefix - homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else "" - path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/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 site_data_path(self) -> Path: - """:return: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" - return self._first_item_as_path_if_multipath(self.site_data_dir) - - @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 `_, the directory - will be under the Homebrew prefix, e.g. ``$homebrew_prefix/var/cache/$appname/$version``. - If `multipath ` is enabled, and we're in Homebrew, - the response is a multi-path string separated by ":", e.g. - ``$homebrew_prefix/var/cache/$appname/$version:/Library/Caches/$appname/$version`` - """ - is_homebrew = "/opt/python" in sys.prefix - homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else "" - path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/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 site_cache_path(self) -> Path: - """:return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" - return self._first_item_as_path_if_multipath(self.site_cache_dir) - - @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", -] diff --git a/.venv/lib/python3.10/site-packages/platformdirs/py.typed b/.venv/lib/python3.10/site-packages/platformdirs/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.10/site-packages/platformdirs/unix.py b/.venv/lib/python3.10/site-packages/platformdirs/unix.py deleted file mode 100644 index fc75d8d..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs/unix.py +++ /dev/null @@ -1,272 +0,0 @@ -"""Unix.""" - -from __future__ import annotations - -import os -import sys -from configparser import ConfigParser -from pathlib import Path -from typing import TYPE_CHECKING, NoReturn - -from .api import PlatformDirsABC - -if TYPE_CHECKING: - from collections.abc import Iterator - -if sys.platform == "win32": - - def getuid() -> NoReturn: - msg = "should only be used on Unix" - raise RuntimeError(msg) - -else: - from os import getuid - - -class Unix(PlatformDirsABC): # noqa: PLR0904 - """ - On Unix/Linux, we follow the `XDG Basedir Spec `_. - - The spec allows overriding directories with environment variables. The examples shown are the default values, - alongside the name of the environment variable that overrides them. Makes use of the `appname - `, `version `, `multipath - `, `opinion `, `ensure_exists - `. - - """ - - @property - def user_data_dir(self) -> str: - """ - :return: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or - ``$XDG_DATA_HOME/$appname/$version`` - """ - path = os.environ.get("XDG_DATA_HOME", "") - if not path.strip(): - path = os.path.expanduser("~/.local/share") # noqa: PTH111 - return self._append_app_name_and_version(path) - - @property - def _site_data_dirs(self) -> list[str]: - path = os.environ.get("XDG_DATA_DIRS", "") - if not path.strip(): - path = f"/usr/local/share{os.pathsep}/usr/share" - return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)] - - @property - def site_data_dir(self) -> str: - """ - :return: data directories shared by users (if `multipath ` is - enabled and ``XDG_DATA_DIRS`` is set and a multi path the response is also a multi path separated by the - OS path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version`` - """ - # XDG default for $XDG_DATA_DIRS; only first, if multipath is False - dirs = self._site_data_dirs - if not self.multipath: - return dirs[0] - return os.pathsep.join(dirs) - - @property - def user_config_dir(self) -> str: - """ - :return: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or - ``$XDG_CONFIG_HOME/$appname/$version`` - """ - path = os.environ.get("XDG_CONFIG_HOME", "") - if not path.strip(): - path = os.path.expanduser("~/.config") # noqa: PTH111 - return self._append_app_name_and_version(path) - - @property - def _site_config_dirs(self) -> list[str]: - path = os.environ.get("XDG_CONFIG_DIRS", "") - if not path.strip(): - path = "/etc/xdg" - return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)] - - @property - def site_config_dir(self) -> str: - """ - :return: config directories shared by users (if `multipath ` - is enabled and ``XDG_CONFIG_DIRS`` is set and a multi path the response is also a multi path separated by - the OS path separator), e.g. ``/etc/xdg/$appname/$version`` - """ - # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False - dirs = self._site_config_dirs - if not self.multipath: - return dirs[0] - return os.pathsep.join(dirs) - - @property - def user_cache_dir(self) -> str: - """ - :return: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or - ``~/$XDG_CACHE_HOME/$appname/$version`` - """ - path = os.environ.get("XDG_CACHE_HOME", "") - if not path.strip(): - path = os.path.expanduser("~/.cache") # noqa: PTH111 - return self._append_app_name_and_version(path) - - @property - def site_cache_dir(self) -> str: - """:return: cache directory shared by users, e.g. ``/var/cache/$appname/$version``""" - return self._append_app_name_and_version("/var/cache") - - @property - def user_state_dir(self) -> str: - """ - :return: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or - ``$XDG_STATE_HOME/$appname/$version`` - """ - path = os.environ.get("XDG_STATE_HOME", "") - if not path.strip(): - path = os.path.expanduser("~/.local/state") # noqa: PTH111 - return self._append_app_name_and_version(path) - - @property - def user_log_dir(self) -> str: - """:return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it""" - path = self.user_state_dir - if self.opinion: - path = os.path.join(path, "log") # noqa: PTH118 - self._optionally_create_directory(path) - return path - - @property - def user_documents_dir(self) -> str: - """:return: documents directory tied to the user, e.g. ``~/Documents``""" - return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents") - - @property - def user_downloads_dir(self) -> str: - """:return: downloads directory tied to the user, e.g. ``~/Downloads``""" - return _get_user_media_dir("XDG_DOWNLOAD_DIR", "~/Downloads") - - @property - def user_pictures_dir(self) -> str: - """:return: pictures directory tied to the user, e.g. ``~/Pictures``""" - return _get_user_media_dir("XDG_PICTURES_DIR", "~/Pictures") - - @property - def user_videos_dir(self) -> str: - """:return: videos directory tied to the user, e.g. ``~/Videos``""" - return _get_user_media_dir("XDG_VIDEOS_DIR", "~/Videos") - - @property - def user_music_dir(self) -> str: - """:return: music directory tied to the user, e.g. ``~/Music``""" - return _get_user_media_dir("XDG_MUSIC_DIR", "~/Music") - - @property - def user_desktop_dir(self) -> str: - """:return: desktop directory tied to the user, e.g. ``~/Desktop``""" - return _get_user_media_dir("XDG_DESKTOP_DIR", "~/Desktop") - - @property - def user_runtime_dir(self) -> str: - """ - :return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or - ``$XDG_RUNTIME_DIR/$appname/$version``. - - For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/user/$(id -u)/$appname/$version`` if - exists, otherwise ``/tmp/runtime-$(id -u)/$appname/$version``, if``$XDG_RUNTIME_DIR`` - is not set. - """ - path = os.environ.get("XDG_RUNTIME_DIR", "") - if not path.strip(): - if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): - path = f"/var/run/user/{getuid()}" - if not Path(path).exists(): - path = f"/tmp/runtime-{getuid()}" # noqa: S108 - else: - path = f"/run/user/{getuid()}" - return self._append_app_name_and_version(path) - - @property - def site_runtime_dir(self) -> str: - """ - :return: runtime directory shared by users, e.g. ``/run/$appname/$version`` or \ - ``$XDG_RUNTIME_DIR/$appname/$version``. - - Note that this behaves almost exactly like `user_runtime_dir` if ``$XDG_RUNTIME_DIR`` is set, but will - fall back to paths associated to the root user instead of a regular logged-in user if it's not set. - - If you wish to ensure that a logged-in root user path is returned e.g. ``/run/user/0``, use `user_runtime_dir` - instead. - - For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/$appname/$version`` if ``$XDG_RUNTIME_DIR`` is not set. - """ - path = os.environ.get("XDG_RUNTIME_DIR", "") - if not path.strip(): - if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): - path = "/var/run" - else: - path = "/run" - return self._append_app_name_and_version(path) - - @property - def site_data_path(self) -> Path: - """:return: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" - return self._first_item_as_path_if_multipath(self.site_data_dir) - - @property - def site_config_path(self) -> Path: - """:return: config path shared by the users, returns the first item, even if ``multipath`` is set to ``True``""" - return self._first_item_as_path_if_multipath(self.site_config_dir) - - @property - def site_cache_path(self) -> Path: - """:return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" - return self._first_item_as_path_if_multipath(self.site_cache_dir) - - def iter_config_dirs(self) -> Iterator[str]: - """:yield: all user and site configuration directories.""" - yield self.user_config_dir - yield from self._site_config_dirs - - def iter_data_dirs(self) -> Iterator[str]: - """:yield: all user and site data directories.""" - yield self.user_data_dir - yield from self._site_data_dirs - - -def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str: - media_dir = _get_user_dirs_folder(env_var) - if media_dir is None: - media_dir = os.environ.get(env_var, "").strip() - if not media_dir: - media_dir = os.path.expanduser(fallback_tilde_path) # noqa: PTH111 - - return media_dir - - -def _get_user_dirs_folder(key: str) -> str | None: - """ - Return directory from user-dirs.dirs config file. - - See https://freedesktop.org/wiki/Software/xdg-user-dirs/. - - """ - user_dirs_config_path = Path(Unix().user_config_dir) / "user-dirs.dirs" - if user_dirs_config_path.exists(): - parser = ConfigParser() - - with user_dirs_config_path.open() as stream: - # Add fake section header, so ConfigParser doesn't complain - parser.read_string(f"[top]\n{stream.read()}") - - if key not in parser["top"]: - return None - - path = parser["top"][key].strip('"') - # Handle relative home paths - return path.replace("$HOME", os.path.expanduser("~")) # noqa: PTH111 - - return None - - -__all__ = [ - "Unix", -] diff --git a/.venv/lib/python3.10/site-packages/platformdirs/version.py b/.venv/lib/python3.10/site-packages/platformdirs/version.py deleted file mode 100644 index 3575282..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs/version.py +++ /dev/null @@ -1,34 +0,0 @@ -# file generated by setuptools-scm -# don't change, don't track in version control - -__all__ = [ - "__version__", - "__version_tuple__", - "version", - "version_tuple", - "__commit_id__", - "commit_id", -] - -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import Tuple - from typing import Union - - VERSION_TUPLE = Tuple[Union[int, str], ...] - COMMIT_ID = Union[str, None] -else: - VERSION_TUPLE = object - COMMIT_ID = object - -version: str -__version__: str -__version_tuple__: VERSION_TUPLE -version_tuple: VERSION_TUPLE -commit_id: COMMIT_ID -__commit_id__: COMMIT_ID - -__version__ = version = '4.5.0' -__version_tuple__ = version_tuple = (4, 5, 0) - -__commit_id__ = commit_id = None diff --git a/.venv/lib/python3.10/site-packages/platformdirs/windows.py b/.venv/lib/python3.10/site-packages/platformdirs/windows.py deleted file mode 100644 index d7bc960..0000000 --- a/.venv/lib/python3.10/site-packages/platformdirs/windows.py +++ /dev/null @@ -1,272 +0,0 @@ -"""Windows.""" - -from __future__ import annotations - -import os -import sys -from functools import lru_cache -from typing import TYPE_CHECKING - -from .api import PlatformDirsABC - -if TYPE_CHECKING: - from collections.abc import Callable - - -class Windows(PlatformDirsABC): - """ - `MSDN on where to store app data files `_. - - Makes use of the `appname `, `appauthor - `, `version `, `roaming - `, `opinion `, `ensure_exists - `. - - """ - - @property - def user_data_dir(self) -> str: - """ - :return: data directory tied to the user, e.g. - ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname`` (not roaming) or - ``%USERPROFILE%\\AppData\\Roaming\\$appauthor\\$appname`` (roaming) - """ - const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA" - path = os.path.normpath(get_win_folder(const)) - return self._append_parts(path) - - def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str: - params = [] - if self.appname: - if self.appauthor is not False: - author = self.appauthor or self.appname - params.append(author) - params.append(self.appname) - if opinion_value is not None and self.opinion: - params.append(opinion_value) - if self.version: - params.append(self.version) - path = os.path.join(path, *params) # noqa: PTH118 - self._optionally_create_directory(path) - return path - - @property - def site_data_dir(self) -> str: - """:return: data directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname``""" - path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA")) - return self._append_parts(path) - - @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 (if opinionated with ``Cache`` folder within ``$appname``) e.g. - ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname\\Cache\\$version`` - """ - path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA")) - return self._append_parts(path, opinion_value="Cache") - - @property - def site_cache_dir(self) -> str: - """:return: cache directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname\\Cache\\$version``""" - path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA")) - return self._append_parts(path, opinion_value="Cache") - - @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, same as `user_data_dir` if not opinionated else ``Logs`` in it""" - path = self.user_data_dir - if self.opinion: - path = os.path.join(path, "Logs") # noqa: PTH118 - self._optionally_create_directory(path) - return path - - @property - def user_documents_dir(self) -> str: - """:return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents``""" - return os.path.normpath(get_win_folder("CSIDL_PERSONAL")) - - @property - def user_downloads_dir(self) -> str: - """:return: downloads directory tied to the user e.g. ``%USERPROFILE%\\Downloads``""" - return os.path.normpath(get_win_folder("CSIDL_DOWNLOADS")) - - @property - def user_pictures_dir(self) -> str: - """:return: pictures directory tied to the user e.g. ``%USERPROFILE%\\Pictures``""" - return os.path.normpath(get_win_folder("CSIDL_MYPICTURES")) - - @property - def user_videos_dir(self) -> str: - """:return: videos directory tied to the user e.g. ``%USERPROFILE%\\Videos``""" - return os.path.normpath(get_win_folder("CSIDL_MYVIDEO")) - - @property - def user_music_dir(self) -> str: - """:return: music directory tied to the user e.g. ``%USERPROFILE%\\Music``""" - return os.path.normpath(get_win_folder("CSIDL_MYMUSIC")) - - @property - def user_desktop_dir(self) -> str: - """:return: desktop directory tied to the user, e.g. ``%USERPROFILE%\\Desktop``""" - return os.path.normpath(get_win_folder("CSIDL_DESKTOPDIRECTORY")) - - @property - def user_runtime_dir(self) -> str: - """ - :return: runtime directory tied to the user, e.g. - ``%USERPROFILE%\\AppData\\Local\\Temp\\$appauthor\\$appname`` - """ - path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp")) # noqa: PTH118 - return self._append_parts(path) - - @property - def site_runtime_dir(self) -> str: - """:return: runtime directory shared by users, same as `user_runtime_dir`""" - return self.user_runtime_dir - - -def get_win_folder_from_env_vars(csidl_name: str) -> str: - """Get folder from environment variables.""" - result = get_win_folder_if_csidl_name_not_env_var(csidl_name) - if result is not None: - return result - - env_var_name = { - "CSIDL_APPDATA": "APPDATA", - "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE", - "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA", - }.get(csidl_name) - if env_var_name is None: - msg = f"Unknown CSIDL name: {csidl_name}" - raise ValueError(msg) - result = os.environ.get(env_var_name) - if result is None: - msg = f"Unset environment variable: {env_var_name}" - raise ValueError(msg) - return result - - -def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None: - """Get a folder for a CSIDL name that does not exist as an environment variable.""" - if csidl_name == "CSIDL_PERSONAL": - return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents") # noqa: PTH118 - - if csidl_name == "CSIDL_DOWNLOADS": - return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Downloads") # noqa: PTH118 - - if csidl_name == "CSIDL_MYPICTURES": - return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Pictures") # noqa: PTH118 - - if csidl_name == "CSIDL_MYVIDEO": - return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Videos") # noqa: PTH118 - - if csidl_name == "CSIDL_MYMUSIC": - return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Music") # noqa: PTH118 - return None - - -def get_win_folder_from_registry(csidl_name: str) -> str: - """ - Get folder from the registry. - - This is a fallback technique at best. I'm not sure if using the registry for these guarantees us the correct answer - for all CSIDL_* names. - - """ - shell_folder_name = { - "CSIDL_APPDATA": "AppData", - "CSIDL_COMMON_APPDATA": "Common AppData", - "CSIDL_LOCAL_APPDATA": "Local AppData", - "CSIDL_PERSONAL": "Personal", - "CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}", - "CSIDL_MYPICTURES": "My Pictures", - "CSIDL_MYVIDEO": "My Video", - "CSIDL_MYMUSIC": "My Music", - }.get(csidl_name) - if shell_folder_name is None: - msg = f"Unknown CSIDL name: {csidl_name}" - raise ValueError(msg) - if sys.platform != "win32": # only needed for mypy type checker to know that this code runs only on Windows - raise NotImplementedError - import winreg # noqa: PLC0415 - - key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") - directory, _ = winreg.QueryValueEx(key, shell_folder_name) - return str(directory) - - -def get_win_folder_via_ctypes(csidl_name: str) -> str: - """Get folder with ctypes.""" - # There is no 'CSIDL_DOWNLOADS'. - # Use 'CSIDL_PROFILE' (40) and append the default folder 'Downloads' instead. - # https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid - - import ctypes # noqa: PLC0415 - - csidl_const = { - "CSIDL_APPDATA": 26, - "CSIDL_COMMON_APPDATA": 35, - "CSIDL_LOCAL_APPDATA": 28, - "CSIDL_PERSONAL": 5, - "CSIDL_MYPICTURES": 39, - "CSIDL_MYVIDEO": 14, - "CSIDL_MYMUSIC": 13, - "CSIDL_DOWNLOADS": 40, - "CSIDL_DESKTOPDIRECTORY": 16, - }.get(csidl_name) - if csidl_const is None: - msg = f"Unknown CSIDL name: {csidl_name}" - raise ValueError(msg) - - buf = ctypes.create_unicode_buffer(1024) - windll = getattr(ctypes, "windll") # noqa: B009 # using getattr to avoid false positive with mypy type checker - windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) - - # Downgrade to short path name if it has high-bit chars. - if any(ord(c) > 255 for c in buf): # noqa: PLR2004 - buf2 = ctypes.create_unicode_buffer(1024) - if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): - buf = buf2 - - if csidl_name == "CSIDL_DOWNLOADS": - return os.path.join(buf.value, "Downloads") # noqa: PTH118 - - return buf.value - - -def _pick_get_win_folder() -> Callable[[str], str]: - try: - import ctypes # noqa: PLC0415 - except ImportError: - pass - else: - if hasattr(ctypes, "windll"): - return get_win_folder_via_ctypes - try: - import winreg # noqa: PLC0415, F401 - except ImportError: - return get_win_folder_from_env_vars - else: - return get_win_folder_from_registry - - -get_win_folder = lru_cache(maxsize=None)(_pick_get_win_folder()) - -__all__ = [ - "Windows", -] diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA deleted file mode 100644 index 67dbe84..0000000 --- a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/METADATA +++ /dev/null @@ -1,277 +0,0 @@ -Metadata-Version: 2.4 -Name: pylint -Version: 4.0.4 -Summary: python code static checker -Author-email: Python Code Quality Authority -License-Expression: GPL-2.0-or-later -Project-URL: Bug Tracker, https://github.com/pylint-dev/pylint/issues -Project-URL: Discord Server, https://discord.com/invite/Egy6P8AMB5 -Project-URL: Docs: Contributor Guide, https://pylint.readthedocs.io/en/latest/development_guide/contributor_guide/index.html -Project-URL: Docs: User Guide, https://pylint.readthedocs.io/en/latest/ -Project-URL: homepage, https://github.com/pylint-dev/pylint -Project-URL: Source Code, https://github.com/pylint-dev/pylint -Project-URL: What's New, https://pylint.readthedocs.io/en/latest/whatsnew/3/ -Keywords: lint,linter,python,static code analysis -Classifier: Development Status :: 6 - Mature -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: 3.14 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Debuggers -Classifier: Topic :: Software Development :: Quality Assurance -Classifier: Topic :: Software Development :: Testing -Classifier: Typing :: Typed -Requires-Python: >=3.10.0 -Description-Content-Type: text/x-rst -License-File: LICENSE -License-File: CONTRIBUTORS.txt -Requires-Dist: astroid<=4.1.dev0,>=4.0.2 -Requires-Dist: colorama>=0.4.5; sys_platform == "win32" -Requires-Dist: dill>=0.2; python_version < "3.11" -Requires-Dist: dill>=0.3.6; python_version >= "3.11" -Requires-Dist: dill>=0.3.7; python_version >= "3.12" -Requires-Dist: isort!=5.13,<8,>=5 -Requires-Dist: mccabe<0.8,>=0.6 -Requires-Dist: platformdirs>=2.2 -Requires-Dist: tomli>=1.1; python_version < "3.11" -Requires-Dist: tomlkit>=0.10.1 -Requires-Dist: typing-extensions>=3.10; python_version < "3.10" -Provides-Extra: spelling -Requires-Dist: pyenchant~=3.2; extra == "spelling" -Provides-Extra: testutils -Requires-Dist: gitpython>3; extra == "testutils" -Dynamic: license-file - -`Pylint`_ -========= - -.. _`Pylint`: https://pylint.readthedocs.io/ - -.. This is used inside the doc to recover the start of the introduction - -.. image:: https://github.com/pylint-dev/pylint/actions/workflows/tests.yaml/badge.svg?branch=main - :target: https://github.com/pylint-dev/pylint/actions - -.. image:: https://codecov.io/gh/pylint-dev/pylint/branch/main/graph/badge.svg?token=ZETEzayrfk - :target: https://codecov.io/gh/pylint-dev/pylint - -.. image:: https://img.shields.io/pypi/v/pylint.svg - :alt: PyPI Package version - :target: https://pypi.python.org/pypi/pylint - -.. image:: https://readthedocs.org/projects/pylint/badge/?version=latest - :target: https://pylint.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status - -.. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/ambv/black - -.. image:: https://img.shields.io/badge/linting-pylint-yellowgreen - :target: https://github.com/pylint-dev/pylint - -.. image:: https://results.pre-commit.ci/badge/github/pylint-dev/pylint/main.svg - :target: https://results.pre-commit.ci/latest/github/pylint-dev/pylint/main - :alt: pre-commit.ci status - -.. image:: https://bestpractices.coreinfrastructure.org/projects/6328/badge - :target: https://bestpractices.coreinfrastructure.org/projects/6328 - :alt: CII Best Practices - -.. image:: https://img.shields.io/ossf-scorecard/github.com/PyCQA/pylint?label=openssf%20scorecard&style=flat - :target: https://api.securityscorecards.dev/projects/github.com/PyCQA/pylint - :alt: OpenSSF Scorecard - -.. image:: https://img.shields.io/discord/825463413634891776.svg - :target: https://discord.gg/qYxpadCgkx - :alt: Discord - -What is Pylint? ---------------- - -Pylint is a `static code analyser`_ for Python 2 or 3. The latest version supports Python -3.10.0 and above. - -.. _`static code analyser`: https://en.wikipedia.org/wiki/Static_code_analysis - -Pylint analyses your code without actually running it. It checks for errors, enforces a -coding standard, looks for `code smells`_, and can make suggestions about how the code -could be refactored. - -.. _`code smells`: https://martinfowler.com/bliki/CodeSmell.html - -Install -------- - -.. This is used inside the doc to recover the start of the short text for installation - -For command line use, pylint is installed with:: - - pip install pylint - -Or if you want to also check spelling with ``enchant`` (you might need to -`install the enchant C library `_): - -.. code-block:: sh - - pip install pylint[spelling] - -It can also be integrated in most editors or IDEs. More information can be found -`in the documentation`_. - -.. _in the documentation: https://pylint.readthedocs.io/en/latest/user_guide/installation/index.html - -.. This is used inside the doc to recover the end of the short text for installation - -What differentiates Pylint? ---------------------------- - -Pylint is not trusting your typing and is inferring the actual values of nodes (for a -start because there was no typing when pylint started off) using its internal code -representation (astroid). If your code is ``import logging as argparse``, Pylint -can check and know that ``argparse.error(...)`` is in fact a logging call and not an -argparse call. This makes pylint slower, but it also lets pylint find more issues if -your code is not fully typed. - - [inference] is the killer feature that keeps us using [pylint] in our project despite how painfully slow it is. - - `Realist pylint user`_, 2022 - -.. _`Realist pylint user`: https://github.com/charliermarsh/ruff/issues/970#issuecomment-1381067064 - -pylint, not afraid of being a little slower than it already is, is also a lot more thorough than other linters. -There are more checks, including some opinionated ones that are deactivated by default -but can be enabled using configuration. - -How to use pylint ------------------ - -Pylint isn't smarter than you: it may warn you about things that you have -conscientiously done or check for some things that you don't care about. -During adoption, especially in a legacy project where pylint was never enforced, -it's best to start with the ``--errors-only`` flag, then disable -convention and refactor messages with ``--disable=C,R`` and progressively -re-evaluate and re-enable messages as your priorities evolve. - -Pylint is highly configurable and permits to write plugins in order to add your -own checks (for example, for internal libraries or an internal rule). Pylint also has an -ecosystem of existing plugins for popular frameworks and third-party libraries. - -.. note:: - - Pylint supports the Python standard library out of the box. Third-party - libraries are not always supported, so a plugin might be needed. A good place - to start is ``PyPI`` which often returns a plugin by searching for - ``pylint ``. `pylint-pydantic`_, `pylint-django`_ and - `pylint-sonarjson`_ are examples of such plugins. More information about plugins - and how to load them can be found at `plugins`_. - -.. _`plugins`: https://pylint.readthedocs.io/en/latest/development_guide/how_tos/plugins.html#plugins -.. _`pylint-pydantic`: https://pypi.org/project/pylint-pydantic -.. _`pylint-django`: https://github.com/pylint-dev/pylint-django -.. _`pylint-sonarjson`: https://github.com/cnescatlab/pylint-sonarjson-catlab - -Advised linters alongside pylint --------------------------------- - -Projects that you might want to use alongside pylint include ruff_ (**really** fast, -with builtin auto-fix and a large number of checks taken from popular linters, but -implemented in ``rust``) or flake8_ (a framework to implement your own checks in python using ``ast`` directly), -mypy_, pyright_ / pylance or pyre_ (typing checks), bandit_ (security oriented checks), black_ and -isort_ (auto-formatting), autoflake_ (automated removal of unused imports or variables), pyupgrade_ -(automated upgrade to newer python syntax) and pydocstringformatter_ (automated pep257). - -.. _ruff: https://github.com/astral-sh/ruff -.. _flake8: https://github.com/PyCQA/flake8 -.. _bandit: https://github.com/PyCQA/bandit -.. _mypy: https://github.com/python/mypy -.. _pyright: https://github.com/microsoft/pyright -.. _pyre: https://github.com/facebook/pyre-check -.. _black: https://github.com/psf/black -.. _autoflake: https://github.com/myint/autoflake -.. _pyupgrade: https://github.com/asottile/pyupgrade -.. _pydocstringformatter: https://github.com/DanielNoord/pydocstringformatter -.. _isort: https://pycqa.github.io/isort/ - -Additional tools included in pylint ------------------------------------ - -Pylint ships with two additional tools: - -- pyreverse_ (standalone tool that generates package and class diagrams.) -- symilar_ (duplicate code finder that is also integrated in pylint) - -.. _pyreverse: https://pylint.readthedocs.io/en/latest/additional_tools/pyreverse/index.html -.. _symilar: https://pylint.readthedocs.io/en/latest/additional_tools/symilar/index.html - - -.. This is used inside the doc to recover the end of the introduction - -Contributing ------------- - -.. This is used inside the doc to recover the start of the short text for contribution - -We welcome all forms of contributions such as updates for documentation, new code, checking issues for duplicates or telling us -that we can close them, confirming that issues still exist, `creating issues because -you found a bug or want a feature`_, etc. Everything is much appreciated! - -Please follow the `code of conduct`_ and check `the Contributor Guides`_ if you want to -make a code contribution. - -.. _creating issues because you found a bug or want a feature: https://pylint.readthedocs.io/en/latest/contact.html#bug-reports-feedback -.. _code of conduct: https://github.com/pylint-dev/pylint/blob/main/CODE_OF_CONDUCT.md -.. _the Contributor Guides: https://pylint.readthedocs.io/en/latest/development_guide/contribute.html - -.. This is used inside the doc to recover the end of the short text for contribution - -Show your usage ------------------ - -You can place this badge in your README to let others know your project uses pylint. - - .. image:: https://img.shields.io/badge/linting-pylint-yellowgreen - :target: https://github.com/pylint-dev/pylint - -Learn how to add a badge to your documentation in `the badge documentation`_. - -.. _the badge documentation: https://pylint.readthedocs.io/en/latest/user_guide/installation/badge.html - -License -------- - -pylint is, with a few exceptions listed below, `GPLv2 `_. - -The icon files are licensed under the `CC BY-SA 4.0 `_ license: - -- `doc/logo.png `_ -- `doc/logo.svg `_ - -Support -------- - -Please check `the contact information`_. - -.. _`the contact information`: https://pylint.readthedocs.io/en/latest/contact.html - -.. |tideliftlogo| image:: https://raw.githubusercontent.com/pylint-dev/pylint/main/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png - :width: 200 - :alt: Tidelift - -.. list-table:: - :widths: 10 100 - - * - |tideliftlogo| - - Professional support for pylint is available as part of the `Tidelift - Subscription`_. Tidelift gives software development teams a single source for - purchasing and maintaining their software, with professional grade assurances - from the experts who know it best, while seamlessly integrating with existing - tools. - -.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-pylint?utm_source=pypi-pylint&utm_medium=referral&utm_campaign=readme diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD deleted file mode 100644 index 83717f3..0000000 --- a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/RECORD +++ /dev/null @@ -1,371 +0,0 @@ -../../../bin/pylint,sha256=_JZ6ZVXY51NODFkOdzhjJ4mgGifYosPGa69byGZkSds,234 -../../../bin/pylint-config,sha256=Gkqx9qzro9VIkfwUp4Z-h2EipEOJZqWJ1LBnJnLtk-g,250 -../../../bin/pyreverse,sha256=p-UM5HRhrmYfd56gc8UcsCl72nuBWj0LUKFEjtO-QG4,240 -../../../bin/symilar,sha256=ZOjPYBZodF7yQWkzZRYmpxwPJgYTmFiGeyZYbaLI9vw,236 -pylint-4.0.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pylint-4.0.4.dist-info/METADATA,sha256=nerPzgR0iVcum1LsxxFZb-DSe6j5yk--SmEq4XnSu3U,12151 -pylint-4.0.4.dist-info/RECORD,, -pylint-4.0.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pylint-4.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 -pylint-4.0.4.dist-info/entry_points.txt,sha256=0wBRzcsPs1QNE1gWMZ5yepPnOat1elSm-EwS_0ngOY4,149 -pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt,sha256=g_s20VXmEOY3GA1b8QsBD7s-CCQaLoVaSIluiV7gb6g,33198 -pylint-4.0.4.dist-info/licenses/LICENSE,sha256=-XsUCA3ouEkNYOs9Yg68QZlD4HeUZtHdDV1vaP4ZXc0,17984 -pylint-4.0.4.dist-info/top_level.txt,sha256=j6Z9i__pIuaiCka6Ul9YIy6yI5aw5QbCtldLvZlMksE,7 -pylint/__init__.py,sha256=rKFMFLSkAqRInnRBeVYb4_HJ5adpcStTPW_zI7GuVaY,3821 -pylint/__main__.py,sha256=dd_IH1XzarvFnYJeY2QqhYpNAcPSHX6nbA4p8JJdwwo,315 -pylint/__pkginfo__.py,sha256=3FSdantqN7TyAIn8i8EyVvBFLZjGsipXTEPRf-x5NJg,1359 -pylint/__pycache__/__init__.cpython-310.pyc,, -pylint/__pycache__/__main__.cpython-310.pyc,, -pylint/__pycache__/__pkginfo__.cpython-310.pyc,, -pylint/__pycache__/constants.cpython-310.pyc,, -pylint/__pycache__/exceptions.cpython-310.pyc,, -pylint/__pycache__/graph.cpython-310.pyc,, -pylint/__pycache__/interfaces.cpython-310.pyc,, -pylint/__pycache__/typing.cpython-310.pyc,, -pylint/checkers/__init__.py,sha256=2Seg87ewDYfL3WQGVo0aPrlMrAlhsafvHWdqKvCk6bw,4235 -pylint/checkers/__pycache__/__init__.cpython-310.pyc,, -pylint/checkers/__pycache__/async_checker.cpython-310.pyc,, -pylint/checkers/__pycache__/bad_chained_comparison.cpython-310.pyc,, -pylint/checkers/__pycache__/base_checker.cpython-310.pyc,, -pylint/checkers/__pycache__/clear_lru_cache.cpython-310.pyc,, -pylint/checkers/__pycache__/dataclass_checker.cpython-310.pyc,, -pylint/checkers/__pycache__/deprecated.cpython-310.pyc,, -pylint/checkers/__pycache__/design_analysis.cpython-310.pyc,, -pylint/checkers/__pycache__/dunder_methods.cpython-310.pyc,, -pylint/checkers/__pycache__/ellipsis_checker.cpython-310.pyc,, -pylint/checkers/__pycache__/exceptions.cpython-310.pyc,, -pylint/checkers/__pycache__/format.cpython-310.pyc,, -pylint/checkers/__pycache__/imports.cpython-310.pyc,, -pylint/checkers/__pycache__/lambda_expressions.cpython-310.pyc,, -pylint/checkers/__pycache__/logging.cpython-310.pyc,, -pylint/checkers/__pycache__/match_statements_checker.cpython-310.pyc,, -pylint/checkers/__pycache__/method_args.cpython-310.pyc,, -pylint/checkers/__pycache__/misc.cpython-310.pyc,, -pylint/checkers/__pycache__/modified_iterating_checker.cpython-310.pyc,, -pylint/checkers/__pycache__/nested_min_max.cpython-310.pyc,, -pylint/checkers/__pycache__/newstyle.cpython-310.pyc,, -pylint/checkers/__pycache__/non_ascii_names.cpython-310.pyc,, -pylint/checkers/__pycache__/raw_metrics.cpython-310.pyc,, -pylint/checkers/__pycache__/spelling.cpython-310.pyc,, -pylint/checkers/__pycache__/stdlib.cpython-310.pyc,, -pylint/checkers/__pycache__/strings.cpython-310.pyc,, -pylint/checkers/__pycache__/symilar.cpython-310.pyc,, -pylint/checkers/__pycache__/threading_checker.cpython-310.pyc,, -pylint/checkers/__pycache__/typecheck.cpython-310.pyc,, -pylint/checkers/__pycache__/unicode.cpython-310.pyc,, -pylint/checkers/__pycache__/unsupported_version.cpython-310.pyc,, -pylint/checkers/__pycache__/utils.cpython-310.pyc,, -pylint/checkers/__pycache__/variables.cpython-310.pyc,, -pylint/checkers/async_checker.py,sha256=GAcE3XJTkRmjIWrXTL_7-0ZBhYUHqZfMiAmJCUpoB0I,4026 -pylint/checkers/bad_chained_comparison.py,sha256=3kximBOnJoj41mP0fOrZr-OVNI2-ki-e7fBcfTDnrOg,2238 -pylint/checkers/base/__init__.py,sha256=Tz0g36wveH6w6Iu3JZN2OZI8yHQCBF1eJLw8nIn8IlE,1697 -pylint/checkers/base/__pycache__/__init__.cpython-310.pyc,, -pylint/checkers/base/__pycache__/basic_checker.cpython-310.pyc,, -pylint/checkers/base/__pycache__/basic_error_checker.cpython-310.pyc,, -pylint/checkers/base/__pycache__/comparison_checker.cpython-310.pyc,, -pylint/checkers/base/__pycache__/docstring_checker.cpython-310.pyc,, -pylint/checkers/base/__pycache__/function_checker.cpython-310.pyc,, -pylint/checkers/base/__pycache__/pass_checker.cpython-310.pyc,, -pylint/checkers/base/basic_checker.py,sha256=h0s4owQ4iO8vL4qh6R4d22qMxcqOWeESPmnxulfjnvY,40129 -pylint/checkers/base/basic_error_checker.py,sha256=dLA2PMdnsLKmCA02FeZDVtNnYkzXZ_IkUpx4HEOnefo,24227 -pylint/checkers/base/comparison_checker.py,sha256=NoBRs7SoJk11IxBzaeqBNGIAsAY9U2M0wBmowCYpkhU,13876 -pylint/checkers/base/docstring_checker.py,sha256=FTOAtEfQkSsHXGq1tZPvXfwmHdACaaakms4f_D2JG-g,7647 -pylint/checkers/base/function_checker.py,sha256=tJZq0G7PtAMO5JXjwJCEHCUUjAEpqPtGLYg_W3v3eAU,5866 -pylint/checkers/base/name_checker/__init__.py,sha256=VK-zwZj9rKxprzTnoXQGeVDW3Yutnc5UL-6rHvvGmuo,699 -pylint/checkers/base/name_checker/__pycache__/__init__.cpython-310.pyc,, -pylint/checkers/base/name_checker/__pycache__/checker.cpython-310.pyc,, -pylint/checkers/base/name_checker/__pycache__/naming_style.cpython-310.pyc,, -pylint/checkers/base/name_checker/checker.py,sha256=zFzpl6kOr6il60RTJluL-tViBwxfSCNNqIjIxtoXzkw,31895 -pylint/checkers/base/name_checker/naming_style.py,sha256=U1Fgx9i3eskySSKHPeE3Hm2WAqfAGELG_ZwOF7qLHow,5911 -pylint/checkers/base/pass_checker.py,sha256=IrTskPQkMYvAQKSoixfY_zKrlDyJMMBh-cC1Wa4E608,1041 -pylint/checkers/base_checker.py,sha256=TaNiXaXDFkiSJ27sQ4OC1amvX26THAFb6XttCuZ1ilE,9221 -pylint/checkers/classes/__init__.py,sha256=V2WMlI77SPVkD1KaSBAaRTx2PgC51rYKOzse8VOw2-8,654 -pylint/checkers/classes/__pycache__/__init__.cpython-310.pyc,, -pylint/checkers/classes/__pycache__/class_checker.cpython-310.pyc,, -pylint/checkers/classes/__pycache__/special_methods_checker.cpython-310.pyc,, -pylint/checkers/classes/class_checker.py,sha256=GZpF_ipUiHR9BXe1WVhE4nxXTTJ5AsdA8QDpP1zsb6k,93474 -pylint/checkers/classes/special_methods_checker.py,sha256=VIxi68yQQ_z42uTdFUOFrYX6k8lAW5ym_bymKfcb3XU,15178 -pylint/checkers/clear_lru_cache.py,sha256=wKfHSibt2rKL8p7ITUsCy7ClVzxHD1ru8AkwtYUE7P0,1071 -pylint/checkers/dataclass_checker.py,sha256=3i_4NfSFXpQJRrJcplzY2Khu82ywETEHrwywyws0pRo,4502 -pylint/checkers/deprecated.py,sha256=UDvNQkc8Xy4y56HhF4mleyHZuq2hk4-Va2YPd5mvPcc,11549 -pylint/checkers/design_analysis.py,sha256=AoQ45dKLHCW9CPCM5KRarYE_sA32z4fqLdzJ1uccl_k,24682 -pylint/checkers/dunder_methods.py,sha256=kWECwPubeb0nhaTcqVQ-OyTHwGcX8iAjj_vUgdzl8hg,3892 -pylint/checkers/ellipsis_checker.py,sha256=Qw7l0vN_26OTOpH3yNOTgHQYRRCGF_sUb5enzbbJXpU,2024 -pylint/checkers/exceptions.py,sha256=xdJ5D4rQEjIHUju9T0h2YFtgAlPeBKi2EUPRagPBET8,26499 -pylint/checkers/format.py,sha256=c9uKUtWYfNXbFXcpa809WaAVJSD7VeowioX3Xn7SoCI,28293 -pylint/checkers/imports.py,sha256=ryzEDQepcNVEHcYyRdq2AYB7kglNI8PG5O5zveBgC9c,49657 -pylint/checkers/lambda_expressions.py,sha256=OmzNgzZO34cfOqF4OJz_LkxRYbgwpPrVS3K1bLsw2_0,3603 -pylint/checkers/logging.py,sha256=xs-iY6sqaPQIRbQHR4CxfovfpuOdQptq1lPU0RQ6Bx4,16207 -pylint/checkers/match_statements_checker.py,sha256=3OiruqvpBFaFgHqO64uWGuNf45BNXRph0ZaGKtbcls4,8372 -pylint/checkers/method_args.py,sha256=88dKdFAvs4w5EbX1FhmeuOBWiWEotGsYPBtDtgEzP18,4764 -pylint/checkers/misc.py,sha256=Zh9VdepO2kBzW78KZZbXd-rbamDLf5ze7TjN4reBwew,7059 -pylint/checkers/modified_iterating_checker.py,sha256=BUt1sGRUGn4kKTRnYM7WMuhPme58R793JFunkO7K89o,7578 -pylint/checkers/nested_min_max.py,sha256=dpfLsbxy3YuniUp4k_KBgyIJ6Qnmc1bFFkEkzaA7YIQ,5934 -pylint/checkers/newstyle.py,sha256=r_i-y_cYcDmeMOxA-PFTQhaWBxUbHH3eWiCxTCDaK94,3985 -pylint/checkers/non_ascii_names.py,sha256=xnmfGSwVc2mTzGyMtDBU1E0PK9pkx28w70CwZSDGbPE,7244 -pylint/checkers/raw_metrics.py,sha256=oN_us6e660qb4BIRxl5TkhLcYpOMiI2AKEahqhcOlGU,3705 -pylint/checkers/refactoring/__init__.py,sha256=g8LJprDaWRc6QEVtmz9HvQhGsS6rnwMPe9QxrrMKE7E,1124 -pylint/checkers/refactoring/__pycache__/__init__.cpython-310.pyc,, -pylint/checkers/refactoring/__pycache__/implicit_booleaness_checker.cpython-310.pyc,, -pylint/checkers/refactoring/__pycache__/not_checker.cpython-310.pyc,, -pylint/checkers/refactoring/__pycache__/recommendation_checker.cpython-310.pyc,, -pylint/checkers/refactoring/__pycache__/refactoring_checker.cpython-310.pyc,, -pylint/checkers/refactoring/implicit_booleaness_checker.py,sha256=sUiUuD4C82OlLY4viIfPdXUgFMXvRj3b6d6333EApBM,16768 -pylint/checkers/refactoring/not_checker.py,sha256=vvVEcw2TFzoV3UJdFp2vFdfDgu1q8V80-D4RsX3APwU,2976 -pylint/checkers/refactoring/recommendation_checker.py,sha256=XlBtjmbLarVU3uXiotaw5Zleq3nmOnlfNyD9VD-5v8g,18738 -pylint/checkers/refactoring/refactoring_checker.py,sha256=Tl_qjaiEmP4-hv0iyVElJO6WQuPYpXTk0HzTVpj4YHs,101282 -pylint/checkers/spelling.py,sha256=ln2QkDdxjBCCVxKlUA4WPxUnawmTe64i85mtY9dat1c,16334 -pylint/checkers/stdlib.py,sha256=GdpSRiSw4SvpsvRql1Gn0pd4qFREXnbOXDaOr_GYK0Y,37355 -pylint/checkers/strings.py,sha256=SQDASZqpm1PpdpudGnFonNNxiOwoTDWoWvve9dTjanE,44979 -pylint/checkers/symilar.py,sha256=IJgZ7DG1icg1ZevJOeuoH6ekKqwiQZwPfWOlpcSNX6g,33937 -pylint/checkers/threading_checker.py,sha256=H_fy_ySghjCd-wfG4pPMhS9eF6X9593wtDDV7nGo018,1951 -pylint/checkers/typecheck.py,sha256=zUqh4TrfFp78eq3keBJ-_ItsUIxb8LxGKdQ_JYizz_Y,91391 -pylint/checkers/unicode.py,sha256=4U0ABwAxjQoU3Jk7kbdLZuH7zYjcVJafL8Ambu-4k6Y,18474 -pylint/checkers/unsupported_version.py,sha256=gWzlJXbJ4CM7TsjmygzWh5LnX7AVXoz0v7NeyDfdIhE,7745 -pylint/checkers/utils.py,sha256=ydl-fHVeesi5hf3yQcdAnr5gONRbMMXxldWS9gjq_Ro,80376 -pylint/checkers/variables.py,sha256=UNI7OH0cl_6yYsjyJ-jQdKMWCICPMIHf0epuSXeRXwQ,140475 -pylint/config/__init__.py,sha256=Pq5uSrT_lQQe68zth16TBbe_EpzKUci3nKXxzZAFE20,387 -pylint/config/__pycache__/__init__.cpython-310.pyc,, -pylint/config/__pycache__/argument.cpython-310.pyc,, -pylint/config/__pycache__/arguments_manager.cpython-310.pyc,, -pylint/config/__pycache__/arguments_provider.cpython-310.pyc,, -pylint/config/__pycache__/callback_actions.cpython-310.pyc,, -pylint/config/__pycache__/config_file_parser.cpython-310.pyc,, -pylint/config/__pycache__/config_initialization.cpython-310.pyc,, -pylint/config/__pycache__/deprecation_actions.cpython-310.pyc,, -pylint/config/__pycache__/exceptions.cpython-310.pyc,, -pylint/config/__pycache__/find_default_config_files.cpython-310.pyc,, -pylint/config/__pycache__/help_formatter.cpython-310.pyc,, -pylint/config/__pycache__/utils.cpython-310.pyc,, -pylint/config/_breaking_changes/__init__.py,sha256=FNE1P6pTPQU13GtgMGxLRkMxAIVG_VC6Goi5nfgAIYQ,6658 -pylint/config/_breaking_changes/__pycache__/__init__.cpython-310.pyc,, -pylint/config/_pylint_config/__init__.py,sha256=B9J0yKzTWcEqJreLhgFmq0J4lGfz4Fg_GFNASCYO814,571 -pylint/config/_pylint_config/__pycache__/__init__.cpython-310.pyc,, -pylint/config/_pylint_config/__pycache__/generate_command.cpython-310.pyc,, -pylint/config/_pylint_config/__pycache__/help_message.cpython-310.pyc,, -pylint/config/_pylint_config/__pycache__/main.cpython-310.pyc,, -pylint/config/_pylint_config/__pycache__/setup.cpython-310.pyc,, -pylint/config/_pylint_config/__pycache__/utils.cpython-310.pyc,, -pylint/config/_pylint_config/generate_command.py,sha256=kvUxyyvZNxKWVbmaxnYcX5Auv4pPra5bhaExDuSr_fM,1718 -pylint/config/_pylint_config/help_message.py,sha256=fLjKdnNZbRindEpGMRux7wTpdBwX3WLIE48om-5VYlE,2007 -pylint/config/_pylint_config/main.py,sha256=taUrBKRaS_N256KQPgvu9ElHLkSWStL5_aL4hfgDwCA,855 -pylint/config/_pylint_config/setup.py,sha256=imOS5tT_5mcmB0IclU1H1LqQZ6qCwskFyZh6Vi6l7-U,1613 -pylint/config/_pylint_config/utils.py,sha256=t8cbd_dQUga6Rt6YuT7aynnWqKXUEGJ73zEdbWFtTic,3558 -pylint/config/argument.py,sha256=A1VGmjAegYJYU_ZK0KHIQLl7hnSCZtpAqWBRqqTsY3Y,14859 -pylint/config/arguments_manager.py,sha256=Jz8UgGTqvCHpMg7-EFDecg7-Q3Qp94OxjDpAYE1AybI,14798 -pylint/config/arguments_provider.py,sha256=EG2Hw8zwbk4QvXCeqBGOYLWNL9xHf_DK8yBeYmdmd7M,2392 -pylint/config/callback_actions.py,sha256=fGPnUppIJR9MM0bEWwcp8RxcsFOaNUcFmt8y7oQ9L8c,13391 -pylint/config/config_file_parser.py,sha256=RhOf_peHAzFDpcahsOsEoIagdNeY8j7c6E8wEKk9fj8,4513 -pylint/config/config_initialization.py,sha256=1IOA61MshdiGhHKENCBgdKphZbMluUKjx51NX2hocnI,7570 -pylint/config/deprecation_actions.py,sha256=LAxHHR6kF0k82rCSTb_BOZT2wOSR027IzMzcS7mb4Yg,2983 -pylint/config/exceptions.py,sha256=GLd4fUNrbIky8ZgQrbOgGWLxXhcs3wlZtOvXjRLVap4,826 -pylint/config/find_default_config_files.py,sha256=WyuViPtEuTpl9ZV3fgbUgCe_qQ1q9oL3x323ZmXQKEY,4634 -pylint/config/help_formatter.py,sha256=Gd4ZYCVFu5VgnIlBLHTm3elByo0-UXNo1lQLJm3JuGo,2583 -pylint/config/utils.py,sha256=VjuKef8rQiWoHkarX3Ir8kB1lW5oP3a02emBXZj601A,8774 -pylint/constants.py,sha256=tP7w-gdZGXHmrAcqT-i6_sALrPpkMJvvQef0as5bPjY,9044 -pylint/exceptions.py,sha256=Ysrp0ddkUlebm2n4lWH8tYyqWdOImyyvdliMlGGbMgo,1726 -pylint/extensions/__init__.py,sha256=QNagpSKuKHwFXh3jUpvZGtFP27OT0NSec_vXfUS-cuk,585 -pylint/extensions/__pycache__/__init__.cpython-310.pyc,, -pylint/extensions/__pycache__/_check_docs_utils.cpython-310.pyc,, -pylint/extensions/__pycache__/bad_builtin.cpython-310.pyc,, -pylint/extensions/__pycache__/broad_try_clause.cpython-310.pyc,, -pylint/extensions/__pycache__/check_elif.cpython-310.pyc,, -pylint/extensions/__pycache__/code_style.cpython-310.pyc,, -pylint/extensions/__pycache__/comparison_placement.cpython-310.pyc,, -pylint/extensions/__pycache__/confusing_elif.cpython-310.pyc,, -pylint/extensions/__pycache__/consider_refactoring_into_while_condition.cpython-310.pyc,, -pylint/extensions/__pycache__/consider_ternary_expression.cpython-310.pyc,, -pylint/extensions/__pycache__/dict_init_mutate.cpython-310.pyc,, -pylint/extensions/__pycache__/docparams.cpython-310.pyc,, -pylint/extensions/__pycache__/docstyle.cpython-310.pyc,, -pylint/extensions/__pycache__/dunder.cpython-310.pyc,, -pylint/extensions/__pycache__/empty_comment.cpython-310.pyc,, -pylint/extensions/__pycache__/eq_without_hash.cpython-310.pyc,, -pylint/extensions/__pycache__/for_any_all.cpython-310.pyc,, -pylint/extensions/__pycache__/magic_value.cpython-310.pyc,, -pylint/extensions/__pycache__/mccabe.cpython-310.pyc,, -pylint/extensions/__pycache__/no_self_use.cpython-310.pyc,, -pylint/extensions/__pycache__/overlapping_exceptions.cpython-310.pyc,, -pylint/extensions/__pycache__/private_import.cpython-310.pyc,, -pylint/extensions/__pycache__/redefined_loop_name.cpython-310.pyc,, -pylint/extensions/__pycache__/redefined_variable_type.cpython-310.pyc,, -pylint/extensions/__pycache__/set_membership.cpython-310.pyc,, -pylint/extensions/__pycache__/typing.cpython-310.pyc,, -pylint/extensions/__pycache__/while_used.cpython-310.pyc,, -pylint/extensions/_check_docs_utils.py,sha256=onbbn32fvHpBbJxMxlIc3qNTcRkp0z_imyrZXJFkb6w,29676 -pylint/extensions/bad_builtin.py,sha256=vyKS9BQ3yElI58n-MHz8oB3_lCMqp-FmedHH_49mJug,2268 -pylint/extensions/broad_try_clause.py,sha256=56N3eMlJ_1cfHA9ek5LpkbIOhnoFk0aC6R8W7S0Pt3c,2268 -pylint/extensions/check_elif.py,sha256=XduStUiTNc4SOSlMmZyD5EPfknZNPfL2_eENK0YX4JY,2149 -pylint/extensions/code_style.py,sha256=P2EA9653eqE34xmKHQXcwHkgjAHiB78FCwfKCGSJL3k,14530 -pylint/extensions/comparison_placement.py,sha256=UTYXBM4jXsOV5VrCqRbP62kYdztGaCjQ42fNsGtqtVM,2362 -pylint/extensions/confusing_elif.py,sha256=ab3tf81CQnRmdxyGU40Fb4pM2On4jsI4YfYlpv6id5k,2049 -pylint/extensions/consider_refactoring_into_while_condition.py,sha256=XYivi6FUHIq47w9-2qVXGGHoJS-4Rk1hSxt9pT-G9fE,3321 -pylint/extensions/consider_ternary_expression.py,sha256=gNm1CrJqLCrCYNcZtTK30uwh-Mu4k7r7YK4UDYnPtrA,1640 -pylint/extensions/dict_init_mutate.py,sha256=3Q3RW0ALETIOXl6epxP7uodh8z9fTNPQdfatApRYheY,1782 -pylint/extensions/docparams.py,sha256=K6A-UiwFkJlg7K61XFdn4L-jt4tDgcH49D8C7xB9KZc,26456 -pylint/extensions/docstyle.py,sha256=wh5S31An6fYB6ZJu3RAhGuHTdMbgO6SrfxWiU4449Bo,2953 -pylint/extensions/dunder.py,sha256=Te-SuvRftTc4JnuA4q4fqRwaMNadr3mWAiPMYpCHhdM,2511 -pylint/extensions/empty_comment.py,sha256=B_NXCNYu_7EJmIrdENpt9ffuQGOnzRqy5TB7KmgZmsY,1963 -pylint/extensions/eq_without_hash.py,sha256=EghMfVWHCJGr-gqY26Qbzw6Do3XuyMkbgn7kTRfhcH8,1470 -pylint/extensions/for_any_all.py,sha256=9KskGf_XLjOCNsdQFDoAgD2Q-Sjk6YcLu1UMs5ridPE,5849 -pylint/extensions/magic_value.py,sha256=_eSTknhC7ZX4ZQWDrPTVnK8mJgHaooo4rN0StuS5CF0,4260 -pylint/extensions/mccabe.py,sha256=oapX2HZjfWRLd2YoAgR0sDZBk200HUNKyCEINpRFy0o,7521 -pylint/extensions/no_self_use.py,sha256=Dnr0IvBcpCPr1s8ghcuZi_yqUWUYLeS981aLXtZ0430,3694 -pylint/extensions/overlapping_exceptions.py,sha256=nO9Bb27sQfokoO8PFB-K3jN5rtK9uZyMAQVehuAIS1M,3270 -pylint/extensions/private_import.py,sha256=JoF_IaSCoV3ffXH9h-ymkxc952ZirwU2NCwWR64CG4g,11194 -pylint/extensions/redefined_loop_name.py,sha256=QvIVd8gD-8FpbnBT4VLXN6h1vszPgb9rJVLVgtcRGFw,3230 -pylint/extensions/redefined_variable_type.py,sha256=ga8WIDw57G6qWiWIY4SV5kzrDlK1RQ3BtkAJRoNxqZI,4105 -pylint/extensions/set_membership.py,sha256=GKnGo_kRnF462QJL15VcRBo-3Ml_HJTG0r8brQXFS-c,1806 -pylint/extensions/typing.py,sha256=ixCaGc0cI71GQ2mq3WGtnYh0Kf_XrNS3G_6-FugwAcU,22622 -pylint/extensions/while_used.py,sha256=IZKtKqTAsbqenOp_Gik4_iEL9dHPSdYbe3MPD8IJLq8,1103 -pylint/graph.py,sha256=gBkYH8T-w0FCBtG-lWzUGkJ_6cth7aecb_CTXXqTAeU,7101 -pylint/interfaces.py,sha256=91EedZoL9a6L76-3620eHeMnFFvtZ3x1LGaAEEdEv2w,1191 -pylint/lint/__init__.py,sha256=YH2V4VbgfBCHkle4vg7uRPKiNKdBoR5AK9xnpLQF4cs,1394 -pylint/lint/__pycache__/__init__.cpython-310.pyc,, -pylint/lint/__pycache__/base_options.cpython-310.pyc,, -pylint/lint/__pycache__/caching.cpython-310.pyc,, -pylint/lint/__pycache__/expand_modules.cpython-310.pyc,, -pylint/lint/__pycache__/message_state_handler.cpython-310.pyc,, -pylint/lint/__pycache__/parallel.cpython-310.pyc,, -pylint/lint/__pycache__/pylinter.cpython-310.pyc,, -pylint/lint/__pycache__/report_functions.cpython-310.pyc,, -pylint/lint/__pycache__/run.cpython-310.pyc,, -pylint/lint/__pycache__/utils.cpython-310.pyc,, -pylint/lint/base_options.py,sha256=wbQAoSdHlDPXoaG9bUFSFODeHGlxacqqy786aYKKGLc,21533 -pylint/lint/caching.py,sha256=mdNDPZBwYWJw17e7_1QvAE_l827KnJfxAL6CfSoJ2xw,2424 -pylint/lint/expand_modules.py,sha256=kkupMfK-onAI0uLusZ_a_taSkKyrE3Z5lpRuUuyp6wU,7190 -pylint/lint/message_state_handler.py,sha256=0x6jM8kjzmhPnMM9YEXKaHIROeI1SaS4s8V6KopW08k,17960 -pylint/lint/parallel.py,sha256=bchzKK-yKYXkGo2s-hfm_zL9Nx_vJG4DpX71Mq_QbUU,6425 -pylint/lint/pylinter.py,sha256=bWgIuTdLx2sADtGZ6krJ0xEutt7mBE_DosL_fl-atmM,52076 -pylint/lint/report_functions.py,sha256=ESzONaMBZRE-wj13RdglQ_YlDvRUFEyKE2_B7MlBSAg,2947 -pylint/lint/run.py,sha256=uemxV_n1-3XMh6mbtPW-Pb7QvHYVhA04hgfUDE56Ltg,9894 -pylint/lint/utils.py,sha256=r4u1nbT1budbfzL-Y-IGIaEKqUrNPo_wzrzj6TZGbOg,3357 -pylint/message/__init__.py,sha256=83tEoCsTjG15vPoEnVW99qg5Lpy1Ee14kwjrX_PK-eQ,632 -pylint/message/__pycache__/__init__.cpython-310.pyc,, -pylint/message/__pycache__/_deleted_message_ids.cpython-310.pyc,, -pylint/message/__pycache__/message.cpython-310.pyc,, -pylint/message/__pycache__/message_definition.cpython-310.pyc,, -pylint/message/__pycache__/message_definition_store.cpython-310.pyc,, -pylint/message/__pycache__/message_id_store.cpython-310.pyc,, -pylint/message/_deleted_message_ids.py,sha256=6xtPh55G_rOwh319gl8a2iNMZPkWzFwujnzQZVlYttY,7578 -pylint/message/message.py,sha256=Qndr3rNqYW5KxEfqRV5wEXO47NVGvElkFH1n2Cc6BL0,2165 -pylint/message/message_definition.py,sha256=Uzbvffv6fuFkxkBHBTZHN3DkSgwIqZTIt5dZUrAiTOg,5013 -pylint/message/message_definition_store.py,sha256=b7uFTDz9nj8KMms2BffjiWZtWJbD6BcymazX2DkRq5M,5083 -pylint/message/message_id_store.py,sha256=27ID6Ueo5qyH8jNScKOOqbAkWkjrSnn-SP3Dbp-0XBo,6370 -pylint/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pylint/pyreverse/__init__.py,sha256=eHl5rdin7-cPqgefwZteuF89noKc-nhavpyH1Y6ppyk,284 -pylint/pyreverse/__pycache__/__init__.cpython-310.pyc,, -pylint/pyreverse/__pycache__/diadefslib.cpython-310.pyc,, -pylint/pyreverse/__pycache__/diagrams.cpython-310.pyc,, -pylint/pyreverse/__pycache__/dot_printer.cpython-310.pyc,, -pylint/pyreverse/__pycache__/inspector.cpython-310.pyc,, -pylint/pyreverse/__pycache__/main.cpython-310.pyc,, -pylint/pyreverse/__pycache__/mermaidjs_printer.cpython-310.pyc,, -pylint/pyreverse/__pycache__/plantuml_printer.cpython-310.pyc,, -pylint/pyreverse/__pycache__/printer.cpython-310.pyc,, -pylint/pyreverse/__pycache__/printer_factory.cpython-310.pyc,, -pylint/pyreverse/__pycache__/utils.cpython-310.pyc,, -pylint/pyreverse/__pycache__/writer.cpython-310.pyc,, -pylint/pyreverse/diadefslib.py,sha256=GG7IT7ChcjQUCE6QMXg3Ur-sA0xliaHHSaZvYyBGpfs,11456 -pylint/pyreverse/diagrams.py,sha256=v8stLuI5ZOwaP8jJG6GZPo2PQ6Ew6ZFvRAq5n7NSUcE,13099 -pylint/pyreverse/dot_printer.py,sha256=oShyureybX_H_IvDE5DTug2vVBXPLrVxHuWN77X52XM,6661 -pylint/pyreverse/inspector.py,sha256=XB7SY5-fhgW9A3muMr_r2hHrkuR5xFGgR1enxF5kPCE,20876 -pylint/pyreverse/main.py,sha256=d4Z21tAhrmazJUig0XroCZhCCcSi6T73Bs-5uN4BmH0,11732 -pylint/pyreverse/mermaidjs_printer.py,sha256=oM0KaAWhyL0wi5tMcMEOtjvgt4aE6T1fJGjGg75JmhM,4508 -pylint/pyreverse/plantuml_printer.py,sha256=Iq4cAsv4DpVzDoAVXf0URt_qiYL9xELaJLnohvlbsaA,3578 -pylint/pyreverse/printer.py,sha256=xCf6OZMSCbJQh1yawoL_k1gpzq1LhAI6FJiRIF446oE,3758 -pylint/pyreverse/printer_factory.py,sha256=YOzS71r89gBWtuJdb35AuQNXqacHTNkppBIFNleDHCY,835 -pylint/pyreverse/utils.py,sha256=hd0feL7pm2OjNGdv6XYxOtzB6NdOCWiZdUOyMDSeC7M,8346 -pylint/pyreverse/writer.py,sha256=RcTBGIUX4jCRBwkS8LwFKGPehR2FPjRa7Hq43-Ue5DI,8038 -pylint/reporters/__init__.py,sha256=w2FTS9pTKhlQyLDhjf-JjJdTvlt3pnSt-FxitNsTDeY,1072 -pylint/reporters/__pycache__/__init__.cpython-310.pyc,, -pylint/reporters/__pycache__/base_reporter.cpython-310.pyc,, -pylint/reporters/__pycache__/collecting_reporter.cpython-310.pyc,, -pylint/reporters/__pycache__/json_reporter.cpython-310.pyc,, -pylint/reporters/__pycache__/multi_reporter.cpython-310.pyc,, -pylint/reporters/__pycache__/progress_reporters.cpython-310.pyc,, -pylint/reporters/__pycache__/reports_handler_mix_in.cpython-310.pyc,, -pylint/reporters/__pycache__/text.cpython-310.pyc,, -pylint/reporters/base_reporter.py,sha256=oBD52xuayLtGk9VthD-VETNnh9QwFCiNsvhCiwCn3G0,3087 -pylint/reporters/collecting_reporter.py,sha256=46dS4dzm2HBe9Lcr5AuQl1kx2OLa83YVpoorqGX5Nig,735 -pylint/reporters/json_reporter.py,sha256=1cjhYjQxmEoxOca55kX6a9-xEvCcu-E-qS0lE3-icN8,6395 -pylint/reporters/multi_reporter.py,sha256=tIceA90VF49orK3RtxBjLEi-VNwK9H-1zwEAbEpgoHI,3771 -pylint/reporters/progress_reporters.py,sha256=llNcEHHb35WVvpjPS5hMl5JL0GgWUQhsUhfDDro_Jsw,1083 -pylint/reporters/reports_handler_mix_in.py,sha256=QPThQi4t7RkiQsZlFQfwXG2c1_Fgghan12ADB1HfEpg,3069 -pylint/reporters/text.py,sha256=oxJScVsNOEI2ljQcBDItYz4vbDJ8ueWebDmRVVEnkJc,9999 -pylint/reporters/ureports/__init__.py,sha256=nLdCdbJuOW6AfeCVl5BZ90ZV_Ms1EOeMcg_xP8C-JqA,320 -pylint/reporters/ureports/__pycache__/__init__.cpython-310.pyc,, -pylint/reporters/ureports/__pycache__/base_writer.cpython-310.pyc,, -pylint/reporters/ureports/__pycache__/nodes.cpython-310.pyc,, -pylint/reporters/ureports/__pycache__/text_writer.cpython-310.pyc,, -pylint/reporters/ureports/base_writer.py,sha256=WcGtCmHJjlp3YT-KRFHLQ6w1KkrVQeQeITfXvrb5WYo,3440 -pylint/reporters/ureports/nodes.py,sha256=OhlNjL1OEZIure-4XhYcnZEUdfFTgwBRHxkvWtdQKJo,5315 -pylint/reporters/ureports/text_writer.py,sha256=4qsrpWU7vr8whu0xbZtgRXzZo7KSaQF-5mZHp8VnEUQ,3616 -pylint/testutils/__init__.py,sha256=PbMNUX0K1Ra_Fwwy4Lxha7xBtBX1ptSpMuCFcJ9oDEA,1319 -pylint/testutils/__pycache__/__init__.cpython-310.pyc,, -pylint/testutils/__pycache__/_run.cpython-310.pyc,, -pylint/testutils/__pycache__/checker_test_case.cpython-310.pyc,, -pylint/testutils/__pycache__/configuration_test.cpython-310.pyc,, -pylint/testutils/__pycache__/constants.cpython-310.pyc,, -pylint/testutils/__pycache__/decorator.cpython-310.pyc,, -pylint/testutils/__pycache__/get_test_info.cpython-310.pyc,, -pylint/testutils/__pycache__/global_test_linter.cpython-310.pyc,, -pylint/testutils/__pycache__/lint_module_test.cpython-310.pyc,, -pylint/testutils/__pycache__/output_line.cpython-310.pyc,, -pylint/testutils/__pycache__/pyreverse.cpython-310.pyc,, -pylint/testutils/__pycache__/reporter_for_tests.cpython-310.pyc,, -pylint/testutils/__pycache__/tokenize_str.cpython-310.pyc,, -pylint/testutils/__pycache__/unittest_linter.cpython-310.pyc,, -pylint/testutils/__pycache__/utils.cpython-310.pyc,, -pylint/testutils/_primer/__init__.py,sha256=ODIorqHjiCK9DXPi1NgBawd46drp6MlKM4-V9CEk7E4,389 -pylint/testutils/_primer/__pycache__/__init__.cpython-310.pyc,, -pylint/testutils/_primer/__pycache__/package_to_lint.cpython-310.pyc,, -pylint/testutils/_primer/__pycache__/primer.cpython-310.pyc,, -pylint/testutils/_primer/__pycache__/primer_command.cpython-310.pyc,, -pylint/testutils/_primer/__pycache__/primer_compare_command.cpython-310.pyc,, -pylint/testutils/_primer/__pycache__/primer_prepare_command.cpython-310.pyc,, -pylint/testutils/_primer/__pycache__/primer_run_command.cpython-310.pyc,, -pylint/testutils/_primer/package_to_lint.py,sha256=bUaVe90Cn_o0-ZGDQ2lg5AitLKml3G6z8J5kFykCLP4,4741 -pylint/testutils/_primer/primer.py,sha256=txjjMeYhi6T4rQ3SDOQtlFkEcjZ_1JSgnaREP_tJyn0,4685 -pylint/testutils/_primer/primer_command.py,sha256=ow1w5XXtPvEm56fgA5SnwTgeBfejZv2zcipBS_foGqc,999 -pylint/testutils/_primer/primer_compare_command.py,sha256=dHV04WOMAUN1FQ5cZDIDoMXBkPDTisqhf6VQ9nkw-H0,6989 -pylint/testutils/_primer/primer_prepare_command.py,sha256=2crSGDxfhBEltEc2NH7Rgw_ujw-excceHfEGWSAzjCk,2040 -pylint/testutils/_primer/primer_run_command.py,sha256=AZKyb9w5d3NhWOUUMk5FviJvecqAUKCFVhSBCaHYj3Q,4520 -pylint/testutils/_run.py,sha256=lukrQmA9nD5iEuQ-hVOVgMMXHQu99NCw8FOh1RJF35Y,1430 -pylint/testutils/checker_test_case.py,sha256=jnTFL-m7ueC7BmHymsXnZ7R5cKtVsMpMRgQ4I0a6qbk,3325 -pylint/testutils/configuration_test.py,sha256=kH4h98m8dJyEFwBjGzYgUY1xXG1E7fiYZKlYZY8Oebk,5421 -pylint/testutils/constants.py,sha256=7GaUpGWSP6wFXBXUuaqsfXWpCHp3t2e6_C3CDm7L0jg,1376 -pylint/testutils/decorator.py,sha256=1s06oWObolRBPbwXcYd2fgmHhyBObYuaJTr7RX7kG9k,1262 -pylint/testutils/functional/__init__.py,sha256=Gl5HuUawpyTQWCbydzPzLLrvQVs-wX890rG_vwwLDkg,800 -pylint/testutils/functional/__pycache__/__init__.cpython-310.pyc,, -pylint/testutils/functional/__pycache__/find_functional_tests.cpython-310.pyc,, -pylint/testutils/functional/__pycache__/lint_module_output_update.cpython-310.pyc,, -pylint/testutils/functional/__pycache__/test_file.cpython-310.pyc,, -pylint/testutils/functional/find_functional_tests.py,sha256=8Qb7DlAXUMiZH_Cwm96AxVSK_fPPlj51caMxUxHDwjA,5345 -pylint/testutils/functional/lint_module_output_update.py,sha256=2AE432n8NHDUL47GP_-9eFLnd2vK61uyRuN32cJPHZ0,1529 -pylint/testutils/functional/test_file.py,sha256=u6Pvb7fWESu7IenrvQbiwJY8D7_PlsdldwyX_PHGFKc,4346 -pylint/testutils/get_test_info.py,sha256=wxPeK4mMELPV6FhOav8y5RJY3xwerB55a5NM_2-hjFI,2137 -pylint/testutils/global_test_linter.py,sha256=AAfD8FrniXJW_yPtm79FVCMjEYcJEYMj7V-V_xZamSA,695 -pylint/testutils/lint_module_test.py,sha256=g4zhMciClHi_lPPEa9em2kp6mrFSPWKlkDb8tSuYg_A,13268 -pylint/testutils/output_line.py,sha256=nKvWm8tnqy7nN0arPgFF5GlDnXtkmmQz1IZ_S4Xt7Rc,3994 -pylint/testutils/pyreverse.py,sha256=5M4uqY_gGEgCe7JSlYjFZMYc0Tx7k6t7j39RhUwnm9c,4403 -pylint/testutils/reporter_for_tests.py,sha256=59f73wUKi9-gXSpLT1UyCUnjC0MbAl5lDrVnN6nvVYk,2321 -pylint/testutils/testing_pylintrc,sha256=Y2Tex2VkUOP6dbE8pWQQRcWsTkue8J27y-nBpiAGPLU,264 -pylint/testutils/tokenize_str.py,sha256=TBA3SPx1kQX87AVJkYxCEG_UPID4zD3zIz_Qa6-OyuE,457 -pylint/testutils/unittest_linter.py,sha256=mtO59-zFLN0RoyJeVXByJ9UM9jENNdfJCSZuylhHOq0,2692 -pylint/testutils/utils.py,sha256=-k2ZsWgu5gABhmUvAOERG3L5nT-ShD3fjRZNbfbtWh0,3107 -pylint/typing.py,sha256=Na9VBos05Qx1t7WBq9iwQKeWa_qrXMgFBa_sGJGhD5I,3374 -pylint/utils/__init__.py,sha256=ci5XlmHI6bUyJlvUvLmQJiL0lQC7CsyL6CDeuF_VIvQ,1266 -pylint/utils/__pycache__/__init__.cpython-310.pyc,, -pylint/utils/__pycache__/ast_walker.cpython-310.pyc,, -pylint/utils/__pycache__/docs.cpython-310.pyc,, -pylint/utils/__pycache__/file_state.cpython-310.pyc,, -pylint/utils/__pycache__/linterstats.cpython-310.pyc,, -pylint/utils/__pycache__/pragma_parser.cpython-310.pyc,, -pylint/utils/__pycache__/utils.cpython-310.pyc,, -pylint/utils/ast_walker.py,sha256=blguZYCwdmk20z22B0mZmw4FxJxzB5RX2M_jPhOfJIY,3959 -pylint/utils/docs.py,sha256=pxGJ1bV9-2y8gR53rPEprwvTu5lGSo_VLW7Dha74X6o,3406 -pylint/utils/file_state.py,sha256=orrjTq59eGm9IGpasxuab7hrxwaqebuhS5UCwlX7Hes,9628 -pylint/utils/linterstats.py,sha256=Bv2uxJzM_XLyJGW7XpyvSOeWBmxVVDjG2A53OjGOVjM,13056 -pylint/utils/pragma_parser.py,sha256=VeSO93CgYtq7BpjI0qjbq2vZMPYv_6U41xx9O1A_CWk,5052 -pylint/utils/utils.py,sha256=xxg1jpCC9Rc0VyKj33R3LBMVCgPRlvS0jMXEIyjI9EA,10883 diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/REQUESTED b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL deleted file mode 100644 index e7fa31b..0000000 --- a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (80.9.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt deleted file mode 100644 index 09401fa..0000000 --- a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/entry_points.txt +++ /dev/null @@ -1,5 +0,0 @@ -[console_scripts] -pylint = pylint:run_pylint -pylint-config = pylint:_run_pylint_config -pyreverse = pylint:run_pyreverse -symilar = pylint:run_symilar diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt deleted file mode 100644 index 146e1b9..0000000 --- a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/CONTRIBUTORS.txt +++ /dev/null @@ -1,699 +0,0 @@ -# This file is autocompleted by 'contributors-txt', -# using the configuration in 'script/.contributors_aliases.json'. -# Do not add new persons manually and only add information without -# using '-' as the line first character. -# Please verify that your change are stable if you modify manually. - -Ex-maintainers --------------- -- Claudiu Popa -- Sylvain Thénault : main author / maintainer -- Torsten Marek - - -Maintainers ------------ -- Pierre Sassoulas -- Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> -- Jacob Walls -- Marc Mueller <30130371+cdce8p@users.noreply.github.com> -- Hippo91 -- Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> -- Andreas Finkler <3929834+DudeNr33@users.noreply.github.com> -- Matus Valo -- Dani Alcala <112832187+clavedeluna@users.noreply.github.com> -- Łukasz Rogalski -- Nick Drozd : performance improvements to astroid -- Ashley Whetter -- Bryce Guinta -- Yu Shao, Pang <36848472+yushao2@users.noreply.github.com> -- Dimitri Prybysh - * multiple-imports, not-iterable, not-a-mapping, various patches. -- Roy Williams (Lyft) - * added check for implementing __eq__ without implementing __hash__, - * Added Python 3 check for accessing Exception.message. - * Added Python 3 check for calling encode/decode with invalid codecs. - * Added Python 3 check for accessing sys.maxint. - * Added Python 3 check for bad import statements. - * Added Python 3 check for accessing deprecated methods on the 'string' module, - various patches. -- Florian Bruhin -- Arianna Yang - - -Contributors ------------- - -We would not be here without folks that contributed patches, pull requests, -issues and their time to pylint. We're incredibly grateful to all of these -contributors: - -- Emile Anclin (Logilab): python 3 support -- Michal Nowikowski : - * wrong-spelling-in-comment - * wrong-spelling-in-docstring - * parallel execution on multiple CPUs -- Julthep Nandakwang -- Bruno Daniel : check_docs extension. -- Sushobhit <31987769+sushobhit27@users.noreply.github.com> (sushobhit27) - * Added new check 'comparison-with-itself'. - * Added new check 'useless-import-alias'. - * Added support of annotations in missing-type-doc and missing-return-type-doc. - * Added new check 'comparison-with-callable'. - * Removed six package dependency. - * Added new check 'chained-comparison'. - * Added new check 'useless-object-inheritance'. -- Brett Cannon : - * Port source code to be Python 2/3 compatible - * Python 3 checker -- Laura Médioni (Logilab, on behalf of the CNES): - * misplaced-comparison-constant - * no-classmethod-decorator - * no-staticmethod-decorator - * too-many-nested-blocks, - * too-many-boolean-expressions - * unneeded-not - * wrong-import-order - * ungrouped-imports, - * wrong-import-position - * redefined-variable-type -- Harutaka Kawamura -- Alexandre Fayolle (Logilab): TkInter gui, documentation, debian support -- Ville Skyttä -- Zen Lee <53538590+zenlyj@users.noreply.github.com> -- Julien Cristau (Logilab): python 3 support -- Moisés López <6644187+moylop260@users.noreply.github.com>: - * Support for deprecated-modules in modules not installed, - * Refactor wrong-import-order to integrate it with `isort` library - * Add check too-complex with mccabe for cyclomatic complexity - * Refactor wrong-import-position to skip try-import and nested cases - * Add consider-merging-isinstance, superfluous-else-return - * Fix consider-using-ternary for 'True and True and True or True' case - * Add bad-docstring-quotes and docstring-first-line-empty - * Add missing-timeout - * Fix false negative for `deprecated-module` when a `__import__` method is used instead of `import` sentence -- Adrien Di Mascio -- Frank Harrison (doublethefish) -- Pierre-Yves David -- David Shea : invalid sequence and slice index -- Gunung P. Wibisono <55311527+gunungpw@users.noreply.github.com> -- Derek Gustafson -- Cezar Elnazli : deprecated-method -- Joseph Young <80432516+jpy-git@users.noreply.github.com> (jpy-git) -- Tim Martin -- Ollie <46904826+ollie-iterators@users.noreply.github.com> -- Julian Grimm <51880314+Julfried@users.noreply.github.com> -- Tushar Sadhwani (tusharsadhwani) -- Nicolas Chauvat -- orSolocate <38433858+orSolocate@users.noreply.github.com> -- Radu Ciorba : not-context-manager and confusing-with-statement warnings. -- Holger Peters -- Cosmin Poieană : unichr-builtin and improvements to bad-open-mode. -- Yilei "Dolee" Yang -- Steven Myint : duplicate-except. -- Peter Kolbus (Garmin) -- Luigi Bertaco Cristofolini (luigibertaco) -- Glenn Matthews : - * autogenerated documentation for optional extensions, - * bug fixes and enhancements for docparams (née check_docs) extension -- crazybolillo -- correctmost <134317971+correctmost@users.noreply.github.com> -- Vlad Temian : redundant-unittest-assert and the JSON reporter. -- Julien Jehannet -- Boris Feld -- Anthony Sottile -- Andrew Haigh (nelfin) -- Robert Hofer -- Pedro Algarvio (s0undt3ch) -- Julien Palard -- Hugo van Kemenade -- David Liu (david-yz-liu) -- Dan Goldsmith : support for msg-template in HTML reporter. -- Buck Evan -- Mariatta Wijaya - * Added new check `logging-fstring-interpolation` - * Documentation typo fixes -- Jakub Wilk -- Eli Fine (eli88fine): Fixed false positive duplicate code warning for lines with symbols only -- Émile Crater -- Pavel Roskin -- David Gilman -- へーさん -- Thomas Hisch -- Marianna Polatoglou : minor contribution for wildcard import check -- Manuel Vázquez Acosta -- Luis Escobar (Vauxoo): Add bad-docstring-quotes and docstring-first-line-empty -- Lucas Cimon -- Konstantina Saketou <56515303+ksaketou@users.noreply.github.com> -- Konstantin -- Jim Robertson -- Ethan Leba -- Enji Cooper -- Drum Ogilvie -- David Lindquist : logging-format-interpolation warning. -- Daniel Harding -- Anthony Truchet -- Alexander Todorov : - * added new error conditions to 'bad-super-call', - * Added new check for incorrect len(SEQUENCE) usage, - * Added new extension for comparison against empty string constants, - * Added new extension which detects comparing integers to zero, - * Added new useless-return checker, - * Added new try-except-raise checker -- theirix -- Téo Bouvard -- Sviatoslav Sydorenko -- Stavros Ntentos <133706+stdedos@users.noreply.github.com> -- Nicolas Boulenguez -- Mihai Balint -- Mark Bell -- Levi Gruspe -- Jakub Kuczys -- Hornwitser : fix import graph -- Fureigh -- David Douard -- Daniel Balparda (Google): GPyLint maintainer (Google's pylint variant) -- Christian Clauss -- Bastien Vallet (Djailla) -- Aru Sahni : Git ignoring, regex-based ignores -- Andreas Freimuth : fix indentation checking with tabs -- Alexandru Coman -- jpkotta -- Thomas Grainger -- Takahide Nojima -- Taewon D. Kim -- Sneaky Pete -- Sergey B Kirpichev -- Sandro Tosi : Debian packaging -- Rogdham -- Rene Zhang -- Paul Lichtenberger -- Or Bahari -- Mr. Senko -- Mike Frysinger -- Martin von Gagern (Google): Added 'raising-format-tuple' warning. -- Martin Vielsmaier -- Martin Pool (Google): - * warnings for anomalous backslashes - * symbolic names for messages (like 'unused') - * etc. -- Martin Bašti - * Added new check for shallow copy of os.environ - * Added new check for useless `with threading.Lock():` statement -- Marcus Näslund (naslundx) -- Marco Pernigotti <7657251+mpernigo@users.noreply.github.com> -- Marco Forte -- James Addison <55152140+jayaddison@users.noreply.github.com> -- Ionel Maries Cristian -- Gergely Kalmár -- Damien Baty -- Benjamin Drung : contributing Debian Developer -- Anubhav <35621759+anubh-v@users.noreply.github.com> -- Antonio Quarta -- Andrew J. Simmons -- Alvaro Frias -- Alexey Pelykh -- Alex Prabhat Bara -- wtracy -- jessebrennan -- chohner -- aatle <168398276+aatle@users.noreply.github.com> -- Tiago Honorato <61059243+tiagohonorato@users.noreply.github.com> -- Steven M. Vascellaro -- Robin Tweedie <70587124+robin-wayve@users.noreply.github.com> -- Roberto Leinardi : PyCharm plugin maintainer -- Ricardo Gemignani -- Piotr Idzik <65706193+vil02@users.noreply.github.com> -- Pieter Engelbrecht -- Philipp Albrecht (pylbrecht) -- Nicolas Dickreuter -- Nick Bastin -- Nathaniel Manista : suspicious lambda checking -- Maksym Humetskyi (mhumetskyi) - * Fixed ignored empty functions by similarities checker with "ignore-signatures" option enabled - * Ignore function decorators signatures as well by similarities checker with "ignore-signatures" option enabled - * Ignore class methods and nested functions signatures as well by similarities checker with "ignore-signatures" option enabled -- Kylian -- Konstantin Manna -- Kai Mueller <15907922+kasium@users.noreply.github.com> -- Joshua Cannon -- John Leach -- James Morgensen : ignored-modules option applies to import errors. -- Jaehoon Hwang (jaehoonhwang) -- Huw Jones -- Gideon <87426140+GideonBear@users.noreply.github.com> -- Ganden Schaffner -- Frost Ming -- Federico Bond -- Erik Wright -- Erik Eriksson : Added overlapping-except error check. -- Emmanuel Ferdman -- Dave Bunten -- Daniel Wang -- Daniel Mouritzen -- Dan Hemberger <846186+hemberger@users.noreply.github.com> -- Chris Rebert : unidiomatic-typecheck. -- Aurelien Campeas -- Alexander Pervakov -- Alain Leufroy -- Akhil Kamat -- Adam Williamson -- Aaron Liu -- xmo-odoo -- tbennett0 -- purajit <7026198+purajit@users.noreply.github.com> -- omarandlorraine <64254276+omarandlorraine@users.noreply.github.com> -- craig-sh -- bernie gray -- azinneck0485 <123660683+azinneck0485@users.noreply.github.com> -- Wing Lian -- Wes Turner (Google): added new check 'inconsistent-quotes' -- Tyler Thieding -- Tobias Hernstig <30827238+thernstig@users.noreply.github.com> -- Smixi -- Simu Toni -- Sergei Lebedev <185856+superbobry@users.noreply.github.com> -- Scott Worley -- Saugat Pachhai -- Samuel FORESTIER -- Rémi Cardona -- Ryan Ozawa -- Roger Sheu <78449574+rogersheu@users.noreply.github.com> -- Raphael Gaschignard -- Ram Rachum (cool-RR) -- Radostin Stoyanov -- Peter Bittner -- Paul Renvoisé -- PHeanEX -- Omega Weapon -- Nikolai Kristiansen -- Nick Pesce -- Nedelcu Ioan-Andrei <138256980+nedelcu-ioan@users.noreply.github.com> -- Nathan Marrow -- Mikhail Fesenko -- Matthew Suozzo -- Matthew Beckers <17108752+mattlbeck@users.noreply.github.com> (mattlbeck) -- Mark Roman Miller : fix inline defs in too-many-statements -- MalanB -- Mads Kiilerich -- Maarten ter Huurne -- Lefteris Karapetsas -- LCD 47 -- Jérome Perrin -- Justin Li -- John Kirkham -- Jens H. Nielsen -- Jake Lishman -- Ioana Tagirta : fix bad thread instantiation check -- Ikraduya Edian : Added new checks 'consider-using-generator' and 'use-a-generator'. -- Hugues Bruant -- Hashem Nasarat -- Harut -- Grygorii Iermolenko -- Grizzly Nyo -- Gabriel R. Sezefredo : Fixed "exception-escape" false positive with generators -- Filipe Brandenburger -- Fantix King (UChicago) -- Eric McDonald <221418+emcd@users.noreply.github.com> -- Elias Dorneles : minor adjust to config defaults and docs -- Elazrod56 -- Edward K. Ream -- Derek Harland -- David Pursehouse -- Daniel Miller -- Christoph Blessing <33834216+cblessing24@users.noreply.github.com> -- Chris Murray -- Chris Lamb -- Charles Hebert -- Carli Freudenberg (CarliJoy) - * Fixed issue 5281, added Unicode checker - * Improve non-ascii-name checker -- Bruce Dawson -- Brian Shaginaw : prevent error on exception check for functions -- Benny Mueller -- Ben James -- Ben Green -- Batuhan Taskaya -- Artem Yurchenko -- Alexander Kapshuna -- Akshay Choudhary <153769403+Akshay9715@users.noreply.github.com> -- Adam Parkin -- 谭九鼎 <109224573@qq.com> -- Łukasz Sznuk -- zasca -- y2kbugger -- vinnyrose -- ttenhoeve-aa -- thinwybk -- temyurchenko <44875844+temyurchenko@users.noreply.github.com> -- syutbai -- sur.la.route <17788706+christopherpickering@users.noreply.github.com> -- sdet_liang -- pavan-msys <149513767+pavan-msys@users.noreply.github.com> -- paschich -- oittaa <8972248+oittaa@users.noreply.github.com> -- nyabkun <75878387+nyabkun@users.noreply.github.com> -- nhdsd -- moxian -- mar-chi-pan -- lrjball <50599110+lrjball@users.noreply.github.com> -- levon-d -- laike9m -- kyoto7250 <50972773+kyoto7250@users.noreply.github.com> -- kriek -- kdestin <101366538+kdestin@users.noreply.github.com> -- jaydesl <35102795+jaydesl@users.noreply.github.com> -- jab -- gracejiang16 <70730457+gracejiang16@users.noreply.github.com> -- glmdgrielson <32415403+glmdgrielson@users.noreply.github.com> -- glegoux -- gaurikholkar -- flyingbot91 -- fly -- fahhem -- fadedDexofan -- epenet <6771947+epenet@users.noreply.github.com> -- danields -- cosven -- cordis-dev -- cherryblossom <31467609+cherryblossom000@users.noreply.github.com> -- bluesheeptoken -- anatoly techtonik -- amelenty -- akirchhoff-modular -- agutole -- Zeckie <49095968+Zeckie@users.noreply.github.com> -- Zeb Nicholls - * Made W9011 compatible with 'of' syntax in return types -- Yuval Langer -- Yury Gribov -- Yuri Bochkarev : Added epytext support to docparams extension. -- Youngsoo Sung -- Yory <39745367+yory8@users.noreply.github.com> -- Yoichi Nakayama -- Yeting Li (yetingli) -- Yannack -- Yann Dirson -- Yang Yang -- Xi Shen -- Winston H <56998716+winstxnhdw@users.noreply.github.com> -- Will Shanks -- Viorel Știrbu : intern-builtin warning. -- VictorT -- Victor Jiajunsu <16359131+jiajunsu@users.noreply.github.com> -- ViRuSTriNiTy -- Val Lorentz -- Ulrich Eckhardt -- Udi Fuchs -- Trevor Bekolay - * Added --list-msgs-enabled command -- Tomer Chachamu : simplifiable-if-expression -- Tomasz Michalski -- Tomasz Magulski -- Tom -- Tim Hatch -- Tim Gates -- Tianyu Chen <124018391+UTsweetyfish@users.noreply.github.com> -- Théo Battrel -- Thomas Benhamou -- Theodore Ni <3806110+tjni@users.noreply.github.com> -- Tanvi Moharir <74228962+tanvimoharir@users.noreply.github.com>: Fix for invalid toml config -- T.Rzepka -- Svetoslav Neykov -- SubaruArai <78188579+SubaruArai@users.noreply.github.com> -- Stéphane Wirtel : nonlocal-without-binding -- Stephen Longofono <8992396+SLongofono@users.noreply.github.com> -- Stephane Odul <1504511+sodul@users.noreply.github.com> -- Stanislav Levin -- Sorin Sbarnea -- Slavfox -- Skip Montanaro -- Sigurd Spieckermann <2206639+sisp@users.noreply.github.com> -- Shiv Venkatasubrahmanyam -- Sebastian Müller -- Sayyed Faisal Ali <80758388+C0DE-SLAYER@users.noreply.github.com> -- Sasha Bagan -- Sardorbek Imomaliev -- Santiago Castro -- Samuel Freilich (sfreilich) -- Sam Vermeiren <88253337+PaaEl@users.noreply.github.com> -- Ryan McGuire -- Ry4an Brase -- Ruro -- Roshan Shetty -- Roman Ivanov -- Robert Schweizer -- Reverb Chu -- Renat Galimov -- Rebecca Turner (9999years) -- Randall Leeds -- Ranadheer Gorrepati <35244169+ranadheerg@users.noreply.github.com> -- Ramon Saraiva -- Ramiro Leal-Cavazos (ramiro050): Fixed bug preventing pylint from working with Emacs tramp -- RSTdefg <34202999+RSTdefg@users.noreply.github.com> -- R. N. West <98110034+rnwst@users.noreply.github.com> -- Qwiddle13 <32040075+Qwiddle13@users.noreply.github.com> -- Quentin Young -- Prajwal Borkar -- Petr Pulc : require whitespace around annotations -- Peter Dawyndt -- Peter Dave Hello -- Peter Aronoff -- Paul Cochrane -- Patrik -- Pascal Corpet -- Pablo Galindo Salgado - * Fix false positive 'Non-iterable value' with async comprehensions. -- Osher De Paz -- Oisín Moran -- Obscuron -- Noam Yorav-Raphael -- Noah-Agnel <138210920+Noah-Agnel@users.noreply.github.com> -- Nir Soffer -- Niko Wenselowski -- Nikita Sobolev -- Nick Smith -- Neowizard -- Ned Batchelder -- Natalie Serebryakova -- Naglis Jonaitis <827324+naglis@users.noreply.github.com> -- Moody -- Mitchell Young : minor adjustment to docparams -- Mitar -- Ming Lyu -- Mikhail f. Shiryaev -- Mike Fiedler (miketheman) -- Mike Bryant -- Mike Bernard -- Michka Popoff -- Michal Vasilek -- Michael Scott Cuthbert -- Michael Kefeder -- Michael K -- Michael Hudson-Doyle -- Michael Giuffrida -- Melvin Hazeleger <31448155+melvio@users.noreply.github.com> -- Meltem Kenis -- Mehdi Drissi -- Matěj Grabovský -- Matthijs Blom <19817960+MatthijsBlom@users.noreply.github.com> -- Matej Spiller Muys -- Matej Marušák -- Marzuk Rashid -- Markus Siebenhaar <41283549+siehar@users.noreply.github.com> -- Marco Edward Gorelli : Documented Jupyter integration -- Marcin Kurczewski (rr-) -- Maik Röder -- Lumír 'Frenzy' Balhar -- Ludovic Aubry -- Louis Sautier -- Lorena Buciu <46202743+lorena-b@users.noreply.github.com> -- Logan Miller <14319179+komodo472@users.noreply.github.com> -- Kári Tristan Helgason -- Kurian Benoy <70306694+kurianbenoy-aot@users.noreply.github.com> -- Krzysztof Czapla -- Kraig Brockschmidt -- Kound -- KotlinIsland <65446343+KotlinIsland@users.noreply.github.com> -- Kosarchuk Sergey -- Konrad Weihmann <46938494+priv-kweihmann@users.noreply.github.com> -- Kian Meng, Ang -- Kevin Phillips -- Kevin Jing Qiu -- Kenneth Schackart -- Kayran Schmidt <59456929+yumasheta@users.noreply.github.com> -- Karthik Nadig -- Jürgen Hermann -- Josselin Feist -- Jonathan Kotta -- John Paraskevopoulos : add 'differing-param-doc' and 'differing-type-doc' -- John McGehee -- John Gabriele -- John Belmonte -- Johannes Maron -- Joffrey Mander -- Jochen Preusche -- Jeroen Seegers : - * Fixed `toml` dependency issue -- Jeremy Fleischman -- Jason Owen -- Jason Lau -- Jared Garst -- Jared Deckard -- Janne Rönkkö -- Jamie Scott -- James Sinclair -- James M. Allen -- James Lingard -- James Broadhead -- Jakub Kulík -- Jakob Normark -- Jacques Kvam -- Jace Browning : updated default report format with clickable paths -- JZ -- JT Olds -- Iggy Eom -- Ige-kun <178478713+Ige-kun@users.noreply.github.com> -- Hayden Richards <62866982+SupImDos@users.noreply.github.com> - * Fixed "no-self-use" for async methods - * Fixed "docparams" extension for async functions and methods -- Harshil <37377066+harshil21@users.noreply.github.com> -- Harry -- Gwanbin Park -- Grégoire <96051754+gregoire-mullvad@users.noreply.github.com> -- Grant Welch -- Giuseppe Valente -- Gary Tyler McLeod -- Felix von Drigalski -- Felix Preuschoff <37065638+felixp98@users.noreply.github.com> -- Fabrice Douchant -- Fabio Natali -- Fabian Damken -- Eric Froemling -- Emmanuel Chaudron -- Elizabeth Bott <52465744+elizabethbott@users.noreply.github.com> -- Ekin Dursun -- Eisuke Kawashima -- Edgemaster -- Eddie Darling -- Drew Risinger -- Dr. Nick -- Don Kirkby -- Don Jayamanne -- Dominic Lavery -- Dmytro Kyrychuk -- Dionisio E Alonso -- DetachHead <57028336+DetachHead@users.noreply.github.com> -- Dennis Keck <26092524+fellhorn@users.noreply.github.com> -- Denis Laxalde -- David Lawson -- David Cain -- Danny Hermes -- Daniele Procida -- Daniela Plascencia -- Daniel Werner -- Daniel R. Neal (danrneal) -- Daniel Draper -- Daniel Dorani (doranid) -- Daniel Brookman <53625739+dbrookman@users.noreply.github.com> -- Dan Garrette -- Damien Nozay -- Cubicpath -- Craig Citro -- Cosmo -- Clément Schreiner -- Clément Pit-Claudel -- Christopher Zurcher -- ChandanChainani -- Carl Crowder : don't evaluate the value of arguments for 'dangerous-default-value' -- Carey Metcalfe : demoted `try-except-raise` from error to warning -- Cameron Olechowski -- Calin Don -- Caio Carrara -- C.A.M. Gerlach -- Bruno P. Kinoshita -- Brice Chardin -- Brian C. Lane -- Brandon W Maister -- BioGeek -- Berker ŞAL -- Benjamin Partzsch <32679788+bnjmnp@users.noreply.github.com> -- Benjamin Graham -- Benedikt Morbach -- Ben Greiner -- Barak Shoshany -- Banjamin Freeman -- Ayushi Kotiyal <70513726+Ayushikotiyal@users.noreply.github.com> -- Avram Lubkin -- Athos Ribeiro : Fixed dict-keys-not-iterating false positive for inverse containment checks -- Arun Persaud -- Arthur Lutz -- Antonio Ossa -- Antonio Gámiz Delgado <73933988+antoniogamizbadger@users.noreply.github.com> -- Anthony VEREZ -- Anthony Tan -- Anthony Foglia (Google): Added simple string slots check. -- Anentropic -- Andy Young -- Andy Palmer <25123779+ninezerozeronine@users.noreply.github.com> -- Andrzej Klajnert -- Andrew Howe -- Andres Perez Hortal -- Andre Hora -- Aman Salwan <121633121+AmanSal1@users.noreply.github.com> -- Alok Singh <8325708+alok@users.noreply.github.com> -- Allan Chandler <95424144+allanc65@users.noreply.github.com> (allanc65) - * Fixed issue 5452, false positive missing-param-doc for multi-line Google-style params -- Alex Waygood -- Alex Mor <5476113+nashcontrol@users.noreply.github.com> -- Alex Jurkiewicz -- Alex Hearn -- Alex Fortin -- Aleksander Mamla -- Alan Evangelista -- Alan Chan -- Aivar Annamaa -- Aidan Haase <44787650+haasea@users.noreply.github.com> -- Ahirnish Pareek : 'keyword-arg-before-var-arg' check -- Agustin Marquez -- Adrian Chirieac -- Aditya Gupta (adityagupta1089) - * Added ignore_signatures to duplicate checker -- Adam Tuft <73994535+adamtuft@users.noreply.github.com> -- Adam Dangoor -- 243f6a88 85a308d3 <33170174+243f6a8885a308d313198a2e037@users.noreply.github.com> - - -Co-Author ---------- -The following persons were credited manually but did not commit themselves -under this name, or we did not manage to find their commits in the history. - -- Agustin Toledo -- Amaury Forgeot d'Arc: check names imported from a module exists in the module -- Anthony Tan -- Axel Muller -- Benjamin Niemann: allow block level enabling/disabling of messages -- Bernard Nauwelaerts -- Bill Wendling -- Brian van den Broek: windows installation documentation -- Craig Henriques -- D. Alphus (Alphadelta14) -- Daniil Kharkov -- Eero Vuojolahti -- Fabio Zadrozny -- Gauthier Sebaux -- James DesLauriers -- manderj -- Mirko Friedenhagen -- Nicholas Smith -- Nuzula H. Yudaka (Nuzhuka) -- Pek Chhan -- Peter Hammond -- Pierre Rouleau -- Richard Goodman: simplifiable-if-expression (with Tomer Chachamu) -- Sebastian Ulrich -- Takashi Hirashima -- Thomas Snowden: fix missing-docstring for inner functions -- Wolfgang Grafen -- Yannick Brehon diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE deleted file mode 100644 index 8c4c849..0000000 --- a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/licenses/LICENSE +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt b/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt deleted file mode 100644 index 7fb0ea1..0000000 --- a/.venv/lib/python3.10/site-packages/pylint-4.0.4.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pylint diff --git a/.venv/lib/python3.10/site-packages/pylint/__init__.py b/.venv/lib/python3.10/site-packages/pylint/__init__.py deleted file mode 100644 index 3752a02..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/__init__.py +++ /dev/null @@ -1,119 +0,0 @@ -# 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__", - "modify_sys_path", - "run_pylint", - "run_pyreverse", - "run_symilar", - "version", -] - -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 - - sys.exit(PyreverseRun(argv or sys.argv[1:]).run()) - - -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.symilar import Run as SymilarRun - - SymilarRun(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) - - -def _catch_valueerror(unraisable: sys.UnraisableHookArgs) -> None: # pragma: no cover - """Overwrite sys.unraisablehook to catch incorrect ValueError. - - Python 3.12 introduced changes that sometimes cause astroid to emit ValueErrors - with 'generator already executing'. Fixed in Python 3.12.3 and 3.13. - - https://github.com/pylint-dev/pylint/issues/9138 - """ - if ( - isinstance(unraisable.exc_value, ValueError) - and unraisable.exc_value.args[0] == "generator already executing" - ): - return - - sys.__unraisablehook__(unraisable) - - -if (3, 12, 0) <= sys.version_info[:3] < (3, 12, 3) or sys.version_info >= (3, 12, 5): - sys.unraisablehook = _catch_valueerror - - -version = __version__ diff --git a/.venv/lib/python3.10/site-packages/pylint/__main__.py b/.venv/lib/python3.10/site-packages/pylint/__main__.py deleted file mode 100644 index 448ac55..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/__main__.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/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() diff --git a/.venv/lib/python3.10/site-packages/pylint/__pkginfo__.py b/.venv/lib/python3.10/site-packages/pylint/__pkginfo__.py deleted file mode 100644 index a8d436b..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/__pkginfo__.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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__ = "4.0.4" - - -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__) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/__init__.py b/.venv/lib/python3.10/site-packages/pylint/checkers/__init__.py deleted file mode 100644 index 78f453a..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/__init__.py +++ /dev/null @@ -1,140 +0,0 @@ -# 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 from and , - 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"), - ] - - # pylint: disable=possibly-used-before-assignment - 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", - "BaseRawFileChecker", - "BaseTokenChecker", - "DeprecatedMixin", - "initialize", - "register_plugins", -] diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py deleted file mode 100644 index 7c61f7f..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/async_checker.py +++ /dev/null @@ -1,97 +0,0 @@ -# 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 -import astroid.bases -import astroid.exceptions -from astroid import nodes - -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: - match inferred := checker_utils.safe_infer(ctx_mgr): - case _ if not inferred: - continue - case nodes.AsyncFunctionDef(): - # Check if we are dealing with a function decorated - # with contextlib.asynccontextmanager. - if decorated_with(inferred, self._async_generators): - continue - case astroid.bases.AsyncGenerator(): - # Check if we are dealing with a function decorated - # with contextlib.asynccontextmanager. - if decorated_with(inferred.parent, self._async_generators): - continue - case _: - 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)) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py b/.venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py deleted file mode 100644 index 2e19121..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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)) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py deleted file mode 100644 index 355697c..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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__ = [ - "KNOWN_NAME_TYPES_WITH_STYLE", - "AnyStyle", - "CamelCaseStyle", - "NameChecker", - "NamingStyle", - "PascalCaseStyle", - "SnakeCaseStyle", - "UpperCaseStyle", -] - -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.function_checker import FunctionChecker -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)) - linter.register_checker(FunctionChecker(linter)) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py deleted file mode 100644 index 774b7e8..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py +++ /dev/null @@ -1,962 +0,0 @@ -# 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 bases, 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"): - 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, - 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, 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, nodes.TypeAlias) - ) - ): - 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, nodes.TryStar)) - 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") - 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 - match call.func: - case nodes.Attribute(expr=nodes.Call()): - # Chained call, the intermediate call might - # return something else (but we don't check that, yet). - return - - 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.keywords, node.args.kwarg): - return - elif call.keywords: - return - - if node.args.vararg: - if self._has_variadic_argument(call.starargs, node.args.vararg): - return - elif call.starargs: - 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() - match call_node.func.expr: - case nodes.Call(func=nodes.Name(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()): - match name: - case "exec": - self.add_message("exec-used", node=node) - case "reversed": - self._check_reversed(node) - case "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.""" - match node.test: - case nodes.Tuple(elts=elts) if len(elts) > 0: - self.add_message("assert-on-tuple", node=node, confidence=HIGH) - case nodes.Const(value=str() as val): - when = "never" if val else "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: - match k: - case nodes.Const(): - key = k.value - case nodes.Attribute(): - key = k.as_string() - case _: - 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: - match argument: - case util.UninferableBase(): - return - case 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 - - case nodes.List() | nodes.Tuple(): - return - - case astroid.Instance() if not self._py38_plus: - # 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 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 itertools.pairwise(pairs): - 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 - - match node.value: - case nodes.Name(): - if len(targets) != 1: - return - rhs_names = [node.value] - case 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]) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py deleted file mode 100644 index a4e9208..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py +++ /dev/null @@ -1,647 +0,0 @@ -# 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 - -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__",)) -FORWARD_REF_QNAME = {"typing.ForwardRef", "annotationlib.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 (nodes.Break): the break node of interest. - - Returns: - nodes.For or nodes.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 (nodes.For, nodes.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 - - -def _extract_register_target(dec: nodes.NodeNG) -> nodes.NodeNG | None: - """ - If decorator `dec` looks like `@func.register(...)` or `@func.register`, - return the `func` target node (Name or Attribute). Otherwise return None. - """ - if isinstance(dec, nodes.Call): - func_part = dec.func - if isinstance(func_part, nodes.Attribute) and func_part.attrname == "register": - return func_part.expr - return None - - if isinstance(dec, nodes.Attribute) and dec.attrname == "register": - return dec.expr - - return None - - -def _inferred_has_singledispatchmethod(target: nodes.NodeNG) -> bool: - """ - Infer `target` and return True if the inferred object has a - @singledispatchmethod decorator. - """ - inferred = utils.safe_infer(target) - if not inferred: - return False - - if isinstance(inferred, (nodes.FunctionDef, nodes.AsyncFunctionDef)): - decorators = inferred.decorators - if isinstance(decorators, nodes.Decorators): - for dec in decorators.nodes: - inferred_dec = utils.safe_infer(dec) - if ( - inferred_dec - and inferred_dec.qname() == "functools.singledispatchmethod" - ): - return True - - return False - - -def _is_singledispatchmethod_registration(node: nodes.FunctionDef) -> bool: - """ - Return True if `node` is a function decorated like: - - @func.register(...) - def _(…): ... - - where `func` is a singledispatchmethod (i.e. its base was decorated - with @singledispatchmethod). - """ - decorators = node.decorators - if not decorators: - return False - - for dec in decorators.nodes: - target = _extract_register_target(dec) - if target is None: - continue - - if _inferred_has_singledispatchmethod(target): - 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 %r 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.", - ), - "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)}, - ), - "W0136": ( - "'continue' discouraged inside 'finally' clause", - "continue-in-finally", - "Emitted when the `continue` keyword is found " - "inside a finally clause. This will raise a SyntaxWarning " - "starting in Python 3.14.", - ), - "W0137": ( - "'break' discouraged inside 'finally' clause", - "break-in-finally", - "Emitted when the `break` keyword is found " - "inside a finally clause. This will raise a SyntaxWarning " - "starting in Python 3.14.", - ), - } - - @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(): - match elem: - case nodes.Tuple(): - return self._too_many_starred_for_tuple(elem) - case 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: - match assign_target := node.targets[0]: - case nodes.Starred(): - # Check *a = b - self.add_message("invalid-star-assignment-target", node=node) - case nodes.Tuple(): - # Check *a, *b = ... - 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 = {} - for arg in node.args.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", "break-in-finally") - 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) - ): - self.add_message("continue-in-finally", node=node) - if ( - isinstance(parent, nodes.Try) - and node in parent.finalbody - and isinstance(node, nodes.Break) - ): - self.add_message("break-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 - - if _is_singledispatchmethod_registration(node): - return - - # Skip typing.overload() functions. - if utils.is_overload_stub(node): - return - - # Exempt functions redefined on a condition. - if isinstance(node.parent, nodes.If): - match node.parent.test: - case nodes.UnaryOp(op="not", operand=nodes.Name(name=name)) if ( - name == node.name - ): - # Exempt "if not " cases - return - case nodes.Compare( - left=nodes.Name(name=name), - ops=[["is", nodes.Const(value=None)]], - ) if ( - name == node.name - ): - # Exempt "if is not None" cases - 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() in FORWARD_REF_QNAME - ): - return - - self.add_message( - "function-redefined", - node=node, - args=(redeftype, defined_self.fromlineno), - ) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py deleted file mode 100644 index 395a7a3..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py +++ /dev/null @@ -1,352 +0,0 @@ -# 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: - match node: - case nodes.Call(args=[nodes.Const(value=str() as value)]) if ( - value.lower() == "nan" - ): - return node.inferred()[0].pytype() == "builtins.float" # type: ignore[no-any-return] - return False - except AttributeError: - return False - - def _is_numpy_nan(node: nodes.NodeNG) -> bool: - match node: - case nodes.Attribute(attrname="NaN", expr=nodes.Name(name=name)): - return name in {"numpy", "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.""" - match literal: - case nodes.Const(value=bool() | None): - # Not interested in these values. - return - case nodes.Const(value=bytes() | str() | int() | float()): - pass - case nodes.List() | nodes.Dict() | nodes.Set(): - # Inline list, dict and set nodes - pass - case _: - return - - 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.as_string(), operator, right_operand.as_string()), - 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=node, left=left, right=right) - elif isinstance(left, nodes.Name) and _is_one_arg_pos_call(right): - # transforming Y == type(x) case to type(x) == Y - self._check_type_x_is_y(node=node, left=right, right=left) - - def _check_type_x_is_y( - self, node: nodes.Compare, left: nodes.NodeNG, 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 _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) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py deleted file mode 100644 index 710c98b..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py +++ /dev/null @@ -1,203 +0,0 @@ -# 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 isinstance(docstring, nodes.Const): - return str(docstring.value) - return None - - -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": "", - "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": "", - "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 - match node.body: - case [nodes.Expr(value=nodes.Call() as value), *_]: - # Most likely a string with a format call. Let's see. - match utils.safe_infer(value.func): - case astroid.BoundMethod( - bound=astroid.Instance(name="str" | "unicode" | "bytes") - ): - # Strings. - return - match node_type: - case "module": - message = "missing-module-docstring" - case "class": - message = "missing-class-docstring" - case _: - 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 - ) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py deleted file mode 100644 index 2826b40..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/base/function_checker.py +++ /dev/null @@ -1,149 +0,0 @@ -# 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 - -"""Function checker for Python code.""" - -from __future__ import annotations - -from itertools import chain - -from astroid import nodes - -from pylint.checkers import utils -from pylint.checkers.base.basic_checker import _BasicChecker - - -class FunctionChecker(_BasicChecker): - """Check if a function definition handles possible side effects.""" - - msgs = { - "W0135": ( - "The context used in function %r will not be exited.", - "contextmanager-generator-missing-cleanup", - "Used when a contextmanager is used inside a generator function" - " and the cleanup is not handled.", - ) - } - - @utils.only_required_for_messages("contextmanager-generator-missing-cleanup") - def visit_functiondef(self, node: nodes.FunctionDef) -> None: - self._check_contextmanager_generator_missing_cleanup(node) - - @utils.only_required_for_messages("contextmanager-generator-missing-cleanup") - def visit_asyncfunctiondef(self, node: nodes.AsyncFunctionDef) -> None: - self._check_contextmanager_generator_missing_cleanup(node) - - def _check_contextmanager_generator_missing_cleanup( - self, node: nodes.FunctionDef - ) -> None: - """Check a FunctionDef to find if it is a generator - that uses a contextmanager internally. - - If it is, check if the contextmanager is properly cleaned up. Otherwise, add message. - - :param node: FunctionDef node to check - :type node: nodes.FunctionDef - """ - # if function does not use a Yield statement, it can't be a generator - with_nodes = list(node.nodes_of_class(nodes.With)) - if not with_nodes: - return - # check for Yield inside the With statement - yield_nodes = list( - chain.from_iterable( - with_node.nodes_of_class(nodes.Yield) for with_node in with_nodes - ) - ) - if not yield_nodes: - return - - # infer the call that yields a value, and check if it is a contextmanager - for with_node in with_nodes: - for call, held in with_node.items: - if held is None: - # if we discard the value, then we can skip checking it - continue - - # safe infer is a generator - inferred_node = getattr(utils.safe_infer(call), "parent", None) - if not isinstance(inferred_node, nodes.FunctionDef): - continue - if self._node_fails_contextmanager_cleanup(inferred_node, yield_nodes): - self.add_message( - "contextmanager-generator-missing-cleanup", - node=with_node, - args=(node.name,), - ) - - @staticmethod - def _node_fails_contextmanager_cleanup( - node: nodes.FunctionDef, yield_nodes: list[nodes.Yield] - ) -> bool: - """Check if a node fails contextmanager cleanup. - - Current checks for a contextmanager: - - only if the context manager yields a non-constant value - - only if the context manager lacks a finally, or does not catch GeneratorExit - - only if some statement follows the yield, some manually cleanup happens - - :param node: Node to check - :type node: nodes.FunctionDef - :return: True if fails, False otherwise - :param yield_nodes: List of Yield nodes in the function body - :type yield_nodes: list[nodes.Yield] - :rtype: bool - """ - - def check_handles_generator_exceptions(try_node: nodes.Try) -> bool: - # needs to handle either GeneratorExit, Exception, or bare except - for handler in try_node.handlers: - if handler.type is None: - # handles all exceptions (bare except) - return True - inferred = utils.safe_infer(handler.type) - if inferred and inferred.qname() in { - "builtins.GeneratorExit", - "builtins.Exception", - }: - return True - return False - - # if context manager yields a non-constant value, then continue checking - if any( - yield_node.value is None or isinstance(yield_node.value, nodes.Const) - for yield_node in yield_nodes - ): - return False - - # Check if yield expression is last statement - yield_nodes = list(node.nodes_of_class(nodes.Yield)) - if len(yield_nodes) == 1: - n = yield_nodes[0].parent - while n is not node: - if n.next_sibling() is not None: - break - n = n.parent - else: - # No next statement found - return False - - # if function body has multiple Try, filter down to the ones that have a yield node - try_with_yield_nodes = [ - try_node - for try_node in node.nodes_of_class(nodes.Try) - if any(try_node.nodes_of_class(nodes.Yield)) - ] - if not try_with_yield_nodes: - # no try blocks at all, so checks after this line do not apply - return True - # if the contextmanager has a finally block, then it is fine - if all(try_node.finalbody for try_node in try_with_yield_nodes): - return False - # if the contextmanager catches GeneratorExit, then it is fine - if all( - check_handles_generator_exceptions(try_node) - for try_node in try_with_yield_nodes - ): - return False - return True diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py deleted file mode 100644 index 9339807..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# 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__ = [ - "KNOWN_NAME_TYPES_WITH_STYLE", - "AnyStyle", - "CamelCaseStyle", - "NameChecker", - "NamingStyle", - "PascalCaseStyle", - "SnakeCaseStyle", - "UpperCaseStyle", -] - -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, -) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py deleted file mode 100644 index 9b3ed0e..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py +++ /dev/null @@ -1,804 +0,0 @@ -# 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, cast - -import astroid -from astroid import bases, nodes, util -from astroid.typing import InferenceResult - -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)?(? 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 and 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 not (node_type == "method" and 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": "", - "help": "Good variable names which should always be accepted," - " separated by a comma.", - }, - ), - ( - "good-names-rgxs", - { - "default": "", - "type": "regexp_csv", - "metavar": "", - "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": "", - "help": "Bad variable names which should always be refused, " - "separated by a comma.", - }, - ), - ( - "bad-names-rgxs", - { - "default": "", - "type": "regexp_csv", - "metavar": "", - "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": "", - "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": "", - "help": "Include a hint for the correct naming format with invalid-name.", - }, - ), - ( - "property-classes", - { - "default": ("abc.abstractproperty",), - "type": "csv", - "metavar": "", - "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) - ) and not utils.is_assign_name_annotated_with(anodes[0], "Final"): - 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,too-many-statements - 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) - - elif isinstance(assign_type, nodes.TypeVar): - self._check_name("typevar", node.name, node) - - elif isinstance(assign_type, nodes.ParamSpec): - self._check_name("paramspec", node.name, node) - - elif isinstance(assign_type, nodes.TypeVarTuple): - self._check_name("typevartuple", node.name, node) - - elif isinstance(assign_type, nodes.TypeAlias): - self._check_name("typealias", node.name, node) - - # Check names defined in module scope - elif isinstance(frame, nodes.Module): - # Check names defined in AnnAssign nodes - if isinstance(assign_type, nodes.AnnAssign) and self._assigns_typealias( - assign_type.annotation - ): - self._check_name("typealias", node.name, node) - - # Check names defined in Assign nodes - elif isinstance(assign_type, (nodes.Assign, nodes.AnnAssign)): - inferred_assign_type = ( - utils.safe_infer(assign_type.value) if assign_type.value else None - ) - - # Check TypeVar's and TypeAliases assigned alone or in tuple assignment - if isinstance(node.parent, nodes.Assign): - if typevar_node_type := self._assigns_typevar(assign_type.value): - self._check_name( - typevar_node_type, 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 typevar_node_type := self._assigns_typevar(assigner): - self._check_name( - typevar_node_type, - 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 - - elif inferred_assign_type in (None, util.Uninferable): - return - - # Check classes (TypeVar's are classes so they need to be excluded first) - elif self._should_check_class_regex(inferred_assign_type): - self._check_name("class", node.name, node) - - # Don't emit if the name redefines an import in an ImportError except handler - # nor any other reassignment. - elif ( - not (redefines_import := _redefines_import(node)) - and not isinstance( - inferred_assign_type, (nodes.FunctionDef, nodes.Lambda) - ) - and not utils.is_reassigned_before_current(node, node.name) - and not utils.is_reassigned_after_current(node, node.name) - and not utils.get_node_first_ancestor_of_type( - node, (nodes.For, nodes.While) - ) - ): - if not self._meets_exception_for_non_consts( - inferred_assign_type, node.name - ): - self._check_name("const", node.name, node) - else: - node_type = "variable" - iattrs = tuple(node.frame().igetattr(node.name)) - if ( - util.Uninferable in iattrs - and self._name_regexps["const"].match(node.name) is not None - ): - return - # Do the exclusive assignment analysis on attrs, not iattrs. - # iattrs locations could be anywhere (inference result). - attrs = tuple(node.frame().getattr(node.name)) - if len(attrs) > 1 and all( - astroid.are_exclusive(*combo) - for combo in itertools.combinations(attrs, 2) - ): - node_type = "const" - if not self._meets_exception_for_non_consts( - inferred_assign_type, node.name - ): - self._check_name( - node_type, - node.name, - node, - disallowed_check_only=redefines_import, - ) - - # 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) and not any( - frame.local_attr_ancestors(node.name) - ): - 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 _meets_exception_for_non_consts( - self, inferred_assign_type: InferenceResult | None, name: str - ) -> bool: - if isinstance(inferred_assign_type, nodes.Const): - return False - regexp = self._name_regexps["variable"] - return regexp.match(name) is not None - - def _should_check_class_regex( - self, inferred_assign_type: InferenceResult | None - ) -> bool: - if isinstance(inferred_assign_type, nodes.ClassDef): - return True - if isinstance(inferred_assign_type, bases.Instance) and { - "EnumMeta", - "TypedDict", - }.intersection( - { - ancestor.name - for ancestor in cast(InferenceResult, inferred_assign_type).mro() - } - ): - return True - if ( - isinstance(inferred_assign_type, nodes.FunctionDef) - and inferred_assign_type.qname() == "typing.Annotated" - ): - return True - return False - - def _recursive_check_names(self, args: list[nodes.AssignName]) -> None: - """Check names in a possibly recursive list .""" - 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) -> str | None: - """Check if a node is assigning a TypeVar and return TypeVar type.""" - if isinstance(node, nodes.Call): - inferred = utils.safe_infer(node.func) - if isinstance(inferred, nodes.ClassDef): - qname = inferred.qname() - for typevar_node_typ, qnames in TYPE_VAR_QNAMES.items(): - if qname in qnames: - return typevar_node_typ - return None - - @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, bases.UnionType)): - qname = inferred.qname() - if qname == "typing.TypeAlias": - return True - if qname in {".Union", "builtins.Union", "builtins.UnionType"}: - # 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.""" - variance: TypeVarVariance = TypeVarVariance.invariant - match node.parent: - case nodes.Assign(): - keywords = node.assign_type().value.keywords - args = node.assign_type().value.args - case 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 - case _: # PEP 695 generic type nodes - keywords = () - args = () - variance = TypeVarVariance.inferred - - 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 - - match variance: - case TypeVarVariance.inferred: - # Ignore variance check for PEP 695 type parameters. - # The variance is inferred by the type checker. - # Adding _co or _contra suffix can help to reason about TypeVar. - pass - case 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, - ) - case TypeVarVariance.covariant if 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, - ) - case TypeVarVariance.contravariant if 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, - ) - case TypeVarVariance.invariant if name.endswith(("_co", "_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, - ) diff --git a/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py b/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py deleted file mode 100644 index 41f7bb8..0000000 --- a/.venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py +++ /dev/null @@ -1,187 +0,0 @@ -# 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", - "paramspec", - "typevartuple", - "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": "