summaryrefslogtreecommitdiff
path: root/venv/Lib/site-packages
diff options
context:
space:
mode:
authorSann Yay Aye2020-01-30 12:57:51 +0530
committerSann Yay Aye2020-01-30 12:57:51 +0530
commit190966e010e321e4df56d40104ec80467a870e53 (patch)
treef97ac913ec59a975ad64d5a3cd61e11923d98a69 /venv/Lib/site-packages
parent7ecaa6f103b2755dc3bb3fae10a0d7ab28162596 (diff)
downloadChemical-Simulator-GUI-190966e010e321e4df56d40104ec80467a870e53.tar.gz
Chemical-Simulator-GUI-190966e010e321e4df56d40104ec80467a870e53.tar.bz2
Chemical-Simulator-GUI-190966e010e321e4df56d40104ec80467a870e53.zip
undo&redo_implementation
Diffstat (limited to 'venv/Lib/site-packages')
-rw-r--r--venv/Lib/site-packages/__pycache__/mccabe.cpython-37.pycbin0 -> 11136 bytes
-rw-r--r--venv/Lib/site-packages/__pycache__/six.cpython-37.pycbin0 -> 26864 bytes
-rw-r--r--venv/Lib/site-packages/astroid-2.3.3.dist-info/COPYING339
-rw-r--r--venv/Lib/site-packages/astroid-2.3.3.dist-info/COPYING.LESSER510
-rw-r--r--venv/Lib/site-packages/astroid-2.3.3.dist-info/INSTALLER1
-rw-r--r--venv/Lib/site-packages/astroid-2.3.3.dist-info/METADATA117
-rw-r--r--venv/Lib/site-packages/astroid-2.3.3.dist-info/RECORD145
-rw-r--r--venv/Lib/site-packages/astroid-2.3.3.dist-info/WHEEL5
-rw-r--r--venv/Lib/site-packages/astroid-2.3.3.dist-info/top_level.txt1
-rw-r--r--venv/Lib/site-packages/astroid/__init__.py166
-rw-r--r--venv/Lib/site-packages/astroid/__pkginfo__.py51
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/__init__.cpython-37.pycbin0 -> 4434 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/__pkginfo__.cpython-37.pycbin0 -> 1357 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/_ast.cpython-37.pycbin0 -> 1444 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/arguments.cpython-37.pycbin0 -> 7017 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/as_string.cpython-37.pycbin0 -> 26379 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/bases.cpython-37.pycbin0 -> 15625 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/builder.cpython-37.pycbin0 -> 12292 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/context.cpython-37.pycbin0 -> 4277 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/decorators.cpython-37.pycbin0 -> 3518 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/exceptions.cpython-37.pycbin0 -> 9369 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/helpers.cpython-37.pycbin0 -> 7264 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/inference.cpython-37.pycbin0 -> 21439 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/manager.cpython-37.pycbin0 -> 9301 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/mixins.cpython-37.pycbin0 -> 5780 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/modutils.cpython-37.pycbin0 -> 17081 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/node_classes.cpython-37.pycbin0 -> 120480 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/nodes.cpython-37.pycbin0 -> 2054 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/objects.cpython-37.pycbin0 -> 8501 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/protocols.cpython-37.pycbin0 -> 16680 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/raw_building.cpython-37.pycbin0 -> 11290 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/rebuilder.cpython-37.pycbin0 -> 39150 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/scoped_nodes.cpython-37.pycbin0 -> 70867 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/test_utils.cpython-37.pycbin0 -> 2502 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/transforms.cpython-37.pycbin0 -> 3436 bytes
-rw-r--r--venv/Lib/site-packages/astroid/__pycache__/util.cpython-37.pycbin0 -> 5704 bytes
-rw-r--r--venv/Lib/site-packages/astroid/_ast.py49
-rw-r--r--venv/Lib/site-packages/astroid/arguments.py285
-rw-r--r--venv/Lib/site-packages/astroid/as_string.py633
-rw-r--r--venv/Lib/site-packages/astroid/bases.py542
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_argparse.cpython-37.pycbin0 -> 1063 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_attrs.cpython-37.pycbin0 -> 1575 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_builtin_inference.cpython-37.pycbin0 -> 19796 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_collections.cpython-37.pycbin0 -> 2492 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_crypt.cpython-37.pycbin0 -> 850 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_curses.cpython-37.pycbin0 -> 3368 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_dataclasses.cpython-37.pycbin0 -> 1278 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_dateutil.cpython-37.pycbin0 -> 682 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_fstrings.cpython-37.pycbin0 -> 1565 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_functools.cpython-37.pycbin0 -> 4591 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_gi.cpython-37.pycbin0 -> 4007 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_hashlib.cpython-37.pycbin0 -> 1924 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_http.cpython-37.pycbin0 -> 10289 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_io.cpython-37.pycbin0 -> 1305 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_mechanize.cpython-37.pycbin0 -> 718 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_multiprocessing.cpython-37.pycbin0 -> 2520 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_namedtuple_enum.cpython-37.pycbin0 -> 11452 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_nose.cpython-37.pycbin0 -> 2047 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-37.pycbin0 -> 623 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-37.pycbin0 -> 1029 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-37.pycbin0 -> 1711 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-37.pycbin0 -> 1261 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-37.pycbin0 -> 7661 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_umath.cpython-37.pycbin0 -> 5199 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_ndarray.cpython-37.pycbin0 -> 8323 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-37.pycbin0 -> 3232 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_utils.cpython-37.pycbin0 -> 1736 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_pkg_resources.cpython-37.pycbin0 -> 2189 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_pytest.cpython-37.pycbin0 -> 2159 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_qt.cpython-37.pycbin0 -> 2092 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_random.cpython-37.pycbin0 -> 2172 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_re.cpython-37.pycbin0 -> 1066 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_six.cpython-37.pycbin0 -> 5521 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_ssl.cpython-37.pycbin0 -> 3600 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_subprocess.cpython-37.pycbin0 -> 3401 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_threading.cpython-37.pycbin0 -> 737 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_typing.cpython-37.pycbin0 -> 2337 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/__pycache__/brain_uuid.cpython-37.pycbin0 -> 629 bytes
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_argparse.py33
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_attrs.py65
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_builtin_inference.py829
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_collections.py74
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_crypt.py26
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_curses.py179
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_dataclasses.py50
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_dateutil.py28
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_fstrings.py51
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_functools.py158
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_gi.py220
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_hashlib.py67
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_http.py201
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_io.py45
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_mechanize.py29
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_multiprocessing.py106
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_namedtuple_enum.py449
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_nose.py77
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py23
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_numpy_core_function_base.py29
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_numpy_core_multiarray.py55
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_numpy_core_numeric.py43
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_numpy_core_numerictypes.py250
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_numpy_core_umath.py105
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_numpy_ndarray.py153
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_numpy_random_mtrand.py70
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_numpy_utils.py56
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_pkg_resources.py75
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_pytest.py88
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_qt.py82
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_random.py75
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_re.py36
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_six.py200
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_ssl.py74
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_subprocess.py111
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_threading.py31
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_typing.py96
-rw-r--r--venv/Lib/site-packages/astroid/brain/brain_uuid.py20
-rw-r--r--venv/Lib/site-packages/astroid/builder.py435
-rw-r--r--venv/Lib/site-packages/astroid/context.py179
-rw-r--r--venv/Lib/site-packages/astroid/decorators.py141
-rw-r--r--venv/Lib/site-packages/astroid/exceptions.py230
-rw-r--r--venv/Lib/site-packages/astroid/helpers.py273
-rw-r--r--venv/Lib/site-packages/astroid/inference.py943
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/__init__.py0
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/__pycache__/__init__.cpython-37.pycbin0 -> 184 bytes
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/__pycache__/dunder_lookup.cpython-37.pycbin0 -> 2137 bytes
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/__pycache__/objectmodel.cpython-37.pycbin0 -> 25143 bytes
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/_import/__init__.py0
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/__init__.cpython-37.pycbin0 -> 192 bytes
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/spec.cpython-37.pycbin0 -> 9399 bytes
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/util.cpython-37.pycbin0 -> 408 bytes
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/_import/spec.py344
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/_import/util.py10
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/dunder_lookup.py66
-rw-r--r--venv/Lib/site-packages/astroid/interpreter/objectmodel.py738
-rw-r--r--venv/Lib/site-packages/astroid/manager.py337
-rw-r--r--venv/Lib/site-packages/astroid/mixins.py160
-rw-r--r--venv/Lib/site-packages/astroid/modutils.py698
-rw-r--r--venv/Lib/site-packages/astroid/node_classes.py4775
-rw-r--r--venv/Lib/site-packages/astroid/nodes.py175
-rw-r--r--venv/Lib/site-packages/astroid/objects.py282
-rw-r--r--venv/Lib/site-packages/astroid/protocols.py766
-rw-r--r--venv/Lib/site-packages/astroid/raw_building.py468
-rw-r--r--venv/Lib/site-packages/astroid/rebuilder.py1090
-rw-r--r--venv/Lib/site-packages/astroid/scoped_nodes.py2836
-rw-r--r--venv/Lib/site-packages/astroid/test_utils.py73
-rw-r--r--venv/Lib/site-packages/astroid/transforms.py90
-rw-r--r--venv/Lib/site-packages/astroid/util.py164
-rw-r--r--venv/Lib/site-packages/colorama-0.4.3.dist-info/INSTALLER1
-rw-r--r--venv/Lib/site-packages/colorama-0.4.3.dist-info/LICENSE.txt27
-rw-r--r--venv/Lib/site-packages/colorama-0.4.3.dist-info/METADATA411
-rw-r--r--venv/Lib/site-packages/colorama-0.4.3.dist-info/RECORD18
-rw-r--r--venv/Lib/site-packages/colorama-0.4.3.dist-info/WHEEL6
-rw-r--r--venv/Lib/site-packages/colorama-0.4.3.dist-info/top_level.txt1
-rw-r--r--venv/Lib/site-packages/colorama/__init__.py6
-rw-r--r--venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-37.pycbin0 -> 425 bytes
-rw-r--r--venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-37.pycbin0 -> 3323 bytes
-rw-r--r--venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-37.pycbin0 -> 7579 bytes
-rw-r--r--venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-37.pycbin0 -> 1644 bytes
-rw-r--r--venv/Lib/site-packages/colorama/__pycache__/win32.cpython-37.pycbin0 -> 3838 bytes
-rw-r--r--venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-37.pycbin0 -> 4586 bytes
-rw-r--r--venv/Lib/site-packages/colorama/ansi.py102
-rw-r--r--venv/Lib/site-packages/colorama/ansitowin32.py257
-rw-r--r--venv/Lib/site-packages/colorama/initialise.py80
-rw-r--r--venv/Lib/site-packages/colorama/win32.py152
-rw-r--r--venv/Lib/site-packages/colorama/winterm.py169
-rw-r--r--venv/Lib/site-packages/isort-4.3.21.dist-info/INSTALLER1
-rw-r--r--venv/Lib/site-packages/isort-4.3.21.dist-info/LICENSE21
-rw-r--r--venv/Lib/site-packages/isort-4.3.21.dist-info/METADATA697
-rw-r--r--venv/Lib/site-packages/isort-4.3.21.dist-info/RECORD30
-rw-r--r--venv/Lib/site-packages/isort-4.3.21.dist-info/WHEEL6
-rw-r--r--venv/Lib/site-packages/isort-4.3.21.dist-info/entry_points.txt9
-rw-r--r--venv/Lib/site-packages/isort-4.3.21.dist-info/top_level.txt1
-rw-r--r--venv/Lib/site-packages/isort/__init__.py28
-rw-r--r--venv/Lib/site-packages/isort/__main__.py9
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/__init__.cpython-37.pycbin0 -> 1597 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/__main__.cpython-37.pycbin0 -> 348 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/finders.cpython-37.pycbin0 -> 11987 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/hooks.cpython-37.pycbin0 -> 3208 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/isort.cpython-37.pycbin0 -> 29949 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/main.cpython-37.pycbin0 -> 16724 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/natural.cpython-37.pycbin0 -> 2308 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/pie_slice.cpython-37.pycbin0 -> 5008 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/pylama_isort.cpython-37.pycbin0 -> 1009 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/settings.cpython-37.pycbin0 -> 12274 bytes
-rw-r--r--venv/Lib/site-packages/isort/__pycache__/utils.cpython-37.pycbin0 -> 1600 bytes
-rw-r--r--venv/Lib/site-packages/isort/finders.py382
-rw-r--r--venv/Lib/site-packages/isort/hooks.py91
-rw-r--r--venv/Lib/site-packages/isort/isort.py1060
-rw-r--r--venv/Lib/site-packages/isort/main.py401
-rw-r--r--venv/Lib/site-packages/isort/natural.py47
-rw-r--r--venv/Lib/site-packages/isort/pie_slice.py154
-rw-r--r--venv/Lib/site-packages/isort/pylama_isort.py29
-rw-r--r--venv/Lib/site-packages/isort/settings.py356
-rw-r--r--venv/Lib/site-packages/isort/utils.py53
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/AUTHORS.rst10
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/INSTALLER1
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/LICENSE21
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/METADATA166
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/RECORD20
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/WHEEL5
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/top_level.txt1
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/__init__.py23
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/__pycache__/__init__.cpython-37.pycbin0 -> 564 bytes
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/__pycache__/_version.cpython-37.pycbin0 -> 209 bytes
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/__pycache__/compat.cpython-37.pycbin0 -> 441 bytes
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/__pycache__/simple.cpython-37.pycbin0 -> 7764 bytes
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/__pycache__/slots.cpython-37.pycbin0 -> 15235 bytes
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/__pycache__/utils.cpython-37.pycbin0 -> 784 bytes
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/_version.py4
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/cext.cp37-win_amd64.pydbin0 -> 31744 bytes
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/compat.py9
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/simple.py246
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/slots.py414
-rw-r--r--venv/Lib/site-packages/lazy_object_proxy/utils.py13
-rw-r--r--venv/Lib/site-packages/mccabe-0.6.1.dist-info/DESCRIPTION.rst152
-rw-r--r--venv/Lib/site-packages/mccabe-0.6.1.dist-info/INSTALLER1
-rw-r--r--venv/Lib/site-packages/mccabe-0.6.1.dist-info/METADATA178
-rw-r--r--venv/Lib/site-packages/mccabe-0.6.1.dist-info/RECORD10
-rw-r--r--venv/Lib/site-packages/mccabe-0.6.1.dist-info/WHEEL6
-rw-r--r--venv/Lib/site-packages/mccabe-0.6.1.dist-info/entry_points.txt3
-rw-r--r--venv/Lib/site-packages/mccabe-0.6.1.dist-info/metadata.json1
-rw-r--r--venv/Lib/site-packages/mccabe-0.6.1.dist-info/top_level.txt1
-rw-r--r--venv/Lib/site-packages/mccabe.py347
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/__pycache__/__init__.cpython-37.pycbin0 -> 215 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/__pycache__/__main__.cpython-37.pycbin0 -> 467 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/__init__.cpython-37.pycbin0 -> 1859 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/build_env.cpython-37.pycbin0 -> 7472 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/cache.cpython-37.pycbin0 -> 7067 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/configuration.cpython-37.pycbin0 -> 9852 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/download.cpython-37.pycbin0 -> 21188 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/exceptions.cpython-37.pycbin0 -> 11759 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/index.cpython-37.pycbin0 -> 25269 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/locations.cpython-37.pycbin0 -> 4446 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/pep425tags.cpython-37.pycbin0 -> 8178 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/pyproject.cpython-37.pycbin0 -> 3195 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/resolve.cpython-37.pycbin0 -> 9113 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/wheel.cpython-37.pycbin0 -> 25946 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/__init__.cpython-37.pycbin0 -> 290 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/autocompletion.cpython-37.pycbin0 -> 5103 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/base_command.cpython-37.pycbin0 -> 7831 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/cmdoptions.cpython-37.pycbin0 -> 16906 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/main_parser.cpython-37.pycbin0 -> 2376 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/parser.cpython-37.pycbin0 -> 8954 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/status_codes.cpython-37.pycbin0 -> 419 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/__init__.cpython-37.pycbin0 -> 2518 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/check.cpython-37.pycbin0 -> 1343 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/completion.cpython-37.pycbin0 -> 3092 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/configuration.cpython-37.pycbin0 -> 6444 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/download.cpython-37.pycbin0 -> 4731 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/freeze.cpython-37.pycbin0 -> 2888 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/hash.cpython-37.pycbin0 -> 2082 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/help.cpython-37.pycbin0 -> 1258 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/install.cpython-37.pycbin0 -> 12475 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/list.cpython-37.pycbin0 -> 8724 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/search.cpython-37.pycbin0 -> 4324 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/show.cpython-37.pycbin0 -> 5905 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/uninstall.cpython-37.pycbin0 -> 2714 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/wheel.cpython-37.pycbin0 -> 5017 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/__init__.cpython-37.pycbin0 -> 278 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/candidate.cpython-37.pycbin0 -> 1327 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/format_control.cpython-37.pycbin0 -> 2281 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/index.cpython-37.pycbin0 -> 1182 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/link.cpython-37.pycbin0 -> 5016 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/__init__.cpython-37.pycbin0 -> 214 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/check.cpython-37.pycbin0 -> 3644 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/freeze.cpython-37.pycbin0 -> 5629 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/prepare.cpython-37.pycbin0 -> 10290 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/__init__.cpython-37.pycbin0 -> 1711 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/constructors.cpython-37.pycbin0 -> 7631 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_file.cpython-37.pycbin0 -> 9209 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_install.cpython-37.pycbin0 -> 25046 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_set.cpython-37.pycbin0 -> 6046 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_tracker.cpython-37.pycbin0 -> 3160 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_uninstall.cpython-37.pycbin0 -> 17003 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/__init__.cpython-37.pycbin0 -> 209 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/appdirs.cpython-37.pycbin0 -> 8056 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/compat.cpython-37.pycbin0 -> 6157 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/deprecation.cpython-37.pycbin0 -> 2583 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/encoding.cpython-37.pycbin0 -> 1286 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/filesystem.cpython-37.pycbin0 -> 678 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/glibc.cpython-37.pycbin0 -> 1697 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/hashes.cpython-37.pycbin0 -> 3616 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/logging.cpython-37.pycbin0 -> 7843 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/misc.cpython-37.pycbin0 -> 25748 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/models.cpython-37.pycbin0 -> 1958 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/outdated.cpython-37.pycbin0 -> 4094 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/packaging.cpython-37.pycbin0 -> 2630 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/setuptools_build.cpython-37.pycbin0 -> 404 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/temp_dir.cpython-37.pycbin0 -> 4931 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/typing.cpython-37.pycbin0 -> 1353 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/ui.cpython-37.pycbin0 -> 12315 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/__init__.cpython-37.pycbin0 -> 15400 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/bazaar.cpython-37.pycbin0 -> 3817 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/git.cpython-37.pycbin0 -> 9459 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/mercurial.cpython-37.pycbin0 -> 3790 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/subversion.cpython-37.pycbin0 -> 6002 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/__init__.cpython-37.pycbin0 -> 2897 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/appdirs.cpython-37.pycbin0 -> 20626 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/pyparsing.cpython-37.pycbin0 -> 220624 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/retrying.cpython-37.pycbin0 -> 8107 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/six.cpython-37.pycbin0 -> 26431 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-37.pycbin0 -> 567 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-37.pycbin0 -> 3053 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/cache.cpython-37.pycbin0 -> 1783 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/compat.cpython-37.pycbin0 -> 774 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/controller.cpython-37.pycbin0 -> 7653 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-37.pycbin0 -> 2171 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-37.pycbin0 -> 4255 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-37.pycbin0 -> 675 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-37.pycbin0 -> 311 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-37.pycbin0 -> 3245 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-37.pycbin0 -> 1567 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/certifi/__pycache__/__init__.cpython-37.pycbin0 -> 274 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/certifi/__pycache__/core.cpython-37.pycbin0 -> 535 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/__init__.cpython-37.pycbin0 -> 861 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/big5freq.cpython-37.pycbin0 -> 27196 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/big5prober.cpython-37.pycbin0 -> 1137 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/chardistribution.cpython-37.pycbin0 -> 6323 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-37.pycbin0 -> 2244 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/charsetprober.cpython-37.pycbin0 -> 3454 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-37.pycbin0 -> 2901 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/compat.cpython-37.pycbin0 -> 372 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/cp949prober.cpython-37.pycbin0 -> 1144 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/enums.cpython-37.pycbin0 -> 2635 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/escprober.cpython-37.pycbin0 -> 2622 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/escsm.cpython-37.pycbin0 -> 7083 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-37.pycbin0 -> 2430 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-37.pycbin0 -> 12080 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euckrprober.cpython-37.pycbin0 -> 1145 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-37.pycbin0 -> 27200 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euctwprober.cpython-37.pycbin0 -> 1145 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-37.pycbin0 -> 19124 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-37.pycbin0 -> 1153 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-37.pycbin0 -> 2987 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/jisfreq.cpython-37.pycbin0 -> 22152 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/jpcntx.cpython-37.pycbin0 -> 38031 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-37.pycbin0 -> 23645 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-37.pycbin0 -> 29101 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-37.pycbin0 -> 23603 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-37.pycbin0 -> 22232 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-37.pycbin0 -> 22211 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-37.pycbin0 -> 22234 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/latin1prober.cpython-37.pycbin0 -> 2944 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-37.pycbin0 -> 2249 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-37.pycbin0 -> 1140 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcssm.cpython-37.pycbin0 -> 15695 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-37.pycbin0 -> 3002 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-37.pycbin0 -> 1630 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sjisprober.cpython-37.pycbin0 -> 2456 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/universaldetector.cpython-37.pycbin0 -> 5846 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/utf8prober.cpython-37.pycbin0 -> 1987 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/version.cpython-37.pycbin0 -> 456 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/__init__.cpython-37.pycbin0 -> 461 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/ansi.cpython-37.pycbin0 -> 3359 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-37.pycbin0 -> 7615 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/initialise.cpython-37.pycbin0 -> 1680 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/win32.cpython-37.pycbin0 -> 3874 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/winterm.cpython-37.pycbin0 -> 4622 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/__init__.cpython-37.pycbin0 -> 1059 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/compat.cpython-37.pycbin0 -> 32069 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/resources.cpython-37.pycbin0 -> 10903 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/scripts.cpython-37.pycbin0 -> 11093 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/util.cpython-37.pycbin0 -> 47971 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/__init__.cpython-37.pycbin0 -> 1330 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-37.pycbin0 -> 13777 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-37.pycbin0 -> 22668 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-37.pycbin0 -> 41569 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_utils.cpython-37.pycbin0 -> 3322 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/constants.cpython-37.pycbin0 -> 66234 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/html5parser.cpython-37.pycbin0 -> 97831 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/serializer.cpython-37.pycbin0 -> 10847 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-37.pycbin0 -> 443 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-37.pycbin0 -> 1526 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/datrie.cpython-37.pycbin0 -> 2045 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-37.pycbin0 -> 2248 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-37.pycbin0 -> 3338 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-37.pycbin0 -> 11261 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-37.pycbin0 -> 11870 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-37.pycbin0 -> 4015 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/__init__.cpython-37.pycbin0 -> 275 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/core.cpython-37.pycbin0 -> 9078 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/idnadata.cpython-37.pycbin0 -> 21449 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/intranges.cpython-37.pycbin0 -> 1815 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/package_data.cpython-37.pycbin0 -> 229 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/__init__.cpython-37.pycbin0 -> 9925 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/linklockfile.cpython-37.pycbin0 -> 2306 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/mkdirlockfile.cpython-37.pycbin0 -> 2668 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/__init__.cpython-37.pycbin0 -> 2087 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/_version.cpython-37.pycbin0 -> 236 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/exceptions.cpython-37.pycbin0 -> 2192 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/fallback.cpython-37.pycbin0 -> 24564 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/__about__.cpython-37.pycbin0 -> 749 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/__init__.cpython-37.pycbin0 -> 587 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/_compat.cpython-37.pycbin0 -> 1027 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/_structures.cpython-37.pycbin0 -> 2879 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/markers.cpython-37.pycbin0 -> 8882 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/requirements.cpython-37.pycbin0 -> 4000 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/specifiers.cpython-37.pycbin0 -> 19777 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/utils.cpython-37.pycbin0 -> 1465 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/version.cpython-37.pycbin0 -> 11969 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/__init__.cpython-37.pycbin0 -> 300 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/compat.cpython-37.pycbin0 -> 1036 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/wrappers.cpython-37.pycbin0 -> 5501 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-37.pycbin0 -> 96837 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-37.pycbin0 -> 660 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/__init__.cpython-37.pycbin0 -> 3928 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/bar.cpython-37.pycbin0 -> 2750 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/helpers.cpython-37.pycbin0 -> 3034 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/spinner.cpython-37.pycbin0 -> 1509 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/__init__.cpython-37.pycbin0 -> 394 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/core.cpython-37.pycbin0 -> 957 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/parser.cpython-37.pycbin0 -> 10074 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/test.cpython-37.pycbin0 -> 1257 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/utils.cpython-37.pycbin0 -> 2156 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/writer.cpython-37.pycbin0 -> 3591 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/__init__.cpython-37.pycbin0 -> 3505 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/__version__.cpython-37.pycbin0 -> 568 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/_internal_utils.cpython-37.pycbin0 -> 1326 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/adapters.cpython-37.pycbin0 -> 16903 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/api.cpython-37.pycbin0 -> 6509 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/auth.cpython-37.pycbin0 -> 8360 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/certs.cpython-37.pycbin0 -> 651 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/compat.cpython-37.pycbin0 -> 1630 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/cookies.cpython-37.pycbin0 -> 18805 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/exceptions.cpython-37.pycbin0 -> 5523 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/hooks.cpython-37.pycbin0 -> 998 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/models.cpython-37.pycbin0 -> 24126 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/packages.cpython-37.pycbin0 -> 528 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/sessions.cpython-37.pycbin0 -> 19446 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/status_codes.cpython-37.pycbin0 -> 4184 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/structures.cpython-37.pycbin0 -> 4397 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/utils.cpython-37.pycbin0 -> 22057 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/__init__.cpython-37.pycbin0 -> 2133 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/_collections.cpython-37.pycbin0 -> 10697 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/connection.cpython-37.pycbin0 -> 10166 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-37.pycbin0 -> 23671 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/exceptions.cpython-37.pycbin0 -> 10418 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/fields.cpython-37.pycbin0 -> 5886 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/filepost.cpython-37.pycbin0 -> 2778 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-37.pycbin0 -> 12738 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/request.cpython-37.pycbin0 -> 5600 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/response.cpython-37.pycbin0 -> 18765 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-37.pycbin0 -> 217 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-37.pycbin0 -> 1113 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-37.pycbin0 -> 4923 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-37.pycbin0 -> 331 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/__pycache__/six.cpython-37.pycbin0 -> 24419 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-37.pycbin0 -> 572 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-37.pycbin0 -> 1009 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/connection.cpython-37.pycbin0 -> 3184 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/queue.cpython-37.pycbin0 -> 1058 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/request.cpython-37.pycbin0 -> 3239 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/response.cpython-37.pycbin0 -> 1987 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/retry.cpython-37.pycbin0 -> 12673 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-37.pycbin0 -> 9565 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-37.pycbin0 -> 8788 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/url.cpython-37.pycbin0 -> 5198 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/wait.cpython-37.pycbin0 -> 3150 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/webencodings/__pycache__/__init__.cpython-37.pycbin0 -> 9693 bytes
-rw-r--r--venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/webencodings/__pycache__/labels.cpython-37.pycbin0 -> 4107 bytes
-rw-r--r--venv/Lib/site-packages/pylint-2.4.4.dist-info/COPYING340
-rw-r--r--venv/Lib/site-packages/pylint-2.4.4.dist-info/INSTALLER1
-rw-r--r--venv/Lib/site-packages/pylint-2.4.4.dist-info/METADATA202
-rw-r--r--venv/Lib/site-packages/pylint-2.4.4.dist-info/RECORD161
-rw-r--r--venv/Lib/site-packages/pylint-2.4.4.dist-info/WHEEL5
-rw-r--r--venv/Lib/site-packages/pylint-2.4.4.dist-info/entry_points.txt6
-rw-r--r--venv/Lib/site-packages/pylint-2.4.4.dist-info/top_level.txt1
-rw-r--r--venv/Lib/site-packages/pylint/__init__.py43
-rw-r--r--venv/Lib/site-packages/pylint/__main__.py7
-rw-r--r--venv/Lib/site-packages/pylint/__pkginfo__.py85
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/__init__.cpython-37.pycbin0 -> 1084 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/__main__.cpython-37.pycbin0 -> 210 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/__pkginfo__.cpython-37.pycbin0 -> 2632 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/config.cpython-37.pycbin0 -> 25815 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/constants.cpython-37.pycbin0 -> 1018 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/epylint.cpython-37.pycbin0 -> 4958 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/exceptions.cpython-37.pycbin0 -> 1361 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/graph.cpython-37.pycbin0 -> 5211 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/interfaces.cpython-37.pycbin0 -> 3665 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/lint.cpython-37.pycbin0 -> 45362 bytes
-rw-r--r--venv/Lib/site-packages/pylint/__pycache__/testutils.cpython-37.pycbin0 -> 9521 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__init__.py64
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/__init__.cpython-37.pycbin0 -> 1580 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/async.cpython-37.pycbin0 -> 2722 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/base.cpython-37.pycbin0 -> 61785 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/base_checker.cpython-37.pycbin0 -> 6481 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/classes.cpython-37.pycbin0 -> 44537 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-37.pycbin0 -> 11667 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/exceptions.cpython-37.pycbin0 -> 15668 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/format.cpython-37.pycbin0 -> 31580 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/imports.cpython-37.pycbin0 -> 25427 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/logging.cpython-37.pycbin0 -> 10919 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/misc.cpython-37.pycbin0 -> 4597 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/newstyle.cpython-37.pycbin0 -> 2422 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/python3.cpython-37.pycbin0 -> 34941 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-37.pycbin0 -> 3254 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/refactoring.cpython-37.pycbin0 -> 45321 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/similar.cpython-37.pycbin0 -> 12304 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/spelling.cpython-37.pycbin0 -> 9755 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/stdlib.cpython-37.pycbin0 -> 12738 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/strings.cpython-37.pycbin0 -> 17427 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/typecheck.cpython-37.pycbin0 -> 40274 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/utils.cpython-37.pycbin0 -> 31460 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/__pycache__/variables.cpython-37.pycbin0 -> 44587 bytes
-rw-r--r--venv/Lib/site-packages/pylint/checkers/async.py89
-rw-r--r--venv/Lib/site-packages/pylint/checkers/base.py2333
-rw-r--r--venv/Lib/site-packages/pylint/checkers/base_checker.py187
-rw-r--r--venv/Lib/site-packages/pylint/checkers/classes.py1844
-rw-r--r--venv/Lib/site-packages/pylint/checkers/design_analysis.py496
-rw-r--r--venv/Lib/site-packages/pylint/checkers/exceptions.py546
-rw-r--r--venv/Lib/site-packages/pylint/checkers/format.py1332
-rw-r--r--venv/Lib/site-packages/pylint/checkers/imports.py981
-rw-r--r--venv/Lib/site-packages/pylint/checkers/logging.py384
-rw-r--r--venv/Lib/site-packages/pylint/checkers/misc.py171
-rw-r--r--venv/Lib/site-packages/pylint/checkers/newstyle.py127
-rw-r--r--venv/Lib/site-packages/pylint/checkers/python3.py1398
-rw-r--r--venv/Lib/site-packages/pylint/checkers/raw_metrics.py119
-rw-r--r--venv/Lib/site-packages/pylint/checkers/refactoring.py1510
-rw-r--r--venv/Lib/site-packages/pylint/checkers/similar.py452
-rw-r--r--venv/Lib/site-packages/pylint/checkers/spelling.py411
-rw-r--r--venv/Lib/site-packages/pylint/checkers/stdlib.py452
-rw-r--r--venv/Lib/site-packages/pylint/checkers/strings.py755
-rw-r--r--venv/Lib/site-packages/pylint/checkers/typecheck.py1770
-rw-r--r--venv/Lib/site-packages/pylint/checkers/utils.py1253
-rw-r--r--venv/Lib/site-packages/pylint/checkers/variables.py1987
-rw-r--r--venv/Lib/site-packages/pylint/config.py913
-rw-r--r--venv/Lib/site-packages/pylint/constants.py43
-rw-r--r--venv/Lib/site-packages/pylint/epylint.py197
-rw-r--r--venv/Lib/site-packages/pylint/exceptions.py29
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__init__.py0
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/__init__.cpython-37.pycbin0 -> 181 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/_check_docs_utils.cpython-37.pycbin0 -> 18819 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/bad_builtin.cpython-37.pycbin0 -> 1967 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/broad_try_clause.cpython-37.pycbin0 -> 1702 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/check_docs.cpython-37.pycbin0 -> 685 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/check_elif.cpython-37.pycbin0 -> 2647 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/comparetozero.cpython-37.pycbin0 -> 1959 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/docparams.cpython-37.pycbin0 -> 14746 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/docstyle.cpython-37.pycbin0 -> 2503 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/emptystring.cpython-37.pycbin0 -> 2035 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/mccabe.cpython-37.pycbin0 -> 5579 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/overlapping_exceptions.cpython-37.pycbin0 -> 2604 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/__pycache__/redefined_variable_type.cpython-37.pycbin0 -> 3248 bytes
-rw-r--r--venv/Lib/site-packages/pylint/extensions/_check_docs_utils.py792
-rw-r--r--venv/Lib/site-packages/pylint/extensions/bad_builtin.py69
-rw-r--r--venv/Lib/site-packages/pylint/extensions/broad_try_clause.py59
-rw-r--r--venv/Lib/site-packages/pylint/extensions/check_docs.py23
-rw-r--r--venv/Lib/site-packages/pylint/extensions/check_elif.py77
-rw-r--r--venv/Lib/site-packages/pylint/extensions/comparetozero.py74
-rw-r--r--venv/Lib/site-packages/pylint/extensions/docparams.py536
-rw-r--r--venv/Lib/site-packages/pylint/extensions/docstyle.py89
-rw-r--r--venv/Lib/site-packages/pylint/extensions/emptystring.py74
-rw-r--r--venv/Lib/site-packages/pylint/extensions/mccabe.py196
-rw-r--r--venv/Lib/site-packages/pylint/extensions/overlapping_exceptions.py88
-rw-r--r--venv/Lib/site-packages/pylint/extensions/redefined_variable_type.py116
-rw-r--r--venv/Lib/site-packages/pylint/graph.py197
-rw-r--r--venv/Lib/site-packages/pylint/interfaces.py102
-rw-r--r--venv/Lib/site-packages/pylint/lint.py1817
-rw-r--r--venv/Lib/site-packages/pylint/message/__init__.py54
-rw-r--r--venv/Lib/site-packages/pylint/message/__pycache__/__init__.cpython-37.pycbin0 -> 664 bytes
-rw-r--r--venv/Lib/site-packages/pylint/message/__pycache__/message.cpython-37.pycbin0 -> 1225 bytes
-rw-r--r--venv/Lib/site-packages/pylint/message/__pycache__/message_definition.cpython-37.pycbin0 -> 2982 bytes
-rw-r--r--venv/Lib/site-packages/pylint/message/__pycache__/message_definition_store.cpython-37.pycbin0 -> 4075 bytes
-rw-r--r--venv/Lib/site-packages/pylint/message/__pycache__/message_handler_mix_in.cpython-37.pycbin0 -> 11049 bytes
-rw-r--r--venv/Lib/site-packages/pylint/message/__pycache__/message_id_store.cpython-37.pycbin0 -> 4925 bytes
-rw-r--r--venv/Lib/site-packages/pylint/message/message.py53
-rw-r--r--venv/Lib/site-packages/pylint/message/message_definition.py84
-rw-r--r--venv/Lib/site-packages/pylint/message/message_definition_store.py90
-rw-r--r--venv/Lib/site-packages/pylint/message/message_handler_mix_in.py393
-rw-r--r--venv/Lib/site-packages/pylint/message/message_id_store.py128
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/__init__.py8
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/__pycache__/__init__.cpython-37.pycbin0 -> 241 bytes
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/__pycache__/diadefslib.cpython-37.pycbin0 -> 7621 bytes
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/__pycache__/diagrams.cpython-37.pycbin0 -> 8716 bytes
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/__pycache__/inspector.cpython-37.pycbin0 -> 10187 bytes
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/__pycache__/main.cpython-37.pycbin0 -> 4522 bytes
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/__pycache__/utils.cpython-37.pycbin0 -> 5787 bytes
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/__pycache__/vcgutils.cpython-37.pycbin0 -> 4697 bytes
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/__pycache__/writer.cpython-37.pycbin0 -> 7286 bytes
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/diadefslib.py238
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/diagrams.py268
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/inspector.py357
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/main.py214
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/utils.py220
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/vcgutils.py229
-rw-r--r--venv/Lib/site-packages/pylint/pyreverse/writer.py213
-rw-r--r--venv/Lib/site-packages/pylint/reporters/__init__.py34
-rw-r--r--venv/Lib/site-packages/pylint/reporters/__pycache__/__init__.cpython-37.pycbin0 -> 823 bytes
-rw-r--r--venv/Lib/site-packages/pylint/reporters/__pycache__/base_reporter.cpython-37.pycbin0 -> 2767 bytes
-rw-r--r--venv/Lib/site-packages/pylint/reporters/__pycache__/collecting_reporter.cpython-37.pycbin0 -> 817 bytes
-rw-r--r--venv/Lib/site-packages/pylint/reporters/__pycache__/json_reporter.cpython-37.pycbin0 -> 2003 bytes
-rw-r--r--venv/Lib/site-packages/pylint/reporters/__pycache__/reports_handler_mix_in.cpython-37.pycbin0 -> 3028 bytes
-rw-r--r--venv/Lib/site-packages/pylint/reporters/__pycache__/text.cpython-37.pycbin0 -> 7263 bytes
-rw-r--r--venv/Lib/site-packages/pylint/reporters/base_reporter.py66
-rw-r--r--venv/Lib/site-packages/pylint/reporters/collecting_reporter.py21
-rw-r--r--venv/Lib/site-packages/pylint/reporters/json_reporter.py58
-rw-r--r--venv/Lib/site-packages/pylint/reporters/reports_handler_mix_in.py79
-rw-r--r--venv/Lib/site-packages/pylint/reporters/text.py247
-rw-r--r--venv/Lib/site-packages/pylint/reporters/ureports/__init__.py96
-rw-r--r--venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/__init__.cpython-37.pycbin0 -> 3065 bytes
-rw-r--r--venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/nodes.cpython-37.pycbin0 -> 6062 bytes
-rw-r--r--venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/text_writer.cpython-37.pycbin0 -> 3673 bytes
-rw-r--r--venv/Lib/site-packages/pylint/reporters/ureports/nodes.py188
-rw-r--r--venv/Lib/site-packages/pylint/reporters/ureports/text_writer.py94
-rw-r--r--venv/Lib/site-packages/pylint/testutils.py298
-rw-r--r--venv/Lib/site-packages/pylint/utils/__init__.py64
-rw-r--r--venv/Lib/site-packages/pylint/utils/__pycache__/__init__.cpython-37.pycbin0 -> 869 bytes
-rw-r--r--venv/Lib/site-packages/pylint/utils/__pycache__/ast_walker.cpython-37.pycbin0 -> 2078 bytes
-rw-r--r--venv/Lib/site-packages/pylint/utils/__pycache__/file_state.cpython-37.pycbin0 -> 3852 bytes
-rw-r--r--venv/Lib/site-packages/pylint/utils/__pycache__/utils.cpython-37.pycbin0 -> 10339 bytes
-rw-r--r--venv/Lib/site-packages/pylint/utils/ast_walker.py79
-rw-r--r--venv/Lib/site-packages/pylint/utils/file_state.py138
-rw-r--r--venv/Lib/site-packages/pylint/utils/utils.py371
-rw-r--r--venv/Lib/site-packages/six-1.14.0.dist-info/INSTALLER1
-rw-r--r--venv/Lib/site-packages/six-1.14.0.dist-info/LICENSE18
-rw-r--r--venv/Lib/site-packages/six-1.14.0.dist-info/METADATA49
-rw-r--r--venv/Lib/site-packages/six-1.14.0.dist-info/RECORD8
-rw-r--r--venv/Lib/site-packages/six-1.14.0.dist-info/WHEEL6
-rw-r--r--venv/Lib/site-packages/six-1.14.0.dist-info/top_level.txt1
-rw-r--r--venv/Lib/site-packages/six.py980
-rw-r--r--venv/Lib/site-packages/typed_ast-1.4.1.dist-info/INSTALLER1
-rw-r--r--venv/Lib/site-packages/typed_ast-1.4.1.dist-info/LICENSE290
-rw-r--r--venv/Lib/site-packages/typed_ast-1.4.1.dist-info/METADATA28
-rw-r--r--venv/Lib/site-packages/typed_ast-1.4.1.dist-info/RECORD18
-rw-r--r--venv/Lib/site-packages/typed_ast-1.4.1.dist-info/WHEEL5
-rw-r--r--venv/Lib/site-packages/typed_ast-1.4.1.dist-info/top_level.txt3
-rw-r--r--venv/Lib/site-packages/typed_ast/__init__.py1
-rw-r--r--venv/Lib/site-packages/typed_ast/__pycache__/__init__.cpython-37.pycbin0 -> 197 bytes
-rw-r--r--venv/Lib/site-packages/typed_ast/__pycache__/ast27.cpython-37.pycbin0 -> 12419 bytes
-rw-r--r--venv/Lib/site-packages/typed_ast/__pycache__/ast3.cpython-37.pycbin0 -> 13335 bytes
-rw-r--r--venv/Lib/site-packages/typed_ast/__pycache__/conversions.cpython-37.pycbin0 -> 7916 bytes
-rw-r--r--venv/Lib/site-packages/typed_ast/_ast27.cp37-win_amd64.pydbin0 -> 165888 bytes
-rw-r--r--venv/Lib/site-packages/typed_ast/_ast3.cp37-win_amd64.pydbin0 -> 186368 bytes
-rw-r--r--venv/Lib/site-packages/typed_ast/ast27.py324
-rw-r--r--venv/Lib/site-packages/typed_ast/ast3.py348
-rw-r--r--venv/Lib/site-packages/typed_ast/conversions.py232
-rw-r--r--venv/Lib/site-packages/typed_ast/tests/__pycache__/test_basics.cpython-37.pycbin0 -> 7451 bytes
-rw-r--r--venv/Lib/site-packages/typed_ast/tests/test_basics.py326
-rw-r--r--venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/PKG-INFO166
-rw-r--r--venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/SOURCES.txt10
-rw-r--r--venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/dependency_links.txt1
-rw-r--r--venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/installed-files.txt12
-rw-r--r--venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/top_level.txt1
-rw-r--r--venv/Lib/site-packages/wrapt/__init__.py16
-rw-r--r--venv/Lib/site-packages/wrapt/__pycache__/__init__.cpython-37.pycbin0 -> 984 bytes
-rw-r--r--venv/Lib/site-packages/wrapt/__pycache__/decorators.cpython-37.pycbin0 -> 8918 bytes
-rw-r--r--venv/Lib/site-packages/wrapt/__pycache__/importer.cpython-37.pycbin0 -> 4242 bytes
-rw-r--r--venv/Lib/site-packages/wrapt/__pycache__/wrappers.cpython-37.pycbin0 -> 24030 bytes
-rw-r--r--venv/Lib/site-packages/wrapt/decorators.py514
-rw-r--r--venv/Lib/site-packages/wrapt/importer.py230
-rw-r--r--venv/Lib/site-packages/wrapt/wrappers.py943
651 files changed, 62630 insertions, 0 deletions
diff --git a/venv/Lib/site-packages/__pycache__/mccabe.cpython-37.pyc b/venv/Lib/site-packages/__pycache__/mccabe.cpython-37.pyc
new file mode 100644
index 0000000..44dca75
--- /dev/null
+++ b/venv/Lib/site-packages/__pycache__/mccabe.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/__pycache__/six.cpython-37.pyc b/venv/Lib/site-packages/__pycache__/six.cpython-37.pyc
new file mode 100644
index 0000000..07b383b
--- /dev/null
+++ b/venv/Lib/site-packages/__pycache__/six.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid-2.3.3.dist-info/COPYING b/venv/Lib/site-packages/astroid-2.3.3.dist-info/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/venv/Lib/site-packages/astroid-2.3.3.dist-info/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, 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 Lesser 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 Street, 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.
+
+ <signature of Ty Coon>, 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 Lesser General
+Public License instead of this License.
diff --git a/venv/Lib/site-packages/astroid-2.3.3.dist-info/COPYING.LESSER b/venv/Lib/site-packages/astroid-2.3.3.dist-info/COPYING.LESSER
new file mode 100644
index 0000000..2d2d780
--- /dev/null
+++ b/venv/Lib/site-packages/astroid-2.3.3.dist-info/COPYING.LESSER
@@ -0,0 +1,510 @@
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard. To achieve this, non-free programs must
+be allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least
+ three years, to give the same user the materials specified in
+ Subsection 6a, above, for a charge no more than the cost of
+ performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms
+of the ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library.
+It is safest to attach them to the start of each source file to most
+effectively convey the exclusion of warranty; and each file should
+have at least the "copyright" line and a pointer to where the full
+notice is found.
+
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or
+your school, if any, to sign a "copyright disclaimer" for the library,
+if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James
+ Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/venv/Lib/site-packages/astroid-2.3.3.dist-info/INSTALLER b/venv/Lib/site-packages/astroid-2.3.3.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/Lib/site-packages/astroid-2.3.3.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/Lib/site-packages/astroid-2.3.3.dist-info/METADATA b/venv/Lib/site-packages/astroid-2.3.3.dist-info/METADATA
new file mode 100644
index 0000000..2805693
--- /dev/null
+++ b/venv/Lib/site-packages/astroid-2.3.3.dist-info/METADATA
@@ -0,0 +1,117 @@
+Metadata-Version: 2.1
+Name: astroid
+Version: 2.3.3
+Summary: An abstract syntax tree for Python with inference support.
+Home-page: https://github.com/PyCQA/astroid
+Author: Python Code Quality Authority
+Author-email: code-quality@python.org
+License: LGPL
+Platform: UNKNOWN
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Software Development :: Quality Assurance
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=3.5.*
+Requires-Dist: lazy-object-proxy (==1.4.*)
+Requires-Dist: six (~=1.12)
+Requires-Dist: wrapt (==1.11.*)
+Requires-Dist: typed-ast (<1.5,>=1.4.0) ; implementation_name == "cpython" and python_version < "3.8"
+
+Astroid
+=======
+
+.. image:: https://travis-ci.org/PyCQA/astroid.svg?branch=master
+ :target: https://travis-ci.org/PyCQA/astroid
+
+.. image:: https://ci.appveyor.com/api/projects/status/co3u42kunguhbh6l/branch/master?svg=true
+ :alt: AppVeyor Build Status
+ :target: https://ci.appveyor.com/project/PCManticore/astroid
+
+.. image:: https://coveralls.io/repos/github/PyCQA/astroid/badge.svg?branch=master
+ :target: https://coveralls.io/github/PyCQA/astroid?branch=master
+
+.. 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
+
+.. |tideliftlogo| image:: doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png
+ :width: 75
+ :height: 60
+ :alt: Tidelift
+
+.. list-table::
+ :widths: 10 100
+
+ * - |tideliftlogo|
+ - 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, older versions of astroid will still supported until 2020.
+
+Test
+----
+
+Tests are in the 'test' subdirectory. To launch the whole tests suite, you can use
+either `tox` or `pytest`::
+
+ tox
+ pytest astroid
+
+
diff --git a/venv/Lib/site-packages/astroid-2.3.3.dist-info/RECORD b/venv/Lib/site-packages/astroid-2.3.3.dist-info/RECORD
new file mode 100644
index 0000000..f5d983e
--- /dev/null
+++ b/venv/Lib/site-packages/astroid-2.3.3.dist-info/RECORD
@@ -0,0 +1,145 @@
+astroid-2.3.3.dist-info/COPYING,sha256=qxX9UmvY3Rip5368E5ZWv00z6X_HI4zRG_YOK5uGZsY,17987
+astroid-2.3.3.dist-info/COPYING.LESSER,sha256=qb3eVhbs3R6YC0TzYGAO6Hg7H5m4zIOivrFjoKOQ6GE,26527
+astroid-2.3.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+astroid-2.3.3.dist-info/METADATA,sha256=i0Ut5kY28jjA7pIT7o-_UbHKI5HbTXA0xQubIxcHO8w,3869
+astroid-2.3.3.dist-info/RECORD,,
+astroid-2.3.3.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92
+astroid-2.3.3.dist-info/top_level.txt,sha256=HsdW4O2x7ZXRj6k-agi3RaQybGLobI3VSE-jt4vQUXM,8
+astroid/__init__.py,sha256=tJJMsKzMv8hUgw3y0VQAAMx9BO-nrNUcNy_wI0XBFXo,5538
+astroid/__pkginfo__.py,sha256=vS7X-qu0abKFCIxjA0h9994nl1zj7Ziu3lEz9jniONU,2053
+astroid/__pycache__/__init__.cpython-37.pyc,,
+astroid/__pycache__/__pkginfo__.cpython-37.pyc,,
+astroid/__pycache__/_ast.cpython-37.pyc,,
+astroid/__pycache__/arguments.cpython-37.pyc,,
+astroid/__pycache__/as_string.cpython-37.pyc,,
+astroid/__pycache__/bases.cpython-37.pyc,,
+astroid/__pycache__/builder.cpython-37.pyc,,
+astroid/__pycache__/context.cpython-37.pyc,,
+astroid/__pycache__/decorators.cpython-37.pyc,,
+astroid/__pycache__/exceptions.cpython-37.pyc,,
+astroid/__pycache__/helpers.cpython-37.pyc,,
+astroid/__pycache__/inference.cpython-37.pyc,,
+astroid/__pycache__/manager.cpython-37.pyc,,
+astroid/__pycache__/mixins.cpython-37.pyc,,
+astroid/__pycache__/modutils.cpython-37.pyc,,
+astroid/__pycache__/node_classes.cpython-37.pyc,,
+astroid/__pycache__/nodes.cpython-37.pyc,,
+astroid/__pycache__/objects.cpython-37.pyc,,
+astroid/__pycache__/protocols.cpython-37.pyc,,
+astroid/__pycache__/raw_building.cpython-37.pyc,,
+astroid/__pycache__/rebuilder.cpython-37.pyc,,
+astroid/__pycache__/scoped_nodes.cpython-37.pyc,,
+astroid/__pycache__/test_utils.cpython-37.pyc,,
+astroid/__pycache__/transforms.cpython-37.pyc,,
+astroid/__pycache__/util.cpython-37.pyc,,
+astroid/_ast.py,sha256=6OGeHGRbK6oLmrsw6-UOpLFlIV1rStrA7BNpKGsu5Lw,1406
+astroid/arguments.py,sha256=cui-UmbEeywSk0eitSrOhi9F0Ci2clS4qYXTi8uXRs4,11783
+astroid/as_string.py,sha256=8SoRjh8UlDRWkbFMTvse9th8flPt6iu9xOcBip1s1f8,22411
+astroid/bases.py,sha256=G2Zs5OEHoshjLJT8e-ApDH9Q3EZtC27cKJ5yKf84_7w,18698
+astroid/brain/__pycache__/brain_argparse.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_attrs.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_builtin_inference.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_collections.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_crypt.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_curses.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_dataclasses.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_dateutil.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_fstrings.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_functools.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_gi.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_hashlib.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_http.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_io.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_mechanize.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_multiprocessing.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_namedtuple_enum.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_nose.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_numpy_core_umath.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_numpy_ndarray.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_numpy_utils.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_pkg_resources.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_pytest.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_qt.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_random.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_re.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_six.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_ssl.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_subprocess.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_threading.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_typing.cpython-37.pyc,,
+astroid/brain/__pycache__/brain_uuid.cpython-37.pyc,,
+astroid/brain/brain_argparse.py,sha256=VEeMCr3OIjHmCy35uc-kX6nJ5_NUOAimpGJMr6CChoA,1024
+astroid/brain/brain_attrs.py,sha256=k8zJqIXsIbQrncthrzyB5NtdPTktgVi9wG7nyl8xMzs,2208
+astroid/brain/brain_builtin_inference.py,sha256=Ttwr1Ekt1_czEF50uEjY0dA5S89WFqyyBl0sWPUaYnE,27206
+astroid/brain/brain_collections.py,sha256=8Vmsb9I19er3MycZtT6qWDrIMV_SEHtl87gTPC5qQHc,2651
+astroid/brain/brain_crypt.py,sha256=gA7Q4GVuAM4viuTGWM6SNTPQXv5Gr_mFapyKMTRcsJ0,875
+astroid/brain/brain_curses.py,sha256=tDnlCP1bEvleqCMz856yua9mM5um1p_JendFhT4rBFk,3303
+astroid/brain/brain_dataclasses.py,sha256=5WndOYSY0oi2v-Od6KdPte-FKt00LoNRH2riSB4S1os,1647
+astroid/brain/brain_dateutil.py,sha256=q2dyV2907Bw4n7m2W4EEdok3Ndv8NzeIQxAZwXBiS14,795
+astroid/brain/brain_fstrings.py,sha256=VKVMijgLE2pg2dtXM6GGFgONOxOg8qA9D5V6dYzWTbQ,2121
+astroid/brain/brain_functools.py,sha256=gGMs0cEMVXR9pRPeu3LqkMARE6yzymvC7pzmRbJCWIY,5400
+astroid/brain/brain_gi.py,sha256=-EpcKf9z3wT_7v0k0WXIZtgk3-213lkfUX9bxeKOM3Y,6810
+astroid/brain/brain_hashlib.py,sha256=cp30hX5HhWqbWG3zqcNu8N3aHGeQK4DPi4ac8owBonU,2163
+astroid/brain/brain_http.py,sha256=-cQohgE5uQ5eBBjjFg7P5c2OlganAK6yZOKA6EkKd6o,10317
+astroid/brain/brain_io.py,sha256=DJcTFMTexrsHaGg2-kHoXwonddu13ImT7NEjiF1xPiU,1470
+astroid/brain/brain_mechanize.py,sha256=xTBc-u2DMmMPeci7DVFs4L2T98DwwLF_Ob5YZviLPp8,889
+astroid/brain/brain_multiprocessing.py,sha256=4iLBXpB7Bgy_hGVx-xhV7spYKg5tc4OybIiBcuwNL7U,3017
+astroid/brain/brain_namedtuple_enum.py,sha256=JBRVBhPSicUAixPdeEerhnxeEJtVnS7T1FkVhvJcDZU,15722
+astroid/brain/brain_nose.py,sha256=kECw2jHmX0IUPX4Gx3XVGrflKGnlgPB79QHt6WU2cwQ,2211
+astroid/brain/brain_numpy_core_fromnumeric.py,sha256=_mtg-7jySDnDoxhtrNtimVZ_lbsm63jb7U0iqcBjgLY,626
+astroid/brain/brain_numpy_core_function_base.py,sha256=2jtHOa_RCMlig7UZVUWSmICFvotvu7bZKCdLZhbTc0Q,1173
+astroid/brain/brain_numpy_core_multiarray.py,sha256=e-igYgbLP8UhCq3VSlRhykhXyoMcO2M7UOcrbzfuWpQ,1890
+astroid/brain/brain_numpy_core_numeric.py,sha256=RP9L1GfhPBGK3KQeeDoo-OyFUvkVNksw0sc9a6t3NJ8,1389
+astroid/brain/brain_numpy_core_numerictypes.py,sha256=RBRdil8D5qtTj6yquQ6_JwYACKRM7vfh4p7nwy3MYLk,7706
+astroid/brain/brain_numpy_core_umath.py,sha256=GGTCDVNDKEAppXjjToNzawa8lpCFr9GEh0OY3eQulec,5279
+astroid/brain/brain_numpy_ndarray.py,sha256=GMDomYcpCfCoKa1amdtQPsdy_VMPot3QUaG9mxlApBk,8417
+astroid/brain/brain_numpy_random_mtrand.py,sha256=It76Xh4atuxwGtsHiXe4llvEKyKh0R5Wa7MgG5y5vVU,3284
+astroid/brain/brain_numpy_utils.py,sha256=NxY99MzQ-m2Md_nofdAU30DFmse2CjpgqfWvYoMDDOc,1622
+astroid/brain/brain_pkg_resources.py,sha256=S_5UED1Zg8ObEJumRdpYGnjxZzemh_G_NFj3p5NGPfc,2262
+astroid/brain/brain_pytest.py,sha256=RXaNUVqy2R0et0Upn4GJkVgq5SG8Pl7zLlhqQg8Xx3Q,2384
+astroid/brain/brain_qt.py,sha256=FXdziZGGzFRzukhZguFoMY4q6PSsp6ZhNJovpzDG_Kc,2464
+astroid/brain/brain_random.py,sha256=2RZY-QEXMNWp7E6h0l0-ke-DtjKTOFlTdjiQZi3XdQc,2432
+astroid/brain/brain_re.py,sha256=le7VJHUAf80HyE_aQCh7_8FyDVK6JwNWA--c9RaMVQ8,1128
+astroid/brain/brain_six.py,sha256=6QHcKXoYf8yMMXWkx3g3lK0kqB5OFeYcXwjUTdgWTMw,6146
+astroid/brain/brain_ssl.py,sha256=2quiZVA_BW8PWmkAsOuYanq9Hvb93LT7c9YVslw3r14,3634
+astroid/brain/brain_subprocess.py,sha256=iXuKDWsUJhJDdKLDm6N8EiBw78Pjn-Xw-UJFk5gvup0,3668
+astroid/brain/brain_threading.py,sha256=73Inb3j7Tps5LQDJDGZOgR-bawttS1rk1l0LUL1WR1o,818
+astroid/brain/brain_typing.py,sha256=iFw33beNCitCJjJNvccIY6SsFJcdKVDdl-56DxDioh0,2780
+astroid/brain/brain_uuid.py,sha256=flWrk1Ve7oqYrO8GTZ3To8RBYteRfYwvash-s9KiU9o,564
+astroid/builder.py,sha256=0wrC4-ausU_nEEkgI8LJTsrNFN_XCbOkqoG2DsKCsks,16023
+astroid/context.py,sha256=VsyUDVB1J9fk1o8MQoE4ygfC7gdNjVYVUD4Bhgs9JM0,5164
+astroid/decorators.py,sha256=m0v63YRiQKc66-g8ckkYeJ0d5cji8AhkUxFPbTfLVDc,4229
+astroid/exceptions.py,sha256=_IJRdLfyNSPVjxYgEd11Uu9XpdqE7uBCVOEIxt3ua70,7047
+astroid/helpers.py,sha256=3HOFwK0ieIoLu7JhrbM1r0zxPyDtTl2oNSv-tXQ2iRw,9170
+astroid/inference.py,sha256=0diHXE-ZGiWU9y31flQa3YZhg6-v4dZgD4PPFAlHJGc,33023
+astroid/interpreter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+astroid/interpreter/__pycache__/__init__.cpython-37.pyc,,
+astroid/interpreter/__pycache__/dunder_lookup.cpython-37.pyc,,
+astroid/interpreter/__pycache__/objectmodel.cpython-37.pyc,,
+astroid/interpreter/_import/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+astroid/interpreter/_import/__pycache__/__init__.cpython-37.pyc,,
+astroid/interpreter/_import/__pycache__/spec.cpython-37.pyc,,
+astroid/interpreter/_import/__pycache__/util.cpython-37.pyc,,
+astroid/interpreter/_import/spec.py,sha256=L48FismdLnk6wjyAzIzJocKVdkBmbQlJgxwzeJ2_luA,11318
+astroid/interpreter/_import/util.py,sha256=inubUz6F3_kaMFaeleKUW6E6wCMIPrhU882zvwEZ02I,255
+astroid/interpreter/dunder_lookup.py,sha256=dP-AZU_aGPNt03b1ttrMglxzeU3NtgnG0MfpSLPH6sg,2155
+astroid/interpreter/objectmodel.py,sha256=7wQbTJhoUwH89x3tBfaA9WLaudBjwKcNpsBPWBQM_7U,23935
+astroid/manager.py,sha256=p7YPLYupDzG05OxR8qqF4fWMJExFAGIjTbVunPT3ECQ,12998
+astroid/mixins.py,sha256=F2rv2Ow7AU3YT_2jitVJik95ZWRVK6hpf8BrkkspzUY,5571
+astroid/modutils.py,sha256=1mBU_-rZH5-9K4nXB9hPi4mesi-pdlDltM_A-OU3zec,23425
+astroid/node_classes.py,sha256=FVYqErzW6lEHEZz3x_ZsqpyR1nyNOvnt0_Oi86btwAQ,140093
+astroid/nodes.py,sha256=tzYNu1tTF8bemsDitnSj7RFjQR2hrwlMDTwAmULoU5A,2957
+astroid/objects.py,sha256=q6ffgYLpyHENUY8BtiZAPHhnz91LJbQFkuaQnrNtf7g,9879
+astroid/protocols.py,sha256=Y-Mupe42X_FrdDC6KwnLyUM4yByWicR_tfqaSGWopT0,26828
+astroid/raw_building.py,sha256=HKYGE5Ll3g0WKntVErqCacQFiyTa5OVuVieIhkvckbc,16808
+astroid/rebuilder.py,sha256=q1XtkOYkykbRhk2UXhuMGsnGZFMzCDxdvTaG4VEh6Mw,41835
+astroid/scoped_nodes.py,sha256=C-ZcmS7QNkIBGUb2wc-hbHaUtOvfcOkQxYhD8xPrwjQ,94141
+astroid/test_utils.py,sha256=Q9SsfJDCJqSdRzEkp_5i1xLGcbFDztqqkdRjjLH476o,2314
+astroid/transforms.py,sha256=1npwJWcQUSIjcpcWd1pc-dJhtHOyiboQHsETAIQd5co,3377
+astroid/util.py,sha256=jg5LnqbWSZTZP1KgpxGBuC6Lfwhn9Jb2T2TohXghmC0,4785
diff --git a/venv/Lib/site-packages/astroid-2.3.3.dist-info/WHEEL b/venv/Lib/site-packages/astroid-2.3.3.dist-info/WHEEL
new file mode 100644
index 0000000..3b5c403
--- /dev/null
+++ b/venv/Lib/site-packages/astroid-2.3.3.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.6)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/venv/Lib/site-packages/astroid-2.3.3.dist-info/top_level.txt b/venv/Lib/site-packages/astroid-2.3.3.dist-info/top_level.txt
new file mode 100644
index 0000000..450d4fe
--- /dev/null
+++ b/venv/Lib/site-packages/astroid-2.3.3.dist-info/top_level.txt
@@ -0,0 +1 @@
+astroid
diff --git a/venv/Lib/site-packages/astroid/__init__.py b/venv/Lib/site-packages/astroid/__init__.py
new file mode 100644
index 0000000..d36a5b4
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__init__.py
@@ -0,0 +1,166 @@
+# Copyright (c) 2006-2013, 2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""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 extends class defined in the python's _ast module with some
+additional methods and attributes. Instance attributes are added by a
+builder object, which can either generate extended ast (let's call
+them astroid ;) by visiting an existent ast tree or by inspecting living
+object. Methods are added by monkey patching ast classes.
+
+Main modules are:
+
+* nodes and scoped_nodes for more information about methods and
+ attributes added to different node classes
+
+* the manager contains a high level object to get astroid trees from
+ source files and living objects. It maintains a cache of previously
+ constructed tree for quick access
+
+* builder contains the class responsible to build astroid trees
+"""
+
+import enum
+import itertools
+import os
+import sys
+
+import wrapt
+
+
+_Context = enum.Enum("Context", "Load Store Del")
+Load = _Context.Load
+Store = _Context.Store
+Del = _Context.Del
+del _Context
+
+
+from .__pkginfo__ import version as __version__
+
+# WARNING: internal imports order matters !
+
+# pylint: disable=redefined-builtin
+
+# make all exception classes accessible from astroid package
+from astroid.exceptions import *
+
+# make all node classes accessible from astroid package
+from astroid.nodes import *
+
+# trigger extra monkey-patching
+from astroid import inference
+
+# more stuff available
+from astroid import raw_building
+from astroid.bases import BaseInstance, Instance, BoundMethod, UnboundMethod
+from astroid.node_classes import are_exclusive, unpack_infer
+from astroid.scoped_nodes import builtin_lookup
+from astroid.builder import parse, extract_node
+from astroid.util import Uninferable
+
+# make a manager instance (borg) accessible from astroid package
+from astroid.manager import AstroidManager
+
+MANAGER = AstroidManager()
+del AstroidManager
+
+# transform utilities (filters and decorator)
+
+
+# pylint: disable=dangerous-default-value
+@wrapt.decorator
+def _inference_tip_cached(func, instance, args, kwargs, _cache={}):
+ """Cache decorator used for inference tips"""
+ node = args[0]
+ try:
+ return iter(_cache[func, node])
+ except KeyError:
+ result = func(*args, **kwargs)
+ # Need to keep an iterator around
+ original, copy = itertools.tee(result)
+ _cache[func, node] = list(copy)
+ return original
+
+
+# pylint: enable=dangerous-default-value
+
+
+def inference_tip(infer_function, raise_on_overwrite=False):
+ """Given an instance specific inference function, return a function to be
+ given to MANAGER.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
+
+ MANAGER.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, infer_function=infer_function):
+ if (
+ raise_on_overwrite
+ and node._explicit_inference is not None
+ and node._explicit_inference is not infer_function
+ ):
+ raise InferenceOverwriteError(
+ "Inference already set to {existing_inference}. "
+ "Trying to overwrite with {new_inference} for {node}".format(
+ existing_inference=infer_function,
+ new_inference=node._explicit_inference,
+ node=node,
+ )
+ )
+ # pylint: disable=no-value-for-parameter
+ node._explicit_inference = _inference_tip_cached(infer_function)
+ return node
+
+ return transform
+
+
+def register_module_extender(manager, module_name, get_extension_mod):
+ def transform(node):
+ 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)
+
+
+# load brain plugins
+BRAIN_MODULES_DIR = os.path.join(os.path.dirname(__file__), "brain")
+if BRAIN_MODULES_DIR not in sys.path:
+ # add it to the end of the list so user path take precedence
+ sys.path.append(BRAIN_MODULES_DIR)
+# load modules in this directory
+for module in os.listdir(BRAIN_MODULES_DIR):
+ if module.endswith(".py"):
+ __import__(module[:-3])
diff --git a/venv/Lib/site-packages/astroid/__pkginfo__.py b/venv/Lib/site-packages/astroid/__pkginfo__.py
new file mode 100644
index 0000000..4a17b5d
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pkginfo__.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2015-2017 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Radosław Ganczarek <radoslaw@ganczarek.in>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 Calen Pennington <cale@edx.org>
+# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""astroid packaging information"""
+
+version = "2.3.3"
+numversion = tuple(int(elem) for elem in version.split(".") if elem.isdigit())
+
+extras_require = {}
+install_requires = [
+ "lazy_object_proxy==1.4.*",
+ "six~=1.12",
+ "wrapt==1.11.*",
+ 'typed-ast>=1.4.0,<1.5;implementation_name== "cpython" and python_version<"3.8"',
+]
+
+# pylint: disable=redefined-builtin; why license is a builtin anyway?
+license = "LGPL"
+
+author = "Python Code Quality Authority"
+author_email = "code-quality@python.org"
+mailinglist = "mailto://%s" % author_email
+web = "https://github.com/PyCQA/astroid"
+
+description = "An abstract syntax tree for Python with inference support."
+
+classifiers = [
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: Software Development :: Quality Assurance",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+]
diff --git a/venv/Lib/site-packages/astroid/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..eb28207
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/__pkginfo__.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/__pkginfo__.cpython-37.pyc
new file mode 100644
index 0000000..ed3f17b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/__pkginfo__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/_ast.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/_ast.cpython-37.pyc
new file mode 100644
index 0000000..c6f8a74
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/_ast.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/arguments.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/arguments.cpython-37.pyc
new file mode 100644
index 0000000..64896f7
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/arguments.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/as_string.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/as_string.cpython-37.pyc
new file mode 100644
index 0000000..372e534
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/as_string.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/bases.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/bases.cpython-37.pyc
new file mode 100644
index 0000000..366b834
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/bases.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/builder.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/builder.cpython-37.pyc
new file mode 100644
index 0000000..6ff12eb
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/builder.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/context.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/context.cpython-37.pyc
new file mode 100644
index 0000000..777eede
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/context.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/decorators.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/decorators.cpython-37.pyc
new file mode 100644
index 0000000..1bc12f8
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/decorators.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/exceptions.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/exceptions.cpython-37.pyc
new file mode 100644
index 0000000..211001b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/exceptions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/helpers.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/helpers.cpython-37.pyc
new file mode 100644
index 0000000..bae7ec3
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/helpers.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/inference.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/inference.cpython-37.pyc
new file mode 100644
index 0000000..c9328c1
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/inference.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/manager.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/manager.cpython-37.pyc
new file mode 100644
index 0000000..31b45d7
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/manager.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/mixins.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/mixins.cpython-37.pyc
new file mode 100644
index 0000000..7b5b9e4
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/mixins.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/modutils.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/modutils.cpython-37.pyc
new file mode 100644
index 0000000..a0f3b48
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/modutils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/node_classes.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/node_classes.cpython-37.pyc
new file mode 100644
index 0000000..7abdd4b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/node_classes.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/nodes.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/nodes.cpython-37.pyc
new file mode 100644
index 0000000..18c04f8
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/nodes.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/objects.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/objects.cpython-37.pyc
new file mode 100644
index 0000000..460886a
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/objects.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/protocols.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/protocols.cpython-37.pyc
new file mode 100644
index 0000000..d628662
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/protocols.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/raw_building.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/raw_building.cpython-37.pyc
new file mode 100644
index 0000000..0b414cf
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/raw_building.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/rebuilder.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/rebuilder.cpython-37.pyc
new file mode 100644
index 0000000..13516ca
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/rebuilder.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/scoped_nodes.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/scoped_nodes.cpython-37.pyc
new file mode 100644
index 0000000..d767b50
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/scoped_nodes.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/test_utils.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/test_utils.cpython-37.pyc
new file mode 100644
index 0000000..4b6fba6
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/test_utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/transforms.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/transforms.cpython-37.pyc
new file mode 100644
index 0000000..b2f4230
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/transforms.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/__pycache__/util.cpython-37.pyc b/venv/Lib/site-packages/astroid/__pycache__/util.cpython-37.pyc
new file mode 100644
index 0000000..b5e4fe7
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/__pycache__/util.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/_ast.py b/venv/Lib/site-packages/astroid/_ast.py
new file mode 100644
index 0000000..2e44c1f
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/_ast.py
@@ -0,0 +1,49 @@
+import ast
+from collections import namedtuple
+from functools import partial
+from typing import Optional
+import sys
+
+_ast_py2 = _ast_py3 = None
+try:
+ import typed_ast.ast3 as _ast_py3
+ import typed_ast.ast27 as _ast_py2
+except ImportError:
+ pass
+
+
+PY38 = sys.version_info[:2] >= (3, 8)
+if PY38:
+ # On Python 3.8, typed_ast was merged back into `ast`
+ _ast_py3 = ast
+
+
+FunctionType = namedtuple("FunctionType", ["argtypes", "returns"])
+
+
+def _get_parser_module(parse_python_two: bool = False):
+ if parse_python_two:
+ parser_module = _ast_py2
+ else:
+ parser_module = _ast_py3
+ return parser_module or ast
+
+
+def _parse(string: str, parse_python_two: bool = False):
+ parse_module = _get_parser_module(parse_python_two=parse_python_two)
+ parse_func = parse_module.parse
+ if _ast_py3:
+ if PY38:
+ parse_func = partial(parse_func, type_comments=True)
+ if not parse_python_two:
+ parse_func = partial(parse_func, feature_version=sys.version_info.minor)
+ return parse_func(string)
+
+
+def parse_function_type_comment(type_comment: str) -> Optional[FunctionType]:
+ """Given a correct type comment, obtain a FunctionType object"""
+ if _ast_py3 is None:
+ return None
+
+ func_type = _ast_py3.parse(type_comment, "<type_comment>", "func_type")
+ return FunctionType(argtypes=func_type.argtypes, returns=func_type.returns)
diff --git a/venv/Lib/site-packages/astroid/arguments.py b/venv/Lib/site-packages/astroid/arguments.py
new file mode 100644
index 0000000..c4bdc6d
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/arguments.py
@@ -0,0 +1,285 @@
+# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+from astroid import bases
+from astroid import context as contextmod
+from astroid import exceptions
+from astroid import nodes
+from astroid import util
+
+
+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.
+ """
+
+ def __init__(self, callcontext, argument_context_map=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()
+ self._unpacked_args = self._unpack_args(args)
+ self._unpacked_kwargs = self._unpack_keywords(keywords)
+
+ self.positional_arguments = [
+ arg for arg in self._unpacked_args if arg is not util.Uninferable
+ ]
+ self.keyword_arguments = {
+ key: value
+ for key, value in self._unpacked_kwargs.items()
+ if value is not util.Uninferable
+ }
+
+ @classmethod
+ def from_call(cls, call_node):
+ """Get a CallSite object from the given Call node."""
+ callcontext = contextmod.CallContext(call_node.args, call_node.keywords)
+ return cls(callcontext)
+
+ def has_invalid_arguments(self):
+ """Check if in the current CallSite were passed *invalid* arguments
+
+ This can mean multiple things. For instance, if an unpacking
+ of an invalid object was passed, then this method will return True.
+ Other cases can be when the arguments can't be inferred by astroid,
+ for example, by passing objects which aren't known statically.
+ """
+ return len(self.positional_arguments) != len(self._unpacked_args)
+
+ def has_invalid_keywords(self):
+ """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):
+ values = {}
+ context = contextmod.InferenceContext()
+ context.extra_context = self.argument_context_map
+ for name, value in keywords:
+ if name is None:
+ # Then it's an unpacking operation (**)
+ try:
+ inferred = next(value.infer(context=context))
+ except exceptions.InferenceError:
+ values[name] = util.Uninferable
+ continue
+
+ if not isinstance(inferred, nodes.Dict):
+ # Not something we can work with.
+ values[name] = util.Uninferable
+ continue
+
+ for dict_key, dict_value in inferred.items:
+ try:
+ dict_key = next(dict_key.infer(context=context))
+ except exceptions.InferenceError:
+ values[name] = util.Uninferable
+ continue
+ if not isinstance(dict_key, nodes.Const):
+ values[name] = util.Uninferable
+ continue
+ if not isinstance(dict_key.value, str):
+ values[name] = util.Uninferable
+ continue
+ if dict_key.value in values:
+ # The name is already in the dictionary
+ values[dict_key.value] = util.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):
+ values = []
+ context = contextmod.InferenceContext()
+ context.extra_context = self.argument_context_map
+ for arg in args:
+ if isinstance(arg, nodes.Starred):
+ try:
+ inferred = next(arg.value.infer(context=context))
+ except exceptions.InferenceError:
+ values.append(util.Uninferable)
+ continue
+
+ if inferred is util.Uninferable:
+ values.append(util.Uninferable)
+ continue
+ if not hasattr(inferred, "elts"):
+ values.append(util.Uninferable)
+ continue
+ values.extend(inferred.elts)
+ else:
+ values.append(arg)
+ return values
+
+ def infer_argument(self, funcnode, name, context):
+ """infer a function argument value according to the call context
+
+ Arguments:
+ funcnode: The function being called.
+ name: The name of the argument whose value is being inferred.
+ context: Inference context object
+ """
+ if name in self.duplicated_keywords:
+ raise exceptions.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:
+ raise exceptions.InferenceError(
+ "Too many positional arguments "
+ "passed to {func!r} that does "
+ "not have *args.",
+ call_site=self,
+ func=funcnode,
+ arg=name,
+ context=context,
+ )
+
+ positional = self.positional_arguments[: len(funcnode.args.args)]
+ vararg = self.positional_arguments[len(funcnode.args.args) :]
+ argindex = funcnode.args.find_argname(name)[0]
+ kwonlyargs = {arg.name for arg in funcnode.args.kwonlyargs}
+ kwargs = {
+ key: value
+ for key, value in self.keyword_arguments.items()
+ if key not in kwonlyargs
+ }
+ # If there are too few positionals compared to
+ # what the function expects to receive, check to see
+ # if the missing positional arguments were passed
+ # as keyword arguments and if so, place them into the
+ # positional args list.
+ if len(positional) < len(funcnode.args.args):
+ for func_arg in funcnode.args.args:
+ if func_arg.name in kwargs:
+ arg = kwargs.pop(func_arg.name)
+ positional.append(arg)
+
+ if argindex is not None:
+ # 2. first argument of instance/class method
+ if argindex == 0 and funcnode.type in ("method", "classmethod"):
+ if context.boundnode is not None:
+ boundnode = context.boundnode
+ else:
+ # 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():
+ return iter((boundnode,))
+
+ if funcnode.type == "method":
+ if not isinstance(boundnode, bases.Instance):
+ boundnode = bases.Instance(boundnode)
+ 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"):
+ 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 exceptions.InferenceError(
+ "Inference failed to find values for all keyword arguments "
+ "to {func!r}: {unpacked_kwargs!r} doesn't correspond to "
+ "{keyword_arguments!r}.",
+ keyword_arguments=self.keyword_arguments,
+ unpacked_kwargs=self._unpacked_kwargs,
+ call_site=self,
+ func=funcnode,
+ arg=name,
+ context=context,
+ )
+ kwarg = nodes.Dict(
+ lineno=funcnode.args.lineno,
+ col_offset=funcnode.args.col_offset,
+ parent=funcnode.args,
+ )
+ kwarg.postinit(
+ [(nodes.const_factory(key), value) for key, value in kwargs.items()]
+ )
+ return iter((kwarg,))
+ if funcnode.args.vararg == name:
+ # It wants all the args that were passed into
+ # the call site.
+ if self.has_invalid_arguments():
+ raise exceptions.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 exceptions.NoDefault:
+ pass
+ raise exceptions.InferenceError(
+ "No value found for argument {name} to " "{func!r}",
+ call_site=self,
+ func=funcnode,
+ arg=name,
+ context=context,
+ )
diff --git a/venv/Lib/site-packages/astroid/as_string.py b/venv/Lib/site-packages/astroid/as_string.py
new file mode 100644
index 0000000..3cd6e0d
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/as_string.py
@@ -0,0 +1,633 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
+# Copyright (c) 2013-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2016 Jared Garst <jgarst@users.noreply.github.com>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 rr- <rr-@sakuya.pl>
+# Copyright (c) 2018 brendanator <brendan.maginnis@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""This module renders Astroid nodes as string:
+
+* :func:`to_code` function return equivalent (hopefully valid) python string
+
+* :func:`dump` function return an internal representation of nodes found
+ in the tree, useful for debugging or understanding the tree structure
+"""
+
+# pylint: disable=unused-argument
+
+DOC_NEWLINE = "\0"
+
+
+class AsStringVisitor:
+ """Visitor to render an Astroid node as a valid python code string"""
+
+ def __init__(self, indent):
+ self.indent = indent
+
+ def __call__(self, node):
+ """Makes this visitor behave as a simple function"""
+ return node.accept(self).replace(DOC_NEWLINE, "\n")
+
+ def _docs_dedent(self, doc):
+ """Stop newlines in docs being indented by self._stmt_list"""
+ return '\n%s"""%s"""' % (self.indent, doc.replace("\n", DOC_NEWLINE))
+
+ def _stmt_list(self, stmts, indent=True):
+ """return a list of nodes to string"""
+ stmts = "\n".join(nstr for nstr in [n.accept(self) for n in stmts] if nstr)
+ if indent:
+ return self.indent + stmts.replace("\n", "\n" + self.indent)
+
+ return stmts
+
+ def _precedence_parens(self, node, child, is_left=True):
+ """Wrap child in parens only if required to keep same semantics"""
+ if self._should_wrap(node, child, is_left):
+ return "(%s)" % child.accept(self)
+
+ return child.accept(self)
+
+ def _should_wrap(self, node, child, is_left):
+ """Wrap child if:
+ - it has lower precedence
+ - same precedence with position opposite to associativity direction
+ """
+ node_precedence = node.op_precedence()
+ child_precedence = child.op_precedence()
+
+ if node_precedence > child_precedence:
+ # 3 * (4 + 5)
+ return True
+
+ if (
+ node_precedence == child_precedence
+ and is_left != node.op_left_associative()
+ ):
+ # 3 - (4 - 5)
+ # (2**3)**4
+ return True
+
+ return False
+
+ ## visit_<node> methods ###########################################
+
+ def visit_arguments(self, node):
+ """return an astroid.Function node as string"""
+ return node.format_args()
+
+ def visit_assignattr(self, node):
+ """return an astroid.AssAttr node as string"""
+ return self.visit_attribute(node)
+
+ def visit_assert(self, node):
+ """return an astroid.Assert node as string"""
+ if node.fail:
+ return "assert %s, %s" % (node.test.accept(self), node.fail.accept(self))
+ return "assert %s" % node.test.accept(self)
+
+ def visit_assignname(self, node):
+ """return an astroid.AssName node as string"""
+ return node.name
+
+ def visit_assign(self, node):
+ """return an astroid.Assign node as string"""
+ lhs = " = ".join(n.accept(self) for n in node.targets)
+ return "%s = %s" % (lhs, node.value.accept(self))
+
+ def visit_augassign(self, node):
+ """return an astroid.AugAssign node as string"""
+ return "%s %s %s" % (node.target.accept(self), node.op, node.value.accept(self))
+
+ def visit_annassign(self, node):
+ """Return an astroid.AugAssign node as string"""
+
+ target = node.target.accept(self)
+ annotation = node.annotation.accept(self)
+ if node.value is None:
+ return "%s: %s" % (target, annotation)
+ return "%s: %s = %s" % (target, annotation, node.value.accept(self))
+
+ def visit_repr(self, node):
+ """return an astroid.Repr node as string"""
+ return "`%s`" % node.value.accept(self)
+
+ def visit_binop(self, node):
+ """return an astroid.BinOp node as string"""
+ left = self._precedence_parens(node, node.left)
+ right = self._precedence_parens(node, node.right, is_left=False)
+ if node.op == "**":
+ return "%s%s%s" % (left, node.op, right)
+
+ return "%s %s %s" % (left, node.op, right)
+
+ def visit_boolop(self, node):
+ """return an astroid.BoolOp node as string"""
+ values = ["%s" % self._precedence_parens(node, n) for n in node.values]
+ return (" %s " % node.op).join(values)
+
+ def visit_break(self, node):
+ """return an astroid.Break node as string"""
+ return "break"
+
+ def visit_call(self, node):
+ """return an astroid.Call node as string"""
+ expr_str = self._precedence_parens(node, node.func)
+ args = [arg.accept(self) for arg in node.args]
+ if node.keywords:
+ keywords = [kwarg.accept(self) for kwarg in node.keywords]
+ else:
+ keywords = []
+
+ args.extend(keywords)
+ return "%s(%s)" % (expr_str, ", ".join(args))
+
+ def visit_classdef(self, node):
+ """return an astroid.ClassDef node as string"""
+ decorate = node.decorators.accept(self) if node.decorators else ""
+ bases = ", ".join(n.accept(self) for n in node.bases)
+ metaclass = node.metaclass()
+ if metaclass and not node.has_metaclass_hack():
+ if bases:
+ bases = "(%s, metaclass=%s)" % (bases, metaclass.name)
+ else:
+ bases = "(metaclass=%s)" % metaclass.name
+ else:
+ bases = "(%s)" % bases if bases else ""
+ docs = self._docs_dedent(node.doc) if node.doc else ""
+ return "\n\n%sclass %s%s:%s\n%s\n" % (
+ decorate,
+ node.name,
+ bases,
+ docs,
+ self._stmt_list(node.body),
+ )
+
+ def visit_compare(self, node):
+ """return an astroid.Compare node as string"""
+ rhs_str = " ".join(
+ [
+ "%s %s" % (op, self._precedence_parens(node, expr, is_left=False))
+ for op, expr in node.ops
+ ]
+ )
+ return "%s %s" % (self._precedence_parens(node, node.left), rhs_str)
+
+ def visit_comprehension(self, node):
+ """return an astroid.Comprehension node as string"""
+ ifs = "".join(" if %s" % n.accept(self) for n in node.ifs)
+ return "for %s in %s%s" % (
+ node.target.accept(self),
+ node.iter.accept(self),
+ ifs,
+ )
+
+ def visit_const(self, node):
+ """return an astroid.Const node as string"""
+ if node.value is Ellipsis:
+ return "..."
+ return repr(node.value)
+
+ def visit_continue(self, node):
+ """return an astroid.Continue node as string"""
+ return "continue"
+
+ def visit_delete(self, node): # XXX check if correct
+ """return an astroid.Delete node as string"""
+ return "del %s" % ", ".join(child.accept(self) for child in node.targets)
+
+ def visit_delattr(self, node):
+ """return an astroid.DelAttr node as string"""
+ return self.visit_attribute(node)
+
+ def visit_delname(self, node):
+ """return an astroid.DelName node as string"""
+ return node.name
+
+ def visit_decorators(self, node):
+ """return an astroid.Decorators node as string"""
+ return "@%s\n" % "\n@".join(item.accept(self) for item in node.nodes)
+
+ def visit_dict(self, node):
+ """return an astroid.Dict node as string"""
+ return "{%s}" % ", ".join(self._visit_dict(node))
+
+ def _visit_dict(self, node):
+ 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 "%s: %s" % (key, value)
+
+ def visit_dictunpack(self, node):
+ return "**"
+
+ def visit_dictcomp(self, node):
+ """return an astroid.DictComp node as string"""
+ return "{%s: %s %s}" % (
+ node.key.accept(self),
+ node.value.accept(self),
+ " ".join(n.accept(self) for n in node.generators),
+ )
+
+ def visit_expr(self, node):
+ """return an astroid.Discard node as string"""
+ return node.value.accept(self)
+
+ def visit_emptynode(self, node):
+ """dummy method for visiting an Empty node"""
+ return ""
+
+ def visit_excepthandler(self, node):
+ if node.type:
+ if node.name:
+ excs = "except %s, %s" % (
+ node.type.accept(self),
+ node.name.accept(self),
+ )
+ else:
+ excs = "except %s" % node.type.accept(self)
+ else:
+ excs = "except"
+ return "%s:\n%s" % (excs, self._stmt_list(node.body))
+
+ def visit_ellipsis(self, node):
+ """return an astroid.Ellipsis node as string"""
+ return "..."
+
+ def visit_empty(self, node):
+ """return an Empty node as string"""
+ return ""
+
+ def visit_exec(self, node):
+ """return an astroid.Exec node as string"""
+ if node.locals:
+ return "exec %s in %s, %s" % (
+ node.expr.accept(self),
+ node.locals.accept(self),
+ node.globals.accept(self),
+ )
+ if node.globals:
+ return "exec %s in %s" % (node.expr.accept(self), node.globals.accept(self))
+ return "exec %s" % node.expr.accept(self)
+
+ def visit_extslice(self, node):
+ """return an astroid.ExtSlice node as string"""
+ return ", ".join(dim.accept(self) for dim in node.dims)
+
+ def visit_for(self, node):
+ """return an astroid.For node as string"""
+ fors = "for %s in %s:\n%s" % (
+ node.target.accept(self),
+ node.iter.accept(self),
+ self._stmt_list(node.body),
+ )
+ if node.orelse:
+ fors = "%s\nelse:\n%s" % (fors, self._stmt_list(node.orelse))
+ return fors
+
+ def visit_importfrom(self, node):
+ """return an astroid.ImportFrom node as string"""
+ return "from %s import %s" % (
+ "." * (node.level or 0) + node.modname,
+ _import_string(node.names),
+ )
+
+ def visit_functiondef(self, node):
+ """return an astroid.Function node as string"""
+ decorate = node.decorators.accept(self) if node.decorators else ""
+ docs = self._docs_dedent(node.doc) if node.doc else ""
+ trailer = ":"
+ if node.returns:
+ return_annotation = " -> " + node.returns.as_string()
+ trailer = return_annotation + ":"
+ def_format = "\n%sdef %s(%s)%s%s\n%s"
+ return def_format % (
+ decorate,
+ node.name,
+ node.args.accept(self),
+ trailer,
+ docs,
+ self._stmt_list(node.body),
+ )
+
+ def visit_generatorexp(self, node):
+ """return an astroid.GeneratorExp node as string"""
+ return "(%s %s)" % (
+ node.elt.accept(self),
+ " ".join(n.accept(self) for n in node.generators),
+ )
+
+ def visit_attribute(self, node):
+ """return an astroid.Getattr node as string"""
+ return "%s.%s" % (self._precedence_parens(node, node.expr), node.attrname)
+
+ def visit_global(self, node):
+ """return an astroid.Global node as string"""
+ return "global %s" % ", ".join(node.names)
+
+ def visit_if(self, node):
+ """return an astroid.If node as string"""
+ ifs = ["if %s:\n%s" % (node.test.accept(self), self._stmt_list(node.body))]
+ if node.has_elif_block():
+ ifs.append("el%s" % self._stmt_list(node.orelse, indent=False))
+ elif node.orelse:
+ ifs.append("else:\n%s" % self._stmt_list(node.orelse))
+ return "\n".join(ifs)
+
+ def visit_ifexp(self, node):
+ """return an astroid.IfExp node as string"""
+ return "%s if %s else %s" % (
+ 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):
+ """return an astroid.Import node as string"""
+ return "import %s" % _import_string(node.names)
+
+ def visit_keyword(self, node):
+ """return an astroid.Keyword node as string"""
+ if node.arg is None:
+ return "**%s" % node.value.accept(self)
+ return "%s=%s" % (node.arg, node.value.accept(self))
+
+ def visit_lambda(self, node):
+ """return an astroid.Lambda node as string"""
+ args = node.args.accept(self)
+ body = node.body.accept(self)
+ if args:
+ return "lambda %s: %s" % (args, body)
+
+ return "lambda: %s" % body
+
+ def visit_list(self, node):
+ """return an astroid.List node as string"""
+ return "[%s]" % ", ".join(child.accept(self) for child in node.elts)
+
+ def visit_listcomp(self, node):
+ """return an astroid.ListComp node as string"""
+ return "[%s %s]" % (
+ node.elt.accept(self),
+ " ".join(n.accept(self) for n in node.generators),
+ )
+
+ def visit_module(self, node):
+ """return an astroid.Module node as string"""
+ docs = '"""%s"""\n\n' % node.doc if node.doc else ""
+ return docs + "\n".join(n.accept(self) for n in node.body) + "\n\n"
+
+ def visit_name(self, node):
+ """return an astroid.Name node as string"""
+ return node.name
+
+ def visit_pass(self, node):
+ """return an astroid.Pass node as string"""
+ return "pass"
+
+ def visit_print(self, node):
+ """return an astroid.Print node as string"""
+ nodes = ", ".join(n.accept(self) for n in node.values)
+ if not node.nl:
+ nodes = "%s," % nodes
+ if node.dest:
+ return "print >> %s, %s" % (node.dest.accept(self), nodes)
+ return "print %s" % nodes
+
+ def visit_raise(self, node):
+ """return an astroid.Raise node as string"""
+ if node.exc:
+ if node.inst:
+ if node.tback:
+ return "raise %s, %s, %s" % (
+ node.exc.accept(self),
+ node.inst.accept(self),
+ node.tback.accept(self),
+ )
+ return "raise %s, %s" % (node.exc.accept(self), node.inst.accept(self))
+ return "raise %s" % node.exc.accept(self)
+ return "raise"
+
+ def visit_return(self, node):
+ """return an astroid.Return node as string"""
+ if node.is_tuple_return() and len(node.value.elts) > 1:
+ elts = [child.accept(self) for child in node.value.elts]
+ return "return %s" % ", ".join(elts)
+
+ if node.value:
+ return "return %s" % node.value.accept(self)
+
+ return "return"
+
+ def visit_index(self, node):
+ """return an astroid.Index node as string"""
+ return node.value.accept(self)
+
+ def visit_set(self, node):
+ """return an astroid.Set node as string"""
+ return "{%s}" % ", ".join(child.accept(self) for child in node.elts)
+
+ def visit_setcomp(self, node):
+ """return an astroid.SetComp node as string"""
+ return "{%s %s}" % (
+ node.elt.accept(self),
+ " ".join(n.accept(self) for n in node.generators),
+ )
+
+ def visit_slice(self, node):
+ """return an astroid.Slice node as string"""
+ lower = node.lower.accept(self) if node.lower else ""
+ upper = node.upper.accept(self) if node.upper else ""
+ step = node.step.accept(self) if node.step else ""
+ if step:
+ return "%s:%s:%s" % (lower, upper, step)
+ return "%s:%s" % (lower, upper)
+
+ def visit_subscript(self, node):
+ """return an astroid.Subscript node as string"""
+ idx = node.slice
+ if idx.__class__.__name__.lower() == "index":
+ idx = idx.value
+ idxstr = idx.accept(self)
+ if idx.__class__.__name__.lower() == "tuple" and idx.elts:
+ # Remove parenthesis in tuple and extended slice.
+ # a[(::1, 1:)] is not valid syntax.
+ idxstr = idxstr[1:-1]
+ return "%s[%s]" % (self._precedence_parens(node, node.value), idxstr)
+
+ def visit_tryexcept(self, node):
+ """return an astroid.TryExcept node as string"""
+ trys = ["try:\n%s" % self._stmt_list(node.body)]
+ for handler in node.handlers:
+ trys.append(handler.accept(self))
+ if node.orelse:
+ trys.append("else:\n%s" % self._stmt_list(node.orelse))
+ return "\n".join(trys)
+
+ def visit_tryfinally(self, node):
+ """return an astroid.TryFinally node as string"""
+ return "try:\n%s\nfinally:\n%s" % (
+ self._stmt_list(node.body),
+ self._stmt_list(node.finalbody),
+ )
+
+ def visit_tuple(self, node):
+ """return an astroid.Tuple node as string"""
+ if len(node.elts) == 1:
+ return "(%s, )" % node.elts[0].accept(self)
+ return "(%s)" % ", ".join(child.accept(self) for child in node.elts)
+
+ def visit_unaryop(self, node):
+ """return an astroid.UnaryOp node as string"""
+ if node.op == "not":
+ operator = "not "
+ else:
+ operator = node.op
+ return "%s%s" % (operator, self._precedence_parens(node, node.operand))
+
+ def visit_while(self, node):
+ """return an astroid.While node as string"""
+ whiles = "while %s:\n%s" % (node.test.accept(self), self._stmt_list(node.body))
+ if node.orelse:
+ whiles = "%s\nelse:\n%s" % (whiles, self._stmt_list(node.orelse))
+ return whiles
+
+ def visit_with(self, node): # 'with' without 'as' is possible
+ """return an astroid.With node as string"""
+ items = ", ".join(
+ ("%s" % expr.accept(self)) + (vars and " as %s" % (vars.accept(self)) or "")
+ for expr, vars in node.items
+ )
+ return "with %s:\n%s" % (items, self._stmt_list(node.body))
+
+ def visit_yield(self, node):
+ """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 "(%s)" % (expr,)
+
+ def visit_starred(self, node):
+ """return Starred node as string"""
+ return "*" + node.value.accept(self)
+
+ # These aren't for real AST nodes, but for inference objects.
+
+ def visit_frozenset(self, node):
+ return node.parent.accept(self)
+
+ def visit_super(self, node):
+ return node.parent.accept(self)
+
+ def visit_uninferable(self, node):
+ return str(node)
+
+
+class AsStringVisitor3(AsStringVisitor):
+ """AsStringVisitor3 overwrites some AsStringVisitor methods"""
+
+ def visit_excepthandler(self, node):
+ if node.type:
+ if node.name:
+ excs = "except %s as %s" % (
+ node.type.accept(self),
+ node.name.accept(self),
+ )
+ else:
+ excs = "except %s" % node.type.accept(self)
+ else:
+ excs = "except"
+ return "%s:\n%s" % (excs, self._stmt_list(node.body))
+
+ def visit_nonlocal(self, node):
+ """return an astroid.Nonlocal node as string"""
+ return "nonlocal %s" % ", ".join(node.names)
+
+ def visit_raise(self, node):
+ """return an astroid.Raise node as string"""
+ if node.exc:
+ if node.cause:
+ return "raise %s from %s" % (
+ node.exc.accept(self),
+ node.cause.accept(self),
+ )
+ return "raise %s" % node.exc.accept(self)
+ return "raise"
+
+ def visit_yieldfrom(self, node):
+ """ Return an astroid.YieldFrom node as string. """
+ yi_val = (" " + node.value.accept(self)) if node.value else ""
+ expr = "yield from" + yi_val
+ if node.parent.is_statement:
+ return expr
+
+ return "(%s)" % (expr,)
+
+ def visit_asyncfunctiondef(self, node):
+ function = super(AsStringVisitor3, self).visit_functiondef(node)
+ return "async " + function.strip()
+
+ def visit_await(self, node):
+ return "await %s" % node.value.accept(self)
+
+ def visit_asyncwith(self, node):
+ return "async %s" % self.visit_with(node)
+
+ def visit_asyncfor(self, node):
+ return "async %s" % self.visit_for(node)
+
+ def visit_joinedstr(self, node):
+ # Special treatment for constants,
+ # as we want to join literals not reprs
+ string = "".join(
+ value.value if type(value).__name__ == "Const" else value.accept(self)
+ for value in node.values
+ )
+ return "f'%s'" % string
+
+ def visit_formattedvalue(self, node):
+ return "{%s}" % node.value.accept(self)
+
+ def visit_comprehension(self, node):
+ """return an astroid.Comprehension node as string"""
+ return "%s%s" % (
+ "async " if node.is_async else "",
+ super(AsStringVisitor3, self).visit_comprehension(node),
+ )
+
+ def visit_namedexpr(self, node):
+ """Return an assignment expression node as string"""
+ target = node.target.accept(self)
+ value = node.value.accept(self)
+ return "%s := %s" % (target, value)
+
+
+def _import_string(names):
+ """return a list of (name, asname) formatted as a string"""
+ _names = []
+ for name, asname in names:
+ if asname is not None:
+ _names.append("%s as %s" % (name, asname))
+ else:
+ _names.append(name)
+ return ", ".join(_names)
+
+
+AsStringVisitor = AsStringVisitor3
+
+# This sets the default indent to 4 spaces.
+to_code = AsStringVisitor(" ")
diff --git a/venv/Lib/site-packages/astroid/bases.py b/venv/Lib/site-packages/astroid/bases.py
new file mode 100644
index 0000000..d5b042a
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/bases.py
@@ -0,0 +1,542 @@
+# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2016-2017 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2017 Calen Pennington <calen.pennington@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Daniel Colascione <dancol@dancol.org>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""This module contains base classes and functions for the nodes and some
+inference utils.
+"""
+
+import builtins
+import collections
+
+from astroid import context as contextmod
+from astroid import exceptions
+from astroid import util
+
+objectmodel = util.lazy_import("interpreter.objectmodel")
+helpers = util.lazy_import("helpers")
+BUILTINS = builtins.__name__
+manager = util.lazy_import("manager")
+MANAGER = manager.AstroidManager()
+
+# TODO: check if needs special treatment
+BUILTINS = "builtins"
+BOOL_SPECIAL_METHOD = "__bool__"
+
+PROPERTIES = {BUILTINS + ".property", "abc.abstractproperty"}
+# 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",
+}
+
+
+def _is_property(meth):
+ if PROPERTIES.intersection(meth.decoratornames()):
+ return True
+ stripped = {
+ name.split(".")[-1]
+ for name in meth.decoratornames()
+ if name is not util.Uninferable
+ }
+ if any(name in stripped for name in POSSIBLE_PROPERTIES):
+ return True
+
+ # Lookup for subclasses of *property*
+ if not meth.decorators:
+ return False
+ for decorator in meth.decorators.nodes or ():
+ inferred = helpers.safe_infer(decorator)
+ if inferred is None or inferred is util.Uninferable:
+ continue
+ if inferred.__class__.__name__ == "ClassDef":
+ for base_class in inferred.bases:
+ if base_class.__class__.__name__ != "Name":
+ continue
+ module, _ = base_class.lookup(base_class.name)
+ if module.name == BUILTINS and base_class.name == "property":
+ return True
+
+ return False
+
+
+class Proxy:
+ """a simple proxy object
+
+ Note:
+
+ Subclasses of this object will need a custom __getattr__
+ if new instance attributes are created. See the Const class
+ """
+
+ _proxied = None # proxied object may be set by class or by instance
+
+ def __init__(self, proxied=None):
+ if proxied is not None:
+ self._proxied = proxied
+
+ def __getattr__(self, name):
+ if name == "_proxied":
+ return getattr(self.__class__, "_proxied")
+ if name in self.__dict__:
+ return self.__dict__[name]
+ return getattr(self._proxied, name)
+
+ def infer(self, context=None):
+ yield self
+
+
+def _infer_stmts(stmts, context, frame=None):
+ """Return an iterator on statements inferred by each statement in *stmts*."""
+ inferred = False
+ if context is not None:
+ name = context.lookupname
+ context = context.clone()
+ else:
+ name = None
+ context = contextmod.InferenceContext()
+
+ for stmt in stmts:
+ if stmt is util.Uninferable:
+ yield stmt
+ inferred = True
+ continue
+ context.lookupname = stmt._infer_name(frame, name)
+ try:
+ for inferred in stmt.infer(context=context):
+ yield inferred
+ inferred = True
+ except exceptions.NameInferenceError:
+ continue
+ except exceptions.InferenceError:
+ yield util.Uninferable
+ inferred = True
+ if not inferred:
+ raise exceptions.InferenceError(
+ "Inference failed for all members of {stmts!r}.",
+ stmts=stmts,
+ frame=frame,
+ context=context,
+ )
+
+
+def _infer_method_result_truth(instance, method_name, context):
+ # Get the method from the instance and try to infer
+ # its return's truth value.
+ meth = next(instance.igetattr(method_name, context=context), None)
+ if meth and hasattr(meth, "infer_call_result"):
+ if not meth.callable():
+ return util.Uninferable
+ try:
+ for value in meth.infer_call_result(instance, context=context):
+ if value is util.Uninferable:
+ return value
+
+ inferred = next(value.infer(context=context))
+ return inferred.bool_value()
+ except exceptions.InferenceError:
+ pass
+ return util.Uninferable
+
+
+class BaseInstance(Proxy):
+ """An instance base class, which provides lookup methods for potential instances."""
+
+ special_attributes = None
+
+ def display_type(self):
+ return "Instance of"
+
+ def getattr(self, name, context=None, lookupclass=True):
+ try:
+ values = self._proxied.instance_attr(name, context)
+ except exceptions.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 exceptions.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 exceptions.AttributeInferenceError:
+ pass
+ return values
+
+ def igetattr(self, name, context=None):
+ """inferred getattr"""
+ if not context:
+ context = contextmod.InferenceContext()
+ try:
+ # avoid recursively inferring the same attr on the same class
+ if context.push((self._proxied, name)):
+ raise exceptions.InferenceError(
+ message="Cannot infer the same attribute again",
+ node=self,
+ context=context,
+ )
+
+ # XXX frame should be self._proxied, or not ?
+ get_attr = self.getattr(name, context, lookupclass=False)
+ yield from _infer_stmts(
+ self._wrap_attr(get_attr, context), context, frame=self
+ )
+ except exceptions.AttributeInferenceError as error:
+ 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 exceptions.AttributeInferenceError as error:
+ raise exceptions.InferenceError(**vars(error)) from error
+
+ def _wrap_attr(self, attrs, context=None):
+ """wrap bound methods of attrs in a InstanceMethod proxies"""
+ for attr in attrs:
+ if isinstance(attr, UnboundMethod):
+ if _is_property(attr):
+ yield from attr.infer_call_result(self, context)
+ else:
+ yield BoundMethod(attr, self)
+ elif hasattr(attr, "name") and attr.name == "<lambda>":
+ if attr.args.args and attr.args.args[0].name == "self":
+ yield BoundMethod(attr, self)
+ continue
+ yield attr
+ else:
+ yield attr
+
+ def infer_call_result(self, caller, context=None):
+ """infer what a class instance is returning when called"""
+ context = contextmod.bind_context_to_node(context, self)
+ inferred = False
+ for node in self._proxied.igetattr("__call__", context):
+ if node is util.Uninferable or not node.callable():
+ continue
+ for res in node.infer_call_result(caller, context):
+ inferred = True
+ yield res
+ if not inferred:
+ raise exceptions.InferenceError(node=self, caller=caller, context=context)
+
+
+class Instance(BaseInstance):
+ """A special node representing a class instance."""
+
+ # pylint: disable=unnecessary-lambda
+ special_attributes = util.lazy_descriptor(lambda: objectmodel.InstanceModel())
+
+ def __repr__(self):
+ return "<Instance of %s.%s at 0x%s>" % (
+ self._proxied.root().name,
+ self._proxied.name,
+ id(self),
+ )
+
+ def __str__(self):
+ return "Instance of %s.%s" % (self._proxied.root().name, self._proxied.name)
+
+ def callable(self):
+ try:
+ self._proxied.getattr("__call__", class_context=False)
+ return True
+ except exceptions.AttributeInferenceError:
+ return False
+
+ def pytype(self):
+ return self._proxied.qname()
+
+ def display_type(self):
+ return "Instance of"
+
+ def bool_value(self):
+ """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 = contextmod.InferenceContext()
+ context.callcontext = contextmod.CallContext(args=[])
+ context.boundnode = self
+
+ try:
+ result = _infer_method_result_truth(self, BOOL_SPECIAL_METHOD, context)
+ except (exceptions.InferenceError, exceptions.AttributeInferenceError):
+ # Fallback to __len__.
+ try:
+ result = _infer_method_result_truth(self, "__len__", context)
+ except (exceptions.AttributeInferenceError, exceptions.InferenceError):
+ return True
+ return result
+
+ # This is set in inference.py.
+ def getitem(self, index, context=None):
+ pass
+
+
+class UnboundMethod(Proxy):
+ """a special node representing a method not bound to an instance"""
+
+ # pylint: disable=unnecessary-lambda
+ special_attributes = util.lazy_descriptor(lambda: objectmodel.UnboundMethodModel())
+
+ def __repr__(self):
+ frame = self._proxied.parent.frame()
+ return "<%s %s of %s at 0x%s" % (
+ self.__class__.__name__,
+ self._proxied.name,
+ frame.qname(),
+ id(self),
+ )
+
+ def implicit_parameters(self):
+ return 0
+
+ def is_bound(self):
+ return False
+
+ def getattr(self, name, context=None):
+ if name in self.special_attributes:
+ return [self.special_attributes.lookup(name)]
+ return self._proxied.getattr(name, context)
+
+ def igetattr(self, name, context=None):
+ if name in self.special_attributes:
+ return iter((self.special_attributes.lookup(name),))
+ return self._proxied.igetattr(name, context)
+
+ def infer_call_result(self, caller, context):
+ """
+ The boundnode of the regular context with a function called
+ on ``object.__new__`` will be of type ``object``,
+ which is incorrect for the argument in general.
+ If no context is given the ``object.__new__`` call argument will
+ 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 builtin object, the result is an
+ # instance of the class given as first argument.
+ if (
+ self._proxied.name == "__new__"
+ and self._proxied.parent.frame().qname() == "%s.object" % BUILTINS
+ ):
+ if caller.args:
+ node_context = context.extra_context.get(caller.args[0])
+ infer = caller.args[0].infer(context=node_context)
+ else:
+ infer = []
+ return (Instance(x) if x is not util.Uninferable else x for x in infer)
+ return self._proxied.infer_call_result(caller, context)
+
+ def bool_value(self):
+ return True
+
+
+class BoundMethod(UnboundMethod):
+ """a special node representing a method bound to an instance"""
+
+ # pylint: disable=unnecessary-lambda
+ special_attributes = util.lazy_descriptor(lambda: objectmodel.BoundMethodModel())
+
+ def __init__(self, proxy, bound):
+ UnboundMethod.__init__(self, proxy)
+ self.bound = bound
+
+ def implicit_parameters(self):
+ return 1
+
+ def is_bound(self):
+ return True
+
+ def _infer_type_new_call(self, caller, context):
+ """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 import node_classes
+
+ # Verify the metaclass
+ mcs = next(caller.args[0].infer(context=context))
+ if mcs.__class__.__name__ != "ClassDef":
+ # Not a valid first argument.
+ return None
+ if not mcs.is_subtype_of("%s.type" % BUILTINS):
+ # Not a valid metaclass.
+ return None
+
+ # Verify the name
+ name = next(caller.args[1].infer(context=context))
+ if name.__class__.__name__ != "Const":
+ # Not a valid name, needs to be a const.
+ return None
+ if not isinstance(name.value, str):
+ # Needs to be a string.
+ return None
+
+ # Verify the bases
+ bases = next(caller.args[2].infer(context=context))
+ if bases.__class__.__name__ != "Tuple":
+ # Needs to be a tuple.
+ return None
+ inferred_bases = [next(elt.infer(context=context)) for elt in bases.elts]
+ if any(base.__class__.__name__ != "ClassDef" for base in inferred_bases):
+ # All the bases needs to be Classes
+ return None
+
+ # Verify the attributes.
+ attrs = next(caller.args[3].infer(context=context))
+ if attrs.__class__.__name__ != "Dict":
+ # Needs to be a dictionary.
+ return None
+ cls_locals = collections.defaultdict(list)
+ for key, value in attrs.items:
+ key = next(key.infer(context=context))
+ value = next(value.infer(context=context))
+ # Ignore non string keys
+ if key.__class__.__name__ == "Const" and isinstance(key.value, str):
+ cls_locals[key.value].append(value)
+
+ # Build the class from now.
+ cls = mcs.__class__(
+ name=name.value,
+ lineno=caller.lineno,
+ col_offset=caller.col_offset,
+ parent=caller,
+ )
+ empty = node_classes.Pass()
+ cls.postinit(
+ bases=bases.elts,
+ body=[empty],
+ decorators=[],
+ newstyle=True,
+ metaclass=mcs,
+ keywords=[],
+ )
+ cls.locals = cls_locals
+ return cls
+
+ def infer_call_result(self, caller, context=None):
+ context = contextmod.bind_context_to_node(context, self.bound)
+ if (
+ self.bound.__class__.__name__ == "ClassDef"
+ and self.bound.name == "type"
+ and self.name == "__new__"
+ and len(caller.args) == 4
+ ):
+ # Check if we have a ``type.__new__(mcs, name, bases, attrs)`` call.
+ new_cls = self._infer_type_new_call(caller, context)
+ if new_cls:
+ return iter((new_cls,))
+
+ return super(BoundMethod, self).infer_call_result(caller, context)
+
+ def bool_value(self):
+ return True
+
+
+class Generator(BaseInstance):
+ """a special node representing a generator.
+
+ Proxied class is set once for all in raw_building.
+ """
+
+ # pylint: disable=unnecessary-lambda
+ special_attributes = util.lazy_descriptor(lambda: objectmodel.GeneratorModel())
+
+ # pylint: disable=super-init-not-called
+ def __init__(self, parent=None):
+ self.parent = parent
+
+ def callable(self):
+ return False
+
+ def pytype(self):
+ return "%s.generator" % BUILTINS
+
+ def display_type(self):
+ return "Generator"
+
+ def bool_value(self):
+ return True
+
+ def __repr__(self):
+ return "<Generator(%s) l.%s at 0x%s>" % (
+ self._proxied.name,
+ self.lineno,
+ id(self),
+ )
+
+ def __str__(self):
+ return "Generator(%s)" % (self._proxied.name)
+
+
+class AsyncGenerator(Generator):
+ """Special node representing an async generator"""
+
+ def pytype(self):
+ return "%s.async_generator" % BUILTINS
+
+ def display_type(self):
+ return "AsyncGenerator"
+
+ def __repr__(self):
+ return "<AsyncGenerator(%s) l.%s at 0x%s>" % (
+ self._proxied.name,
+ self.lineno,
+ id(self),
+ )
+
+ def __str__(self):
+ return "AsyncGenerator(%s)" % (self._proxied.name)
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_argparse.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_argparse.cpython-37.pyc
new file mode 100644
index 0000000..02f8cf7
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_argparse.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_attrs.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_attrs.cpython-37.pyc
new file mode 100644
index 0000000..7cf4841
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_attrs.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_builtin_inference.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_builtin_inference.cpython-37.pyc
new file mode 100644
index 0000000..c2a6f46
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_builtin_inference.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_collections.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_collections.cpython-37.pyc
new file mode 100644
index 0000000..af5833f
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_collections.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_crypt.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_crypt.cpython-37.pyc
new file mode 100644
index 0000000..a895bb5
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_crypt.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_curses.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_curses.cpython-37.pyc
new file mode 100644
index 0000000..e33a68c
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_curses.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_dataclasses.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_dataclasses.cpython-37.pyc
new file mode 100644
index 0000000..ead95a8
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_dataclasses.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_dateutil.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_dateutil.cpython-37.pyc
new file mode 100644
index 0000000..94c253f
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_dateutil.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_fstrings.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_fstrings.cpython-37.pyc
new file mode 100644
index 0000000..807c54d
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_fstrings.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_functools.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_functools.cpython-37.pyc
new file mode 100644
index 0000000..1d0fbe5
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_functools.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_gi.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_gi.cpython-37.pyc
new file mode 100644
index 0000000..115a75b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_gi.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_hashlib.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_hashlib.cpython-37.pyc
new file mode 100644
index 0000000..8cd6565
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_hashlib.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_http.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_http.cpython-37.pyc
new file mode 100644
index 0000000..ca12de5
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_http.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_io.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_io.cpython-37.pyc
new file mode 100644
index 0000000..5befdcd
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_io.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_mechanize.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_mechanize.cpython-37.pyc
new file mode 100644
index 0000000..e02f078
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_mechanize.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_multiprocessing.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_multiprocessing.cpython-37.pyc
new file mode 100644
index 0000000..4c20ea7
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_multiprocessing.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_namedtuple_enum.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_namedtuple_enum.cpython-37.pyc
new file mode 100644
index 0000000..4f6155a
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_namedtuple_enum.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_nose.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_nose.cpython-37.pyc
new file mode 100644
index 0000000..872060b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_nose.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-37.pyc
new file mode 100644
index 0000000..275e716
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-37.pyc
new file mode 100644
index 0000000..1b3da4c
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-37.pyc
new file mode 100644
index 0000000..4e9eb31
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-37.pyc
new file mode 100644
index 0000000..6f6e302
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-37.pyc
new file mode 100644
index 0000000..0c77435
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_umath.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_umath.cpython-37.pyc
new file mode 100644
index 0000000..bb8593b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_core_umath.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_ndarray.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_ndarray.cpython-37.pyc
new file mode 100644
index 0000000..f663c18
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_ndarray.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-37.pyc
new file mode 100644
index 0000000..32a3b7b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_utils.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_utils.cpython-37.pyc
new file mode 100644
index 0000000..0e950e7
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_numpy_utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_pkg_resources.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_pkg_resources.cpython-37.pyc
new file mode 100644
index 0000000..bca107d
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_pkg_resources.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_pytest.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_pytest.cpython-37.pyc
new file mode 100644
index 0000000..c6647f8
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_pytest.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_qt.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_qt.cpython-37.pyc
new file mode 100644
index 0000000..01d5160
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_qt.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_random.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_random.cpython-37.pyc
new file mode 100644
index 0000000..b5d2c69
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_random.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_re.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_re.cpython-37.pyc
new file mode 100644
index 0000000..e317433
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_re.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_six.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_six.cpython-37.pyc
new file mode 100644
index 0000000..b5deac2
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_six.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_ssl.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_ssl.cpython-37.pyc
new file mode 100644
index 0000000..90e94c9
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_ssl.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_subprocess.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_subprocess.cpython-37.pyc
new file mode 100644
index 0000000..ac6c87d
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_subprocess.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_threading.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_threading.cpython-37.pyc
new file mode 100644
index 0000000..a9214ba
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_threading.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_typing.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_typing.cpython-37.pyc
new file mode 100644
index 0000000..9cb0782
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_typing.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/__pycache__/brain_uuid.cpython-37.pyc b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_uuid.cpython-37.pyc
new file mode 100644
index 0000000..f6850ba
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/__pycache__/brain_uuid.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/brain/brain_argparse.py b/venv/Lib/site-packages/astroid/brain/brain_argparse.py
new file mode 100644
index 0000000..d489911
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_argparse.py
@@ -0,0 +1,33 @@
+from astroid import MANAGER, arguments, nodes, inference_tip, UseInferenceDefault
+
+
+def infer_namespace(node, context=None):
+ callsite = arguments.CallSite.from_call(node)
+ if not callsite.keyword_arguments:
+ # Cannot make sense of it.
+ raise UseInferenceDefault()
+
+ class_node = nodes.ClassDef("Namespace", "docstring")
+ class_node.parent = node.parent
+ for attr in set(callsite.keyword_arguments):
+ fake_node = nodes.EmptyNode()
+ fake_node.parent = class_node
+ fake_node.attrname = attr
+ class_node.instance_attrs[attr] = [fake_node]
+ return iter((class_node.instantiate_class(),))
+
+
+def _looks_like_namespace(node):
+ 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
+
+
+MANAGER.register_transform(
+ nodes.Call, inference_tip(infer_namespace), _looks_like_namespace
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_attrs.py b/venv/Lib/site-packages/astroid/brain/brain_attrs.py
new file mode 100644
index 0000000..670736f
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_attrs.py
@@ -0,0 +1,65 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+"""
+Astroid hook for the attrs library
+
+Without this hook pylint reports unsupported-assignment-operation
+for attrs classes
+"""
+
+import astroid
+from astroid import MANAGER
+
+
+ATTRIB_NAMES = frozenset(("attr.ib", "attrib", "attr.attrib"))
+ATTRS_NAMES = frozenset(("attr.s", "attrs", "attr.attrs", "attr.attributes"))
+
+
+def is_decorated_with_attrs(node, decorator_names=ATTRS_NAMES):
+ """Return True if 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, astroid.Call): # decorator with arguments
+ decorator_attribute = decorator_attribute.func
+ if decorator_attribute.as_string() in decorator_names:
+ return True
+ return False
+
+
+def attr_attributes_transform(node):
+ """Given that the ClassNode has an attr decorator,
+ rewrite class attributes as instance attributes
+ """
+ # Astroid can't infer this attribute properly
+ # Prevents https://github.com/PyCQA/pylint/issues/1884
+ node.locals["__attrs_attrs__"] = [astroid.Unknown(parent=node)]
+
+ for cdefbodynode in node.body:
+ if not isinstance(cdefbodynode, (astroid.Assign, astroid.AnnAssign)):
+ continue
+ if isinstance(cdefbodynode.value, astroid.Call):
+ if cdefbodynode.value.func.as_string() not in ATTRIB_NAMES:
+ continue
+ else:
+ continue
+ targets = (
+ cdefbodynode.targets
+ if hasattr(cdefbodynode, "targets")
+ else [cdefbodynode.target]
+ )
+ for target in targets:
+
+ rhs_node = astroid.Unknown(
+ lineno=cdefbodynode.lineno,
+ col_offset=cdefbodynode.col_offset,
+ parent=cdefbodynode,
+ )
+ node.locals[target.name] = [rhs_node]
+ node.instance_attrs[target.name] = [rhs_node]
+
+
+MANAGER.register_transform(
+ astroid.ClassDef, attr_attributes_transform, is_decorated_with_attrs
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_builtin_inference.py b/venv/Lib/site-packages/astroid/brain/brain_builtin_inference.py
new file mode 100644
index 0000000..2dd7cc5
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_builtin_inference.py
@@ -0,0 +1,829 @@
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Rene Zhang <rz99@cornell.edu>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid hooks for various builtins."""
+
+from functools import partial
+from textwrap import dedent
+
+import six
+from astroid import (
+ MANAGER,
+ UseInferenceDefault,
+ AttributeInferenceError,
+ inference_tip,
+ InferenceError,
+ NameInferenceError,
+ AstroidTypeError,
+ MroError,
+)
+from astroid import arguments
+from astroid.builder import AstroidBuilder
+from astroid import helpers
+from astroid import nodes
+from astroid import objects
+from astroid import scoped_nodes
+from astroid import util
+
+
+OBJECT_DUNDER_NEW = "object.__new__"
+
+
+def _extend_str(class_node, rvalue):
+ """function to extend builtin str/unicode class"""
+ code = dedent(
+ """
+ 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 ''
+ 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}
+ """
+ )
+ code = code.format(rvalue=rvalue)
+ fake = AstroidBuilder(MANAGER).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 = MANAGER.builtins_module
+ for class_name, transform in class_transforms.items():
+ transform(builtin_ast[class_name])
+
+
+_extend_builtins(
+ {
+ "bytes": partial(_extend_str, rvalue="b''"),
+ "str": partial(_extend_str, rvalue="''"),
+ }
+)
+
+
+def _builtin_filter_predicate(node, builtin_name):
+ if isinstance(node.func, nodes.Name) and node.func.name == builtin_name:
+ return True
+ if isinstance(node.func, nodes.Attribute):
+ return (
+ node.func.attrname == "fromkeys"
+ and isinstance(node.func.expr, nodes.Name)
+ and node.func.expr.name == "dict"
+ )
+ return False
+
+
+def register_builtin_transform(transform, builtin_name):
+ """Register a new transform function for the given *builtin_name*.
+
+ The transform function must accept two parameters, a node and
+ an optional context.
+ """
+
+ def _transform_wrapper(node, context=None):
+ result = transform(node, context=context)
+ if result:
+ if not result.parent:
+ # Let the transformation function determine
+ # the parent for its result. Otherwise,
+ # we set it to be the node we transformed from.
+ result.parent = node
+
+ if result.lineno is None:
+ result.lineno = node.lineno
+ if 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, context, node_type, transform):
+ args = node.args
+ if not args:
+ return node_type()
+ if len(node.args) > 1:
+ raise UseInferenceDefault()
+
+ arg, = args
+ transformed = transform(arg)
+ if not transformed:
+ try:
+ inferred = next(arg.infer(context=context))
+ except (InferenceError, StopIteration):
+ raise UseInferenceDefault()
+ if inferred is util.Uninferable:
+ raise UseInferenceDefault()
+ transformed = transform(inferred)
+ if not transformed or transformed is util.Uninferable:
+ raise UseInferenceDefault()
+ return transformed
+
+
+def _container_generic_transform(arg, klass, iterables, build_elts):
+ if isinstance(arg, klass):
+ return arg
+ elif isinstance(arg, iterables):
+ if all(isinstance(elt, nodes.Const) for elt in arg.elts):
+ elts = [elt.value for elt in arg.elts]
+ else:
+ # TODO: Does not handle deduplication for sets.
+ elts = filter(None, map(helpers.safe_infer, arg.elts))
+ elif isinstance(arg, nodes.Dict):
+ # Dicts need to have consts as strings already.
+ if not all(isinstance(elt[0], nodes.Const) for elt in arg.items):
+ raise UseInferenceDefault()
+ elts = [item[0].value for item in arg.items]
+ elif isinstance(arg, nodes.Const) and isinstance(
+ arg.value, (six.string_types, six.binary_type)
+ ):
+ elts = arg.value
+ else:
+ return
+ return klass.from_elements(elts=build_elts(elts))
+
+
+def _infer_builtin_container(
+ node, context, klass=None, iterables=None, build_elts=None
+):
+ transform_func = partial(
+ _container_generic_transform,
+ 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):
+ is_iterable = lambda n: isinstance(n, (nodes.List, nodes.Tuple, nodes.Set))
+ try:
+ inferred = next(arg.infer(context))
+ except (InferenceError, NameInferenceError):
+ raise UseInferenceDefault()
+ if isinstance(inferred, nodes.Dict):
+ items = inferred.items
+ elif is_iterable(inferred):
+ items = []
+ for elt in inferred.elts:
+ # If an item is not a pair of two items,
+ # then fallback to the default inference.
+ # Also, take in consideration only hashable items,
+ # tuples and consts. We are choosing Names as well.
+ if not is_iterable(elt):
+ raise UseInferenceDefault()
+ if len(elt.elts) != 2:
+ raise UseInferenceDefault()
+ if not isinstance(elt.elts[0], (nodes.Tuple, nodes.Const, nodes.Name)):
+ raise UseInferenceDefault()
+ items.append(tuple(elt.elts))
+ else:
+ raise UseInferenceDefault()
+ return items
+
+
+def infer_dict(node, context=None):
+ """Try to infer a dict call to a Dict node.
+
+ The function treats the following cases:
+
+ * dict()
+ * dict(mapping)
+ * dict(iterable)
+ * dict(iterable, **kwargs)
+ * dict(mapping, **kwargs)
+ * dict(**kwargs)
+
+ If a case can't be inferred, we'll fallback to default inference.
+ """
+ call = arguments.CallSite.from_call(node)
+ if call.has_invalid_arguments() or call.has_invalid_keywords():
+ raise UseInferenceDefault
+
+ args = call.positional_arguments
+ kwargs = list(call.keyword_arguments.items())
+
+ if not args and not kwargs:
+ # dict()
+ return nodes.Dict()
+ elif kwargs and not args:
+ # dict(a=1, b=2, c=4)
+ items = [(nodes.Const(key), value) for key, value in kwargs]
+ elif len(args) == 1 and kwargs:
+ # dict(some_iterable, b=2, c=4)
+ elts = _get_elts(args[0], context)
+ keys = [(nodes.Const(key), value) for key, value in kwargs]
+ items = elts + keys
+ elif len(args) == 1:
+ items = _get_elts(args[0], context)
+ else:
+ raise UseInferenceDefault()
+
+ value = nodes.Dict(
+ col_offset=node.col_offset, lineno=node.lineno, parent=node.parent
+ )
+ value.postinit(items)
+ return value
+
+
+def infer_super(node, context=None):
+ """Understand super calls.
+
+ There are some restrictions for what can be understood:
+
+ * unbounded super (one argument form) is not understood.
+
+ * if the super call is not inside a function (classmethod or method),
+ then the default inference will be used.
+
+ * if the super arguments can't be inferred, the default inference
+ will be used.
+ """
+ if len(node.args) == 1:
+ # Ignore unbounded super.
+ raise UseInferenceDefault
+
+ scope = node.scope()
+ if not isinstance(scope, nodes.FunctionDef):
+ # Ignore non-method uses of super.
+ raise UseInferenceDefault
+ if scope.type not in ("classmethod", "method"):
+ # Not interested in staticmethods.
+ raise UseInferenceDefault
+
+ cls = scoped_nodes.get_wrapping_class(scope)
+ if not len(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:
+ raise UseInferenceDefault
+ try:
+ mro_type = next(node.args[1].infer(context=context))
+ except InferenceError:
+ raise UseInferenceDefault
+
+ if mro_pointer is util.Uninferable or mro_type is util.Uninferable:
+ # No way we could understand this.
+ raise UseInferenceDefault
+
+ super_obj = objects.Super(
+ mro_pointer=mro_pointer, mro_type=mro_type, self_class=cls, scope=scope
+ )
+ super_obj.parent = node
+ return super_obj
+
+
+def _infer_getattr_args(node, context):
+ if len(node.args) not in (2, 3):
+ # Not a valid getattr call.
+ raise UseInferenceDefault
+
+ try:
+ obj = next(node.args[0].infer(context=context))
+ attr = next(node.args[1].infer(context=context))
+ except InferenceError:
+ raise UseInferenceDefault
+
+ if obj is util.Uninferable or attr is util.Uninferable:
+ # 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, six.string_types
+ )
+ if not is_string:
+ raise UseInferenceDefault
+
+ return obj, attr.value
+
+
+def infer_getattr(node, context=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 (
+ obj is util.Uninferable
+ or attr is util.Uninferable
+ 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 InferenceError:
+ raise UseInferenceDefault
+
+ raise UseInferenceDefault
+
+
+def infer_hasattr(node, context=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 (
+ obj is util.Uninferable
+ or attr is util.Uninferable
+ 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=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:
+ return util.Uninferable
+ if inferred is util.Uninferable:
+ return util.Uninferable
+ return nodes.Const(inferred.callable())
+
+
+def infer_bool(node, context=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:
+ return util.Uninferable
+ if inferred is util.Uninferable:
+ return util.Uninferable
+
+ bool_value = inferred.bool_value()
+ if bool_value is util.Uninferable:
+ return util.Uninferable
+ return nodes.Const(bool_value)
+
+
+def infer_type(node, context=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=None):
+ """Understand `slice` calls."""
+ args = node.args
+ if not 0 < len(args) <= 3:
+ raise UseInferenceDefault
+
+ infer_func = partial(helpers.safe_infer, context=context)
+ args = [infer_func(arg) for arg in args]
+ for arg in args:
+ if not arg or arg is util.Uninferable:
+ raise UseInferenceDefault
+ if not isinstance(arg, nodes.Const):
+ raise UseInferenceDefault
+ if not isinstance(arg.value, (type(None), int)):
+ raise UseInferenceDefault
+
+ if len(args) < 3:
+ # Make sure we have 3 arguments.
+ args.extend([None] * (3 - len(args)))
+
+ slice_node = nodes.Slice(
+ lineno=node.lineno, col_offset=node.col_offset, parent=node.parent
+ )
+ slice_node.postinit(*args)
+ return slice_node
+
+
+def _infer_object__new__decorator(node, context=None):
+ # Instantiate class immediately
+ # since that's what @object.__new__ does
+ return iter((node.instantiate_class(),))
+
+
+def _infer_object__new__decorator_check(node):
+ """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=None):
+ """Infer issubclass() calls
+
+ :param nodes.Call callnode: an `issubclass` call
+ :param InferenceContext: 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)
+ 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(
+ "Expected two arguments, got {count}".format(
+ count=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 as exc:
+ raise UseInferenceDefault from exc
+ if not isinstance(obj_type, nodes.ClassDef):
+ raise UseInferenceDefault("TypeError: arg 1 must be class")
+
+ # The right hand argument is the class(es) that the given
+ # object is to be checked against.
+ try:
+ class_container = _class_or_tuple_to_container(
+ class_or_tuple_node, context=context
+ )
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
+ try:
+ issubclass_bool = helpers.object_issubclass(obj_type, class_container, context)
+ except AstroidTypeError as exc:
+ raise UseInferenceDefault("TypeError: " + str(exc)) from exc
+ except MroError as exc:
+ raise UseInferenceDefault from exc
+ return nodes.Const(issubclass_bool)
+
+
+def infer_isinstance(callnode, context=None):
+ """Infer isinstance calls
+
+ :param nodes.Call callnode: an isinstance call
+ :param InferenceContext: context for call
+ (currently unused but is a common interface for inference)
+ :rtype nodes.Const: Boolean Const value of isinstance call
+
+ :raises UseInferenceDefault: If the node cannot be inferred
+ """
+ call = arguments.CallSite.from_call(callnode)
+ 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(
+ "Expected two arguments, got {count}".format(
+ count=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:
+ raise UseInferenceDefault
+ try:
+ isinstance_bool = helpers.object_isinstance(obj_node, class_container, context)
+ except AstroidTypeError as exc:
+ raise UseInferenceDefault("TypeError: " + str(exc))
+ except MroError as exc:
+ raise UseInferenceDefault from exc
+ if isinstance_bool is util.Uninferable:
+ raise UseInferenceDefault
+ return nodes.Const(isinstance_bool)
+
+
+def _class_or_tuple_to_container(node, context=None):
+ # Move inferences results into container
+ # to simplify later logic
+ # raises InferenceError if any of the inferences fall through
+ node_infer = next(node.infer(context=context))
+ # arg2 MUST be a type or a TUPLE of types
+ # for isinstance
+ if isinstance(node_infer, nodes.Tuple):
+ class_container = [
+ next(node.infer(context=context)) for node in node_infer.elts
+ ]
+ class_container = [
+ klass_node for klass_node in class_container if klass_node is not None
+ ]
+ else:
+ class_container = [node_infer]
+ return class_container
+
+
+def infer_len(node, context=None):
+ """Infer length calls
+
+ :param nodes.Call node: len call to infer
+ :param context.InferenceContext: node context
+ :rtype nodes.Const: a Const node with the inferred length, if possible
+ """
+ call = arguments.CallSite.from_call(node)
+ 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=None):
+ """Infer str() calls
+
+ :param nodes.Call node: str() call to infer
+ :param context.InferenceContext: node context
+ :rtype nodes.Const: a Const containing an empty string
+ """
+ call = arguments.CallSite.from_call(node)
+ 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=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)
+ 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 as exc:
+ raise UseInferenceDefault(str(exc)) from exc
+
+ if first_value is util.Uninferable:
+ 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=None):
+ """Infer dict.fromkeys
+
+ :param nodes.Call node: dict.fromkeys() call to infer
+ :param context.InferenceContext: node context
+ :rtype nodes.Dict:
+ a Dictionary containing the values that astroid was able to infer.
+ In case the inference failed for any reason, an empty dictionary
+ will be inferred instead.
+ """
+
+ def _build_dict_with_elements(elements):
+ new_node = nodes.Dict(
+ col_offset=node.col_offset, lineno=node.lineno, parent=node.parent
+ )
+ new_node.postinit(elements)
+ return new_node
+
+ call = arguments.CallSite.from_call(node)
+ 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:
+ 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)
+
+ elif isinstance(inferred_values, nodes.Const) and isinstance(
+ inferred_values.value, (str, bytes)
+ ):
+ elements = [
+ (nodes.Const(element), default) for element in inferred_values.value
+ ]
+ return _build_dict_with_elements(elements)
+ elif 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([])
+
+
+# Builtins inference
+register_builtin_transform(infer_bool, "bool")
+register_builtin_transform(infer_super, "super")
+register_builtin_transform(infer_callable, "callable")
+register_builtin_transform(infer_getattr, "getattr")
+register_builtin_transform(infer_hasattr, "hasattr")
+register_builtin_transform(infer_tuple, "tuple")
+register_builtin_transform(infer_set, "set")
+register_builtin_transform(infer_list, "list")
+register_builtin_transform(infer_dict, "dict")
+register_builtin_transform(infer_frozenset, "frozenset")
+register_builtin_transform(infer_type, "type")
+register_builtin_transform(infer_slice, "slice")
+register_builtin_transform(infer_isinstance, "isinstance")
+register_builtin_transform(infer_issubclass, "issubclass")
+register_builtin_transform(infer_len, "len")
+register_builtin_transform(infer_str, "str")
+register_builtin_transform(infer_int, "int")
+register_builtin_transform(infer_dict_fromkeys, "dict.fromkeys")
+
+
+# Infer object.__new__ calls
+MANAGER.register_transform(
+ nodes.ClassDef,
+ inference_tip(_infer_object__new__decorator),
+ _infer_object__new__decorator_check,
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_collections.py b/venv/Lib/site-packages/astroid/brain/brain_collections.py
new file mode 100644
index 0000000..e5b09ec
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_collections.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2018 Ioana Tagirta <ioana.tagirta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+import sys
+
+import astroid
+
+
+def _collections_transform():
+ return astroid.parse(
+ """
+ class defaultdict(dict):
+ default_factory = None
+ def __missing__(self, key): pass
+ def __getitem__(self, key): return default_factory
+
+ """
+ + _deque_mock()
+ + _ordered_dict_mock()
+ )
+
+
+def _deque_mock():
+ base_deque_class = """
+ class deque(object):
+ maxlen = 0
+ def __init__(self, iterable=None, maxlen=None):
+ self.iterable = iterable or []
+ def append(self, x): pass
+ def appendleft(self, x): pass
+ def clear(self): pass
+ def count(self, x): return 0
+ def extend(self, iterable): pass
+ def extendleft(self, iterable): pass
+ def pop(self): return self.iterable[0]
+ def popleft(self): return self.iterable[0]
+ def remove(self, value): pass
+ def reverse(self): return reversed(self.iterable)
+ def rotate(self, n=1): return self
+ def __iter__(self): return self
+ def __reversed__(self): return self.iterable[::-1]
+ def __getitem__(self, index): return self.iterable[index]
+ def __setitem__(self, index, value): pass
+ def __delitem__(self, index): pass
+ def __bool__(self): return bool(self.iterable)
+ def __nonzero__(self): return bool(self.iterable)
+ def __contains__(self, o): return o in self.iterable
+ def __len__(self): return len(self.iterable)
+ def __copy__(self): return deque(self.iterable)
+ def copy(self): return deque(self.iterable)
+ def index(self, x, start=0, end=0): return 0
+ def insert(self, x, i): 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"""
+ 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"""
+ return base_ordered_dict_class
+
+
+astroid.register_module_extender(astroid.MANAGER, "collections", _collections_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_crypt.py b/venv/Lib/site-packages/astroid/brain/brain_crypt.py
new file mode 100644
index 0000000..491ee23
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_crypt.py
@@ -0,0 +1,26 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+import sys
+import astroid
+
+PY37 = sys.version_info >= (3, 7)
+
+if PY37:
+ # Since Python 3.7 Hashing Methods are added
+ # dynamically to globals()
+
+ def _re_transform():
+ return astroid.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)
+ """
+ )
+
+ astroid.register_module_extender(astroid.MANAGER, "crypt", _re_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_curses.py b/venv/Lib/site-packages/astroid/brain/brain_curses.py
new file mode 100644
index 0000000..68e88b9
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_curses.py
@@ -0,0 +1,179 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+import astroid
+
+
+def _curses_transform():
+ return astroid.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
+ """
+ )
+
+
+astroid.register_module_extender(astroid.MANAGER, "curses", _curses_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_dataclasses.py b/venv/Lib/site-packages/astroid/brain/brain_dataclasses.py
new file mode 100644
index 0000000..7a25e0c
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_dataclasses.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/PyCQA/astroid/blob/master/COPYING.LESSER
+"""
+Astroid hook for the dataclasses library
+"""
+
+import astroid
+from astroid import MANAGER
+
+
+DATACLASSES_DECORATORS = frozenset(("dataclasses.dataclass", "dataclass"))
+
+
+def is_decorated_with_dataclass(node, decorator_names=DATACLASSES_DECORATORS):
+ """Return True if a decorated node has a `dataclass` decorator applied."""
+ if not node.decorators:
+ return False
+ for decorator_attribute in node.decorators.nodes:
+ if isinstance(decorator_attribute, astroid.Call): # decorator with arguments
+ decorator_attribute = decorator_attribute.func
+ if decorator_attribute.as_string() in decorator_names:
+ return True
+ return False
+
+
+def dataclass_transform(node):
+ """Rewrite a dataclass to be easily understood by pylint"""
+
+ for assign_node in node.body:
+ if not isinstance(assign_node, (astroid.AnnAssign, astroid.Assign)):
+ continue
+
+ targets = (
+ assign_node.targets
+ if hasattr(assign_node, "targets")
+ else [assign_node.target]
+ )
+ for target in targets:
+ rhs_node = astroid.Unknown(
+ lineno=assign_node.lineno,
+ col_offset=assign_node.col_offset,
+ parent=assign_node,
+ )
+ node.instance_attrs[target.name] = [rhs_node]
+ node.locals[target.name] = [rhs_node]
+
+
+MANAGER.register_transform(
+ astroid.ClassDef, dataclass_transform, is_decorated_with_dataclass
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_dateutil.py b/venv/Lib/site-packages/astroid/brain/brain_dateutil.py
new file mode 100644
index 0000000..a1c270f
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_dateutil.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 raylu <lurayl@gmail.com>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid hooks for dateutil"""
+
+import textwrap
+
+from astroid import MANAGER, register_module_extender
+from astroid.builder import AstroidBuilder
+
+
+def dateutil_transform():
+ return AstroidBuilder(MANAGER).string_build(
+ textwrap.dedent(
+ """
+ import datetime
+ def parse(timestr, parserinfo=None, **kwargs):
+ return datetime.datetime()
+ """
+ )
+ )
+
+
+register_module_extender(MANAGER, "dateutil.parser", dateutil_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_fstrings.py b/venv/Lib/site-packages/astroid/brain/brain_fstrings.py
new file mode 100644
index 0000000..7d8c7b6
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_fstrings.py
@@ -0,0 +1,51 @@
+# Copyright (c) 2017 Claudiu Popa <pcmanticore@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+import collections
+import sys
+
+import astroid
+
+
+def _clone_node_with_lineno(node, parent, lineno):
+ cls = node.__class__
+ other_fields = node._other_fields
+ _astroid_fields = node._astroid_fields
+ init_params = {"lineno": lineno, "col_offset": node.col_offset, "parent": parent}
+ postinit_params = {param: getattr(node, param) for param in _astroid_fields}
+ if other_fields:
+ init_params.update({param: getattr(node, param) for param in other_fields})
+ new_node = cls(**init_params)
+ if hasattr(node, "postinit") and _astroid_fields:
+ for param, child in postinit_params.items():
+ if child and not isinstance(child, collections.Sequence):
+ cloned_child = _clone_node_with_lineno(
+ node=child, lineno=new_node.lineno, parent=new_node
+ )
+ postinit_params[param] = cloned_child
+ new_node.postinit(**postinit_params)
+ return new_node
+
+
+def _transform_formatted_value(node):
+ if node.value and node.value.lineno == 1:
+ if node.lineno != node.value.lineno:
+ new_node = astroid.FormattedValue(
+ lineno=node.lineno, col_offset=node.col_offset, parent=node.parent
+ )
+ new_value = _clone_node_with_lineno(
+ node=node.value, lineno=node.lineno, parent=new_node
+ )
+ new_node.postinit(value=new_value, format_spec=node.format_spec)
+ return new_node
+
+
+if sys.version_info[:2] >= (3, 6):
+ # TODO: this fix tries to *patch* http://bugs.python.org/issue29051
+ # The problem is that FormattedValue.value, which is a Name node,
+ # has wrong line numbers, usually 1. This creates problems for pylint,
+ # which expects correct line numbers for things such as message control.
+ astroid.MANAGER.register_transform(
+ astroid.FormattedValue, _transform_formatted_value
+ )
diff --git a/venv/Lib/site-packages/astroid/brain/brain_functools.py b/venv/Lib/site-packages/astroid/brain/brain_functools.py
new file mode 100644
index 0000000..8b594ef
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_functools.py
@@ -0,0 +1,158 @@
+# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+"""Astroid hooks for understanding functools library module."""
+from functools import partial
+from itertools import chain
+
+import astroid
+from astroid import arguments
+from astroid import BoundMethod
+from astroid import extract_node
+from astroid import helpers
+from astroid.interpreter import objectmodel
+from astroid import MANAGER
+from astroid import objects
+
+
+LRU_CACHE = "functools.lru_cache"
+
+
+class LruWrappedModel(objectmodel.FunctionModel):
+ """Special attribute model for functions decorated with functools.lru_cache.
+
+ The said decorators patches at decoration time some functions onto
+ the decorated function.
+ """
+
+ @property
+ def attr___wrapped__(self):
+ return self._instance
+
+ @property
+ def attr_cache_info(self):
+ cache_info = extract_node(
+ """
+ from functools import _CacheInfo
+ _CacheInfo(0, 0, 0, 0)
+ """
+ )
+
+ class CacheInfoBoundMethod(BoundMethod):
+ def infer_call_result(self, caller, context=None):
+ yield helpers.safe_infer(cache_info)
+
+ return CacheInfoBoundMethod(proxy=self._instance, bound=self._instance)
+
+ @property
+ def attr_cache_clear(self):
+ node = extract_node("""def cache_clear(self): pass""")
+ return BoundMethod(proxy=node, bound=self._instance.parent.scope())
+
+
+def _transform_lru_cache(node, context=None):
+ # TODO: this is not ideal, since the node should be immutable,
+ # but due to https://github.com/PyCQA/astroid/issues/354,
+ # there's not much we can do now.
+ # Replacing the node would work partially, because,
+ # in pylint, the old node would still be available, leading
+ # to spurious false positives.
+ node.special_attributes = LruWrappedModel()(node)
+ return
+
+
+def _functools_partial_inference(node, context=None):
+ call = arguments.CallSite.from_call(node)
+ number_of_positional = len(call.positional_arguments)
+ if number_of_positional < 1:
+ raise astroid.UseInferenceDefault(
+ "functools.partial takes at least one argument"
+ )
+ if number_of_positional == 1 and not call.keyword_arguments:
+ raise astroid.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 astroid.InferenceError as exc:
+ raise astroid.UseInferenceDefault from exc
+ if inferred_wrapped_function is astroid.Uninferable:
+ raise astroid.UseInferenceDefault("Cannot infer the wrapped function")
+ if not isinstance(inferred_wrapped_function, astroid.FunctionDef):
+ raise astroid.UseInferenceDefault("The wrapped function is not a function")
+
+ # Determine if the passed keywords into the callsite are supported
+ # by the wrapped function.
+ function_parameters = chain(
+ inferred_wrapped_function.args.args or (),
+ inferred_wrapped_function.args.posonlyargs or (),
+ inferred_wrapped_function.args.kwonlyargs or (),
+ )
+ parameter_names = set(
+ param.name
+ for param in function_parameters
+ if isinstance(param, astroid.AssignName)
+ )
+ if set(call.keyword_arguments) - parameter_names:
+ raise astroid.UseInferenceDefault(
+ "wrapped function received unknown parameters"
+ )
+
+ partial_function = objects.PartialFunction(
+ call,
+ name=inferred_wrapped_function.name,
+ doc=inferred_wrapped_function.doc,
+ lineno=inferred_wrapped_function.lineno,
+ col_offset=inferred_wrapped_function.col_offset,
+ parent=inferred_wrapped_function.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,
+ )
+ return iter((partial_function,))
+
+
+def _looks_like_lru_cache(node):
+ """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, astroid.Call):
+ continue
+ if _looks_like_functools_member(decorator, "lru_cache"):
+ return True
+ return False
+
+
+def _looks_like_functools_member(node, member):
+ """Check if the given Call node is a functools.partial call"""
+ if isinstance(node.func, astroid.Name):
+ return node.func.name == member
+ elif isinstance(node.func, astroid.Attribute):
+ return (
+ node.func.attrname == member
+ and isinstance(node.func.expr, astroid.Name)
+ and node.func.expr.name == "functools"
+ )
+
+
+_looks_like_partial = partial(_looks_like_functools_member, member="partial")
+
+
+MANAGER.register_transform(
+ astroid.FunctionDef, _transform_lru_cache, _looks_like_lru_cache
+)
+
+
+MANAGER.register_transform(
+ astroid.Call,
+ astroid.inference_tip(_functools_partial_inference),
+ _looks_like_partial,
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_gi.py b/venv/Lib/site-packages/astroid/brain/brain_gi.py
new file mode 100644
index 0000000..0970610
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_gi.py
@@ -0,0 +1,220 @@
+# Copyright (c) 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2014 Cole Robinson <crobinso@redhat.com>
+# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 David Shea <dshea@redhat.com>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2016 Giuseppe Scrivano <gscrivan@redhat.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid hooks for the Python 2 GObject introspection bindings.
+
+Helps with understanding everything imported from 'gi.repository'
+"""
+
+import inspect
+import itertools
+import sys
+import re
+import warnings
+
+from astroid import MANAGER, AstroidBuildingError, nodes
+from astroid.builder import AstroidBuilder
+
+
+_inspected_modules = {}
+
+_identifier_re = r"^[A-Za-z_]\w*$"
+
+
+def _gi_build_stub(parent):
+ """
+ Inspect the passed module recursively and build stubs for functions,
+ classes, etc.
+ """
+ classes = {}
+ functions = {}
+ constants = {}
+ methods = {}
+ for name in dir(parent):
+ if name.startswith("__"):
+ continue
+
+ # Check if this is a valid name in python
+ if not re.match(_identifier_re, name):
+ continue
+
+ try:
+ obj = getattr(parent, name)
+ except:
+ continue
+
+ if inspect.isclass(obj):
+ classes[name] = obj
+ elif inspect.isfunction(obj) or inspect.isbuiltin(obj):
+ functions[name] = obj
+ elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj):
+ methods[name] = obj
+ elif (
+ str(obj).startswith("<flags")
+ or str(obj).startswith("<enum ")
+ or str(obj).startswith("<GType ")
+ or inspect.isdatadescriptor(obj)
+ ):
+ constants[name] = 0
+ elif isinstance(obj, (int, str)):
+ constants[name] = obj
+ elif callable(obj):
+ # Fall back to a function for anything callable
+ functions[name] = obj
+ else:
+ # Assume everything else is some manner of constant
+ constants[name] = 0
+
+ ret = ""
+
+ if constants:
+ ret += "# %s constants\n\n" % parent.__name__
+ for name in sorted(constants):
+ if name[0].isdigit():
+ # GDK has some busted constant names like
+ # Gdk.EventType.2BUTTON_PRESS
+ continue
+
+ val = constants[name]
+
+ strval = str(val)
+ if isinstance(val, str):
+ strval = '"%s"' % str(val).replace("\\", "\\\\")
+ ret += "%s = %s\n" % (name, strval)
+
+ if ret:
+ ret += "\n\n"
+ if functions:
+ ret += "# %s functions\n\n" % parent.__name__
+ for name in sorted(functions):
+ ret += "def %s(*args, **kwargs):\n" % name
+ ret += " pass\n"
+
+ if ret:
+ ret += "\n\n"
+ if methods:
+ ret += "# %s methods\n\n" % parent.__name__
+ for name in sorted(methods):
+ ret += "def %s(self, *args, **kwargs):\n" % name
+ ret += " pass\n"
+
+ if ret:
+ ret += "\n\n"
+ if classes:
+ ret += "# %s classes\n\n" % parent.__name__
+ for name, obj in sorted(classes.items()):
+ base = "object"
+ if issubclass(obj, Exception):
+ base = "Exception"
+ ret += "class %s(%s):\n" % (name, base)
+
+ classret = _gi_build_stub(obj)
+ if not classret:
+ classret = "pass\n"
+
+ for line in classret.splitlines():
+ ret += " " + line + "\n"
+ ret += "\n"
+
+ return ret
+
+
+def _import_gi_module(modname):
+ # we only consider gi.repository submodules
+ if not modname.startswith("gi.repository."):
+ raise AstroidBuildingError(modname=modname)
+ # build astroid representation unless we already tried so
+ if modname not in _inspected_modules:
+ modnames = [modname]
+ optional_modnames = []
+
+ # GLib and GObject may have some special case handling
+ # in pygobject that we need to cope with. However at
+ # least as of pygobject3-3.13.91 the _glib module doesn't
+ # exist anymore, so if treat these modules as optional.
+ if modname == "gi.repository.GLib":
+ optional_modnames.append("gi._glib")
+ elif modname == "gi.repository.GObject":
+ optional_modnames.append("gi._gobject")
+
+ try:
+ modcode = ""
+ for m in itertools.chain(modnames, optional_modnames):
+ try:
+ with warnings.catch_warnings():
+ # Just inspecting the code can raise gi deprecation
+ # warnings, so ignore them.
+ try:
+ from gi import PyGIDeprecationWarning, PyGIWarning
+
+ warnings.simplefilter("ignore", PyGIDeprecationWarning)
+ warnings.simplefilter("ignore", PyGIWarning)
+ except Exception:
+ pass
+
+ __import__(m)
+ modcode += _gi_build_stub(sys.modules[m])
+ except ImportError:
+ if m not in optional_modnames:
+ raise
+ except ImportError:
+ astng = _inspected_modules[modname] = None
+ else:
+ astng = AstroidBuilder(MANAGER).string_build(modcode, modname)
+ _inspected_modules[modname] = astng
+ else:
+ astng = _inspected_modules[modname]
+ if astng is None:
+ raise AstroidBuildingError(modname=modname)
+ return astng
+
+
+def _looks_like_require_version(node):
+ # Return whether this looks like a call to gi.require_version(<name>, <version>)
+ # Only accept function calls with two constant arguments
+ if len(node.args) != 2:
+ return False
+
+ if not all(isinstance(arg, nodes.Const) for arg in node.args):
+ return False
+
+ func = node.func
+ if isinstance(func, nodes.Attribute):
+ if func.attrname != "require_version":
+ return False
+ if isinstance(func.expr, nodes.Name) and func.expr.name == "gi":
+ return True
+
+ return False
+
+ if isinstance(func, nodes.Name):
+ return func.name == "require_version"
+
+ return False
+
+
+def _register_require_version(node):
+ # Load the gi.require_version locally
+ try:
+ import gi
+
+ gi.require_version(node.args[0].value, node.args[1].value)
+ except Exception:
+ pass
+
+ return node
+
+
+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/site-packages/astroid/brain/brain_hashlib.py b/venv/Lib/site-packages/astroid/brain/brain_hashlib.py
new file mode 100644
index 0000000..98ae774
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_hashlib.py
@@ -0,0 +1,67 @@
+# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2018 Ioana Tagirta <ioana.tagirta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+import sys
+
+import six
+
+import astroid
+
+PY36 = sys.version_info >= (3, 6)
+
+
+def _hashlib_transform():
+ signature = "value=''"
+ template = """
+ class %(name)s(object):
+ def __init__(self, %(signature)s): pass
+ def digest(self):
+ return %(digest)s
+ def copy(self):
+ return self
+ def update(self, value): pass
+ def hexdigest(self):
+ 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"], signature
+ )
+ if PY36:
+ 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"
+ 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"
+ new_algorithms = dict.fromkeys(
+ ["sha3_224", "sha3_256", "sha3_384", "sha3_512", "shake_128", "shake_256"],
+ signature,
+ )
+ algorithms_with_signature.update(new_algorithms)
+ algorithms_with_signature.update(
+ {"blake2b": blake2b_signature, "blake2s": blake2s_signature}
+ )
+ classes = "".join(
+ template
+ % {
+ "name": hashfunc,
+ "digest": 'b""' if six.PY3 else '""',
+ "signature": signature,
+ }
+ for hashfunc, signature in algorithms_with_signature.items()
+ )
+ return astroid.parse(classes)
+
+
+astroid.register_module_extender(astroid.MANAGER, "hashlib", _hashlib_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_http.py b/venv/Lib/site-packages/astroid/brain/brain_http.py
new file mode 100644
index 0000000..a3aa814
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_http.py
@@ -0,0 +1,201 @@
+# Copyright (c) 2018 Claudiu Popa <pcmanticore@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid brain hints for some of the `http` module."""
+import textwrap
+
+import astroid
+from astroid.builder import AstroidBuilder
+
+
+def _http_transform():
+ code = textwrap.dedent(
+ """
+ from collections import namedtuple
+ _HTTPStatus = namedtuple('_HTTPStatus', 'value phrase description')
+
+ class HTTPStatus:
+
+ # informational
+ CONTINUE = _HTTPStatus(100, 'Continue', 'Request received, please continue')
+ SWITCHING_PROTOCOLS = _HTTPStatus(101, 'Switching Protocols',
+ 'Switching to new protocol; obey Upgrade header')
+ PROCESSING = _HTTPStatus(102, 'Processing', '')
+ OK = _HTTPStatus(200, 'OK', 'Request fulfilled, document follows')
+ CREATED = _HTTPStatus(201, 'Created', 'Document created, URL follows')
+ ACCEPTED = _HTTPStatus(202, 'Accepted',
+ 'Request accepted, processing continues off-line')
+ NON_AUTHORITATIVE_INFORMATION = _HTTPStatus(203,
+ 'Non-Authoritative Information', 'Request fulfilled from cache')
+ NO_CONTENT = _HTTPStatus(204, 'No Content', 'Request fulfilled, nothing follows')
+ RESET_CONTENT =_HTTPStatus(205, 'Reset Content', 'Clear input form for further input')
+ PARTIAL_CONTENT = _HTTPStatus(206, 'Partial Content', 'Partial content follows')
+ MULTI_STATUS = _HTTPStatus(207, 'Multi-Status', '')
+ ALREADY_REPORTED = _HTTPStatus(208, 'Already Reported', '')
+ IM_USED = _HTTPStatus(226, 'IM Used', '')
+ MULTIPLE_CHOICES = _HTTPStatus(300, 'Multiple Choices',
+ 'Object has several resources -- see URI list')
+ MOVED_PERMANENTLY = _HTTPStatus(301, 'Moved Permanently',
+ 'Object moved permanently -- see URI list')
+ FOUND = _HTTPStatus(302, 'Found', 'Object moved temporarily -- see URI list')
+ SEE_OTHER = _HTTPStatus(303, 'See Other', 'Object moved -- see Method and URL list')
+ NOT_MODIFIED = _HTTPStatus(304, 'Not Modified',
+ 'Document has not changed since given time')
+ USE_PROXY = _HTTPStatus(305, 'Use Proxy',
+ 'You must use proxy specified in Location to access this resource')
+ TEMPORARY_REDIRECT = _HTTPStatus(307, 'Temporary Redirect',
+ 'Object moved temporarily -- see URI list')
+ PERMANENT_REDIRECT = _HTTPStatus(308, 'Permanent Redirect',
+ 'Object moved permanently -- see URI list')
+ BAD_REQUEST = _HTTPStatus(400, 'Bad Request',
+ 'Bad request syntax or unsupported method')
+ UNAUTHORIZED = _HTTPStatus(401, 'Unauthorized',
+ 'No permission -- see authorization schemes')
+ PAYMENT_REQUIRED = _HTTPStatus(402, 'Payment Required',
+ 'No payment -- see charging schemes')
+ FORBIDDEN = _HTTPStatus(403, 'Forbidden',
+ 'Request forbidden -- authorization will not help')
+ NOT_FOUND = _HTTPStatus(404, 'Not Found',
+ 'Nothing matches the given URI')
+ METHOD_NOT_ALLOWED = _HTTPStatus(405, 'Method Not Allowed',
+ 'Specified method is invalid for this resource')
+ NOT_ACCEPTABLE = _HTTPStatus(406, 'Not Acceptable',
+ 'URI not available in preferred format')
+ PROXY_AUTHENTICATION_REQUIRED = _HTTPStatus(407,
+ 'Proxy Authentication Required',
+ 'You must authenticate with this proxy before proceeding')
+ REQUEST_TIMEOUT = _HTTPStatus(408, 'Request Timeout',
+ 'Request timed out; try again later')
+ CONFLICT = _HTTPStatus(409, 'Conflict', 'Request conflict')
+ GONE = _HTTPStatus(410, 'Gone',
+ 'URI no longer exists and has been permanently removed')
+ LENGTH_REQUIRED = _HTTPStatus(411, 'Length Required',
+ 'Client must specify Content-Length')
+ PRECONDITION_FAILED = _HTTPStatus(412, 'Precondition Failed',
+ 'Precondition in headers is false')
+ REQUEST_ENTITY_TOO_LARGE = _HTTPStatus(413, 'Request Entity Too Large',
+ 'Entity is too large')
+ REQUEST_URI_TOO_LONG = _HTTPStatus(414, 'Request-URI Too Long',
+ 'URI is too long')
+ UNSUPPORTED_MEDIA_TYPE = _HTTPStatus(415, 'Unsupported Media Type',
+ 'Entity body in unsupported format')
+ REQUESTED_RANGE_NOT_SATISFIABLE = _HTTPStatus(416,
+ 'Requested Range Not Satisfiable',
+ 'Cannot satisfy request range')
+ EXPECTATION_FAILED = _HTTPStatus(417, 'Expectation Failed',
+ 'Expect condition could not be satisfied')
+ MISDIRECTED_REQUEST = _HTTPStatus(421, 'Misdirected Request',
+ 'Server is not able to produce a response')
+ UNPROCESSABLE_ENTITY = _HTTPStatus(422, 'Unprocessable Entity')
+ LOCKED = _HTTPStatus(423, 'Locked')
+ FAILED_DEPENDENCY = _HTTPStatus(424, 'Failed Dependency')
+ UPGRADE_REQUIRED = _HTTPStatus(426, 'Upgrade Required')
+ PRECONDITION_REQUIRED = _HTTPStatus(428, 'Precondition Required',
+ 'The origin server requires the request to be conditional')
+ TOO_MANY_REQUESTS = _HTTPStatus(429, 'Too Many Requests',
+ 'The user has sent too many requests in '
+ 'a given amount of time ("rate limiting")')
+ REQUEST_HEADER_FIELDS_TOO_LARGE = _HTTPStatus(431,
+ 'Request Header Fields Too Large',
+ 'The server is unwilling to process the request because its header '
+ 'fields are too large')
+ UNAVAILABLE_FOR_LEGAL_REASONS = _HTTPStatus(451,
+ 'Unavailable For Legal Reasons',
+ 'The server is denying access to the '
+ 'resource as a consequence of a legal demand')
+ INTERNAL_SERVER_ERROR = _HTTPStatus(500, 'Internal Server Error',
+ 'Server got itself in trouble')
+ NOT_IMPLEMENTED = _HTTPStatus(501, 'Not Implemented',
+ 'Server does not support this operation')
+ BAD_GATEWAY = _HTTPStatus(502, 'Bad Gateway',
+ 'Invalid responses from another server/proxy')
+ SERVICE_UNAVAILABLE = _HTTPStatus(503, 'Service Unavailable',
+ 'The server cannot process the request due to a high load')
+ GATEWAY_TIMEOUT = _HTTPStatus(504, 'Gateway Timeout',
+ 'The gateway server did not receive a timely response')
+ HTTP_VERSION_NOT_SUPPORTED = _HTTPStatus(505, 'HTTP Version Not Supported',
+ 'Cannot fulfill request')
+ VARIANT_ALSO_NEGOTIATES = _HTTPStatus(506, 'Variant Also Negotiates')
+ INSUFFICIENT_STORAGE = _HTTPStatus(507, 'Insufficient Storage')
+ LOOP_DETECTED = _HTTPStatus(508, 'Loop Detected')
+ NOT_EXTENDED = _HTTPStatus(510, 'Not Extended')
+ NETWORK_AUTHENTICATION_REQUIRED = _HTTPStatus(511,
+ 'Network Authentication Required',
+ 'The client needs to authenticate to gain network access')
+ """
+ )
+ return AstroidBuilder(astroid.MANAGER).string_build(code)
+
+
+def _http_client_transform():
+ return AstroidBuilder(astroid.MANAGER).string_build(
+ textwrap.dedent(
+ """
+ from http import HTTPStatus
+
+ CONTINUE = HTTPStatus.CONTINUE
+ SWITCHING_PROTOCOLS = HTTPStatus.SWITCHING_PROTOCOLS
+ PROCESSING = HTTPStatus.PROCESSING
+ OK = HTTPStatus.OK
+ CREATED = HTTPStatus.CREATED
+ ACCEPTED = HTTPStatus.ACCEPTED
+ NON_AUTHORITATIVE_INFORMATION = HTTPStatus.NON_AUTHORITATIVE_INFORMATION
+ NO_CONTENT = HTTPStatus.NO_CONTENT
+ RESET_CONTENT = HTTPStatus.RESET_CONTENT
+ PARTIAL_CONTENT = HTTPStatus.PARTIAL_CONTENT
+ MULTI_STATUS = HTTPStatus.MULTI_STATUS
+ ALREADY_REPORTED = HTTPStatus.ALREADY_REPORTED
+ IM_USED = HTTPStatus.IM_USED
+ MULTIPLE_CHOICES = HTTPStatus.MULTIPLE_CHOICES
+ MOVED_PERMANENTLY = HTTPStatus.MOVED_PERMANENTLY
+ FOUND = HTTPStatus.FOUND
+ SEE_OTHER = HTTPStatus.SEE_OTHER
+ NOT_MODIFIED = HTTPStatus.NOT_MODIFIED
+ USE_PROXY = HTTPStatus.USE_PROXY
+ TEMPORARY_REDIRECT = HTTPStatus.TEMPORARY_REDIRECT
+ PERMANENT_REDIRECT = HTTPStatus.PERMANENT_REDIRECT
+ BAD_REQUEST = HTTPStatus.BAD_REQUEST
+ UNAUTHORIZED = HTTPStatus.UNAUTHORIZED
+ PAYMENT_REQUIRED = HTTPStatus.PAYMENT_REQUIRED
+ FORBIDDEN = HTTPStatus.FORBIDDEN
+ NOT_FOUND = HTTPStatus.NOT_FOUND
+ METHOD_NOT_ALLOWED = HTTPStatus.METHOD_NOT_ALLOWED
+ NOT_ACCEPTABLE = HTTPStatus.NOT_ACCEPTABLE
+ PROXY_AUTHENTICATION_REQUIRED = HTTPStatus.PROXY_AUTHENTICATION_REQUIRED
+ REQUEST_TIMEOUT = HTTPStatus.REQUEST_TIMEOUT
+ CONFLICT = HTTPStatus.CONFLICT
+ GONE = HTTPStatus.GONE
+ LENGTH_REQUIRED = HTTPStatus.LENGTH_REQUIRED
+ PRECONDITION_FAILED = HTTPStatus.PRECONDITION_FAILED
+ REQUEST_ENTITY_TOO_LARGE = HTTPStatus.REQUEST_ENTITY_TOO_LARGE
+ REQUEST_URI_TOO_LONG = HTTPStatus.REQUEST_URI_TOO_LONG
+ UNSUPPORTED_MEDIA_TYPE = HTTPStatus.UNSUPPORTED_MEDIA_TYPE
+ REQUESTED_RANGE_NOT_SATISFIABLE = HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE
+ EXPECTATION_FAILED = HTTPStatus.EXPECTATION_FAILED
+ UNPROCESSABLE_ENTITY = HTTPStatus.UNPROCESSABLE_ENTITY
+ LOCKED = HTTPStatus.LOCKED
+ FAILED_DEPENDENCY = HTTPStatus.FAILED_DEPENDENCY
+ UPGRADE_REQUIRED = HTTPStatus.UPGRADE_REQUIRED
+ PRECONDITION_REQUIRED = HTTPStatus.PRECONDITION_REQUIRED
+ TOO_MANY_REQUESTS = HTTPStatus.TOO_MANY_REQUESTS
+ REQUEST_HEADER_FIELDS_TOO_LARGE = HTTPStatus.REQUEST_HEADER_FIELDS_TOO_LARGE
+ INTERNAL_SERVER_ERROR = HTTPStatus.INTERNAL_SERVER_ERROR
+ NOT_IMPLEMENTED = HTTPStatus.NOT_IMPLEMENTED
+ BAD_GATEWAY = HTTPStatus.BAD_GATEWAY
+ SERVICE_UNAVAILABLE = HTTPStatus.SERVICE_UNAVAILABLE
+ GATEWAY_TIMEOUT = HTTPStatus.GATEWAY_TIMEOUT
+ HTTP_VERSION_NOT_SUPPORTED = HTTPStatus.HTTP_VERSION_NOT_SUPPORTED
+ VARIANT_ALSO_NEGOTIATES = HTTPStatus.VARIANT_ALSO_NEGOTIATES
+ INSUFFICIENT_STORAGE = HTTPStatus.INSUFFICIENT_STORAGE
+ LOOP_DETECTED = HTTPStatus.LOOP_DETECTED
+ NOT_EXTENDED = HTTPStatus.NOT_EXTENDED
+ NETWORK_AUTHENTICATION_REQUIRED = HTTPStatus.NETWORK_AUTHENTICATION_REQUIRED
+ """
+ )
+ )
+
+
+astroid.register_module_extender(astroid.MANAGER, "http", _http_transform)
+astroid.register_module_extender(astroid.MANAGER, "http.client", _http_client_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_io.py b/venv/Lib/site-packages/astroid/brain/brain_io.py
new file mode 100644
index 0000000..4c68922
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_io.py
@@ -0,0 +1,45 @@
+# Copyright (c) 2016 Claudiu Popa <pcmanticore@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid brain hints for some of the _io C objects."""
+
+import astroid
+
+
+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 = astroid.MANAGER.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)
+
+
+astroid.MANAGER.register_transform(
+ astroid.ClassDef, _transform_buffered, lambda node: node.name in BUFFERED
+)
+astroid.MANAGER.register_transform(
+ astroid.ClassDef,
+ _transform_text_io_wrapper,
+ lambda node: node.name == TextIOWrapper,
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_mechanize.py b/venv/Lib/site-packages/astroid/brain/brain_mechanize.py
new file mode 100644
index 0000000..93f282e
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_mechanize.py
@@ -0,0 +1,29 @@
+# Copyright (c) 2012-2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+from astroid import MANAGER, register_module_extender
+from astroid.builder import AstroidBuilder
+
+
+def mechanize_transform():
+ return AstroidBuilder(MANAGER).string_build(
+ """
+
+class Browser(object):
+ 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
+
+"""
+ )
+
+
+register_module_extender(MANAGER, "mechanize", mechanize_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_multiprocessing.py b/venv/Lib/site-packages/astroid/brain/brain_multiprocessing.py
new file mode 100644
index 0000000..71256ee
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_multiprocessing.py
@@ -0,0 +1,106 @@
+# Copyright (c) 2016 Claudiu Popa <pcmanticore@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+import sys
+
+import astroid
+from astroid import exceptions
+
+
+def _multiprocessing_transform():
+ module = astroid.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 = astroid.parse(
+ """
+ from multiprocessing.context import DefaultContext, BaseContext
+ default = DefaultContext()
+ base = BaseContext()
+ """
+ )
+ try:
+ context = next(node["default"].infer())
+ base = next(node["base"].infer())
+ except exceptions.InferenceError:
+ return module
+
+ for node in (context, base):
+ for key, value in node.locals.items():
+ if key.startswith("_"):
+ continue
+
+ value = value[0]
+ if isinstance(value, astroid.FunctionDef):
+ # We need to rebound this, since otherwise
+ # it will have an extra argument (self).
+ value = astroid.BoundMethod(value, node)
+ module[key] = value
+ return module
+
+
+def _multiprocessing_managers_transform():
+ return astroid.parse(
+ """
+ import array
+ import threading
+ import multiprocessing.pool as pool
+
+ import six
+
+ 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 = six.moves.queue.Queue
+ Event = threading.Event
+ RLock = threading.RLock
+ 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
+ """
+ )
+
+
+astroid.register_module_extender(
+ astroid.MANAGER, "multiprocessing.managers", _multiprocessing_managers_transform
+)
+astroid.register_module_extender(
+ astroid.MANAGER, "multiprocessing", _multiprocessing_transform
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_namedtuple_enum.py b/venv/Lib/site-packages/astroid/brain/brain_namedtuple_enum.py
new file mode 100644
index 0000000..de24067
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_namedtuple_enum.py
@@ -0,0 +1,449 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2012-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
+# Copyright (c) 2015 David Shea <dshea@redhat.com>
+# Copyright (c) 2015 Philip Lorenz <philip@bithub.de>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2016 Mateusz Bysiek <mb@mbdev.pl>
+# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid hooks for the Python standard library."""
+
+import functools
+import keyword
+from textwrap import dedent
+
+from astroid import MANAGER, UseInferenceDefault, inference_tip, InferenceError
+from astroid import arguments
+from astroid import exceptions
+from astroid import nodes
+from astroid.builder import AstroidBuilder, extract_node
+from astroid import util
+
+
+TYPING_NAMEDTUPLE_BASENAMES = {"NamedTuple", "typing.NamedTuple"}
+ENUM_BASE_NAMES = {
+ "Enum",
+ "IntEnum",
+ "enum.Enum",
+ "enum.IntEnum",
+ "IntFlag",
+ "enum.IntFlag",
+}
+
+
+def _infer_first(node, context):
+ if node is util.Uninferable:
+ raise UseInferenceDefault
+ try:
+ value = next(node.infer(context=context))
+ if value is util.Uninferable:
+ raise UseInferenceDefault()
+ else:
+ return value
+ except StopIteration:
+ raise InferenceError()
+
+
+def _find_func_form_arguments(node, context):
+ def _extract_namedtuple_arg_or_keyword(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, base_type, context=None, enum=False):
+ """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 = names.value.replace(",", " ").split()
+ except AttributeError:
+ if not enum:
+ attributes = [
+ _infer_first(const, context).value for const in names.elts
+ ]
+ 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
+ if not attributes:
+ raise AttributeError
+ except (AttributeError, exceptions.InferenceError):
+ raise UseInferenceDefault()
+
+ # 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, "docstring")
+ class_node.parent = node.parent
+ # set base class=tuple
+ class_node.bases.append(base_type)
+ # 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):
+ 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, context=None):
+ """Specific inference function for namedtuple Call node"""
+ tuple_base_name = nodes.Name(name="tuple", parent=node.root())
+ class_node, name, attributes = infer_func_form(
+ node, tuple_base_name, context=context
+ )
+ call_site = arguments.CallSite.from_call(node)
+ func = next(extract_node("import collections; collections.namedtuple").infer())
+ try:
+ rename = next(call_site.infer_argument(func, "rename", context)).bool_value()
+ except InferenceError:
+ rename = False
+
+ if rename:
+ attributes = _get_renamed_namedtuple_attributes(attributes)
+
+ replace_args = ", ".join("{arg}=None".format(arg=arg) 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(MANAGER).string_build(
+ """
+class %(name)s(tuple):
+ __slots__ = ()
+ _fields = %(fields)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)s):
+ return self
+ def __getnewargs__(self):
+ return tuple(self)
+%(field_defs)s
+ """
+ % {
+ "name": name,
+ "fields": attributes,
+ "field_defs": field_defs,
+ "replace_args": replace_args,
+ }
+ )
+ class_node.locals["_asdict"] = fake.body[0].locals["_asdict"]
+ class_node.locals["_make"] = fake.body[0].locals["_make"]
+ class_node.locals["_replace"] = fake.body[0].locals["_replace"]
+ class_node.locals["_fields"] = fake.body[0].locals["_fields"]
+ for attr in attributes:
+ class_node.locals[attr] = fake.body[0].locals[attr]
+ # we use UseInferenceDefault, we can't be a generator so return an iterator
+ return iter([class_node])
+
+
+def _get_renamed_namedtuple_attributes(field_names):
+ names = list(field_names)
+ seen = set()
+ for i, name in enumerate(field_names):
+ if (
+ not all(c.isalnum() or c == "_" for c in name)
+ or keyword.iskeyword(name)
+ or not name
+ or name[0].isdigit()
+ or name.startswith("_")
+ or name in seen
+ ):
+ names[i] = "_%d" % i
+ seen.add(name)
+ return tuple(names)
+
+
+def infer_enum(node, context=None):
+ """ Specific inference function for enum Call node. """
+ enum_meta = extract_node(
+ """
+ class EnumMeta(object):
+ 'docstring'
+ def __call__(self, node):
+ class EnumAttribute(object):
+ name = ''
+ value = 0
+ return EnumAttribute()
+ def __iter__(self):
+ class EnumAttribute(object):
+ name = ''
+ value = 0
+ return [EnumAttribute()]
+ def __reversed__(self):
+ class EnumAttribute(object):
+ name = ''
+ value = 0
+ return (EnumAttribute, )
+ def __next__(self):
+ return next(iter(self))
+ def __getitem__(self, attr):
+ class Value(object):
+ @property
+ def name(self):
+ return ''
+ @property
+ def value(self):
+ return attr
+
+ return Value()
+ __members__ = ['']
+ """
+ )
+ class_node = infer_func_form(node, enum_meta, context=context, enum=True)[0]
+ return iter([class_node.instantiate_class()])
+
+
+INT_FLAG_ADDITION_METHODS = """
+ def __or__(self, other):
+ return {name}(self.value | other.value)
+ def __and__(self, other):
+ return {name}(self.value & other.value)
+ def __xor__(self, other):
+ return {name}(self.value ^ other.value)
+ def __add__(self, other):
+ return {name}(self.value + other.value)
+ def __div__(self, other):
+ return {name}(self.value / other.value)
+ def __invert__(self):
+ return {name}(~self.value)
+ def __mul__(self, other):
+ return {name}(self.value * other.value)
+"""
+
+
+def infer_enum_class(node):
+ """ Specific inference for enums. """
+ for basename in node.basenames:
+ # TODO: doesn't handle subclasses yet. This implementation
+ # is a hack to support enums.
+ if basename not in ENUM_BASE_NAMES:
+ continue
+ if node.root().name == "enum":
+ # Skip if the class is directly from enum module.
+ break
+ for local, values in node.locals.items():
+ if any(not isinstance(value, nodes.AssignName) for value in values):
+ continue
+
+ targets = []
+ 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]
+
+ inferred_return_value = None
+ if isinstance(stmt, nodes.Assign):
+ 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:
+ # Replace all the assignments with our mocked class.
+ classdef = dedent(
+ """
+ class {name}({types}):
+ @property
+ def value(self):
+ return {return_value}
+ @property
+ def name(self):
+ return "{name}"
+ """.format(
+ name=target.name,
+ types=", ".join(node.basenames),
+ return_value=inferred_return_value,
+ )
+ )
+ if "IntFlag" in basename:
+ # Alright, we need to add some additional methods.
+ # Unfortunately we still can't infer the resulting objects as
+ # Enum members, but once we'll be able to do that, the following
+ # should result in some nice symbolic execution
+ classdef += INT_FLAG_ADDITION_METHODS.format(name=target.name)
+
+ fake = AstroidBuilder(MANAGER).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())
+ node.locals[local] = new_targets
+ break
+ return node
+
+
+def infer_typing_namedtuple_class(class_node, context=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)
+ generated_class_node = next(infer_named_tuple(node, context))
+ for method in class_node.mymethods():
+ generated_class_node.locals[method.name] = [method]
+
+ for assign in class_node.body:
+ if not isinstance(assign, nodes.Assign):
+ continue
+
+ for target in assign.targets:
+ attr = target.name
+ generated_class_node.locals[attr] = class_node.locals[attr]
+
+ return iter((generated_class_node,))
+
+
+def infer_typing_namedtuple(node, context=None):
+ """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:
+ raise UseInferenceDefault
+
+ if func.qname() != "typing.NamedTuple":
+ raise UseInferenceDefault
+
+ if len(node.args) != 2:
+ raise UseInferenceDefault
+
+ if not isinstance(node.args[1], (nodes.List, nodes.Tuple)):
+ raise UseInferenceDefault
+
+ names = []
+ for elt in node.args[1].elts:
+ if not isinstance(elt, (nodes.List, nodes.Tuple)):
+ raise UseInferenceDefault
+ if len(elt.elts) != 2:
+ raise UseInferenceDefault
+ names.append(elt.elts[0].as_string())
+
+ typename = node.args[0].as_string()
+ if names:
+ field_names = "({},)".format(",".join(names))
+ else:
+ field_names = "''"
+ node = extract_node(
+ "namedtuple({typename}, {fields})".format(typename=typename, fields=field_names)
+ )
+ return infer_named_tuple(node, context)
+
+
+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=lambda cls: any(
+ basename for basename in cls.basenames if basename in ENUM_BASE_NAMES
+ ),
+)
+MANAGER.register_transform(
+ nodes.ClassDef, inference_tip(infer_typing_namedtuple_class), _has_namedtuple_base
+)
+MANAGER.register_transform(
+ nodes.Call, inference_tip(infer_typing_namedtuple), _looks_like_typing_namedtuple
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_nose.py b/venv/Lib/site-packages/astroid/brain/brain_nose.py
new file mode 100644
index 0000000..7b12d76
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_nose.py
@@ -0,0 +1,77 @@
+# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""Hooks for nose library."""
+
+import re
+import textwrap
+
+import astroid
+import astroid.builder
+
+_BUILDER = astroid.builder.AstroidBuilder(astroid.MANAGER)
+
+
+def _pep8(name, caps=re.compile("([A-Z])")):
+ return caps.sub(lambda m: "_" + m.groups()[0].lower(), name)
+
+
+def _nose_tools_functions():
+ """Get an iterator of names and bound methods."""
+ module = _BUILDER.string_build(
+ textwrap.dedent(
+ """
+ import unittest
+
+ class Test(unittest.TestCase):
+ pass
+ a = Test()
+ """
+ )
+ )
+ try:
+ case = next(module["a"].infer())
+ except astroid.InferenceError:
+ return
+ for method in case.methods():
+ if method.name.startswith("assert") and "_" not in method.name:
+ pep8_name = _pep8(method.name)
+ yield pep8_name, astroid.BoundMethod(method, case)
+ if method.name == "assertEqual":
+ # nose also exports assert_equals.
+ yield "assert_equals", astroid.BoundMethod(method, case)
+
+
+def _nose_tools_transform(node):
+ for method_name, method in _nose_tools_functions():
+ node.locals[method_name] = [method]
+
+
+def _nose_tools_trivial_transform():
+ """Custom transform for the nose.tools module."""
+ stub = _BUILDER.string_build("""__all__ = []""")
+ all_entries = ["ok_", "eq_"]
+
+ for pep8_name, method in _nose_tools_functions():
+ all_entries.append(pep8_name)
+ stub[pep8_name] = method
+
+ # Update the __all__ variable, since nose.tools
+ # does this manually with .append.
+ all_assign = stub["__all__"].parent
+ all_object = astroid.List(all_entries)
+ all_object.parent = all_assign
+ all_assign.value = all_object
+ return stub
+
+
+astroid.register_module_extender(
+ astroid.MANAGER, "nose.tools.trivial", _nose_tools_trivial_transform
+)
+astroid.MANAGER.register_transform(
+ astroid.Module, _nose_tools_transform, lambda n: n.name == "nose.tools"
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py
new file mode 100644
index 0000000..43b30e4
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py
@@ -0,0 +1,23 @@
+# Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""Astroid hooks for numpy.core.fromnumeric module."""
+
+import astroid
+
+
+def numpy_core_fromnumeric_transform():
+ return astroid.parse(
+ """
+ def sum(a, axis=None, dtype=None, out=None, keepdims=None, initial=None):
+ return numpy.ndarray([0, 0])
+ """
+ )
+
+
+astroid.register_module_extender(
+ astroid.MANAGER, "numpy.core.fromnumeric", numpy_core_fromnumeric_transform
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_numpy_core_function_base.py b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_function_base.py
new file mode 100644
index 0000000..05a73d9
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_function_base.py
@@ -0,0 +1,29 @@
+# Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""Astroid hooks for numpy.core.function_base module."""
+
+import functools
+import astroid
+from brain_numpy_utils import looks_like_numpy_member, infer_numpy_member
+
+
+METHODS_TO_BE_INFERRED = {
+ "linspace": """def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0):
+ return numpy.ndarray([0, 0])""",
+ "logspace": """def logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, axis=0):
+ return numpy.ndarray([0, 0])""",
+ "geomspace": """def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0):
+ return numpy.ndarray([0, 0])""",
+}
+
+for func_name, func_src in METHODS_TO_BE_INFERRED.items():
+ inference_function = functools.partial(infer_numpy_member, func_src)
+ astroid.MANAGER.register_transform(
+ astroid.Attribute,
+ astroid.inference_tip(inference_function),
+ functools.partial(looks_like_numpy_member, func_name),
+ )
diff --git a/venv/Lib/site-packages/astroid/brain/brain_numpy_core_multiarray.py b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_multiarray.py
new file mode 100644
index 0000000..3032acc
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_multiarray.py
@@ -0,0 +1,55 @@
+# Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""Astroid hooks for numpy.core.multiarray module."""
+
+import functools
+import astroid
+from brain_numpy_utils import looks_like_numpy_member, infer_numpy_member
+
+
+def numpy_core_multiarray_transform():
+ return astroid.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])
+ """
+ )
+
+
+astroid.register_module_extender(
+ astroid.MANAGER, "numpy.core.multiarray", numpy_core_multiarray_transform
+)
+
+
+METHODS_TO_BE_INFERRED = {
+ "array": """def array(object, dtype=None, copy=True, order='K', subok=False, ndmin=0):
+ return numpy.ndarray([0, 0])""",
+ "dot": """def dot(a, b, out=None):
+ return numpy.ndarray([0, 0])""",
+ "empty_like": """def empty_like(a, dtype=None, order='K', subok=True):
+ return numpy.ndarray((0, 0))""",
+ "concatenate": """def concatenate(arrays, axis=None, out=None):
+ return numpy.ndarray((0, 0))""",
+ "where": """def where(condition, x=None, y=None):
+ return numpy.ndarray([0, 0])""",
+ "empty": """def empty(shape, dtype=float, order='C'):
+ return numpy.ndarray([0, 0])""",
+ "zeros": """def zeros(shape, dtype=float, order='C'):
+ return numpy.ndarray([0, 0])""",
+}
+
+for method_name, function_src in METHODS_TO_BE_INFERRED.items():
+ inference_function = functools.partial(infer_numpy_member, function_src)
+ astroid.MANAGER.register_transform(
+ astroid.Attribute,
+ astroid.inference_tip(inference_function),
+ functools.partial(looks_like_numpy_member, method_name),
+ )
diff --git a/venv/Lib/site-packages/astroid/brain/brain_numpy_core_numeric.py b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_numeric.py
new file mode 100644
index 0000000..ba43c94
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_numeric.py
@@ -0,0 +1,43 @@
+# Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""Astroid hooks for numpy.core.numeric module."""
+
+import functools
+import astroid
+from brain_numpy_utils import looks_like_numpy_member, infer_numpy_member
+
+
+def numpy_core_numeric_transform():
+ return astroid.parse(
+ """
+ # different functions defined in numeric.py
+ import numpy
+ def zeros_like(a, dtype=None, order='K', subok=True): return numpy.ndarray((0, 0))
+ def ones_like(a, dtype=None, order='K', subok=True): return numpy.ndarray((0, 0))
+ def full_like(a, fill_value, dtype=None, order='K', subok=True): return numpy.ndarray((0, 0))
+ """
+ )
+
+
+astroid.register_module_extender(
+ astroid.MANAGER, "numpy.core.numeric", numpy_core_numeric_transform
+)
+
+
+METHODS_TO_BE_INFERRED = {
+ "ones": """def ones(shape, dtype=None, order='C'):
+ return numpy.ndarray([0, 0])"""
+}
+
+
+for method_name, function_src in METHODS_TO_BE_INFERRED.items():
+ inference_function = functools.partial(infer_numpy_member, function_src)
+ astroid.MANAGER.register_transform(
+ astroid.Attribute,
+ astroid.inference_tip(inference_function),
+ functools.partial(looks_like_numpy_member, method_name),
+ )
diff --git a/venv/Lib/site-packages/astroid/brain/brain_numpy_core_numerictypes.py b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_numerictypes.py
new file mode 100644
index 0000000..42021fa
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_numerictypes.py
@@ -0,0 +1,250 @@
+# Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+# TODO(hippo91) : correct the methods signature.
+
+"""Astroid hooks for numpy.core.numerictypes module."""
+
+import astroid
+
+
+def numpy_core_numerictypes_transform():
+ return astroid.parse(
+ """
+ # different types defined in numerictypes.py
+ class generic(object):
+ def __init__(self, value):
+ self.T = None
+ self.base = None
+ self.data = None
+ self.dtype = None
+ self.flags = None
+ self.flat = None
+ 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): return uninferable
+ 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
+
+
+ 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
+ """
+ )
+
+
+astroid.register_module_extender(
+ astroid.MANAGER, "numpy.core.numerictypes", numpy_core_numerictypes_transform
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_numpy_core_umath.py b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_umath.py
new file mode 100644
index 0000000..459d38c
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_numpy_core_umath.py
@@ -0,0 +1,105 @@
+# Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""Astroid hooks for numpy.core.umath module."""
+
+import astroid
+
+
+def numpy_core_umath_transform():
+ ufunc_optional_keyword_arguments = (
+ """out=None, where=True, casting='same_kind', order='K', """
+ """dtype=None, subok=True"""
+ )
+ return astroid.parse(
+ """
+ # Constants
+ e = 2.718281828459045
+ euler_gamma = 0.5772156649015329
+
+ # No arg functions
+ def geterrobj(): return []
+
+ # One arg functions
+ def seterrobj(errobj): return None
+
+ # One arg functions with optional kwargs
+ def arccos(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def arccosh(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def arcsin(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def arcsinh(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def arctan(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def arctanh(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def cbrt(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def conj(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def conjugate(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def cosh(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def deg2rad(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def degrees(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def exp2(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def expm1(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def fabs(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def frexp(x, {opt_args:s}): return (numpy.ndarray((0, 0)), numpy.ndarray((0, 0)))
+ def isfinite(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def isinf(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def log(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def log1p(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def log2(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def logical_not(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def modf(x, {opt_args:s}): return (numpy.ndarray((0, 0)), numpy.ndarray((0, 0)))
+ def negative(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def rad2deg(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def radians(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def reciprocal(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def rint(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def sign(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def signbit(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def sinh(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def spacing(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def square(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def tan(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def tanh(x, {opt_args:s}): return numpy.ndarray((0, 0))
+ def trunc(x, {opt_args:s}): return numpy.ndarray((0, 0))
+
+ # Two args functions with optional kwargs
+ def bitwise_and(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def bitwise_or(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def bitwise_xor(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def copysign(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def divide(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def equal(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def floor_divide(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def fmax(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def fmin(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def fmod(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def greater(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def hypot(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def ldexp(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def left_shift(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def less(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def logaddexp(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def logaddexp2(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def logical_and(x1, x2, {opt_args:s}): return numpy.ndarray([0, 0])
+ def logical_or(x1, x2, {opt_args:s}): return numpy.ndarray([0, 0])
+ def logical_xor(x1, x2, {opt_args:s}): return numpy.ndarray([0, 0])
+ def maximum(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def minimum(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def nextafter(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def not_equal(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def power(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def remainder(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def right_shift(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def subtract(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ def true_divide(x1, x2, {opt_args:s}): return numpy.ndarray((0, 0))
+ """.format(
+ opt_args=ufunc_optional_keyword_arguments
+ )
+ )
+
+
+astroid.register_module_extender(
+ astroid.MANAGER, "numpy.core.umath", numpy_core_umath_transform
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_numpy_ndarray.py b/venv/Lib/site-packages/astroid/brain/brain_numpy_ndarray.py
new file mode 100644
index 0000000..8c231a3
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_numpy_ndarray.py
@@ -0,0 +1,153 @@
+# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""Astroid hooks for numpy ndarray class."""
+
+import functools
+import astroid
+
+
+def infer_numpy_ndarray(node, context=None):
+ ndarray = """
+ class ndarray(object):
+ def __init__(self, shape, dtype=float, buffer=None, offset=0,
+ strides=None, order=None):
+ self.T = None
+ self.base = None
+ self.ctypes = None
+ self.data = None
+ self.dtype = None
+ self.flags = None
+ self.flat = None
+ self.imag = None
+ self.itemsize = None
+ self.nbytes = None
+ self.ndim = None
+ self.real = None
+ self.shape = None
+ 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): 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])
+ """
+ node = astroid.extract_node(ndarray)
+ return node.infer(context=context)
+
+
+def _looks_like_numpy_ndarray(node):
+ return isinstance(node, astroid.Attribute) and node.attrname == "ndarray"
+
+
+astroid.MANAGER.register_transform(
+ astroid.Attribute,
+ astroid.inference_tip(infer_numpy_ndarray),
+ _looks_like_numpy_ndarray,
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_numpy_random_mtrand.py b/venv/Lib/site-packages/astroid/brain/brain_numpy_random_mtrand.py
new file mode 100644
index 0000000..772bfc4
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_numpy_random_mtrand.py
@@ -0,0 +1,70 @@
+# Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+# TODO(hippo91) : correct the functions return types
+"""Astroid hooks for numpy.random.mtrand module."""
+
+import astroid
+
+
+def numpy_random_mtrand_transform():
+ return astroid.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_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
+ """
+ )
+
+
+astroid.register_module_extender(
+ astroid.MANAGER, "numpy.random.mtrand", numpy_random_mtrand_transform
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_numpy_utils.py b/venv/Lib/site-packages/astroid/brain/brain_numpy_utils.py
new file mode 100644
index 0000000..2bad01e
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_numpy_utils.py
@@ -0,0 +1,56 @@
+# Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""Different utilities for the numpy brains"""
+
+
+import astroid
+
+
+def infer_numpy_member(src, node, context=None):
+ node = astroid.extract_node(src)
+ return node.infer(context=context)
+
+
+def _is_a_numpy_module(node: astroid.node_classes.Name) -> bool:
+ """
+ Returns True if the node is a representation of a numpy module.
+
+ For example in :
+ import numpy as np
+ x = np.linspace(1, 2)
+ The node <Name.np> is a representation of the numpy module.
+
+ :param node: node to test
+ :return: True if the node is a representation of the numpy module.
+ """
+ module_nickname = node.name
+ potential_import_target = [
+ x for x in node.lookup(module_nickname)[1] if isinstance(x, astroid.Import)
+ ]
+ for target in potential_import_target:
+ if ("numpy", module_nickname) in target.names:
+ return True
+ return False
+
+
+def looks_like_numpy_member(
+ member_name: str, node: astroid.node_classes.NodeNG
+) -> bool:
+ """
+ Returns True if the node is a member of numpy whose
+ name is member_name.
+
+ :param member_name: name of the member
+ :param node: node to test
+ :return: True if the node is a member of numpy
+ """
+ return (
+ isinstance(node, astroid.Attribute)
+ and node.attrname == member_name
+ and isinstance(node.expr, astroid.Name)
+ and _is_a_numpy_module(node.expr)
+ )
diff --git a/venv/Lib/site-packages/astroid/brain/brain_pkg_resources.py b/venv/Lib/site-packages/astroid/brain/brain_pkg_resources.py
new file mode 100644
index 0000000..25e7649
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_pkg_resources.py
@@ -0,0 +1,75 @@
+# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+import astroid
+from astroid import parse
+from astroid import inference_tip
+from astroid import register_module_extender
+from astroid import MANAGER
+
+
+def pkg_resources_transform():
+ return parse(
+ """
+def require(*requirements):
+ return pkg_resources.working_set.require(*requirements)
+
+def run_script(requires, script_name):
+ return pkg_resources.working_set.run_script(requires, script_name)
+
+def iter_entry_points(group, name=None):
+ return pkg_resources.working_set.iter_entry_points(group, name)
+
+def resource_exists(package_or_requirement, resource_name):
+ return get_provider(package_or_requirement).has_resource(resource_name)
+
+def resource_isdir(package_or_requirement, resource_name):
+ return get_provider(package_or_requirement).resource_isdir(
+ resource_name)
+
+def resource_filename(package_or_requirement, resource_name):
+ return get_provider(package_or_requirement).get_resource_filename(
+ self, resource_name)
+
+def resource_stream(package_or_requirement, resource_name):
+ return get_provider(package_or_requirement).get_resource_stream(
+ self, resource_name)
+
+def resource_string(package_or_requirement, resource_name):
+ return get_provider(package_or_requirement).get_resource_string(
+ self, resource_name)
+
+def resource_listdir(package_or_requirement, resource_name):
+ return get_provider(package_or_requirement).resource_listdir(
+ resource_name)
+
+def extraction_error():
+ pass
+
+def get_cache_path(archive_name, names=()):
+ extract_path = self.extraction_path or get_default_cache()
+ target_path = os.path.join(extract_path, archive_name+'-tmp', *names)
+ return target_path
+
+def postprocess(tempname, filename):
+ pass
+
+def set_extraction_path(path):
+ pass
+
+def cleanup_resources(force=False):
+ pass
+
+def get_distribution(dist):
+ return Distribution(dist)
+
+_namespace_packages = {}
+"""
+ )
+
+
+register_module_extender(MANAGER, "pkg_resources", pkg_resources_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_pytest.py b/venv/Lib/site-packages/astroid/brain/brain_pytest.py
new file mode 100644
index 0000000..d7e3ac8
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_pytest.py
@@ -0,0 +1,88 @@
+# Copyright (c) 2014-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Jeff Quast <contact@jeffquast.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2016 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid hooks for pytest."""
+from __future__ import absolute_import
+from astroid import MANAGER, register_module_extender
+from astroid.builder import AstroidBuilder
+
+
+def pytest_transform():
+ return AstroidBuilder(MANAGER).string_build(
+ """
+
+try:
+ import _pytest.mark
+ import _pytest.recwarn
+ import _pytest.runner
+ import _pytest.python
+ import _pytest.skipping
+ import _pytest.assertion
+except ImportError:
+ pass
+else:
+ deprecated_call = _pytest.recwarn.deprecated_call
+ warns = _pytest.recwarn.warns
+
+ exit = _pytest.runner.exit
+ fail = _pytest.runner.fail
+ skip = _pytest.runner.skip
+ importorskip = _pytest.runner.importorskip
+
+ xfail = _pytest.skipping.xfail
+ mark = _pytest.mark.MarkGenerator()
+ raises = _pytest.python.raises
+
+ # New in pytest 3.0
+ try:
+ approx = _pytest.python.approx
+ register_assert_rewrite = _pytest.assertion.register_assert_rewrite
+ except AttributeError:
+ pass
+
+
+# Moved in pytest 3.0
+
+try:
+ import _pytest.freeze_support
+ freeze_includes = _pytest.freeze_support.freeze_includes
+except ImportError:
+ try:
+ import _pytest.genscript
+ freeze_includes = _pytest.genscript.freeze_includes
+ except ImportError:
+ pass
+
+try:
+ import _pytest.debugging
+ set_trace = _pytest.debugging.pytestPDB().set_trace
+except ImportError:
+ try:
+ import _pytest.pdb
+ set_trace = _pytest.pdb.pytestPDB().set_trace
+ except ImportError:
+ pass
+
+try:
+ import _pytest.fixtures
+ fixture = _pytest.fixtures.fixture
+ yield_fixture = _pytest.fixtures.yield_fixture
+except ImportError:
+ try:
+ import _pytest.python
+ fixture = _pytest.python.fixture
+ yield_fixture = _pytest.python.yield_fixture
+ except ImportError:
+ pass
+"""
+ )
+
+
+register_module_extender(MANAGER, "pytest", pytest_transform)
+register_module_extender(MANAGER, "py.test", pytest_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_qt.py b/venv/Lib/site-packages/astroid/brain/brain_qt.py
new file mode 100644
index 0000000..8679d14
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_qt.py
@@ -0,0 +1,82 @@
+# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2017 Roy Wright <roy@wright.org>
+# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid hooks for the PyQT library."""
+
+from astroid import MANAGER, register_module_extender
+from astroid.builder import AstroidBuilder
+from astroid import nodes
+from astroid import parse
+
+
+def _looks_like_signal(node, signal_name="pyqtSignal"):
+ if "__class__" in node.instance_attrs:
+ try:
+ cls = node.instance_attrs["__class__"][0]
+ return cls.name == signal_name
+ except AttributeError:
+ # return False if the cls does not have a name attribute
+ pass
+ return False
+
+
+def transform_pyqt_signal(node):
+ module = parse(
+ """
+ class pyqtSignal(object):
+ def connect(self, slot, type=None, no_receiver_check=False):
+ pass
+ def disconnect(self, slot):
+ pass
+ def emit(self, *args):
+ pass
+ """
+ )
+ signal_cls = 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):
+ module = parse(
+ """
+ class NotPySideSignal(object):
+ def connect(self, receiver, type=None):
+ pass
+ def disconnect(self, receiver):
+ pass
+ def emit(self, *args):
+ pass
+ """
+ )
+ signal_cls = 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(MANAGER).string_build(
+ """
+
+def SIGNAL(signal_name): pass
+
+class QObject(object):
+ def emit(self, signal): pass
+"""
+ )
+
+
+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/site-packages/astroid/brain/brain_random.py b/venv/Lib/site-packages/astroid/brain/brain_random.py
new file mode 100644
index 0000000..5ec858a
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_random.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/PyCQA/astroid/blob/master/COPYING.LESSER
+import random
+
+import astroid
+from astroid import helpers
+from astroid import MANAGER
+
+
+ACCEPTED_ITERABLES_FOR_SAMPLE = (astroid.List, astroid.Set, astroid.Tuple)
+
+
+def _clone_node_with_lineno(node, parent, lineno):
+ cls = node.__class__
+ other_fields = node._other_fields
+ _astroid_fields = node._astroid_fields
+ init_params = {"lineno": lineno, "col_offset": node.col_offset, "parent": parent}
+ postinit_params = {param: getattr(node, param) for param in _astroid_fields}
+ if other_fields:
+ init_params.update({param: getattr(node, param) for param in other_fields})
+ new_node = cls(**init_params)
+ if hasattr(node, "postinit") and _astroid_fields:
+ new_node.postinit(**postinit_params)
+ return new_node
+
+
+def infer_random_sample(node, context=None):
+ if len(node.args) != 2:
+ raise astroid.UseInferenceDefault
+
+ length = node.args[1]
+ if not isinstance(length, astroid.Const):
+ raise astroid.UseInferenceDefault
+ if not isinstance(length.value, int):
+ raise astroid.UseInferenceDefault
+
+ inferred_sequence = helpers.safe_infer(node.args[0], context=context)
+ if not inferred_sequence:
+ raise astroid.UseInferenceDefault
+
+ if not isinstance(inferred_sequence, ACCEPTED_ITERABLES_FOR_SAMPLE):
+ raise astroid.UseInferenceDefault
+
+ if length.value > len(inferred_sequence.elts):
+ # In this case, this will raise a ValueError
+ raise astroid.UseInferenceDefault
+
+ try:
+ elts = random.sample(inferred_sequence.elts, length.value)
+ except ValueError:
+ raise astroid.UseInferenceDefault
+
+ new_node = astroid.List(
+ lineno=node.lineno, col_offset=node.col_offset, parent=node.scope()
+ )
+ new_elts = [
+ _clone_node_with_lineno(elt, parent=new_node, lineno=new_node.lineno)
+ for elt in elts
+ ]
+ new_node.postinit(new_elts)
+ return iter((new_node,))
+
+
+def _looks_like_random_sample(node):
+ func = node.func
+ if isinstance(func, astroid.Attribute):
+ return func.attrname == "sample"
+ if isinstance(func, astroid.Name):
+ return func.name == "sample"
+ return False
+
+
+MANAGER.register_transform(
+ astroid.Call, astroid.inference_tip(infer_random_sample), _looks_like_random_sample
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_re.py b/venv/Lib/site-packages/astroid/brain/brain_re.py
new file mode 100644
index 0000000..c7ee51a
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_re.py
@@ -0,0 +1,36 @@
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+import sys
+import astroid
+
+PY36 = sys.version_info >= (3, 6)
+
+if PY36:
+ # Since Python 3.6 there is the RegexFlag enum
+ # where every entry will be exposed via updating globals()
+
+ def _re_transform():
+ return astroid.parse(
+ """
+ import sre_compile
+ ASCII = sre_compile.SRE_FLAG_ASCII
+ IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE
+ LOCALE = sre_compile.SRE_FLAG_LOCALE
+ UNICODE = sre_compile.SRE_FLAG_UNICODE
+ MULTILINE = sre_compile.SRE_FLAG_MULTILINE
+ DOTALL = sre_compile.SRE_FLAG_DOTALL
+ VERBOSE = sre_compile.SRE_FLAG_VERBOSE
+ A = ASCII
+ I = IGNORECASE
+ L = LOCALE
+ U = UNICODE
+ M = MULTILINE
+ S = DOTALL
+ X = VERBOSE
+ TEMPLATE = sre_compile.SRE_FLAG_TEMPLATE
+ T = TEMPLATE
+ DEBUG = sre_compile.SRE_FLAG_DEBUG
+ """
+ )
+
+ astroid.register_module_extender(astroid.MANAGER, "re", _re_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_six.py b/venv/Lib/site-packages/astroid/brain/brain_six.py
new file mode 100644
index 0000000..b342fbf
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_six.py
@@ -0,0 +1,200 @@
+# Copyright (c) 2014-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""Astroid hooks for six module."""
+
+from textwrap import dedent
+
+from astroid import MANAGER, register_module_extender
+from astroid.builder import AstroidBuilder
+from astroid.exceptions import (
+ AstroidBuildingError,
+ InferenceError,
+ AttributeInferenceError,
+)
+from astroid import nodes
+
+
+SIX_ADD_METACLASS = "six.add_metaclass"
+
+
+def _indent(text, prefix, predicate=None):
+ """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.
+ """
+ if predicate is None:
+ predicate = lambda line: line.strip()
+
+ 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 imp import reload as 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(MANAGER).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(MANAGER).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:
+ raise AstroidBuildingError(modname=modname)
+ if isinstance(import_attr, nodes.Import):
+ submodule = MANAGER.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):
+ 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):
+ """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:
+ continue
+ if func.qname() == SIX_ADD_METACLASS and decorator.args:
+ metaclass = decorator.args[0]
+ node._metaclass = metaclass
+ return node
+
+
+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,
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_ssl.py b/venv/Lib/site-packages/astroid/brain/brain_ssl.py
new file mode 100644
index 0000000..893d8a2
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_ssl.py
@@ -0,0 +1,74 @@
+# Copyright (c) 2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid hooks for the ssl library."""
+
+from astroid import MANAGER, register_module_extender
+from astroid.builder import AstroidBuilder
+from astroid import nodes
+from astroid import parse
+
+
+def ssl_transform():
+ return parse(
+ """
+ from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION
+ from _ssl import _SSLContext, MemoryBIO
+ from _ssl import (
+ SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError,
+ SSLSyscallError, SSLEOFError,
+ )
+ from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED
+ from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj
+ from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes
+ try:
+ from _ssl import RAND_egd
+ except ImportError:
+ # LibreSSL does not provide RAND_egd
+ pass
+ from _ssl import (OP_ALL, OP_CIPHER_SERVER_PREFERENCE,
+ OP_NO_COMPRESSION, OP_NO_SSLv2, OP_NO_SSLv3,
+ OP_NO_TLSv1, OP_NO_TLSv1_1, OP_NO_TLSv1_2,
+ OP_SINGLE_DH_USE, OP_SINGLE_ECDH_USE)
+
+ from _ssl import (ALERT_DESCRIPTION_ACCESS_DENIED, ALERT_DESCRIPTION_BAD_CERTIFICATE,
+ ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE,
+ ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE,
+ ALERT_DESCRIPTION_BAD_RECORD_MAC,
+ ALERT_DESCRIPTION_CERTIFICATE_EXPIRED,
+ ALERT_DESCRIPTION_CERTIFICATE_REVOKED,
+ ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN,
+ ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE,
+ ALERT_DESCRIPTION_CLOSE_NOTIFY, ALERT_DESCRIPTION_DECODE_ERROR,
+ ALERT_DESCRIPTION_DECOMPRESSION_FAILURE,
+ ALERT_DESCRIPTION_DECRYPT_ERROR,
+ ALERT_DESCRIPTION_HANDSHAKE_FAILURE,
+ ALERT_DESCRIPTION_ILLEGAL_PARAMETER,
+ ALERT_DESCRIPTION_INSUFFICIENT_SECURITY,
+ ALERT_DESCRIPTION_INTERNAL_ERROR,
+ ALERT_DESCRIPTION_NO_RENEGOTIATION,
+ ALERT_DESCRIPTION_PROTOCOL_VERSION,
+ ALERT_DESCRIPTION_RECORD_OVERFLOW,
+ ALERT_DESCRIPTION_UNEXPECTED_MESSAGE,
+ ALERT_DESCRIPTION_UNKNOWN_CA,
+ ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY,
+ ALERT_DESCRIPTION_UNRECOGNIZED_NAME,
+ ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE,
+ ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION,
+ ALERT_DESCRIPTION_USER_CANCELLED)
+ from _ssl import (SSL_ERROR_EOF, SSL_ERROR_INVALID_ERROR_CODE, SSL_ERROR_SSL,
+ SSL_ERROR_SYSCALL, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_READ,
+ SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_ZERO_RETURN)
+ from _ssl import VERIFY_CRL_CHECK_CHAIN, VERIFY_CRL_CHECK_LEAF, VERIFY_DEFAULT, VERIFY_X509_STRICT
+ from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN
+ from _ssl import _OPENSSL_API_VERSION
+ from _ssl import PROTOCOL_SSLv23, PROTOCOL_TLSv1, PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2
+ from _ssl import PROTOCOL_TLS, PROTOCOL_TLS_CLIENT, PROTOCOL_TLS_SERVER
+ """
+ )
+
+
+register_module_extender(MANAGER, "ssl", ssl_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_subprocess.py b/venv/Lib/site-packages/astroid/brain/brain_subprocess.py
new file mode 100644
index 0000000..c14dc55
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_subprocess.py
@@ -0,0 +1,111 @@
+# Copyright (c) 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+import sys
+import textwrap
+
+import astroid
+
+
+PY37 = sys.version_info >= (3, 7)
+PY36 = sys.version_info >= (3, 6)
+
+
+def _subprocess_transform():
+ communicate = (bytes("string", "ascii"), bytes("string", "ascii"))
+ communicate_signature = "def communicate(self, input=None, timeout=None)"
+ if PY37:
+ init = """
+ def __init__(self, args, bufsize=0, executable=None,
+ stdin=None, stdout=None, stderr=None,
+ preexec_fn=None, close_fds=False, shell=False,
+ cwd=None, env=None, universal_newlines=False,
+ startupinfo=None, creationflags=0, restore_signals=True,
+ start_new_session=False, pass_fds=(), *,
+ encoding=None, errors=None, text=None):
+ pass
+ """
+ elif PY36:
+ init = """
+ def __init__(self, args, bufsize=0, executable=None,
+ stdin=None, stdout=None, stderr=None,
+ preexec_fn=None, close_fds=False, shell=False,
+ cwd=None, env=None, universal_newlines=False,
+ startupinfo=None, creationflags=0, restore_signals=True,
+ start_new_session=False, pass_fds=(), *,
+ encoding=None, errors=None):
+ pass
+ """
+ else:
+ init = """
+ def __init__(self, args, bufsize=0, executable=None,
+ stdin=None, stdout=None, stderr=None,
+ preexec_fn=None, close_fds=False, shell=False,
+ cwd=None, env=None, universal_newlines=False,
+ startupinfo=None, creationflags=0, restore_signals=True,
+ start_new_session=False, pass_fds=()):
+ pass
+ """
+ wait_signature = "def wait(self, timeout=None)"
+ ctx_manager = """
+ def __enter__(self): return self
+ def __exit__(self, *args): pass
+ """
+ py3_args = "args = []"
+ code = textwrap.dedent(
+ """
+ def check_output(
+ args, *,
+ stdin=None,
+ stderr=None,
+ shell=False,
+ cwd=None,
+ encoding=None,
+ errors=None,
+ universal_newlines=False,
+ timeout=None,
+ env=None
+ ):
+
+ if universal_newlines:
+ return ""
+ return b""
+ class Popen(object):
+ returncode = pid = 0
+ stdin = stdout = stderr = file()
+ %(py3_args)s
+
+ %(communicate_signature)s:
+ return %(communicate)r
+ %(wait_signature)s:
+ 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)s
+ """
+ % {
+ "communicate": communicate,
+ "communicate_signature": communicate_signature,
+ "wait_signature": wait_signature,
+ "ctx_manager": ctx_manager,
+ "py3_args": py3_args,
+ }
+ )
+
+ init_lines = textwrap.dedent(init).splitlines()
+ indented_init = "\n".join(" " * 4 + line for line in init_lines)
+ code += indented_init
+ return astroid.parse(code)
+
+
+astroid.register_module_extender(astroid.MANAGER, "subprocess", _subprocess_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_threading.py b/venv/Lib/site-packages/astroid/brain/brain_threading.py
new file mode 100644
index 0000000..dffa55a
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_threading.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+import astroid
+
+
+def _thread_transform():
+ return astroid.parse(
+ """
+ class lock(object):
+ def acquire(self, blocking=True, timeout=-1):
+ pass
+ def release(self):
+ pass
+ def __enter__(self):
+ return True
+ def __exit__(self, *args):
+ pass
+ def locked(self):
+ return False
+
+ def Lock():
+ return lock()
+ """
+ )
+
+
+astroid.register_module_extender(astroid.MANAGER, "threading", _thread_transform)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_typing.py b/venv/Lib/site-packages/astroid/brain/brain_typing.py
new file mode 100644
index 0000000..9ff7227
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_typing.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2017-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 David Euresti <github@euresti.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+"""Astroid hooks for typing.py support."""
+import typing
+
+from astroid import (
+ MANAGER,
+ UseInferenceDefault,
+ extract_node,
+ inference_tip,
+ nodes,
+ InferenceError,
+)
+
+
+TYPING_NAMEDTUPLE_BASENAMES = {"NamedTuple", "typing.NamedTuple"}
+TYPING_TYPEVARS = {"TypeVar", "NewType"}
+TYPING_TYPEVARS_QUALIFIED = {"typing.TypeVar", "typing.NewType"}
+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(typing.__all__)
+
+
+def looks_like_typing_typevar_or_newtype(node):
+ 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, context=None):
+ """Infer a typing.TypeVar(...) or typing.NewType(...) call"""
+ try:
+ func = next(node.func.infer(context=context))
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
+
+ if func.qname() not in TYPING_TYPEVARS_QUALIFIED:
+ raise UseInferenceDefault
+ if not node.args:
+ raise UseInferenceDefault
+
+ typename = node.args[0].as_string().strip("'")
+ node = extract_node(TYPING_TYPE_TEMPLATE.format(typename))
+ return node.infer(context=context)
+
+
+def _looks_like_typing_subscript(node):
+ """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
+ elif isinstance(node, nodes.Attribute):
+ return node.attrname in TYPING_MEMBERS
+ elif isinstance(node, nodes.Subscript):
+ return _looks_like_typing_subscript(node.value)
+ return False
+
+
+def infer_typing_attr(node, context=None):
+ """Infer a typing.X[...] subscript"""
+ try:
+ value = next(node.value.infer())
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
+
+ if not value.qname().startswith("typing."):
+ raise UseInferenceDefault
+
+ node = extract_node(TYPING_TYPE_TEMPLATE.format(value.qname().split(".")[-1]))
+ return node.infer(context=context)
+
+
+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
+)
diff --git a/venv/Lib/site-packages/astroid/brain/brain_uuid.py b/venv/Lib/site-packages/astroid/brain/brain_uuid.py
new file mode 100644
index 0000000..8bda631
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/brain/brain_uuid.py
@@ -0,0 +1,20 @@
+# Copyright (c) 2017 Claudiu Popa <pcmanticore@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Astroid hooks for the UUID module."""
+
+
+from astroid import MANAGER
+from astroid import nodes
+
+
+def _patch_uuid_class(node):
+ # The .int member is patched using __dict__
+ node.locals["int"] = [nodes.Const(0, parent=node)]
+
+
+MANAGER.register_transform(
+ nodes.ClassDef, _patch_uuid_class, lambda node: node.qname() == "uuid.UUID"
+)
diff --git a/venv/Lib/site-packages/astroid/builder.py b/venv/Lib/site-packages/astroid/builder.py
new file mode 100644
index 0000000..ac71093
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/builder.py
@@ -0,0 +1,435 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2013 Phil Schaf <flying-sheep@web.de>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2015 Google, Inc.
+# Copyright (c) 2014 Alexander Presnyakov <flagist0@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""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.
+"""
+
+import os
+import textwrap
+from tokenize import detect_encoding
+
+from astroid._ast import _parse
+from astroid import bases
+from astroid import exceptions
+from astroid import manager
+from astroid import modutils
+from astroid import raw_building
+from astroid import rebuilder
+from astroid import nodes
+from astroid import util
+
+# 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 = "#@"
+
+MANAGER = manager.AstroidManager()
+
+
+def open_source_file(filename):
+ with open(filename, "rb") as byte_stream:
+ encoding = detect_encoding(byte_stream.readline)[0]
+ stream = open(filename, "r", newline=None, encoding=encoding)
+ data = stream.read()
+ return stream, encoding, data
+
+
+def _can_assign_attr(node, attrname):
+ try:
+ slots = node.slots()
+ except NotImplementedError:
+ pass
+ else:
+ if slots and attrname not in {slot.value for slot in slots}:
+ return False
+ return True
+
+
+class AstroidBuilder(raw_building.InspectBuilder):
+ """Class for building an astroid tree from source code or from a live module.
+
+ The param *manager* specifies the manager class which should be used.
+ If no manager is given, then the default one will be used. The
+ param *apply_transforms* determines if the transforms should be
+ applied after the tree was built from source or from a live object,
+ by default being True.
+ """
+
+ # pylint: disable=redefined-outer-name
+ def __init__(self, manager=None, apply_transforms=True):
+ super(AstroidBuilder, self).__init__()
+ self._manager = manager or MANAGER
+ self._apply_transforms = apply_transforms
+
+ def module_build(self, module, modname=None):
+ """Build an astroid from a living module instance."""
+ node = None
+ path = getattr(module, "__file__", None)
+ if 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)
+ return node
+
+ def file_build(self, path, modname=None):
+ """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 IOError as exc:
+ raise exceptions.AstroidBuildingError(
+ "Unable to load file {path}:\n{error}",
+ modname=modname,
+ path=path,
+ error=exc,
+ ) from exc
+ except (SyntaxError, LookupError) as exc:
+ raise exceptions.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 exceptions.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 = self._data_build(data, modname, path)
+ return self._post_build(module, encoding)
+
+ def string_build(self, data, modname="", path=None):
+ """Build astroid from source code string."""
+ module = self._data_build(data, modname, path)
+ module.file_bytes = data.encode("utf-8")
+ return self._post_build(module, "utf-8")
+
+ def _post_build(self, module, encoding):
+ """Handles encoding and delayed nodes after a module has been built"""
+ module.file_encoding = encoding
+ self._manager.cache_module(module)
+ # post tree building steps after we stored the module in the cache:
+ for from_node in module._import_from_nodes:
+ if from_node.modname == "__future__":
+ for symbol, _ in from_node.names:
+ module.future_imports.add(symbol)
+ self.add_from_names_to_locals(from_node)
+ # handle delayed assattr nodes
+ for delayed in module._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, modname, path):
+ """Build tree node from data and add some informations"""
+ try:
+ node = _parse(data + "\n")
+ except (TypeError, ValueError, SyntaxError) as exc:
+ raise exceptions.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)
+ module = builder.visit_module(node, modname, node_file, package)
+ module._import_from_nodes = builder._import_from_nodes
+ module._delayed_assattr = builder._delayed_assattr
+ return module
+
+ def add_from_names_to_locals(self, node):
+ """Store imported names to the locals
+
+ Resort the locals if coming from a delayed node
+ """
+ _key_func = lambda node: node.fromlineno
+
+ def sort_locals(my_list):
+ my_list.sort(key=_key_func)
+
+ for (name, asname) in node.names:
+ if name == "*":
+ try:
+ imported = node.do_import_module()
+ except exceptions.AstroidBuildingError:
+ continue
+ for name in imported.public_names():
+ node.parent.set_local(name, node)
+ sort_locals(node.parent.scope().locals[name])
+ else:
+ node.parent.set_local(asname or name, node)
+ sort_locals(node.parent.scope().locals[asname or name])
+
+ def delayed_assattr(self, node):
+ """Visit a AssAttr node
+
+ This adds name to locals and handle members definition.
+ """
+ try:
+ frame = node.frame()
+ for inferred in node.expr.infer():
+ if inferred is util.Uninferable:
+ continue
+ try:
+ if inferred.__class__ is bases.Instance:
+ inferred = inferred._proxied
+ iattrs = inferred.instance_attrs
+ if not _can_assign_attr(inferred, node.attrname):
+ continue
+ elif isinstance(inferred, bases.Instance):
+ # Const, Tuple, ... we may be wrong, may be not, but
+ # anyway we don't want to pollute builtin's namespace
+ continue
+ elif inferred.is_function:
+ iattrs = inferred.instance_attrs
+ else:
+ iattrs = inferred.locals
+ except AttributeError:
+ # XXX log error
+ continue
+ values = iattrs.setdefault(node.attrname, [])
+ if node in values:
+ continue
+ # get assign in __init__ first XXX useful ?
+ if (
+ frame.name == "__init__"
+ and values
+ and values[0].frame().name != "__init__"
+ ):
+ values.insert(0, node)
+ else:
+ values.append(node)
+ except exceptions.InferenceError:
+ pass
+
+
+def build_namespace_package_module(name, path):
+ return nodes.Module(name, doc="", path=path, package=True)
+
+
+def parse(code, module_name="", path=None, apply_transforms=True):
+ """Parses a source string in order to obtain an astroid AST from it
+
+ :param str code: The code for the module.
+ :param str module_name: The name for the module, if any
+ :param str path: The path for the module
+ :param bool apply_transforms:
+ Apply the transforms for the give code. Use it if you
+ don't want the default transforms to be applied.
+ """
+ code = textwrap.dedent(code)
+ builder = AstroidBuilder(manager=MANAGER, apply_transforms=apply_transforms)
+ return builder.string_build(code, modname=module_name, path=path)
+
+
+def _extract_expressions(node):
+ """Find expressions in a call to _TRANSIENT_FUNCTION and extract them.
+
+ The function walks the AST recursively to search for expressions that
+ are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an
+ expression, it completely removes the function call node from the tree,
+ replacing it by the wrapped expression inside the parent.
+
+ :param node: An astroid node.
+ :type node: astroid.bases.NodeNG
+ :yields: The sequence of wrapped expressions on the modified tree
+ expression can be found.
+ """
+ if (
+ isinstance(node, nodes.Call)
+ and isinstance(node.func, nodes.Name)
+ and node.func.name == _TRANSIENT_FUNCTION
+ ):
+ real_expr = node.args[0]
+ 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, tuple)):
+ 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, line):
+ """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)):
+ # 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, module_name=""):
+ """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.
+ :rtype: astroid.bases.NodeNG, or a list of nodes.
+ """
+
+ def _extract(node):
+ if isinstance(node, nodes.Expr):
+ return node.value
+
+ return node
+
+ requested_lines = []
+ 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 = []
+ 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]
+ if len(extracted) == 1:
+ return extracted[0]
+ return extracted
diff --git a/venv/Lib/site-packages/astroid/context.py b/venv/Lib/site-packages/astroid/context.py
new file mode 100644
index 0000000..70a9208
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/context.py
@@ -0,0 +1,179 @@
+# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Various context related utilities, including inference and call contexts."""
+import contextlib
+import pprint
+from typing import Optional
+
+
+class InferenceContext:
+ """Provide context for inference
+
+ Store already inferred nodes to save time
+ Account for already visited nodes to infinite stop infinite recursion
+ """
+
+ __slots__ = (
+ "path",
+ "lookupname",
+ "callcontext",
+ "boundnode",
+ "inferred",
+ "extra_context",
+ )
+
+ def __init__(self, path=None, inferred=None):
+ self.path = path or set()
+ """
+ :type: set(tuple(NodeNG, optional(str)))
+
+ Path of visited nodes and their lookupname
+
+ Currently this key is ``(node, context.lookupname)``
+ """
+ self.lookupname = None
+ """
+ :type: optional[str]
+
+ 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 = None
+ """
+ :type: optional[CallContext]
+
+ The call arguments and keywords for the given context
+ """
+ self.boundnode = None
+ """
+ :type: optional[NodeNG]
+
+ The bound node of the given context
+
+ e.g. the bound node of object.__new__(cls) is the object node
+ """
+ self.inferred = inferred or {}
+ """
+ :type: dict(seq, seq)
+
+ Inferred node contexts to their mapped results
+ Currently the key is ``(node, lookupname, callcontext, boundnode)``
+ and the value is tuple of the inferred results
+ """
+ self.extra_context = {}
+ """
+ :type: dict(NodeNG, Context)
+
+ Context that needs to be passed down through call stacks
+ for call arguments
+ """
+
+ def push(self, node):
+ """Push node into inference path
+
+ :return: True if node is already in context path else False
+ :rtype: bool
+
+ 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):
+ """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, inferred=self.inferred)
+ clone.callcontext = self.callcontext
+ clone.boundnode = self.boundnode
+ clone.extra_context = self.extra_context
+ return clone
+
+ def cache_generator(self, key, generator):
+ """Cache result of generator into dictionary
+
+ Used to cache inference results"""
+ results = []
+ for result in generator:
+ results.append(result)
+ yield result
+
+ self.inferred[key] = tuple(results)
+
+ @contextlib.contextmanager
+ def restore_path(self):
+ path = set(self.path)
+ yield
+ self.path = path
+
+ def __str__(self):
+ state = (
+ "%s=%s"
+ % (field, pprint.pformat(getattr(self, field), width=80 - len(field)))
+ for field in self.__slots__
+ )
+ return "%s(%s)" % (type(self).__name__, ",\n ".join(state))
+
+
+class CallContext:
+ """Holds information for a call site."""
+
+ __slots__ = ("args", "keywords")
+
+ def __init__(self, args, keywords=None):
+ """
+ :param List[NodeNG] args: Call positional arguments
+ :param Union[List[nodes.Keyword], None] keywords: Call keywords
+ """
+ self.args = args
+ if keywords:
+ keywords = [(arg.arg, arg.value) for arg in keywords]
+ else:
+ keywords = []
+ self.keywords = keywords
+
+
+def copy_context(context: Optional[InferenceContext]) -> InferenceContext:
+ """Clone a context if given, or return a fresh contexxt"""
+ if context is not None:
+ return context.clone()
+
+ return InferenceContext()
+
+
+def bind_context_to_node(context, node):
+ """Give a context a boundnode
+ to retrieve the correct function name or attribute value
+ with from further inference.
+
+ Do not use an existing context since the boundnode could then
+ be incorrectly propagated higher up in the call stack.
+
+ :param context: Context to use
+ :type context: Optional(context)
+
+ :param node: Node to do name lookups from
+ :type node NodeNG:
+
+ :returns: A new context
+ :rtype: InferenceContext
+ """
+ context = copy_context(context)
+ context.boundnode = node
+ return context
diff --git a/venv/Lib/site-packages/astroid/decorators.py b/venv/Lib/site-packages/astroid/decorators.py
new file mode 100644
index 0000000..1448757
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/decorators.py
@@ -0,0 +1,141 @@
+# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+""" A few useful function/method decorators."""
+
+import functools
+
+import wrapt
+
+from astroid import context as contextmod
+from astroid import exceptions
+from astroid import util
+
+
+@wrapt.decorator
+def cached(func, instance, args, kwargs):
+ """Simple decorator to cache result of method calls without args."""
+ cache = getattr(instance, "__cache", None)
+ if cache is None:
+ instance.__cache = cache = {}
+ try:
+ return cache[func]
+ except KeyError:
+ cache[func] = result = func(*args, **kwargs)
+ return result
+
+
+class cachedproperty:
+ """ Provides a cached property equivalent to the stacking of
+ @cached and @property, but more efficient.
+
+ After first usage, the <property_name> becomes part of the object's
+ __dict__. Doing:
+
+ del obj.<property_name> empties the cache.
+
+ Idea taken from the pyramid_ framework and the mercurial_ project.
+
+ .. _pyramid: http://pypi.python.org/pypi/pyramid
+ .. _mercurial: http://pypi.python.org/pypi/Mercurial
+ """
+
+ __slots__ = ("wrapped",)
+
+ def __init__(self, wrapped):
+ try:
+ wrapped.__name__
+ except AttributeError as exc:
+ raise TypeError("%s must have a __name__ attribute" % wrapped) from exc
+ self.wrapped = wrapped
+
+ @property
+ def __doc__(self):
+ doc = getattr(self.wrapped, "__doc__", None)
+ return "<wrapped by the cachedproperty decorator>%s" % (
+ "\n%s" % doc if doc else ""
+ )
+
+ def __get__(self, inst, objtype=None):
+ if inst is None:
+ return self
+ val = self.wrapped(inst)
+ setattr(inst, self.wrapped.__name__, val)
+ return val
+
+
+def path_wrapper(func):
+ """return the given infer function wrapped to handle the path
+
+ Used to stop inference if the node has already been looked
+ at for a given `InferenceContext` to prevent infinite recursion
+ """
+
+ @functools.wraps(func)
+ def wrapped(node, context=None, _func=func, **kwargs):
+ """wrapper function handling context"""
+ if context is None:
+ context = contextmod.InferenceContext()
+ if context.push(node):
+ return None
+
+ yielded = set()
+ generator = _func(node, context, **kwargs)
+ try:
+ while True:
+ res = next(generator)
+ # 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)
+ except StopIteration as error:
+ if error.args:
+ return error.args[0]
+ return None
+
+ return wrapped
+
+
+@wrapt.decorator
+def yes_if_nothing_inferred(func, instance, args, kwargs):
+ generator = func(*args, **kwargs)
+
+ try:
+ yield next(generator)
+ except StopIteration:
+ # generator is empty
+ yield util.Uninferable
+ return
+
+ yield from generator
+
+
+@wrapt.decorator
+def raise_if_nothing_inferred(func, instance, args, kwargs):
+ generator = func(*args, **kwargs)
+
+ try:
+ yield next(generator)
+ except StopIteration as error:
+ # generator is empty
+ if error.args:
+ # pylint: disable=not-a-mapping
+ raise exceptions.InferenceError(**error.args[0])
+ raise exceptions.InferenceError(
+ "StopIteration raised without any error information."
+ )
+
+ yield from generator
diff --git a/venv/Lib/site-packages/astroid/exceptions.py b/venv/Lib/site-packages/astroid/exceptions.py
new file mode 100644
index 0000000..7e9d655
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/exceptions.py
@@ -0,0 +1,230 @@
+# Copyright (c) 2007, 2009-2010, 2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""this module contains exceptions used in the astroid library
+"""
+from astroid import util
+
+
+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="", **kws):
+ super(AstroidError, self).__init__(message)
+ self.message = message
+ for key, value in kws.items():
+ setattr(self, key, value)
+
+ def __str__(self):
+ return self.message.format(**vars(self))
+
+
+class AstroidBuildingError(AstroidError):
+ """exception class when we are unable to build an astroid representation
+
+ Standard attributes:
+ modname: Name of the module that AST construction failed for.
+ error: Exception raised during construction.
+ """
+
+ def __init__(self, message="Failed to import module {modname}.", **kws):
+ super(AstroidBuildingError, self).__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.
+ """
+
+ level = None
+ name = None
+
+ def __init__(
+ self,
+ message="Relative import with too many levels " "({level}) for module {name!r}",
+ **kws
+ ):
+ super(TooManyLevelsError, self).__init__(message, **kws)
+
+
+class AstroidSyntaxError(AstroidBuildingError):
+ """Exception class used when a module can't be parsed."""
+
+
+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.
+ """
+
+ func = None
+ name = None
+
+ def __init__(self, message="{func!r} has no default for {name!r}.", **kws):
+ super(NoDefault, self).__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.
+ """
+
+ context = None
+
+
+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.
+ """
+
+ mros = ()
+ cls = None
+
+ def __str__(self):
+ mro_names = ", ".join(
+ "({})".format(", ".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.
+ """
+
+ super_ = None
+
+ def __str__(self):
+ return self.message.format(**vars(self.super_))
+
+
+class InferenceError(ResolveError):
+ """raised when we are unable to infer a node
+
+ Standard attributes:
+ node: The node inference was called on.
+ context: InferenceContext object.
+ """
+
+ node = None
+ context = None
+
+ def __init__(self, message="Inference failed for {node!r}.", **kws):
+ super(InferenceError, self).__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.
+ """
+
+ name = None
+ scope = None
+
+ def __init__(self, message="{name!r} not found in {scope!r}.", **kws):
+ super(NameInferenceError, self).__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.
+ """
+
+ target = None
+ attribute = None
+
+ def __init__(self, message="{attribute!r} not found on {target!r}.", **kws):
+ super(AttributeInferenceError, self).__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."""
+
+
+class AstroidTypeError(AstroidError):
+ """Raised when a TypeError would be expected in Python code."""
+
+
+class InferenceOverwriteError(AstroidError):
+ """Raised when an inference tip is overwritten
+
+ Currently only used for debugging.
+ """
+
+
+# Backwards-compatibility aliases
+OperationError = util.BadOperationMessage
+UnaryOperationError = util.BadUnaryOperationMessage
+BinaryOperationError = util.BadBinaryOperationMessage
+
+SuperArgumentTypeError = SuperError
+UnresolvableName = NameInferenceError
+NotFoundError = AttributeInferenceError
+AstroidBuildingException = AstroidBuildingError
diff --git a/venv/Lib/site-packages/astroid/helpers.py b/venv/Lib/site-packages/astroid/helpers.py
new file mode 100644
index 0000000..be133b3
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/helpers.py
@@ -0,0 +1,273 @@
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""
+Various helper utilities.
+"""
+
+import builtins as builtins_mod
+
+from astroid import bases
+from astroid import context as contextmod
+from astroid import exceptions
+from astroid import manager
+from astroid import nodes
+from astroid import raw_building
+from astroid import scoped_nodes
+from astroid import util
+
+
+BUILTINS = builtins_mod.__name__
+
+
+def _build_proxy_class(cls_name, builtins):
+ proxy = raw_building.build_class(cls_name)
+ proxy.parent = builtins
+ return proxy
+
+
+def _function_type(function, builtins):
+ if isinstance(function, scoped_nodes.Lambda):
+ if function.root().name == BUILTINS:
+ cls_name = "builtin_function_or_method"
+ else:
+ cls_name = "function"
+ elif isinstance(function, bases.BoundMethod):
+ cls_name = "method"
+ elif isinstance(function, bases.UnboundMethod):
+ cls_name = "function"
+ return _build_proxy_class(cls_name, builtins)
+
+
+def _object_type(node, context=None):
+ astroid_manager = manager.AstroidManager()
+ builtins = astroid_manager.builtins_module
+ context = context or contextmod.InferenceContext()
+
+ for inferred in node.infer(context=context):
+ if isinstance(inferred, scoped_nodes.ClassDef):
+ if inferred.newstyle:
+ metaclass = inferred.metaclass(context=context)
+ if metaclass:
+ yield metaclass
+ continue
+ yield builtins.getattr("type")[0]
+ elif isinstance(inferred, (scoped_nodes.Lambda, bases.UnboundMethod)):
+ yield _function_type(inferred, builtins)
+ elif isinstance(inferred, scoped_nodes.Module):
+ yield _build_proxy_class("module", builtins)
+ else:
+ yield inferred._proxied
+
+
+def object_type(node, context=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 exceptions.InferenceError:
+ return util.Uninferable
+ if len(types) > 1 or not types:
+ return util.Uninferable
+ return list(types)[0]
+
+
+def _object_type_is_subclass(obj_type, class_or_seq, context=None):
+ if not isinstance(class_or_seq, (tuple, list)):
+ class_seq = (class_or_seq,)
+ else:
+ class_seq = class_or_seq
+
+ if obj_type is util.Uninferable:
+ return util.Uninferable
+
+ # Instances are not types
+ class_seq = [
+ item if not isinstance(item, bases.Instance) else util.Uninferable
+ for item in class_seq
+ ]
+ # strict compatibility with issubclass
+ # issubclass(type, (object, 1)) evaluates to true
+ # issubclass(object, (1, type)) raises TypeError
+ for klass in class_seq:
+ if klass is util.Uninferable:
+ raise exceptions.AstroidTypeError("arg 2 must be a type or tuple of types")
+
+ for obj_subclass in obj_type.mro():
+ if obj_subclass == klass:
+ return True
+ return False
+
+
+def object_isinstance(node, class_or_seq, context=None):
+ """Check if a node 'isinstance' any node in class_or_seq
+
+ :param node: A given node
+ :param class_or_seq: Union[nodes.NodeNG, Sequence[nodes.NodeNG]]
+ :rtype: bool
+
+ :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
+ """
+ obj_type = object_type(node, context)
+ if obj_type is util.Uninferable:
+ return util.Uninferable
+ return _object_type_is_subclass(obj_type, class_or_seq, context=context)
+
+
+def object_issubclass(node, class_or_seq, context=None):
+ """Check if a type is a subclass of any node in class_or_seq
+
+ :param node: A given node
+ :param class_or_seq: Union[Nodes.NodeNG, Sequence[nodes.NodeNG]]
+ :rtype: bool
+
+ :raises AstroidTypeError: if the given ``classes_or_seq`` are not types
+ :raises AstroidError: if the type of the given node cannot be inferred
+ or its type's mro doesn't work
+ """
+ if not isinstance(node, nodes.ClassDef):
+ raise TypeError("{node} needs to be a ClassDef node".format(node=node))
+ return _object_type_is_subclass(node, class_or_seq, context=context)
+
+
+def safe_infer(node, context=None):
+ """Return the inferred value for the given node.
+
+ Return None if inference failed or if there is some ambiguity (more than
+ one node has been inferred).
+ """
+ try:
+ inferit = node.infer(context=context)
+ value = next(inferit)
+ except exceptions.InferenceError:
+ return None
+ try:
+ next(inferit)
+ return None # None if there is ambiguity on the inferred node
+ except exceptions.InferenceError:
+ return None # there is some kind of ambiguity
+ except StopIteration:
+ return value
+
+
+def has_known_bases(klass, context=None):
+ """Return true if all base classes of a class could be inferred."""
+ try:
+ return klass._all_bases_known
+ except AttributeError:
+ pass
+ for base in klass.bases:
+ result = safe_infer(base, context=context)
+ # TODO: check for A->B->A->B pattern in class structure too?
+ if (
+ not isinstance(result, scoped_nodes.ClassDef)
+ or result is klass
+ or not has_known_bases(result, context=context)
+ ):
+ klass._all_bases_known = False
+ return False
+ klass._all_bases_known = True
+ return True
+
+
+def _type_check(type1, type2):
+ if not all(map(has_known_bases, (type1, type2))):
+ raise exceptions._NonDeducibleTypeHierarchy
+
+ if not all([type1.newstyle, type2.newstyle]):
+ return False
+ try:
+ return type1 in type2.mro()[:-1]
+ except exceptions.MroError:
+ # The MRO is invalid.
+ raise exceptions._NonDeducibleTypeHierarchy
+
+
+def is_subtype(type1, type2):
+ """Check if *type1* is a subtype of *type2*."""
+ return _type_check(type1=type2, type2=type1)
+
+
+def is_supertype(type1, type2):
+ """Check if *type2* is a supertype of *type1*."""
+ return _type_check(type1, type2)
+
+
+def class_instance_as_index(node):
+ """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 = contextmod.InferenceContext()
+ context.callcontext = contextmod.CallContext(args=[node])
+
+ try:
+ for inferred in node.igetattr("__index__", context=context):
+ if not isinstance(inferred, bases.BoundMethod):
+ continue
+
+ for result in inferred.infer_call_result(node, context=context):
+ if isinstance(result, nodes.Const) and isinstance(result.value, int):
+ return result
+ except exceptions.InferenceError:
+ pass
+ return None
+
+
+def object_len(node, context=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
+ :rtype int: Integer length of node
+ """
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid.objects import FrozenSet
+
+ inferred_node = safe_infer(node, context=context)
+ if inferred_node is None or inferred_node is util.Uninferable:
+ raise exceptions.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)
+ try:
+ node_type = object_type(inferred_node, context=context)
+ len_call = next(node_type.igetattr("__len__", context=context))
+ except exceptions.AttributeInferenceError:
+ raise exceptions.AstroidTypeError(
+ "object of type '{}' has no len()".format(len_call.pytype())
+ )
+
+ result_of_len = next(len_call.infer_call_result(node, context))
+ if (
+ isinstance(result_of_len, nodes.Const)
+ and result_of_len.pytype() == "builtins.int"
+ ):
+ return result_of_len.value
+ raise exceptions.AstroidTypeError(
+ "'{}' object cannot be interpreted as an integer".format(result_of_len)
+ )
diff --git a/venv/Lib/site-packages/astroid/inference.py b/venv/Lib/site-packages/astroid/inference.py
new file mode 100644
index 0000000..77c6b1d
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/inference.py
@@ -0,0 +1,943 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2017 Michał Masłowski <m.maslowski@clearcode.cc>
+# Copyright (c) 2017 Calen Pennington <cale@edx.org>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""this module contains a set of functions to handle inference on astroid trees
+"""
+
+import functools
+import itertools
+import operator
+
+from astroid import bases
+from astroid import context as contextmod
+from astroid import exceptions
+from astroid import decorators
+from astroid import helpers
+from astroid import manager
+from astroid import nodes
+from astroid.interpreter import dunder_lookup
+from astroid import protocols
+from astroid import util
+
+
+MANAGER = manager.AstroidManager()
+
+
+# .infer method ###############################################################
+
+
+def infer_end(self, context=None):
+ """inference's end for node such as Module, ClassDef, FunctionDef,
+ Const...
+
+ """
+ yield self
+
+
+nodes.Module._infer = infer_end
+nodes.ClassDef._infer = infer_end
+nodes.FunctionDef._infer = infer_end
+nodes.Lambda._infer = infer_end
+nodes.Const._infer = infer_end
+nodes.Slice._infer = infer_end
+
+
+def _infer_sequence_helper(node, context=None):
+ """Infer all values based on _BaseContainer.elts"""
+ values = []
+
+ for elt in node.elts:
+ if isinstance(elt, nodes.Starred):
+ starred = helpers.safe_infer(elt.value, context)
+ if not starred:
+ raise exceptions.InferenceError(node=node, context=context)
+ if not hasattr(starred, "elts"):
+ raise exceptions.InferenceError(node=node, context=context)
+ values.extend(_infer_sequence_helper(starred))
+ elif isinstance(elt, nodes.NamedExpr):
+ value = helpers.safe_infer(elt.value, context)
+ if not value:
+ raise exceptions.InferenceError(node=node, context=context)
+ values.append(value)
+ else:
+ values.append(elt)
+ return values
+
+
+@decorators.raise_if_nothing_inferred
+def infer_sequence(self, context=None):
+ has_starred_named_expr = any(
+ isinstance(e, (nodes.Starred, nodes.NamedExpr)) for e in self.elts
+ )
+ if has_starred_named_expr:
+ values = _infer_sequence_helper(self, context)
+ new_seq = type(self)(
+ lineno=self.lineno, col_offset=self.col_offset, parent=self.parent
+ )
+ new_seq.postinit(values)
+
+ yield new_seq
+ else:
+ yield self
+
+
+nodes.List._infer = infer_sequence
+nodes.Tuple._infer = infer_sequence
+nodes.Set._infer = infer_sequence
+
+
+def infer_map(self, context=None):
+ if not any(isinstance(k, nodes.DictUnpack) for k, _ in self.items):
+ yield self
+ else:
+ items = _infer_map(self, context)
+ new_seq = type(self)(self.lineno, self.col_offset, self.parent)
+ new_seq.postinit(list(items.items()))
+ yield new_seq
+
+
+def _update_with_replacement(lhs_dict, rhs_dict):
+ """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 duplicte keys
+ in inferred Dict items
+
+ :param dict(nodes.NodeNG, nodes.NodeNG) lhs_dict: Dictionary to 'merge' nodes into
+ :param dict(nodes.NodeNG, nodes.NodeNG) rhs_dict: Dictionary with nodes to pull from
+ :return dict(nodes.NodeNG, nodes.NodeNG): merged dictionary of nodes
+ """
+ combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items())
+ # Overwrite keys which have the same string values
+ string_map = {key.as_string(): (key, value) for key, value in combined_dict}
+ # Return to dictionary
+ return dict(string_map.values())
+
+
+def _infer_map(node, context):
+ """Infer all values based on Dict.items"""
+ values = {}
+ for name, value in node.items:
+ if isinstance(name, nodes.DictUnpack):
+ double_starred = helpers.safe_infer(value, context)
+ if not double_starred:
+ raise exceptions.InferenceError
+ if not isinstance(double_starred, nodes.Dict):
+ raise exceptions.InferenceError(node=node, context=context)
+ unpack_items = _infer_map(double_starred, context)
+ values = _update_with_replacement(values, unpack_items)
+ else:
+ key = helpers.safe_infer(name, context=context)
+ value = helpers.safe_infer(value, context=context)
+ if any(not elem for elem in (key, value)):
+ raise exceptions.InferenceError(node=node, context=context)
+ values = _update_with_replacement(values, {key: value})
+ return values
+
+
+nodes.Dict._infer = infer_map
+
+
+def _higher_function_scope(node):
+ """ 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.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
+
+
+def infer_name(self, context=None):
+ """infer a Name: use name lookup rules"""
+ frame, stmts = self.lookup(self.name)
+ if not stmts:
+ # Try to see if the name is enclosed in a nested function
+ # and use the higher (first function) scope for searching.
+ parent_function = _higher_function_scope(self.scope())
+ if parent_function:
+ _, stmts = parent_function.lookup(self.name)
+
+ if not stmts:
+ raise exceptions.NameInferenceError(
+ name=self.name, scope=self.scope(), context=context
+ )
+ context = contextmod.copy_context(context)
+ context.lookupname = self.name
+ return bases._infer_stmts(stmts, context, frame)
+
+
+# pylint: disable=no-value-for-parameter
+nodes.Name._infer = decorators.raise_if_nothing_inferred(
+ decorators.path_wrapper(infer_name)
+)
+nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_call(self, context=None):
+ """infer a Call node by trying to guess what the function returns"""
+ callcontext = contextmod.copy_context(context)
+ callcontext.callcontext = contextmod.CallContext(
+ args=self.args, keywords=self.keywords
+ )
+ callcontext.boundnode = None
+ if context is not None:
+ callcontext.extra_context = _populate_context_lookup(self, context.clone())
+
+ for callee in self.func.infer(context):
+ if callee is util.Uninferable:
+ yield callee
+ continue
+ try:
+ if hasattr(callee, "infer_call_result"):
+ yield from callee.infer_call_result(caller=self, context=callcontext)
+ except exceptions.InferenceError:
+ continue
+ return dict(node=self, context=context)
+
+
+nodes.Call._infer = infer_call
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_import(self, context=None, asname=True):
+ """infer an Import node: return the imported module/object"""
+ name = context.lookupname
+ if name is None:
+ raise exceptions.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 exceptions.AstroidBuildingError as exc:
+ raise exceptions.InferenceError(node=self, context=context) from exc
+
+
+nodes.Import._infer = infer_import
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_import_from(self, context=None, asname=True):
+ """infer a ImportFrom node: return the imported module/object"""
+ name = context.lookupname
+ if name is None:
+ raise exceptions.InferenceError(node=self, context=context)
+ if asname:
+ name = self.real_name(name)
+
+ try:
+ module = self.do_import_module()
+ except exceptions.AstroidBuildingError as exc:
+ raise exceptions.InferenceError(node=self, context=context) from exc
+
+ try:
+ context = contextmod.copy_context(context)
+ context.lookupname = name
+ stmts = module.getattr(name, ignore_locals=module is self.root())
+ return bases._infer_stmts(stmts, context)
+ except exceptions.AttributeInferenceError as error:
+ raise exceptions.InferenceError(
+ error.message, target=self, attribute=name, context=context
+ ) from error
+
+
+nodes.ImportFrom._infer = infer_import_from
+
+
+def infer_attribute(self, context=None):
+ """infer an Attribute node by using getattr on the associated object"""
+ for owner in self.expr.infer(context):
+ if owner is util.Uninferable:
+ yield owner
+ continue
+
+ if context and context.boundnode:
+ # This handles the situation where the attribute is accessed through a subclass
+ # of a base class and the attribute is defined at the base class's level,
+ # by taking in consideration a redefinition in the subclass.
+ if isinstance(owner, bases.Instance) and isinstance(
+ context.boundnode, bases.Instance
+ ):
+ try:
+ if helpers.is_subtype(
+ helpers.object_type(context.boundnode),
+ helpers.object_type(owner),
+ ):
+ owner = context.boundnode
+ except exceptions._NonDeducibleTypeHierarchy:
+ # Can't determine anything useful.
+ pass
+
+ try:
+ context.boundnode = owner
+ yield from owner.igetattr(self.attrname, context)
+ context.boundnode = None
+ except (exceptions.AttributeInferenceError, exceptions.InferenceError):
+ context.boundnode = None
+ except AttributeError:
+ # XXX method / function
+ context.boundnode = None
+ return dict(node=self, context=context)
+
+
+nodes.Attribute._infer = decorators.raise_if_nothing_inferred(
+ decorators.path_wrapper(infer_attribute)
+)
+# won't work with a path wrapper
+nodes.AssignAttr.infer_lhs = decorators.raise_if_nothing_inferred(infer_attribute)
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_global(self, context=None):
+ if context.lookupname is None:
+ raise exceptions.InferenceError(node=self, context=context)
+ try:
+ return bases._infer_stmts(self.root().getattr(context.lookupname), context)
+ except exceptions.AttributeInferenceError as error:
+ raise exceptions.InferenceError(
+ error.message, target=self, attribute=context.lookupname, context=context
+ ) from error
+
+
+nodes.Global._infer = infer_global
+
+
+_SUBSCRIPT_SENTINEL = object()
+
+
+@decorators.raise_if_nothing_inferred
+def infer_subscript(self, context=None):
+ """Inference for subscripts
+
+ We're understanding if the index is a Const
+ or a slice, passing the result of inference
+ to the value's `getitem` method, which should
+ handle each supported index type accordingly.
+ """
+
+ found_one = False
+ for value in self.value.infer(context):
+ if value is util.Uninferable:
+ yield util.Uninferable
+ return None
+ for index in self.slice.infer(context):
+ if index is util.Uninferable:
+ yield util.Uninferable
+ return None
+
+ # Try to deduce the index value.
+ index_value = _SUBSCRIPT_SENTINEL
+ if value.__class__ == bases.Instance:
+ index_value = index
+ else:
+ if index.__class__ == bases.Instance:
+ instance_as_index = helpers.class_instance_as_index(index)
+ if instance_as_index:
+ index_value = instance_as_index
+ else:
+ index_value = index
+ if index_value is _SUBSCRIPT_SENTINEL:
+ raise exceptions.InferenceError(node=self, context=context)
+
+ try:
+ assigned = value.getitem(index_value, context)
+ except (
+ exceptions.AstroidTypeError,
+ exceptions.AstroidIndexError,
+ exceptions.AttributeInferenceError,
+ AttributeError,
+ ) as exc:
+ raise exceptions.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 assigned is util.Uninferable:
+ yield util.Uninferable
+ return None
+ yield from assigned.infer(context)
+ found_one = True
+
+ if found_one:
+ return dict(node=self, context=context)
+ return None
+
+
+nodes.Subscript._infer = decorators.path_wrapper(infer_subscript)
+nodes.Subscript.infer_lhs = infer_subscript
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def _infer_boolop(self, context=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:
+ values = [value.infer(context=context) for value in values]
+ except exceptions.InferenceError:
+ yield util.Uninferable
+ return None
+
+ for pair in itertools.product(*values):
+ if any(item is util.Uninferable for item in pair):
+ # Can't infer the final result, just yield Uninferable.
+ yield util.Uninferable
+ continue
+
+ bool_values = [item.bool_value() for item in pair]
+ if any(item is util.Uninferable for item in bool_values):
+ # 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 dict(node=self, context=context)
+
+
+nodes.BoolOp._infer = _infer_boolop
+
+
+# UnaryOp, BinOp and AugAssign inferences
+
+
+def _filter_operation_errors(self, infer_callable, context, error):
+ for result in infer_callable(self, context):
+ if isinstance(result, error):
+ # For the sake of .infer(), we don't care about operation
+ # errors, which is the job of pylint. So return something
+ # which shows that we can't infer the result.
+ yield util.Uninferable
+ else:
+ yield result
+
+
+def _infer_unaryop(self, context=None):
+ """Infer what an UnaryOp should return when evaluated."""
+ for operand in self.operand.infer(context):
+ try:
+ yield operand.infer_unary_op(self.op)
+ except TypeError as exc:
+ # The operand doesn't support this operation.
+ yield util.BadUnaryOperationMessage(operand, self.op, exc)
+ except AttributeError as exc:
+ meth = protocols.UNARY_OP_METHOD[self.op]
+ if meth is None:
+ # `not node`. Determine node's boolean
+ # value and negate its result, unless it is
+ # Uninferable, which will be returned as is.
+ bool_value = operand.bool_value()
+ if bool_value is not util.Uninferable:
+ yield nodes.const_factory(not bool_value)
+ else:
+ yield util.Uninferable
+ else:
+ if not isinstance(operand, (bases.Instance, nodes.ClassDef)):
+ # The operation was used on something which
+ # doesn't support it.
+ yield util.BadUnaryOperationMessage(operand, self.op, exc)
+ continue
+
+ try:
+ try:
+ methods = dunder_lookup.lookup(operand, meth)
+ except exceptions.AttributeInferenceError:
+ yield util.BadUnaryOperationMessage(operand, self.op, exc)
+ continue
+
+ meth = methods[0]
+ inferred = next(meth.infer(context=context))
+ if inferred is util.Uninferable or not inferred.callable():
+ continue
+
+ context = contextmod.copy_context(context)
+ context.callcontext = contextmod.CallContext(args=[operand])
+ 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 exceptions.AttributeInferenceError as exc:
+ # The unary operation special method was not found.
+ yield util.BadUnaryOperationMessage(operand, self.op, exc)
+ except exceptions.InferenceError:
+ yield util.Uninferable
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_unaryop(self, context=None):
+ """Infer what an UnaryOp should return when evaluated."""
+ yield from _filter_operation_errors(
+ self, _infer_unaryop, context, util.BadUnaryOperationMessage
+ )
+ return dict(node=self, context=context)
+
+
+nodes.UnaryOp._infer_unaryop = _infer_unaryop
+nodes.UnaryOp._infer = infer_unaryop
+
+
+def _is_not_implemented(const):
+ """Check if the given const node is NotImplemented."""
+ return isinstance(const, nodes.Const) and const.value is NotImplemented
+
+
+def _invoke_binop_inference(instance, opnode, op, other, context, method_name):
+ """Invoke binary operation inference on the given instance."""
+ methods = dunder_lookup.lookup(instance, method_name)
+ context = contextmod.bind_context_to_node(context, instance)
+ method = methods[0]
+ inferred = next(method.infer(context=context))
+ if inferred is util.Uninferable:
+ raise exceptions.InferenceError
+ return instance.infer_binary_op(opnode, op, other, context, inferred)
+
+
+def _aug_op(instance, opnode, op, other, context, reverse=False):
+ """Get an inference callable for an augmented binary operation."""
+ method_name = protocols.AUGMENTED_OP_METHOD[op]
+ return functools.partial(
+ _invoke_binop_inference,
+ instance=instance,
+ op=op,
+ opnode=opnode,
+ other=other,
+ context=context,
+ method_name=method_name,
+ )
+
+
+def _bin_op(instance, opnode, op, other, context, reverse=False):
+ """Get an inference callable for a normal binary operation.
+
+ If *reverse* is True, then the reflected method will be used instead.
+ """
+ if reverse:
+ method_name = protocols.REFLECTED_BIN_OP_METHOD[op]
+ else:
+ method_name = protocols.BIN_OP_METHOD[op]
+ return functools.partial(
+ _invoke_binop_inference,
+ instance=instance,
+ op=op,
+ opnode=opnode,
+ other=other,
+ context=context,
+ method_name=method_name,
+ )
+
+
+def _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 = contextmod.CallContext(args=[arg])
+ new_context.boundnode = None
+ yield new_context
+
+
+def _same_type(type1, type2):
+ """Check if type1 is the same as type2."""
+ return type1.qname() == type2.qname()
+
+
+def _get_binop_flow(
+ left, left_type, binary_opnode, right, right_type, context, reverse_context
+):
+ """Get the flow for binary operations.
+
+ The rules are a bit messy:
+
+ * if left and right have the same type, then only one
+ method will be called, left.__op__(right)
+ * if left and right are unrelated typewise, then first
+ left.__op__(right) is tried and if this does not exist
+ or returns NotImplemented, then right.__rop__(left) is tried.
+ * if left is a subtype of right, then only left.__op__(right)
+ is tried.
+ * if left is a supertype of right, then right.__rop__(left)
+ is first tried and then left.__op__(right)
+ """
+ op = binary_opnode.op
+ if _same_type(left_type, right_type):
+ methods = [_bin_op(left, binary_opnode, op, right, context)]
+ elif helpers.is_subtype(left_type, right_type):
+ methods = [_bin_op(left, binary_opnode, op, right, context)]
+ elif helpers.is_supertype(left_type, right_type):
+ methods = [
+ _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True),
+ _bin_op(left, binary_opnode, op, right, context),
+ ]
+ else:
+ methods = [
+ _bin_op(left, binary_opnode, op, right, context),
+ _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True),
+ ]
+ return methods
+
+
+def _get_aug_flow(
+ left, left_type, aug_opnode, right, right_type, context, reverse_context
+):
+ """Get the flow for augmented binary operations.
+
+ The rules are a bit messy:
+
+ * if left and right have the same type, then left.__augop__(right)
+ is first tried and then left.__op__(right).
+ * if left and right are unrelated typewise, then
+ left.__augop__(right) is tried, then left.__op__(right)
+ is tried and then right.__rop__(left) is tried.
+ * if left is a subtype of right, then left.__augop__(right)
+ is tried and then left.__op__(right).
+ * if left is a supertype of right, then left.__augop__(right)
+ is tried, then right.__rop__(left) and then
+ left.__op__(right)
+ """
+ bin_op = aug_opnode.op.strip("=")
+ aug_op = aug_opnode.op
+ if _same_type(left_type, right_type):
+ methods = [
+ _aug_op(left, aug_opnode, aug_op, right, context),
+ _bin_op(left, aug_opnode, bin_op, right, context),
+ ]
+ elif helpers.is_subtype(left_type, right_type):
+ methods = [
+ _aug_op(left, aug_opnode, aug_op, right, context),
+ _bin_op(left, aug_opnode, bin_op, right, context),
+ ]
+ elif helpers.is_supertype(left_type, right_type):
+ methods = [
+ _aug_op(left, aug_opnode, aug_op, right, context),
+ _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True),
+ _bin_op(left, aug_opnode, bin_op, right, context),
+ ]
+ else:
+ methods = [
+ _aug_op(left, aug_opnode, aug_op, right, context),
+ _bin_op(left, aug_opnode, bin_op, right, context),
+ _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True),
+ ]
+ return methods
+
+
+def _infer_binary_operation(left, right, binary_opnode, context, flow_factory):
+ """Infer a binary operation between a left operand and a right operand
+
+ This is used by both normal binary operations and augmented binary
+ operations, the only difference is the flow factory used.
+ """
+
+ context, reverse_context = _get_binop_contexts(context, left, right)
+ left_type = helpers.object_type(left)
+ right_type = helpers.object_type(right)
+ methods = flow_factory(
+ left, left_type, binary_opnode, right, right_type, context, reverse_context
+ )
+ for method in methods:
+ try:
+ results = list(method())
+ except AttributeError:
+ continue
+ except exceptions.AttributeInferenceError:
+ continue
+ except exceptions.InferenceError:
+ yield util.Uninferable
+ return
+ else:
+ if any(result is util.Uninferable for result in results):
+ yield util.Uninferable
+ return
+
+ if all(map(_is_not_implemented, results)):
+ continue
+ not_implemented = sum(
+ 1 for result in results if _is_not_implemented(result)
+ )
+ if not_implemented and not_implemented != len(results):
+ # Can't infer yet what this is.
+ yield util.Uninferable
+ return
+
+ yield from results
+ return
+ # The operation doesn't seem to be supported so let the caller know about it
+ yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type)
+
+
+def _infer_binop(self, context):
+ """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 contextmod.InferenceContext()
+ lhs_context = contextmod.copy_context(context)
+ rhs_context = contextmod.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(value is util.Uninferable for value in (rhs, lhs)):
+ # Don't know how to process this.
+ yield util.Uninferable
+ return
+
+ try:
+ yield from _infer_binary_operation(lhs, rhs, self, context, _get_binop_flow)
+ except exceptions._NonDeducibleTypeHierarchy:
+ yield util.Uninferable
+
+
+@decorators.yes_if_nothing_inferred
+@decorators.path_wrapper
+def infer_binop(self, context=None):
+ return _filter_operation_errors(
+ self, _infer_binop, context, util.BadBinaryOperationMessage
+ )
+
+
+nodes.BinOp._infer_binop = _infer_binop
+nodes.BinOp._infer = infer_binop
+
+
+def _infer_augassign(self, context=None):
+ """Inference logic for augmented binary operations."""
+ if context is None:
+ context = contextmod.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(value is util.Uninferable for value in (rhs, lhs)):
+ # Don't know how to process this.
+ yield util.Uninferable
+ return
+
+ try:
+ yield from _infer_binary_operation(
+ left=lhs,
+ right=rhs,
+ binary_opnode=self,
+ context=context,
+ flow_factory=_get_aug_flow,
+ )
+ except exceptions._NonDeducibleTypeHierarchy:
+ yield util.Uninferable
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_augassign(self, context=None):
+ return _filter_operation_errors(
+ self, _infer_augassign, context, util.BadBinaryOperationMessage
+ )
+
+
+nodes.AugAssign._infer_augassign = _infer_augassign
+nodes.AugAssign._infer = infer_augassign
+
+# End of binary operation inference.
+
+
+@decorators.raise_if_nothing_inferred
+def infer_arguments(self, context=None):
+ name = context.lookupname
+ if name is None:
+ raise exceptions.InferenceError(node=self, context=context)
+ return protocols._arguments_infer_argname(self, name, context)
+
+
+nodes.Arguments._infer = infer_arguments
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_assign(self, context=None):
+ """infer a AssignName/AssignAttr: need to inspect the RHS part of the
+ assign node
+ """
+ stmt = self.statement()
+ if isinstance(stmt, nodes.AugAssign):
+ return stmt.infer(context)
+
+ stmts = list(self.assigned_stmts(context=context))
+ return bases._infer_stmts(stmts, context)
+
+
+nodes.AssignName._infer = infer_assign
+nodes.AssignAttr._infer = infer_assign
+
+
+@decorators.raise_if_nothing_inferred
+@decorators.path_wrapper
+def infer_empty_node(self, context=None):
+ if not self.has_underlying_object():
+ yield util.Uninferable
+ else:
+ try:
+ yield from MANAGER.infer_ast_from_something(self.object, context=context)
+ except exceptions.AstroidError:
+ yield util.Uninferable
+
+
+nodes.EmptyNode._infer = infer_empty_node
+
+
+@decorators.raise_if_nothing_inferred
+def infer_index(self, context=None):
+ return self.value.infer(context)
+
+
+nodes.Index._infer = infer_index
+
+# TODO: move directly into bases.Instance when the dependency hell
+# will be solved.
+def instance_getitem(self, index, context=None):
+ # Rewrap index to Const for this case
+ new_context = contextmod.bind_context_to_node(context, self)
+ if not context:
+ context = new_context
+
+ # Create a new callcontext for providing index as an argument.
+ new_context.callcontext = contextmod.CallContext(args=[index])
+
+ method = next(self.igetattr("__getitem__", context=context), None)
+ if not isinstance(method, bases.BoundMethod):
+ raise exceptions.InferenceError(
+ "Could not find __getitem__ for {node!r}.", node=self, context=context
+ )
+
+ return next(method.infer_call_result(self, new_context))
+
+
+bases.Instance.getitem = instance_getitem
+
+
+def _populate_context_lookup(call, context):
+ # Allows context to be saved for later
+ # for inference inside a function
+ context_lookup = {}
+ if context is None:
+ return context_lookup
+ for arg in call.args:
+ if isinstance(arg, nodes.Starred):
+ context_lookup[arg.value] = context
+ else:
+ context_lookup[arg] = context
+ keywords = call.keywords if call.keywords is not None else []
+ for keyword in keywords:
+ context_lookup[keyword.value] = context
+ return context_lookup
+
+
+@decorators.raise_if_nothing_inferred
+def infer_ifexp(self, context=None):
+ """Support IfExp inference
+
+ If we can't infer the truthiness of the condition, we default
+ to inferring both branches. Otherwise, we infer either branch
+ depending on the condition.
+ """
+ both_branches = False
+ # We use two separate contexts for evaluating lhs and rhs because
+ # evaluating lhs may leave some undesired entries in context.path
+ # which may not let us infer right value of rhs.
+
+ context = context or contextmod.InferenceContext()
+ lhs_context = contextmod.copy_context(context)
+ rhs_context = contextmod.copy_context(context)
+ try:
+ test = next(self.test.infer(context=context.clone()))
+ except exceptions.InferenceError:
+ both_branches = True
+ else:
+ if test is not util.Uninferable:
+ if test.bool_value():
+ yield from self.body.infer(context=lhs_context)
+ else:
+ yield from self.orelse.infer(context=rhs_context)
+ else:
+ both_branches = True
+ if both_branches:
+ yield from self.body.infer(context=lhs_context)
+ yield from self.orelse.infer(context=rhs_context)
+
+
+nodes.IfExp._infer = infer_ifexp
diff --git a/venv/Lib/site-packages/astroid/interpreter/__init__.py b/venv/Lib/site-packages/astroid/interpreter/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/__init__.py
diff --git a/venv/Lib/site-packages/astroid/interpreter/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/astroid/interpreter/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..1bd9d33
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/interpreter/__pycache__/dunder_lookup.cpython-37.pyc b/venv/Lib/site-packages/astroid/interpreter/__pycache__/dunder_lookup.cpython-37.pyc
new file mode 100644
index 0000000..4001903
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/__pycache__/dunder_lookup.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/interpreter/__pycache__/objectmodel.cpython-37.pyc b/venv/Lib/site-packages/astroid/interpreter/__pycache__/objectmodel.cpython-37.pyc
new file mode 100644
index 0000000..dc15f91
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/__pycache__/objectmodel.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/interpreter/_import/__init__.py b/venv/Lib/site-packages/astroid/interpreter/_import/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/_import/__init__.py
diff --git a/venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..6cdce33
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/spec.cpython-37.pyc b/venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/spec.cpython-37.pyc
new file mode 100644
index 0000000..1f091df
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/spec.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/util.cpython-37.pyc b/venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/util.cpython-37.pyc
new file mode 100644
index 0000000..2a7fdbb
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/_import/__pycache__/util.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/astroid/interpreter/_import/spec.py b/venv/Lib/site-packages/astroid/interpreter/_import/spec.py
new file mode 100644
index 0000000..84e093b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/_import/spec.py
@@ -0,0 +1,344 @@
+# Copyright (c) 2016-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2017 Chris Philip <chrisp533@gmail.com>
+# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
+# Copyright (c) 2017 ioanatia <ioanatia@users.noreply.github.com>
+# Copyright (c) 2017 Calen Pennington <cale@edx.org>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+import abc
+import collections
+import distutils
+import enum
+import imp
+import os
+import sys
+import zipimport
+
+try:
+ import importlib.machinery
+
+ _HAS_MACHINERY = True
+except ImportError:
+ _HAS_MACHINERY = False
+
+try:
+ from functools import lru_cache
+except ImportError:
+ from backports.functools_lru_cache import lru_cache
+
+from . import util
+
+ModuleType = enum.Enum(
+ "ModuleType",
+ "C_BUILTIN C_EXTENSION PKG_DIRECTORY "
+ "PY_CODERESOURCE PY_COMPILED PY_FROZEN PY_RESOURCE "
+ "PY_SOURCE PY_ZIPMODULE PY_NAMESPACE",
+)
+_ImpTypes = {
+ imp.C_BUILTIN: ModuleType.C_BUILTIN,
+ imp.C_EXTENSION: ModuleType.C_EXTENSION,
+ imp.PKG_DIRECTORY: ModuleType.PKG_DIRECTORY,
+ imp.PY_COMPILED: ModuleType.PY_COMPILED,
+ imp.PY_FROZEN: ModuleType.PY_FROZEN,
+ imp.PY_SOURCE: ModuleType.PY_SOURCE,
+}
+if hasattr(imp, "PY_RESOURCE"):
+ _ImpTypes[imp.PY_RESOURCE] = ModuleType.PY_RESOURCE
+if hasattr(imp, "PY_CODERESOURCE"):
+ _ImpTypes[imp.PY_CODERESOURCE] = ModuleType.PY_CODERESOURCE
+
+
+def _imp_type_to_module_type(imp_type):
+ return _ImpTypes[imp_type]
+
+
+_ModuleSpec = collections.namedtuple(
+ "_ModuleSpec", "name type location " "origin submodule_search_locations"
+)
+
+
+class ModuleSpec(_ModuleSpec):
+ """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.
+ """
+
+ def __new__(
+ cls,
+ name,
+ module_type,
+ location=None,
+ origin=None,
+ submodule_search_locations=None,
+ ):
+ return _ModuleSpec.__new__(
+ cls,
+ name=name,
+ type=module_type,
+ location=location,
+ origin=origin,
+ submodule_search_locations=submodule_search_locations,
+ )
+
+
+class Finder:
+ """A finder is a class which knows how to find a particular module."""
+
+ def __init__(self, path=None):
+ self._path = path or sys.path
+
+ @abc.abstractmethod
+ def find_module(self, modname, module_parts, processed, submodule_path):
+ """Find the given module
+
+ Each finder is responsible for each protocol of finding, as long as
+ they all return a ModuleSpec.
+
+ :param str modname: The module which needs to be searched.
+ :param list module_parts: It should be a list of strings,
+ where each part contributes to the module's
+ namespace.
+ :param list processed: What parts from the module parts were processed
+ so far.
+ :param list submodule_path: A list of paths where the module
+ can be looked into.
+ :returns: A ModuleSpec, describing how and where the module was found,
+ None, otherwise.
+ """
+
+ def contribute_to_path(self, spec, processed):
+ """Get a list of extra paths where this finder can search."""
+
+
+class ImpFinder(Finder):
+ """A finder based on the imp module."""
+
+ def find_module(self, modname, module_parts, processed, submodule_path):
+ if submodule_path is not None:
+ submodule_path = list(submodule_path)
+ try:
+ stream, mp_filename, mp_desc = imp.find_module(modname, submodule_path)
+ except ImportError:
+ return None
+
+ # Close resources.
+ if stream:
+ stream.close()
+
+ return ModuleSpec(
+ name=modname,
+ location=mp_filename,
+ module_type=_imp_type_to_module_type(mp_desc[2]),
+ )
+
+ def contribute_to_path(self, spec, processed):
+ if spec.location is None:
+ # Builtin.
+ return None
+
+ if _is_setuptools_namespace(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))
+ ]
+ # We already import distutils elsewhere in astroid,
+ # so if it is the same module, we can use it directly.
+ elif spec.name == "distutils" and spec.location in distutils.__path__:
+ # distutils is patched inside virtualenvs to pick up submodules
+ # from the original Python, not from the virtualenv itself.
+ path = list(distutils.__path__)
+ else:
+ path = [spec.location]
+ return path
+
+
+class ExplicitNamespacePackageFinder(ImpFinder):
+ """A finder for the explicit namespace packages, generated through pkg_resources."""
+
+ def find_module(self, modname, module_parts, processed, submodule_path):
+ if processed:
+ modname = ".".join(processed + [modname])
+ if util.is_namespace(modname) and modname in sys.modules:
+ submodule_path = sys.modules[modname].__path__
+ return ModuleSpec(
+ name=modname,
+ location="",
+ origin="namespace",
+ module_type=ModuleType.PY_NAMESPACE,
+ submodule_search_locations=submodule_path,
+ )
+ return None
+
+ def contribute_to_path(self, spec, processed):
+ return spec.submodule_search_locations
+
+
+class ZipFinder(Finder):
+ """Finder that knows how to find a module inside zip files."""
+
+ def __init__(self, path):
+ super(ZipFinder, self).__init__(path)
+ self._zipimporters = _precache_zipimporters(path)
+
+ def find_module(self, modname, module_parts, processed, submodule_path):
+ try:
+ file_type, filename, path = _search_zip(module_parts, self._zipimporters)
+ except ImportError:
+ return None
+
+ return ModuleSpec(
+ name=modname,
+ location=filename,
+ origin="egg",
+ module_type=file_type,
+ submodule_search_locations=path,
+ )
+
+
+class PathSpecFinder(Finder):
+ """Finder based on importlib.machinery.PathFinder."""
+
+ def find_module(self, modname, module_parts, processed, submodule_path):
+ spec = importlib.machinery.PathFinder.find_spec(modname, path=submodule_path)
+ if spec:
+ # origin can be either a string on older Python versions
+ # or None in case it is a namespace package:
+ # https://github.com/python/cpython/pull/5481
+ is_namespace_pkg = spec.origin in ("namespace", None)
+ location = spec.origin if not is_namespace_pkg else None
+ module_type = ModuleType.PY_NAMESPACE if is_namespace_pkg else None
+ spec = ModuleSpec(
+ name=spec.name,
+ location=location,
+ origin=spec.origin,
+ module_type=module_type,
+ submodule_search_locations=list(spec.submodule_search_locations or []),
+ )
+ return spec
+
+ def contribute_to_path(self, spec, processed):
+ if spec.type == ModuleType.PY_NAMESPACE:
+ return spec.submodule_search_locations
+ return None
+
+
+_SPEC_FINDERS = (ImpFinder, ZipFinder)
+if _HAS_MACHINERY:
+ _SPEC_FINDERS += (PathSpecFinder,)
+_SPEC_FINDERS += (ExplicitNamespacePackageFinder,)
+
+
+def _is_setuptools_namespace(location):
+ try:
+ with open(os.path.join(location, "__init__.py"), "rb") as stream:
+ data = stream.read(4096)
+ except IOError:
+ pass
+ else:
+ 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
+
+
+@lru_cache()
+def _cached_set_diff(left, right):
+ result = set(left)
+ result.difference_update(right)
+ return result
+
+
+def _precache_zipimporters(path=None):
+ pic = sys.path_importer_cache
+
+ # When measured, despite having the same complexity (O(n)),
+ # converting to tuples and then caching the conversion to sets
+ # and the set difference is faster than converting to sets
+ # and then only caching the set difference.
+
+ req_paths = tuple(path or sys.path)
+ cached_paths = tuple(pic)
+ new_paths = _cached_set_diff(req_paths, cached_paths)
+ for entry_path in new_paths:
+ try:
+ pic[entry_path] = zipimport.zipimporter(entry_path)
+ except zipimport.ZipImportError:
+ continue
+ return pic
+
+
+def _search_zip(modpath, pic):
+ for filepath, importer in list(pic.items()):
+ if importer is not None:
+ found = importer.find_module(modpath[0])
+ if found:
+ if not importer.find_module(os.path.sep.join(modpath)):
+ raise ImportError(
+ "No module named %s in %s/%s"
+ % (".".join(modpath[1:]), filepath, modpath)
+ )
+ # import code; code.interact(local=locals())
+ return (
+ ModuleType.PY_ZIPMODULE,
+ os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath),
+ filepath,
+ )
+ raise ImportError("No module named %s" % ".".join(modpath))
+
+
+def _find_spec_with_path(search_path, modname, module_parts, processed, submodule_path):
+ finders = [finder(search_path) for finder in _SPEC_FINDERS]
+ for finder in finders:
+ spec = finder.find_module(modname, module_parts, processed, submodule_path)
+ if spec is None:
+ continue
+ return finder, spec
+
+ raise ImportError("No module named %s" % ".".join(module_parts))
+
+
+def find_spec(modpath, path=None):
+ """Find a spec for the given module.
+
+ :type modpath: list or tuple
+ :param modpath:
+ split module's name (i.e name of a module or package split
+ on '.'), with leading empty strings for explicit relative import
+
+ :type path: list or None
+ :param path:
+ optional list of path where the module or package should be
+ searched (use sys.path if nothing or None is given)
+
+ :rtype: ModuleSpec
+ :return: A module spec, which describes how the module was
+ found and where.
+ """
+ _path = path or sys.path
+
+ # Need a copy for not mutating the argument.
+ modpath = modpath[:]
+
+ submodule_path = None
+ module_parts = modpath[:]
+ processed = []
+
+ while modpath:
+ modname = modpath.pop(0)
+ finder, spec = _find_spec_with_path(
+ _path, modname, module_parts, processed, submodule_path or path
+ )
+ processed.append(modname)
+ if modpath:
+ submodule_path = finder.contribute_to_path(spec, processed)
+
+ if spec.type == ModuleType.PKG_DIRECTORY:
+ spec = spec._replace(submodule_search_locations=submodule_path)
+
+ return spec
diff --git a/venv/Lib/site-packages/astroid/interpreter/_import/util.py b/venv/Lib/site-packages/astroid/interpreter/_import/util.py
new file mode 100644
index 0000000..a917bd3
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/_import/util.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+
+try:
+ import pkg_resources
+except ImportError:
+ pkg_resources = None
+
+
+def is_namespace(modname):
+ return pkg_resources is not None and modname in pkg_resources._namespace_packages
diff --git a/venv/Lib/site-packages/astroid/interpreter/dunder_lookup.py b/venv/Lib/site-packages/astroid/interpreter/dunder_lookup.py
new file mode 100644
index 0000000..0ae9bc9
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/dunder_lookup.py
@@ -0,0 +1,66 @@
+# Copyright (c) 2016-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Contains logic for retrieving special methods.
+
+This implementation does not rely on the dot attribute access
+logic, found in ``.getattr()``. The difference between these two
+is that the dunder methods are looked with the type slots
+(you can find more about these here
+http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/)
+As such, the lookup for the special methods is actually simpler than
+the dot attribute access.
+"""
+import itertools
+
+import astroid
+from astroid import exceptions
+
+
+def _lookup_in_mro(node, name):
+ 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 exceptions.AttributeInferenceError(attribute=name, target=node)
+
+ return values
+
+
+def lookup(node, name):
+ """Lookup the given special method name in the given *node*
+
+ If the special method was found, then a list of attributes
+ will be returned. Otherwise, `astroid.AttributeInferenceError`
+ is going to be raised.
+ """
+ if isinstance(
+ node, (astroid.List, astroid.Tuple, astroid.Const, astroid.Dict, astroid.Set)
+ ):
+ return _builtin_lookup(node, name)
+ if isinstance(node, astroid.Instance):
+ return _lookup_in_mro(node, name)
+ if isinstance(node, astroid.ClassDef):
+ return _class_lookup(node, name)
+
+ raise exceptions.AttributeInferenceError(attribute=name, target=node)
+
+
+def _class_lookup(node, name):
+ metaclass = node.metaclass()
+ if metaclass is None:
+ raise exceptions.AttributeInferenceError(attribute=name, target=node)
+
+ return _lookup_in_mro(metaclass, name)
+
+
+def _builtin_lookup(node, name):
+ values = node.locals.get(name, [])
+ if not values:
+ raise exceptions.AttributeInferenceError(attribute=name, target=node)
+
+ return values
diff --git a/venv/Lib/site-packages/astroid/interpreter/objectmodel.py b/venv/Lib/site-packages/astroid/interpreter/objectmodel.py
new file mode 100644
index 0000000..5e488d9
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/interpreter/objectmodel.py
@@ -0,0 +1,738 @@
+# Copyright (c) 2016-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2017 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2017 Calen Pennington <cale@edx.org>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+"""
+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.
+"""
+
+import itertools
+import pprint
+import os
+import types
+from functools import lru_cache
+
+import astroid
+from astroid import context as contextmod
+from astroid import exceptions
+from astroid import node_classes
+
+
+IMPL_PREFIX = "attr_"
+
+
+def _dunder_dict(instance, attributes):
+ obj = node_classes.Dict(parent=instance)
+
+ # 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
+
+
+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):
+ return name in self.attributes()
+
+ @lru_cache(maxsize=None)
+ def attributes(self):
+ """Get the attributes which are exported by this object model."""
+ return [
+ obj[len(IMPL_PREFIX) :] for obj in dir(self) if obj.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 exceptions.AttributeInferenceError(target=self._instance, attribute=name)
+
+
+class ModuleModel(ObjectModel):
+ def _builtins(self):
+ builtins_ast_module = astroid.MANAGER.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 exceptions.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=self._instance.doc, parent=self._instance)
+
+ @property
+ def attr___file__(self):
+ return node_classes.Const(value=self._instance.file, parent=self._instance)
+
+ @property
+ def attr___dict__(self):
+ return _dunder_dict(self._instance, self._instance.globals)
+
+ @property
+ def attr___package__(self):
+ if not self._instance.package:
+ value = ""
+ else:
+ value = self._instance.name
+
+ return node_classes.Const(value=value, parent=self._instance)
+
+ # These are related to the Python 3 implementation of the
+ # import system,
+ # https://docs.python.org/3/reference/import.html#import-related-module-attributes
+
+ @property
+ def attr___spec__(self):
+ # No handling for now.
+ return node_classes.Unknown()
+
+ @property
+ def attr___loader__(self):
+ # No handling for now.
+ return node_classes.Unknown()
+
+ @property
+ def attr___cached__(self):
+ # No handling for now.
+ return node_classes.Unknown()
+
+
+class FunctionModel(ObjectModel):
+ @property
+ def attr___name__(self):
+ return node_classes.Const(value=self._instance.name, parent=self._instance)
+
+ @property
+ def attr___doc__(self):
+ return node_classes.Const(value=self._instance.doc, 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)
+
+ 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)
+
+ 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 exceptions.NoDefault:
+ continue
+
+ name = node_classes.Const(arg.name, parent=parent)
+ yield name, default
+
+ args = self._instance.args
+ obj = node_classes.Dict(parent=self._instance)
+ 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):
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid import bases
+
+ func = self._instance
+
+ class DescriptorBoundMethod(bases.BoundMethod):
+ """Bound method which knows how to understand calling descriptor binding."""
+
+ def implicit_parameters(self):
+ # Different than BoundMethod since the signature
+ # is different.
+ return 0
+
+ def infer_call_result(self, caller, context=None):
+ if len(caller.args) > 2 or len(caller.args) < 1:
+ raise exceptions.InferenceError(
+ "Invalid arguments for descriptor binding",
+ target=self,
+ context=context,
+ )
+
+ context = contextmod.copy_context(context)
+ cls = next(caller.args[0].infer(context=context))
+
+ if cls is astroid.Uninferable:
+ raise exceptions.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,
+ doc=func.doc,
+ lineno=func.lineno,
+ col_offset=func.col_offset,
+ parent=cls,
+ )
+ # pylint: disable=no-member
+ new_func.postinit(func.args, func.body, func.decorators, func.returns)
+
+ # 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
+ positional_or_keyword_params = func.args.args.copy()
+ positional_or_keyword_params.append(astroid.AssignName(name="type"))
+
+ positional_only_params = func.args.posonlyargs.copy()
+
+ arguments = astroid.Arguments(parent=func.args.parent)
+ arguments.postinit(
+ args=positional_or_keyword_params,
+ posonlyargs=positional_only_params,
+ defaults=[],
+ kwonlyargs=[],
+ kw_defaults=[],
+ annotations=[],
+ )
+ return arguments
+
+ return DescriptorBoundMethod(proxy=self._instance, bound=self._instance)
+
+ # These are here just for completion.
+ @property
+ def attr___ne__(self):
+ return node_classes.Unknown()
+
+ attr___subclasshook__ = attr___ne__
+ attr___str__ = attr___ne__
+ attr___sizeof__ = attr___ne__
+ attr___setattr___ = attr___ne__
+ attr___repr__ = attr___ne__
+ attr___reduce__ = attr___ne__
+ attr___reduce_ex__ = attr___ne__
+ attr___new__ = 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___init__ = 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):
+ @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(self._instance.doc)
+
+ @property
+ def attr___mro__(self):
+ if not self._instance.newstyle:
+ raise exceptions.AttributeInferenceError(
+ target=self._instance, attribute="__mro__"
+ )
+
+ mro = self._instance.mro()
+ obj = node_classes.Tuple(parent=self._instance)
+ obj.postinit(mro)
+ return obj
+
+ @property
+ def attr_mro(self):
+ if not self._instance.newstyle:
+ raise exceptions.AttributeInferenceError(
+ target=self._instance, attribute="mro"
+ )
+
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid import bases
+
+ 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, context=None):
+ 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 = contextmod.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.
+ """
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid import bases
+ from astroid import scoped_nodes
+
+ if not self._instance.newstyle:
+ raise exceptions.AttributeInferenceError(
+ target=self._instance, attribute="__subclasses__"
+ )
+
+ qname = self._instance.qname()
+ root = self._instance.root()
+ classes = [
+ cls
+ for cls in root.nodes_of_class(scoped_nodes.ClassDef)
+ if cls != self._instance and cls.is_subtype_of(qname)
+ ]
+
+ obj = node_classes.List(parent=self._instance)
+ obj.postinit(classes)
+
+ class SubclassesBoundMethod(bases.BoundMethod):
+ def infer_call_result(self, caller, context=None):
+ 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)
+
+
+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 BoundMethodModel(FunctionModel):
+ @property
+ def attr___func__(self):
+ return self._instance._proxied._proxied
+
+ @property
+ def attr___self__(self):
+ return self._instance.bound
+
+
+class GeneratorModel(FunctionModel):
+ def __new__(cls, *args, **kwargs):
+ # Append the values from the GeneratorType unto this object.
+ ret = super(GeneratorModel, cls).__new__(cls, *args, **kwargs)
+ generator = astroid.MANAGER.builtins_module["generator"]
+ for name, values in generator.locals.items():
+ method = values[0]
+ patched = lambda cls, meth=method: meth
+
+ setattr(type(ret), IMPL_PREFIX + name, property(patched))
+
+ return ret
+
+ @property
+ def attr___name__(self):
+ return node_classes.Const(
+ value=self._instance.parent.name, parent=self._instance
+ )
+
+ @property
+ def attr___doc__(self):
+ return node_classes.Const(
+ value=self._instance.parent.doc, parent=self._instance
+ )
+
+
+class AsyncGeneratorModel(GeneratorModel):
+ def __new__(cls, *args, **kwargs):
+ # Append the values from the AGeneratorType unto this object.
+ ret = super().__new__(cls, *args, **kwargs)
+ astroid_builtins = astroid.MANAGER.builtins_module
+ generator = astroid_builtins.get("async_generator")
+ if generator is None:
+ # Make it backward compatible.
+ generator = astroid_builtins.get("generator")
+
+ for name, values in generator.locals.items():
+ method = values[0]
+ patched = lambda cls, meth=method: meth
+
+ setattr(type(ret), IMPL_PREFIX + name, property(patched))
+
+ return ret
+
+
+class InstanceModel(ObjectModel):
+ @property
+ def attr___class__(self):
+ return self._instance._proxied
+
+ @property
+ def attr___module__(self):
+ return node_classes.Const(self._instance.root().qname())
+
+ @property
+ def attr___doc__(self):
+ return node_classes.Const(self._instance.doc)
+
+ @property
+ def attr___dict__(self):
+ return _dunder_dict(self._instance, self._instance.instance_attrs)
+
+
+# Exception instances
+
+
+class ExceptionInstanceModel(InstanceModel):
+ @property
+ def attr_args(self):
+ message = node_classes.Const("")
+ args = node_classes.Tuple(parent=self._instance)
+ args.postinit((message,))
+ return args
+
+ @property
+ def attr___traceback__(self):
+ builtins_ast_module = astroid.MANAGER.builtins_module
+ traceback_type = builtins_ast_module[types.TracebackType.__name__]
+ return traceback_type.instantiate_class()
+
+
+class SyntaxErrorInstanceModel(ExceptionInstanceModel):
+ @property
+ def attr_text(self):
+ return node_classes.Const("")
+
+
+class OSErrorInstanceModel(ExceptionInstanceModel):
+ @property
+ def attr_filename(self):
+ return node_classes.Const("")
+
+ @property
+ def attr_errno(self):
+ return node_classes.Const(0)
+
+ @property
+ def attr_strerror(self):
+ return node_classes.Const("")
+
+ attr_filename2 = attr_filename
+
+
+class ImportErrorInstanceModel(ExceptionInstanceModel):
+ @property
+ def attr_name(self):
+ return node_classes.Const("")
+
+ @property
+ def attr_path(self):
+ return node_classes.Const("")
+
+
+BUILTIN_EXCEPTIONS = {
+ "builtins.SyntaxError": SyntaxErrorInstanceModel,
+ "builtins.ImportError": ImportErrorInstanceModel,
+ # 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, context=None):
+ yield obj
+
+ meth = next(self._instance._proxied.igetattr(name))
+ return DictMethodBoundMethod(proxy=meth, bound=self._instance)
+
+ @property
+ def attr_items(self):
+ 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)
+
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid import objects
+
+ obj = objects.DictItems(obj)
+ return self._generic_dict_attribute(obj, "items")
+
+ @property
+ def attr_keys(self):
+ keys = [key for (key, _) in self._instance.items]
+ obj = node_classes.List(parent=self._instance)
+ obj.postinit(elts=keys)
+
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid import objects
+
+ obj = objects.DictKeys(obj)
+ return self._generic_dict_attribute(obj, "keys")
+
+ @property
+ def attr_values(self):
+
+ values = [value for (_, value) in self._instance.items]
+ obj = node_classes.List(parent=self._instance)
+ obj.postinit(values)
+
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid import objects
+
+ obj = objects.DictValues(obj)
+ return self._generic_dict_attribute(obj, "values")
diff --git a/venv/Lib/site-packages/astroid/manager.py b/venv/Lib/site-packages/astroid/manager.py
new file mode 100644
index 0000000..e5fd0d6
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/manager.py
@@ -0,0 +1,337 @@
+# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 BioGeek <jeroen.vangoey@gmail.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2017 Iva Miholic <ivamiho@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""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)
+"""
+
+import os
+import zipimport
+
+from astroid import exceptions
+from astroid.interpreter._import import spec
+from astroid import modutils
+from astroid import transforms
+
+
+ZIP_IMPORT_EXTS = (".zip", ".egg", ".whl")
+
+
+def safe_repr(obj):
+ try:
+ return repr(obj)
+ except Exception: # pylint: disable=broad-except
+ return "???"
+
+
+class AstroidManager:
+ """the astroid manager, responsible to build astroid from files
+ or modules.
+
+ Use the Borg pattern.
+ """
+
+ name = "astroid loader"
+ brain = {}
+
+ def __init__(self):
+ self.__dict__ = AstroidManager.brain
+ if not self.__dict__:
+ # NOTE: cache entries are added by the [re]builder
+ self.astroid_cache = {}
+ self._mod_file_cache = {}
+ self._failed_import_hooks = []
+ self.always_load_extensions = False
+ self.optimize_ast = False
+ self.extension_package_whitelist = set()
+ self._transform = transforms.TransformVisitor()
+
+ # Export these APIs for convenience
+ self.register_transform = self._transform.register_transform
+ self.unregister_transform = self._transform.unregister_transform
+ self.max_inferable_values = 100
+
+ @property
+ def builtins_module(self):
+ return self.astroid_cache["builtins"]
+
+ def visit_transforms(self, node):
+ """Visit the transforms and apply them to the given *node*."""
+ return self._transform.visit(node)
+
+ def ast_from_file(self, filepath, modname=None, fallback=True, source=False):
+ """given a module name, return the astroid object"""
+ try:
+ filepath = modutils.get_source_file(filepath, include_no_ext=True)
+ source = True
+ except modutils.NoSourceFile:
+ pass
+ if modname is None:
+ try:
+ modname = ".".join(modutils.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]
+ if source:
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid.builder import AstroidBuilder
+
+ return AstroidBuilder(self).file_build(filepath, modname)
+ if fallback and modname:
+ return self.ast_from_module_name(modname)
+ raise exceptions.AstroidBuildingError(
+ "Unable to build an AST for {path}.", path=filepath
+ )
+
+ def _build_stub_module(self, modname):
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid.builder import AstroidBuilder
+
+ return AstroidBuilder(self).string_build("", modname)
+
+ def _build_namespace_module(self, modname, path):
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid.builder import build_namespace_package_module
+
+ return build_namespace_package_module(modname, path)
+
+ def _can_load_extension(self, modname):
+ if self.always_load_extensions:
+ return True
+ if modutils.is_standard_module(modname):
+ return True
+ parts = modname.split(".")
+ return any(
+ ".".join(parts[:x]) in self.extension_package_whitelist
+ for x in range(1, len(parts) + 1)
+ )
+
+ def ast_from_module_name(self, modname, context_file=None):
+ """given a module name, return the astroid object"""
+ if modname in self.astroid_cache:
+ return self.astroid_cache[modname]
+ if modname == "__main__":
+ return self._build_stub_module(modname)
+ old_cwd = os.getcwd()
+ if context_file:
+ 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:
+ module = modutils.load_module_from_name(modname)
+ except Exception as ex:
+ raise exceptions.AstroidImportError(
+ "Loading {modname} failed with:\n{error}",
+ modname=modname,
+ path=found_spec.location,
+ ) from ex
+ return self.ast_from_module(module, modname)
+
+ elif found_spec.type == spec.ModuleType.PY_COMPILED:
+ raise exceptions.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
+ )
+
+ if found_spec.location is None:
+ raise exceptions.AstroidImportError(
+ "Can't find a file for module {modname}.", modname=modname
+ )
+
+ return self.ast_from_file(found_spec.location, modname, fallback=False)
+ except exceptions.AstroidBuildingError as e:
+ for hook in self._failed_import_hooks:
+ try:
+ return hook(modname)
+ except exceptions.AstroidBuildingError:
+ pass
+ raise e
+ finally:
+ os.chdir(old_cwd)
+
+ def zip_import_data(self, filepath):
+ if zipimport is None:
+ return None
+
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid.builder import AstroidBuilder
+
+ builder = AstroidBuilder(self)
+ for ext in ZIP_IMPORT_EXTS:
+ try:
+ eggpath, resource = filepath.rsplit(ext + os.path.sep, 1)
+ except ValueError:
+ continue
+ try:
+ importer = zipimport.zipimporter(eggpath + ext)
+ zmodname = resource.replace(os.path.sep, ".")
+ if importer.is_package(resource):
+ zmodname = zmodname + ".__init__"
+ module = builder.string_build(
+ importer.get_source(resource), zmodname, filepath
+ )
+ return module
+ except Exception: # pylint: disable=broad-except
+ continue
+ return None
+
+ def file_from_module_name(self, modname, contextfile):
+ try:
+ value = self._mod_file_cache[(modname, contextfile)]
+ except KeyError:
+ try:
+ value = modutils.file_info_from_modpath(
+ modname.split("."), context_file=contextfile
+ )
+ except ImportError as ex:
+ value = exceptions.AstroidImportError(
+ "Failed to import module {modname} with error:\n{error}.",
+ modname=modname,
+ error=ex,
+ )
+ self._mod_file_cache[(modname, contextfile)] = value
+ if isinstance(value, exceptions.AstroidBuildingError):
+ raise value
+ return value
+
+ def ast_from_module(self, module, modname=None):
+ """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 modutils.is_python_source(filepath):
+ return self.ast_from_file(filepath, modname)
+ except AttributeError:
+ pass
+
+ # pylint: disable=import-outside-toplevel; circular import
+ from astroid.builder import AstroidBuilder
+
+ return AstroidBuilder(self).module_build(module, modname)
+
+ def ast_from_class(self, klass, modname=None):
+ """get astroid for the given class"""
+ if modname is None:
+ try:
+ modname = klass.__module__
+ except AttributeError as exc:
+ raise exceptions.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)
+ return modastroid.getattr(klass.__name__)[0] # XXX
+
+ def infer_ast_from_something(self, obj, context=None):
+ """infer astroid for the given class"""
+ if hasattr(obj, "__class__") and not isinstance(obj, type):
+ klass = obj.__class__
+ else:
+ klass = obj
+ try:
+ modname = klass.__module__
+ except AttributeError as exc:
+ raise exceptions.AstroidBuildingError(
+ "Unable to get module for {class_repr}.",
+ cls=klass,
+ class_repr=safe_repr(klass),
+ ) from exc
+ except Exception as exc:
+ raise exceptions.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 exceptions.AstroidBuildingError(
+ "Unable to get name for {class_repr}:\n",
+ cls=klass,
+ class_repr=safe_repr(klass),
+ ) from exc
+ except Exception as exc:
+ raise exceptions.AstroidImportError(
+ "Unexpected error while retrieving name for {class_repr}:\n" "{error}",
+ cls=klass,
+ class_repr=safe_repr(klass),
+ ) from exc
+ # take care, on living object __module__ is regularly wrong :(
+ modastroid = self.ast_from_module_name(modname)
+ if klass is obj:
+ for inferred in modastroid.igetattr(name, context):
+ yield inferred
+ else:
+ for inferred in modastroid.igetattr(name, context):
+ yield inferred.instantiate_class()
+
+ def register_failed_import_hook(self, hook):
+ """Registers a hook to resolve imports that cannot be found otherwise.
+
+ `hook` must be a function that accepts a single argument `modname` which
+ contains the name of the module or package that could not be imported.
+ If `hook` can resolve the import, must return a node of type `astroid.Module`,
+ otherwise, it must raise `AstroidBuildingError`.
+ """
+ self._failed_import_hooks.append(hook)
+
+ def cache_module(self, module):
+ """Cache a module if no module with the same name is known yet."""
+ self.astroid_cache.setdefault(module.name, module)
+
+ def bootstrap(self):
+ """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):
+ """Clear the underlying cache. Also bootstraps the builtins module."""
+ self.astroid_cache.clear()
+ self.bootstrap()
diff --git a/venv/Lib/site-packages/astroid/mixins.py b/venv/Lib/site-packages/astroid/mixins.py
new file mode 100644
index 0000000..497a840
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/mixins.py
@@ -0,0 +1,160 @@
+# Copyright (c) 2010-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""This module contains some mixins for the different nodes.
+"""
+import itertools
+
+from astroid import decorators
+from astroid import exceptions
+
+
+class BlockRangeMixIn:
+ """override block range """
+
+ @decorators.cachedproperty
+ def blockstart_tolineno(self):
+ return self.lineno
+
+ def _elsed_block_range(self, lineno, orelse, last=None):
+ """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 FilterStmtsMixin:
+ """Mixin for statement filtering and assignment type"""
+
+ def _get_filtered_stmts(self, _, node, _stmts, mystmt):
+ """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 AssignTypeMixin:
+ def assign_type(self):
+ return self
+
+ def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt):
+ """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 ParentAssignTypeMixin(AssignTypeMixin):
+ def assign_type(self):
+ return self.parent.assign_type()
+
+
+class ImportFromMixin(FilterStmtsMixin):
+ """MixIn for From and Import Nodes"""
+
+ def _infer_name(self, frame, name):
+ return name
+
+ def do_import_module(self, modname=None):
+ """return the ast for a module whose name is <modname> imported by <self>
+ """
+ # handle special case where we are on a package node importing a module
+ # using the same name as the package, which may end in an infinite loop
+ # on relative imports
+ # XXX: no more needed ?
+ mymodule = self.root()
+ level = getattr(self, "level", None) # Import as no level
+ if modname is None:
+ modname = self.modname
+ # XXX we should investigate deeper if we really want to check
+ # importing itself: modname and mymodule.name be relative or absolute
+ if mymodule.relative_to_absolute_name(modname, level) == mymodule.name:
+ # FIXME: we used to raise InferenceError here, but why ?
+ return mymodule
+
+ return mymodule.import_module(
+ modname, level=level, relative_only=level and level >= 1
+ )
+
+ def real_name(self, asname):
+ """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 exceptions.AttributeInferenceError(
+ "Could not find original name for {attribute} in {target!r}",
+ target=self,
+ attribute=asname,
+ )
+
+
+class MultiLineBlockMixin:
+ """Mixin for nodes with 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.
+ """
+
+ @decorators.cachedproperty
+ 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_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()
+
+ @decorators.cached
+ def _get_assign_nodes(self):
+ children_assign_nodes = (
+ child_node._get_assign_nodes()
+ for block in self._multi_line_blocks
+ for child_node in block
+ )
+ return list(itertools.chain.from_iterable(children_assign_nodes))
+
+
+class NoChildrenMixin:
+ """Mixin for nodes with no children, e.g. Pass."""
+
+ def get_children(self):
+ yield from ()
diff --git a/venv/Lib/site-packages/astroid/modutils.py b/venv/Lib/site-packages/astroid/modutils.py
new file mode 100644
index 0000000..0c009b1
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/modutils.py
@@ -0,0 +1,698 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2014 Denis Laxalde <denis.laxalde@logilab.fr>
+# Copyright (c) 2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Radosław Ganczarek <radoslaw@ganczarek.in>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2018 Mario Corchero <mcorcherojim@bloomberg.net>
+# Copyright (c) 2018 Mario Corchero <mariocj89@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""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
+"""
+import imp
+import os
+import platform
+import sys
+import itertools
+from distutils.sysconfig import get_python_lib # pylint: disable=import-error
+
+# pylint: disable=import-error, no-name-in-module
+from distutils.errors import DistutilsPlatformError
+
+# distutils is replaced by virtualenv with a module that does
+# weird path manipulations in order to get to the
+# real distutils module.
+
+from .interpreter._import import spec
+from .interpreter._import import util
+
+if sys.platform.startswith("win"):
+ PY_SOURCE_EXTS = ("py", "pyw")
+ PY_COMPILED_EXTS = ("dll", "pyd")
+else:
+ PY_SOURCE_EXTS = ("py",)
+ PY_COMPILED_EXTS = ("so",)
+
+
+try:
+ # The explicit sys.prefix is to work around a patch in virtualenv that
+ # replaces the 'real' sys.prefix (i.e. the location of the binary)
+ # with the prefix from which the virtualenv was created. This throws
+ # off the detection logic for standard library modules, thus the
+ # workaround.
+ STD_LIB_DIRS = {
+ get_python_lib(standard_lib=True, prefix=sys.prefix),
+ # Take care of installations where exec_prefix != prefix.
+ get_python_lib(standard_lib=True, prefix=sys.exec_prefix),
+ get_python_lib(standard_lib=True),
+ }
+# get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to
+# non-valid path, see https://bugs.pypy.org/issue1164
+except DistutilsPlatformError:
+ STD_LIB_DIRS = set()
+
+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.
+ STD_LIB_DIRS.add(os.path.join(sys.real_prefix, "dlls"))
+ 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 platform.python_implementation() == "PyPy":
+ _root = os.path.join(sys.prefix, "lib_pypy")
+ STD_LIB_DIRS.add(_root)
+ try:
+ # real_prefix is defined when running inside virtualenv.
+ STD_LIB_DIRS.add(os.path.join(sys.real_prefix, "lib_pypy"))
+ except AttributeError:
+ pass
+ del _root
+if os.name == "posix":
+ # Need the real prefix is we're under a virtualenv, otherwise
+ # the usual one will do.
+ try:
+ prefix = sys.real_prefix
+ except AttributeError:
+ prefix = sys.prefix
+
+ def _posix_path(path):
+ 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/PyCQA/pylint/issues/712#issuecomment-163178753
+ STD_LIB_DIRS.add(_posix_path("lib64"))
+
+EXT_LIB_DIRS = {get_python_lib(), get_python_lib(True)}
+IS_JYTHON = platform.python_implementation() == "Jython"
+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):
+ return os.path.normcase(os.path.abspath(path))
+
+
+def _canonicalize_path(path):
+ return os.path.realpath(os.path.expanduser(path))
+
+
+def _path_from_filename(filename, is_jython=IS_JYTHON):
+ 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, dirnames, filenames):
+ """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)
+
+
+_NORM_PATH_CACHE = {}
+
+
+def _cache_normalize_path(path):
+ """abspath 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.
+ try:
+ return _NORM_PATH_CACHE[path]
+ except KeyError:
+ if not path: # don't cache result for ''
+ return _normalize_path(path)
+ result = _NORM_PATH_CACHE[path] = _normalize_path(path)
+ return result
+
+
+def load_module_from_name(dotted_name, path=None, use_sys=True):
+ """Load a Python module from its name.
+
+ :type dotted_name: str
+ :param dotted_name: python name of a module or package
+
+ :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)
+
+ :type use_sys: bool
+ :param use_sys:
+ boolean indicating whether the sys.modules dictionary should be
+ used or not
+
+
+ :raise ImportError: if the module or package is not found
+
+ :rtype: module
+ :return: the loaded module
+ """
+ return load_module_from_modpath(dotted_name.split("."), path, use_sys)
+
+
+def load_module_from_modpath(parts, path=None, use_sys=1):
+ """Load a python module from its split name.
+
+ :type parts: list(str) or tuple(str)
+ :param parts:
+ python name of a module or package split on '.'
+
+ :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)
+
+ :type use_sys: bool
+ :param use_sys:
+ boolean indicating whether the sys.modules dictionary should be used or not
+
+ :raise ImportError: if the module or package is not found
+
+ :rtype: module
+ :return: the loaded module
+ """
+ if use_sys:
+ try:
+ return sys.modules[".".join(parts)]
+ except KeyError:
+ pass
+ modpath = []
+ prevmodule = None
+ for part in parts:
+ modpath.append(part)
+ curname = ".".join(modpath)
+ module = None
+ if len(modpath) != len(parts):
+ # even with use_sys=False, should try to get outer packages from sys.modules
+ module = sys.modules.get(curname)
+ elif use_sys:
+ # because it may have been indirectly loaded through a parent
+ module = sys.modules.get(curname)
+ if module is None:
+ mp_file, mp_filename, mp_desc = imp.find_module(part, path)
+ module = imp.load_module(curname, mp_file, mp_filename, mp_desc)
+ # mp_file still needs to be closed.
+ if mp_file:
+ mp_file.close()
+ if prevmodule:
+ setattr(prevmodule, part, module)
+ _file = getattr(module, "__file__", "")
+ prevmodule = module
+ if not _file and util.is_namespace(curname):
+ continue
+ if not _file and len(modpath) != len(parts):
+ raise ImportError("no module in %s" % ".".join(parts[len(modpath) :]))
+ path = [os.path.dirname(_file)]
+ return module
+
+
+def load_module_from_file(filepath, path=None, use_sys=True, extrapath=None):
+ """Load a Python module from it's path.
+
+ :type filepath: str
+ :param filepath: path to the python module or package
+
+ :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)
+
+ :type use_sys: bool
+ :param use_sys:
+ boolean indicating whether the sys.modules dictionary should be
+ used or not
+
+
+ :raise ImportError: if the module or package is not found
+
+ :rtype: module
+ :return: the loaded module
+ """
+ modpath = modpath_from_file(filepath, extrapath)
+ return load_module_from_modpath(modpath, path, use_sys)
+
+
+def check_modpath_has_init(path, mod_path):
+ """check there are some __init__.py all along the way"""
+ modpath = []
+ for part in mod_path:
+ modpath.append(part)
+ path = os.path.join(path, part)
+ if not _has_init(path):
+ old_namespace = util.is_namespace(".".join(modpath))
+ if not old_namespace:
+ return False
+ return True
+
+
+def _get_relative_base_path(filename, path_to_check):
+ """Extracts the relative mod path of the file to import from
+
+ Check if a file is within the passed in path and if so, returns the
+ relative mod path from the one passed in.
+
+ If the filename is no in path_to_check, returns None
+
+ Note this function will look for both abs and realpath of the file,
+ this allows to find the relative base path even if the file is a
+ symlink of a file in the passed in path
+
+ Examples:
+ _get_relative_base_path("/a/b/c/d.py", "/a/b") -> ["c","d"]
+ _get_relative_base_path("/a/b/c/d.py", "/dev") -> None
+ """
+ importable_path = None
+ path_to_check = os.path.normcase(path_to_check)
+ abs_filename = os.path.abspath(filename)
+ if os.path.normcase(abs_filename).startswith(path_to_check):
+ importable_path = abs_filename
+
+ real_filename = os.path.realpath(filename)
+ if os.path.normcase(real_filename).startswith(path_to_check):
+ importable_path = real_filename
+
+ if importable_path:
+ base_path = os.path.splitext(importable_path)[0]
+ relative_base_path = base_path[len(path_to_check) :]
+ return [pkg for pkg in relative_base_path.split(os.sep) if pkg]
+
+ return None
+
+
+def modpath_from_file_with_callback(filename, extrapath=None, is_package_cb=None):
+ filename = os.path.expanduser(_path_from_filename(filename))
+
+ if extrapath is not None:
+ for path_ in itertools.chain(map(_canonicalize_path, extrapath), extrapath):
+ path = os.path.abspath(path_)
+ if not path:
+ continue
+ submodpath = _get_relative_base_path(filename, path)
+ if not submodpath:
+ continue
+ if is_package_cb(path, submodpath[:-1]):
+ return extrapath[path_].split(".") + submodpath
+
+ for path in itertools.chain(map(_canonicalize_path, sys.path), sys.path):
+ path = _cache_normalize_path(path)
+ if not path:
+ continue
+ modpath = _get_relative_base_path(filename, path)
+ if not modpath:
+ continue
+ if is_package_cb(path, modpath[:-1]):
+ return modpath
+
+ raise ImportError(
+ "Unable to find module for %s in %s" % (filename, ", \n".join(sys.path))
+ )
+
+
+def modpath_from_file(filename, extrapath=None):
+ """given a file path return the corresponding split module's name
+ (i.e 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 extrapath: dict
+ :param extrapath:
+ optional extra search path, with path as key and package name for the path
+ as value. This is usually useful to handle package split in multiple
+ directories using __path__ trick.
+
+
+ :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, extrapath, check_modpath_has_init)
+
+
+def file_from_modpath(modpath, path=None, context_file=None):
+ return file_info_from_modpath(modpath, path, context_file).location
+
+
+def file_info_from_modpath(modpath, path=None, context_file=None):
+ """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
+
+ :type modpath: list or tuple
+ :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!)
+
+ :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)
+
+ :type context_file: str or None
+ :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
+
+ :rtype: (str or None, import type)
+ :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 = 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__, module_type=imp.PY_SOURCE
+ )
+ return _spec_from_modpath(modpath, path, context)
+
+
+def get_module_part(dotted_name, context_file=None):
+ """given a dotted name return the module part of the name :
+
+ >>> get_module_part('astroid.as_string.dump')
+ 'astroid.as_string'
+
+ :type dotted_name: str
+ :param dotted_name: full name of the identifier we are interested in
+
+ :type context_file: str or None
+ :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
+
+ :rtype: str or None
+ :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 = 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
+ while parts[starti] == "": # for all further dots: change context
+ starti += 1
+ 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, blacklist, list_all=False):
+ """given a package directory return a list of all available python
+ module's files in the package and its subpackages
+
+ :type src_directory: str
+ :param src_directory:
+ path of the directory corresponding to the package
+
+ :type blacklist: list or tuple
+ :param blacklist: iterable
+ list of files or directories to ignore.
+
+ :type list_all: bool
+ :param list_all:
+ get files from all paths, including ones without __init__.py
+
+ :rtype: list
+ :return:
+ the list of all available python module's files in the package and
+ its subpackages
+ """
+ files = []
+ 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" not in 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, include_no_ext=False):
+ """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...)
+
+ :type filename: str
+ :param filename: python module's file name
+
+
+ :raise NoSourceFile: if no source file exists on the file system
+
+ :rtype: str
+ :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)
+ for ext in PY_SOURCE_EXTS:
+ source_path = "%s.%s" % (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):
+ """
+ rtype: bool
+ return: True if the filename is a python source file
+ """
+ return os.path.splitext(filename)[1][1:] in PY_SOURCE_EXTS
+
+
+def is_standard_module(modname, std_path=None):
+ """try to guess if a module is a standard python module (by default,
+ see `std_path` parameter's description)
+
+ :type modname: str
+ :param modname: name of the module we are interested in
+
+ :type std_path: list(str) or tuple(str)
+ :param std_path: list of path considered has standard
+
+
+ :rtype: bool
+ :return:
+ true if the module:
+ - is located on the path listed in one of the directory in `std_path`
+ - is a built-in module
+ """
+ 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
+ for path in std_path:
+ if filename.startswith(_cache_normalize_path(path)):
+ return True
+ return False
+
+
+def is_relative(modname, from_file):
+ """return true if the given module name is relative to the given
+ file name
+
+ :type modname: str
+ :param modname: name of the module we are interested in
+
+ :type from_file: str
+ :param from_file:
+ path of the module from which modname has been imported
+
+ :rtype: bool
+ :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
+ try:
+ stream, _, _ = imp.find_module(modname.split(".")[0], [from_file])
+
+ # Close the stream to avoid ResourceWarnings.
+ if stream:
+ stream.close()
+ return True
+ except ImportError:
+ return False
+
+
+# internal only functions #####################################################
+
+
+def _spec_from_modpath(modpath, path=None, context=None):
+ """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:
+ 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:
+ 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):
+ """return true if the given filename should be considered as a python file
+
+ .pyc and .pyo are ignored
+ """
+ return filename.endswith((".py", ".so", ".pyd", ".pyw"))
+
+
+def _has_init(directory):
+ """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):
+ return specobj.type == spec.ModuleType.PY_NAMESPACE
+
+
+def is_directory(specobj):
+ return specobj.type == spec.ModuleType.PKG_DIRECTORY
diff --git a/venv/Lib/site-packages/astroid/node_classes.py b/venv/Lib/site-packages/astroid/node_classes.py
new file mode 100644
index 0000000..994c96b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/node_classes.py
@@ -0,0 +1,4775 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2016-2017 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2016 Jared Garst <jgarst@users.noreply.github.com>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2016 Dave Baum <dbaum@google.com>
+# Copyright (c) 2017-2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 rr- <rr-@sakuya.pl>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 brendanator <brendan.maginnis@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+# pylint: disable=too-many-lines; https://github.com/PyCQA/astroid/issues/465
+
+"""Module for some node classes. More nodes in scoped_nodes.py
+"""
+
+import abc
+import builtins as builtins_mod
+import itertools
+import pprint
+import sys
+from functools import lru_cache, singledispatch as _singledispatch
+
+from astroid import as_string
+from astroid import bases
+from astroid import context as contextmod
+from astroid import decorators
+from astroid import exceptions
+from astroid import manager
+from astroid import mixins
+from astroid import util
+
+
+BUILTINS = builtins_mod.__name__
+MANAGER = manager.AstroidManager()
+PY38 = sys.version_info[:2] >= (3, 8)
+
+
+def _is_const(value):
+ return isinstance(value, tuple(CONST_CLS))
+
+
+@decorators.raise_if_nothing_inferred
+def unpack_infer(stmt, context=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 dict(node=stmt, context=context)
+ # if inferred is a final node, return it and stop
+ inferred = next(stmt.infer(context))
+ if inferred is stmt:
+ yield inferred
+ return dict(node=stmt, context=context)
+ # else, infer recursively, except Uninferable object that should be returned as is
+ for inferred in stmt.infer(context):
+ if inferred is util.Uninferable:
+ yield inferred
+ else:
+ yield from unpack_infer(inferred, context)
+
+ return dict(node=stmt, context=context)
+
+
+def are_exclusive(
+ stmt1, stmt2, exceptions=None
+): # pylint: disable=redefined-outer-name
+ """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 TryExcept statement, look if nodes are
+ in exclusive branches
+ """
+ # index stmt1's parents
+ stmt1_parents = {}
+ children = {}
+ node = stmt1.parent
+ previous = stmt1
+ while node:
+ stmt1_parents[node] = 1
+ children[node] = previous
+ previous = node
+ node = node.parent
+ # climb among stmt2's parents until we find a common parent
+ node = stmt2.parent
+ previous = stmt2
+ while node:
+ if node in stmt1_parents:
+ # if the common parent is a If or TryExcept statement, look if
+ # nodes are in exclusive branches
+ if isinstance(node, If) and exceptions is None:
+ if (
+ node.locate_child(previous)[1]
+ is not node.locate_child(children[node])[1]
+ ):
+ return True
+ elif isinstance(node, TryExcept):
+ 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
+ node = node.parent
+ return False
+
+
+# getitem() helpers.
+
+_SLICE_SENTINEL = object()
+
+
+def _slice_value(index, context=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 exceptions.InferenceError:
+ 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=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 exceptions.AstroidTypeError(
+ message="Could not infer slice used in subscript",
+ node=node,
+ index=node.parent,
+ context=context,
+ )
+
+
+def _container_getitem(instance, elts, index, context=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 IndexError as exc:
+ raise exceptions.AstroidIndexError(
+ message="Index {index!s} out of range",
+ node=instance,
+ index=index,
+ context=context,
+ ) from exc
+ except TypeError as exc:
+ raise exceptions.AstroidTypeError(
+ message="Type error {error!r}", node=instance, index=index, context=context
+ ) from exc
+
+ raise exceptions.AstroidTypeError("Could not use %s as subscript index" % index)
+
+
+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
+}
+
+
+class NodeNG:
+ """ A node of the new Abstract Syntax Tree (AST).
+
+ This is the base class for all Astroid node classes.
+ """
+
+ is_statement = False
+ """Whether this node indicates a statement.
+
+ :type: bool
+ """
+ optional_assign = 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.
+
+ :type: bool
+ """
+ is_function = False # True for FunctionDef nodes
+ """Whether this node indicates a function.
+
+ :type: bool
+ """
+ is_lambda = False
+ # Attributes below are set by the builder module or by raw factories
+ lineno = None
+ """The line that this node appears on in the source code.
+
+ :type: int or None
+ """
+ col_offset = None
+ """The column that this node appears on in the source code.
+
+ :type: int or None
+ """
+ parent = None
+ """The parent node in the syntax tree.
+
+ :type: NodeNG or None
+ """
+ _astroid_fields = ()
+ """Node attributes that contain child nodes.
+
+ This is redefined in most concrete classes.
+
+ :type: tuple(str)
+ """
+ _other_fields = ()
+ """Node attributes that do not contain child nodes.
+
+ :type: tuple(str)
+ """
+ _other_other_fields = ()
+ """Attributes that contain AST-dependent fields.
+
+ :type: tuple(str)
+ """
+ # instance specific inference function infer(node, context)
+ _explicit_inference = None
+
+ def __init__(self, lineno=None, col_offset=None, parent=None):
+ """
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.lineno = lineno
+ self.col_offset = col_offset
+ self.parent = parent
+
+ def infer(self, context=None, **kwargs):
+ """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 not None:
+ context = context.extra_context.get(self, context)
+ if self._explicit_inference is not None:
+ # explicit_inference is not bound, give it self explicitly
+ try:
+ # pylint: disable=not-callable
+ return self._explicit_inference(self, context, **kwargs)
+ except exceptions.UseInferenceDefault:
+ pass
+
+ if not context:
+ return self._infer(context, **kwargs)
+
+ key = (self, context.lookupname, context.callcontext, context.boundnode)
+ if key in context.inferred:
+ return iter(context.inferred[key])
+
+ gen = context.cache_generator(key, self._infer(context, **kwargs))
+ return util.limit_inference(gen, MANAGER.max_inferable_values)
+
+ def _repr_name(self):
+ """Get a name for nice representation.
+
+ This is either :attr:`name`, :attr:`attrname`, or the empty string.
+
+ :returns: The nice name.
+ :rtype: str
+ """
+ names = {"name", "attrname"}
+ if all(name not in self._astroid_fields for name in names):
+ return getattr(self, "name", getattr(self, "attrname", ""))
+ return ""
+
+ def __str__(self):
+ 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)
+ 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("%s=%s" % (field, "".join(inner)))
+
+ return string % {
+ "cname": cname,
+ "rname": rname,
+ "fields": (",\n" + " " * alignment).join(result),
+ }
+
+ def __repr__(self):
+ rname = self._repr_name()
+ 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": self.fromlineno,
+ "id": id(self),
+ }
+
+ def accept(self, visitor):
+ """Visit this node using the given visitor."""
+ func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
+ return func(self)
+
+ def get_children(self):
+ """Get the child nodes below this node.
+
+ :returns: The children.
+ :rtype: iterable(NodeNG)
+ """
+ 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
+
+ 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
+ """
+ for field in self._astroid_fields[::-1]:
+ attr = getattr(self, field)
+ if not attr: # None or empty listy / tuple
+ continue
+ if isinstance(attr, (list, tuple)):
+ return attr[-1]
+
+ return attr
+ return None
+
+ def parent_of(self, node):
+ """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: True if this node is the parent of the given node,
+ False otherwise.
+ :rtype: bool
+ """
+ parent = node.parent
+ while parent is not None:
+ if self is parent:
+ return True
+ parent = parent.parent
+ return False
+
+ def statement(self):
+ """The first parent node, including self, marked as statement node.
+
+ :returns: The first parent statement.
+ :rtype: NodeNG
+ """
+ if self.is_statement:
+ return self
+ return self.parent.statement()
+
+ def frame(self):
+ """The first parent frame node.
+
+ A frame node is a :class:`Module`, :class:`FunctionDef`,
+ or :class:`ClassDef`.
+
+ :returns: The first parent frame node.
+ :rtype: Module or FunctionDef or ClassDef
+ """
+ return self.parent.frame()
+
+ def scope(self):
+ """The first parent node defining a new scope.
+
+ :returns: The first parent scope node.
+ :rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr
+ """
+ if self.parent:
+ return self.parent.scope()
+ return None
+
+ def root(self):
+ """Return the root node of the syntax tree.
+
+ :returns: The root node.
+ :rtype: Module
+ """
+ if self.parent:
+ return self.parent.root()
+ return self
+
+ 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 exceptions.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 exceptions.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
+
+ @decorators.cachedproperty
+ def fromlineno(self):
+ """The first line that this node appears on in the source code.
+
+ :type: int or None
+ """
+ if self.lineno is None:
+ return self._fixed_source_line()
+
+ return self.lineno
+
+ @decorators.cachedproperty
+ def tolineno(self):
+ """The last line that this node appears on in the source code.
+
+ :type: int or None
+ """
+ if not self._astroid_fields:
+ # can't have children
+ lastchild = None
+ else:
+ lastchild = self.last_child()
+ if lastchild is None:
+ return self.fromlineno
+
+ return lastchild.tolineno
+
+ def _fixed_source_line(self):
+ """Attempt to find the line that this node appears on.
+
+ We need this method since not all nodes have :attr:`lineno` set.
+
+ :returns: The line number of this node,
+ or None if this could not be determined.
+ :rtype: int or None
+ """
+ line = self.lineno
+ _node = self
+ try:
+ while line is None:
+ _node = next(_node.get_children())
+ line = _node.lineno
+ except StopIteration:
+ _node = self.parent
+ while _node and line is None:
+ line = _node.lineno
+ _node = _node.parent
+ return line
+
+ def block_range(self, lineno):
+ """Get a range from the given line number to where this node ends.
+
+ :param lineno: The line number to start the range at.
+ :type lineno: int
+
+ :returns: The range of line numbers that this node belongs to,
+ starting at the given line number.
+ :rtype: tuple(int, int or None)
+ """
+ return lineno, self.tolineno
+
+ def set_local(self, name, stmt):
+ """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.
+ :type name: str
+
+ :param stmt: The statement that defines the given name.
+ :type stmt: NodeNG
+ """
+ self.parent.set_local(name, stmt)
+
+ def nodes_of_class(self, klass, skip_klass=None):
+ """Get the nodes (including this one or below) of the given types.
+
+ :param klass: The types of node to search for.
+ :type klass: builtins.type or tuple(builtins.type)
+
+ :param skip_klass: The types of node to ignore. This is useful to ignore
+ subclasses of :attr:`klass`.
+ :type skip_klass: builtins.type or tuple(builtins.type)
+
+ :returns: The node of the given types.
+ :rtype: iterable(NodeNG)
+ """
+ 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)
+
+ @decorators.cached
+ def _get_assign_nodes(self):
+ 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_lambdas(self):
+ yield from ()
+
+ def _infer_name(self, frame, name):
+ # overridden for ImportFrom, Import, Global, TryExcept and Arguments
+ pass
+
+ def _infer(self, context=None):
+ """we don't know how to resolve a statement by default"""
+ # this method is overridden by most concrete classes
+ raise exceptions.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):
+ """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):
+ """Whether this node defines something that is callable.
+
+ :returns: True if this defines something that is callable,
+ False otherwise.
+ :rtype: bool
+ """
+ return False
+
+ def eq(self, value):
+ return False
+
+ def as_string(self):
+ """Get the source code that this node represents.
+
+ :returns: The source code.
+ :rtype: str
+ """
+ return as_string.to_code(self)
+
+ def repr_tree(
+ self,
+ ids=False,
+ include_linenos=False,
+ ast_state=False,
+ indent=" ",
+ max_depth=0,
+ max_width=80,
+ ):
+ """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; 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; 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
+ + "<Recursion on %s with id=%s" % (type(node).__name__, id(node))
+ )
+ return False
+ done.add(node)
+
+ if max_depth and depth > max_depth:
+ result.append("...")
+ return False
+ depth += 1
+ cur_indent += indent
+ if ids:
+ result.append("%s<0x%x>(\n" % (type(node).__name__, id(node)))
+ else:
+ result.append("%s(" % 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("%s=" % 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]:
+ result.append("%s=" % field)
+ _repr_tree(getattr(node, field), result, done, cur_indent, depth)
+ result.append(",\n")
+ result.append(cur_indent)
+ result.append("%s=" % fields[-1])
+ _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, depth)
+ broken = True
+ result.append(")")
+ return broken
+
+ result = []
+ _repr_tree(self, result, set())
+ return "".join(result)
+
+ def bool_value(self):
+ """Determine the boolean value of this node.
+
+ The boolean value of a node can have three
+ possible values:
+
+ * False: For instance, empty data structures,
+ False, empty strings, instances which return
+ explicitly False from the __nonzero__ / __bool__
+ method.
+ * True: Most of constructs are True by default:
+ classes, functions, modules etc
+ * Uninferable: The inference engine is uncertain of the
+ node's value.
+
+ :returns: The boolean value of this node.
+ :rtype: bool or Uninferable
+ """
+ return util.Uninferable
+
+ def op_precedence(self):
+ # Look up by class name or default to highest precedence
+ return OP_PRECEDENCE.get(self.__class__.__name__, len(OP_PRECEDENCE))
+
+ def op_left_associative(self):
+ # Everything is left associative except `**` and IfExp
+ return True
+
+
+class Statement(NodeNG):
+ """Statement node adding a few attributes"""
+
+ is_statement = True
+ """Whether this node indicates a statement.
+
+ :type: bool
+ """
+
+ 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:
+ pass
+
+ 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 _BaseContainer(
+ mixins.ParentAssignTypeMixin, NodeNG, bases.Instance, metaclass=abc.ABCMeta
+):
+ """Base class for Set, FrozenSet, Tuple and List."""
+
+ _astroid_fields = ("elts",)
+
+ def __init__(self, lineno=None, col_offset=None, parent=None):
+ """
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.elts = []
+ """The elements in the node.
+
+ :type: list(NodeNG)
+ """
+
+ super(_BaseContainer, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, elts):
+ """Do some setup after initialisation.
+
+ :param elts: The list of elements the that node contains.
+ :type elts: list(NodeNG)
+ """
+ self.elts = elts
+
+ @classmethod
+ def from_elements(cls, elts=None):
+ """Create a node of this type from the given list of elements.
+
+ :param elts: The list of elements that the node should contain.
+ :type elts: list(NodeNG)
+
+ :returns: A new node containing the given elements.
+ :rtype: NodeNG
+ """
+ node = cls()
+ if elts is None:
+ node.elts = []
+ else:
+ 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):
+ """Determine the boolean value of this node.
+
+ :returns: The boolean value of this node.
+ :rtype: bool or Uninferable
+ """
+ return bool(self.elts)
+
+ @abc.abstractmethod
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+
+ def get_children(self):
+ yield from self.elts
+
+
+class LookupMixIn:
+ """Mixin to look up a name in the right scope."""
+
+ @lru_cache(maxsize=None)
+ def lookup(self, name):
+ """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.
+ :type name: str
+
+ :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).
+ :rtype: tuple(str, list(NodeNG))
+ """
+ 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 = contextmod.InferenceContext()
+ return bases._infer_stmts(stmts, context, frame)
+
+ def _get_filtered_node_statements(self, nodes):
+ statements = [(node, node.statement()) for node in 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, ExceptHandler) for _, stmt in statements
+ ):
+ statements = [
+ (node, stmt) for node, stmt in statements if stmt.parent_of(self)
+ ]
+ return statements
+
+ def _filter_stmts(self, stmts, frame, offset):
+ """Filter the given list of statements to remove ignorable statements.
+
+ 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 stmts: The statements to filter.
+ :type stmts: list(NodeNG)
+
+ :param frame: The frame that all of the given statements belong to.
+ :type frame: NodeNG
+
+ :param offset: The line offset to filter statements up to.
+ :type offset: int
+
+ :returns: The filtered statements.
+ :rtype: list(NodeNG)
+ """
+ # if offset == -1, my actual frame is not the inner frame but its parent
+ #
+ # class A(B): pass
+ #
+ # we need this to resolve B correctly
+ if offset == -1:
+ myframe = self.frame().parent.frame()
+ else:
+ myframe = self.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 self.statement() is myframe and myframe.parent:
+ myframe = myframe.parent.frame()
+ mystmt = self.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.fromlineno is not None:
+ assert mystmt.fromlineno is not None, mystmt
+ mylineno = mystmt.fromlineno + offset
+ else:
+ # disabling lineno filtering
+ mylineno = 0
+
+ _stmts = []
+ _stmt_parents = []
+ statements = self._get_filtered_node_statements(stmts)
+
+ for node, stmt in statements:
+ # line filtering is on and we have reached our location, break
+ if 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(self):
+ continue
+ assert hasattr(node, "assign_type"), (
+ node,
+ node.scope(),
+ node.scope().locals,
+ )
+ assign_type = node.assign_type()
+ if node.has_base(self):
+ break
+
+ _stmts, done = assign_type._get_filtered_stmts(self, node, _stmts, mystmt)
+ if done:
+ break
+
+ optional_assign = assign_type.optional_assign
+ if optional_assign and assign_type.parent_of(self):
+ # we are inside a loop, loop var assignment is hiding previous
+ # assignment
+ _stmts = [node]
+ _stmt_parents = [stmt.parent]
+ continue
+
+ if isinstance(assign_type, NamedExpr):
+ _stmts = [node]
+ continue
+
+ # 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 are_exclusive(_stmts[pindex], node)):
+ if (
+ # In case of partial function node, if the statement is different
+ # from the origin function then it can be deleted otherwise it should
+ # remain to be able to correctly infer the call to origin function.
+ not node.is_function
+ or node.qname() != "PartialFunction"
+ or node.name != _stmts[pindex].name
+ ):
+ del _stmt_parents[pindex]
+ del _stmts[pindex]
+ if isinstance(node, AssignName):
+ if not optional_assign and stmt.parent is mystmt.parent:
+ _stmts = []
+ _stmt_parents = []
+ elif isinstance(node, DelName):
+ _stmts = []
+ _stmt_parents = []
+ continue
+ if not are_exclusive(self, node):
+ _stmts.append(node)
+ _stmt_parents.append(stmt.parent)
+ return _stmts
+
+
+# Name classes
+
+
+class AssignName(
+ mixins.NoChildrenMixin, LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG
+):
+ """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.
+
+ >>> node = astroid.extract_node('variable = range(10)')
+ >>> node
+ <Assign l.1 at 0x7effe1db8550>
+ >>> list(node.get_children())
+ [<AssignName.variable l.1 at 0x7effe1db8748>, <Call l.1 at 0x7effe1db8630>]
+ >>> list(node.get_children())[0].as_string()
+ 'variable'
+ """
+
+ _other_fields = ("name",)
+
+ def __init__(self, name=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param name: The name that is assigned to.
+ :type name: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.name = name
+ """The name that is assigned to.
+
+ :type: str or None
+ """
+
+ super(AssignName, self).__init__(lineno, col_offset, parent)
+
+
+class DelName(
+ mixins.NoChildrenMixin, LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG
+):
+ """Variation of :class:`ast.Delete` representing deletion of a name.
+
+ A :class:`DelName` is the name of something that is deleted.
+
+ >>> node = astroid.extract_node("del variable #@")
+ >>> list(node.get_children())
+ [<DelName.variable l.1 at 0x7effe1da4d30>]
+ >>> list(node.get_children())[0].as_string()
+ 'variable'
+ """
+
+ _other_fields = ("name",)
+
+ def __init__(self, name=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param name: The name that is being deleted.
+ :type name: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.name = name
+ """The name that is being deleted.
+
+ :type: str or None
+ """
+
+ super(DelName, self).__init__(lineno, col_offset, parent)
+
+
+class Name(mixins.NoChildrenMixin, LookupMixIn, NodeNG):
+ """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`.
+
+ >>> node = astroid.extract_node('range(10)')
+ >>> node
+ <Call l.1 at 0x7effe1db8710>
+ >>> list(node.get_children())
+ [<Name.range l.1 at 0x7effe1db86a0>, <Const.int l.1 at 0x7effe1db8518>]
+ >>> list(node.get_children())[0].as_string()
+ 'range'
+ """
+
+ _other_fields = ("name",)
+
+ def __init__(self, name=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param name: The name that this node refers to.
+ :type name: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.name = name
+ """The name that this node refers to.
+
+ :type: str or None
+ """
+
+ super(Name, self).__init__(lineno, col_offset, parent)
+
+ def _get_name_nodes(self):
+ yield self
+
+ for child_node in self.get_children():
+ yield from child_node._get_name_nodes()
+
+
+class Arguments(mixins.AssignTypeMixin, NodeNG):
+ """Class representing an :class:`ast.arguments` node.
+
+ An :class:`Arguments` node represents that arguments in a
+ function definition.
+
+ >>> node = astroid.extract_node('def foo(bar): pass')
+ >>> node
+ <FunctionDef.foo l.1 at 0x7effe1db8198>
+ >>> node.args
+ <Arguments l.1 at 0x7effe1db82e8>
+ """
+
+ # Python 3.4+ uses a different approach regarding annotations,
+ # each argument is a new class, _ast.arg, which exposes an
+ # 'annotation' attribute. In astroid though, arguments are exposed
+ # as is in the Arguments node and the only way to expose annotations
+ # is by using something similar with Python 3.3:
+ # - we expose 'varargannotation' and 'kwargannotation' of annotations
+ # of varargs and kwargs.
+ # - we expose 'annotation', a list with annotations for
+ # for each normal argument. If an argument doesn't have an
+ # annotation, its value will be None.
+
+ _astroid_fields = (
+ "args",
+ "defaults",
+ "kwonlyargs",
+ "posonlyargs",
+ "kw_defaults",
+ "annotations",
+ "varargannotation",
+ "kwargannotation",
+ "kwonlyargs_annotations",
+ "type_comment_args",
+ )
+ varargannotation = None
+ """The type annotation for the variable length arguments.
+
+ :type: NodeNG
+ """
+ kwargannotation = None
+ """The type annotation for the variable length keyword arguments.
+
+ :type: NodeNG
+ """
+
+ _other_fields = ("vararg", "kwarg")
+
+ def __init__(self, vararg=None, kwarg=None, parent=None):
+ """
+ :param vararg: The name of the variable length arguments.
+ :type vararg: str or None
+
+ :param kwarg: The name of the variable length keyword arguments.
+ :type kwarg: str or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ super(Arguments, self).__init__(parent=parent)
+ self.vararg = vararg
+ """The name of the variable length arguments.
+
+ :type: str or None
+ """
+
+ self.kwarg = kwarg
+ """The name of the variable length keyword arguments.
+
+ :type: str or None
+ """
+
+ self.args = []
+ """The names of the required arguments.
+
+ :type: list(AssignName)
+ """
+
+ self.defaults = []
+ """The default values for arguments that can be passed positionally.
+
+ :type: list(NodeNG)
+ """
+
+ self.kwonlyargs = []
+ """The keyword arguments that cannot be passed positionally.
+
+ :type: list(AssignName)
+ """
+
+ self.posonlyargs = []
+ """The arguments that can only be passed positionally.
+
+ :type: list(AssignName)
+ """
+
+ self.kw_defaults = []
+ """The default values for keyword arguments that cannot be passed positionally.
+
+ :type: list(NodeNG)
+ """
+
+ self.annotations = []
+ """The type annotations of arguments that can be passed positionally.
+
+ :type: list(NodeNG)
+ """
+
+ self.posonlyargs_annotations = []
+ """The type annotations of arguments that can only be passed positionally.
+
+ :type: list(NodeNG)
+ """
+
+ self.kwonlyargs_annotations = []
+ """The type annotations of arguments that cannot be passed positionally.
+
+ :type: list(NodeNG)
+ """
+
+ self.type_comment_args = []
+ """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: list(NodeNG or None)
+ """
+
+ # pylint: disable=too-many-arguments
+ def postinit(
+ self,
+ args,
+ defaults,
+ kwonlyargs,
+ kw_defaults,
+ annotations,
+ posonlyargs=None,
+ kwonlyargs_annotations=None,
+ posonlyargs_annotations=None,
+ varargannotation=None,
+ kwargannotation=None,
+ type_comment_args=None,
+ ):
+ """Do some setup after initialisation.
+
+ :param args: The names of the required arguments.
+ :type args: list(AssignName)
+
+ :param defaults: The default values for arguments that can be passed
+ positionally.
+ :type defaults: list(NodeNG)
+
+ :param kwonlyargs: The keyword arguments that cannot be passed
+ positionally.
+ :type kwonlyargs: list(AssignName)
+
+ :param posonlyargs: The arguments that can only be passed
+ positionally.
+ :type kwonlyargs: list(AssignName)
+
+ :param kw_defaults: The default values for keyword arguments that
+ cannot be passed positionally.
+ :type kw_defaults: list(NodeNG)
+
+ :param annotations: The type annotations of arguments that can be
+ passed positionally.
+ :type annotations: list(NodeNG)
+
+ :param kwonlyargs_annotations: The type annotations of arguments that
+ cannot be passed positionally. This should always be passed in
+ Python 3.
+ :type kwonlyargs_annotations: list(NodeNG)
+
+ :param posonlyargs_annotations: The type annotations of arguments that
+ can only be passed positionally. This should always be passed in
+ Python 3.
+ :type posonlyargs_annotations: list(NodeNG)
+
+ :param varargannotation: The type annotation for the variable length
+ arguments.
+ :type varargannotation: NodeNG
+
+ :param kwargannotation: The type annotation for the variable length
+ keyword arguments.
+ :type kwargannotation: NodeNG
+
+ :param type_comment_args: The type annotation,
+ passed by a type comment, of each argument.
+ :type type_comment_args: list(NodeNG or 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
+ self.varargannotation = varargannotation
+ self.kwargannotation = kwargannotation
+ self.type_comment_args = type_comment_args
+
+ # pylint: disable=too-many-arguments
+
+ def _infer_name(self, frame, name):
+ if self.parent is frame:
+ return name
+ return None
+
+ @decorators.cachedproperty
+ def fromlineno(self):
+ """The first line that this node appears on in the source code.
+
+ :type: int or None
+ """
+ lineno = super(Arguments, self).fromlineno
+ return max(lineno, self.parent.fromlineno or 0)
+
+ def format_args(self):
+ """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))
+ result.append("/")
+ if self.args:
+ result.append(
+ _format_args(
+ self.args,
+ positional_or_keyword_defaults,
+ getattr(self, "annotations", None),
+ )
+ )
+ if self.vararg:
+ result.append("*%s" % self.vararg)
+ if self.kwonlyargs:
+ if not self.vararg:
+ result.append("*")
+ result.append(
+ _format_args(
+ self.kwonlyargs, self.kw_defaults, self.kwonlyargs_annotations
+ )
+ )
+ if self.kwarg:
+ result.append("**%s" % self.kwarg)
+ return ", ".join(result)
+
+ 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 = list(itertools.chain((self.posonlyargs or ()), self.args or ()))
+ index = _find_arg(argname, args)[0]
+ if index is not None:
+ idx = index - (len(args) - len(self.defaults))
+ if idx >= 0:
+ return self.defaults[idx]
+ index = _find_arg(argname, self.kwonlyargs)[0]
+ if index is not None and self.kw_defaults[index] is not None:
+ return self.kw_defaults[index]
+ raise exceptions.NoDefault(func=self.parent, name=argname)
+
+ def is_argument(self, name):
+ """Check if the given name is defined in the arguments.
+
+ :param name: The name to check for.
+ :type name: str
+
+ :returns: True if the given name is defined in the arguments,
+ False otherwise.
+ :rtype: bool
+ """
+ if name == self.vararg:
+ return True
+ if name == self.kwarg:
+ return True
+ return (
+ self.find_argname(name, rec=True)[1] is not None
+ or self.kwonlyargs
+ and _find_arg(name, self.kwonlyargs, rec=True)[1] is not None
+ )
+
+ def find_argname(self, argname, rec=False):
+ """Get the index and :class:`AssignName` node for given name.
+
+ :param argname: The name of the argument to search for.
+ :type argname: str
+
+ :param rec: Whether or not to include arguments in unpacked tuples
+ in the search.
+ :type rec: bool
+
+ :returns: The index and node for the argument.
+ :rtype: tuple(str or None, AssignName or None)
+ """
+ if (
+ self.args or self.posonlyargs
+ ): # self.args may be None in some cases (builtin function)
+ arguments = itertools.chain(self.posonlyargs or (), self.args or ())
+ return _find_arg(argname, arguments, rec)
+ return None, None
+
+ def get_children(self):
+ yield from self.posonlyargs or ()
+ yield from self.args or ()
+
+ yield from self.defaults
+ yield from self.kwonlyargs
+
+ for elt in self.kw_defaults:
+ 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
+
+
+def _find_arg(argname, args, rec=False):
+ for i, arg in enumerate(args):
+ if isinstance(arg, Tuple):
+ if rec:
+ found = _find_arg(argname, arg.elts)
+ if found[0] is not None:
+ return found
+ elif arg.name == argname:
+ return i, arg
+ return None, None
+
+
+def _format_args(args, defaults=None, annotations=None):
+ values = []
+ if args is None:
+ return ""
+ if annotations is None:
+ annotations = []
+ if defaults is not None:
+ default_offset = len(args) - len(defaults)
+ packed = itertools.zip_longest(args, annotations)
+ for i, (arg, annotation) in enumerate(packed):
+ if isinstance(arg, Tuple):
+ values.append("(%s)" % _format_args(arg.elts))
+ else:
+ argname = arg.name
+ default_sep = "="
+ if annotation is not None:
+ argname += ": " + annotation.as_string()
+ default_sep = " = "
+ values.append(argname)
+
+ if defaults is not None and i >= default_offset:
+ if defaults[i - default_offset] is not None:
+ values[-1] += default_sep + defaults[i - default_offset].as_string()
+ return ", ".join(values)
+
+
+class AssignAttr(mixins.ParentAssignTypeMixin, NodeNG):
+ """Variation of :class:`ast.Assign` representing assignment to an attribute.
+
+ >>> node = astroid.extract_node('self.attribute = range(10)')
+ >>> node
+ <Assign l.1 at 0x7effe1d521d0>
+ >>> list(node.get_children())
+ [<AssignAttr.attribute l.1 at 0x7effe1d52320>, <Call l.1 at 0x7effe1d522e8>]
+ >>> list(node.get_children())[0].as_string()
+ 'self.attribute'
+ """
+
+ _astroid_fields = ("expr",)
+ _other_fields = ("attrname",)
+ expr = None
+ """What has the attribute that is being assigned to.
+
+ :type: NodeNG or None
+ """
+
+ def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param attrname: The name of the attribute being assigned to.
+ :type attrname: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.attrname = attrname
+ """The name of the attribute being assigned to.
+
+ :type: str or None
+ """
+
+ super(AssignAttr, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, expr=None):
+ """Do some setup after initialisation.
+
+ :param expr: What has the attribute that is being assigned to.
+ :type expr: NodeNG or None
+ """
+ self.expr = expr
+
+ def get_children(self):
+ yield self.expr
+
+
+class Assert(Statement):
+ """Class representing an :class:`ast.Assert` node.
+
+ An :class:`Assert` node represents an assert statement.
+
+ >>> node = astroid.extract_node('assert len(things) == 10, "Not enough things"')
+ >>> node
+ <Assert l.1 at 0x7effe1d527b8>
+ """
+
+ _astroid_fields = ("test", "fail")
+ test = None
+ """The test that passes or fails the assertion.
+
+ :type: NodeNG or None
+ """
+ fail = None
+ """The message shown when the assertion fails.
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, test=None, fail=None):
+ """Do some setup after initialisation.
+
+ :param test: The test that passes or fails the assertion.
+ :type test: NodeNG or None
+
+ :param fail: The message shown when the assertion fails.
+ :type fail: NodeNG or 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(mixins.AssignTypeMixin, Statement):
+ """Class representing an :class:`ast.Assign` node.
+
+ An :class:`Assign` is a statement where something is explicitly
+ asssigned to.
+
+ >>> node = astroid.extract_node('variable = range(10)')
+ >>> node
+ <Assign l.1 at 0x7effe1db8550>
+ """
+
+ _astroid_fields = ("targets", "value")
+ _other_other_fields = ("type_annotation",)
+ targets = None
+ """What is being assigned to.
+
+ :type: list(NodeNG) or None
+ """
+ value = None
+ """The value being assigned to the variables.
+
+ :type: NodeNG or None
+ """
+ type_annotation = None
+ """If present, this will contain the type annotation passed by a type comment
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, targets=None, value=None, type_annotation=None):
+ """Do some setup after initialisation.
+
+ :param targets: What is being assigned to.
+ :type targets: list(NodeNG) or None
+
+ :param value: The value being assigned to the variables.
+ :type: NodeNG or None
+ """
+ self.targets = targets
+ self.value = value
+ self.type_annotation = type_annotation
+
+ def get_children(self):
+ yield from self.targets
+
+ yield self.value
+
+ @decorators.cached
+ def _get_assign_nodes(self):
+ return [self] + list(self.value._get_assign_nodes())
+
+ def _get_yield_nodes_skip_lambdas(self):
+ yield from self.value._get_yield_nodes_skip_lambdas()
+
+
+class AnnAssign(mixins.AssignTypeMixin, Statement):
+ """Class representing an :class:`ast.AnnAssign` node.
+
+ An :class:`AnnAssign` is an assignment with a type annotation.
+
+ >>> node = astroid.extract_node('variable: List[int] = range(10)')
+ >>> node
+ <AnnAssign l.1 at 0x7effe1d4c630>
+ """
+
+ _astroid_fields = ("target", "annotation", "value")
+ _other_fields = ("simple",)
+ target = None
+ """What is being assigned to.
+
+ :type: NodeNG or None
+ """
+ annotation = None
+ """The type annotation of what is being assigned to.
+
+ :type: NodeNG
+ """
+ value = None
+ """The value being assigned to the variables.
+
+ :type: NodeNG or None
+ """
+ simple = None
+ """Whether :attr:`target` is a pure name or a complex statement.
+
+ :type: int
+ """
+
+ def postinit(self, target, annotation, simple, value=None):
+ """Do some setup after initialisation.
+
+ :param target: What is being assigned to.
+ :type target: NodeNG
+
+ :param annotation: The type annotation of what is being assigned to.
+ :type: NodeNG
+
+ :param simple: Whether :attr:`target` is a pure name
+ or a complex statement.
+ :type simple: int
+
+ :param value: The value being assigned to the variables.
+ :type: NodeNG or None
+ """
+ self.target = target
+ self.annotation = annotation
+ self.value = value
+ self.simple = simple
+
+ def get_children(self):
+ yield self.target
+ yield self.annotation
+
+ if self.value is not None:
+ yield self.value
+
+
+class AugAssign(mixins.AssignTypeMixin, Statement):
+ """Class representing an :class:`ast.AugAssign` node.
+
+ An :class:`AugAssign` is an assignment paired with an operator.
+
+ >>> node = astroid.extract_node('variable += 1')
+ >>> node
+ <AugAssign l.1 at 0x7effe1db4d68>
+ """
+
+ _astroid_fields = ("target", "value")
+ _other_fields = ("op",)
+ target = None
+ """What is being assigned to.
+
+ :type: NodeNG or None
+ """
+ value = None
+ """The value being assigned to the variable.
+
+ :type: NodeNG or None
+ """
+
+ def __init__(self, op=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param op: The operator that is being combined with the assignment.
+ This includes the equals sign.
+ :type op: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.op = op
+ """The operator that is being combined with the assignment.
+
+ This includes the equals sign.
+
+ :type: str or None
+ """
+
+ super(AugAssign, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, target=None, value=None):
+ """Do some setup after initialisation.
+
+ :param target: What is being assigned to.
+ :type target: NodeNG or None
+
+ :param value: The value being assigned to the variable.
+ :type: NodeNG or None
+ """
+ self.target = target
+ self.value = value
+
+ # This is set by inference.py
+ def _infer_augassign(self, context=None):
+ raise NotImplementedError
+
+ def type_errors(self, context=None):
+ """Get a list of type errors which can occur during inference.
+
+ Each TypeError is represented by a :class:`BadBinaryOperationMessage` ,
+ which holds the original exception.
+
+ :returns: The list of possible type errors.
+ :rtype: list(BadBinaryOperationMessage)
+ """
+ try:
+ results = self._infer_augassign(context=context)
+ return [
+ result
+ for result in results
+ if isinstance(result, util.BadBinaryOperationMessage)
+ ]
+ except exceptions.InferenceError:
+ return []
+
+ def get_children(self):
+ yield self.target
+ yield self.value
+
+
+class Repr(NodeNG):
+ """Class representing an :class:`ast.Repr` node.
+
+ A :class:`Repr` node represents the backtick syntax,
+ which is a deprecated alias for :func:`repr` removed in Python 3.
+
+ >>> node = astroid.extract_node('`variable`')
+ >>> node
+ <Repr l.1 at 0x7fa0951d75d0>
+ """
+
+ _astroid_fields = ("value",)
+ value = None
+ """What is having :func:`repr` called on it.
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, value=None):
+ """Do some setup after initialisation.
+
+ :param value: What is having :func:`repr` called on it.
+ :type value: NodeNG or None
+ """
+ self.value = value
+
+
+class BinOp(NodeNG):
+ """Class representing an :class:`ast.BinOp` node.
+
+ A :class:`BinOp` node is an application of a binary operator.
+
+ >>> node = astroid.extract_node('a + b')
+ >>> node
+ <BinOp l.1 at 0x7f23b2e8cfd0>
+ """
+
+ _astroid_fields = ("left", "right")
+ _other_fields = ("op",)
+ left = None
+ """What is being applied to the operator on the left side.
+
+ :type: NodeNG or None
+ """
+ right = None
+ """What is being applied to the operator on the right side.
+
+ :type: NodeNG or None
+ """
+
+ def __init__(self, op=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param op: The operator.
+ :type: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.op = op
+ """The operator.
+
+ :type: str or None
+ """
+
+ super(BinOp, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, left=None, right=None):
+ """Do some setup after initialisation.
+
+ :param left: What is being applied to the operator on the left side.
+ :type left: NodeNG or None
+
+ :param right: What is being applied to the operator on the right side.
+ :type right: NodeNG or None
+ """
+ self.left = left
+ self.right = right
+
+ # This is set by inference.py
+ def _infer_binop(self, context=None):
+ raise NotImplementedError
+
+ def type_errors(self, context=None):
+ """Get a list of type errors which can occur during inference.
+
+ Each TypeError is represented by a :class:`BadBinaryOperationMessage`,
+ which holds the original exception.
+
+ :returns: The list of possible type errors.
+ :rtype: list(BadBinaryOperationMessage)
+ """
+ try:
+ results = self._infer_binop(context=context)
+ return [
+ result
+ for result in results
+ if isinstance(result, util.BadBinaryOperationMessage)
+ ]
+ except exceptions.InferenceError:
+ return []
+
+ def get_children(self):
+ yield self.left
+ yield self.right
+
+ def op_precedence(self):
+ return OP_PRECEDENCE[self.op]
+
+ def op_left_associative(self):
+ # 2**3**4 == 2**(3**4)
+ return self.op != "**"
+
+
+class BoolOp(NodeNG):
+ """Class representing an :class:`ast.BoolOp` node.
+
+ A :class:`BoolOp` is an application of a boolean operator.
+
+ >>> node = astroid.extract_node('a and b')
+ >>> node
+ <BinOp l.1 at 0x7f23b2e71c50>
+ """
+
+ _astroid_fields = ("values",)
+ _other_fields = ("op",)
+ values = None
+ """The values being applied to the operator.
+
+ :type: list(NodeNG) or None
+ """
+
+ def __init__(self, op=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param op: The operator.
+ :type: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.op = op
+ """The operator.
+
+ :type: str or None
+ """
+
+ super(BoolOp, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, values=None):
+ """Do some setup after initialisation.
+
+ :param values: The values being applied to the operator.
+ :type values: list(NodeNG) or None
+ """
+ self.values = values
+
+ def get_children(self):
+ yield from self.values
+
+ def op_precedence(self):
+ return OP_PRECEDENCE[self.op]
+
+
+class Break(mixins.NoChildrenMixin, Statement):
+ """Class representing an :class:`ast.Break` node.
+
+ >>> node = astroid.extract_node('break')
+ >>> node
+ <Break l.1 at 0x7f23b2e9e5c0>
+ """
+
+
+class Call(NodeNG):
+ """Class representing an :class:`ast.Call` node.
+
+ A :class:`Call` node is a call to a function, method, etc.
+
+ >>> node = astroid.extract_node('function()')
+ >>> node
+ <Call l.1 at 0x7f23b2e71eb8>
+ """
+
+ _astroid_fields = ("func", "args", "keywords")
+ func = None
+ """What is being called.
+
+ :type: NodeNG or None
+ """
+ args = None
+ """The positional arguments being given to the call.
+
+ :type: list(NodeNG) or None
+ """
+ keywords = None
+ """The keyword arguments being given to the call.
+
+ :type: list(NodeNG) or None
+ """
+
+ def postinit(self, func=None, args=None, keywords=None):
+ """Do some setup after initialisation.
+
+ :param func: What is being called.
+ :type func: NodeNG or None
+
+ :param args: The positional arguments being given to the call.
+ :type args: list(NodeNG) or None
+
+ :param keywords: The keyword arguments being given to the call.
+ :type keywords: list(NodeNG) or None
+ """
+ self.func = func
+ self.args = args
+ self.keywords = keywords
+
+ @property
+ def starargs(self):
+ """The positional arguments that unpack something.
+
+ :type: list(Starred)
+ """
+ args = self.args or []
+ return [arg for arg in args if isinstance(arg, Starred)]
+
+ @property
+ def kwargs(self):
+ """The keyword arguments that unpack something.
+
+ :type: list(Keyword)
+ """
+ keywords = self.keywords or []
+ return [keyword for keyword in keywords if keyword.arg is None]
+
+ def get_children(self):
+ yield self.func
+
+ yield from self.args
+
+ yield from self.keywords or ()
+
+
+class Compare(NodeNG):
+ """Class representing an :class:`ast.Compare` node.
+
+ A :class:`Compare` node indicates a comparison.
+
+ >>> node = astroid.extract_node('a <= b <= c')
+ >>> node
+ <Compare l.1 at 0x7f23b2e9e6d8>
+ >>> node.ops
+ [('<=', <Name.b l.1 at 0x7f23b2e9e2b0>), ('<=', <Name.c l.1 at 0x7f23b2e9e390>)]
+ """
+
+ _astroid_fields = ("left", "ops")
+ left = None
+ """The value at the left being applied to a comparison operator.
+
+ :type: NodeNG or None
+ """
+ ops = None
+ """The remainder of the operators and their relevant right hand value.
+
+ :type: list(tuple(str, NodeNG)) or None
+ """
+
+ def postinit(self, left=None, ops=None):
+ """Do some setup after initialisation.
+
+ :param left: The value at the left being applied to a comparison
+ operator.
+ :type left: NodeNG or None
+
+ :param ops: The remainder of the operators
+ and their relevant right hand value.
+ :type ops: list(tuple(str, NodeNG)) or 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
+
+
+class Comprehension(NodeNG):
+ """Class representing an :class:`ast.comprehension` node.
+
+ A :class:`Comprehension` indicates the loop inside any type of
+ comprehension including generator expressions.
+
+ >>> node = astroid.extract_node('[x for x in some_values]')
+ >>> list(node.get_children())
+ [<Name.x l.1 at 0x7f23b2e352b0>, <Comprehension l.1 at 0x7f23b2e35320>]
+ >>> list(node.get_children())[1].as_string()
+ 'for x in some_values'
+ """
+
+ _astroid_fields = ("target", "iter", "ifs")
+ _other_fields = ("is_async",)
+ target = None
+ """What is assigned to by the comprehension.
+
+ :type: NodeNG or None
+ """
+ iter = None
+ """What is iterated over by the comprehension.
+
+ :type: NodeNG or None
+ """
+ ifs = None
+ """The contents of any if statements that filter the comprehension.
+
+ :type: list(NodeNG) or None
+ """
+ is_async = None
+ """Whether this is an asynchronous comprehension or not.
+
+ :type: bool or None
+ """
+
+ def __init__(self, parent=None):
+ """
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ super(Comprehension, self).__init__()
+ self.parent = parent
+
+ # pylint: disable=redefined-builtin; same name as builtin ast module.
+ def postinit(self, target=None, iter=None, ifs=None, is_async=None):
+ """Do some setup after initialisation.
+
+ :param target: What is assigned to by the comprehension.
+ :type target: NodeNG or None
+
+ :param iter: What is iterated over by the comprehension.
+ :type iter: NodeNG or None
+
+ :param ifs: The contents of any if statements that filter
+ the comprehension.
+ :type ifs: list(NodeNG) or None
+
+ :param is_async: Whether this is an asynchronous comprehension or not.
+ :type: bool or None
+ """
+ self.target = target
+ self.iter = iter
+ self.ifs = ifs
+ self.is_async = is_async
+
+ optional_assign = True
+ """Whether this node optionally assigns a variable.
+
+ :type: bool
+ """
+
+ 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):
+ """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(mixins.NoChildrenMixin, NodeNG, bases.Instance):
+ """Class representing any constant including num, str, bool, None, bytes.
+
+ >>> node = astroid.extract_node('(5, "This is a string.", True, None, b"bytes")')
+ >>> node
+ <Tuple.tuple l.1 at 0x7f23b2e358d0>
+ >>> list(node.get_children())
+ [<Const.int l.1 at 0x7f23b2e35940>,
+ <Const.str l.1 at 0x7f23b2e35978>,
+ <Const.bool l.1 at 0x7f23b2e359b0>,
+ <Const.NoneType l.1 at 0x7f23b2e359e8>,
+ <Const.bytes l.1 at 0x7f23b2e35a20>]
+ """
+
+ _other_fields = ("value",)
+
+ def __init__(self, value, lineno=None, col_offset=None, parent=None):
+ """
+ :param value: The value that the constant represents.
+ :type value: object
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.value = value
+ """The value that the constant represents.
+
+ :type: object
+ """
+
+ super(Const, self).__init__(lineno, col_offset, parent)
+
+ 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=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 exceptions.AstroidTypeError(
+ "Could not use type {} as subscript index".format(type(index))
+ )
+
+ try:
+ if isinstance(self.value, (str, bytes)):
+ return Const(self.value[index_value])
+ except IndexError as exc:
+ raise exceptions.AstroidIndexError(
+ message="Index {index!r} out of range",
+ node=self,
+ index=index,
+ context=context,
+ ) from exc
+ except TypeError as exc:
+ raise exceptions.AstroidTypeError(
+ message="Type error {error!r}", node=self, index=index, context=context
+ ) from exc
+
+ raise exceptions.AstroidTypeError("%r (value=%s)" % (self, self.value))
+
+ def has_dynamic_getattr(self):
+ """Check if the node has a custom __getattr__ or __getattribute__.
+
+ :returns: True if the class has a custom
+ __getattr__ or __getattribute__, False otherwise.
+ For a :class:`Const` this is always ``False``.
+ :rtype: bool
+ """
+ return False
+
+ def itered(self):
+ """An iterator over the elements this node contains.
+
+ :returns: The contents of this node.
+ :rtype: iterable(str)
+
+ :raises TypeError: If this node does not represent something that is iterable.
+ """
+ if isinstance(self.value, str):
+ return self.value
+ raise TypeError()
+
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+ return self._proxied.qname()
+
+ def bool_value(self):
+ """Determine the boolean value of this node.
+
+ :returns: The boolean value of this node.
+ :rtype: bool
+ """
+ return bool(self.value)
+
+
+class Continue(mixins.NoChildrenMixin, Statement):
+ """Class representing an :class:`ast.Continue` node.
+
+ >>> node = astroid.extract_node('continue')
+ >>> node
+ <Continue l.1 at 0x7f23b2e35588>
+ """
+
+
+class Decorators(NodeNG):
+ """A node representing a list of decorators.
+
+ A :class:`Decorators` is the decorators that are applied to
+ a method or function.
+
+ >>> node = astroid.extract_node('''
+ @property
+ def my_property(self):
+ return 3
+ ''')
+ >>> node
+ <FunctionDef.my_property l.2 at 0x7f23b2e35d30>
+ >>> list(node.get_children())[0]
+ <Decorators l.1 at 0x7f23b2e35d68>
+ """
+
+ _astroid_fields = ("nodes",)
+ nodes = None
+ """The decorators that this node contains.
+
+ :type: list(Name or Call) or None
+ """
+
+ def postinit(self, nodes):
+ """Do some setup after initialisation.
+
+ :param nodes: The decorators that this node contains.
+ :type nodes: list(Name or Call)
+ """
+ self.nodes = nodes
+
+ def scope(self):
+ """The first parent node defining a new scope.
+
+ :returns: The first parent scope node.
+ :rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr
+ """
+ # skip the function node to go directly to the upper level scope
+ return self.parent.parent.scope()
+
+ def get_children(self):
+ yield from self.nodes
+
+
+class DelAttr(mixins.ParentAssignTypeMixin, NodeNG):
+ """Variation of :class:`ast.Delete` representing deletion of an attribute.
+
+ >>> node = astroid.extract_node('del self.attr')
+ >>> node
+ <Delete l.1 at 0x7f23b2e35f60>
+ >>> list(node.get_children())[0]
+ <DelAttr.attr l.1 at 0x7f23b2e411d0>
+ """
+
+ _astroid_fields = ("expr",)
+ _other_fields = ("attrname",)
+ expr = None
+ """The name that this node represents.
+
+ :type: Name or None
+ """
+
+ def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param attrname: The name of the attribute that is being deleted.
+ :type attrname: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.attrname = attrname
+ """The name of the attribute that is being deleted.
+
+ :type: str or None
+ """
+
+ super(DelAttr, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, expr=None):
+ """Do some setup after initialisation.
+
+ :param expr: The name that this node represents.
+ :type expr: Name or None
+ """
+ self.expr = expr
+
+ def get_children(self):
+ yield self.expr
+
+
+class Delete(mixins.AssignTypeMixin, Statement):
+ """Class representing an :class:`ast.Delete` node.
+
+ A :class:`Delete` is a ``del`` statement this is deleting something.
+
+ >>> node = astroid.extract_node('del self.attr')
+ >>> node
+ <Delete l.1 at 0x7f23b2e35f60>
+ """
+
+ _astroid_fields = ("targets",)
+ targets = None
+ """What is being deleted.
+
+ :type: list(NodeNG) or None
+ """
+
+ def postinit(self, targets=None):
+ """Do some setup after initialisation.
+
+ :param targets: What is being deleted.
+ :type targets: list(NodeNG) or None
+ """
+ self.targets = targets
+
+ def get_children(self):
+ yield from self.targets
+
+
+class Dict(NodeNG, bases.Instance):
+ """Class representing an :class:`ast.Dict` node.
+
+ A :class:`Dict` is a dictionary that is created with ``{}`` syntax.
+
+ >>> node = astroid.extract_node('{1: "1"}')
+ >>> node
+ <Dict.dict l.1 at 0x7f23b2e35cc0>
+ """
+
+ _astroid_fields = ("items",)
+
+ def __init__(self, lineno=None, col_offset=None, parent=None):
+ """
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.items = []
+ """The key-value pairs contained in the dictionary.
+
+ :type: list(tuple(NodeNG, NodeNG))
+ """
+
+ super(Dict, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, items):
+ """Do some setup after initialisation.
+
+ :param items: The key-value pairs contained in the dictionary.
+ :type items: list(tuple(NodeNG, NodeNG))
+ """
+ self.items = items
+
+ @classmethod
+ def from_elements(cls, items=None):
+ """Create a :class:`Dict` of constants from a live dictionary.
+
+ :param items: The items to store in the node.
+ :type items: dict
+
+ :returns: The created dictionary node.
+ :rtype: Dict
+ """
+ node = cls()
+ if items is None:
+ node.items = []
+ else:
+ node.items = [
+ (const_factory(k), const_factory(v) if _is_const(v) else v)
+ for k, v in items.items()
+ # The keys need to be constants
+ if _is_const(k)
+ ]
+ return node
+
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+ return "%s.dict" % BUILTINS
+
+ 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, context=None):
+ """Get an item from this node.
+
+ :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.
+ :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):
+ try:
+ return value.getitem(index, context)
+ except (exceptions.AstroidTypeError, exceptions.AstroidIndexError):
+ continue
+ for inferredkey in key.infer(context):
+ if inferredkey is util.Uninferable:
+ continue
+ if isinstance(inferredkey, Const) and isinstance(index, Const):
+ if inferredkey.value == index.value:
+ return value
+
+ raise exceptions.AstroidIndexError(index)
+
+ def bool_value(self):
+ """Determine the boolean value of this node.
+
+ :returns: The boolean value of this node.
+ :rtype: bool
+ """
+ return bool(self.items)
+
+
+class Expr(Statement):
+ """Class representing an :class:`ast.Expr` node.
+
+ An :class:`Expr` is any expression that does not have its value used or
+ stored.
+
+ >>> node = astroid.extract_node('method()')
+ >>> node
+ <Call l.1 at 0x7f23b2e352b0>
+ >>> node.parent
+ <Expr l.1 at 0x7f23b2e35278>
+ """
+
+ _astroid_fields = ("value",)
+ value = None
+ """What the expression does.
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, value=None):
+ """Do some setup after initialisation.
+
+ :param value: What the expression does.
+ :type value: NodeNG or None
+ """
+ self.value = value
+
+ def get_children(self):
+ yield self.value
+
+ def _get_yield_nodes_skip_lambdas(self):
+ if not self.value.is_lambda:
+ yield from self.value._get_yield_nodes_skip_lambdas()
+
+
+class Ellipsis(mixins.NoChildrenMixin, NodeNG): # pylint: disable=redefined-builtin
+ """Class representing an :class:`ast.Ellipsis` node.
+
+ An :class:`Ellipsis` is the ``...`` syntax.
+
+ >>> node = astroid.extract_node('...')
+ >>> node
+ <Ellipsis l.1 at 0x7f23b2e35160>
+ """
+
+ def bool_value(self):
+ """Determine the boolean value of this node.
+
+ :returns: The boolean value of this node.
+ For an :class:`Ellipsis` this is always ``True``.
+ :rtype: bool
+ """
+ return True
+
+
+class EmptyNode(mixins.NoChildrenMixin, NodeNG):
+ """Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`."""
+
+ object = None
+
+
+class ExceptHandler(mixins.MultiLineBlockMixin, mixins.AssignTypeMixin, Statement):
+ """Class representing an :class:`ast.ExceptHandler`. node.
+
+ An :class:`ExceptHandler` is an ``except`` block on a try-except.
+
+ >>> node = astroid.extract_node('''
+ try:
+ do_something()
+ except Exception as error:
+ print("Error!")
+ ''')
+ >>> node
+ <TryExcept l.2 at 0x7f23b2e9d908>
+ >>> >>> node.handlers
+ [<ExceptHandler l.4 at 0x7f23b2e9e860>]
+ """
+
+ _astroid_fields = ("type", "name", "body")
+ _multi_line_block_fields = ("body",)
+ type = None
+ """The types that the block handles.
+
+ :type: Tuple or NodeNG or None
+ """
+ name = None
+ """The name that the caught exception is assigned to.
+
+ :type: AssignName or None
+ """
+ body = None
+ """The contents of the block.
+
+ :type: list(NodeNG) or None
+ """
+
+ 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
+
+ # pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
+ def postinit(self, type=None, name=None, body=None):
+ """Do some setup after initialisation.
+
+ :param type: The types that the block handles.
+ :type type: Tuple or NodeNG or None
+
+ :param name: The name that the caught exception is assigned to.
+ :type name: AssignName or None
+
+ :param body:The contents of the block.
+ :type body: list(NodeNG) or None
+ """
+ self.type = type
+ self.name = name
+ self.body = body
+
+ @decorators.cachedproperty
+ 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): # pylint: disable=redefined-outer-name
+ """Check if this node handles any of the given exceptions.
+
+ If ``exceptions`` is empty, this will default to ``True``.
+
+ :param exceptions: The name of the exceptions to check for.
+ :type exceptions: list(str)
+ """
+ if self.type is None or exceptions is None:
+ return True
+ for node in self.type._get_name_nodes():
+ if node.name in exceptions:
+ return True
+ return False
+
+
+class Exec(Statement):
+ """Class representing the ``exec`` statement.
+
+ >>> node = astroid.extract_node('exec "True"')
+ >>> node
+ <Exec l.1 at 0x7f0e8106c6d0>
+ """
+
+ _astroid_fields = ("expr", "globals", "locals")
+ expr = None
+ """The expression to be executed.
+
+ :type: NodeNG or None
+ """
+ globals = None
+ """The globals dictionary to execute with.
+
+ :type: NodeNG or None
+ """
+ locals = None
+ """The locals dictionary to execute with.
+
+ :type: NodeNG or None
+ """
+
+ # pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
+ def postinit(self, expr=None, globals=None, locals=None):
+ """Do some setup after initialisation.
+
+ :param expr: The expression to be executed.
+ :type expr: NodeNG or None
+
+ :param globals:The globals dictionary to execute with.
+ :type globals: NodeNG or None
+
+ :param locals: The locals dictionary to execute with.
+ :type locals: NodeNG or None
+ """
+ self.expr = expr
+ self.globals = globals
+ self.locals = locals
+
+
+class ExtSlice(NodeNG):
+ """Class representing an :class:`ast.ExtSlice` node.
+
+ An :class:`ExtSlice` is a complex slice expression.
+
+ >>> node = astroid.extract_node('l[1:3, 5]')
+ >>> node
+ <Subscript l.1 at 0x7f23b2e9e550>
+ >>> node.slice
+ <ExtSlice l.1 at 0x7f23b7b05ef0>
+ """
+
+ _astroid_fields = ("dims",)
+ dims = None
+ """The simple dimensions that form the complete slice.
+
+ :type: list(NodeNG) or None
+ """
+
+ def postinit(self, dims=None):
+ """Do some setup after initialisation.
+
+ :param dims: The simple dimensions that form the complete slice.
+ :type dims: list(NodeNG) or None
+ """
+ self.dims = dims
+
+
+class For(
+ mixins.MultiLineBlockMixin,
+ mixins.BlockRangeMixIn,
+ mixins.AssignTypeMixin,
+ Statement,
+):
+ """Class representing an :class:`ast.For` node.
+
+ >>> node = astroid.extract_node('for thing in things: print(thing)')
+ >>> node
+ <For l.1 at 0x7f23b2e8cf28>
+ """
+
+ _astroid_fields = ("target", "iter", "body", "orelse")
+ _other_other_fields = ("type_annotation",)
+ _multi_line_block_fields = ("body", "orelse")
+ target = None
+ """What the loop assigns to.
+
+ :type: NodeNG or None
+ """
+ iter = None
+ """What the loop iterates over.
+
+ :type: NodeNG or None
+ """
+ body = None
+ """The contents of the body of the loop.
+
+ :type: list(NodeNG) or None
+ """
+ orelse = None
+ """The contents of the ``else`` block of the loop.
+
+ :type: list(NodeNG) or None
+ """
+ type_annotation = None
+ """If present, this will contain the type annotation passed by a type comment
+
+ :type: NodeNG or None
+ """
+
+ # pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
+ def postinit(
+ self, target=None, iter=None, body=None, orelse=None, type_annotation=None
+ ):
+ """Do some setup after initialisation.
+
+ :param target: What the loop assigns to.
+ :type target: NodeNG or None
+
+ :param iter: What the loop iterates over.
+ :type iter: NodeNG or None
+
+ :param body: The contents of the body of the loop.
+ :type body: list(NodeNG) or None
+
+ :param orelse: The contents of the ``else`` block of the loop.
+ :type orelse: list(NodeNG) or None
+ """
+ self.target = target
+ self.iter = iter
+ self.body = body
+ self.orelse = orelse
+ self.type_annotation = type_annotation
+
+ optional_assign = True
+ """Whether this node optionally assigns a variable.
+
+ This is always ``True`` for :class:`For` nodes.
+
+ :type: bool
+ """
+
+ @decorators.cachedproperty
+ 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.
+
+ >>> node = astroid.extract_node('''
+ async def func(things):
+ async for thing in things:
+ print(thing)
+ ''')
+ >>> node
+ <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
+ >>> node.body[0]
+ <AsyncFor l.3 at 0x7f23b2e417b8>
+ """
+
+
+class Await(NodeNG):
+ """Class representing an :class:`ast.Await` node.
+
+ An :class:`Await` is the ``await`` keyword.
+
+ >>> node = astroid.extract_node('''
+ async def func(things):
+ await other_func()
+ ''')
+ >>> node
+ <AsyncFunctionDef.func l.2 at 0x7f23b2e41748>
+ >>> node.body[0]
+ <Expr l.3 at 0x7f23b2e419e8>
+ >>> list(node.body[0].get_children())[0]
+ <Await l.3 at 0x7f23b2e41a20>
+ """
+
+ _astroid_fields = ("value",)
+ value = None
+ """What to wait for.
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, value=None):
+ """Do some setup after initialisation.
+
+ :param value: What to wait for.
+ :type value: NodeNG or None
+ """
+ self.value = value
+
+ def get_children(self):
+ yield self.value
+
+
+class ImportFrom(mixins.NoChildrenMixin, mixins.ImportFromMixin, Statement):
+ """Class representing an :class:`ast.ImportFrom` node.
+
+ >>> node = astroid.extract_node('from my_package import my_module')
+ >>> node
+ <ImportFrom l.1 at 0x7f23b2e415c0>
+ """
+
+ _other_fields = ("modname", "names", "level")
+
+ def __init__(
+ self, fromname, names, level=0, lineno=None, col_offset=None, parent=None
+ ):
+ """
+ :param fromname: The module that is being imported from.
+ :type fromname: str or None
+
+ :param names: What is being imported from the module.
+ :type names: list(tuple(str, str or None))
+
+ :param level: The level of relative import.
+ :type level: int
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.modname = fromname
+ """The module that is being imported from.
+
+ This is ``None`` for relative imports.
+
+ :type: str or None
+ """
+
+ self.names = 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).
+
+ :type: list(tuple(str, str or None))
+ """
+
+ self.level = level
+ """The level of relative import.
+
+ Essentially this is the number of dots in the import.
+ This is always 0 for absolute imports.
+
+ :type: int
+ """
+
+ super(ImportFrom, self).__init__(lineno, col_offset, parent)
+
+
+class Attribute(NodeNG):
+ """Class representing an :class:`ast.Attribute` node."""
+
+ _astroid_fields = ("expr",)
+ _other_fields = ("attrname",)
+ expr = None
+ """The name that this node represents.
+
+ :type: Name or None
+ """
+
+ def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param attrname: The name of the attribute.
+ :type attrname: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.attrname = attrname
+ """The name of the attribute.
+
+ :type: str or None
+ """
+
+ super(Attribute, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, expr=None):
+ """Do some setup after initialisation.
+
+ :param expr: The name that this node represents.
+ :type expr: Name or None
+ """
+ self.expr = expr
+
+ def get_children(self):
+ yield self.expr
+
+
+class Global(mixins.NoChildrenMixin, Statement):
+ """Class representing an :class:`ast.Global` node.
+
+ >>> node = astroid.extract_node('global a_global')
+ >>> node
+ <Global l.1 at 0x7f23b2e9de10>
+ """
+
+ _other_fields = ("names",)
+
+ def __init__(self, names, lineno=None, col_offset=None, parent=None):
+ """
+ :param names: The names being declared as global.
+ :type names: list(str)
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.names = names
+ """The names being declared as global.
+
+ :type: list(str)
+ """
+
+ super(Global, self).__init__(lineno, col_offset, parent)
+
+ def _infer_name(self, frame, name):
+ return name
+
+
+class If(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
+ """Class representing an :class:`ast.If` node.
+
+ >>> node = astroid.extract_node('if condition: print(True)')
+ >>> node
+ <If l.1 at 0x7f23b2e9dd30>
+ """
+
+ _astroid_fields = ("test", "body", "orelse")
+ _multi_line_block_fields = ("body", "orelse")
+ test = None
+ """The condition that the statement tests.
+
+ :type: NodeNG or None
+ """
+ body = None
+ """The contents of the block.
+
+ :type: list(NodeNG) or None
+ """
+ orelse = None
+ """The contents of the ``else`` block.
+
+ :type: list(NodeNG) or None
+ """
+
+ def postinit(self, test=None, body=None, orelse=None):
+ """Do some setup after initialisation.
+
+ :param test: The condition that the statement tests.
+ :type test: NodeNG or None
+
+ :param body: The contents of the block.
+ :type body: list(NodeNG) or None
+
+ :param orelse: The contents of the ``else`` block.
+ :type orelse: list(NodeNG) or None
+ """
+ self.test = test
+ self.body = body
+ self.orelse = orelse
+
+ @decorators.cachedproperty
+ 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):
+ """Get a range from the given line number to where this node ends.
+
+ :param lineno: The line number to start the range at.
+ :type lineno: int
+
+ :returns: The range of line numbers that this node belongs to,
+ starting at the given line number.
+ :rtype: tuple(int, int)
+ """
+ if lineno == self.body[0].fromlineno:
+ return lineno, lineno
+ if lineno <= self.body[-1].tolineno:
+ return lineno, self.body[-1].tolineno
+ return self._elsed_block_range(lineno, self.orelse, self.body[0].fromlineno - 1)
+
+ def get_children(self):
+ yield self.test
+
+ yield from self.body
+ yield from self.orelse
+
+ def has_elif_block(self):
+ return len(self.orelse) == 1 and isinstance(self.orelse[0], If)
+
+
+class IfExp(NodeNG):
+ """Class representing an :class:`ast.IfExp` node.
+
+ >>> node = astroid.extract_node('value if condition else other')
+ >>> node
+ <IfExp l.1 at 0x7f23b2e9dbe0>
+ """
+
+ _astroid_fields = ("test", "body", "orelse")
+ test = None
+ """The condition that the statement tests.
+
+ :type: NodeNG or None
+ """
+ body = None
+ """The contents of the block.
+
+ :type: list(NodeNG) or None
+ """
+ orelse = None
+ """The contents of the ``else`` block.
+
+ :type: list(NodeNG) or None
+ """
+
+ def postinit(self, test=None, body=None, orelse=None):
+ """Do some setup after initialisation.
+
+ :param test: The condition that the statement tests.
+ :type test: NodeNG or None
+
+ :param body: The contents of the block.
+ :type body: list(NodeNG) or None
+
+ :param orelse: The contents of the ``else`` block.
+ :type orelse: list(NodeNG) or 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):
+ # `1 if True else 2 if False else 3` is parsed as
+ # `1 if True else (2 if False else 3)`
+ return False
+
+
+class Import(mixins.NoChildrenMixin, mixins.ImportFromMixin, Statement):
+ """Class representing an :class:`ast.Import` node.
+
+ >>> node = astroid.extract_node('import astroid')
+ >>> node
+ <Import l.1 at 0x7f23b2e4e5c0>
+ """
+
+ _other_fields = ("names",)
+
+ def __init__(self, names=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param names: The names being imported.
+ :type names: list(tuple(str, str or None)) or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.names = 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).
+
+ :type: list(tuple(str, str or None)) or None
+ """
+
+ super(Import, self).__init__(lineno, col_offset, parent)
+
+
+class Index(NodeNG):
+ """Class representing an :class:`ast.Index` node.
+
+ An :class:`Index` is a simple subscript.
+
+ >>> node = astroid.extract_node('things[1]')
+ >>> node
+ <Subscript l.1 at 0x7f23b2e9e2b0>
+ >>> node.slice
+ <Index l.1 at 0x7f23b2e9e6a0>
+ """
+
+ _astroid_fields = ("value",)
+ value = None
+ """The value to subscript with.
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, value=None):
+ """Do some setup after initialisation.
+
+ :param value: The value to subscript with.
+ :type value: NodeNG or None
+ """
+ self.value = value
+
+ def get_children(self):
+ yield self.value
+
+
+class Keyword(NodeNG):
+ """Class representing an :class:`ast.keyword` node.
+
+ >>> node = astroid.extract_node('function(a_kwarg=True)')
+ >>> node
+ <Call l.1 at 0x7f23b2e9e320>
+ >>> node.keywords
+ [<Keyword l.1 at 0x7f23b2e9e9b0>]
+ """
+
+ _astroid_fields = ("value",)
+ _other_fields = ("arg",)
+ value = None
+ """The value being assigned to the keyword argument.
+
+ :type: NodeNG or None
+ """
+
+ def __init__(self, arg=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param arg: The argument being assigned to.
+ :type arg: Name or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.arg = arg
+ """The argument being assigned to.
+
+ :type: Name or None
+ """
+
+ super(Keyword, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, value=None):
+ """Do some setup after initialisation.
+
+ :param value: The value being assigned to the ketword argument.
+ :type value: NodeNG or None
+ """
+ self.value = value
+
+ def get_children(self):
+ yield self.value
+
+
+class List(_BaseContainer):
+ """Class representing an :class:`ast.List` node.
+
+ >>> node = astroid.extract_node('[1, 2, 3]')
+ >>> node
+ <List.list l.1 at 0x7f23b2e9e128>
+ """
+
+ _other_fields = ("ctx",)
+
+ def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param ctx: Whether the list is assigned to or loaded from.
+ :type ctx: Context or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.ctx = ctx
+ """Whether the list is assigned to or loaded from.
+
+ :type: Context or None
+ """
+
+ super(List, self).__init__(lineno, col_offset, parent)
+
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+ return "%s.list" % BUILTINS
+
+ def getitem(self, index, context=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(mixins.NoChildrenMixin, Statement):
+ """Class representing an :class:`ast.Nonlocal` node.
+
+ >>> node = astroid.extract_node('''
+ def function():
+ nonlocal var
+ ''')
+ >>> node
+ <FunctionDef.function l.2 at 0x7f23b2e9e208>
+ >>> node.body[0]
+ <Nonlocal l.3 at 0x7f23b2e9e908>
+ """
+
+ _other_fields = ("names",)
+
+ def __init__(self, names, lineno=None, col_offset=None, parent=None):
+ """
+ :param names: The names being declared as not local.
+ :type names: list(str)
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.names = names
+ """The names being declared as not local.
+
+ :type: list(str)
+ """
+
+ super(Nonlocal, self).__init__(lineno, col_offset, parent)
+
+ def _infer_name(self, frame, name):
+ return name
+
+
+class Pass(mixins.NoChildrenMixin, Statement):
+ """Class representing an :class:`ast.Pass` node.
+
+ >>> node = astroid.extract_node('pass')
+ >>> node
+ <Pass l.1 at 0x7f23b2e9e748>
+ """
+
+
+class Print(Statement):
+ """Class representing an :class:`ast.Print` node.
+
+ >>> node = astroid.extract_node('print "A message"')
+ >>> node
+ <Print l.1 at 0x7f0e8101d290>
+ """
+
+ _astroid_fields = ("dest", "values")
+ dest = None
+ """Where to print to.
+
+ :type: NodeNG or None
+ """
+ values = None
+ """What to print.
+
+ :type: list(NodeNG) or None
+ """
+
+ def __init__(self, nl=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param nl: Whether to print a new line.
+ :type nl: bool or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.nl = nl
+ """Whether to print a new line.
+
+ :type: bool or None
+ """
+
+ super(Print, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, dest=None, values=None):
+ """Do some setup after initialisation.
+
+ :param dest: Where to print to.
+ :type dest: NodeNG or None
+
+ :param values: What to print.
+ :type values: list(NodeNG) or None
+ """
+ self.dest = dest
+ self.values = values
+
+
+class Raise(Statement):
+ """Class representing an :class:`ast.Raise` node.
+
+ >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")')
+ >>> node
+ <Raise l.1 at 0x7f23b2e9e828>
+ """
+
+ exc = None
+ """What is being raised.
+
+ :type: NodeNG or None
+ """
+ _astroid_fields = ("exc", "cause")
+ cause = None
+ """The exception being used to raise this one.
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, exc=None, cause=None):
+ """Do some setup after initialisation.
+
+ :param exc: What is being raised.
+ :type exc: NodeNG or None
+
+ :param cause: The exception being used to raise this one.
+ :type cause: NodeNG or None
+ """
+ self.exc = exc
+ self.cause = cause
+
+ def raises_not_implemented(self):
+ """Check if this node raises a :class:`NotImplementedError`.
+
+ :returns: True if this node raises a :class:`NotImplementedError`,
+ False otherwise.
+ :rtype: bool
+ """
+ if not self.exc:
+ return False
+ for name in self.exc._get_name_nodes():
+ if name.name == "NotImplementedError":
+ return True
+ return False
+
+ def get_children(self):
+ if self.exc is not None:
+ yield self.exc
+
+ if self.cause is not None:
+ yield self.cause
+
+
+class Return(Statement):
+ """Class representing an :class:`ast.Return` node.
+
+ >>> node = astroid.extract_node('return True')
+ >>> node
+ <Return l.1 at 0x7f23b8211908>
+ """
+
+ _astroid_fields = ("value",)
+ value = None
+ """The value being returned.
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, value=None):
+ """Do some setup after initialisation.
+
+ :param value: The value being returned.
+ :type value: NodeNG or None
+ """
+ self.value = value
+
+ def get_children(self):
+ if self.value is not None:
+ yield self.value
+
+ def is_tuple_return(self):
+ return isinstance(self.value, Tuple)
+
+ def _get_return_nodes_skip_functions(self):
+ yield self
+
+
+class Set(_BaseContainer):
+ """Class representing an :class:`ast.Set` node.
+
+ >>> node = astroid.extract_node('{1, 2, 3}')
+ >>> node
+ <Set.set l.1 at 0x7f23b2e71d68>
+ """
+
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+ return "%s.set" % BUILTINS
+
+
+class Slice(NodeNG):
+ """Class representing an :class:`ast.Slice` node.
+
+ >>> node = astroid.extract_node('things[1:3]')
+ >>> node
+ <Subscript l.1 at 0x7f23b2e71f60>
+ >>> node.slice
+ <Slice l.1 at 0x7f23b2e71e80>
+ """
+
+ _astroid_fields = ("lower", "upper", "step")
+ lower = None
+ """The lower index in the slice.
+
+ :type: NodeNG or None
+ """
+ upper = None
+ """The upper index in the slice.
+
+ :type: NodeNG or None
+ """
+ step = None
+ """The step to take between indexes.
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, lower=None, upper=None, step=None):
+ """Do some setup after initialisation.
+
+ :param lower: The lower index in the slice.
+ :value lower: NodeNG or None
+
+ :param upper: The upper index in the slice.
+ :value upper: NodeNG or None
+
+ :param step: The step to take between index.
+ :param step: NodeNG or 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
+
+ @decorators.cachedproperty
+ def _proxied(self):
+ builtins = MANAGER.builtins_module
+ return builtins.getattr("slice")[0]
+
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+ return "%s.slice" % BUILTINS
+
+ def igetattr(self, attrname, context=None):
+ """Infer the possible values of the given attribute on the slice.
+
+ :param attrname: The name of the attribute to infer.
+ :type attrname: str
+
+ :returns: The inferred possible values.
+ :rtype: iterable(NodeNG)
+ """
+ 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=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
+
+
+class Starred(mixins.ParentAssignTypeMixin, NodeNG):
+ """Class representing an :class:`ast.Starred` node.
+
+ >>> node = astroid.extract_node('*args')
+ >>> node
+ <Starred l.1 at 0x7f23b2e41978>
+ """
+
+ _astroid_fields = ("value",)
+ _other_fields = ("ctx",)
+ value = None
+ """What is being unpacked.
+
+ :type: NodeNG or None
+ """
+
+ def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param ctx: Whether the list is assigned to or loaded from.
+ :type ctx: Context or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.ctx = ctx
+ """Whether the starred item is assigned to or loaded from.
+
+ :type: Context or None
+ """
+
+ super(Starred, self).__init__(
+ lineno=lineno, col_offset=col_offset, parent=parent
+ )
+
+ def postinit(self, value=None):
+ """Do some setup after initialisation.
+
+ :param value: What is being unpacked.
+ :type value: NodeNG or None
+ """
+ self.value = value
+
+ def get_children(self):
+ yield self.value
+
+
+class Subscript(NodeNG):
+ """Class representing an :class:`ast.Subscript` node.
+
+ >>> node = astroid.extract_node('things[1:3]')
+ >>> node
+ <Subscript l.1 at 0x7f23b2e71f60>
+ """
+
+ _astroid_fields = ("value", "slice")
+ _other_fields = ("ctx",)
+ value = None
+ """What is being indexed.
+
+ :type: NodeNG or None
+ """
+ slice = None
+ """The slice being used to lookup.
+
+ :type: NodeNG or None
+ """
+
+ def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param ctx: Whether the subscripted item is assigned to or loaded from.
+ :type ctx: Context or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.ctx = ctx
+ """Whether the subscripted item is assigned to or loaded from.
+
+ :type: Context or None
+ """
+
+ super(Subscript, self).__init__(
+ lineno=lineno, col_offset=col_offset, parent=parent
+ )
+
+ # pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
+ def postinit(self, value=None, slice=None):
+ """Do some setup after initialisation.
+
+ :param value: What is being indexed.
+ :type value: NodeNG or None
+
+ :param slice: The slice being used to lookup.
+ :type slice: NodeNG or None
+ """
+ self.value = value
+ self.slice = slice
+
+ def get_children(self):
+ yield self.value
+ yield self.slice
+
+
+class TryExcept(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
+ """Class representing an :class:`ast.TryExcept` node.
+
+ >>> node = astroid.extract_node('''
+ try:
+ do_something()
+ except Exception as error:
+ print("Error!")
+ ''')
+ >>> node
+ <TryExcept l.2 at 0x7f23b2e9d908>
+ """
+
+ _astroid_fields = ("body", "handlers", "orelse")
+ _multi_line_block_fields = ("body", "handlers", "orelse")
+ body = None
+ """The contents of the block to catch exceptions from.
+
+ :type: list(NodeNG) or None
+ """
+ handlers = None
+ """The exception handlers.
+
+ :type: list(ExceptHandler) or None
+ """
+ orelse = None
+ """The contents of the ``else`` block.
+
+ :type: list(NodeNG) or None
+ """
+
+ def postinit(self, body=None, handlers=None, orelse=None):
+ """Do some setup after initialisation.
+
+ :param body: The contents of the block to catch exceptions from.
+ :type body: list(NodeNG) or None
+
+ :param handlers: The exception handlers.
+ :type handlers: list(ExceptHandler) or None
+
+ :param orelse: The contents of the ``else`` block.
+ :type orelse: list(NodeNG) or None
+ """
+ self.body = body
+ self.handlers = handlers
+ self.orelse = orelse
+
+ def _infer_name(self, frame, name):
+ return name
+
+ def block_range(self, lineno):
+ """Get a range from the given line number to where this node ends.
+
+ :param lineno: The line number to start the range at.
+ :type lineno: int
+
+ :returns: The range of line numbers that this node belongs to,
+ starting at the given line number.
+ :rtype: tuple(int, int)
+ """
+ last = None
+ 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 last is None:
+ last = exhandler.body[0].fromlineno - 1
+ return self._elsed_block_range(lineno, self.orelse, last)
+
+ def get_children(self):
+ yield from self.body
+
+ yield from self.handlers or ()
+ yield from self.orelse or ()
+
+
+class TryFinally(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
+ """Class representing an :class:`ast.TryFinally` node.
+
+ >>> node = astroid.extract_node('''
+ try:
+ do_something()
+ except Exception as error:
+ print("Error!")
+ finally:
+ print("Cleanup!")
+ ''')
+ >>> node
+ <TryFinally l.2 at 0x7f23b2e41d68>
+ """
+
+ _astroid_fields = ("body", "finalbody")
+ _multi_line_block_fields = ("body", "finalbody")
+ body = None
+ """The try-except that the finally is attached to.
+
+ :type: list(TryExcept) or None
+ """
+ finalbody = None
+ """The contents of the ``finally`` block.
+
+ :type: list(NodeNG) or None
+ """
+
+ def postinit(self, body=None, finalbody=None):
+ """Do some setup after initialisation.
+
+ :param body: The try-except that the finally is attached to.
+ :type body: list(TryExcept) or None
+
+ :param finalbody: The contents of the ``finally`` block.
+ :type finalbody: list(NodeNG) or None
+ """
+ self.body = body
+ self.finalbody = finalbody
+
+ def block_range(self, lineno):
+ """Get a range from the given line number to where this node ends.
+
+ :param lineno: The line number to start the range at.
+ :type lineno: int
+
+ :returns: The range of line numbers that this node belongs to,
+ starting at the given line number.
+ :rtype: tuple(int, int)
+ """
+ child = self.body[0]
+ # py2.5 try: except: finally:
+ if (
+ isinstance(child, TryExcept)
+ and child.fromlineno == self.fromlineno
+ and child.tolineno >= lineno > self.fromlineno
+ ):
+ return child.block_range(lineno)
+ return self._elsed_block_range(lineno, self.finalbody)
+
+ def get_children(self):
+ yield from self.body
+ yield from self.finalbody
+
+
+class Tuple(_BaseContainer):
+ """Class representing an :class:`ast.Tuple` node.
+
+ >>> node = astroid.extract_node('(1, 2, 3)')
+ >>> node
+ <Tuple.tuple l.1 at 0x7f23b2e41780>
+ """
+
+ _other_fields = ("ctx",)
+
+ def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param ctx: Whether the tuple is assigned to or loaded from.
+ :type ctx: Context or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.ctx = ctx
+ """Whether the tuple is assigned to or loaded from.
+
+ :type: Context or None
+ """
+
+ super(Tuple, self).__init__(lineno, col_offset, parent)
+
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+ return "%s.tuple" % BUILTINS
+
+ def getitem(self, index, context=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 UnaryOp(NodeNG):
+ """Class representing an :class:`ast.UnaryOp` node.
+
+ >>> node = astroid.extract_node('-5')
+ >>> node
+ <UnaryOp l.1 at 0x7f23b2e4e198>
+ """
+
+ _astroid_fields = ("operand",)
+ _other_fields = ("op",)
+ operand = None
+ """What the unary operator is applied to.
+
+ :type: NodeNG or None
+ """
+
+ def __init__(self, op=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param op: The operator.
+ :type: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.op = op
+ """The operator.
+
+ :type: str or None
+ """
+
+ super(UnaryOp, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, operand=None):
+ """Do some setup after initialisation.
+
+ :param operand: What the unary operator is applied to.
+ :type operand: NodeNG or None
+ """
+ self.operand = operand
+
+ # This is set by inference.py
+ def _infer_unaryop(self, context=None):
+ raise NotImplementedError
+
+ def type_errors(self, context=None):
+ """Get a list of type errors which can occur during inference.
+
+ Each TypeError is represented by a :class:`BadBinaryOperationMessage`,
+ which holds the original exception.
+
+ :returns: The list of possible type errors.
+ :rtype: list(BadBinaryOperationMessage)
+ """
+ try:
+ results = self._infer_unaryop(context=context)
+ return [
+ result
+ for result in results
+ if isinstance(result, util.BadUnaryOperationMessage)
+ ]
+ except exceptions.InferenceError:
+ return []
+
+ def get_children(self):
+ yield self.operand
+
+ def op_precedence(self):
+ if self.op == "not":
+ return OP_PRECEDENCE[self.op]
+
+ return super().op_precedence()
+
+
+class While(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
+ """Class representing an :class:`ast.While` node.
+
+ >>> node = astroid.extract_node('''
+ while condition():
+ print("True")
+ ''')
+ >>> node
+ <While l.2 at 0x7f23b2e4e390>
+ """
+
+ _astroid_fields = ("test", "body", "orelse")
+ _multi_line_block_fields = ("body", "orelse")
+ test = None
+ """The condition that the loop tests.
+
+ :type: NodeNG or None
+ """
+ body = None
+ """The contents of the loop.
+
+ :type: list(NodeNG) or None
+ """
+ orelse = None
+ """The contents of the ``else`` block.
+
+ :type: list(NodeNG) or None
+ """
+
+ def postinit(self, test=None, body=None, orelse=None):
+ """Do some setup after initialisation.
+
+ :param test: The condition that the loop tests.
+ :type test: NodeNG or None
+
+ :param body: The contents of the loop.
+ :type body: list(NodeNG) or None
+
+ :param orelse: The contents of the ``else`` block.
+ :type orelse: list(NodeNG) or None
+ """
+ self.test = test
+ self.body = body
+ self.orelse = orelse
+
+ @decorators.cachedproperty
+ 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):
+ """Get a range from the given line number to where this node ends.
+
+ :param lineno: The line number to start the range at.
+ :type lineno: int
+
+ :returns: The range of line numbers that this node belongs to,
+ starting at the given line number.
+ :rtype: tuple(int, int)
+ """
+ return self._elsed_block_range(lineno, self.orelse)
+
+ def get_children(self):
+ yield self.test
+
+ yield from self.body
+ yield from self.orelse
+
+
+class With(
+ mixins.MultiLineBlockMixin,
+ mixins.BlockRangeMixIn,
+ mixins.AssignTypeMixin,
+ Statement,
+):
+ """Class representing an :class:`ast.With` node.
+
+ >>> node = astroid.extract_node('''
+ with open(file_path) as file_:
+ print(file_.read())
+ ''')
+ >>> node
+ <With l.2 at 0x7f23b2e4e710>
+ """
+
+ _astroid_fields = ("items", "body")
+ _other_other_fields = ("type_annotation",)
+ _multi_line_block_fields = ("body",)
+ items = None
+ """The pairs of context managers and the names they are assigned to.
+
+ :type: list(tuple(NodeNG, AssignName or None)) or None
+ """
+ body = None
+ """The contents of the ``with`` block.
+
+ :type: list(NodeNG) or None
+ """
+ type_annotation = None
+ """If present, this will contain the type annotation passed by a type comment
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, items=None, body=None, type_annotation=None):
+ """Do some setup after initialisation.
+
+ :param items: The pairs of context managers and the names
+ they are assigned to.
+ :type items: list(tuple(NodeNG, AssignName or None)) or None
+
+ :param body: The contents of the ``with`` block.
+ :type body: list(NodeNG) or None
+ """
+ self.items = items
+ self.body = body
+ self.type_annotation = type_annotation
+
+ @decorators.cachedproperty
+ 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.
+
+ >>> node = astroid.extract_node('yield True')
+ >>> node
+ <Yield l.1 at 0x7f23b2e4e5f8>
+ """
+
+ _astroid_fields = ("value",)
+ value = None
+ """The value to yield.
+
+ :type: NodeNG or None
+ """
+
+ def postinit(self, value=None):
+ """Do some setup after initialisation.
+
+ :param value: The value to yield.
+ :type value: NodeNG or None
+ """
+ self.value = value
+
+ def get_children(self):
+ if self.value is not None:
+ yield self.value
+
+ def _get_yield_nodes_skip_lambdas(self):
+ yield self
+
+
+class YieldFrom(Yield):
+ """Class representing an :class:`ast.YieldFrom` node."""
+
+
+class DictUnpack(mixins.NoChildrenMixin, NodeNG):
+ """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.
+
+ >>> node = astroid.extract_node('f"Format {type_}"')
+ >>> node
+ <JoinedStr l.1 at 0x7f23b2e4ed30>
+ >>> node.values
+ [<Const.str l.1 at 0x7f23b2e4eda0>, <FormattedValue l.1 at 0x7f23b2e4edd8>]
+ """
+
+ _astroid_fields = ("value", "format_spec")
+ value = None
+ """The value to be formatted into the string.
+
+ :type: NodeNG or None
+ """
+ conversion = None
+ """The type of formatting to be applied to the value.
+
+ .. seealso::
+ :class:`ast.FormattedValue`
+
+ :type: int or None
+ """
+ format_spec = None
+ """The formatting to be applied to the value.
+
+ .. seealso::
+ :class:`ast.FormattedValue`
+
+ :type: JoinedStr or None
+ """
+
+ def postinit(self, value, conversion=None, format_spec=None):
+ """Do some setup after initialisation.
+
+ :param value: The value to be formatted into the string.
+ :type value: NodeNG
+
+ :param conversion: The type of formatting to be applied to the value.
+ :type conversion: int or None
+
+ :param format_spec: The formatting to be applied to the value.
+ :type format_spec: JoinedStr or None
+ """
+ self.value = value
+ self.conversion = conversion
+ self.format_spec = format_spec
+
+ def get_children(self):
+ yield self.value
+
+ if self.format_spec is not None:
+ yield self.format_spec
+
+
+class JoinedStr(NodeNG):
+ """Represents a list of string expressions to be joined.
+
+ >>> node = astroid.extract_node('f"Format {type_}"')
+ >>> node
+ <JoinedStr l.1 at 0x7f23b2e4ed30>
+ """
+
+ _astroid_fields = ("values",)
+ values = None
+ """The string expressions to be joined.
+
+ :type: list(FormattedValue or Const) or None
+ """
+
+ def postinit(self, values=None):
+ """Do some setup after initialisation.
+
+ :param value: The string expressions to be joined.
+
+ :type: list(FormattedValue or Const) or None
+ """
+ self.values = values
+
+ def get_children(self):
+ yield from self.values
+
+
+class NamedExpr(mixins.AssignTypeMixin, NodeNG):
+ """Represents the assignment from the assignment expression
+
+ >>> module = astroid.parse('if a := 1: pass')
+ >>> module.body[0].test
+ <NamedExpr l.1 at 0x7f23b2e4ed30>
+ """
+
+ _astroid_fields = ("target", "value")
+ target = None
+ """The assignment target
+
+ :type: Name
+ """
+ value = None
+ """The value that gets assigned in the expression
+
+ :type: NodeNG
+ """
+
+ def postinit(self, target, value):
+ self.target = target
+ self.value = value
+
+
+class Unknown(mixins.AssignTypeMixin, NodeNG):
+ """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 qname(self):
+ return "Unknown"
+
+ def infer(self, context=None, **kwargs):
+ """Inference on an Unknown node immediately terminates."""
+ yield util.Uninferable
+
+
+# constants ##############################################################
+
+CONST_CLS = {
+ list: List,
+ tuple: Tuple,
+ dict: Dict,
+ set: Set,
+ type(None): Const,
+ type(NotImplemented): Const,
+}
+if PY38:
+ CONST_CLS[type(...)] = Const
+
+
+def _update_const_classes():
+ """update constant classes, so the keys of CONST_CLS can be reused"""
+ klasses = (bool, int, float, complex, str, bytes)
+ for kls in klasses:
+ CONST_CLS[kls] = Const
+
+
+_update_const_classes()
+
+
+def _two_step_initialization(cls, value):
+ instance = cls()
+ instance.postinit(value)
+ return instance
+
+
+def _dict_initialization(cls, value):
+ if isinstance(value, dict):
+ value = tuple(value.items())
+ return _two_step_initialization(cls, value)
+
+
+_CONST_CLS_CONSTRUCTORS = {
+ List: _two_step_initialization,
+ Tuple: _two_step_initialization,
+ Dict: _dict_initialization,
+ Set: _two_step_initialization,
+ Const: lambda cls, value: cls(value),
+}
+
+
+def const_factory(value):
+ """return an astroid node for a python value"""
+ # XXX we should probably be stricter here and only consider stuff in
+ # CONST_CLS or do better treatment: in case where value is not in CONST_CLS,
+ # we should rather recall the builder on this value than returning an empty
+ # node (another option being that const_factory shouldn't be called with something
+ # not in CONST_CLS)
+ assert not isinstance(value, NodeNG)
+
+ # Hack for ignoring elements of a sequence
+ # or a mapping, in order to avoid transforming
+ # each element to an AST. This is fixed in 2.0
+ # and this approach is a temporary hack.
+ if isinstance(value, (list, set, tuple, dict)):
+ elts = []
+ else:
+ elts = value
+
+ try:
+ initializer_cls = CONST_CLS[value.__class__]
+ initializer = _CONST_CLS_CONSTRUCTORS[initializer_cls]
+ return initializer(initializer_cls, elts)
+ except (KeyError, AttributeError):
+ node = EmptyNode()
+ node.object = value
+ return node
+
+
+def is_from_decorator(node):
+ """Return True if the given node is the child of a decorator"""
+ parent = node.parent
+ while parent is not None:
+ if isinstance(parent, Decorators):
+ return True
+ parent = parent.parent
+ return False
diff --git a/venv/Lib/site-packages/astroid/nodes.py b/venv/Lib/site-packages/astroid/nodes.py
new file mode 100644
index 0000000..bf6911a
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/nodes.py
@@ -0,0 +1,175 @@
+# Copyright (c) 2006-2011, 2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2016 Jared Garst <jgarst@users.noreply.github.com>
+# Copyright (c) 2017 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2017 rr- <rr-@sakuya.pl>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Every available node class.
+
+.. seealso::
+ :doc:`ast documentation <green_tree_snakes:nodes>`
+
+All nodes inherit from :class:`~astroid.node_classes.NodeNG`.
+"""
+# pylint: disable=unused-import,redefined-builtin
+
+from astroid.node_classes import (
+ Arguments,
+ AssignAttr,
+ Assert,
+ Assign,
+ AnnAssign,
+ AssignName,
+ AugAssign,
+ Repr,
+ BinOp,
+ BoolOp,
+ Break,
+ Call,
+ Compare,
+ Comprehension,
+ Const,
+ Continue,
+ Decorators,
+ DelAttr,
+ DelName,
+ Delete,
+ Dict,
+ Expr,
+ Ellipsis,
+ EmptyNode,
+ ExceptHandler,
+ Exec,
+ ExtSlice,
+ For,
+ ImportFrom,
+ Attribute,
+ Global,
+ If,
+ IfExp,
+ Import,
+ Index,
+ Keyword,
+ List,
+ Name,
+ NamedExpr,
+ Nonlocal,
+ Pass,
+ Print,
+ Raise,
+ Return,
+ Set,
+ Slice,
+ Starred,
+ Subscript,
+ TryExcept,
+ TryFinally,
+ Tuple,
+ UnaryOp,
+ While,
+ With,
+ Yield,
+ YieldFrom,
+ const_factory,
+ AsyncFor,
+ Await,
+ AsyncWith,
+ FormattedValue,
+ JoinedStr,
+ # Node not present in the builtin ast module.
+ DictUnpack,
+ Unknown,
+)
+from astroid.scoped_nodes import (
+ Module,
+ GeneratorExp,
+ Lambda,
+ DictComp,
+ ListComp,
+ SetComp,
+ FunctionDef,
+ ClassDef,
+ AsyncFunctionDef,
+)
+
+
+ALL_NODE_CLASSES = (
+ AsyncFunctionDef,
+ AsyncFor,
+ AsyncWith,
+ Await,
+ Arguments,
+ AssignAttr,
+ Assert,
+ Assign,
+ AnnAssign,
+ AssignName,
+ AugAssign,
+ Repr,
+ BinOp,
+ BoolOp,
+ Break,
+ Call,
+ ClassDef,
+ Compare,
+ Comprehension,
+ Const,
+ Continue,
+ Decorators,
+ DelAttr,
+ DelName,
+ Delete,
+ Dict,
+ DictComp,
+ DictUnpack,
+ Expr,
+ Ellipsis,
+ EmptyNode,
+ ExceptHandler,
+ Exec,
+ ExtSlice,
+ For,
+ ImportFrom,
+ FunctionDef,
+ Attribute,
+ GeneratorExp,
+ Global,
+ If,
+ IfExp,
+ Import,
+ Index,
+ Keyword,
+ Lambda,
+ List,
+ ListComp,
+ Name,
+ NamedExpr,
+ Nonlocal,
+ Module,
+ Pass,
+ Print,
+ Raise,
+ Return,
+ Set,
+ SetComp,
+ Slice,
+ Starred,
+ Subscript,
+ TryExcept,
+ TryFinally,
+ Tuple,
+ UnaryOp,
+ While,
+ With,
+ Yield,
+ YieldFrom,
+ FormattedValue,
+ JoinedStr,
+)
diff --git a/venv/Lib/site-packages/astroid/objects.py b/venv/Lib/site-packages/astroid/objects.py
new file mode 100644
index 0000000..888ca36
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/objects.py
@@ -0,0 +1,282 @@
+# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""
+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(...))
+"""
+
+import builtins
+
+from astroid import bases
+from astroid import decorators
+from astroid import exceptions
+from astroid import MANAGER
+from astroid import node_classes
+from astroid import scoped_nodes
+from astroid import util
+
+
+BUILTINS = builtins.__name__
+objectmodel = util.lazy_import("interpreter.objectmodel")
+
+
+class FrozenSet(node_classes._BaseContainer):
+ """class representing a FrozenSet composite node"""
+
+ def pytype(self):
+ return "%s.frozenset" % BUILTINS
+
+ def _infer(self, context=None):
+ yield self
+
+ @decorators.cachedproperty
+ def _proxied(self): # pylint: disable=method-hidden
+ ast_builtins = MANAGER.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.
+ """
+
+ # pylint: disable=unnecessary-lambda
+ special_attributes = util.lazy_descriptor(lambda: objectmodel.SuperModel())
+
+ # pylint: disable=super-init-not-called
+ def __init__(self, mro_pointer, mro_type, self_class, scope):
+ self.type = mro_type
+ self.mro_pointer = mro_pointer
+ self._class_based = False
+ self._self_class = self_class
+ self._scope = scope
+
+ def _infer(self, context=None):
+ 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 exceptions.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 exceptions.SuperError(
+ "The second argument to super must be an "
+ "instance or subtype of type, not {type}.",
+ super_=self,
+ )
+
+ if not mro_type.newstyle:
+ raise exceptions.SuperError(
+ "Unable to call super on old-style classes.", super_=self
+ )
+
+ mro = mro_type.mro()
+ if self.mro_pointer not in mro:
+ raise exceptions.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 :]
+
+ @decorators.cachedproperty
+ def _proxied(self):
+ ast_builtins = MANAGER.builtins_module
+ return ast_builtins.getattr("super")[0]
+
+ def pytype(self):
+ return "%s.super" % BUILTINS
+
+ def display_type(self):
+ return "Super of"
+
+ @property
+ def name(self):
+ """Get the name of the MRO pointer."""
+ return self.mro_pointer.name
+
+ def qname(self):
+ return "super"
+
+ def igetattr(self, name, context=None):
+ """Retrieve the inferred values of the given attribute name."""
+
+ if name in self.special_attributes:
+ 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 exceptions.SuperError as exc:
+ raise exceptions.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 exceptions.MroError as exc:
+ raise exceptions.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 bases._is_property(inferred):
+ # TODO: support other descriptors as well.
+ try:
+ yield from inferred.infer_call_result(self, context)
+ except exceptions.InferenceError:
+ yield util.Uninferable
+ else:
+ yield bases.BoundMethod(inferred, cls)
+
+ if not found:
+ raise exceptions.AttributeInferenceError(
+ target=self, attribute=name, context=context
+ )
+
+ def getattr(self, name, context=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.
+ """
+
+ @decorators.cachedproperty
+ 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.
+ """
+
+ # pylint: disable=unnecessary-lambda
+ special_attributes = util.lazy_descriptor(lambda: 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, doc=None, lineno=None, col_offset=None, parent=None
+ ):
+ super().__init__(name, doc, lineno, col_offset, parent)
+ self.filled_positionals = len(call.positional_arguments[1:])
+ self.filled_args = call.positional_arguments[1:]
+ self.filled_keywords = call.keyword_arguments
+
+ def infer_call_result(self, caller=None, context=None):
+ if context:
+ 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):
+ 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)
diff --git a/venv/Lib/site-packages/astroid/protocols.py b/venv/Lib/site-packages/astroid/protocols.py
new file mode 100644
index 0000000..c1825f1
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/protocols.py
@@ -0,0 +1,766 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2017-2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 rr- <rr-@sakuya.pl>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""this module contains a set of functions to handle python protocols for nodes
+where it makes sense.
+"""
+
+import collections
+import operator as operator_mod
+
+import itertools
+
+from astroid import Store
+from astroid import arguments
+from astroid import bases
+from astroid import context as contextmod
+from astroid import exceptions
+from astroid import decorators
+from astroid import node_classes
+from astroid import helpers
+from astroid import nodes
+from astroid import util
+
+raw_building = util.lazy_import("raw_building")
+objects = util.lazy_import("objects")
+
+
+def _reflected_name(name):
+ return "__r" + name[2:]
+
+
+def _augmented_name(name):
+ return "__i" + name[2:]
+
+
+_CONTEXTLIB_MGR = "contextlib.contextmanager"
+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()
+}
+
+UNARY_OP_METHOD = {
+ "+": "__pos__",
+ "-": "__neg__",
+ "~": "__invert__",
+ "not": None, # XXX not '__nonzero__'
+}
+_UNARY_OPERATORS = {
+ "+": operator_mod.pos,
+ "-": operator_mod.neg,
+ "~": operator_mod.invert,
+ "not": operator_mod.not_,
+}
+
+
+def _infer_unary_op(obj, op):
+ func = _UNARY_OPERATORS[op]
+ value = func(obj)
+ return nodes.const_factory(value)
+
+
+nodes.Tuple.infer_unary_op = lambda self, op: _infer_unary_op(tuple(self.elts), op)
+nodes.List.infer_unary_op = lambda self, op: _infer_unary_op(self.elts, op)
+nodes.Set.infer_unary_op = lambda self, op: _infer_unary_op(set(self.elts), op)
+nodes.Const.infer_unary_op = lambda self, op: _infer_unary_op(self.value, op)
+nodes.Dict.infer_unary_op = lambda self, op: _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, opnode, operator, other, context, _):
+ not_implemented = nodes.Const(NotImplemented)
+ if isinstance(other, nodes.Const):
+ 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
+
+
+nodes.Const.infer_binary_op = const_infer_binary_op
+
+
+def _multiply_seq_by_int(self, opnode, other, context):
+ node = self.__class__(parent=opnode)
+ filtered_elts = (
+ helpers.safe_infer(elt, context) or util.Uninferable
+ for elt in self.elts
+ if elt is not util.Uninferable
+ )
+ node.elts = list(filtered_elts) * other.value
+ return node
+
+
+def _filter_uninferable_nodes(elts, context):
+ for elt in elts:
+ if elt is util.Uninferable:
+ yield nodes.Unknown()
+ else:
+ for inferred in elt.infer(context):
+ if inferred is not util.Uninferable:
+ yield inferred
+ else:
+ yield nodes.Unknown()
+
+
+@decorators.yes_if_nothing_inferred
+def tl_infer_binary_op(self, opnode, operator, other, context, method):
+ 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, 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
+ else:
+ yield _multiply_seq_by_int(self, opnode, as_index, context)
+ else:
+ yield not_implemented
+
+
+nodes.Tuple.infer_binary_op = tl_infer_binary_op
+nodes.List.infer_binary_op = tl_infer_binary_op
+
+
+@decorators.yes_if_nothing_inferred
+def instance_class_infer_binary_op(self, opnode, operator, other, context, method):
+ return method.infer_call_result(self, context)
+
+
+bases.Instance.infer_binary_op = instance_class_infer_binary_op
+nodes.ClassDef.infer_binary_op = instance_class_infer_binary_op
+
+
+# assignment ##################################################################
+
+"""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 part is util.Uninferable:
+ continue
+ if not hasattr(part, "itered"):
+ continue
+ try:
+ itered = part.itered()
+ except TypeError:
+ continue
+ for stmt in itered:
+ index_node = nodes.Const(index)
+ try:
+ assigned = stmt.getitem(index_node, context)
+ except (
+ AttributeError,
+ exceptions.AstroidTypeError,
+ exceptions.AstroidIndexError,
+ ):
+ continue
+ if not assign_path:
+ # we achieved to resolved the assignment path,
+ # don't infer the last part
+ yield assigned
+ elif assigned is util.Uninferable:
+ 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 exceptions.InferenceError:
+ break
+
+
+@decorators.raise_if_nothing_inferred
+def for_assigned_stmts(self, node=None, context=None, assign_path=None):
+ if isinstance(self, nodes.AsyncFor) or getattr(self, "is_async", False):
+ # Skip inferring of async code for now
+ return dict(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 dict(node=self, unknown=node, assign_path=assign_path, context=context)
+
+
+nodes.For.assigned_stmts = for_assigned_stmts
+nodes.Comprehension.assigned_stmts = for_assigned_stmts
+
+
+def sequence_assigned_stmts(self, node=None, context=None, assign_path=None):
+ if assign_path is None:
+ assign_path = []
+ try:
+ index = self.elts.index(node)
+ except ValueError as exc:
+ raise exceptions.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
+ )
+
+
+nodes.Tuple.assigned_stmts = sequence_assigned_stmts
+nodes.List.assigned_stmts = sequence_assigned_stmts
+
+
+def assend_assigned_stmts(self, node=None, context=None, assign_path=None):
+ return self.parent.assigned_stmts(node=self, context=context)
+
+
+nodes.AssignName.assigned_stmts = assend_assigned_stmts
+nodes.AssignAttr.assigned_stmts = assend_assigned_stmts
+
+
+def _arguments_infer_argname(self, name, context):
+ # arguments information may be missing, in which case we can't do anything
+ # more
+ if not (self.args or self.vararg or self.kwarg):
+ yield util.Uninferable
+ return
+ # first argument of instance/class method
+ if self.args and getattr(self.args[0], "name", None) == name:
+ functype = self.parent.type
+ 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 is_metaclass or functype == "classmethod":
+ yield cls
+ return
+ if functype == "method":
+ yield bases.Instance(cls)
+ return
+
+ if context and context.callcontext:
+ 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
+ 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 = contextmod.copy_context(context)
+ yield from self.default_value(name).infer(context)
+ yield util.Uninferable
+ except exceptions.NoDefault:
+ yield util.Uninferable
+
+
+def arguments_assigned_stmts(self, node=None, context=None, assign_path=None):
+ if context.callcontext:
+ # reset call context/name
+ callcontext = context.callcontext
+ context = contextmod.copy_context(context)
+ context.callcontext = None
+ args = arguments.CallSite(callcontext)
+ return args.infer_argument(self.parent, node.name, context)
+ return _arguments_infer_argname(self, node.name, context)
+
+
+nodes.Arguments.assigned_stmts = arguments_assigned_stmts
+
+
+@decorators.raise_if_nothing_inferred
+def assign_assigned_stmts(self, node=None, context=None, assign_path=None):
+ if not assign_path:
+ yield self.value
+ return None
+ yield from _resolve_assignment_parts(
+ self.value.infer(context), assign_path, context
+ )
+
+ return dict(node=self, unknown=node, assign_path=assign_path, context=context)
+
+
+def assign_annassigned_stmts(self, node=None, context=None, assign_path=None):
+ for inferred in assign_assigned_stmts(self, node, context, assign_path):
+ if inferred is None:
+ yield util.Uninferable
+ else:
+ yield inferred
+
+
+nodes.Assign.assigned_stmts = assign_assigned_stmts
+nodes.AnnAssign.assigned_stmts = assign_annassigned_stmts
+nodes.AugAssign.assigned_stmts = assign_assigned_stmts
+
+
+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 (exceptions.AstroidTypeError, exceptions.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 assigned is util.Uninferable:
+ 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 exceptions.InferenceError:
+ return
+
+
+@decorators.raise_if_nothing_inferred
+def excepthandler_assigned_stmts(self, node=None, context=None, assign_path=None):
+ for assigned in node_classes.unpack_infer(self.type):
+ if isinstance(assigned, nodes.ClassDef):
+ assigned = objects.ExceptionInstance(assigned)
+
+ yield assigned
+ return dict(node=self, unknown=node, assign_path=assign_path, context=context)
+
+
+nodes.ExceptHandler.assigned_stmts = excepthandler_assigned_stmts
+
+
+def _infer_context_manager(self, mgr, context):
+ inferred = next(mgr.infer(context=context))
+ if isinstance(inferred, bases.Generator):
+ # Check if it is decorated with contextlib.contextmanager.
+ func = inferred.parent
+ if not func.decorators:
+ raise exceptions.InferenceError(
+ "No decorators found on inferred generator %s", node=func
+ )
+
+ for decorator_node in func.decorators.nodes:
+ decorator = next(decorator_node.infer(context))
+ if isinstance(decorator, nodes.FunctionDef):
+ if decorator.qname() == _CONTEXTLIB_MGR:
+ break
+ else:
+ # It doesn't interest us.
+ raise exceptions.InferenceError(node=func)
+
+ # Get the first yield point. If it has multiple yields,
+ # then a RuntimeError will be raised.
+
+ possible_yield_points = func.nodes_of_class(nodes.Yield)
+ # Ignore yields in nested functions
+ yield_point = next(
+ (node for node in possible_yield_points if node.scope() == func), None
+ )
+ if yield_point:
+ if not yield_point.value:
+ const = nodes.Const(None)
+ const.parent = yield_point
+ const.lineno = yield_point.lineno
+ yield const
+ else:
+ yield from yield_point.value.infer(context=context)
+ elif isinstance(inferred, bases.Instance):
+ try:
+ enter = next(inferred.igetattr("__enter__", context=context))
+ except (exceptions.InferenceError, exceptions.AttributeInferenceError):
+ raise exceptions.InferenceError(node=inferred)
+ if not isinstance(enter, bases.BoundMethod):
+ raise exceptions.InferenceError(node=enter)
+ yield from enter.infer_call_result(self, context)
+ else:
+ raise exceptions.InferenceError(node=mgr)
+
+
+@decorators.raise_if_nothing_inferred
+def with_assigned_stmts(self, node=None, context=None, assign_path=None):
+ """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 exceptions.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 exceptions.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 exceptions.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 dict(node=self, unknown=node, assign_path=assign_path, context=context)
+
+
+nodes.With.assigned_stmts = with_assigned_stmts
+
+
+@decorators.raise_if_nothing_inferred
+def named_expr_assigned_stmts(self, node, context=None, assign_path=None):
+ """Infer names and other nodes from an assignment expression"""
+ if self.target == node:
+ yield from self.value.infer(context=context)
+ else:
+ raise exceptions.InferenceError(
+ "Cannot infer NamedExpr node {node!r}",
+ node=self,
+ assign_path=assign_path,
+ context=context,
+ )
+
+
+nodes.NamedExpr.assigned_stmts = named_expr_assigned_stmts
+
+
+@decorators.yes_if_nothing_inferred
+def starred_assigned_stmts(self, node=None, context=None, assign_path=None):
+ """
+ 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-branches,too-many-statements
+ def _determine_starred_iteration_lookups(starred, target, lookups):
+ # 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 exceptions.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 = contextmod.InferenceContext()
+
+ if isinstance(stmt, nodes.Assign):
+ value = stmt.value
+ lhs = stmt.targets[0]
+
+ if sum(1 for _ in lhs.nodes_of_class(nodes.Starred)) > 1:
+ raise exceptions.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 exceptions.InferenceError:
+ yield util.Uninferable
+ return
+ if rhs is util.Uninferable or not hasattr(rhs, "itered"):
+ yield util.Uninferable
+ return
+
+ try:
+ elts = collections.deque(rhs.itered())
+ 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
+ packed = nodes.List(
+ ctx=Store, parent=self, lineno=lhs.lineno, col_offset=lhs.col_offset
+ )
+ packed.postinit(elts=elts)
+ yield packed
+ break
+
+ if isinstance(stmt, nodes.For):
+ try:
+ inferred_iterable = next(stmt.iter.infer(context=context))
+ except exceptions.InferenceError:
+ yield util.Uninferable
+ return
+ if inferred_iterable is util.Uninferable or not hasattr(
+ inferred_iterable, "itered"
+ ):
+ yield util.Uninferable
+ return
+ try:
+ itered = inferred_iterable.itered()
+ except TypeError:
+ yield util.Uninferable
+ return
+
+ target = stmt.target
+
+ if not isinstance(target, nodes.Tuple):
+ raise exceptions.InferenceError(
+ "Could not make sense of this, the target must be a tuple",
+ context=context,
+ )
+
+ lookups = []
+ _determine_starred_iteration_lookups(self, target, lookups)
+ if not lookups:
+ raise exceptions.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),
+ )
+ lookups[-1] = 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 lookup in lookups:
+ if not hasattr(element, "itered"):
+ break
+ if not isinstance(lookup, slice):
+ # Grab just the index, not the whole length
+ lookup = lookup[0]
+ try:
+ itered_inner_element = element.itered()
+ element = itered_inner_element[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=Store, parent=self, lineno=self.lineno, col_offset=self.col_offset
+ )
+ unpacked.postinit(elts=found_element or [])
+ yield unpacked
+ return
+
+ yield util.Uninferable
+
+
+nodes.Starred.assigned_stmts = starred_assigned_stmts
diff --git a/venv/Lib/site-packages/astroid/raw_building.py b/venv/Lib/site-packages/astroid/raw_building.py
new file mode 100644
index 0000000..d94f924
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/raw_building.py
@@ -0,0 +1,468 @@
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Google, Inc.
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Ovidiu Sabou <ovidiu@sabou.org>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""this module contains a set of functions to create astroid trees from scratch
+(build_* functions) or from living object (object_build_* functions)
+"""
+
+import builtins
+import inspect
+import os
+import sys
+import types
+
+from astroid import bases
+from astroid import manager
+from astroid import node_classes
+from astroid import nodes
+
+
+MANAGER = manager.AstroidManager()
+# the keys of CONST_CLS eg python builtin types
+
+_CONSTANTS = tuple(node_classes.CONST_CLS)
+_BUILTINS = vars(builtins)
+TYPE_NONE = type(None)
+TYPE_NOTIMPLEMENTED = type(NotImplemented)
+TYPE_ELLIPSIS = type(...)
+
+
+def _io_discrepancy(member):
+ # _io module names itself `io`: http://bugs.python.org/issue18602
+ member_self = getattr(member, "__self__", None)
+ return (
+ member_self
+ and inspect.ismodule(member_self)
+ and member_self.__name__ == "_io"
+ and member.__module__ == "io"
+ )
+
+
+def _attach_local_node(parent, node, name):
+ node.name = name # needed by add_local_node
+ parent.add_local_node(node)
+
+
+def _add_dunder_class(func, member):
+ """Add a __class__ member to the given func node, if we can determine it."""
+ python_cls = member.__class__
+ cls_name = getattr(python_cls, "__name__", None)
+ if not cls_name:
+ return
+ cls_bases = [ancestor.__name__ for ancestor in python_cls.__bases__]
+ ast_klass = build_class(cls_name, cls_bases, python_cls.__doc__)
+ func.instance_attrs["__class__"] = [ast_klass]
+
+
+_marker = object()
+
+
+def attach_dummy_node(node, name, runtime_object=_marker):
+ """create a dummy node and register it in the locals of the given
+ node with the specified name
+ """
+ enode = nodes.EmptyNode()
+ enode.object = runtime_object
+ _attach_local_node(node, enode, name)
+
+
+def _has_underlying_object(self):
+ return self.object is not None and self.object is not _marker
+
+
+nodes.EmptyNode.has_underlying_object = _has_underlying_object
+
+
+def attach_const_node(node, name, value):
+ """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, membername):
+ """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, doc=None):
+ """create and initialize an astroid Module node"""
+ node = nodes.Module(name, doc, pure_python=False)
+ node.package = False
+ node.parent = None
+ return node
+
+
+def build_class(name, basenames=(), doc=None):
+ """create and initialize an astroid ClassDef node"""
+ node = nodes.ClassDef(name, doc)
+ for base in basenames:
+ basenode = nodes.Name()
+ basenode.name = base
+ node.bases.append(basenode)
+ basenode.parent = node
+ return node
+
+
+def build_function(name, args=None, posonlyargs=None, defaults=None, doc=None):
+ """create and initialize an astroid FunctionDef node"""
+ args, defaults, posonlyargs = args or [], defaults or [], posonlyargs or []
+ # first argument is now a list of decorators
+ func = nodes.FunctionDef(name, doc)
+ func.args = argsnode = nodes.Arguments()
+ argsnode.args = []
+ argsnode.posonlyargs = []
+ for arg in args:
+ argsnode.args.append(nodes.Name())
+ argsnode.args[-1].name = arg
+ argsnode.args[-1].parent = argsnode
+ for arg in posonlyargs:
+ argsnode.posonlyargs.append(nodes.Name())
+ argsnode.posonlyargs[-1].name = arg
+ argsnode.posonlyargs[-1].parent = argsnode
+ argsnode.defaults = []
+ for default in defaults:
+ argsnode.defaults.append(nodes.const_factory(default))
+ argsnode.defaults[-1].parent = argsnode
+ argsnode.kwarg = None
+ argsnode.vararg = None
+ argsnode.parent = func
+ if args:
+ register_arguments(func)
+ return func
+
+
+def build_from_import(fromname, names):
+ """create and initialize an astroid ImportFrom import statement"""
+ return nodes.ImportFrom(fromname, [(name, None) for name in names])
+
+
+def register_arguments(func, args=None):
+ """add given arguments to local
+
+ args is a list that may contains nested lists
+ (i.e. def func(a, (b, c, d)): ...)
+ """
+ if args is None:
+ args = func.args.args
+ if func.args.vararg:
+ func.set_local(func.args.vararg, func.args)
+ if func.args.kwarg:
+ func.set_local(func.args.kwarg, func.args)
+ for arg in args:
+ if isinstance(arg, nodes.Name):
+ func.set_local(arg.name, arg)
+ else:
+ register_arguments(func, arg.elts)
+
+
+def object_build_class(node, member, localname):
+ """create astroid for a living class object"""
+ basenames = [base.__name__ for base in member.__bases__]
+ return _base_class_object_build(node, member, basenames, localname=localname)
+
+
+def object_build_function(node, member, localname):
+ """create astroid for a living function object"""
+ signature = inspect.signature(member)
+ args = []
+ defaults = []
+ posonlyargs = []
+ 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)
+ if param.default is not inspect._empty:
+ defaults.append(param.default)
+ func = build_function(
+ getattr(member, "__name__", None) or localname,
+ args,
+ posonlyargs,
+ defaults,
+ member.__doc__,
+ )
+ node.add_local_node(func, localname)
+
+
+def object_build_datadescriptor(node, member, name):
+ """create astroid for a living data descriptor object"""
+ return _base_class_object_build(node, member, [], name)
+
+
+def object_build_methoddescriptor(node, member, localname):
+ """create astroid for a living method descriptor object"""
+ # FIXME get arguments ?
+ func = build_function(
+ getattr(member, "__name__", None) or localname, doc=member.__doc__
+ )
+ # set node's arguments to None to notice that we have no information, not
+ # and empty argument list
+ func.args.args = None
+ node.add_local_node(func, localname)
+ _add_dunder_class(func, member)
+
+
+def _base_class_object_build(node, member, basenames, name=None, localname=None):
+ """create astroid for a living class object, with a given set of base names
+ (e.g. ancestors)
+ """
+ klass = build_class(
+ name or getattr(member, "__name__", None) or localname,
+ basenames,
+ member.__doc__,
+ )
+ klass._newstyle = isinstance(member, type)
+ node.add_local_node(klass, localname)
+ try:
+ # limit the instantiation trick since it's too dangerous
+ # (such as infinite test execution...)
+ # this at least resolves common case such as Exception.args,
+ # OSError.errno
+ if issubclass(member, Exception):
+ instdict = member().__dict__
+ else:
+ raise TypeError
+ except TypeError:
+ pass
+ else:
+ for item_name, obj in instdict.items():
+ valnode = nodes.EmptyNode()
+ valnode.object = obj
+ valnode.parent = klass
+ valnode.lineno = 1
+ klass.instance_attrs[item_name] = [valnode]
+ return klass
+
+
+def _build_from_function(node, name, member, module):
+ # verify this is not an imported function
+ try:
+ code = member.__code__
+ except AttributeError:
+ # Some implementations don't provide the code object,
+ # such as Jython.
+ code = None
+ filename = getattr(code, "co_filename", None)
+ if filename is None:
+ assert isinstance(member, object)
+ object_build_methoddescriptor(node, member, name)
+ elif filename != getattr(module, "__file__", None):
+ attach_dummy_node(node, name, member)
+ else:
+ object_build_function(node, member, name)
+
+
+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.
+ """
+
+ def __init__(self):
+ self._done = {}
+ self._module = None
+
+ def inspect_build(self, module, modname=None, path=None):
+ """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)
+ node.file = node.path = os.path.abspath(path) if path else path
+ node.name = modname
+ MANAGER.cache_module(node)
+ node.package = hasattr(module, "__path__")
+ self._done = {}
+ self.object_build(node, module)
+ return node
+
+ def object_build(self, node, obj):
+ """recursive method which create a partial ast from real objects
+ (only function, class, and method are handled)
+ """
+ if obj in self._done:
+ return self._done[obj]
+ self._done[obj] = node
+ for name in dir(obj):
+ try:
+ member = getattr(obj, name)
+ except AttributeError:
+ # damned ExtensionClass.Base, I know you're there !
+ attach_dummy_node(node, name)
+ continue
+ if inspect.ismethod(member):
+ member = member.__func__
+ if inspect.isfunction(member):
+ _build_from_function(node, name, member, self._module)
+ elif inspect.isbuiltin(member):
+ if not _io_discrepancy(member) and self.imported_member(
+ node, member, name
+ ):
+ continue
+ object_build_methoddescriptor(node, member, name)
+ elif inspect.isclass(member):
+ if self.imported_member(node, member, name):
+ continue
+ if member in self._done:
+ class_node = self._done[member]
+ if class_node not in node.locals.get(name, ()):
+ node.add_local_node(class_node, name)
+ else:
+ class_node = object_build_class(node, member, name)
+ # recursion
+ self.object_build(class_node, member)
+ if name == "__class__" and class_node.parent is None:
+ class_node.parent = self._done[self._module]
+ elif inspect.ismethoddescriptor(member):
+ assert isinstance(member, object)
+ object_build_methoddescriptor(node, member, name)
+ elif inspect.isdatadescriptor(member):
+ assert isinstance(member, object)
+ object_build_datadescriptor(node, member, name)
+ elif isinstance(member, _CONSTANTS):
+ attach_const_node(node, name, member)
+ elif inspect.isroutine(member):
+ # This should be called for Jython, where some builtin
+ # methods aren't caught by isbuiltin branch.
+ _build_from_function(node, name, member, self._module)
+ else:
+ # create an empty node so that the name is actually defined
+ attach_dummy_node(node, name, member)
+ return None
+
+ def imported_member(self, node, member, name):
+ """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
+
+ real_name = {"gtk": "gtk_gtk", "_io": "io"}.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:
+ getattr(sys.modules[modname], name)
+ except (KeyError, AttributeError):
+ attach_dummy_node(node, name, member)
+ else:
+ attach_import_node(node, modname, name)
+ return True
+ return False
+
+
+### astroid bootstrapping ######################################################
+
+_CONST_PROXY = {}
+
+# TODO : find a nicer way to handle this situation;
+def _set_proxied(const):
+ return _CONST_PROXY[const.value.__class__]
+
+
+def _astroid_bootstrapping():
+ """astroid bootstrapping the builtins module"""
+ # this boot strapping is necessary since we need the Const nodes to
+ # inspect_build builtins, and then we can proxy Const
+ builder = InspectBuilder()
+ astroid_builtin = builder.inspect_build(builtins)
+
+ # pylint: disable=redefined-outer-name
+ for cls, node_cls in node_classes.CONST_CLS.items():
+ if cls is TYPE_NONE:
+ proxy = build_class("NoneType")
+ proxy.parent = astroid_builtin
+ elif cls is TYPE_NOTIMPLEMENTED:
+ proxy = build_class("NotImplementedType")
+ proxy.parent = astroid_builtin
+ elif cls is TYPE_ELLIPSIS:
+ proxy = build_class("Ellipsis")
+ proxy.parent = astroid_builtin
+ else:
+ proxy = astroid_builtin.getattr(cls.__name__)[0]
+ 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__, types.GeneratorType.__doc__
+ )
+ _GeneratorType.parent = astroid_builtin
+ bases.Generator._proxied = _GeneratorType
+ builder.object_build(bases.Generator._proxied, types.GeneratorType)
+
+ if hasattr(types, "AsyncGeneratorType"):
+ # pylint: disable=no-member; AsyncGeneratorType
+ _AsyncGeneratorType = nodes.ClassDef(
+ types.AsyncGeneratorType.__name__, types.AsyncGeneratorType.__doc__
+ )
+ _AsyncGeneratorType.parent = astroid_builtin
+ bases.AsyncGenerator._proxied = _AsyncGeneratorType
+ builder.object_build(bases.AsyncGenerator._proxied, types.AsyncGeneratorType)
+ 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:
+ cls = nodes.ClassDef(_type.__name__, _type.__doc__)
+ cls.parent = astroid_builtin
+ builder.object_build(cls, _type)
+ astroid_builtin[_type.__name__] = cls
+
+
+_astroid_bootstrapping()
diff --git a/venv/Lib/site-packages/astroid/rebuilder.py b/venv/Lib/site-packages/astroid/rebuilder.py
new file mode 100644
index 0000000..fb78f7b
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/rebuilder.py
@@ -0,0 +1,1090 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2014 Alexander Presnyakov <flagist0@gmail.com>
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2016-2017 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2016 Jared Garst <jgarst@users.noreply.github.com>
+# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 rr- <rr-@sakuya.pl>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""this module contains utilities for rebuilding a _ast tree in
+order to get a single Astroid representation
+"""
+
+import sys
+
+import astroid
+from astroid._ast import _parse, _get_parser_module, parse_function_type_comment
+from astroid import nodes
+
+
+CONST_NAME_TRANSFORMS = {"None": None, "True": True, "False": False}
+
+REDIRECT = {
+ "arguments": "Arguments",
+ "comprehension": "Comprehension",
+ "ListCompFor": "Comprehension",
+ "GenExprFor": "Comprehension",
+ "excepthandler": "ExceptHandler",
+ "keyword": "Keyword",
+}
+PY37 = sys.version_info >= (3, 7)
+PY38 = sys.version_info >= (3, 8)
+
+
+def _binary_operators_from_module(module):
+ binary_operators = {
+ module.Add: "+",
+ module.BitAnd: "&",
+ module.BitOr: "|",
+ module.BitXor: "^",
+ module.Div: "/",
+ module.FloorDiv: "//",
+ module.MatMult: "@",
+ module.Mod: "%",
+ module.Mult: "*",
+ module.Pow: "**",
+ module.Sub: "-",
+ module.LShift: "<<",
+ module.RShift: ">>",
+ }
+ return binary_operators
+
+
+def _bool_operators_from_module(module):
+ return {module.And: "and", module.Or: "or"}
+
+
+def _unary_operators_from_module(module):
+ return {module.UAdd: "+", module.USub: "-", module.Not: "not", module.Invert: "~"}
+
+
+def _compare_operators_from_module(module):
+ return {
+ module.Eq: "==",
+ module.Gt: ">",
+ module.GtE: ">=",
+ module.In: "in",
+ module.Is: "is",
+ module.IsNot: "is not",
+ module.Lt: "<",
+ module.LtE: "<=",
+ module.NotEq: "!=",
+ module.NotIn: "not in",
+ }
+
+
+def _contexts_from_module(module):
+ return {
+ module.Load: astroid.Load,
+ module.Store: astroid.Store,
+ module.Del: astroid.Del,
+ module.Param: astroid.Store,
+ }
+
+
+def _visit_or_none(node, attr, visitor, parent, visit="visit", **kws):
+ """If the given node has an attribute, visits the attribute, and
+ otherwise returns None.
+
+ """
+ value = getattr(node, attr, None)
+ if value:
+ return getattr(visitor, visit)(value, parent, **kws)
+
+ return None
+
+
+class TreeRebuilder:
+ """Rebuilds the _ast tree to become an Astroid tree"""
+
+ def __init__(self, manager, parse_python_two: bool = False):
+ self._manager = manager
+ self._global_names = []
+ self._import_from_nodes = []
+ self._delayed_assattr = []
+ self._visit_meths = {}
+
+ # Configure the right classes for the right module
+ self._parser_module = _get_parser_module(parse_python_two=parse_python_two)
+ self._unary_op_classes = _unary_operators_from_module(self._parser_module)
+ self._cmp_op_classes = _compare_operators_from_module(self._parser_module)
+ self._bool_op_classes = _bool_operators_from_module(self._parser_module)
+ self._bin_op_classes = _binary_operators_from_module(self._parser_module)
+ self._context_classes = _contexts_from_module(self._parser_module)
+
+ def _get_doc(self, node):
+ try:
+ if PY37 and hasattr(node, "docstring"):
+ doc = node.docstring
+ return node, doc
+ if node.body and isinstance(node.body[0], self._parser_module.Expr):
+
+ first_value = node.body[0].value
+ if isinstance(first_value, self._parser_module.Str) or (
+ PY38
+ and isinstance(first_value, self._parser_module.Constant)
+ and isinstance(first_value.value, str)
+ ):
+ doc = first_value.value if PY38 else first_value.s
+ node.body = node.body[1:]
+ return node, doc
+ except IndexError:
+ pass # ast built from scratch
+ return node, None
+
+ def _get_context(self, node):
+ return self._context_classes.get(type(node.ctx), astroid.Load)
+
+ def visit_module(self, node, modname, modpath, package):
+ """visit a Module node by returning a fresh instance of it"""
+ node, doc = self._get_doc(node)
+ newnode = nodes.Module(
+ name=modname,
+ doc=doc,
+ file=modpath,
+ path=[modpath],
+ package=package,
+ parent=None,
+ )
+ newnode.postinit([self.visit(child, newnode) for child in node.body])
+ return newnode
+
+ def visit(self, node, parent):
+ 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, name=None):
+ """save assignement 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:
+ node.parent.set_local(node.name, node)
+
+ def visit_arguments(self, node, parent):
+ """visit an Arguments node by returning a fresh instance of it"""
+ vararg, kwarg = node.vararg, node.kwarg
+ newnode = nodes.Arguments(
+ vararg.arg if vararg else None, kwarg.arg if kwarg else None, parent
+ )
+ args = [self.visit(child, newnode) for child in node.args]
+ defaults = [self.visit(child, newnode) for child in node.defaults]
+ varargannotation = None
+ kwargannotation = None
+ posonlyargs = []
+ # change added in 82732 (7c5c678e4164), vararg and kwarg
+ # are instances of `_ast.arg`, not strings
+ if vararg:
+ if node.vararg.annotation:
+ varargannotation = self.visit(node.vararg.annotation, newnode)
+ vararg = vararg.arg
+ if kwarg:
+ if node.kwarg.annotation:
+ kwargannotation = self.visit(node.kwarg.annotation, newnode)
+ kwarg = kwarg.arg
+ kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs]
+ kw_defaults = [
+ self.visit(child, newnode) if child else None for child in node.kw_defaults
+ ]
+ annotations = [
+ self.visit(arg.annotation, newnode) if arg.annotation else None
+ for arg in node.args
+ ]
+ kwonlyargs_annotations = [
+ self.visit(arg.annotation, newnode) if arg.annotation else None
+ for arg in node.kwonlyargs
+ ]
+
+ posonlyargs_annotations = []
+ if PY38:
+ posonlyargs = [self.visit(child, newnode) for child in node.posonlyargs]
+ posonlyargs_annotations = [
+ self.visit(arg.annotation, newnode) if arg.annotation else None
+ for arg in node.posonlyargs
+ ]
+ type_comment_args = [
+ self.check_type_comment(child, parent=newnode) for child in node.args
+ ]
+
+ 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,
+ )
+ # save argument names in locals:
+ if vararg:
+ newnode.parent.set_local(vararg, newnode)
+ if kwarg:
+ newnode.parent.set_local(kwarg, newnode)
+ return newnode
+
+ def visit_assert(self, node, parent):
+ """visit a Assert node by returning a fresh instance of it"""
+ newnode = nodes.Assert(node.lineno, node.col_offset, parent)
+ if node.msg:
+ msg = self.visit(node.msg, newnode)
+ else:
+ msg = None
+ newnode.postinit(self.visit(node.test, newnode), msg)
+ return newnode
+
+ def check_type_comment(self, node, parent):
+ type_comment = getattr(node, "type_comment", None)
+ if not type_comment:
+ return None
+
+ try:
+ type_comment_ast = _parse(type_comment)
+ except SyntaxError:
+ # Invalid type comment, just skip it.
+ 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):
+ type_comment = getattr(node, "type_comment", None)
+ if not type_comment:
+ return None
+
+ try:
+ type_comment_ast = parse_function_type_comment(type_comment)
+ except SyntaxError:
+ # Invalid type comment, just skip it.
+ return None
+
+ returns = None
+ argtypes = [
+ self.visit(elem, node) for elem in (type_comment_ast.argtypes or [])
+ ]
+ if type_comment_ast.returns:
+ returns = self.visit(type_comment_ast.returns, node)
+
+ return returns, argtypes
+
+ def visit_assign(self, node, parent):
+ """visit a Assign node by returning a fresh instance of it"""
+ newnode = nodes.Assign(node.lineno, node.col_offset, 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_assignname(self, node, parent, node_name=None):
+ """visit a node and return a AssignName node"""
+ newnode = nodes.AssignName(
+ node_name,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+ self._save_assignment(newnode)
+ return newnode
+
+ def visit_augassign(self, node, parent):
+ """visit a AugAssign node by returning a fresh instance of it"""
+ newnode = nodes.AugAssign(
+ self._bin_op_classes[type(node.op)] + "=",
+ node.lineno,
+ node.col_offset,
+ parent,
+ )
+ newnode.postinit(
+ self.visit(node.target, newnode), self.visit(node.value, newnode)
+ )
+ return newnode
+
+ def visit_repr(self, node, parent):
+ """visit a Backquote node by returning a fresh instance of it"""
+ newnode = nodes.Repr(node.lineno, node.col_offset, parent)
+ newnode.postinit(self.visit(node.value, newnode))
+ return newnode
+
+ def visit_binop(self, node, parent):
+ """visit a BinOp node by returning a fresh instance of it"""
+ newnode = nodes.BinOp(
+ self._bin_op_classes[type(node.op)], node.lineno, node.col_offset, parent
+ )
+ newnode.postinit(
+ self.visit(node.left, newnode), self.visit(node.right, newnode)
+ )
+ return newnode
+
+ def visit_boolop(self, node, parent):
+ """visit a BoolOp node by returning a fresh instance of it"""
+ newnode = nodes.BoolOp(
+ self._bool_op_classes[type(node.op)], node.lineno, node.col_offset, parent
+ )
+ newnode.postinit([self.visit(child, newnode) for child in node.values])
+ return newnode
+
+ def visit_break(self, node, parent):
+ """visit a Break node by returning a fresh instance of it"""
+ return nodes.Break(
+ getattr(node, "lineno", None), getattr(node, "col_offset", None), parent
+ )
+
+ def visit_call(self, node, parent):
+ """visit a CallFunc node by returning a fresh instance of it"""
+ newnode = nodes.Call(node.lineno, node.col_offset, parent)
+ starargs = _visit_or_none(node, "starargs", self, newnode)
+ kwargs = _visit_or_none(node, "kwargs", self, newnode)
+ args = [self.visit(child, newnode) for child in node.args]
+
+ if node.keywords:
+ keywords = [self.visit(child, newnode) for child in node.keywords]
+ else:
+ keywords = None
+ if starargs:
+ new_starargs = nodes.Starred(
+ col_offset=starargs.col_offset,
+ lineno=starargs.lineno,
+ parent=starargs.parent,
+ )
+ new_starargs.postinit(value=starargs)
+ args.append(new_starargs)
+ if kwargs:
+ new_kwargs = nodes.Keyword(
+ arg=None,
+ col_offset=kwargs.col_offset,
+ lineno=kwargs.lineno,
+ parent=kwargs.parent,
+ )
+ new_kwargs.postinit(value=kwargs)
+ if keywords:
+ keywords.append(new_kwargs)
+ else:
+ keywords = [new_kwargs]
+
+ newnode.postinit(self.visit(node.func, newnode), args, keywords)
+ return newnode
+
+ def visit_classdef(self, node, parent, newstyle=None):
+ """visit a ClassDef node to become astroid"""
+ node, doc = self._get_doc(node)
+ newnode = nodes.ClassDef(node.name, doc, node.lineno, node.col_offset, parent)
+ metaclass = None
+ for keyword in node.keywords:
+ if keyword.arg == "metaclass":
+ metaclass = self.visit(keyword, newnode).value
+ break
+ if node.decorator_list:
+ decorators = self.visit_decorators(node, newnode)
+ else:
+ decorators = None
+ 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"
+ ],
+ )
+ return newnode
+
+ def visit_const(self, node, parent):
+ """visit a Const node by returning a fresh instance of it"""
+ return nodes.Const(
+ node.value,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+
+ def visit_continue(self, node, parent):
+ """visit a Continue node by returning a fresh instance of it"""
+ return nodes.Continue(
+ getattr(node, "lineno", None), getattr(node, "col_offset", None), parent
+ )
+
+ def visit_compare(self, node, parent):
+ """visit a Compare node by returning a fresh instance of it"""
+ newnode = nodes.Compare(node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ self.visit(node.left, newnode),
+ [
+ (self._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, parent):
+ """visit a Comprehension node by returning a fresh instance of it"""
+ newnode = nodes.Comprehension(parent)
+ newnode.postinit(
+ self.visit(node.target, newnode),
+ self.visit(node.iter, newnode),
+ [self.visit(child, newnode) for child in node.ifs],
+ getattr(node, "is_async", None),
+ )
+ return newnode
+
+ def visit_decorators(self, node, parent):
+ """visit a Decorators node by returning a fresh instance of it"""
+ # /!\ node is actually a _ast.FunctionDef node while
+ # parent is an astroid.nodes.FunctionDef node
+ if PY38:
+ # Set the line number of the first decorator for Python 3.8+.
+ lineno = node.decorator_list[0].lineno
+ else:
+ lineno = node.lineno
+ newnode = nodes.Decorators(lineno, node.col_offset, parent)
+ newnode.postinit([self.visit(child, newnode) for child in node.decorator_list])
+ return newnode
+
+ def visit_delete(self, node, parent):
+ """visit a Delete node by returning a fresh instance of it"""
+ newnode = nodes.Delete(node.lineno, node.col_offset, parent)
+ newnode.postinit([self.visit(child, newnode) for child in node.targets])
+ return newnode
+
+ def _visit_dict_items(self, node, parent, newnode):
+ for key, value in zip(node.keys, node.values):
+ rebuilt_value = self.visit(value, newnode)
+ if not key:
+ # Python 3.5 and extended unpacking
+ rebuilt_key = nodes.DictUnpack(
+ rebuilt_value.lineno, rebuilt_value.col_offset, parent
+ )
+ else:
+ rebuilt_key = self.visit(key, newnode)
+ yield rebuilt_key, rebuilt_value
+
+ def visit_dict(self, node, parent):
+ """visit a Dict node by returning a fresh instance of it"""
+ newnode = nodes.Dict(node.lineno, node.col_offset, parent)
+ items = list(self._visit_dict_items(node, parent, newnode))
+ newnode.postinit(items)
+ return newnode
+
+ def visit_dictcomp(self, node, parent):
+ """visit a DictComp node by returning a fresh instance of it"""
+ newnode = nodes.DictComp(node.lineno, node.col_offset, 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, parent):
+ """visit a Expr node by returning a fresh instance of it"""
+ newnode = nodes.Expr(node.lineno, node.col_offset, parent)
+ newnode.postinit(self.visit(node.value, newnode))
+ return newnode
+
+ # Not used in Python 3.8+.
+ def visit_ellipsis(self, node, parent):
+ """visit an Ellipsis node by returning a fresh instance of it"""
+ return nodes.Ellipsis(
+ getattr(node, "lineno", None), getattr(node, "col_offset", None), parent
+ )
+
+ def visit_emptynode(self, node, parent):
+ """visit an EmptyNode node by returning a fresh instance of it"""
+ return nodes.EmptyNode(
+ getattr(node, "lineno", None), getattr(node, "col_offset", None), parent
+ )
+
+ def visit_excepthandler(self, node, parent):
+ """visit an ExceptHandler node by returning a fresh instance of it"""
+ newnode = nodes.ExceptHandler(node.lineno, node.col_offset, parent)
+ # /!\ node.name can be a tuple
+ newnode.postinit(
+ _visit_or_none(node, "type", self, newnode),
+ _visit_or_none(node, "name", self, newnode),
+ [self.visit(child, newnode) for child in node.body],
+ )
+ return newnode
+
+ def visit_exec(self, node, parent):
+ """visit an Exec node by returning a fresh instance of it"""
+ newnode = nodes.Exec(node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ self.visit(node.body, newnode),
+ _visit_or_none(node, "globals", self, newnode),
+ _visit_or_none(node, "locals", self, newnode),
+ )
+ return newnode
+
+ # Not used in Python 3.8+.
+ def visit_extslice(self, node, parent):
+ """visit an ExtSlice node by returning a fresh instance of it"""
+ newnode = nodes.ExtSlice(parent=parent)
+ newnode.postinit([self.visit(dim, newnode) for dim in node.dims])
+ return newnode
+
+ def _visit_for(self, cls, node, parent):
+ """visit a For node by returning a fresh instance of it"""
+ newnode = cls(node.lineno, node.col_offset, 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, parent):
+ return self._visit_for(nodes.For, node, parent)
+
+ def visit_importfrom(self, node, parent):
+ """visit an ImportFrom node by returning a fresh instance of it"""
+ names = [(alias.name, alias.asname) for alias in node.names]
+ newnode = nodes.ImportFrom(
+ node.module or "",
+ names,
+ node.level or None,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+ # store From names to add them to locals after building
+ self._import_from_nodes.append(newnode)
+ return newnode
+
+ def _visit_functiondef(self, cls, node, parent):
+ """visit an FunctionDef node to become astroid"""
+ self._global_names.append({})
+ node, doc = self._get_doc(node)
+
+ lineno = node.lineno
+ if PY38 and 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(node.name, doc, lineno, node.col_offset, parent)
+ if node.decorator_list:
+ decorators = self.visit_decorators(node, newnode)
+ else:
+ decorators = 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)
+ 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,
+ )
+ self._global_names.pop()
+ return newnode
+
+ def visit_functiondef(self, node, parent):
+ return self._visit_functiondef(nodes.FunctionDef, node, parent)
+
+ def visit_generatorexp(self, node, parent):
+ """visit a GeneratorExp node by returning a fresh instance of it"""
+ newnode = nodes.GeneratorExp(node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ self.visit(node.elt, newnode),
+ [self.visit(child, newnode) for child in node.generators],
+ )
+ return newnode
+
+ def visit_attribute(self, node, parent):
+ """visit an Attribute node by returning a fresh instance of it"""
+ context = self._get_context(node)
+ if context == astroid.Del:
+ # FIXME : maybe we should reintroduce and visit_delattr ?
+ # for instance, deactivating assign_ctx
+ newnode = nodes.DelAttr(node.attr, node.lineno, node.col_offset, parent)
+ elif context == astroid.Store:
+ newnode = nodes.AssignAttr(node.attr, node.lineno, node.col_offset, parent)
+ # Prohibit a local save if we are in an ExceptHandler.
+ if not isinstance(parent, astroid.ExceptHandler):
+ self._delayed_assattr.append(newnode)
+ else:
+ newnode = nodes.Attribute(node.attr, node.lineno, node.col_offset, parent)
+ newnode.postinit(self.visit(node.value, newnode))
+ return newnode
+
+ def visit_global(self, node, parent):
+ """visit a Global node to become astroid"""
+ newnode = nodes.Global(
+ node.names,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ 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, parent):
+ """visit an If node by returning a fresh instance of it"""
+ newnode = nodes.If(node.lineno, node.col_offset, 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, parent):
+ """visit a IfExp node by returning a fresh instance of it"""
+ newnode = nodes.IfExp(node.lineno, node.col_offset, 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, parent):
+ """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,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+ # save import names in parent's locals:
+ for (name, asname) in newnode.names:
+ name = asname or name
+ parent.set_local(name.split(".")[0], newnode)
+ return newnode
+
+ # Not used in Python 3.8+.
+ def visit_index(self, node, parent):
+ """visit a Index node by returning a fresh instance of it"""
+ newnode = nodes.Index(parent=parent)
+ newnode.postinit(self.visit(node.value, newnode))
+ return newnode
+
+ def visit_keyword(self, node, parent):
+ """visit a Keyword node by returning a fresh instance of it"""
+ newnode = nodes.Keyword(node.arg, parent=parent)
+ newnode.postinit(self.visit(node.value, newnode))
+ return newnode
+
+ def visit_lambda(self, node, parent):
+ """visit a Lambda node by returning a fresh instance of it"""
+ newnode = nodes.Lambda(node.lineno, node.col_offset, parent)
+ newnode.postinit(self.visit(node.args, newnode), self.visit(node.body, newnode))
+ return newnode
+
+ def visit_list(self, node, parent):
+ """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, parent=parent
+ )
+ newnode.postinit([self.visit(child, newnode) for child in node.elts])
+ return newnode
+
+ def visit_listcomp(self, node, parent):
+ """visit a ListComp node by returning a fresh instance of it"""
+ newnode = nodes.ListComp(node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ self.visit(node.elt, newnode),
+ [self.visit(child, newnode) for child in node.generators],
+ )
+ return newnode
+
+ def visit_name(self, node, parent):
+ """visit a Name node by returning a fresh instance of it"""
+ context = self._get_context(node)
+ # True and False can be assigned to something in py2x, so we have to
+ # check first the context.
+ if context == astroid.Del:
+ newnode = nodes.DelName(node.id, node.lineno, node.col_offset, parent)
+ elif context == astroid.Store:
+ newnode = nodes.AssignName(node.id, node.lineno, node.col_offset, parent)
+ elif node.id in CONST_NAME_TRANSFORMS:
+ newnode = nodes.Const(
+ CONST_NAME_TRANSFORMS[node.id],
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+ return newnode
+ else:
+ newnode = nodes.Name(node.id, node.lineno, node.col_offset, parent)
+ # XXX REMOVE me :
+ if context in (astroid.Del, astroid.Store): # 'Aug' ??
+ self._save_assignment(newnode)
+ return newnode
+
+ def visit_constant(self, node, parent):
+ """visit a Constant node by returning a fresh instance of Const"""
+ return nodes.Const(
+ node.value,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+
+ # Not used in Python 3.8+.
+ def visit_str(self, node, parent):
+ """visit a String/Bytes node by returning a fresh instance of Const"""
+ return nodes.Const(
+ node.s,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+
+ visit_bytes = visit_str
+
+ # Not used in Python 3.8+.
+ def visit_num(self, node, parent):
+ """visit a Num node by returning a fresh instance of Const"""
+ return nodes.Const(
+ node.n,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+
+ def visit_pass(self, node, parent):
+ """visit a Pass node by returning a fresh instance of it"""
+ return nodes.Pass(node.lineno, node.col_offset, parent)
+
+ def visit_print(self, node, parent):
+ """visit a Print node by returning a fresh instance of it"""
+ newnode = nodes.Print(node.nl, node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ _visit_or_none(node, "dest", self, newnode),
+ [self.visit(child, newnode) for child in node.values],
+ )
+ return newnode
+
+ def visit_raise(self, node, parent):
+ """visit a Raise node by returning a fresh instance of it"""
+ newnode = nodes.Raise(node.lineno, node.col_offset, parent)
+ # pylint: disable=too-many-function-args
+ newnode.postinit(
+ _visit_or_none(node, "type", self, newnode),
+ _visit_or_none(node, "inst", self, newnode),
+ _visit_or_none(node, "tback", self, newnode),
+ )
+ return newnode
+
+ def visit_return(self, node, parent):
+ """visit a Return node by returning a fresh instance of it"""
+ newnode = nodes.Return(node.lineno, node.col_offset, parent)
+ if node.value is not None:
+ newnode.postinit(self.visit(node.value, newnode))
+ return newnode
+
+ def visit_set(self, node, parent):
+ """visit a Set node by returning a fresh instance of it"""
+ newnode = nodes.Set(node.lineno, node.col_offset, parent)
+ newnode.postinit([self.visit(child, newnode) for child in node.elts])
+ return newnode
+
+ def visit_setcomp(self, node, parent):
+ """visit a SetComp node by returning a fresh instance of it"""
+ newnode = nodes.SetComp(node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ self.visit(node.elt, newnode),
+ [self.visit(child, newnode) for child in node.generators],
+ )
+ return newnode
+
+ def visit_slice(self, node, parent):
+ """visit a Slice node by returning a fresh instance of it"""
+ newnode = nodes.Slice(parent=parent)
+ newnode.postinit(
+ _visit_or_none(node, "lower", self, newnode),
+ _visit_or_none(node, "upper", self, newnode),
+ _visit_or_none(node, "step", self, newnode),
+ )
+ return newnode
+
+ def visit_subscript(self, node, parent):
+ """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, parent=parent
+ )
+ newnode.postinit(
+ self.visit(node.value, newnode), self.visit(node.slice, newnode)
+ )
+ return newnode
+
+ def visit_tryexcept(self, node, parent):
+ """visit a TryExcept node by returning a fresh instance of it"""
+ newnode = nodes.TryExcept(node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ [self.visit(child, newnode) for child in node.body],
+ [self.visit(child, newnode) for child in node.handlers],
+ [self.visit(child, newnode) for child in node.orelse],
+ )
+ return newnode
+
+ def visit_tryfinally(self, node, parent):
+ """visit a TryFinally node by returning a fresh instance of it"""
+ newnode = nodes.TryFinally(node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ [self.visit(child, newnode) for child in node.body],
+ [self.visit(n, newnode) for n in node.finalbody],
+ )
+ return newnode
+
+ def visit_tuple(self, node, parent):
+ """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, parent=parent
+ )
+ newnode.postinit([self.visit(child, newnode) for child in node.elts])
+ return newnode
+
+ def visit_unaryop(self, node, parent):
+ """visit a UnaryOp node by returning a fresh instance of it"""
+ newnode = nodes.UnaryOp(
+ self._unary_op_classes[node.op.__class__],
+ node.lineno,
+ node.col_offset,
+ parent,
+ )
+ newnode.postinit(self.visit(node.operand, newnode))
+ return newnode
+
+ def visit_while(self, node, parent):
+ """visit a While node by returning a fresh instance of it"""
+ newnode = nodes.While(node.lineno, node.col_offset, 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_with(self, node, parent):
+ newnode = nodes.With(node.lineno, node.col_offset, parent)
+ expr = self.visit(node.context_expr, newnode)
+ if node.optional_vars is not None:
+ optional_vars = self.visit(node.optional_vars, newnode)
+ else:
+ optional_vars = None
+
+ type_annotation = self.check_type_comment(node, parent=newnode)
+ newnode.postinit(
+ items=[(expr, optional_vars)],
+ body=[self.visit(child, newnode) for child in node.body],
+ type_annotation=type_annotation,
+ )
+ return newnode
+
+ def visit_yield(self, node, parent):
+ """visit a Yield node by returning a fresh instance of it"""
+ newnode = nodes.Yield(node.lineno, node.col_offset, parent)
+ if node.value is not None:
+ newnode.postinit(self.visit(node.value, newnode))
+ return newnode
+
+
+class TreeRebuilder3(TreeRebuilder):
+ """extend and overwrite TreeRebuilder for python3k"""
+
+ def visit_arg(self, node, parent):
+ """visit an arg node by returning a fresh AssName instance"""
+ return self.visit_assignname(node, parent, node.arg)
+
+ # Not used in Python 3.8+.
+ def visit_nameconstant(self, node, parent):
+ # in Python 3.4 we have NameConstant for True / False / None
+ return nodes.Const(
+ node.value,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+
+ def visit_excepthandler(self, node, parent):
+ """visit an ExceptHandler node by returning a fresh instance of it"""
+ newnode = nodes.ExceptHandler(node.lineno, node.col_offset, parent)
+ if node.name:
+ name = self.visit_assignname(node, newnode, node.name)
+ else:
+ name = None
+ newnode.postinit(
+ _visit_or_none(node, "type", self, newnode),
+ name,
+ [self.visit(child, newnode) for child in node.body],
+ )
+ return newnode
+
+ def visit_nonlocal(self, node, parent):
+ """visit a Nonlocal node and return a new instance of it"""
+ return nodes.Nonlocal(
+ node.names,
+ getattr(node, "lineno", None),
+ getattr(node, "col_offset", None),
+ parent,
+ )
+
+ def visit_raise(self, node, parent):
+ """visit a Raise node by returning a fresh instance of it"""
+ newnode = nodes.Raise(node.lineno, node.col_offset, parent)
+ # no traceback; anyway it is not used in Pylint
+ newnode.postinit(
+ _visit_or_none(node, "exc", self, newnode),
+ _visit_or_none(node, "cause", self, newnode),
+ )
+ return newnode
+
+ def visit_starred(self, node, parent):
+ """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, parent=parent
+ )
+ newnode.postinit(self.visit(node.value, newnode))
+ return newnode
+
+ def visit_try(self, node, parent):
+ # python 3.3 introduce a new Try node replacing
+ # TryFinally/TryExcept nodes
+ if node.finalbody:
+ newnode = nodes.TryFinally(node.lineno, node.col_offset, parent)
+ if node.handlers:
+ body = [self.visit_tryexcept(node, newnode)]
+ else:
+ body = [self.visit(child, newnode) for child in node.body]
+ newnode.postinit(body, [self.visit(n, newnode) for n in node.finalbody])
+ return newnode
+ if node.handlers:
+ return self.visit_tryexcept(node, parent)
+ return None
+
+ def visit_annassign(self, node, parent):
+ """visit an AnnAssign node by returning a fresh instance of it"""
+ newnode = nodes.AnnAssign(node.lineno, node.col_offset, parent)
+ annotation = _visit_or_none(node, "annotation", self, newnode)
+ newnode.postinit(
+ target=self.visit(node.target, newnode),
+ annotation=annotation,
+ simple=node.simple,
+ value=_visit_or_none(node, "value", self, newnode),
+ )
+ return newnode
+
+ def _visit_with(self, cls, node, parent):
+ if "items" not in node._fields:
+ # python < 3.3
+ return super(TreeRebuilder3, self).visit_with(node, parent)
+
+ newnode = cls(node.lineno, node.col_offset, parent)
+
+ def visit_child(child):
+ expr = self.visit(child.context_expr, newnode)
+ var = _visit_or_none(child, "optional_vars", self, 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, parent):
+ return self._visit_with(nodes.With, node, parent)
+
+ def visit_yieldfrom(self, node, parent):
+ newnode = nodes.YieldFrom(node.lineno, node.col_offset, parent)
+ if node.value is not None:
+ newnode.postinit(self.visit(node.value, newnode))
+ return newnode
+
+ def visit_classdef(self, node, parent, newstyle=True):
+ return super(TreeRebuilder3, self).visit_classdef(
+ node, parent, newstyle=newstyle
+ )
+
+ # Async structs added in Python 3.5
+ def visit_asyncfunctiondef(self, node, parent):
+ return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent)
+
+ def visit_asyncfor(self, node, parent):
+ return self._visit_for(nodes.AsyncFor, node, parent)
+
+ def visit_await(self, node, parent):
+ newnode = nodes.Await(node.lineno, node.col_offset, parent)
+ newnode.postinit(value=self.visit(node.value, newnode))
+ return newnode
+
+ def visit_asyncwith(self, node, parent):
+ return self._visit_with(nodes.AsyncWith, node, parent)
+
+ def visit_joinedstr(self, node, parent):
+ newnode = nodes.JoinedStr(node.lineno, node.col_offset, parent)
+ newnode.postinit([self.visit(child, newnode) for child in node.values])
+ return newnode
+
+ def visit_formattedvalue(self, node, parent):
+ newnode = nodes.FormattedValue(node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ self.visit(node.value, newnode),
+ node.conversion,
+ _visit_or_none(node, "format_spec", self, newnode),
+ )
+ return newnode
+
+ def visit_namedexpr(self, node, parent):
+ newnode = nodes.NamedExpr(node.lineno, node.col_offset, parent)
+ newnode.postinit(
+ self.visit(node.target, newnode), self.visit(node.value, newnode)
+ )
+ return newnode
+
+
+TreeRebuilder = TreeRebuilder3
diff --git a/venv/Lib/site-packages/astroid/scoped_nodes.py b/venv/Lib/site-packages/astroid/scoped_nodes.py
new file mode 100644
index 0000000..d02b653
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/scoped_nodes.py
@@ -0,0 +1,2836 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
+# Copyright (c) 2011, 2013-2015 Google, Inc.
+# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2013 Phil Schaf <flying-sheep@web.de>
+# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
+# Copyright (c) 2015-2016 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2015 Rene Zhang <rz99@cornell.edu>
+# Copyright (c) 2015 Philip Lorenz <philip@bithub.de>
+# Copyright (c) 2016-2017 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2017-2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 David Euresti <david@dropbox.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+"""
+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).
+"""
+
+import builtins
+import sys
+import io
+import itertools
+from typing import Optional, List
+
+from astroid import bases
+from astroid import context as contextmod
+from astroid import exceptions
+from astroid import decorators as decorators_mod
+from astroid.interpreter import objectmodel
+from astroid.interpreter import dunder_lookup
+from astroid import manager
+from astroid import mixins
+from astroid import node_classes
+from astroid import util
+
+
+BUILTINS = builtins.__name__
+ITER_METHODS = ("__iter__", "__getitem__")
+EXCEPTION_BASE_CLASSES = frozenset({"Exception", "BaseException"})
+objects = util.lazy_import("objects")
+
+
+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 exceptions.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_duplicates_mro(sequences, cls, context):
+ for sequence in sequences:
+ names = [
+ (node.lineno, node.qname()) if node.name else None for node in sequence
+ ]
+ last_index = dict(map(reversed, enumerate(names)))
+ if names and names[0] is not None and last_index[names[0]] != 0:
+ raise exceptions.DuplicateBasesError(
+ message="Duplicates found in MROs {mros} for {cls!r}.",
+ mros=sequences,
+ cls=cls,
+ context=context,
+ )
+ yield [
+ node
+ for i, (node, name) in enumerate(zip(sequence, names))
+ if name is None or last_index[name] == i
+ ]
+
+
+def function_to_method(n, klass):
+ if isinstance(n, FunctionDef):
+ if n.type == "classmethod":
+ return bases.BoundMethod(n, klass)
+ if n.type != "staticmethod":
+ return bases.UnboundMethod(n)
+ return n
+
+
+MANAGER = manager.AstroidManager()
+
+
+def builtin_lookup(name):
+ """lookup a name into the builtin module
+ return the list of matching statements and the astroid for the builtin
+ module
+ """
+ builtin_astroid = MANAGER.ast_from_module(builtins)
+ if name == "__dict__":
+ return builtin_astroid, ()
+ try:
+ stmts = builtin_astroid.locals[name]
+ except KeyError:
+ stmts = ()
+ return builtin_astroid, stmts
+
+
+# TODO move this Mixin to mixins.py; problem: 'FunctionDef' in _scope_lookup
+class LocalsDictNodeNG(node_classes.LookupMixIn, node_classes.NodeNG):
+ """ 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 = {}
+ """A map of the name of a local variable to the node defining the local.
+
+ :type: dict(str, NodeNG)
+ """
+
+ def qname(self):
+ """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/pycqa/astroid/issues/278
+ if self.parent is None:
+ return self.name
+ return "%s.%s" % (self.parent.frame().qname(), self.name)
+
+ def frame(self):
+ """The first parent frame node.
+
+ A frame node is a :class:`Module`, :class:`FunctionDef`,
+ or :class:`ClassDef`.
+
+ :returns: The first parent frame node.
+ :rtype: Module or FunctionDef or ClassDef
+ """
+ return self
+
+ def scope(self):
+ """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, name, offset=0):
+ """XXX method for interfacing the scope lookup"""
+ try:
+ stmts = node._filter_stmts(self.locals[name], self, offset)
+ except KeyError:
+ stmts = ()
+ if stmts:
+ return self, stmts
+ if self.parent: # i.e. not Module
+ # nested scope: if parent scope is a function, that's fine
+ # else jump to the module
+ pscope = self.parent.scope()
+ if not pscope.is_function:
+ pscope = pscope.root()
+ return pscope.scope_lookup(node, name)
+ return builtin_lookup(name) # Module
+
+ def set_local(self, name, stmt):
+ """Define that the given name is declared in the given statement node.
+
+ .. seealso:: :meth:`scope`
+
+ :param name: The name that is being defined.
+ :type name: str
+
+ :param stmt: The statement that defines the given name.
+ :type stmt: NodeNG
+ """
+ # assert not stmt in self.locals.get(name, ()), (self, stmt)
+ self.locals.setdefault(name, []).append(stmt)
+
+ __setitem__ = set_local
+
+ def _append_node(self, child):
+ """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)
+ child.parent = self
+
+ def add_local_node(self, child_node, name=None):
+ """Append a child that should alter the locals of this scope node.
+
+ :param child_node: The child node that will alter locals.
+ :type child_node: NodeNG
+
+ :param name: The name of the local that will be altered by
+ the given child node.
+ :type name: str or None
+ """
+ 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)
+
+ def __getitem__(self, item):
+ """The first node the defines the given local.
+
+ :param item: The name of the locally defined object.
+ :type item: str
+
+ :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)
+ """
+ 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):
+ """Check if a local is defined in this scope.
+
+ :param name: The name of the local to check for.
+ :type name: str
+
+ :returns: True if this node has a local of the given name,
+ False otherwise.
+ :rtype: bool
+ """
+ return name in self.locals
+
+
+class Module(LocalsDictNodeNG):
+ """Class representing an :class:`ast.Module` node.
+
+ >>> node = astroid.extract_node('import astroid')
+ >>> node
+ <Import l.1 at 0x7f23b2e4e5c0>
+ >>> node.parent
+ <Module l.0 at 0x7f23b2e4eda0>
+ """
+
+ _astroid_fields = ("body",)
+
+ fromlineno = 0
+ """The first line that this node appears on in the source code.
+
+ :type: int or None
+ """
+ lineno = 0
+ """The line that this node appears on in the source code.
+
+ :type: int or None
+ """
+
+ # attributes below are set by the builder module or by raw factories
+
+ file = None
+ """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.
+
+ :type: str or None
+ """
+ file_bytes = None
+ """The string/bytes that this ast was built from.
+
+ :type: str or bytes or None
+ """
+ file_encoding = None
+ """The encoding of the source file.
+
+ This is used to get unicode out of a source file.
+ Python 2 only.
+
+ :type: str or None
+ """
+ name = None
+ """The name of the module.
+
+ :type: str or None
+ """
+ pure_python = None
+ """Whether the ast was built from source.
+
+ :type: bool or None
+ """
+ package = None
+ """Whether the node represents a package or a module.
+
+ :type: bool or None
+ """
+ globals = None
+ """A map of the name of a global variable to the node defining the global.
+
+ :type: dict(str, NodeNG)
+ """
+
+ # Future imports
+ future_imports = None
+ """The imports from ``__future__``.
+
+ :type: set(str) or None
+ """
+ special_attributes = objectmodel.ModuleModel()
+ """The names of special attributes that this module has.
+
+ :type: objectmodel.ModuleModel
+ """
+
+ # names of module attributes available through the global scope
+ scope_attrs = {"__name__", "__doc__", "__file__", "__path__", "__package__"}
+ """The names of module attributes available through the global scope.
+
+ :type: str(str)
+ """
+
+ _other_fields = (
+ "name",
+ "doc",
+ "file",
+ "path",
+ "package",
+ "pure_python",
+ "future_imports",
+ )
+ _other_other_fields = ("locals", "globals")
+
+ def __init__(
+ self,
+ name,
+ doc,
+ file=None,
+ path: Optional[List[str]] = None,
+ package=None,
+ parent=None,
+ pure_python=True,
+ ):
+ """
+ :param name: The name of the module.
+ :type name: str
+
+ :param doc: The module docstring.
+ :type doc: str
+
+ :param file: The path to the file that this ast has been extracted from.
+ :type file: str or None
+
+ :param path:
+ :type path: Optional[List[str]]
+
+ :param package: Whether the node represents a package or a module.
+ :type package: bool or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+
+ :param pure_python: Whether the ast was built from source.
+ :type pure_python: bool or None
+ """
+ self.name = name
+ self.doc = doc
+ self.file = file
+ self.path = path
+ self.package = package
+ self.parent = parent
+ self.pure_python = pure_python
+ self.locals = self.globals = {}
+ """A map of the name of a local variable to the node defining the local.
+
+ :type: dict(str, NodeNG)
+ """
+ self.body = []
+ """The contents of the module.
+
+ :type: list(NodeNG) or None
+ """
+ self.future_imports = set()
+
+ # pylint: enable=redefined-builtin
+
+ def postinit(self, body=None):
+ """Do some setup after initialisation.
+
+ :param body: The contents of the module.
+ :type body: list(NodeNG) or None
+ """
+ self.body = body
+
+ def _get_stream(self):
+ if self.file_bytes is not None:
+ return io.BytesIO(self.file_bytes)
+ if self.file is not None:
+ 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):
+ """Get a range from where this node starts to where this node ends.
+
+ :param lineno: Unused.
+ :type lineno: int
+
+ :returns: The range of line numbers that this node belongs to.
+ :rtype: tuple(int, int)
+ """
+ return self.fromlineno, self.tolineno
+
+ def scope_lookup(self, node, name, offset=0):
+ """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.
+ :type node: NodeNG
+
+ :param name: The name of the variable to find assignments for.
+ :type name: str
+
+ :param offset: The line offset to filter statements up to.
+ :type offset: int
+
+ :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).
+ :rtype: tuple(str, list(NodeNG))
+ """
+ if name in self.scope_attrs and name not in self.locals:
+ try:
+ return self, self.getattr(name)
+ except exceptions.AttributeInferenceError:
+ return self, ()
+ return self._scope_lookup(node, name, offset)
+
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+ return "%s.module" % BUILTINS
+
+ def display_type(self):
+ """A human readable type of this node.
+
+ :returns: The type of this node.
+ :rtype: str
+ """
+ return "Module"
+
+ def getattr(self, name, context=None, ignore_locals=False):
+ result = []
+ name_in_locals = name in self.locals
+
+ if name in self.special_attributes and not ignore_locals and not name_in_locals:
+ result = [self.special_attributes.lookup(name)]
+ elif not ignore_locals and name_in_locals:
+ result = self.locals[name]
+ elif self.package:
+ try:
+ result = [self.import_module(name, relative_only=True)]
+ except (exceptions.AstroidBuildingError, SyntaxError) as exc:
+ raise exceptions.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 exceptions.AttributeInferenceError(
+ target=self, attribute=name, context=context
+ )
+
+ def igetattr(self, name, context=None):
+ """Infer the possible values of the given variable.
+
+ :param name: The name of the variable to infer.
+ :type name: str
+
+ :returns: The inferred possible values.
+ :rtype: iterable(NodeNG) or None
+ """
+ # set lookup name since this is necessary to infer on import nodes for
+ # instance
+ context = contextmod.copy_context(context)
+ context.lookupname = name
+ try:
+ return bases._infer_stmts(self.getattr(name, context), context, frame=self)
+ except exceptions.AttributeInferenceError as error:
+ raise exceptions.InferenceError(
+ error.message, target=self, attribute=name, context=context
+ ) from error
+
+ def fully_defined(self):
+ """Check if this module has been build from a .py file.
+
+ If so, the module contains a complete representation,
+ including the code.
+
+ :returns: True if the module has been built from a .py file.
+ :rtype: bool
+ """
+ return self.file is not None and self.file.endswith(".py")
+
+ def statement(self):
+ """The first parent node, including self, marked as statement node.
+
+ :returns: The first parent statement.
+ :rtype: NodeNG
+ """
+ return 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):
+ """Whether :pep:`328` absolute import behaviour has been enabled.
+
+ :returns: True if :pep:`328` has been enabled, False otherwise.
+ :rtype: bool
+ """
+ return self._absolute_import_activated
+
+ def import_module(self, modname, relative_only=False, level=None):
+ """Get the ast for a given module as if imported from this module.
+
+ :param modname: The name of the module to "import".
+ :type modname: str
+
+ :param relative_only: Whether to only consider relative imports.
+ :type relative_only: bool
+
+ :param level: The level of relative import.
+ :type level: int or None
+
+ :returns: The imported module ast.
+ :rtype: NodeNG
+ """
+ if relative_only and level is None:
+ level = 0
+ absmodname = self.relative_to_absolute_name(modname, level)
+
+ try:
+ return MANAGER.ast_from_module_name(absmodname)
+ except exceptions.AstroidBuildingError:
+ # we only want to import a sub module or package of this module,
+ # skip here
+ if relative_only:
+ raise
+ return MANAGER.ast_from_module_name(modname)
+
+ def relative_to_absolute_name(self, modname, level):
+ """Get the absolute module name for a relative import.
+
+ The relative import can be implicit or explicit.
+
+ :param modname: The module name to convert.
+ :type modname: str
+
+ :param level: The level of relative import.
+ :type level: int
+
+ :returns: The absolute module name.
+ :rtype: str
+
+ :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
+ if level and self.name.count(".") < level:
+ raise exceptions.TooManyLevelsError(level=level, name=self.name)
+
+ package_name = self.name.rsplit(".", level)[0]
+ 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 "%s.%s" % (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 exceptions.InferenceError:
+ 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 exceptions.InferenceError:
+ return default
+ if not isinstance(explicit, (node_classes.Tuple, node_classes.List)):
+ return default
+
+ str_const = lambda node: (
+ 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 exceptions.InferenceError:
+ 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 publc names.
+ :rtype: list(str)
+ """
+ return [name for name in self.keys() if not name.startswith("_")]
+
+ def bool_value(self):
+ """Determine the boolean value of this node.
+
+ :returns: The boolean value of this node.
+ For a :class:`Module` this is always ``True``.
+ :rtype: bool
+ """
+ return True
+
+ def get_children(self):
+ yield from self.body
+
+
+class ComprehensionScope(LocalsDictNodeNG):
+ """Scoping for different types of comprehensions."""
+
+ def frame(self):
+ """The first parent frame node.
+
+ A frame node is a :class:`Module`, :class:`FunctionDef`,
+ or :class:`ClassDef`.
+
+ :returns: The first parent frame node.
+ :rtype: Module or FunctionDef or ClassDef
+ """
+ return self.parent.frame()
+
+ scope_lookup = LocalsDictNodeNG._scope_lookup
+
+
+class GeneratorExp(ComprehensionScope):
+ """Class representing an :class:`ast.GeneratorExp` node.
+
+ >>> node = astroid.extract_node('(thing for thing in things if thing)')
+ >>> node
+ <GeneratorExp l.1 at 0x7f23b2e4e400>
+ """
+
+ _astroid_fields = ("elt", "generators")
+ _other_other_fields = ("locals",)
+ elt = None
+ """The element that forms the output of the expression.
+
+ :type: NodeNG or None
+ """
+ generators = None
+ """The generators that are looped through.
+
+ :type: list(Comprehension) or None
+ """
+
+ def __init__(self, lineno=None, col_offset=None, parent=None):
+ """
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.locals = {}
+ """A map of the name of a local variable to the node defining the local.
+
+ :type: dict(str, NodeNG)
+ """
+
+ super(GeneratorExp, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, elt=None, generators=None):
+ """Do some setup after initialisation.
+
+ :param elt: The element that forms the output of the expression.
+ :type elt: NodeNG or None
+
+ :param generators: The generators that are looped through.
+ :type generators: list(Comprehension) or None
+ """
+ self.elt = elt
+ if generators is None:
+ self.generators = []
+ else:
+ self.generators = generators
+
+ def bool_value(self):
+ """Determine the boolean value of this node.
+
+ :returns: The boolean value of this node.
+ For a :class:`GeneratorExp` this is always ``True``.
+ :rtype: bool
+ """
+ return True
+
+ def get_children(self):
+ yield self.elt
+
+ yield from self.generators
+
+
+class DictComp(ComprehensionScope):
+ """Class representing an :class:`ast.DictComp` node.
+
+ >>> node = astroid.extract_node('{k:v for k, v in things if k > v}')
+ >>> node
+ <DictComp l.1 at 0x7f23b2e41d68>
+ """
+
+ _astroid_fields = ("key", "value", "generators")
+ _other_other_fields = ("locals",)
+ key = None
+ """What produces the keys.
+
+ :type: NodeNG or None
+ """
+ value = None
+ """What produces the values.
+
+ :type: NodeNG or None
+ """
+ generators = None
+ """The generators that are looped through.
+
+ :type: list(Comprehension) or None
+ """
+
+ def __init__(self, lineno=None, col_offset=None, parent=None):
+ """
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.locals = {}
+ """A map of the name of a local variable to the node defining the local.
+
+ :type: dict(str, NodeNG)
+ """
+
+ super(DictComp, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, key=None, value=None, generators=None):
+ """Do some setup after initialisation.
+
+ :param key: What produces the keys.
+ :type key: NodeNG or None
+
+ :param value: What produces the values.
+ :type value: NodeNG or None
+
+ :param generators: The generators that are looped through.
+ :type generators: list(Comprehension) or None
+ """
+ self.key = key
+ self.value = value
+ if generators is None:
+ self.generators = []
+ else:
+ self.generators = generators
+
+ def bool_value(self):
+ """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.
+
+ >>> node = astroid.extract_node('{thing for thing in things if thing}')
+ >>> node
+ <SetComp l.1 at 0x7f23b2e41898>
+ """
+
+ _astroid_fields = ("elt", "generators")
+ _other_other_fields = ("locals",)
+ elt = None
+ """The element that forms the output of the expression.
+
+ :type: NodeNG or None
+ """
+ generators = None
+ """The generators that are looped through.
+
+ :type: list(Comprehension) or None
+ """
+
+ def __init__(self, lineno=None, col_offset=None, parent=None):
+ """
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.locals = {}
+ """A map of the name of a local variable to the node defining the local.
+
+ :type: dict(str, NodeNG)
+ """
+
+ super(SetComp, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, elt=None, generators=None):
+ """Do some setup after initialisation.
+
+ :param elt: The element that forms the output of the expression.
+ :type elt: NodeNG or None
+
+ :param generators: The generators that are looped through.
+ :type generators: list(Comprehension) or None
+ """
+ self.elt = elt
+ if generators is None:
+ self.generators = []
+ else:
+ self.generators = generators
+
+ def bool_value(self):
+ """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(node_classes.NodeNG):
+ """Class representing an :class:`ast.ListComp` node.
+
+ >>> node = astroid.extract_node('[thing for thing in things if thing]')
+ >>> node
+ <ListComp l.1 at 0x7f23b2e418d0>
+ """
+
+ _astroid_fields = ("elt", "generators")
+ elt = None
+ """The element that forms the output of the expression.
+
+ :type: NodeNG or None
+ """
+ generators = None
+ """The generators that are looped through.
+
+ :type: list(Comprehension) or None
+ """
+
+ def postinit(self, elt=None, generators=None):
+ """Do some setup after initialisation.
+
+ :param elt: The element that forms the output of the expression.
+ :type elt: NodeNG or None
+
+ :param generators: The generators that are looped through.
+ :type generators: list(Comprehension) or None
+ """
+ self.elt = elt
+ self.generators = generators
+
+ def bool_value(self):
+ """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
+
+
+class ListComp(_ListComp, ComprehensionScope):
+ """Class representing an :class:`ast.ListComp` node.
+
+ >>> node = astroid.extract_node('[thing for thing in things if thing]')
+ >>> node
+ <ListComp l.1 at 0x7f23b2e418d0>
+ """
+
+ _other_other_fields = ("locals",)
+
+ def __init__(self, lineno=None, col_offset=None, parent=None):
+ self.locals = {}
+ """A map of the name of a local variable to the node defining it.
+
+ :type: dict(str, NodeNG)
+ """
+
+ super(ListComp, self).__init__(lineno, col_offset, parent)
+
+
+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))
+ except exceptions.InferenceError:
+ return None
+ if isinstance(result, bases.Instance):
+ result = result._proxied
+ if isinstance(result, ClassDef):
+ if result.is_subtype_of("%s.classmethod" % BUILTINS):
+ return "classmethod"
+ if result.is_subtype_of("%s.staticmethod" % BUILTINS):
+ return "staticmethod"
+ return None
+
+
+class Lambda(mixins.FilterStmtsMixin, LocalsDictNodeNG):
+ """Class representing an :class:`ast.Lambda` node.
+
+ >>> node = astroid.extract_node('lambda arg: arg + 1')
+ >>> node
+ <Lambda.<lambda> l.1 at 0x7f23b2e41518>
+ """
+
+ _astroid_fields = ("args", "body")
+ _other_other_fields = ("locals",)
+ name = "<lambda>"
+ is_lambda = True
+
+ def implicit_parameters(self):
+ return 0
+
+ # function's type, 'function' | 'method' | 'staticmethod' | 'classmethod'
+ @property
+ def type(self):
+ """Whether this is a method or function.
+
+ :returns: 'method' if this is a method, 'function' otherwise.
+ :rtype: str
+ """
+ # pylint: disable=no-member
+ if self.args.args and self.args.args[0].name == "self":
+ if isinstance(self.parent.scope(), ClassDef):
+ return "method"
+ return "function"
+
+ def __init__(self, lineno=None, col_offset=None, parent=None):
+ """
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.locals = {}
+ """A map of the name of a local variable to the node defining it.
+
+ :type: dict(str, NodeNG)
+ """
+
+ self.args = []
+ """The arguments that the function takes.
+
+ :type: Arguments or list
+ """
+
+ self.body = []
+ """The contents of the function body.
+
+ :type: list(NodeNG)
+ """
+
+ super(Lambda, self).__init__(lineno, col_offset, parent)
+
+ def postinit(self, args, body):
+ """Do some setup after initialisation.
+
+ :param args: The arguments that the function takes.
+ :type args: Arguments
+
+ :param body: The contents of the function body.
+ :type body: list(NodeNG)
+ """
+ self.args = args
+ self.body = body
+
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+ if "method" in self.type:
+ return "%s.instancemethod" % BUILTINS
+ return "%s.function" % BUILTINS
+
+ def display_type(self):
+ """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):
+ """Whether this node defines something that is callable.
+
+ :returns: True if this defines something that is callable,
+ False otherwise.
+ For a :class:`Lambda` this is always ``True``.
+ :rtype: bool
+ """
+ return True
+
+ def argnames(self):
+ """Get the names of each of the arguments.
+
+ :returns: The names of the arguments.
+ :rtype: list(str)
+ """
+ # pylint: disable=no-member; github.com/pycqa/astroid/issues/291
+ # args is in fact redefined later on by postinit. Can't be changed
+ # to None due to a strong interaction between Lambda and FunctionDef.
+
+ if self.args.args: # maybe None with builtin functions
+ names = _rec_get_names(self.args.args)
+ else:
+ names = []
+ if self.args.vararg:
+ names.append(self.args.vararg)
+ if self.args.kwarg:
+ names.append(self.args.kwarg)
+ return names
+
+ def infer_call_result(self, caller, context=None):
+ """Infer what the function returns when called.
+
+ :param caller: Unused
+ :type caller: object
+ """
+ # pylint: disable=no-member; github.com/pycqa/astroid/issues/291
+ # args is in fact redefined later on by postinit. Can't be changed
+ # to None due to a strong interaction between Lambda and FunctionDef.
+ return self.body.infer(context)
+
+ def scope_lookup(self, node, name, offset=0):
+ """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.
+ :type node: NodeNG
+
+ :param name: The name to find assignments for.
+ :type name: str
+
+ :param offset: The line offset to filter statements up to.
+ :type offset: int
+
+ :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).
+ :rtype: tuple(str, list(NodeNG))
+ """
+ # pylint: disable=no-member; github.com/pycqa/astroid/issues/291
+ # args is in fact redefined later on by postinit. Can't be changed
+ # to None due to a strong interaction between Lambda and FunctionDef.
+
+ if node in self.args.defaults or node in self.args.kw_defaults:
+ 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):
+ """Determine the boolean value of this node.
+
+ :returns: The boolean value of this node.
+ For a :class:`Lambda` this is always ``True``.
+ :rtype: bool
+ """
+ return True
+
+ def get_children(self):
+ yield self.args
+ yield self.body
+
+
+class FunctionDef(mixins.MultiLineBlockMixin, node_classes.Statement, Lambda):
+ """Class representing an :class:`ast.FunctionDef`.
+
+ >>> node = astroid.extract_node('''
+ ... def my_func(arg):
+ ... return arg + 1
+ ... ''')
+ >>> node
+ <FunctionDef.my_func l.2 at 0x7f23b2e71e10>
+ """
+
+ _astroid_fields = ("decorators", "args", "returns", "body")
+ _multi_line_block_fields = ("body",)
+ returns = None
+ decorators = None
+ """The decorators that are applied to this method or function.
+
+ :type: Decorators or None
+ """
+ special_attributes = objectmodel.FunctionModel()
+ """The names of special attributes that this function has.
+
+ :type: objectmodel.FunctionModel
+ """
+ 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", "doc")
+ _other_other_fields = (
+ "locals",
+ "_type",
+ "type_comment_returns",
+ "type_comment_args",
+ )
+ _type = None
+
+ def __init__(self, name=None, doc=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param name: The name of the function.
+ :type name: str or None
+
+ :param doc: The function's docstring.
+ :type doc: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.name = name
+ """The name of the function.
+
+ :type name: str or None
+ """
+
+ self.doc = doc
+ """The function's docstring.
+
+ :type doc: str or None
+ """
+
+ self.instance_attrs = {}
+ super(FunctionDef, self).__init__(lineno, col_offset, parent)
+ if parent:
+ frame = parent.frame()
+ frame.set_local(name, self)
+
+ # pylint: disable=arguments-differ; different than Lambdas
+ def postinit(
+ self,
+ args,
+ body,
+ decorators=None,
+ returns=None,
+ type_comment_returns=None,
+ type_comment_args=None,
+ ):
+ """Do some setup after initialisation.
+
+ :param args: The arguments that the function takes.
+ :type args: Arguments or list
+
+ :param body: The contents of the function body.
+ :type body: list(NodeNG)
+
+ :param decorators: The decorators that are applied to this
+ method or function.
+ :type decorators: Decorators or None
+ :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.
+ """
+ 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
+
+ @decorators_mod.cachedproperty
+ def extra_decorators(self):
+ """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.
+
+ :type: list(NodeNG)
+ """
+ frame = self.parent.frame()
+ if not isinstance(frame, ClassDef):
+ return []
+
+ decorators = []
+ for assign in frame._get_assign_nodes():
+ 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
+
+ @decorators_mod.cachedproperty
+ def type(self): # pylint: disable=invalid-overridden-method
+ """The function type for this node.
+
+ Possible values are: method, function, staticmethod, classmethod.
+
+ :type: str
+ """
+ builtin_descriptors = {"classmethod", "staticmethod"}
+
+ for decorator in self.extra_decorators:
+ if decorator.func.name in builtin_descriptors:
+ return decorator.func.name
+
+ frame = self.parent.frame()
+ type_name = "function"
+ if isinstance(frame, ClassDef):
+ if self.name == "__new__":
+ return "classmethod"
+ if sys.version_info >= (3, 6) and self.name == "__init_subclass__":
+ 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.Call):
+ # Handle the following case:
+ # @some_decorator(arg1, arg2)
+ # def func(...)
+ #
+ try:
+ current = next(node.func.infer())
+ except exceptions.InferenceError:
+ 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("%s.classmethod" % BUILTINS):
+ return "classmethod"
+ if ancestor.is_subtype_of("%s.staticmethod" % BUILTINS):
+ return "staticmethod"
+ except exceptions.InferenceError:
+ pass
+ return type_name
+
+ @decorators_mod.cachedproperty
+ def fromlineno(self):
+ """The first line that this node appears on in the source code.
+
+ :type: int or None
+ """
+ # lineno is the line number of the first decorator, we want the def
+ # statement lineno
+ lineno = self.lineno
+ if self.decorators is not None:
+ lineno += sum(
+ node.tolineno - node.lineno + 1 for node in self.decorators.nodes
+ )
+
+ return lineno
+
+ @decorators_mod.cachedproperty
+ def blockstart_tolineno(self):
+ """The line on which the beginning of this block ends.
+
+ :type: int
+ """
+ return self.args.tolineno
+
+ def block_range(self, lineno):
+ """Get a range from the given line number to where this node ends.
+
+ :param lineno: Unused.
+ :type lineno: int
+
+ :returns: The range of line numbers that this node belongs to,
+ :rtype: tuple(int, int)
+ """
+ return self.fromlineno, self.tolineno
+
+ def getattr(self, name, context=None):
+ """this method doesn't look in the instance_attrs dictionary since it's
+ done by an Instance proxy at inference time.
+ """
+ if name in self.instance_attrs:
+ return self.instance_attrs[name]
+ if name in self.special_attributes:
+ return [self.special_attributes.lookup(name)]
+ raise exceptions.AttributeInferenceError(target=self, attribute=name)
+
+ def igetattr(self, name, context=None):
+ """Inferred getattr, which returns an iterator of inferred statements."""
+ try:
+ return bases._infer_stmts(self.getattr(name, context), context, frame=self)
+ except exceptions.AttributeInferenceError as error:
+ raise exceptions.InferenceError(
+ error.message, target=self, attribute=name, context=context
+ ) from error
+
+ def is_method(self):
+ """Check if this function node represents a method.
+
+ :returns: True if this is a method, False otherwise.
+ :rtype: bool
+ """
+ # 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 isinstance(self.parent.frame(), ClassDef)
+
+ @decorators_mod.cached
+ def decoratornames(self):
+ """Get the qualified names of each of the decorators on this function.
+
+ :returns: The names of the decorators.
+ :rtype: set(str)
+ """
+ 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():
+ result.add(infnode.qname())
+ except exceptions.InferenceError:
+ continue
+ return result
+
+ def is_bound(self):
+ """Check if the function is bound to an instance or class.
+
+ :returns: True if the function is bound to an instance or class,
+ False otherwise.
+ :rtype: bool
+ """
+ return self.type == "classmethod"
+
+ def is_abstract(self, pass_is_abstract=True):
+ """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 'pass' and pass_is_abstract is True
+ * The method is annotated with abc.astractproperty/abc.abstractmethod
+
+ :returns: True if the method is abstract, False otherwise.
+ :rtype: bool
+ """
+ if self.decorators:
+ for node in self.decorators.nodes:
+ try:
+ inferred = next(node.infer())
+ except exceptions.InferenceError:
+ 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 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
+
+ def is_generator(self):
+ """Check if this is a generator function.
+
+ :returns: True is this is a generator function, False otherwise.
+ :rtype: bool
+ """
+ return next(self._get_yield_nodes_skip_lambdas(), False)
+
+ def infer_call_result(self, caller=None, context=None):
+ """Infer what the function returns when called.
+
+ :returns: What the function returns.
+ :rtype: iterable(NodeNG or Uninferable) or None
+ """
+ if self.is_generator():
+ if isinstance(self, AsyncFunctionDef):
+ generator_cls = bases.AsyncGenerator
+ else:
+ generator_cls = bases.Generator
+ result = generator_cls(self)
+ 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 len(self.args.args) == 1
+ and self.args.vararg is not None
+ ):
+ metaclass = next(caller.args[0].infer(context))
+ if isinstance(metaclass, ClassDef):
+ class_bases = [next(arg.infer(context)) for arg in caller.args[1:]]
+ new_class = ClassDef(name="temporary_class")
+ new_class.hide = True
+ new_class.parent = self
+ new_class.postinit(
+ bases=[base for base in class_bases if base != util.Uninferable],
+ body=[],
+ decorators=[],
+ 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 and isinstance(self.body[-1], node_classes.Assert):
+ yield node_classes.Const(None)
+ return
+
+ raise exceptions.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 exceptions.InferenceError:
+ yield util.Uninferable
+
+ def bool_value(self):
+ """Determine the boolean value of this node.
+
+ :returns: The boolean value of this node.
+ For a :class:`FunctionDef` this is always ``True``.
+ :rtype: bool
+ """
+ return True
+
+ def get_children(self):
+ if self.decorators is not None:
+ yield self.decorators
+
+ yield self.args
+
+ if self.returns is not None:
+ yield self.returns
+
+ yield from self.body
+
+ def scope_lookup(self, node, name, offset=0):
+ """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.
+ frame = self.parent.frame()
+ if isinstance(frame, ClassDef):
+ return self, [frame]
+ return super().scope_lookup(node, name, offset)
+
+
+class AsyncFunctionDef(FunctionDef):
+ """Class representing an :class:`ast.FunctionDef` node.
+
+ A :class:`AsyncFunctionDef` is an asynchronous function
+ created with the `async` keyword.
+
+ >>> node = astroid.extract_node('''
+ async def func(things):
+ async for thing in things:
+ print(thing)
+ ''')
+ >>> node
+ <AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
+ >>> node.body[0]
+ <AsyncFor l.3 at 0x7f23b2e417b8>
+ """
+
+
+def _rec_get_names(args, names=None):
+ """return a list of all argument names"""
+ if names is None:
+ names = []
+ for arg in args:
+ if isinstance(arg, node_classes.Tuple):
+ _rec_get_names(arg.elts, names)
+ else:
+ names.append(arg.name)
+ return names
+
+
+def _is_metaclass(klass, seen=None):
+ """ 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():
+ 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 util.Uninferable:
+ continue
+ if baseobj is klass:
+ continue
+ if not isinstance(baseobj, ClassDef):
+ continue
+ if baseobj._type == "metaclass":
+ return True
+ if _is_metaclass(baseobj, seen):
+ return True
+ except exceptions.InferenceError:
+ continue
+ return False
+
+
+def _class_type(klass, ancestors=None):
+ """return a ClassDef node type to differ metaclass and exception
+ from 'regular' classes
+ """
+ # XXX we have to store ancestors in case we have an ancestor loop
+ if klass._type is not None:
+ return klass._type
+ if _is_metaclass(klass):
+ klass._type = "metaclass"
+ elif klass.name.endswith("Exception"):
+ klass._type = "exception"
+ else:
+ if ancestors is None:
+ ancestors = set()
+ klass_name = klass.qname()
+ if klass_name in ancestors:
+ # XXX we are in loop ancestors, and have found no type
+ klass._type = "class"
+ return "class"
+ ancestors.add(klass_name)
+ for base in klass.ancestors(recurs=False):
+ name = _class_type(base, ancestors)
+ if name != "class":
+ if name == "metaclass" and not _is_metaclass(klass):
+ # don't propagate it if the current class
+ # can't be a metaclass
+ continue
+ klass._type = base.type
+ break
+ if klass._type is None:
+ klass._type = "class"
+ return klass._type
+
+
+def get_wrapping_class(node):
+ """Get the class that wraps the given node.
+
+ We consider that a class wraps a node if the class
+ is a parent for the said node.
+
+ :returns: The class that wraps the given node
+ :rtype: ClassDef or None
+ """
+
+ klass = node.frame()
+ while klass is not None and not isinstance(klass, ClassDef):
+ if klass.parent is None:
+ klass = None
+ else:
+ klass = klass.parent.frame()
+ return klass
+
+
+class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, node_classes.Statement):
+ """Class representing an :class:`ast.ClassDef` node.
+
+ >>> node = astroid.extract_node('''
+ class Thing:
+ def my_meth(self, arg):
+ return arg + self.offset
+ ''')
+ >>> node
+ <ClassDef.Thing l.2 at 0x7f23b2e9e748>
+ """
+
+ # some of the attributes below are set by the builder module or
+ # by a raw factories
+
+ # a dictionary of class instances attributes
+ _astroid_fields = ("decorators", "bases", "body") # name
+
+ decorators = None
+ """The decorators that are applied to this class.
+
+ :type: Decorators or None
+ """
+ special_attributes = objectmodel.ClassModel()
+ """The names of special attributes that this class has.
+
+ :type: objectmodel.ClassModel
+ """
+
+ _type = 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", "doc")
+ _other_other_fields = ("locals", "_newstyle")
+ _newstyle = None
+
+ def __init__(self, name=None, doc=None, lineno=None, col_offset=None, parent=None):
+ """
+ :param name: The name of the class.
+ :type name: str or None
+
+ :param doc: The function's docstring.
+ :type doc: str or None
+
+ :param lineno: The line that this node appears on in the source code.
+ :type lineno: int or None
+
+ :param col_offset: The column that this node appears on in the
+ source code.
+ :type col_offset: int or None
+
+ :param parent: The parent node in the syntax tree.
+ :type parent: NodeNG or None
+ """
+ self.instance_attrs = {}
+ self.locals = {}
+ """A map of the name of a local variable to the node defining it.
+
+ :type: dict(str, NodeNG)
+ """
+
+ self.keywords = []
+ """The keywords given to the class definition.
+
+ This is usually for :pep:`3115` style metaclass declaration.
+
+ :type: list(Keyword) or None
+ """
+
+ self.bases = []
+ """What the class inherits from.
+
+ :type: list(NodeNG)
+ """
+
+ self.body = []
+ """The contents of the class body.
+
+ :type: list(NodeNG)
+ """
+
+ self.name = name
+ """The name of the class.
+
+ :type name: str or None
+ """
+
+ self.doc = doc
+ """The class' docstring.
+
+ :type doc: str or None
+ """
+
+ super(ClassDef, self).__init__(lineno, col_offset, parent)
+ if parent is not None:
+ parent.frame().set_local(name, self)
+
+ for local_name, node in self.implicit_locals():
+ self.add_local_node(node, local_name)
+
+ def implicit_parameters(self):
+ return 1
+
+ def implicit_locals(self):
+ """Get implicitly defined class definition locals.
+
+ :returns: the the name and Const pair for each local
+ :rtype: tuple(tuple(str, node_classes.Const), ...)
+ """
+ locals_ = (("__module__", self.special_attributes.attr___module__),)
+ # __qualname__ is defined in PEP3155
+ locals_ += (("__qualname__", self.special_attributes.attr___qualname__),)
+ return locals_
+
+ # pylint: disable=redefined-outer-name
+ def postinit(
+ self, bases, body, decorators, newstyle=None, metaclass=None, keywords=None
+ ):
+ """Do some setup after initialisation.
+
+ :param bases: What the class inherits from.
+ :type bases: list(NodeNG)
+
+ :param body: The contents of the class body.
+ :type body: list(NodeNG)
+
+ :param decorators: The decorators that are applied to this class.
+ :type decorators: Decorators or None
+
+ :param newstyle: Whether this is a new style class or not.
+ :type newstyle: bool or None
+
+ :param metaclass: The metaclass of this class.
+ :type metaclass: NodeNG or None
+
+ :param keywords: The keywords given to the class definition.
+ :type keywords: list(Keyword) or None
+ """
+ self.keywords = keywords
+ self.bases = bases
+ self.body = body
+ self.decorators = decorators
+ if newstyle is not None:
+ self._newstyle = newstyle
+ if metaclass is not None:
+ self._metaclass = metaclass
+
+ def _newstyle_impl(self, context=None):
+ if context is None:
+ context = contextmod.InferenceContext()
+ if self._newstyle is not None:
+ return self._newstyle
+ for base in self.ancestors(recurs=False, context=context):
+ if base._newstyle_impl(context):
+ self._newstyle = True
+ break
+ klass = self.declared_metaclass()
+ # could be any callable, we'd need to infer the result of klass(name,
+ # bases, dict). punt if it's not a class node.
+ if klass is not None and isinstance(klass, ClassDef):
+ self._newstyle = klass._newstyle_impl(context)
+ if self._newstyle is None:
+ self._newstyle = False
+ return self._newstyle
+
+ _newstyle = None
+ newstyle = property(
+ _newstyle_impl,
+ doc=("Whether this is a new style class or not\n\n" ":type: bool or None"),
+ )
+
+ @decorators_mod.cachedproperty
+ 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):
+ """Get a range from the given line number to where this node ends.
+
+ :param lineno: Unused.
+ :type lineno: int
+
+ :returns: The range of line numbers that this node belongs to,
+ :rtype: tuple(int, int)
+ """
+ return self.fromlineno, self.tolineno
+
+ def pytype(self):
+ """Get the name of the type that this node represents.
+
+ :returns: The name of the type.
+ :rtype: str
+ """
+ if self.newstyle:
+ return "%s.type" % BUILTINS
+ return "%s.classobj" % BUILTINS
+
+ def display_type(self):
+ """A human readable type of this node.
+
+ :returns: The type of this node.
+ :rtype: str
+ """
+ return "Class"
+
+ def callable(self):
+ """Whether this node defines something that is callable.
+
+ :returns: True if this defines something that is callable,
+ False otherwise.
+ For a :class:`ClassDef` this is always ``True``.
+ :rtype: bool
+ """
+ return True
+
+ def is_subtype_of(self, type_name, context=None):
+ """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: True if this class is a subtype of the given type,
+ False otherwise.
+ :rtype: bool
+ """
+ if self.qname() == type_name:
+ return True
+ for anc in self.ancestors(context=context):
+ if anc.qname() == type_name:
+ return True
+ return False
+
+ def _infer_type_call(self, caller, context):
+ name_node = next(caller.args[0].infer(context))
+ if isinstance(name_node, node_classes.Const) and isinstance(
+ name_node.value, str
+ ):
+ name = name_node.value
+ else:
+ return util.Uninferable
+
+ result = ClassDef(name, None)
+
+ # Get the bases of the class.
+ class_bases = next(caller.args[1].infer(context))
+ if isinstance(class_bases, (node_classes.Tuple, node_classes.List)):
+ result.bases = class_bases.itered()
+ 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 exceptions.InferenceError:
+ members = None
+
+ if members and isinstance(members, node_classes.Dict):
+ for attr, value in members.items:
+ if isinstance(attr, node_classes.Const) and isinstance(attr.value, str):
+ result.locals[attr.value] = [value]
+
+ result.parent = caller.parent
+ return result
+
+ def infer_call_result(self, caller, context=None):
+ """infer what a class is returning when called"""
+ if (
+ self.is_subtype_of("%s.type" % (BUILTINS,), 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:
+ dunder_call = next(metaclass.igetattr("__call__", context))
+ except exceptions.AttributeInferenceError:
+ 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 = contextmod.bind_context_to_node(context, self)
+ yield from dunder_call.infer_call_result(caller, context)
+ else:
+ if any(cls.name in EXCEPTION_BASE_CLASSES for cls in self.mro()):
+ # Subclasses of exceptions can be exception instances
+ yield objects.ExceptionInstance(self)
+ else:
+ yield bases.Instance(self)
+
+ def scope_lookup(self, node, name, offset=0):
+ """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.
+ :type node: NodeNG
+
+ :param name: The name to find assignments for.
+ :type name: str
+
+ :param offset: The line offset to filter statements up to.
+ :type offset: int
+
+ :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).
+ :rtype: tuple(str, list(NodeNG))
+ """
+ # 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 MANAGER.builtins_module
+ )
+ if (
+ any(node == base or base.parent_of(node) for base in self.bases)
+ or lookup_upper_frame
+ ):
+ # Handle the case where we have either a name
+ # in the bases of a class, which exists before
+ # the actual definition or the case where we have
+ # a Getattr node, with that name.
+ #
+ # name = ...
+ # class A(name):
+ # def name(self): ...
+ #
+ # import name
+ # class A(name.Name):
+ # def name(self): ...
+
+ 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=True, context=None):
+ """Iterate over the base classes in prefixed depth first order.
+
+ :param recurs: Whether to recurse or return direct ancestors only.
+ :type recurs: bool
+
+ :returns: The base classes
+ :rtype: iterable(NodeNG)
+ """
+ # FIXME: should be possible to choose the resolution order
+ # FIXME: inference make infinite loops possible here
+ yielded = {self}
+ if context is None:
+ context = contextmod.InferenceContext()
+ if not self.bases and self.qname() != "builtins.object":
+ yield builtin_lookup("object")[1][0]
+ 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 exceptions.InferenceError:
+ continue
+
+ def local_attr_ancestors(self, name, context=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 = self.mro(context)[1:]
+ except exceptions.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=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):
+ """Whether this class directly inherits from the given node.
+
+ :param node: The node to check for.
+ :type node: NodeNG
+
+ :returns: True if this class directly inherits from the given node.
+ :rtype: bool
+ """
+ return node in self.bases
+
+ def local_attr(self, name, context=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 exceptions.AttributeInferenceError(
+ target=self, attribute=name, context=context
+ )
+
+ def instance_attr(self, name, context=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 exceptions.AttributeInferenceError(
+ target=self, attribute=name, context=context
+ )
+
+ def instantiate_class(self):
+ """Get an :class:`Instance` of the :class:`ClassDef` node.
+
+ :returns: An :class:`Instance` of the :class:`ClassDef` node,
+ or self if this is not possible.
+ :rtype: Instance or ClassDef
+ """
+ return bases.Instance(self)
+
+ def getattr(self, name, context=None, class_context=True):
+ """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.
+ :type name: str
+
+ :param class_context: Whether the attribute can be accessed statically.
+ :type class_context: bool
+
+ :returns: The attribute.
+ :rtype: list(NodeNG)
+
+ :raises AttributeInferenceError: If the attribute cannot be inferred.
+ """
+ values = self.locals.get(name, [])
+ if name in self.special_attributes and class_context and not values:
+ result = [self.special_attributes.lookup(name)]
+ if name == "__bases__":
+ # Need special treatment, since they are mutable
+ # and we need to return all the values.
+ result += values
+ return result
+
+ # don't modify the list in self.locals!
+ values = list(values)
+ for classnode in self.ancestors(recurs=True, context=context):
+ values += classnode.locals.get(name, [])
+
+ if class_context:
+ values += self._metaclass_lookup_attribute(name, context)
+
+ if not values:
+ raise exceptions.AttributeInferenceError(
+ target=self, attribute=name, context=context
+ )
+
+ # Look for AnnAssigns, which are not attributes in the purest sense.
+ for value in values:
+ if isinstance(value, node_classes.AssignName):
+ stmt = value.statement()
+ if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None:
+ raise exceptions.AttributeInferenceError(
+ target=self, attribute=name, context=context
+ )
+ return values
+
+ 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()
+ metaclass = self.metaclass()
+ for cls in {implicit_meta, metaclass}:
+ if cls and cls != self and isinstance(cls, ClassDef):
+ cls_attributes = self._get_attribute_from_metaclass(cls, name, context)
+ attrs.update(set(cls_attributes))
+ return attrs
+
+ def _get_attribute_from_metaclass(self, cls, name, context):
+ try:
+ attrs = cls.getattr(name, context=context, class_context=True)
+ except exceptions.AttributeInferenceError:
+ return
+
+ for attr in bases._infer_stmts(attrs, context, frame=cls):
+ if not isinstance(attr, FunctionDef):
+ yield attr
+ continue
+
+ if bases._is_property(attr):
+ yield from attr.infer_call_result(self, context)
+ 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, context=None, class_context=True):
+ """Infer the possible values of the given variable.
+
+ :param name: The name of the variable to infer.
+ :type name: str
+
+ :returns: The inferred possible values.
+ :rtype: iterable(NodeNG or Uninferable)
+ """
+ # set lookup name since this is necessary to infer on import nodes for
+ # instance
+ context = contextmod.copy_context(context)
+ context.lookupname = name
+ try:
+ attr = self.getattr(name, context, class_context=class_context)[0]
+ for inferred in bases._infer_stmts([attr], 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 exceptions.AttributeInferenceError:
+ yield inferred
+ else:
+ yield util.Uninferable
+ else:
+ yield function_to_method(inferred, self)
+ except exceptions.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 exceptions.InferenceError(
+ error.message, target=self, attribute=name, context=context
+ )
+
+ def has_dynamic_getattr(self, context=None):
+ """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: True if the class has a custom
+ __getattr__ or __getattribute__, False otherwise.
+ :rtype: bool
+ """
+
+ 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 exceptions.AttributeInferenceError:
+ # if self.newstyle: XXX cause an infinite recursion error
+ try:
+ getattribute = self.getattr("__getattribute__", context)[0]
+ return _valid_getattr(getattribute)
+ except exceptions.AttributeInferenceError:
+ pass
+ return False
+
+ def getitem(self, index, context=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 = dunder_lookup.lookup(self, "__getitem__")
+ except exceptions.AttributeInferenceError as exc:
+ raise exceptions.AstroidTypeError(node=self, context=context) from exc
+
+ method = methods[0]
+
+ # Create a new callcontext for providing index as an argument.
+ new_context = contextmod.bind_context_to_node(context, self)
+ new_context.callcontext = contextmod.CallContext(args=[index])
+
+ try:
+ return next(method.infer_call_result(self, new_context))
+ except exceptions.InferenceError:
+ return util.Uninferable
+
+ def methods(self):
+ """Iterate over all of the method defined in this class and its parents.
+
+ :returns: The methods defined on the class.
+ :rtype: iterable(FunctionDef)
+ """
+ done = {}
+ for astroid in itertools.chain(iter((self,)), self.ancestors()):
+ for meth in astroid.mymethods():
+ if meth.name in done:
+ continue
+ done[meth.name] = None
+ yield meth
+
+ def mymethods(self):
+ """Iterate over all of the method defined in this class only.
+
+ :returns: The methods defined on the class.
+ :rtype: iterable(FunctionDef)
+ """
+ for member in self.values():
+ if isinstance(member, FunctionDef):
+ yield member
+
+ def implicit_metaclass(self):
+ """Get the implicit metaclass of the current class.
+
+ For newstyle classes, this will return an instance of builtins.type.
+ For oldstyle classes, it will simply return None, since there's
+ no implicit metaclass there.
+
+ :returns: The metaclass.
+ :rtype: builtins.type or None
+ """
+ if self.newstyle:
+ return builtin_lookup("type")[1][0]
+ return None
+
+ _metaclass = None
+
+ def declared_metaclass(self, context=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.
+ :rtype: NodeNG or None
+ """
+ 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 exceptions.InferenceError:
+ pass
+
+ if self._metaclass:
+ # Expects this from Py3k TreeRebuilder
+ try:
+ return next(
+ node
+ for node in self._metaclass.infer(context=context)
+ if node is not util.Uninferable
+ )
+ except (exceptions.InferenceError, StopIteration):
+ return None
+
+ return None
+
+ def _find_metaclass(self, seen=None, context=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=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.
+ :rtype: NodeNG or None
+ """
+ return self._find_metaclass(context=context)
+
+ def has_metaclass_hack(self):
+ return self._metaclass_hack
+
+ def _islots(self):
+ """ Return an iterator with the inferred slots. """
+ if "__slots__" not in self.locals:
+ return None
+ for slots in self.igetattr("__slots__"):
+ # check if __slots__ is a valid type
+ for meth in ITER_METHODS:
+ try:
+ slots.getattr(meth)
+ break
+ except exceptions.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 values is util.Uninferable:
+ 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 inferred is util.Uninferable:
+ continue
+ if not isinstance(
+ inferred, node_classes.Const
+ ) or not isinstance(inferred.value, str):
+ continue
+ if not inferred.value:
+ continue
+ yield inferred
+ except exceptions.InferenceError:
+ continue
+
+ return None
+
+ def _slots(self):
+ if not self.newstyle:
+ raise NotImplementedError(
+ "The concept of slots is undefined for old-style classes."
+ )
+
+ slots = self._islots()
+ try:
+ first = next(slots)
+ except StopIteration as exc:
+ # The class doesn't have a __slots__ definition or empty slots.
+ if exc.args and exc.args[0] not in ("", None):
+ return exc.args[0]
+ return None
+ return [first] + list(slots)
+
+ # Cached, because inferring them all the time is expensive
+ @decorators_mod.cached
+ def 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():
+ # Not interested in object, since it can't have slots.
+ for cls in self.mro()[:-1]:
+ try:
+ cls_slots = cls._slots()
+ except NotImplementedError:
+ continue
+ if cls_slots is not None:
+ yield from cls_slots
+ else:
+ yield None
+
+ if not self.newstyle:
+ raise NotImplementedError(
+ "The concept of slots is undefined for old-style classes."
+ )
+
+ slots = list(grouped_slots())
+ if not all(slot is not None for slot in slots):
+ return None
+
+ return sorted(slots, key=lambda item: item.value)
+
+ def _inferred_bases(self, context=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 = contextmod.InferenceContext()
+ if not self.bases and self.qname() != "builtins.object":
+ yield builtin_lookup("object")[1][0]
+ return
+
+ for stmt in self.bases:
+ try:
+ baseobj = next(stmt.infer(context=context))
+ except exceptions.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=None):
+ inferred_bases = list(self._inferred_bases(context=context))
+ bases_mro = []
+ for base in inferred_bases:
+ if base is self:
+ continue
+
+ try:
+ mro = base._compute_mro(context=context)
+ bases_mro.append(mro)
+ except NotImplementedError:
+ # Some classes have in their ancestors both newstyle and
+ # old style classes. For these we can't retrieve the .mro,
+ # although in Python it's possible, since the class we are
+ # currently working is in fact new style.
+ # So, we fallback to ancestors here.
+ ancestors = list(base.ancestors(context=context))
+ bases_mro.append(ancestors)
+
+ unmerged_mro = [[self]] + bases_mro + [inferred_bases]
+ unmerged_mro = list(clean_duplicates_mro(unmerged_mro, self, context))
+ return _c3_merge(unmerged_mro, self, context)
+
+ def mro(self, context=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):
+ """Determine the boolean value of this node.
+
+ :returns: The boolean value of this node.
+ For a :class:`ClassDef` this is always ``True``.
+ :rtype: bool
+ """
+ return True
+
+ def get_children(self):
+ if self.decorators is not None:
+ yield self.decorators
+
+ yield from self.bases
+ yield from self.body
+
+ @decorators_mod.cached
+ def _get_assign_nodes(self):
+ children_assign_nodes = (
+ child_node._get_assign_nodes() for child_node in self.body
+ )
+ return list(itertools.chain.from_iterable(children_assign_nodes))
diff --git a/venv/Lib/site-packages/astroid/test_utils.py b/venv/Lib/site-packages/astroid/test_utils.py
new file mode 100644
index 0000000..6c965ef
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/test_utils.py
@@ -0,0 +1,73 @@
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+"""Utility functions for test code that uses astroid ASTs as input."""
+import contextlib
+import functools
+import sys
+import warnings
+
+import pytest
+
+from astroid import nodes
+
+
+def require_version(minver=None, maxver=None):
+ """ Compare version of python interpreter to the given one. Skip the test
+ if older.
+ """
+
+ def parse(string, default=None):
+ string = string or default
+ try:
+ return tuple(int(v) for v in string.split("."))
+ except ValueError as exc:
+ raise ValueError(
+ "{string} is not a correct version : should be X.Y[.Z].".format(
+ string=string
+ )
+ ) from exc
+
+ def check_require_version(f):
+ current = sys.version_info[:3]
+ if parse(minver, "0") < current <= parse(maxver, "4"):
+ return f
+
+ str_version = ".".join(str(v) for v in sys.version_info)
+
+ @functools.wraps(f)
+ def new_f(*args, **kwargs):
+ if minver is not None:
+ pytest.skip(
+ "Needs Python > %s. Current version is %s." % (minver, str_version)
+ )
+ elif maxver is not None:
+ pytest.skip(
+ "Needs Python <= %s. Current version is %s." % (maxver, str_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)
diff --git a/venv/Lib/site-packages/astroid/transforms.py b/venv/Lib/site-packages/astroid/transforms.py
new file mode 100644
index 0000000..e5506cc
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/transforms.py
@@ -0,0 +1,90 @@
+# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+
+import collections
+from functools import lru_cache
+
+
+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.
+ """
+
+ TRANSFORM_MAX_CACHE_SIZE = 10000
+
+ def __init__(self):
+ self.transforms = collections.defaultdict(list)
+
+ @lru_cache(maxsize=TRANSFORM_MAX_CACHE_SIZE)
+ def _transform(self, node):
+ """Call matching transforms for the given node if any and return the
+ transformed node.
+ """
+ cls = node.__class__
+ if cls not in self.transforms:
+ # no transform registered for this class of node
+ return node
+
+ transforms = self.transforms[cls]
+ for transform_func, predicate in transforms:
+ 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:
+ node = ret
+ if ret.__class__ != cls:
+ # Can no longer apply the rest of the transforms.
+ break
+ return node
+
+ def _visit(self, node):
+ if hasattr(node, "_astroid_fields"):
+ for name in node._astroid_fields:
+ value = getattr(node, name)
+ visited = self._visit_generic(value)
+ if visited != value:
+ setattr(node, name, visited)
+ return self._transform(node)
+
+ def _visit_generic(self, 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 not node or isinstance(node, str):
+ return node
+
+ return self._visit(node)
+
+ def register_transform(self, node_class, transform, predicate=None):
+ """Register `transform(node)` function to be applied on the given
+ astroid's `node_class` 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))
+
+ def unregister_transform(self, node_class, transform, predicate=None):
+ """Unregister the given transform."""
+ self.transforms[node_class].remove((transform, predicate))
+
+ def visit(self, module):
+ """Walk the given astroid *tree* and transform each encountered node
+
+ Only the nodes which have transforms registered will actually
+ be replaced or changed.
+ """
+ module.body = [self._visit(child) for child in module.body]
+ return self._transform(module)
diff --git a/venv/Lib/site-packages/astroid/util.py b/venv/Lib/site-packages/astroid/util.py
new file mode 100644
index 0000000..3ab7561
--- /dev/null
+++ b/venv/Lib/site-packages/astroid/util.py
@@ -0,0 +1,164 @@
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
+# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
+
+import warnings
+from itertools import islice
+
+import importlib
+import lazy_object_proxy
+
+
+def lazy_descriptor(obj):
+ class DescriptorProxy(lazy_object_proxy.Proxy):
+ def __get__(self, instance, owner=None):
+ return self.__class__.__get__(self, instance)
+
+ return DescriptorProxy(obj)
+
+
+def lazy_import(module_name):
+ return lazy_object_proxy.Proxy(
+ lambda: importlib.import_module("." + module_name, "astroid")
+ )
+
+
+@object.__new__
+class Uninferable:
+ """Special inference object, which is returned when inference fails."""
+
+ def __repr__(self):
+ return "Uninferable"
+
+ __str__ = __repr__
+
+ def __getattribute__(self, name):
+ 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, **kwargs):
+ return self
+
+ def __bool__(self):
+ return False
+
+ __nonzero__ = __bool__
+
+ def accept(self, visitor):
+ func = getattr(visitor, "visit_uninferable")
+ return func(self)
+
+
+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):
+ helpers = lazy_import("helpers")
+ return helpers.object_type
+
+ def _object_type(self, obj):
+ # pylint: disable=not-callable; can't infer lazy_import
+ objtype = self._object_type_helper(obj)
+ if objtype is Uninferable:
+ return None
+
+ return objtype
+
+ def __str__(self):
+ 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):
+ 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):
+ 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 proxy_alias(alias_name, node_type):
+ """Get a Proxy from the given name to the given node type."""
+ proxy = type(
+ alias_name,
+ (lazy_object_proxy.Proxy,),
+ {
+ "__class__": object.__dict__["__class__"],
+ "__instancecheck__": _instancecheck,
+ },
+ )
+ return proxy(lambda: node_type)
+
+
+def limit_inference(iterator, size):
+ """Limit inference amount.
+
+ Limit inference amount to help with performance issues with
+ exponentially exploding possible results.
+
+ :param iterator: Inference generator to limit
+ :type iterator: Iterator(NodeNG)
+
+ :param size: Maximum mount of nodes yielded plus an
+ Uninferable at the end if limit reached
+ :type size: int
+
+ :yields: A possibly modified generator
+ :rtype param: Iterable
+ """
+ yield from islice(iterator, size)
+ has_more = next(iterator, False)
+ if has_more is not False:
+ yield Uninferable
+ return
diff --git a/venv/Lib/site-packages/colorama-0.4.3.dist-info/INSTALLER b/venv/Lib/site-packages/colorama-0.4.3.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/Lib/site-packages/colorama-0.4.3.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/Lib/site-packages/colorama-0.4.3.dist-info/LICENSE.txt b/venv/Lib/site-packages/colorama-0.4.3.dist-info/LICENSE.txt
new file mode 100644
index 0000000..3105888
--- /dev/null
+++ b/venv/Lib/site-packages/colorama-0.4.3.dist-info/LICENSE.txt
@@ -0,0 +1,27 @@
+Copyright (c) 2010 Jonathan Hartley
+All rights reserved.
+
+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 name of the copyright holders, nor those of its 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 HOLDER 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/site-packages/colorama-0.4.3.dist-info/METADATA b/venv/Lib/site-packages/colorama-0.4.3.dist-info/METADATA
new file mode 100644
index 0000000..c455cb5
--- /dev/null
+++ b/venv/Lib/site-packages/colorama-0.4.3.dist-info/METADATA
@@ -0,0 +1,411 @@
+Metadata-Version: 2.1
+Name: colorama
+Version: 0.4.3
+Summary: Cross-platform colored terminal text.
+Home-page: https://github.com/tartley/colorama
+Author: Jonathan Hartley
+Author-email: tartley@tartley.com
+Maintainer: Arnon Yaari
+License: BSD
+Keywords: color colour terminal text ansi windows crossplatform xplatform
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Terminals
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
+
+.. image:: https://img.shields.io/pypi/v/colorama.svg
+ :target: https://pypi.org/project/colorama/
+ :alt: Latest Version
+
+.. image:: https://img.shields.io/pypi/pyversions/colorama.svg
+ :target: https://pypi.org/project/colorama/
+ :alt: Supported Python versions
+
+.. image:: https://travis-ci.org/tartley/colorama.svg?branch=master
+ :target: https://travis-ci.org/tartley/colorama
+ :alt: Build Status
+
+Download and docs:
+ https://pypi.org/project/colorama/
+Source code & Development:
+ https://github.com/tartley/colorama
+Colorama for Enterprise:
+ https://github.com/tartley/colorama/blob/master/ENTERPRISE.md
+
+Description
+===========
+
+Makes ANSI escape character sequences (for producing colored terminal text and
+cursor positioning) work under MS Windows.
+
+ANSI escape character sequences have long been used to produce colored terminal
+text and cursor positioning on Unix and Macs. Colorama makes this work on
+Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which
+would appear as gobbledygook in the output), and converting them into the
+appropriate win32 calls to modify the state of the terminal. On other platforms,
+Colorama does nothing.
+
+Colorama also provides some shortcuts to help generate ANSI sequences
+but works fine in conjunction with any other ANSI sequence generation library,
+such as the venerable Termcolor (https://pypi.org/project/termcolor/)
+or the fabulous Blessings (https://pypi.org/project/blessings/).
+
+This has the upshot of providing a simple cross-platform API for printing
+colored terminal text from Python, and has the happy side-effect that existing
+applications or libraries which use ANSI sequences to produce colored output on
+Linux or Macs can now also work on Windows, simply by calling
+``colorama.init()``.
+
+An alternative approach is to install ``ansi.sys`` on Windows machines, which
+provides the same behaviour for all applications running in terminals. Colorama
+is intended for situations where that isn't easy (e.g., maybe your app doesn't
+have an installer.)
+
+Demo scripts in the source code repository print some colored text using
+ANSI sequences. Compare their output under Gnome-terminal's built in ANSI
+handling, versus on Windows Command-Prompt using Colorama:
+
+.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png
+ :width: 661
+ :height: 357
+ :alt: ANSI sequences on Ubuntu under gnome-terminal.
+
+.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png
+ :width: 668
+ :height: 325
+ :alt: Same ANSI sequences on Windows, using Colorama.
+
+These screengrabs show that, on Windows, Colorama does not support ANSI 'dim
+text'; it looks the same as 'normal text'.
+
+
+License
+=======
+
+Copyright Jonathan Hartley & Arnon Yaari, 2013. BSD 3-Clause license; see LICENSE file.
+
+
+Dependencies
+============
+
+None, other than Python. Tested on Python 2.7, 3.5, 3.6, 3.7 and 3.8.
+
+Usage
+=====
+
+Initialisation
+--------------
+
+Applications should initialise Colorama using:
+
+.. code-block:: python
+
+ from colorama import init
+ init()
+
+On Windows, calling ``init()`` will filter ANSI escape sequences out of any
+text sent to ``stdout`` or ``stderr``, and replace them with equivalent Win32
+calls.
+
+On other platforms, calling ``init()`` has no effect (unless you request other
+optional functionality; see "Init Keyword Args", below). By design, this permits
+applications to call ``init()`` unconditionally on all platforms, after which
+ANSI output should just work.
+
+To stop using colorama before your program exits, simply call ``deinit()``.
+This will restore ``stdout`` and ``stderr`` to their original values, so that
+Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is
+cheaper to calling ``init()`` again (but does the same thing).
+
+
+Colored Output
+--------------
+
+Cross-platform printing of colored text can then be done using Colorama's
+constant shorthand for ANSI escape sequences:
+
+.. code-block:: python
+
+ from colorama import Fore, Back, Style
+ print(Fore.RED + 'some red text')
+ print(Back.GREEN + 'and with a green background')
+ print(Style.DIM + 'and in dim text')
+ print(Style.RESET_ALL)
+ print('back to normal now')
+
+...or simply by manually printing ANSI sequences from your own code:
+
+.. code-block:: python
+
+ print('\033[31m' + 'some red text')
+ print('\033[39m') # and reset to default color
+
+...or, Colorama can be used happily in conjunction with existing ANSI libraries
+such as Termcolor:
+
+.. code-block:: python
+
+ from colorama import init
+ from termcolor import colored
+
+ # use Colorama to make Termcolor work on Windows too
+ init()
+
+ # then use Termcolor for all colored text output
+ print(colored('Hello, World!', 'green', 'on_red'))
+
+Available formatting constants are::
+
+ Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
+ Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
+ Style: DIM, NORMAL, BRIGHT, RESET_ALL
+
+``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will
+perform this reset automatically on program exit.
+
+
+Cursor Positioning
+------------------
+
+ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for
+an example of how to generate them.
+
+
+Init Keyword Args
+-----------------
+
+``init()`` accepts some ``**kwargs`` to override default behaviour.
+
+init(autoreset=False):
+ If you find yourself repeatedly sending reset sequences to turn off color
+ changes at the end of every print, then ``init(autoreset=True)`` will
+ automate that:
+
+ .. code-block:: python
+
+ from colorama import init
+ init(autoreset=True)
+ print(Fore.RED + 'some red text')
+ print('automatically back to default color again')
+
+init(strip=None):
+ Pass ``True`` or ``False`` to override whether ansi codes should be
+ stripped from the output. The default behaviour is to strip if on Windows
+ or if output is redirected (not a tty).
+
+init(convert=None):
+ Pass ``True`` or ``False`` to override whether to convert ANSI codes in the
+ output into win32 calls. The default behaviour is to convert if on Windows
+ and output is to a tty (terminal).
+
+init(wrap=True):
+ On Windows, colorama works by replacing ``sys.stdout`` and ``sys.stderr``
+ with proxy objects, which override the ``.write()`` method to do their work.
+ If this wrapping causes you problems, then this can be disabled by passing
+ ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or
+ ``strip`` or ``convert`` are True.
+
+ When wrapping is disabled, colored printing on non-Windows platforms will
+ continue to work as normal. To do cross-platform colored output, you can
+ use Colorama's ``AnsiToWin32`` proxy directly:
+
+ .. code-block:: python
+
+ import sys
+ from colorama import init, AnsiToWin32
+ init(wrap=False)
+ stream = AnsiToWin32(sys.stderr).stream
+
+ # Python 2
+ print >>stream, Fore.BLUE + 'blue text on stderr'
+
+ # Python 3
+ print(Fore.BLUE + 'blue text on stderr', file=stream)
+
+
+Installation
+=======================
+colorama is currently installable from PyPI:
+
+ pip install colorama
+
+colorama also can be installed by the conda package manager:
+
+ conda install -c anaconda colorama
+
+
+Status & Known Problems
+=======================
+
+I've personally only tested it on Windows XP (CMD, Console2), Ubuntu
+(gnome-terminal, xterm), and OS X.
+
+Some presumably valid ANSI sequences aren't recognised (see details below),
+but to my knowledge nobody has yet complained about this. Puzzling.
+
+See outstanding issues and wishlist:
+https://github.com/tartley/colorama/issues
+
+If anything doesn't work for you, or doesn't do what you expected or hoped for,
+I'd love to hear about it on that issues list, would be delighted by patches,
+and would be happy to grant commit access to anyone who submits a working patch
+or two.
+
+
+Recognised ANSI Sequences
+=========================
+
+ANSI sequences generally take the form:
+
+ ESC [ <param> ; <param> ... <command>
+
+Where ``<param>`` is an integer, and ``<command>`` is a single letter. Zero or
+more params are passed to a ``<command>``. If no params are passed, it is
+generally synonymous with passing a single zero. No spaces exist in the
+sequence; they have been inserted here simply to read more easily.
+
+The only ANSI sequences that colorama converts into win32 calls are::
+
+ ESC [ 0 m # reset all (colors and brightness)
+ ESC [ 1 m # bright
+ ESC [ 2 m # dim (looks same as normal brightness)
+ ESC [ 22 m # normal brightness
+
+ # FOREGROUND:
+ ESC [ 30 m # black
+ ESC [ 31 m # red
+ ESC [ 32 m # green
+ ESC [ 33 m # yellow
+ ESC [ 34 m # blue
+ ESC [ 35 m # magenta
+ ESC [ 36 m # cyan
+ ESC [ 37 m # white
+ ESC [ 39 m # reset
+
+ # BACKGROUND
+ ESC [ 40 m # black
+ ESC [ 41 m # red
+ ESC [ 42 m # green
+ ESC [ 43 m # yellow
+ ESC [ 44 m # blue
+ ESC [ 45 m # magenta
+ ESC [ 46 m # cyan
+ ESC [ 47 m # white
+ ESC [ 49 m # reset
+
+ # cursor positioning
+ ESC [ y;x H # position cursor at x across, y down
+ ESC [ y;x f # position cursor at x across, y down
+ ESC [ n A # move cursor n lines up
+ ESC [ n B # move cursor n lines down
+ ESC [ n C # move cursor n characters forward
+ ESC [ n D # move cursor n characters backward
+
+ # clear the screen
+ ESC [ mode J # clear the screen
+
+ # clear the line
+ ESC [ mode K # clear the line
+
+Multiple numeric params to the ``'m'`` command can be combined into a single
+sequence::
+
+ ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background
+
+All other ANSI sequences of the form ``ESC [ <param> ; <param> ... <command>``
+are silently stripped from the output on Windows.
+
+Any other form of ANSI sequence, such as single-character codes or alternative
+initial characters, are not recognised or stripped. It would be cool to add
+them though. Let me know if it would be useful for you, via the Issues on
+GitHub.
+
+
+Development
+===========
+
+Help and fixes welcome!
+
+Running tests requires:
+
+- Michael Foord's ``mock`` module to be installed.
+- Tests are written using 2010-era updates to ``unittest``
+
+To run tests::
+
+ python -m unittest discover -p *_test.py
+
+This, like a few other handy commands, is captured in a ``Makefile``.
+
+If you use nose to run the tests, you must pass the ``-s`` flag; otherwise,
+``nosetests`` applies its own proxy to ``stdout``, which confuses the unit
+tests.
+
+
+Professional support
+====================
+
+.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png
+ :alt: Tidelift
+ :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme
+
+.. list-table::
+ :widths: 10 100
+
+ * - |tideliftlogo|
+ - Professional support for colorama 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-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme
+
+
+Thanks
+======
+* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5.
+* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``,
+ providing a solution to issue #7's setuptools/distutils debate,
+ and other fixes.
+* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``.
+* Matthew McCormick for politely pointing out a longstanding crash on non-Win.
+* Ben Hoyt, for a magnificent fix under 64-bit Windows.
+* Jesse at Empty Square for submitting a fix for examples in the README.
+* User 'jamessp', an observant documentation fix for cursor positioning.
+* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7
+ fix.
+* Julien Stuyck, for wisely suggesting Python3 compatible updates to README.
+* Daniel Griffith for multiple fabulous patches.
+* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty
+ output.
+* Roger Binns, for many suggestions, valuable feedback, & bug reports.
+* Tim Golden for thought and much appreciated feedback on the initial idea.
+* User 'Zearin' for updates to the README file.
+* John Szakmeister for adding support for light colors
+* Charles Merriam for adding documentation to demos
+* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes
+* Florian Bruhin for a fix when stdout or stderr are None
+* Thomas Weininger for fixing ValueError on Windows
+* Remi Rampin for better Github integration and fixes to the README file
+* Simeon Visser for closing a file handle using 'with' and updating classifiers
+ to include Python 3.3 and 3.4
+* Andy Neff for fixing RESET of LIGHT_EX colors.
+* Jonathan Hartley for the initial idea and implementation.
+
+
diff --git a/venv/Lib/site-packages/colorama-0.4.3.dist-info/RECORD b/venv/Lib/site-packages/colorama-0.4.3.dist-info/RECORD
new file mode 100644
index 0000000..64d8939
--- /dev/null
+++ b/venv/Lib/site-packages/colorama-0.4.3.dist-info/RECORD
@@ -0,0 +1,18 @@
+colorama-0.4.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+colorama-0.4.3.dist-info/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491
+colorama-0.4.3.dist-info/METADATA,sha256=-ovqULHfBHs9pV2e_Ua8-w2VSVLno-6x36SXSTsWvSc,14432
+colorama-0.4.3.dist-info/RECORD,,
+colorama-0.4.3.dist-info/WHEEL,sha256=8zNYZbwQSXoB9IfXOjPfeNwvAsALAjffgk27FqvCWbo,110
+colorama-0.4.3.dist-info/top_level.txt,sha256=_Kx6-Cni2BT1PEATPhrSRxo0d7kSgfBbHf5o7IF1ABw,9
+colorama/__init__.py,sha256=DqjXH9URVP3IJwmMt7peYw50ns1RNAymIB9-XdPEFV8,239
+colorama/__pycache__/__init__.cpython-37.pyc,,
+colorama/__pycache__/ansi.cpython-37.pyc,,
+colorama/__pycache__/ansitowin32.cpython-37.pyc,,
+colorama/__pycache__/initialise.cpython-37.pyc,,
+colorama/__pycache__/win32.cpython-37.pyc,,
+colorama/__pycache__/winterm.cpython-37.pyc,,
+colorama/ansi.py,sha256=Fi0un-QLqRm-v7o_nKiOqyC8PapBJK7DLV_q9LKtTO0,2524
+colorama/ansitowin32.py,sha256=u8QaqdqS_xYSfNkPM1eRJLHz6JMWPodaJaP0mxgHCDc,10462
+colorama/initialise.py,sha256=PprovDNxMTrvoNHFcL2NZjpH2XzDc8BLxLxiErfUl4k,1915
+colorama/win32.py,sha256=bJ8Il9jwaBN5BJ8bmN6FoYZ1QYuMKv2j8fGrXh7TJjw,5404
+colorama/winterm.py,sha256=2y_2b7Zsv34feAsP67mLOVc-Bgq51mdYGo571VprlrM,6438
diff --git a/venv/Lib/site-packages/colorama-0.4.3.dist-info/WHEEL b/venv/Lib/site-packages/colorama-0.4.3.dist-info/WHEEL
new file mode 100644
index 0000000..8b701e9
--- /dev/null
+++ b/venv/Lib/site-packages/colorama-0.4.3.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.6)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/Lib/site-packages/colorama-0.4.3.dist-info/top_level.txt b/venv/Lib/site-packages/colorama-0.4.3.dist-info/top_level.txt
new file mode 100644
index 0000000..3fcfb51
--- /dev/null
+++ b/venv/Lib/site-packages/colorama-0.4.3.dist-info/top_level.txt
@@ -0,0 +1 @@
+colorama
diff --git a/venv/Lib/site-packages/colorama/__init__.py b/venv/Lib/site-packages/colorama/__init__.py
new file mode 100644
index 0000000..34c263c
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/__init__.py
@@ -0,0 +1,6 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+from .initialise import init, deinit, reinit, colorama_text
+from .ansi import Fore, Back, Style, Cursor
+from .ansitowin32 import AnsiToWin32
+
+__version__ = '0.4.3'
diff --git a/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..270deaf
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-37.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-37.pyc
new file mode 100644
index 0000000..c0942e2
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/__pycache__/ansi.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-37.pyc b/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-37.pyc
new file mode 100644
index 0000000..152004e
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/__pycache__/ansitowin32.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-37.pyc b/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-37.pyc
new file mode 100644
index 0000000..671073a
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/__pycache__/initialise.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-37.pyc b/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-37.pyc
new file mode 100644
index 0000000..2982108
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/__pycache__/win32.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-37.pyc b/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-37.pyc
new file mode 100644
index 0000000..1ebf75c
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/__pycache__/winterm.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/colorama/ansi.py b/venv/Lib/site-packages/colorama/ansi.py
new file mode 100644
index 0000000..7877658
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/ansi.py
@@ -0,0 +1,102 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+'''
+This module generates ANSI character codes to printing colors to terminals.
+See: http://en.wikipedia.org/wiki/ANSI_escape_code
+'''
+
+CSI = '\033['
+OSC = '\033]'
+BEL = '\007'
+
+
+def code_to_chars(code):
+ return CSI + str(code) + 'm'
+
+def set_title(title):
+ return OSC + '2;' + title + BEL
+
+def clear_screen(mode=2):
+ return CSI + str(mode) + 'J'
+
+def clear_line(mode=2):
+ return CSI + str(mode) + 'K'
+
+
+class AnsiCodes(object):
+ def __init__(self):
+ # the subclasses declare class attributes which are numbers.
+ # Upon instantiation we define instance attributes, which are the same
+ # as the class attributes but wrapped with the ANSI escape sequence
+ for name in dir(self):
+ if not name.startswith('_'):
+ value = getattr(self, name)
+ setattr(self, name, code_to_chars(value))
+
+
+class AnsiCursor(object):
+ def UP(self, n=1):
+ return CSI + str(n) + 'A'
+ def DOWN(self, n=1):
+ return CSI + str(n) + 'B'
+ def FORWARD(self, n=1):
+ return CSI + str(n) + 'C'
+ def BACK(self, n=1):
+ return CSI + str(n) + 'D'
+ def POS(self, x=1, y=1):
+ return CSI + str(y) + ';' + str(x) + 'H'
+
+
+class AnsiFore(AnsiCodes):
+ BLACK = 30
+ RED = 31
+ GREEN = 32
+ YELLOW = 33
+ BLUE = 34
+ MAGENTA = 35
+ CYAN = 36
+ WHITE = 37
+ RESET = 39
+
+ # These are fairly well supported, but not part of the standard.
+ LIGHTBLACK_EX = 90
+ LIGHTRED_EX = 91
+ LIGHTGREEN_EX = 92
+ LIGHTYELLOW_EX = 93
+ LIGHTBLUE_EX = 94
+ LIGHTMAGENTA_EX = 95
+ LIGHTCYAN_EX = 96
+ LIGHTWHITE_EX = 97
+
+
+class AnsiBack(AnsiCodes):
+ BLACK = 40
+ RED = 41
+ GREEN = 42
+ YELLOW = 43
+ BLUE = 44
+ MAGENTA = 45
+ CYAN = 46
+ WHITE = 47
+ RESET = 49
+
+ # These are fairly well supported, but not part of the standard.
+ LIGHTBLACK_EX = 100
+ LIGHTRED_EX = 101
+ LIGHTGREEN_EX = 102
+ LIGHTYELLOW_EX = 103
+ LIGHTBLUE_EX = 104
+ LIGHTMAGENTA_EX = 105
+ LIGHTCYAN_EX = 106
+ LIGHTWHITE_EX = 107
+
+
+class AnsiStyle(AnsiCodes):
+ BRIGHT = 1
+ DIM = 2
+ NORMAL = 22
+ RESET_ALL = 0
+
+Fore = AnsiFore()
+Back = AnsiBack()
+Style = AnsiStyle()
+Cursor = AnsiCursor()
diff --git a/venv/Lib/site-packages/colorama/ansitowin32.py b/venv/Lib/site-packages/colorama/ansitowin32.py
new file mode 100644
index 0000000..359c92b
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/ansitowin32.py
@@ -0,0 +1,257 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import re
+import sys
+import os
+
+from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style
+from .winterm import WinTerm, WinColor, WinStyle
+from .win32 import windll, winapi_test
+
+
+winterm = None
+if windll is not None:
+ winterm = WinTerm()
+
+
+class StreamWrapper(object):
+ '''
+ Wraps a stream (such as stdout), acting as a transparent proxy for all
+ attribute access apart from method 'write()', which is delegated to our
+ Converter instance.
+ '''
+ def __init__(self, wrapped, converter):
+ # double-underscore everything to prevent clashes with names of
+ # attributes on the wrapped stream object.
+ self.__wrapped = wrapped
+ self.__convertor = converter
+
+ def __getattr__(self, name):
+ return getattr(self.__wrapped, name)
+
+ def __enter__(self, *args, **kwargs):
+ # special method lookup bypasses __getattr__/__getattribute__, see
+ # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit
+ # thus, contextlib magic methods are not proxied via __getattr__
+ return self.__wrapped.__enter__(*args, **kwargs)
+
+ def __exit__(self, *args, **kwargs):
+ return self.__wrapped.__exit__(*args, **kwargs)
+
+ def write(self, text):
+ self.__convertor.write(text)
+
+ def isatty(self):
+ stream = self.__wrapped
+ if 'PYCHARM_HOSTED' in os.environ:
+ if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__):
+ return True
+ try:
+ stream_isatty = stream.isatty
+ except AttributeError:
+ return False
+ else:
+ return stream_isatty()
+
+ @property
+ def closed(self):
+ stream = self.__wrapped
+ try:
+ return stream.closed
+ except AttributeError:
+ return True
+
+
+class AnsiToWin32(object):
+ '''
+ Implements a 'write()' method which, on Windows, will strip ANSI character
+ sequences from the text, and if outputting to a tty, will convert them into
+ win32 function calls.
+ '''
+ ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer
+ ANSI_OSC_RE = re.compile('\001?\033\\]((?:.|;)*?)(\x07)\002?') # Operating System Command
+
+ def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
+ # The wrapped stream (normally sys.stdout or sys.stderr)
+ self.wrapped = wrapped
+
+ # should we reset colors to defaults after every .write()
+ self.autoreset = autoreset
+
+ # create the proxy wrapping our output stream
+ self.stream = StreamWrapper(wrapped, self)
+
+ on_windows = os.name == 'nt'
+ # We test if the WinAPI works, because even if we are on Windows
+ # we may be using a terminal that doesn't support the WinAPI
+ # (e.g. Cygwin Terminal). In this case it's up to the terminal
+ # to support the ANSI codes.
+ conversion_supported = on_windows and winapi_test()
+
+ # should we strip ANSI sequences from our output?
+ if strip is None:
+ strip = conversion_supported or (not self.stream.closed and not self.stream.isatty())
+ self.strip = strip
+
+ # should we should convert ANSI sequences into win32 calls?
+ if convert is None:
+ convert = conversion_supported and not self.stream.closed and self.stream.isatty()
+ self.convert = convert
+
+ # dict of ansi codes to win32 functions and parameters
+ self.win32_calls = self.get_win32_calls()
+
+ # are we wrapping stderr?
+ self.on_stderr = self.wrapped is sys.stderr
+
+ def should_wrap(self):
+ '''
+ True if this class is actually needed. If false, then the output
+ stream will not be affected, nor will win32 calls be issued, so
+ wrapping stdout is not actually required. This will generally be
+ False on non-Windows platforms, unless optional functionality like
+ autoreset has been requested using kwargs to init()
+ '''
+ return self.convert or self.strip or self.autoreset
+
+ def get_win32_calls(self):
+ if self.convert and winterm:
+ return {
+ AnsiStyle.RESET_ALL: (winterm.reset_all, ),
+ AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),
+ AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),
+ AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),
+ AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),
+ AnsiFore.RED: (winterm.fore, WinColor.RED),
+ AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),
+ AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW),
+ AnsiFore.BLUE: (winterm.fore, WinColor.BLUE),
+ AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA),
+ AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
+ AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
+ AnsiFore.RESET: (winterm.fore, ),
+ AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True),
+ AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True),
+ AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True),
+ AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True),
+ AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True),
+ AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True),
+ AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True),
+ AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True),
+ AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
+ AnsiBack.RED: (winterm.back, WinColor.RED),
+ AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
+ AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW),
+ AnsiBack.BLUE: (winterm.back, WinColor.BLUE),
+ AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA),
+ AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
+ AnsiBack.WHITE: (winterm.back, WinColor.GREY),
+ AnsiBack.RESET: (winterm.back, ),
+ AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True),
+ AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True),
+ AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True),
+ AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True),
+ AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True),
+ AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True),
+ AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True),
+ AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True),
+ }
+ return dict()
+
+ def write(self, text):
+ if self.strip or self.convert:
+ self.write_and_convert(text)
+ else:
+ self.wrapped.write(text)
+ self.wrapped.flush()
+ if self.autoreset:
+ self.reset_all()
+
+
+ def reset_all(self):
+ if self.convert:
+ self.call_win32('m', (0,))
+ elif not self.strip and not self.stream.closed:
+ self.wrapped.write(Style.RESET_ALL)
+
+
+ def write_and_convert(self, text):
+ '''
+ Write the given text to our wrapped stream, stripping any ANSI
+ sequences from the text, and optionally converting them into win32
+ calls.
+ '''
+ cursor = 0
+ text = self.convert_osc(text)
+ for match in self.ANSI_CSI_RE.finditer(text):
+ start, end = match.span()
+ self.write_plain_text(text, cursor, start)
+ self.convert_ansi(*match.groups())
+ cursor = end
+ self.write_plain_text(text, cursor, len(text))
+
+
+ def write_plain_text(self, text, start, end):
+ if start < end:
+ self.wrapped.write(text[start:end])
+ self.wrapped.flush()
+
+
+ def convert_ansi(self, paramstring, command):
+ if self.convert:
+ params = self.extract_params(command, paramstring)
+ self.call_win32(command, params)
+
+
+ def extract_params(self, command, paramstring):
+ if command in 'Hf':
+ params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';'))
+ while len(params) < 2:
+ # defaults:
+ params = params + (1,)
+ else:
+ params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0)
+ if len(params) == 0:
+ # defaults:
+ if command in 'JKm':
+ params = (0,)
+ elif command in 'ABCD':
+ params = (1,)
+
+ return params
+
+
+ def call_win32(self, command, params):
+ if command == 'm':
+ for param in params:
+ if param in self.win32_calls:
+ func_args = self.win32_calls[param]
+ func = func_args[0]
+ args = func_args[1:]
+ kwargs = dict(on_stderr=self.on_stderr)
+ func(*args, **kwargs)
+ elif command in 'J':
+ winterm.erase_screen(params[0], on_stderr=self.on_stderr)
+ elif command in 'K':
+ winterm.erase_line(params[0], on_stderr=self.on_stderr)
+ elif command in 'Hf': # cursor position - absolute
+ winterm.set_cursor_position(params, on_stderr=self.on_stderr)
+ elif command in 'ABCD': # cursor position - relative
+ n = params[0]
+ # A - up, B - down, C - forward, D - back
+ x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command]
+ winterm.cursor_adjust(x, y, on_stderr=self.on_stderr)
+
+
+ def convert_osc(self, text):
+ for match in self.ANSI_OSC_RE.finditer(text):
+ start, end = match.span()
+ text = text[:start] + text[end:]
+ paramstring, command = match.groups()
+ if command in '\x07': # \x07 = BEL
+ params = paramstring.split(";")
+ # 0 - change title and icon (we will only change title)
+ # 1 - change icon (we don't support this)
+ # 2 - change title
+ if params[0] in '02':
+ winterm.set_title(params[1])
+ return text
diff --git a/venv/Lib/site-packages/colorama/initialise.py b/venv/Lib/site-packages/colorama/initialise.py
new file mode 100644
index 0000000..430d066
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/initialise.py
@@ -0,0 +1,80 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+import atexit
+import contextlib
+import sys
+
+from .ansitowin32 import AnsiToWin32
+
+
+orig_stdout = None
+orig_stderr = None
+
+wrapped_stdout = None
+wrapped_stderr = None
+
+atexit_done = False
+
+
+def reset_all():
+ if AnsiToWin32 is not None: # Issue #74: objects might become None at exit
+ AnsiToWin32(orig_stdout).reset_all()
+
+
+def init(autoreset=False, convert=None, strip=None, wrap=True):
+
+ if not wrap and any([autoreset, convert, strip]):
+ raise ValueError('wrap=False conflicts with any other arg=True')
+
+ global wrapped_stdout, wrapped_stderr
+ global orig_stdout, orig_stderr
+
+ orig_stdout = sys.stdout
+ orig_stderr = sys.stderr
+
+ if sys.stdout is None:
+ wrapped_stdout = None
+ else:
+ sys.stdout = wrapped_stdout = \
+ wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
+ if sys.stderr is None:
+ wrapped_stderr = None
+ else:
+ sys.stderr = wrapped_stderr = \
+ wrap_stream(orig_stderr, convert, strip, autoreset, wrap)
+
+ global atexit_done
+ if not atexit_done:
+ atexit.register(reset_all)
+ atexit_done = True
+
+
+def deinit():
+ if orig_stdout is not None:
+ sys.stdout = orig_stdout
+ if orig_stderr is not None:
+ sys.stderr = orig_stderr
+
+
+@contextlib.contextmanager
+def colorama_text(*args, **kwargs):
+ init(*args, **kwargs)
+ try:
+ yield
+ finally:
+ deinit()
+
+
+def reinit():
+ if wrapped_stdout is not None:
+ sys.stdout = wrapped_stdout
+ if wrapped_stderr is not None:
+ sys.stderr = wrapped_stderr
+
+
+def wrap_stream(stream, convert, strip, autoreset, wrap):
+ if wrap:
+ wrapper = AnsiToWin32(stream,
+ convert=convert, strip=strip, autoreset=autoreset)
+ if wrapper.should_wrap():
+ stream = wrapper.stream
+ return stream
diff --git a/venv/Lib/site-packages/colorama/win32.py b/venv/Lib/site-packages/colorama/win32.py
new file mode 100644
index 0000000..c2d8360
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/win32.py
@@ -0,0 +1,152 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+
+# from winbase.h
+STDOUT = -11
+STDERR = -12
+
+try:
+ import ctypes
+ from ctypes import LibraryLoader
+ windll = LibraryLoader(ctypes.WinDLL)
+ from ctypes import wintypes
+except (AttributeError, ImportError):
+ windll = None
+ SetConsoleTextAttribute = lambda *_: None
+ winapi_test = lambda *_: None
+else:
+ from ctypes import byref, Structure, c_char, POINTER
+
+ COORD = wintypes._COORD
+
+ class CONSOLE_SCREEN_BUFFER_INFO(Structure):
+ """struct in wincon.h."""
+ _fields_ = [
+ ("dwSize", COORD),
+ ("dwCursorPosition", COORD),
+ ("wAttributes", wintypes.WORD),
+ ("srWindow", wintypes.SMALL_RECT),
+ ("dwMaximumWindowSize", COORD),
+ ]
+ def __str__(self):
+ return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (
+ self.dwSize.Y, self.dwSize.X
+ , self.dwCursorPosition.Y, self.dwCursorPosition.X
+ , self.wAttributes
+ , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right
+ , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X
+ )
+
+ _GetStdHandle = windll.kernel32.GetStdHandle
+ _GetStdHandle.argtypes = [
+ wintypes.DWORD,
+ ]
+ _GetStdHandle.restype = wintypes.HANDLE
+
+ _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
+ _GetConsoleScreenBufferInfo.argtypes = [
+ wintypes.HANDLE,
+ POINTER(CONSOLE_SCREEN_BUFFER_INFO),
+ ]
+ _GetConsoleScreenBufferInfo.restype = wintypes.BOOL
+
+ _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
+ _SetConsoleTextAttribute.argtypes = [
+ wintypes.HANDLE,
+ wintypes.WORD,
+ ]
+ _SetConsoleTextAttribute.restype = wintypes.BOOL
+
+ _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition
+ _SetConsoleCursorPosition.argtypes = [
+ wintypes.HANDLE,
+ COORD,
+ ]
+ _SetConsoleCursorPosition.restype = wintypes.BOOL
+
+ _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA
+ _FillConsoleOutputCharacterA.argtypes = [
+ wintypes.HANDLE,
+ c_char,
+ wintypes.DWORD,
+ COORD,
+ POINTER(wintypes.DWORD),
+ ]
+ _FillConsoleOutputCharacterA.restype = wintypes.BOOL
+
+ _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute
+ _FillConsoleOutputAttribute.argtypes = [
+ wintypes.HANDLE,
+ wintypes.WORD,
+ wintypes.DWORD,
+ COORD,
+ POINTER(wintypes.DWORD),
+ ]
+ _FillConsoleOutputAttribute.restype = wintypes.BOOL
+
+ _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW
+ _SetConsoleTitleW.argtypes = [
+ wintypes.LPCWSTR
+ ]
+ _SetConsoleTitleW.restype = wintypes.BOOL
+
+ def _winapi_test(handle):
+ csbi = CONSOLE_SCREEN_BUFFER_INFO()
+ success = _GetConsoleScreenBufferInfo(
+ handle, byref(csbi))
+ return bool(success)
+
+ def winapi_test():
+ return any(_winapi_test(h) for h in
+ (_GetStdHandle(STDOUT), _GetStdHandle(STDERR)))
+
+ def GetConsoleScreenBufferInfo(stream_id=STDOUT):
+ handle = _GetStdHandle(stream_id)
+ csbi = CONSOLE_SCREEN_BUFFER_INFO()
+ success = _GetConsoleScreenBufferInfo(
+ handle, byref(csbi))
+ return csbi
+
+ def SetConsoleTextAttribute(stream_id, attrs):
+ handle = _GetStdHandle(stream_id)
+ return _SetConsoleTextAttribute(handle, attrs)
+
+ def SetConsoleCursorPosition(stream_id, position, adjust=True):
+ position = COORD(*position)
+ # If the position is out of range, do nothing.
+ if position.Y <= 0 or position.X <= 0:
+ return
+ # Adjust for Windows' SetConsoleCursorPosition:
+ # 1. being 0-based, while ANSI is 1-based.
+ # 2. expecting (x,y), while ANSI uses (y,x).
+ adjusted_position = COORD(position.Y - 1, position.X - 1)
+ if adjust:
+ # Adjust for viewport's scroll position
+ sr = GetConsoleScreenBufferInfo(STDOUT).srWindow
+ adjusted_position.Y += sr.Top
+ adjusted_position.X += sr.Left
+ # Resume normal processing
+ handle = _GetStdHandle(stream_id)
+ return _SetConsoleCursorPosition(handle, adjusted_position)
+
+ def FillConsoleOutputCharacter(stream_id, char, length, start):
+ handle = _GetStdHandle(stream_id)
+ char = c_char(char.encode())
+ length = wintypes.DWORD(length)
+ num_written = wintypes.DWORD(0)
+ # Note that this is hard-coded for ANSI (vs wide) bytes.
+ success = _FillConsoleOutputCharacterA(
+ handle, char, length, start, byref(num_written))
+ return num_written.value
+
+ def FillConsoleOutputAttribute(stream_id, attr, length, start):
+ ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )'''
+ handle = _GetStdHandle(stream_id)
+ attribute = wintypes.WORD(attr)
+ length = wintypes.DWORD(length)
+ num_written = wintypes.DWORD(0)
+ # Note that this is hard-coded for ANSI (vs wide) bytes.
+ return _FillConsoleOutputAttribute(
+ handle, attribute, length, start, byref(num_written))
+
+ def SetConsoleTitle(title):
+ return _SetConsoleTitleW(title)
diff --git a/venv/Lib/site-packages/colorama/winterm.py b/venv/Lib/site-packages/colorama/winterm.py
new file mode 100644
index 0000000..0fdb4ec
--- /dev/null
+++ b/venv/Lib/site-packages/colorama/winterm.py
@@ -0,0 +1,169 @@
+# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
+from . import win32
+
+
+# from wincon.h
+class WinColor(object):
+ BLACK = 0
+ BLUE = 1
+ GREEN = 2
+ CYAN = 3
+ RED = 4
+ MAGENTA = 5
+ YELLOW = 6
+ GREY = 7
+
+# from wincon.h
+class WinStyle(object):
+ NORMAL = 0x00 # dim text, dim background
+ BRIGHT = 0x08 # bright text, dim background
+ BRIGHT_BACKGROUND = 0x80 # dim text, bright background
+
+class WinTerm(object):
+
+ def __init__(self):
+ self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes
+ self.set_attrs(self._default)
+ self._default_fore = self._fore
+ self._default_back = self._back
+ self._default_style = self._style
+ # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style.
+ # So that LIGHT_EX colors and BRIGHT style do not clobber each other,
+ # we track them separately, since LIGHT_EX is overwritten by Fore/Back
+ # and BRIGHT is overwritten by Style codes.
+ self._light = 0
+
+ def get_attrs(self):
+ return self._fore + self._back * 16 + (self._style | self._light)
+
+ def set_attrs(self, value):
+ self._fore = value & 7
+ self._back = (value >> 4) & 7
+ self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND)
+
+ def reset_all(self, on_stderr=None):
+ self.set_attrs(self._default)
+ self.set_console(attrs=self._default)
+ self._light = 0
+
+ def fore(self, fore=None, light=False, on_stderr=False):
+ if fore is None:
+ fore = self._default_fore
+ self._fore = fore
+ # Emulate LIGHT_EX with BRIGHT Style
+ if light:
+ self._light |= WinStyle.BRIGHT
+ else:
+ self._light &= ~WinStyle.BRIGHT
+ self.set_console(on_stderr=on_stderr)
+
+ def back(self, back=None, light=False, on_stderr=False):
+ if back is None:
+ back = self._default_back
+ self._back = back
+ # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style
+ if light:
+ self._light |= WinStyle.BRIGHT_BACKGROUND
+ else:
+ self._light &= ~WinStyle.BRIGHT_BACKGROUND
+ self.set_console(on_stderr=on_stderr)
+
+ def style(self, style=None, on_stderr=False):
+ if style is None:
+ style = self._default_style
+ self._style = style
+ self.set_console(on_stderr=on_stderr)
+
+ def set_console(self, attrs=None, on_stderr=False):
+ if attrs is None:
+ attrs = self.get_attrs()
+ handle = win32.STDOUT
+ if on_stderr:
+ handle = win32.STDERR
+ win32.SetConsoleTextAttribute(handle, attrs)
+
+ def get_position(self, handle):
+ position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
+ # Because Windows coordinates are 0-based,
+ # and win32.SetConsoleCursorPosition expects 1-based.
+ position.X += 1
+ position.Y += 1
+ return position
+
+ def set_cursor_position(self, position=None, on_stderr=False):
+ if position is None:
+ # I'm not currently tracking the position, so there is no default.
+ # position = self.get_position()
+ return
+ handle = win32.STDOUT
+ if on_stderr:
+ handle = win32.STDERR
+ win32.SetConsoleCursorPosition(handle, position)
+
+ def cursor_adjust(self, x, y, on_stderr=False):
+ handle = win32.STDOUT
+ if on_stderr:
+ handle = win32.STDERR
+ position = self.get_position(handle)
+ adjusted_position = (position.Y + y, position.X + x)
+ win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False)
+
+ def erase_screen(self, mode=0, on_stderr=False):
+ # 0 should clear from the cursor to the end of the screen.
+ # 1 should clear from the cursor to the beginning of the screen.
+ # 2 should clear the entire screen, and move cursor to (1,1)
+ handle = win32.STDOUT
+ if on_stderr:
+ handle = win32.STDERR
+ csbi = win32.GetConsoleScreenBufferInfo(handle)
+ # get the number of character cells in the current buffer
+ cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y
+ # get number of character cells before current cursor position
+ cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X
+ if mode == 0:
+ from_coord = csbi.dwCursorPosition
+ cells_to_erase = cells_in_screen - cells_before_cursor
+ elif mode == 1:
+ from_coord = win32.COORD(0, 0)
+ cells_to_erase = cells_before_cursor
+ elif mode == 2:
+ from_coord = win32.COORD(0, 0)
+ cells_to_erase = cells_in_screen
+ else:
+ # invalid mode
+ return
+ # fill the entire screen with blanks
+ win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
+ # now set the buffer's attributes accordingly
+ win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
+ if mode == 2:
+ # put the cursor where needed
+ win32.SetConsoleCursorPosition(handle, (1, 1))
+
+ def erase_line(self, mode=0, on_stderr=False):
+ # 0 should clear from the cursor to the end of the line.
+ # 1 should clear from the cursor to the beginning of the line.
+ # 2 should clear the entire line.
+ handle = win32.STDOUT
+ if on_stderr:
+ handle = win32.STDERR
+ csbi = win32.GetConsoleScreenBufferInfo(handle)
+ if mode == 0:
+ from_coord = csbi.dwCursorPosition
+ cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
+ elif mode == 1:
+ from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
+ cells_to_erase = csbi.dwCursorPosition.X
+ elif mode == 2:
+ from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
+ cells_to_erase = csbi.dwSize.X
+ else:
+ # invalid mode
+ return
+ # fill the entire screen with blanks
+ win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
+ # now set the buffer's attributes accordingly
+ win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
+
+ def set_title(self, title):
+ win32.SetConsoleTitle(title)
diff --git a/venv/Lib/site-packages/isort-4.3.21.dist-info/INSTALLER b/venv/Lib/site-packages/isort-4.3.21.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/Lib/site-packages/isort-4.3.21.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/Lib/site-packages/isort-4.3.21.dist-info/LICENSE b/venv/Lib/site-packages/isort-4.3.21.dist-info/LICENSE
new file mode 100644
index 0000000..b5083a5
--- /dev/null
+++ b/venv/Lib/site-packages/isort-4.3.21.dist-info/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/site-packages/isort-4.3.21.dist-info/METADATA b/venv/Lib/site-packages/isort-4.3.21.dist-info/METADATA
new file mode 100644
index 0000000..fbc7f6c
--- /dev/null
+++ b/venv/Lib/site-packages/isort-4.3.21.dist-info/METADATA
@@ -0,0 +1,697 @@
+Metadata-Version: 2.1
+Name: isort
+Version: 4.3.21
+Summary: A Python utility / library to sort Python imports.
+Home-page: https://github.com/timothycrosley/isort
+Author: Timothy Crosley
+Author-email: timothy.crosley@gmail.com
+License: MIT
+Keywords: Refactor,Python,Python2,Python3,Refactoring,Imports,Sort,Clean
+Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: Natural Language :: English
+Classifier: Environment :: Console
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+Requires-Dist: futures ; python_version < "3.2"
+Requires-Dist: backports.functools-lru-cache ; python_version < "3.2"
+Provides-Extra: pipfile
+Requires-Dist: pipreqs ; extra == 'pipfile'
+Requires-Dist: requirementslib ; extra == 'pipfile'
+Provides-Extra: pyproject
+Requires-Dist: toml ; extra == 'pyproject'
+Provides-Extra: requirements
+Requires-Dist: pipreqs ; extra == 'requirements'
+Requires-Dist: pip-api ; extra == 'requirements'
+Provides-Extra: xdg_home
+Requires-Dist: appdirs (>=1.4.0) ; extra == 'xdg_home'
+
+.. image:: https://raw.github.com/timothycrosley/isort/master/logo.png
+ :alt: isort
+
+########
+
+.. image:: https://badge.fury.io/py/isort.svg
+ :target: https://badge.fury.io/py/isort
+ :alt: PyPI version
+
+.. image:: https://travis-ci.org/timothycrosley/isort.svg?branch=master
+ :target: https://travis-ci.org/timothycrosley/isort
+ :alt: Build Status
+
+
+.. image:: https://coveralls.io/repos/timothycrosley/isort/badge.svg?branch=release%2F2.6.0&service=github
+ :target: https://coveralls.io/github/timothycrosley/isort?branch=release%2F2.6.0
+ :alt: Coverage
+
+.. image:: https://img.shields.io/github/license/mashape/apistatus.svg
+ :target: https://pypi.org/project/hug/
+ :alt: License
+
+.. image:: https://badges.gitter.im/Join%20Chat.svg
+ :alt: Join the chat at https://gitter.im/timothycrosley/isort
+ :target: https://gitter.im/timothycrosley/isort?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+
+.. image:: https://pepy.tech/badge/isort
+ :alt: Downloads
+ :target: https://pepy.tech/project/isort
+
+isort your python imports for you so you don't have to.
+
+isort is a Python utility / library to sort imports alphabetically, and automatically separated into sections.
+It provides a command line utility, Python library and `plugins for various editors <https://github.com/timothycrosley/isort/wiki/isort-Plugins>`_ to quickly sort all your imports.
+It currently cleanly supports Python 2.7 and 3.4+ without any dependencies.
+
+.. image:: https://raw.github.com/timothycrosley/isort/develop/example.gif
+ :alt: Example Usage
+
+Before isort:
+
+.. code-block:: python
+
+ from my_lib import Object
+
+ print("Hey")
+
+ 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("yo")
+
+After isort:
+
+.. code-block:: 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:
+
+.. code-block:: bash
+
+ pip install isort
+
+Install isort with requirements.txt support:
+
+.. code-block:: bash
+
+ pip install isort[requirements]
+
+Install isort with Pipfile support:
+
+.. code-block:: bash
+
+ pip install isort[pipfile]
+
+Install isort with both formats support:
+
+.. code-block:: bash
+
+ pip install isort[requirements,pipfile]
+
+Using isort
+===========
+
+**From the command line**:
+
+.. code-block:: bash
+
+ isort mypythonfile.py mypythonfile2.py
+
+or recursively:
+
+.. code-block:: bash
+
+ isort -rc .
+
+*which is equivalent to:*
+
+.. code-block:: bash
+
+ isort **/*.py
+
+or to see the proposed changes without applying them:
+
+.. code-block:: bash
+
+ isort mypythonfile.py --diff
+
+Finally, to atomically run isort against a project, only applying changes if they don't introduce syntax errors do:
+
+.. code-block:: bash
+
+ isort -rc --atomic .
+
+(Note: this is disabled by default as it keeps isort from being able to run against code written using a different version of Python)
+
+**From within Python**:
+
+.. code-block:: bash
+
+ from isort import SortImports
+
+ SortImports("pythonfile.py")
+
+or:
+
+.. code-block:: bash
+
+ from isort import SortImports
+
+ new_contents = SortImports(file_contents=old_contents).output
+
+**From within Kate:**
+
+.. code-block:: bash
+
+ ctrl+[
+
+or:
+
+.. code-block:: bash
+
+ menu > Python > Sort Imports
+
+Installing isort's Kate plugin
+==============================
+
+For KDE 4.13+ / Pate 2.0+:
+
+.. code-block:: bash
+
+ wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/isort_plugin.py --output-document ~/.kde/share/apps/kate/pate/isort_plugin.py
+ wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/isort_plugin_ui.rc --output-document ~/.kde/share/apps/kate/pate/isort_plugin_ui.rc
+ wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/katepart_isort.desktop --output-document ~/.kde/share/kde4/services/katepart_isort.desktop
+
+For all older versions:
+
+.. code-block:: bash
+
+ wget https://raw.github.com/timothycrosley/isort/master/kate_plugin/isort_plugin_old.py --output-document ~/.kde/share/apps/kate/pate/isort_plugin.py
+
+You will then need to restart kate and enable Python Plugins as well as the isort plugin itself.
+
+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/timothycrosley/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.
+
+How does isort work?
+====================
+
+isort parses specified files for global level import lines (imports outside of try / except blocks, functions, etc..)
+and puts them all at the top of the file grouped together by the type of import:
+
+- Future
+- Python Standard Library
+- Third Party
+- Current Python Project
+- Explicitly Local (. before import, as in: ``from . import x``)
+- Custom Separate Sections (Defined by forced_separate list in configuration file)
+- Custom Sections (Defined by sections list in configuration file)
+
+Inside of each section the imports are sorted alphabetically. isort automatically removes duplicate python imports,
+and wraps long from imports to the specified line length (defaults to 79).
+
+When will isort not work?
+=========================
+
+If you ever have the situation where you need to have a try / except block in the middle of top-level imports or if
+your import order is directly linked to precedence.
+
+For example: a common practice in Django settings files is importing * from various settings files to form
+a new settings file. In this case if any of the imports change order you are changing the settings definition itself.
+
+However, you can configure isort to skip over just these files - or even to force certain imports to the top.
+
+Configuring isort
+=================
+
+If you find the default isort settings do not work well for your project, isort provides several ways to adjust
+the behavior.
+
+To configure isort for a single user create a ``~/.isort.cfg`` or ``$XDG_CONFIG_HOME/isort.cfg`` file:
+
+.. code-block:: ini
+
+ [settings]
+ line_length=120
+ force_to_top=file1.py,file2.py
+ skip=file3.py,file4.py
+ known_future_library=future,pies
+ known_standard_library=std,std2
+ known_third_party=randomthirdparty
+ known_first_party=mylib1,mylib2
+ indent=' '
+ multi_line_output=3
+ length_sort=1
+ forced_separate=django.contrib,django.utils
+ default_section=FIRSTPARTY
+ no_lines_before=LOCALFOLDER
+
+Additionally, you can specify project level configuration simply by placing a ``.isort.cfg`` file at the root of your
+project. isort will look up to 25 directories up, from the file it is ran against, to find a project specific configuration.
+
+Or, if you prefer, you can add an ``isort`` or ``tool:isort`` section to your project's ``setup.cfg`` or ``tox.ini`` file with any desired settings.
+
+You can also add your desired settings under a ``[tool.isort]`` section in your ``pyproject.toml`` file.
+
+You can then override any of these settings by using command line arguments, or by passing in override values to the
+SortImports class.
+
+Finally, as of version 3.0 isort supports editorconfig files using the standard syntax defined here:
+https://editorconfig.org/
+
+Meaning you place any standard isort configuration parameters within a .editorconfig file under the ``*.py`` section
+and they will be honored.
+
+For a full list of isort settings and their meanings `take a look at the isort wiki <https://github.com/timothycrosley/isort/wiki/isort-Settings>`_.
+
+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 6 possible settings:
+
+**0 - Grid**
+
+.. code-block:: python
+
+ from third_party import (lib1, lib2, lib3,
+ lib4, lib5, ...)
+
+**1 - Vertical**
+
+.. code-block:: python
+
+ from third_party import (lib1,
+ lib2,
+ lib3
+ lib4,
+ lib5,
+ ...)
+
+**2 - Hanging Indent**
+
+.. code-block:: python
+
+ from third_party import \
+ lib1, lib2, lib3, \
+ lib4, lib5, lib6
+
+**3 - Vertical Hanging Indent**
+
+.. code-block:: python
+
+ from third_party import (
+ lib1,
+ lib2,
+ lib3,
+ lib4,
+ )
+
+**4 - Hanging Grid**
+
+.. code-block:: python
+
+ from third_party import (
+ lib1, lib2, lib3, lib4,
+ lib5, ...)
+
+**5 - Hanging Grid Grouped**
+
+.. code-block:: python
+
+ from third_party import (
+ lib1, lib2, lib3, lib4,
+ lib5, ...
+ )
+
+**6 - Hanging Grid Grouped, No Trailing Comma**
+
+In Mode 5 isort leaves a single extra space to maintain consistency of output when a comma is added at the end.
+Mode 6 is the same - except that no extra space is maintained leading to the possibility of lines one character longer.
+You can enforce a trailing comma by using this in conjunction with `-tc` or `trailing_comma: True`.
+
+.. code-block:: python
+
+ from third_party import (
+ lib1, lib2, lib3, lib4,
+ lib5
+ )
+
+**7 - NOQA**
+
+.. code-block:: python
+
+ from third_party import lib1, lib2, lib3, ... # NOQA
+
+Alternatively, you can set ``force_single_line`` to ``True`` (``-sl`` on the command line) and every import will appear on its
+own line:
+
+.. code-block:: python
+
+ from third_party import lib1
+ from third_party import lib2
+ from third_party import lib3
+ ...
+
+Note: 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:
+
+.. code-block:: 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:
+
+.. code-block:: python
+
+ from __future__ import (absolute_import, division,
+ print_function, unicode_literals)
+
+Will be produced instead of:
+
+.. code-block:: 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
+============================
+
+You can change the section order with ``sections`` option from the default of:
+
+.. code-block:: ini
+
+ FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
+
+to your preference:
+
+.. code-block:: ini
+
+ sections=FUTURE,STDLIB,FIRSTPARTY,THIRDPARTY,LOCALFOLDER
+
+You also can define your own sections and their order.
+
+Example:
+
+.. code-block:: ini
+
+ known_django=django
+ known_pandas=pandas,numpy
+ sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,PANDAS,FIRSTPARTY,LOCALFOLDER
+
+would create two new sections with the specified known modules.
+
+The ``no_lines_before`` option will prevent the listed sections from being split from the previous section by an empty line.
+
+Example:
+
+.. code-block:: ini
+
+ sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
+ no_lines_before=LOCALFOLDER
+
+would produce a section with both FIRSTPARTY and LOCALFOLDER modules combined.
+
+Auto-comment import sections
+============================
+
+Some projects prefer to have import sections uniquely titled to aid in identifying the sections quickly
+when visually scanning. isort can automate this as well. To do this simply set the ``import_heading_{section_name}``
+setting for each section you wish to have auto commented - to the desired comment.
+
+For Example:
+
+.. code-block:: ini
+
+ import_heading_stdlib=Standard Library
+ import_heading_firstparty=My Stuff
+
+Would lead to output looking like the following:
+
+.. code-block:: python
+
+ # Standard Library
+ import os
+ import sys
+
+ import django.settings
+
+ # My Stuff
+ import myproject.test
+
+Ordering by import length
+=========================
+
+isort also makes it easy to sort your imports by length, simply by setting the ``length_sort`` option to ``True``.
+This will result in the following output style:
+
+.. code-block:: python
+
+ from evn.util import (
+ Pool,
+ Dict,
+ Options,
+ Constant,
+ DecayDict,
+ UnexpectedCodePath,
+ )
+
+It is also possible to opt-in to sorting imports by length for only specific
+sections by using ``length_sort_`` followed by the section name as a
+configuration item, e.g.::
+
+ length_sort_stdlib=1
+
+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``:
+
+.. code-block:: python
+
+ import module # isort:skip
+
+or:
+
+.. code-block:: 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:
+
+.. code-block:: python
+
+ """ my_module.py
+ Best module ever
+
+ isort:skip_file
+ """
+
+ import b
+ import a
+
+Adding an import to multiple files
+==================================
+
+isort makes it easy to add an import statement across multiple files, while being assured it's correctly placed.
+
+From the command line:
+
+.. code-block:: bash
+
+ isort -a "from __future__ import print_function" *.py
+
+from within Kate:
+
+.. code-block::
+
+ ctrl+]
+
+or:
+
+.. code-block::
+
+ menu > Python > Add Import
+
+Removing an import from multiple files
+======================================
+
+isort also makes it easy to remove an import from multiple files, without having to be concerned with how it was originally
+formatted.
+
+From the command line:
+
+.. code-block:: bash
+
+ isort -rm "os.system" *.py
+
+from within Kate:
+
+.. code-block::
+
+ ctrl+shift+]
+
+or:
+
+.. code-block::
+
+ menu > Python > Remove Import
+
+Using isort to verify code
+==========================
+
+The ``--check-only`` option
+---------------------------
+
+isort can also be used to 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``.
+
+.. code-block:: bash
+
+ isort **/*.py -c -vb
+
+ 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:
+
+https://gist.github.com/acdha/8717683
+
+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.
+
+To cause the commit to fail if there are isort errors (strict mode), include the following in
+``.git/hooks/pre-commit``:
+
+.. code-block:: python
+
+ #!/usr/bin/env python
+ import sys
+ from isort.hooks import git_hook
+
+ sys.exit(git_hook(strict=True, modify=True))
+
+If you just want to display warnings, but allow the commit to happen anyway, call ``git_hook`` without
+the `strict` parameter. If you want to display warnings, but not also fix the code, call ``git_hook`` without
+the `modify` parameter.
+
+Setuptools integration
+----------------------
+
+Upon installation, isort enables a ``setuptools`` command that checks Python files
+declared by your project.
+
+Running ``python setup.py isort`` on the command line will check the files
+listed in your ``py_modules`` and ``packages``. If any warning is found,
+the command will exit with an error code:
+
+.. code-block:: bash
+
+ $ python setup.py isort
+
+Also, to allow users to be able to use the command without having to install
+isort themselves, add isort to the setup_requires of your ``setup()`` like so:
+
+.. code-block:: python
+
+ setup(
+ name="project",
+ packages=["project"],
+
+ setup_requires=[
+ "isort"
+ ]
+ )
+
+
+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 :)
+
+--------------------------------------------
+
+Thanks and I hope you find isort useful!
+
+~Timothy Crosley
+
+
diff --git a/venv/Lib/site-packages/isort-4.3.21.dist-info/RECORD b/venv/Lib/site-packages/isort-4.3.21.dist-info/RECORD
new file mode 100644
index 0000000..fbe22ff
--- /dev/null
+++ b/venv/Lib/site-packages/isort-4.3.21.dist-info/RECORD
@@ -0,0 +1,30 @@
+../../Scripts/isort.exe,sha256=LehBbE3s782QB5Pj-GAeCX3qPl-km1TABLOXedSMAcE,102790
+isort-4.3.21.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+isort-4.3.21.dist-info/LICENSE,sha256=BjKUABw9Uj26y6ud1UrCKZgnVsyvWSylMkCysM3YIGU,1089
+isort-4.3.21.dist-info/METADATA,sha256=8fY1DuLOn_UnCH58A8AcsCUZpYWeLCsQF-n-GIXlxOM,19749
+isort-4.3.21.dist-info/RECORD,,
+isort-4.3.21.dist-info/WHEEL,sha256=h_aVn5OB2IERUjMbi2pucmR_zzWJtk303YXvhh60NJ8,110
+isort-4.3.21.dist-info/entry_points.txt,sha256=2M99eSFpnteDm3ekW8jya2a3A0-vFntKdT1fP93Tyks,148
+isort-4.3.21.dist-info/top_level.txt,sha256=mrBLoVpJnQWBbnMOSdzkjN1E9Z-e3Zd-MRlo88bstUk,6
+isort/__init__.py,sha256=_DTTMASePJCqsKnRJPf_YgFv5ZJOHg1uPLkrQHcqA2c,1380
+isort/__main__.py,sha256=9tThPqyOnY86bHaxJ0WciTd-rfKN3O-PQoNGBO2xhio,205
+isort/__pycache__/__init__.cpython-37.pyc,,
+isort/__pycache__/__main__.cpython-37.pyc,,
+isort/__pycache__/finders.cpython-37.pyc,,
+isort/__pycache__/hooks.cpython-37.pyc,,
+isort/__pycache__/isort.cpython-37.pyc,,
+isort/__pycache__/main.cpython-37.pyc,,
+isort/__pycache__/natural.cpython-37.pyc,,
+isort/__pycache__/pie_slice.cpython-37.pyc,,
+isort/__pycache__/pylama_isort.cpython-37.pyc,,
+isort/__pycache__/settings.cpython-37.pyc,,
+isort/__pycache__/utils.cpython-37.pyc,,
+isort/finders.py,sha256=0w39ygCFuOv40OHixflrOv_Xna8K78usa5ySwS9GkWE,13185
+isort/hooks.py,sha256=GcyPMF7raq3B1z4ubbzIWoMiY5DePDni0Nct5U87uMQ,3230
+isort/isort.py,sha256=krLW0QgwnVjUD3hYTpQmWkMa5TDEZxx6AbX80vlVNoA,53910
+isort/main.py,sha256=rS7dAu_0T-8Jxi3sDWu00IOjt4j0r3vJi6bXZn6RnQg,21974
+isort/natural.py,sha256=hlcWsGKfIUC4Atjp5IIqDCmg1madY6ave9oNiTGjJ0Q,1794
+isort/pie_slice.py,sha256=hO6l1XocvGAd8XTR8526r-G7XIncUQB53_DHQ4AZEYI,5612
+isort/pylama_isort.py,sha256=wF6NOEVuibme0l-5pH9pCW1j4vGaFamuwll494TnzDI,785
+isort/settings.py,sha256=4_Jf-9GaBy9fi6UJctLqesIAMAegWekRIJdJmH5TBNE,17452
+isort/utils.py,sha256=KtazEoeX9XmtcrUGP6xl5lBX7Ye2N08ACGaWxiGcIaE,1344
diff --git a/venv/Lib/site-packages/isort-4.3.21.dist-info/WHEEL b/venv/Lib/site-packages/isort-4.3.21.dist-info/WHEEL
new file mode 100644
index 0000000..78e6f69
--- /dev/null
+++ b/venv/Lib/site-packages/isort-4.3.21.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.4)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/Lib/site-packages/isort-4.3.21.dist-info/entry_points.txt b/venv/Lib/site-packages/isort-4.3.21.dist-info/entry_points.txt
new file mode 100644
index 0000000..3a77a18
--- /dev/null
+++ b/venv/Lib/site-packages/isort-4.3.21.dist-info/entry_points.txt
@@ -0,0 +1,9 @@
+[console_scripts]
+isort = isort.main:main
+
+[distutils.commands]
+isort = isort.main:ISortCommand
+
+[pylama.linter]
+isort = isort.pylama_isort:Linter
+
diff --git a/venv/Lib/site-packages/isort-4.3.21.dist-info/top_level.txt b/venv/Lib/site-packages/isort-4.3.21.dist-info/top_level.txt
new file mode 100644
index 0000000..2a79243
--- /dev/null
+++ b/venv/Lib/site-packages/isort-4.3.21.dist-info/top_level.txt
@@ -0,0 +1 @@
+isort
diff --git a/venv/Lib/site-packages/isort/__init__.py b/venv/Lib/site-packages/isort/__init__.py
new file mode 100644
index 0000000..9a0a073
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__init__.py
@@ -0,0 +1,28 @@
+"""__init__.py.
+
+Defines the isort module to include the SortImports utility class as well as any defined settings.
+
+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.
+
+"""
+
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from . import settings # noqa: F401
+from .isort import SortImports # noqa: F401
+
+__version__ = "4.3.21"
diff --git a/venv/Lib/site-packages/isort/__main__.py b/venv/Lib/site-packages/isort/__main__.py
new file mode 100644
index 0000000..91cc154
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__main__.py
@@ -0,0 +1,9 @@
+from __future__ import absolute_import
+
+from isort.pie_slice import apply_changes_to_python_environment
+
+apply_changes_to_python_environment()
+
+from isort.main import main # noqa: E402 isort:skip
+
+main()
diff --git a/venv/Lib/site-packages/isort/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..041c992
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/__main__.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/__main__.cpython-37.pyc
new file mode 100644
index 0000000..63b2c4d
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/__main__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/finders.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/finders.cpython-37.pyc
new file mode 100644
index 0000000..a579698
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/finders.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/hooks.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/hooks.cpython-37.pyc
new file mode 100644
index 0000000..509a974
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/hooks.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/isort.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/isort.cpython-37.pyc
new file mode 100644
index 0000000..fce8cbf
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/isort.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/main.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/main.cpython-37.pyc
new file mode 100644
index 0000000..efb6118
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/main.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/natural.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/natural.cpython-37.pyc
new file mode 100644
index 0000000..4eb1b12
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/natural.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/pie_slice.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/pie_slice.cpython-37.pyc
new file mode 100644
index 0000000..f024ebb
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/pie_slice.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/pylama_isort.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/pylama_isort.cpython-37.pyc
new file mode 100644
index 0000000..f2bbef1
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/pylama_isort.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/settings.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/settings.cpython-37.pyc
new file mode 100644
index 0000000..d8a295d
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/settings.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/isort/__pycache__/utils.cpython-37.pyc
new file mode 100644
index 0000000..d50b861
--- /dev/null
+++ b/venv/Lib/site-packages/isort/__pycache__/utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/isort/finders.py b/venv/Lib/site-packages/isort/finders.py
new file mode 100644
index 0000000..225bd12
--- /dev/null
+++ b/venv/Lib/site-packages/isort/finders.py
@@ -0,0 +1,382 @@
+"""Finders try to find right section for passed module name
+"""
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import inspect
+import os
+import os.path
+import re
+import sys
+import sysconfig
+from fnmatch import fnmatch
+from glob import glob
+
+from .pie_slice import PY2
+from .utils import chdir, exists_case_sensitive
+
+try:
+ from pipreqs import pipreqs
+except ImportError:
+ pipreqs = None
+
+try:
+ from pip_api import parse_requirements
+except ImportError:
+ parse_requirements = None
+
+try:
+ from requirementslib import Pipfile
+except ImportError:
+ Pipfile = None
+
+try:
+ from functools import lru_cache
+except ImportError:
+ from backports.functools_lru_cache import lru_cache
+
+
+KNOWN_SECTION_MAPPING = {
+ 'STDLIB': 'STANDARD_LIBRARY',
+ 'FUTURE': 'FUTURE_LIBRARY',
+ 'FIRSTPARTY': 'FIRST_PARTY',
+ 'THIRDPARTY': 'THIRD_PARTY',
+}
+
+
+class BaseFinder(object):
+ def __init__(self, config, sections):
+ self.config = config
+ self.sections = sections
+
+
+class ForcedSeparateFinder(BaseFinder):
+ def find(self, module_name):
+ 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 = '%s*' % forced_separate
+
+ if fnmatch(module_name, path_glob) or fnmatch(module_name, '.' + path_glob):
+ return forced_separate
+
+
+class LocalFinder(BaseFinder):
+ def find(self, module_name):
+ if module_name.startswith("."):
+ return self.sections.LOCALFOLDER
+
+
+class KnownPatternFinder(BaseFinder):
+ def __init__(self, config, sections):
+ super(KnownPatternFinder, self).__init__(config, sections)
+
+ self.known_patterns = []
+ for placement in reversed(self.sections):
+ known_placement = KNOWN_SECTION_MAPPING.get(placement, placement)
+ config_key = 'known_{0}'.format(known_placement.lower())
+ known_patterns = self.config.get(config_key, [])
+ 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))
+
+ @staticmethod
+ def _is_package(path):
+ """
+ Evaluates if path is a python package
+ """
+ if PY2:
+ return os.path.exists(os.path.join(path, '__init__.py'))
+ else:
+ return os.path.isdir(path)
+
+ def _parse_known_pattern(self, pattern):
+ """
+ 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(pattern)
+ if self._is_package(os.path.join(pattern, filename))
+ ]
+ else:
+ patterns = [pattern]
+
+ return patterns
+
+ def find(self, module_name):
+ # 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
+
+
+class PathFinder(BaseFinder):
+ def __init__(self, config, sections):
+ super(PathFinder, self).__init__(config, sections)
+
+ # restore the original import path (i.e. not the path to bin/isort)
+ self.paths = [os.getcwd()]
+
+ # virtual env
+ self.virtual_env = self.config.get('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 = False
+ if self.virtual_env:
+ self.virtual_env_src = '{0}/src/'.format(self.virtual_env)
+ for path in glob('{0}/lib/python*/site-packages'.format(self.virtual_env)):
+ if path not in self.paths:
+ self.paths.append(path)
+ for path in glob('{0}/lib/python*/*/site-packages'.format(self.virtual_env)):
+ if path not in self.paths:
+ self.paths.append(path)
+ for path in glob('{0}/src/*'.format(self.virtual_env)):
+ if os.path.isdir(path):
+ self.paths.append(path)
+
+ # conda
+ self.conda_env = self.config.get('conda_env') or os.environ.get('CONDA_PREFIX')
+ if self.conda_env:
+ self.conda_env = os.path.realpath(self.conda_env)
+ for path in glob('{0}/lib/python*/site-packages'.format(self.conda_env)):
+ if path not in self.paths:
+ self.paths.append(path)
+ for path in glob('{0}/lib/python*/*/site-packages'.format(self.conda_env)):
+ if path not in self.paths:
+ self.paths.append(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)
+
+ # handle compiled libraries
+ self.ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") or ".so"
+
+ # add system paths
+ for path in sys.path[1:]:
+ if path not in self.paths:
+ self.paths.append(path)
+
+ def find(self, module_name):
+ for prefix in self.paths:
+ package_path = "/".join((prefix, module_name.split(".")[0]))
+ is_module = (exists_case_sensitive(package_path + ".py") or
+ exists_case_sensitive(package_path + ".so") or
+ exists_case_sensitive(package_path + self.ext_suffix) 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:
+ return self.sections.THIRDPARTY
+ if 'dist-packages' in prefix:
+ return self.sections.THIRDPARTY
+ if self.virtual_env and self.virtual_env_src in prefix:
+ return self.sections.THIRDPARTY
+ if self.conda_env and self.conda_env in prefix:
+ return self.sections.THIRDPARTY
+ if os.path.normcase(prefix).startswith(self.stdlib_lib_prefix):
+ return self.sections.STDLIB
+ return self.config['default_section']
+
+
+class ReqsBaseFinder(BaseFinder):
+ def __init__(self, config, sections, path='.'):
+ super(ReqsBaseFinder, self).__init__(config, sections)
+ self.path = path
+ if self.enabled:
+ self.mapping = self._load_mapping()
+ self.names = self._load_names()
+
+ @staticmethod
+ def _load_mapping():
+ """Return list of mappings `package_name -> module_name`
+
+ Example:
+ django-haystack -> haystack
+ """
+ if not pipreqs:
+ return
+ path = os.path.dirname(inspect.getfile(pipreqs))
+ path = os.path.join(path, 'mapping')
+ with open(path) as f:
+ # pypi_name: import_name
+ return dict(line.strip().split(":")[::-1] for line in f)
+
+ def _load_names(self):
+ """Return list of thirdparty modules from requirements
+ """
+ names = []
+ for path in self._get_files():
+ for name in self._get_names(path):
+ names.append(self._normalize_name(name))
+ return names
+
+ @staticmethod
+ def _get_parents(path):
+ prev = ''
+ while path != prev:
+ prev = path
+ yield path
+ path = os.path.dirname(path)
+
+ def _get_files(self):
+ """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):
+ for file_path in self._get_files_from_dir(path):
+ yield file_path
+
+ def _normalize_name(self, name):
+ """Convert package name to module name
+
+ Examples:
+ Django -> django
+ django-haystack -> haystack
+ Flask-RESTFul -> flask_restful
+ """
+ if self.mapping:
+ name = self.mapping.get(name, name)
+ return name.lower().replace('-', '_')
+
+ def find(self, module_name):
+ # required lib not installed yet
+ if not self.enabled:
+ return
+
+ module_name, _sep, _submodules = module_name.partition('.')
+ module_name = module_name.lower()
+ if not module_name:
+ return
+
+ for name in self.names:
+ if module_name == name:
+ return self.sections.THIRDPARTY
+
+
+class RequirementsFinder(ReqsBaseFinder):
+ exts = ('.txt', '.in')
+ enabled = bool(parse_requirements)
+
+ def _get_files_from_dir(self, path):
+ """Return paths to requirements files from passed dir.
+ """
+ return RequirementsFinder._get_files_from_dir_cached(path)
+
+ @classmethod
+ @lru_cache(maxsize=16)
+ def _get_files_from_dir_cached(cls, path):
+ result = []
+
+ 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(path):
+ for ext in cls.exts:
+ if subfile_name.endswith(ext):
+ result.append(os.path.join(path, subfile_name))
+ continue
+
+ # *requirements*.{txt,in}
+ if os.path.isfile(full_path):
+ for ext in cls.exts:
+ if fname.endswith(ext):
+ result.append(full_path)
+ break
+
+ return result
+
+ def _get_names(self, path):
+ """Load required packages from path to requirements file
+ """
+ return RequirementsFinder._get_names_cached(path)
+
+ @classmethod
+ @lru_cache(maxsize=16)
+ def _get_names_cached(cls, path):
+ results = []
+
+ with chdir(os.path.dirname(path)):
+ requirements = parse_requirements(path)
+ for req in requirements.values():
+ if req.name:
+ results.append(req.name)
+
+ return results
+
+
+class PipfileFinder(ReqsBaseFinder):
+ enabled = bool(Pipfile)
+
+ def _get_names(self, path):
+ with chdir(path):
+ project = Pipfile.load(path)
+ for req in project.packages:
+ yield req.name
+
+ def _get_files_from_dir(self, path):
+ if 'Pipfile' in os.listdir(path):
+ yield path
+
+
+class DefaultFinder(BaseFinder):
+ def find(self, module_name):
+ return self.config['default_section']
+
+
+class FindersManager(object):
+ finders = (
+ ForcedSeparateFinder,
+ LocalFinder,
+ KnownPatternFinder,
+ PathFinder,
+ PipfileFinder,
+ RequirementsFinder,
+ DefaultFinder,
+ )
+
+ def __init__(self, config, sections, finders=None):
+ self.verbose = config.get('verbose', False)
+
+ finders = self.finders if finders is None else finders
+ self.finders = []
+ for finder in finders:
+ try:
+ self.finders.append(finder(config, sections))
+ except Exception as exception:
+ # if one finder fails to instantiate isort can continue using the rest
+ if self.verbose:
+ print('{} encountered an error ({}) during instantiation and cannot be used'.format(finder.__name__,
+ str(exception)))
+ self.finders = tuple(self.finders)
+
+ def find(self, module_name):
+ for finder in self.finders:
+ try:
+ section = finder.find(module_name)
+ 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('{} encountered an error ({}) while trying to identify the {} module'.format(finder.__name__,
+ str(exception),
+ module_name))
+ if section is not None:
+ return section
diff --git a/venv/Lib/site-packages/isort/hooks.py b/venv/Lib/site-packages/isort/hooks.py
new file mode 100644
index 0000000..16a16e1
--- /dev/null
+++ b/venv/Lib/site-packages/isort/hooks.py
@@ -0,0 +1,91 @@
+"""isort.py.
+
+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)
+
+Copyright (C) 2015 Helen Sherwood-Taylor
+
+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.
+
+"""
+import subprocess
+
+from isort import SortImports
+
+
+def get_output(command):
+ """
+ Run a command and return raw output
+
+ :param str command: the command to run
+ :returns: the stdout output of the command
+ """
+ return subprocess.check_output(command.split())
+
+
+def get_lines(command):
+ """
+ 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().decode('utf-8') for line in stdout.splitlines()]
+
+
+def git_hook(strict=False, modify=False):
+ """
+ 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.
+
+ :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"
+ files_modified = get_lines(diff_cmd)
+
+ errors = 0
+ for filename in files_modified:
+ if filename.endswith('.py'):
+ # Get the staged contents of the file
+ staged_cmd = "git show :%s" % filename
+ staged_contents = get_output(staged_cmd)
+
+ sort = SortImports(
+ file_path=filename,
+ file_contents=staged_contents.decode(),
+ check=True
+ )
+
+ if sort.incorrectly_sorted:
+ errors += 1
+ if modify:
+ SortImports(
+ file_path=filename,
+ file_contents=staged_contents.decode(),
+ check=False,
+ )
+
+ return errors if strict else 0
diff --git a/venv/Lib/site-packages/isort/isort.py b/venv/Lib/site-packages/isort/isort.py
new file mode 100644
index 0000000..245e53f
--- /dev/null
+++ b/venv/Lib/site-packages/isort/isort.py
@@ -0,0 +1,1060 @@
+"""isort.py.
+
+Exposes a simple library to sort through imports within Python code
+
+usage:
+ SortImports(file_name)
+or:
+ sorted = SortImports(file_contents=file_contents).output
+
+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.
+
+"""
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import codecs
+import copy
+import io
+import itertools
+import os
+import re
+import sys
+from collections import OrderedDict, namedtuple
+from datetime import datetime
+from difflib import unified_diff
+
+from . import settings
+from .finders import FindersManager
+from .natural import nsorted
+from .pie_slice import input
+
+
+class SortImports(object):
+ incorrectly_sorted = False
+ skipped = False
+
+ def __init__(self, file_path=None, file_contents=None, file_=None, write_to_stdout=False, check=False,
+ show_diff=False, settings_path=None, ask_to_apply=False, run_path='', check_skip=True,
+ extension=None, **setting_overrides):
+ if not settings_path and file_path:
+ settings_path = os.path.dirname(os.path.abspath(file_path))
+ settings_path = settings_path or os.getcwd()
+
+ self.config = settings.from_path(settings_path).copy()
+ for key, value in setting_overrides.items():
+ access_key = key.replace('not_', '').lower()
+ # The sections config needs to retain order and can't be converted to a set.
+ if access_key != 'sections' and type(self.config.get(access_key)) in (list, tuple):
+ if key.startswith('not_'):
+ self.config[access_key] = list(set(self.config[access_key]).difference(value))
+ else:
+ self.config[access_key] = list(set(self.config[access_key]).union(value))
+ else:
+ self.config[key] = value
+
+ if self.config['force_alphabetical_sort']:
+ self.config.update({'force_alphabetical_sort_within_sections': True,
+ 'no_sections': True,
+ 'lines_between_types': 1,
+ 'from_first': True})
+
+ indent = str(self.config['indent'])
+ if indent.isdigit():
+ indent = " " * int(indent)
+ else:
+ indent = indent.strip("'").strip('"')
+ if indent.lower() == "tab":
+ indent = "\t"
+ self.config['indent'] = indent
+
+ self.config['comment_prefix'] = self.config['comment_prefix'].strip("'").strip('"')
+
+ self.place_imports = {}
+ self.import_placements = {}
+ self.remove_imports = [self._format_simplified(removal) for removal in self.config['remove_imports']]
+ self.add_imports = [self._format_natural(addition) for addition in self.config['add_imports']]
+ self._section_comments = ["# " + value for key, value in self.config.items() if
+ key.startswith('import_heading') and value]
+
+ self.file_encoding = 'utf-8'
+ file_name = file_path
+ self.file_path = file_path or ""
+ if file_path:
+ file_path = os.path.abspath(file_path)
+ if check_skip:
+ if run_path and file_path.startswith(run_path):
+ file_name = file_path.replace(run_path, '', 1)
+ else:
+ file_name = file_path
+ run_path = ''
+
+ if settings.should_skip(file_name, self.config, run_path):
+ self.skipped = True
+ if self.config['verbose']:
+ print("WARNING: {0} was skipped as it's listed in 'skip' setting"
+ " or matches a glob in 'skip_glob' setting".format(file_path))
+ file_contents = None
+ if not self.skipped and not file_contents:
+ with io.open(file_path, 'rb') as f:
+ file_encoding = coding_check(f)
+ with io.open(file_path, encoding=file_encoding, newline='') as file_to_import_sort:
+ try:
+ file_contents = file_to_import_sort.read()
+ self.file_path = file_path
+ self.file_encoding = file_encoding
+ encoding_success = True
+ except UnicodeDecodeError:
+ encoding_success = False
+
+ if not encoding_success:
+ with io.open(file_path, newline='') as file_to_import_sort:
+ try:
+ file_contents = file_to_import_sort.read()
+ self.file_path = file_path
+ self.file_encoding = file_to_import_sort.encoding
+ except UnicodeDecodeError:
+ encoding_success = False
+ file_contents = None
+ self.skipped = True
+ if self.config['verbose']:
+ print("WARNING: {} was skipped as it couldn't be opened with the given "
+ "{} encoding or {} fallback encoding".format(file_path,
+ self.file_encoding,
+ file_to_import_sort.encoding))
+ elif file_:
+ try:
+ file_.seek(0)
+ self.file_encoding = coding_check(file_)
+ file_.seek(0)
+ except (io.UnsupportedOperation, IOError):
+ pass
+ reader = codecs.getreader(self.file_encoding)
+ file_contents = reader(file_).read()
+
+ # try to decode file_contents
+ if file_contents:
+ try:
+ basestring
+ # python 2
+ need_decode = (str, bytes)
+ except NameError:
+ # python 3
+ need_decode = bytes
+
+ if isinstance(file_contents, need_decode):
+ file_contents = file_contents.decode(coding_check(file_contents.splitlines()))
+
+ if file_contents is None or ("isort:" + "skip_file") in file_contents:
+ self.skipped = True
+ self.output = None
+ if write_to_stdout and file_contents:
+ sys.stdout.write(file_contents)
+ return
+
+ if self.config['line_ending']:
+ self.line_separator = self.config['line_ending']
+ else:
+ if '\r\n' in file_contents:
+ self.line_separator = '\r\n'
+ elif '\r' in file_contents:
+ self.line_separator = '\r'
+ else:
+ self.line_separator = '\n'
+ self.in_lines = file_contents.split(self.line_separator)
+ self.original_length = len(self.in_lines)
+ if (self.original_length > 1 or self.in_lines[:1] not in ([], [""])) or self.config['force_adds']:
+ for add_import in self.add_imports:
+ self.in_lines.append(add_import)
+ self.number_of_lines = len(self.in_lines)
+
+ if not extension:
+ self.extension = file_name.split('.')[-1] if file_name else "py"
+ else:
+ self.extension = extension
+
+ self.out_lines = []
+ self.comments = {'from': {}, 'straight': {}, 'nested': {}, 'above': {'straight': {}, 'from': {}}}
+ self.imports = OrderedDict()
+ self.as_map = {}
+
+ section_names = self.config['sections']
+ self.sections = namedtuple('Sections', section_names)(*[name for name in section_names])
+ for section in itertools.chain(self.sections, self.config['forced_separate']):
+ self.imports[section] = {'straight': OrderedDict(), 'from': OrderedDict()}
+
+ self.finder = FindersManager(config=self.config, sections=self.sections)
+
+ self.index = 0
+ self.import_index = -1
+ self._first_comment_index_start = -1
+ self._first_comment_index_end = -1
+ self._parse()
+ if self.import_index != -1:
+ self._add_formatted_imports()
+ self.length_change = len(self.out_lines) - self.original_length
+ while self.out_lines and self.out_lines[-1].strip() == "":
+ self.out_lines.pop(-1)
+ self.out_lines.append("")
+ self.output = self.line_separator.join(self.out_lines)
+ if self.config['atomic']:
+ try:
+ compile(self._strip_top_comments(self.out_lines, self.line_separator), self.file_path, 'exec', 0, 1)
+ except SyntaxError:
+ self.output = file_contents
+ self.incorrectly_sorted = True
+ try:
+ compile(self._strip_top_comments(self.in_lines, self.line_separator), self.file_path, 'exec', 0, 1)
+ print("ERROR: {0} isort would have introduced syntax errors, please report to the project!".
+ format(self.file_path))
+ except SyntaxError:
+ print("ERROR: {0} File contains syntax errors.".format(self.file_path))
+
+ return
+ if check:
+ check_output = self.output
+ check_against = file_contents
+ if self.config['ignore_whitespace']:
+ check_output = check_output.replace(self.line_separator, "").replace(" ", "").replace("\x0c", "")
+ check_against = check_against.replace(self.line_separator, "").replace(" ", "").replace("\x0c", "")
+
+ if check_output.strip() == check_against.strip():
+ if self.config['verbose']:
+ print("SUCCESS: {0} Everything Looks Good!".format(self.file_path))
+ return
+
+ print("ERROR: {0} Imports are incorrectly sorted.".format(self.file_path))
+ self.incorrectly_sorted = True
+ if show_diff or self.config['show_diff']:
+ self._show_diff(file_contents)
+ elif write_to_stdout:
+ if sys.version_info[0] < 3:
+ self.output = self.output.encode(self.file_encoding)
+ sys.stdout.write(self.output)
+ elif file_name and not check:
+ if self.output == file_contents:
+ return
+
+ if ask_to_apply:
+ self._show_diff(file_contents)
+ answer = None
+ while answer not in ('yes', 'y', 'no', 'n', 'quit', 'q'):
+ answer = input("Apply suggested changes to '{0}' [y/n/q]? ".format(self.file_path)).lower()
+ if answer in ('no', 'n'):
+ return
+ if answer in ('quit', 'q'):
+ sys.exit(1)
+ with io.open(self.file_path, encoding=self.file_encoding, mode='w', newline='') as output_file:
+ if not self.config['quiet']:
+ print("Fixing {0}".format(self.file_path))
+ output_file.write(self.output)
+
+ @property
+ def correctly_sorted(self):
+ return not self.incorrectly_sorted
+
+ def _show_diff(self, file_contents):
+ for line in unified_diff(
+ file_contents.splitlines(1),
+ self.output.splitlines(1),
+ fromfile=self.file_path + ':before',
+ tofile=self.file_path + ':after',
+ fromfiledate=str(datetime.fromtimestamp(os.path.getmtime(self.file_path))
+ if self.file_path else datetime.now()),
+ tofiledate=str(datetime.now())
+ ):
+ sys.stdout.write(line)
+
+ @staticmethod
+ def _strip_top_comments(lines, line_separator):
+ """Strips # comments that exist at the top of the given lines"""
+ lines = copy.copy(lines)
+ while lines and lines[0].startswith("#"):
+ lines = lines[1:]
+ return line_separator.join(lines)
+
+ def place_module(self, module_name):
+ """Tries to determine if a module is a python std import, third party import, or project code:
+
+ if it can't determine - it assumes it is project code
+
+ """
+ return self.finder.find(module_name)
+
+ def _get_line(self):
+ """Returns the current line from the file while incrementing the index."""
+ line = self.in_lines[self.index]
+ self.index += 1
+ return line
+
+ @staticmethod
+ def _import_type(line):
+ """If the current line is an import line it will return its type (from or straight)"""
+ if "isort:skip" in line:
+ return
+ elif line.startswith('import '):
+ return "straight"
+ elif line.startswith('from '):
+ return "from"
+
+ def _at_end(self):
+ """returns True if we are at the end of the file."""
+ return self.index == self.number_of_lines
+
+ @staticmethod
+ def _module_key(module_name, config, sub_imports=False, ignore_case=False, section_name=None):
+ 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.isupper() and len(module_name) > 1:
+ prefix = "A"
+ elif module_name[0:1].isupper():
+ prefix = "B"
+ else:
+ prefix = "C"
+ if not config['case_sensitive']:
+ module_name = module_name.lower()
+ if section_name is None or 'length_sort_' + str(section_name).lower() not in config:
+ length_sort = config['length_sort']
+ else:
+ length_sort = config['length_sort_' + str(section_name).lower()]
+ return "{0}{1}{2}".format(module_name in config['force_to_top'] and "A" or "B", prefix,
+ length_sort and (str(len(module_name)) + ":" + module_name) or module_name)
+
+ def _add_comments(self, comments, original_string=""):
+ """
+ Returns a string with comments added if ignore_comments is not set.
+ """
+
+ if not self.config['ignore_comments']:
+ return comments and "{0}{1} {2}".format(self._strip_comments(original_string)[0],
+ self.config['comment_prefix'],
+ "; ".join(comments)) or original_string
+
+ return comments and self._strip_comments(original_string)[0]
+
+ def _wrap(self, line):
+ """
+ Returns an import wrapped to the specified line-length, if possible.
+ """
+ wrap_mode = self.config['multi_line_output']
+ if len(line) > self.config['line_length'] and wrap_mode != settings.WrapModes.NOQA:
+ line_without_comment = line
+ comment = None
+ if '#' in line:
+ line_without_comment, comment = line.split('#', 1)
+ for splitter in ("import ", ".", "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:
+ line_parts[-1] = '{0}#{1}'.format(line_parts[-1], comment)
+ next_line = []
+ while (len(line) + 2) > (self.config['wrap_length'] or self.config['line_length']) and line_parts:
+ next_line.append(line_parts.pop())
+ line = splitter.join(line_parts)
+ if not line:
+ line = next_line.pop()
+
+ cont_line = self._wrap(self.config['indent'] + splitter.join(next_line).lstrip())
+ if self.config['use_parentheses']:
+ if splitter == "as ":
+ output = "{0}{1}{2}".format(line, splitter, cont_line.lstrip())
+ else:
+ output = "{0}{1}({2}{3}{4}{5})".format(
+ line, splitter, self.line_separator, cont_line,
+ "," if self.config['include_trailing_comma'] else "",
+ self.line_separator if wrap_mode in (settings.WrapModes.VERTICAL_HANGING_INDENT,
+ settings.WrapModes.VERTICAL_GRID_GROUPED)
+ else "")
+ lines = output.split(self.line_separator)
+ if self.config['comment_prefix'] in lines[-1] and lines[-1].endswith(')'):
+ line, comment = lines[-1].split(self.config['comment_prefix'], 1)
+ lines[-1] = line + ')' + self.config['comment_prefix'] + comment[:-1]
+ return self.line_separator.join(lines)
+ return "{0}{1}\\{2}{3}".format(line, splitter, self.line_separator, cont_line)
+ elif len(line) > self.config['line_length'] and wrap_mode == settings.WrapModes.NOQA:
+ if "# NOQA" not in line:
+ return "{0}{1} NOQA".format(line, self.config['comment_prefix'])
+
+ return line
+
+ def _add_straight_imports(self, straight_modules, section, section_output):
+ for module in straight_modules:
+ if module in self.remove_imports:
+ continue
+
+ if module in self.as_map:
+ import_definition = ''
+ if self.config['keep_direct_and_as_imports']:
+ import_definition = "import {0}\n".format(module)
+ import_definition += "import {0} as {1}".format(module, self.as_map[module])
+ else:
+ import_definition = "import {0}".format(module)
+
+ comments_above = self.comments['above']['straight'].pop(module, None)
+ if comments_above:
+ section_output.extend(comments_above)
+ section_output.append(self._add_comments(self.comments['straight'].get(module), import_definition))
+
+ def _add_from_imports(self, from_modules, section, section_output, ignore_case):
+ for module in from_modules:
+ if module in self.remove_imports:
+ continue
+
+ import_start = "from {0} import ".format(module)
+ from_imports = list(self.imports[section]['from'][module])
+ if not self.config['no_inline_sort'] or self.config['force_single_line']:
+ from_imports = nsorted(from_imports, key=lambda key: self._module_key(key, self.config, True, ignore_case, section_name=section))
+ if self.remove_imports:
+ from_imports = [line for line in from_imports if not "{0}.{1}".format(module, line) in
+ self.remove_imports]
+
+ sub_modules = ['{0}.{1}'.format(module, from_import) for from_import in from_imports]
+ as_imports = {
+ from_import: "{0} as {1}".format(from_import, self.as_map[sub_module])
+ for from_import, sub_module in zip(from_imports, sub_modules)
+ if sub_module in self.as_map
+ }
+ if self.config['combine_as_imports'] and not ("*" in from_imports and self.config['combine_star']):
+ for from_import in copy.copy(from_imports):
+ if from_import in as_imports:
+ from_imports[from_imports.index(from_import)] = as_imports.pop(from_import)
+
+ while from_imports:
+ comments = self.comments['from'].pop(module, ())
+ if "*" in from_imports and self.config['combine_star']:
+ import_statement = self._wrap(self._add_comments(comments, "{0}*".format(import_start)))
+ from_imports = None
+ elif self.config['force_single_line']:
+ import_statements = []
+ while from_imports:
+ from_import = from_imports.pop(0)
+ if from_import in as_imports:
+ from_comments = self.comments['straight'].get('{}.{}'.format(module, from_import))
+ import_statements.append(self._add_comments(from_comments,
+ self._wrap(import_start + as_imports[from_import])))
+ continue
+ single_import_line = self._add_comments(comments, import_start + from_import)
+ comment = self.comments['nested'].get(module, {}).pop(from_import, None)
+ if comment:
+ single_import_line += "{0} {1}".format(comments and ";" or self.config['comment_prefix'],
+ comment)
+ import_statements.append(self._wrap(single_import_line))
+ comments = None
+ import_statement = self.line_separator.join(import_statements)
+ else:
+ while from_imports and from_imports[0] in as_imports:
+ from_import = from_imports.pop(0)
+ from_comments = self.comments['straight'].get('{}.{}'.format(module, from_import))
+ above_comments = self.comments['above']['from'].pop(module, None)
+ if above_comments:
+ section_output.extend(above_comments)
+
+ section_output.append(self._add_comments(from_comments,
+ self._wrap(import_start + as_imports[from_import])))
+
+ star_import = False
+ if "*" in from_imports:
+ section_output.append(self._add_comments(comments, "{0}*".format(import_start)))
+ from_imports.remove('*')
+ star_import = True
+ comments = None
+
+ for from_import in copy.copy(from_imports):
+ if from_import in as_imports:
+ continue
+ comment = self.comments['nested'].get(module, {}).pop(from_import, None)
+ if comment:
+ single_import_line = self._add_comments(comments, import_start + from_import)
+ single_import_line += "{0} {1}".format(comments and ";" or self.config['comment_prefix'],
+ comment)
+ above_comments = self.comments['above']['from'].pop(module, None)
+ if above_comments:
+ section_output.extend(above_comments)
+ section_output.append(self._wrap(single_import_line))
+ from_imports.remove(from_import)
+ comments = None
+
+ from_import_section = []
+ while from_imports and from_imports[0] not in as_imports:
+ from_import_section.append(from_imports.pop(0))
+ if star_import:
+ import_statement = import_start + (", ").join(from_import_section)
+ else:
+ import_statement = self._add_comments(comments, import_start + (", ").join(from_import_section))
+ if not from_import_section:
+ import_statement = ""
+
+ do_multiline_reformat = False
+
+ force_grid_wrap = self.config['force_grid_wrap']
+ if force_grid_wrap and len(from_import_section) >= force_grid_wrap:
+ do_multiline_reformat = True
+
+ if len(import_statement) > self.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) > self.config['line_length'] and len(from_import_section) > 0 and
+ self.config['multi_line_output'] not in (1, 0)):
+ do_multiline_reformat = True
+
+ if do_multiline_reformat:
+ import_statement = self._multi_line_reformat(import_start, from_import_section, comments)
+ if self.config['multi_line_output'] == 0:
+ self.config['multi_line_output'] = 4
+ try:
+ other_import_statement = self._multi_line_reformat(import_start, from_import_section, comments)
+ if (max(len(x)
+ for x in import_statement.split('\n')) > self.config['line_length']):
+ import_statement = other_import_statement
+ finally:
+ self.config['multi_line_output'] = 0
+ if not do_multiline_reformat and len(import_statement) > self.config['line_length']:
+ import_statement = self._wrap(import_statement)
+
+ if import_statement:
+ above_comments = self.comments['above']['from'].pop(module, None)
+ if above_comments:
+ section_output.extend(above_comments)
+ section_output.append(import_statement)
+
+ def _multi_line_reformat(self, import_start, from_imports, comments):
+ output_mode = settings.WrapModes._fields[self.config['multi_line_output']].lower()
+ formatter = getattr(self, "_output_" + output_mode, self._output_grid)
+ dynamic_indent = " " * (len(import_start) + 1)
+ indent = self.config['indent']
+ line_length = self.config['wrap_length'] or self.config['line_length']
+ import_statement = formatter(import_start, copy.copy(from_imports),
+ dynamic_indent, indent, line_length, comments)
+ if self.config['balanced_wrapping']:
+ lines = import_statement.split(self.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 = import_statement
+ while (len(lines[-1]) < minimum_length and
+ len(lines) == line_count and line_length > 10):
+ import_statement = new_import_statement
+ line_length -= 1
+ new_import_statement = formatter(import_start, copy.copy(from_imports),
+ dynamic_indent, indent, line_length, comments)
+ lines = new_import_statement.split(self.line_separator)
+ if import_statement.count(self.line_separator) == 0:
+ return self._wrap(import_statement)
+ return import_statement
+
+ def _add_formatted_imports(self):
+ """Adds the imports back to the file.
+
+ (at the index of the first import) sorted alphabetically and split between groups
+
+ """
+ sort_ignore_case = self.config['force_alphabetical_sort_within_sections']
+ sections = itertools.chain(self.sections, self.config['forced_separate'])
+
+ if self.config['no_sections']:
+ self.imports['no_sections'] = {'straight': [], 'from': {}}
+ for section in sections:
+ self.imports['no_sections']['straight'].extend(self.imports[section].get('straight', []))
+ self.imports['no_sections']['from'].update(self.imports[section].get('from', {}))
+ sections = ('no_sections', )
+
+ output = []
+ pending_lines_before = False
+ for section in sections:
+ straight_modules = self.imports[section]['straight']
+ straight_modules = nsorted(straight_modules, key=lambda key: self._module_key(key, self.config, section_name=section))
+ from_modules = self.imports[section]['from']
+ from_modules = nsorted(from_modules, key=lambda key: self._module_key(key, self.config, section_name=section))
+
+ section_output = []
+ if self.config['from_first']:
+ self._add_from_imports(from_modules, section, section_output, sort_ignore_case)
+ if self.config['lines_between_types'] and from_modules and straight_modules:
+ section_output.extend([''] * self.config['lines_between_types'])
+ self._add_straight_imports(straight_modules, section, section_output)
+ else:
+ self._add_straight_imports(straight_modules, section, section_output)
+ if self.config['lines_between_types'] and from_modules and straight_modules:
+ section_output.extend([''] * self.config['lines_between_types'])
+ self._add_from_imports(from_modules, section, section_output, sort_ignore_case)
+
+ if self.config['force_sort_within_sections']:
+ def by_module(line):
+ section = 'B'
+ if line.startswith('#'):
+ return 'AA'
+
+ line = re.sub('^from ', '', line)
+ line = re.sub('^import ', '', line)
+ if line.split(' ')[0] in self.config['force_to_top']:
+ section = 'A'
+ if not self.config['order_by_type']:
+ line = line.lower()
+ return '{0}{1}'.format(section, line)
+ section_output = nsorted(section_output, key=by_module)
+
+ section_name = section
+ no_lines_before = section_name in self.config['no_lines_before']
+
+ if section_output:
+ if section_name in self.place_imports:
+ self.place_imports[section_name] = section_output
+ continue
+
+ section_title = self.config.get('import_heading_' + str(section_name).lower(), '')
+ if section_title:
+ section_comment = "# {0}".format(section_title)
+ if section_comment not in self.out_lines[0:1] and section_comment not in self.in_lines[0:1]:
+ section_output.insert(0, section_comment)
+
+ if pending_lines_before or not no_lines_before:
+ output += ([''] * self.config['lines_between_sections'])
+
+ output += section_output
+
+ pending_lines_before = False
+ else:
+ pending_lines_before = pending_lines_before or not no_lines_before
+
+ while output and output[-1].strip() == '':
+ output.pop()
+ while output and output[0].strip() == '':
+ output.pop(0)
+
+ output_at = 0
+ if self.import_index < self.original_length:
+ output_at = self.import_index
+ elif self._first_comment_index_end != -1 and self._first_comment_index_start <= 2:
+ output_at = self._first_comment_index_end
+ self.out_lines[output_at:0] = output
+
+ imports_tail = output_at + len(output)
+ while [character.strip() for character in self.out_lines[imports_tail: imports_tail + 1]] == [""]:
+ self.out_lines.pop(imports_tail)
+
+ if len(self.out_lines) > imports_tail:
+ next_construct = ""
+ self._in_quote = False
+ tail = self.out_lines[imports_tail:]
+
+ for index, line in enumerate(tail):
+ in_quote = self._in_quote
+ if not self._skip_line(line) and line.strip():
+ if line.strip().startswith("#") and len(tail) > (index + 1) and tail[index + 1].strip():
+ continue
+ next_construct = line
+ break
+ elif not in_quote:
+ parts = line.split()
+ if len(parts) >= 3 and parts[1] == '=' and "'" not in parts[0] and '"' not in parts[0]:
+ next_construct = line
+ break
+
+ if self.config['lines_after_imports'] != -1:
+ self.out_lines[imports_tail:0] = ["" for line in range(self.config['lines_after_imports'])]
+ elif self.extension != "pyi" and (next_construct.startswith("def ") or
+ next_construct.startswith("class ") or
+ next_construct.startswith("@") or
+ next_construct.startswith("async def")):
+ self.out_lines[imports_tail:0] = ["", ""]
+ else:
+ self.out_lines[imports_tail:0] = [""]
+
+ if self.place_imports:
+ new_out_lines = []
+ for index, line in enumerate(self.out_lines):
+ new_out_lines.append(line)
+ if line in self.import_placements:
+ new_out_lines.extend(self.place_imports[self.import_placements[line]])
+ if len(self.out_lines) <= index or self.out_lines[index + 1].strip() != "":
+ new_out_lines.append("")
+ self.out_lines = new_out_lines
+
+ def _output_grid(self, statement, imports, white_space, indent, line_length, comments):
+ statement += "(" + imports.pop(0)
+ while imports:
+ next_import = imports.pop(0)
+ next_statement = self._add_comments(comments, statement + ", " + next_import)
+ if len(next_statement.split(self.line_separator)[-1]) + 1 > line_length:
+ lines = ['{0}{1}'.format(white_space, next_import.split(" ")[0])]
+ for part in next_import.split(" ")[1:]:
+ new_line = '{0} {1}'.format(lines[-1], part)
+ if len(new_line) + 1 > line_length:
+ lines.append('{0}{1}'.format(white_space, part))
+ else:
+ lines[-1] = new_line
+ next_import = self.line_separator.join(lines)
+ statement = (self._add_comments(comments, "{0},".format(statement)) +
+ "{0}{1}".format(self.line_separator, next_import))
+ comments = None
+ else:
+ statement += ", " + next_import
+ return statement + ("," if self.config['include_trailing_comma'] else "") + ")"
+
+ def _output_vertical(self, statement, imports, white_space, indent, line_length, comments):
+ first_import = self._add_comments(comments, imports.pop(0) + ",") + self.line_separator + white_space
+ return "{0}({1}{2}{3})".format(
+ statement,
+ first_import,
+ ("," + self.line_separator + white_space).join(imports),
+ "," if self.config['include_trailing_comma'] else "",
+ )
+
+ def _output_hanging_indent(self, statement, imports, white_space, indent, line_length, comments):
+ statement += imports.pop(0)
+ while imports:
+ next_import = imports.pop(0)
+ next_statement = self._add_comments(comments, statement + ", " + next_import)
+ if len(next_statement.split(self.line_separator)[-1]) + 3 > line_length:
+ next_statement = (self._add_comments(comments, "{0}, \\".format(statement)) +
+ "{0}{1}{2}".format(self.line_separator, indent, next_import))
+ comments = None
+ statement = next_statement
+ return statement
+
+ def _output_vertical_hanging_indent(self, statement, imports, white_space, indent, line_length, comments):
+ return "{0}({1}{2}{3}{4}{5}{2})".format(
+ statement,
+ self._add_comments(comments),
+ self.line_separator,
+ indent,
+ ("," + self.line_separator + indent).join(imports),
+ "," if self.config['include_trailing_comma'] else "",
+ )
+
+ def _output_vertical_grid_common(self, statement, imports, white_space, indent, line_length, comments,
+ need_trailing_char):
+ statement += self._add_comments(comments, "(") + self.line_separator + indent + imports.pop(0)
+ while imports:
+ next_import = imports.pop(0)
+ next_statement = "{0}, {1}".format(statement, next_import)
+ current_line_length = len(next_statement.split(self.line_separator)[-1])
+ if imports or need_trailing_char:
+ # If we have more imports we need to account for a comma after this import
+ # We might also need to account for a closing ) we're going to add.
+ current_line_length += 1
+ if current_line_length > line_length:
+ next_statement = "{0},{1}{2}{3}".format(statement, self.line_separator, indent, next_import)
+ statement = next_statement
+ if self.config['include_trailing_comma']:
+ statement += ','
+ return statement
+
+ def _output_vertical_grid(self, statement, imports, white_space, indent, line_length, comments):
+ return self._output_vertical_grid_common(statement, imports, white_space, indent, line_length, comments,
+ True) + ")"
+
+ def _output_vertical_grid_grouped(self, statement, imports, white_space, indent, line_length, comments):
+ return self._output_vertical_grid_common(statement, imports, white_space, indent, line_length, comments,
+ True) + self.line_separator + ")"
+
+ def _output_vertical_grid_grouped_no_comma(self, statement, imports, white_space, indent, line_length, comments):
+ return self._output_vertical_grid_common(statement, imports, white_space, indent, line_length, comments,
+ False) + self.line_separator + ")"
+
+ def _output_noqa(self, statement, imports, white_space, indent, line_length, comments):
+ retval = '{0}{1}'.format(statement, ', '.join(imports))
+ comment_str = ' '.join(comments)
+ if comments:
+ if len(retval) + len(self.config['comment_prefix']) + 1 + len(comment_str) <= line_length:
+ return '{0}{1} {2}'.format(retval, self.config['comment_prefix'], comment_str)
+ else:
+ if len(retval) <= line_length:
+ return retval
+ if comments:
+ if "NOQA" in comments:
+ return '{0}{1} {2}'.format(retval, self.config['comment_prefix'], comment_str)
+ else:
+ return '{0}{1} NOQA {2}'.format(retval, self.config['comment_prefix'], comment_str)
+ else:
+ return '{0}{1} NOQA'.format(retval, self.config['comment_prefix'])
+
+ @staticmethod
+ def _strip_comments(line, comments=None):
+ """Removes comments from import line."""
+ if comments is None:
+ comments = []
+
+ new_comments = False
+ comment_start = line.find("#")
+ if comment_start != -1:
+ comments.append(line[comment_start + 1:].strip())
+ new_comments = True
+ line = line[:comment_start]
+
+ return line, comments, new_comments
+
+ @staticmethod
+ def _format_simplified(import_line):
+ 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
+
+ @staticmethod
+ def _format_natural(import_line):
+ import_line = import_line.strip()
+ if not import_line.startswith("from ") and not import_line.startswith("import "):
+ if "." not in import_line:
+ return "import {0}".format(import_line)
+ parts = import_line.split(".")
+ end = parts.pop(-1)
+ return "from {0} import {1}".format(".".join(parts), end)
+
+ return import_line
+
+ def _skip_line(self, line):
+ skip_line = self._in_quote
+ if self.index == 1 and line.startswith("#"):
+ self._in_top_comment = True
+ return True
+ elif self._in_top_comment:
+ if not line.startswith("#") or line in self._section_comments:
+ self._in_top_comment = False
+ self._first_comment_index_end = self.index - 1
+
+ if '"' in line or "'" in line:
+ index = 0
+ if self._first_comment_index_start == -1 and (line.startswith('"') or line.startswith("'")):
+ self._first_comment_index_start = self.index
+ while index < len(line):
+ if line[index] == "\\":
+ index += 1
+ elif self._in_quote:
+ if line[index:index + len(self._in_quote)] == self._in_quote:
+ self._in_quote = False
+ if self._first_comment_index_end < self._first_comment_index_start:
+ self._first_comment_index_end = self.index
+ elif line[index] in ("'", '"'):
+ long_quote = line[index:index + 3]
+ if long_quote in ('"""', "'''"):
+ self._in_quote = long_quote
+ index += 2
+ else:
+ self._in_quote = line[index]
+ elif line[index] == "#":
+ break
+ index += 1
+
+ return skip_line or self._in_quote or self._in_top_comment
+
+ def _strip_syntax(self, import_string):
+ import_string = import_string.replace("_import", "[[i]]")
+ for remove_syntax in ['\\', '(', ')', ',']:
+ import_string = import_string.replace(remove_syntax, " ")
+ import_list = import_string.split()
+ for key in ('from', 'import'):
+ if key in import_list:
+ import_list.remove(key)
+ import_string = ' '.join(import_list)
+ import_string = import_string.replace("[[i]]", "_import")
+ return import_string.replace("{ ", "{|").replace(" }", "|}")
+
+ def _parse(self):
+ """Parses a python file taking out and categorizing imports."""
+ self._in_quote = False
+ self._in_top_comment = False
+ while not self._at_end():
+ raw_line = line = self._get_line()
+ line = line.replace("from.import ", "from . import ")
+ line = line.replace("\t", " ").replace('import*', 'import *')
+ line = line.replace(" .import ", " . import ")
+ statement_index = self.index
+ skip_line = self._skip_line(line)
+
+ if line in self._section_comments and not skip_line:
+ if self.import_index == -1:
+ self.import_index = self.index - 1
+ continue
+
+ if "isort:imports-" in line and line.startswith("#"):
+ section = line.split("isort:imports-")[-1].split()[0].upper()
+ self.place_imports[section] = []
+ self.import_placements[line] = section
+
+ if ";" in line:
+ for part in (part.strip() for part in line.split(";")):
+ if part and not part.startswith("from ") and not part.startswith("import "):
+ skip_line = True
+
+ import_type = self._import_type(line)
+ if not import_type or skip_line:
+ self.out_lines.append(raw_line)
+ continue
+
+ for line in (line.strip() for line in line.split(";")):
+ import_type = self._import_type(line)
+ if not import_type:
+ self.out_lines.append(line)
+ continue
+
+ if self.import_index == -1:
+ self.import_index = self.index - 1
+ nested_comments = {}
+ import_string, comments, new_comments = self._strip_comments(line)
+ stripped_line = [part for part in self._strip_syntax(import_string).strip().split(" ") if part]
+ if import_type == "from" and len(stripped_line) == 2 and stripped_line[1] != "*" and new_comments:
+ nested_comments[stripped_line[-1]] = comments[0]
+
+ if "(" in line.split("#")[0] and not self._at_end():
+ while not line.strip().endswith(")") and not self._at_end():
+ line, comments, new_comments = self._strip_comments(self._get_line(), comments)
+ stripped_line = self._strip_syntax(line).strip()
+ if import_type == "from" and stripped_line and " " not in stripped_line and new_comments:
+ nested_comments[stripped_line] = comments[-1]
+ import_string += self.line_separator + line
+ else:
+ while line.strip().endswith("\\"):
+ line, comments, new_comments = self._strip_comments(self._get_line(), comments)
+
+ # Still need to check for parentheses after an escaped line
+ if "(" in line.split("#")[0] and ")" not in line.split("#")[0] and not self._at_end():
+ stripped_line = self._strip_syntax(line).strip()
+ if import_type == "from" and stripped_line and " " not in stripped_line and new_comments:
+ nested_comments[stripped_line] = comments[-1]
+ import_string += self.line_separator + line
+
+ while not line.strip().endswith(")") and not self._at_end():
+ line, comments, new_comments = self._strip_comments(self._get_line(), comments)
+ stripped_line = self._strip_syntax(line).strip()
+ if import_type == "from" and stripped_line and " " not in stripped_line and new_comments:
+ nested_comments[stripped_line] = comments[-1]
+ import_string += self.line_separator + line
+
+ stripped_line = self._strip_syntax(line).strip()
+ if import_type == "from" and stripped_line and " " not in stripped_line and new_comments:
+ nested_comments[stripped_line] = comments[-1]
+ if import_string.strip().endswith(" import") or line.strip().startswith("import "):
+ import_string += self.line_separator + line
+ else:
+ import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip()
+
+ if import_type == "from":
+ import_string = import_string.replace("import(", "import (")
+ parts = import_string.split(" import ")
+ from_import = parts[0].split(" ")
+ import_string = " import ".join([from_import[0] + " " + "".join(from_import[1:])] + parts[1:])
+
+ imports = [item.replace("{|", "{ ").replace("|}", " }") for item in
+ self._strip_syntax(import_string).split()]
+ if "as" in imports and (imports.index('as') + 1) < len(imports):
+ while "as" in imports:
+ index = imports.index('as')
+ if import_type == "from":
+ module = imports[0] + "." + imports[index - 1]
+ self.as_map[module] = imports[index + 1]
+ else:
+ module = imports[index - 1]
+ self.as_map[module] = imports[index + 1]
+ if not self.config['combine_as_imports']:
+ self.comments['straight'][module] = comments
+ comments = []
+ del imports[index:index + 2]
+ if import_type == "from":
+ import_from = imports.pop(0)
+ placed_module = self.place_module(import_from)
+ if self.config['verbose']:
+ print("from-type place_module for %s returned %s" % (import_from, placed_module))
+ if placed_module == '':
+ print(
+ "WARNING: could not place module {0} of line {1} --"
+ " Do you need to define a default section?".format(import_from, line)
+ )
+ root = self.imports[placed_module][import_type]
+ for import_name in imports:
+ associated_comment = nested_comments.get(import_name)
+ if associated_comment:
+ self.comments['nested'].setdefault(import_from, {})[import_name] = associated_comment
+ comments.pop(comments.index(associated_comment))
+ if comments:
+ self.comments['from'].setdefault(import_from, []).extend(comments)
+
+ if len(self.out_lines) > max(self.import_index, self._first_comment_index_end + 1, 1) - 1:
+ last = self.out_lines and self.out_lines[-1].rstrip() or ""
+ while (last.startswith("#") and not last.endswith('"""') and not last.endswith("'''") and
+ 'isort:imports-' not in last):
+ self.comments['above']['from'].setdefault(import_from, []).insert(0, self.out_lines.pop(-1))
+ if len(self.out_lines) > max(self.import_index - 1, self._first_comment_index_end + 1, 1) - 1:
+ last = self.out_lines[-1].rstrip()
+ else:
+ last = ""
+ if statement_index - 1 == self.import_index:
+ self.import_index -= len(self.comments['above']['from'].get(import_from, []))
+
+ if import_from not in root:
+ root[import_from] = OrderedDict()
+ root[import_from].update((module, None) for module in imports)
+ else:
+ for module in imports:
+ if comments:
+ self.comments['straight'][module] = comments
+ comments = None
+
+ if len(self.out_lines) > max(self.import_index, self._first_comment_index_end + 1, 1) - 1:
+
+ last = self.out_lines and self.out_lines[-1].rstrip() or ""
+ while (last.startswith("#") and not last.endswith('"""') and not last.endswith("'''") and
+ 'isort:imports-' not in last):
+ self.comments['above']['straight'].setdefault(module, []).insert(0,
+ self.out_lines.pop(-1))
+ if len(self.out_lines) > 0 and len(self.out_lines) != self._first_comment_index_end:
+ last = self.out_lines[-1].rstrip()
+ else:
+ last = ""
+ if self.index - 1 == self.import_index:
+ self.import_index -= len(self.comments['above']['straight'].get(module, []))
+ placed_module = self.place_module(module)
+ if self.config['verbose']:
+ print("else-type place_module for %s returned %s" % (module, placed_module))
+ if placed_module == '':
+ print(
+ "WARNING: could not place module {0} of line {1} --"
+ " Do you need to define a default section?".format(import_from, line)
+ )
+ self.imports[placed_module][import_type][module] = None
+
+
+def coding_check(lines, default='utf-8'):
+
+ # see https://www.python.org/dev/peps/pep-0263/
+ pattern = re.compile(br'coding[:=]\s*([-\w.]+)')
+
+ for line_number, line in enumerate(lines, 1):
+ groups = re.findall(pattern, line)
+ if groups:
+ return groups[0].decode('ascii')
+ if line_number > 2:
+ break
+
+ return default
diff --git a/venv/Lib/site-packages/isort/main.py b/venv/Lib/site-packages/isort/main.py
new file mode 100644
index 0000000..fe36d11
--- /dev/null
+++ b/venv/Lib/site-packages/isort/main.py
@@ -0,0 +1,401 @@
+''' Tool for sorting imports alphabetically, and automatically separated into sections.
+
+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.
+
+'''
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import argparse
+import functools
+import glob
+import os
+import re
+import sys
+
+import setuptools
+
+from isort import SortImports, __version__
+from isort.settings import DEFAULT_SECTIONS, WrapModes, default, from_path, should_skip
+
+INTRO = r"""
+/#######################################################################\
+
+ `sMMy`
+ .yyyy- `
+ ##soos## ./o.
+ ` ``..-..` ``...`.`` ` ```` ``-ssso```
+ .s:-y- .+osssssso/. ./ossss+:so+:` :+o-`/osso:+sssssssso/
+ .s::y- osss+.``.`` -ssss+-.`-ossso` ssssso/::..::+ssss:::.
+ .s::y- /ssss+//:-.` `ssss+ `ssss+ sssso` :ssss`
+ .s::y- `-/+oossssso/ `ssss/ sssso ssss/ :ssss`
+ .y-/y- ````:ssss` ossso. :ssss: ssss/ :ssss.
+ `/so:` `-//::/osss+ `+ssss+-/ossso: /sso- `osssso/.
+ \/ `-/oooo++/- .:/++:/++/-` .. `://++/.
+
+
+ isort your Python imports for you so you don't have to
+
+ VERSION {0}
+
+\########################################################################/
+""".format(__version__)
+
+shebang_re = re.compile(br'^#!.*\bpython[23w]?\b')
+
+
+def is_python_file(path):
+ _root, ext = os.path.splitext(path)
+ if ext in ('.py', '.pyi'):
+ return True
+ if ext in ('.pex', ):
+ return False
+
+ # Skip editor backup files.
+ if path.endswith('~'):
+ return False
+
+ try:
+ with open(path, 'rb') as fp:
+ line = fp.readline(100)
+ except IOError:
+ return False
+ else:
+ return bool(shebang_re.match(line))
+
+
+class SortAttempt(object):
+ def __init__(self, incorrectly_sorted, skipped):
+ self.incorrectly_sorted = incorrectly_sorted
+ self.skipped = skipped
+
+
+def sort_imports(file_name, **arguments):
+ try:
+ result = SortImports(file_name, **arguments)
+ return SortAttempt(result.incorrectly_sorted, result.skipped)
+ except IOError as e:
+ print("WARNING: Unable to parse file {0} due to {1}".format(file_name, e))
+ return None
+
+
+def iter_source_code(paths, config, skipped):
+ """Iterate over all Python source files defined in paths."""
+ if 'not_skip' in config:
+ config['skip'] = list(set(config['skip']).difference(config['not_skip']))
+
+ for path in paths:
+ if os.path.isdir(path):
+ for dirpath, dirnames, filenames in os.walk(path, topdown=True, followlinks=True):
+ for dirname in list(dirnames):
+ if should_skip(dirname, config, dirpath):
+ skipped.append(dirname)
+ dirnames.remove(dirname)
+ for filename in filenames:
+ filepath = os.path.join(dirpath, filename)
+ if is_python_file(filepath):
+ relative_file = os.path.relpath(filepath, path)
+ if should_skip(relative_file, config, path):
+ skipped.append(filename)
+ else:
+ yield filepath
+ else:
+ yield path
+
+
+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"
+ user_options = []
+
+ def initialize_options(self):
+ default_settings = default.copy()
+ for key, value in default_settings.items():
+ setattr(self, key, value)
+
+ def finalize_options(self):
+ "Get options from config files."
+ self.arguments = {}
+ computed_settings = from_path(os.getcwd())
+ for key, value in computed_settings.items():
+ self.arguments[key] = value
+
+ def distribution_files(self):
+ """Find distribution packages."""
+ # This is verbatim from flake8
+ if self.distribution.packages:
+ 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:
+ 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 "%s.py" % filename
+ # Don't miss the setup.py file itself
+ yield "setup.py"
+
+ def run(self):
+ arguments = self.arguments
+ wrong_sorted_files = False
+ arguments['check'] = True
+ for path in self.distribution_files():
+ for python_file in glob.iglob(os.path.join(path, '*.py')):
+ try:
+ incorrectly_sorted = SortImports(python_file, **arguments).incorrectly_sorted
+ if incorrectly_sorted:
+ wrong_sorted_files = True
+ except IOError as e:
+ print("WARNING: Unable to parse file {0} due to {1}".format(python_file, e))
+ if wrong_sorted_files:
+ sys.exit(1)
+
+
+def parse_args(argv=None):
+ parser = argparse.ArgumentParser(description='Sort Python import definitions alphabetically '
+ 'within logical sections. Run with no arguments to run '
+ 'interactively. Run with `-` as the first argument to read from '
+ 'stdin. Otherwise provide a list of files to sort.')
+ inline_args_group = parser.add_mutually_exclusive_group()
+ parser.add_argument('-a', '--add-import', dest='add_imports', action='append',
+ help='Adds the specified import line to all files, '
+ 'automatically determining correct placement.')
+ parser.add_argument('-ac', '--atomic', dest='atomic', action='store_true',
+ help="Ensures the output doesn't save if the resulting file contains syntax errors.")
+ parser.add_argument('-af', '--force-adds', dest='force_adds', action='store_true',
+ help='Forces import adds even if the original file is empty.')
+ parser.add_argument('-b', '--builtin', dest='known_standard_library', action='append',
+ help='Force sortImports to recognize a module as part of the python standard library.')
+ parser.add_argument('-c', '--check-only', action='store_true', dest="check",
+ help='Checks the file for unsorted / unformatted imports and prints them to the '
+ 'command line without modifying the file.')
+ parser.add_argument('-ca', '--combine-as', dest='combine_as_imports', action='store_true',
+ help="Combines as imports on the same line.")
+ parser.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.")
+ parser.add_argument('-d', '--stdout', help='Force resulting output to stdout, instead of in-place.',
+ dest='write_to_stdout', action='store_true')
+ parser.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")
+ parser.add_argument('-ds', '--no-sections', help='Put all imports into the same section bucket', dest='no_sections',
+ action='store_true')
+ parser.add_argument('-dt', '--dont-order-by-type', dest='dont_order_by_type',
+ action='store_true', help='Only order imports alphabetically, do not attempt type ordering')
+ parser.add_argument('-e', '--balanced', dest='balanced_wrapping', action='store_true',
+ help='Balances wrapping to produce the most consistent line length possible')
+ parser.add_argument('-f', '--future', dest='known_future_library', action='append',
+ help='Force sortImports to recognize a module as part of the future compatibility libraries.')
+ parser.add_argument('-fas', '--force-alphabetical-sort', action='store_true', dest="force_alphabetical_sort",
+ help='Force all imports to be sorted as a single section')
+ parser.add_argument('-fass', '--force-alphabetical-sort-within-sections', action='store_true',
+ dest="force_alphabetical_sort", help='Force all imports to be sorted alphabetically within a '
+ 'section')
+ parser.add_argument('-ff', '--from-first', dest='from_first',
+ help="Switches the typical ordering preference, showing from imports first then straight ones.")
+ parser.add_argument('-fgw', '--force-grid-wrap', nargs='?', const=2, type=int, dest="force_grid_wrap",
+ help='Force number of from imports (defaults to 2) to be grid wrapped regardless of line '
+ 'length')
+ parser.add_argument('-fss', '--force-sort-within-sections', action='store_true', dest="force_sort_within_sections",
+ help='Force imports to be sorted by module, independent of import_type')
+ parser.add_argument('-i', '--indent', help='String to place for indents defaults to " " (4 spaces).',
+ dest='indent', type=str)
+ parser.add_argument('-j', '--jobs', help='Number of files to process in parallel.',
+ dest='jobs', type=int)
+ parser.add_argument('-k', '--keep-direct-and-as', dest='keep_direct_and_as_imports', action='store_true',
+ help="Turns off default behavior that removes direct imports when as imports exist.")
+ parser.add_argument('-l', '--lines', help='[Deprecated] The max length of an import line (used for wrapping '
+ 'long imports).',
+ dest='line_length', type=int)
+ parser.add_argument('-lai', '--lines-after-imports', dest='lines_after_imports', type=int)
+ parser.add_argument('-lbt', '--lines-between-types', dest='lines_between_types', type=int)
+ parser.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.")
+ parser.add_argument('-ls', '--length-sort', help='Sort imports by their string length.',
+ dest='length_sort', action='store_true')
+ parser.add_argument('-m', '--multi-line', dest='multi_line_output', type=int, choices=range(len(WrapModes)),
+ help='Multi line output (0-grid, 1-vertical, 2-hanging, 3-vert-hanging, 4-vert-grid, '
+ '5-vert-grid-grouped, 6-vert-grid-grouped-no-comma).')
+ 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`).')
+ parser.add_argument('-nlb', '--no-lines-before', help='Sections which should not be split with previous by empty lines',
+ dest='no_lines_before', action='append')
+ parser.add_argument('-ns', '--dont-skip', help='Files that sort imports should never skip over.',
+ dest='not_skip', action='append')
+ parser.add_argument('-o', '--thirdparty', dest='known_third_party', action='append',
+ help='Force sortImports to recognize a module as being part of a third party library.')
+ parser.add_argument('-ot', '--order-by-type', dest='order_by_type',
+ action='store_true', help='Order imports by type in addition to alphabetically')
+ parser.add_argument('-p', '--project', dest='known_first_party', action='append',
+ help='Force sortImports to recognize a module as being part of the current python project.')
+ parser.add_argument('-q', '--quiet', action='store_true', dest="quiet",
+ help='Shows extra quiet output, only errors are outputted.')
+ parser.add_argument('-r', dest='ambiguous_r_flag', action='store_true')
+ parser.add_argument('-rm', '--remove-import', dest='remove_imports', action='append',
+ help='Removes the specified import from all files.')
+ parser.add_argument('-rr', '--reverse-relative', dest='reverse_relative', action='store_true',
+ help='Reverse order of relative imports.')
+ parser.add_argument('-rc', '--recursive', dest='recursive', action='store_true',
+ help='Recursively look for Python files of which to sort imports')
+ parser.add_argument('-s', '--skip', help='Files that sort imports should skip over. If you want to skip multiple '
+ 'files you should specify twice: --skip file1 --skip file2.', dest='skip', action='append')
+ parser.add_argument('-sd', '--section-default', dest='default_section',
+ help='Sets the default section for imports (by default FIRSTPARTY) options: ' +
+ str(DEFAULT_SECTIONS))
+ parser.add_argument('-sg', '--skip-glob', help='Files that sort imports should skip over.', dest='skip_glob',
+ action='append')
+ 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')
+ parser.add_argument('-sp', '--settings-path', dest="settings_path",
+ help='Explicitly set the settings path instead of auto determining based on file location.')
+ parser.add_argument('-t', '--top', help='Force specific imports to the top of their appropriate section.',
+ dest='force_to_top', action='append')
+ parser.add_argument('-tc', '--trailing-comma', dest='include_trailing_comma', action='store_true',
+ help='Includes a trailing comma on multi line imports that include parentheses.')
+ parser.add_argument('-up', '--use-parentheses', dest='use_parentheses', action='store_true',
+ help='Use parenthesis for line continuation on length limit instead of slashes.')
+ parser.add_argument('-v', '--version', action='store_true', dest='show_version')
+ parser.add_argument('-vb', '--verbose', action='store_true', dest="verbose",
+ help='Shows verbose output, such as when files are skipped or when a check is successful.')
+ parser.add_argument('--virtual-env', dest='virtual_env',
+ help='Virtual environment to use for determining whether a package is third-party')
+ parser.add_argument('--conda-env', dest='conda_env',
+ help='Conda environment to use for determining whether a package is third-party')
+ parser.add_argument('-vn', '--version-number', action='version', version=__version__,
+ help='Returns just the current version number without the logo')
+ parser.add_argument('-w', '--line-width', help='The max length of an import line (used for wrapping long imports).',
+ dest='line_length', type=int)
+ parser.add_argument('-wl', '--wrap-length', dest='wrap_length',
+ help="Specifies how long lines that are wrapped should be, if not set line_length is used.")
+ parser.add_argument('-ws', '--ignore-whitespace', action='store_true', dest="ignore_whitespace",
+ help='Tells isort to ignore whitespace differences when --check-only is being used.')
+ parser.add_argument('-y', '--apply', dest='apply', action='store_true',
+ help='Tells isort to apply changes recursively without asking')
+ parser.add_argument('--unsafe', dest='unsafe', action='store_true',
+ help='Tells isort to look for files in standard library directories, etc. '
+ 'where it may not be safe to operate in')
+ parser.add_argument('--case-sensitive', dest='case_sensitive', action='store_true',
+ help='Tells isort to include casing when sorting module names')
+ parser.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 command')
+ parser.add_argument('files', nargs='*', help='One or more Python source files that need their imports sorted.')
+
+ arguments = {key: value for key, value in vars(parser.parse_args(argv)).items() if value}
+ if 'dont_order_by_type' in arguments:
+ arguments['order_by_type'] = False
+ if arguments.pop('unsafe', False):
+ arguments['safety_excludes'] = False
+ return arguments
+
+
+def main(argv=None):
+ arguments = parse_args(argv)
+ if arguments.get('show_version'):
+ print(INTRO)
+ return
+
+ if arguments.get('ambiguous_r_flag'):
+ print('ERROR: Deprecated -r flag set. This flag has been replaced with -rm to remove ambiguity between it and '
+ '-rc for recursive')
+ sys.exit(1)
+
+ arguments['check_skip'] = False
+ if 'settings_path' in arguments:
+ sp = arguments['settings_path']
+ arguments['settings_path'] = os.path.abspath(sp) if os.path.isdir(sp) else os.path.dirname(os.path.abspath(sp))
+ if not os.path.isdir(arguments['settings_path']):
+ print("WARNING: settings_path dir does not exist: {0}".format(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']):
+ print("WARNING: virtual_env dir does not exist: {0}".format(arguments['virtual_env']))
+
+ file_names = arguments.pop('files', [])
+ if file_names == ['-']:
+ try:
+ # python 3
+ file_ = sys.stdin.buffer
+ except AttributeError:
+ # python 2
+ file_ = sys.stdin
+ SortImports(file_=file_, write_to_stdout=True, **arguments)
+ else:
+ if not file_names:
+ file_names = ['.']
+ arguments['recursive'] = True
+ if not arguments.get('apply', False):
+ arguments['ask_to_apply'] = True
+
+ config = from_path(arguments.get('settings_path', '') or os.path.abspath(file_names[0]) or os.getcwd()).copy()
+ config.update(arguments)
+ wrong_sorted_files = False
+ skipped = []
+
+ if config.get('filter_files'):
+ filtered_files = []
+ for file_name in file_names:
+ if should_skip(file_name, config):
+ skipped.append(file_name)
+ else:
+ filtered_files.append(file_name)
+ file_names = filtered_files
+
+ if arguments.get('recursive', False):
+ file_names = iter_source_code(file_names, config, skipped)
+ num_skipped = 0
+ if config['verbose'] or config.get('show_logo', False):
+ print(INTRO)
+
+ jobs = arguments.get('jobs')
+ if jobs:
+ import multiprocessing
+ executor = multiprocessing.Pool(jobs)
+ attempt_iterator = executor.imap(functools.partial(sort_imports, **arguments), file_names)
+ else:
+ attempt_iterator = (sort_imports(file_name, **arguments) for file_name in file_names)
+
+ for sort_attempt in attempt_iterator:
+ if not sort_attempt:
+ continue
+ 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
+
+ if wrong_sorted_files:
+ sys.exit(1)
+
+ num_skipped += len(skipped)
+ if num_skipped and not arguments.get('quiet', False):
+ if config['verbose']:
+ for was_skipped in skipped:
+ print("WARNING: {0} was skipped as it's listed in 'skip' setting"
+ " or matches a glob in 'skip_glob' setting".format(was_skipped))
+ print("Skipped {0} files".format(num_skipped))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/venv/Lib/site-packages/isort/natural.py b/venv/Lib/site-packages/isort/natural.py
new file mode 100644
index 0000000..c02b42c
--- /dev/null
+++ b/venv/Lib/site-packages/isort/natural.py
@@ -0,0 +1,47 @@
+"""isort/natural.py.
+
+Enables sorting strings that contain numbers naturally
+
+usage:
+ natural.nsorted(list)
+
+Copyright (C) 2013 Timothy Edmund Crosley
+
+Implementation originally from @HappyLeapSecond stack overflow user in response to:
+ https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside
+
+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.
+
+"""
+import re
+
+
+def _atoi(text):
+ return int(text) if text.isdigit() else text
+
+
+def _natural_keys(text):
+ return [_atoi(c) for c in re.split(r'(\d+)', text)]
+
+
+def nsorted(to_sort, key=None):
+ """Returns a naturally sorted list"""
+ if key is None:
+ key_callback = _natural_keys
+ else:
+ def key_callback(item):
+ return _natural_keys(key(item))
+
+ return sorted(to_sort, key=key_callback)
diff --git a/venv/Lib/site-packages/isort/pie_slice.py b/venv/Lib/site-packages/isort/pie_slice.py
new file mode 100644
index 0000000..569ea76
--- /dev/null
+++ b/venv/Lib/site-packages/isort/pie_slice.py
@@ -0,0 +1,154 @@
+"""pie_slice/overrides.py.
+
+Overrides Python syntax to conform to the Python3 version as much as possible using a '*' import
+
+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.
+
+"""
+from __future__ import absolute_import
+
+import collections
+import sys
+
+__version__ = "1.1.0"
+
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+VERSION = sys.version_info
+
+__all__ = ['PY2', 'PY3', 'lru_cache', 'apply_changes_to_python_environment']
+
+
+if PY3:
+ input = input
+
+ def apply_changes_to_python_environment():
+ pass
+else:
+ input = raw_input # noqa: F821
+
+ python_environment_changes_applied = False
+
+ import sys
+ stdout = sys.stdout
+ stderr = sys.stderr
+
+ def apply_changes_to_python_environment():
+ global python_environment_changes_applied
+ if python_environment_changes_applied or sys.getdefaultencoding() == 'utf-8':
+ python_environment_changes_applied = True
+ return
+
+ try:
+ reload(sys)
+ sys.stdout = stdout
+ sys.stderr = stderr
+ sys.setdefaultencoding('utf-8')
+ except NameError: # Python 3
+ sys.exit('This should not happen!')
+
+ python_environment_changes_applied = True
+
+
+if sys.version_info < (3, 2):
+ try:
+ from threading import Lock
+ except ImportError:
+ from dummy_threading import Lock
+
+ from functools import wraps
+
+ _CacheInfo = collections.namedtuple("CacheInfo", "hits misses maxsize currsize")
+
+ def lru_cache(maxsize=100):
+ """Least-recently-used cache decorator.
+ Taking from: https://github.com/MiCHiLU/python-functools32/blob/master/functools32/functools32.py
+ with slight modifications.
+ If *maxsize* is set to None, the LRU features are disabled and the cache
+ can grow without bound.
+ Arguments to the cached function must be hashable.
+ View the cache statistics named tuple (hits, misses, maxsize, currsize) with
+ f.cache_info(). Clear the cache and statistics with f.cache_clear().
+ Access the underlying function with f.__wrapped__.
+ See: https://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
+
+ """
+ def decorating_function(user_function, tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
+ hits, misses = [0], [0]
+ kwd_mark = (object(),) # separates positional and keyword args
+ lock = Lock()
+
+ if maxsize is None:
+ CACHE = {}
+
+ @wraps(user_function)
+ def wrapper(*args, **kwds):
+ key = args
+ if kwds:
+ key += kwd_mark + tuple(sorted(kwds.items()))
+ try:
+ result = CACHE[key]
+ hits[0] += 1
+ return result
+ except KeyError:
+ pass
+ result = user_function(*args, **kwds)
+ CACHE[key] = result
+ misses[0] += 1
+ return result
+ else:
+ CACHE = collections.OrderedDict()
+
+ @wraps(user_function)
+ def wrapper(*args, **kwds):
+ key = args
+ if kwds:
+ key += kwd_mark + tuple(sorted(kwds.items()))
+ with lock:
+ cached = CACHE.get(key, None)
+ if cached:
+ del CACHE[key]
+ CACHE[key] = cached
+ hits[0] += 1
+ return cached
+ result = user_function(*args, **kwds)
+ with lock:
+ CACHE[key] = result # record recent use of this key
+ misses[0] += 1
+ while len(CACHE) > maxsize:
+ CACHE.popitem(last=False)
+ return result
+
+ def cache_info():
+ """Report CACHE statistics."""
+ with lock:
+ return _CacheInfo(hits[0], misses[0], maxsize, len(CACHE))
+
+ def cache_clear():
+ """Clear the CACHE and CACHE statistics."""
+ with lock:
+ CACHE.clear()
+ hits[0] = misses[0] = 0
+
+ wrapper.cache_info = cache_info
+ wrapper.cache_clear = cache_clear
+ return wrapper
+
+ return decorating_function
+
+else:
+ from functools import lru_cache
diff --git a/venv/Lib/site-packages/isort/pylama_isort.py b/venv/Lib/site-packages/isort/pylama_isort.py
new file mode 100644
index 0000000..6fa235f
--- /dev/null
+++ b/venv/Lib/site-packages/isort/pylama_isort.py
@@ -0,0 +1,29 @@
+import os
+import sys
+
+from pylama.lint import Linter as BaseLinter
+
+from .isort import SortImports
+
+
+class Linter(BaseLinter):
+
+ def allow(self, path):
+ """Determine if this path should be linted."""
+ return path.endswith('.py')
+
+ def run(self, path, **meta):
+ """Lint the file. Return an array of error dicts if appropriate."""
+ with open(os.devnull, 'w') as devnull:
+ # Suppress isort messages
+ sys.stdout = devnull
+
+ if SortImports(path, check=True).incorrectly_sorted:
+ return [{
+ 'lnum': 0,
+ 'col': 0,
+ 'text': 'Incorrectly sorted imports.',
+ 'type': 'ISORT'
+ }]
+ else:
+ return []
diff --git a/venv/Lib/site-packages/isort/settings.py b/venv/Lib/site-packages/isort/settings.py
new file mode 100644
index 0000000..a69471e
--- /dev/null
+++ b/venv/Lib/site-packages/isort/settings.py
@@ -0,0 +1,356 @@
+"""isort/settings.py.
+
+Defines how the default settings for isort should be loaded
+
+(First from the default setting dictionary at the top of the file, then overridden by any settings
+ in ~/.isort.cfg or $XDG_CONFIG_HOME/isort.cfg if there are any)
+
+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.
+
+"""
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import fnmatch
+import io
+import os
+import posixpath
+import re
+import sys
+import warnings
+from collections import namedtuple
+from distutils.util import strtobool
+
+from .pie_slice import lru_cache
+from .utils import difference, union
+
+try:
+ import configparser
+except ImportError:
+ import ConfigParser as configparser
+
+try:
+ import toml
+except ImportError:
+ toml = False
+
+try:
+ import appdirs
+ if appdirs.system == 'darwin':
+ appdirs.system = 'linux2'
+except ImportError:
+ appdirs = None
+
+MAX_CONFIG_SEARCH_DEPTH = 25 # The number of parent directories isort will look for a config file within
+DEFAULT_SECTIONS = ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER')
+
+safety_exclude_re = re.compile(
+ r"/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist|\.pants\.d"
+ r"|lib/python[0-9].[0-9]+)/"
+)
+
+WrapModes = ('GRID', 'VERTICAL', 'HANGING_INDENT', 'VERTICAL_HANGING_INDENT', 'VERTICAL_GRID', 'VERTICAL_GRID_GROUPED',
+ 'VERTICAL_GRID_GROUPED_NO_COMMA', 'NOQA')
+WrapModes = namedtuple('WrapModes', WrapModes)(*range(len(WrapModes)))
+
+# Note that none of these lists must be complete as they are simply fallbacks for when included auto-detection fails.
+default = {'force_to_top': [],
+ 'skip': [],
+ 'skip_glob': [],
+ 'line_length': 79,
+ 'wrap_length': 0,
+ 'line_ending': None,
+ 'sections': DEFAULT_SECTIONS,
+ 'no_sections': False,
+ 'known_future_library': ['__future__'],
+ 'known_standard_library': ['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__', 'abc', 'aepack', 'aetools',
+ 'aetypes', 'aifc', 'al', 'anydbm', 'applesingle', 'argparse', 'array', 'ast',
+ 'asynchat', 'asyncio', 'asyncore', 'atexit', 'audioop', 'autoGIL', 'base64',
+ 'bdb', 'binascii', 'binhex', 'bisect', 'bsddb', 'buildtools', 'builtins',
+ 'bz2', 'cPickle', 'cProfile', 'cStringIO', 'calendar', 'cd', 'cfmfile', 'cgi',
+ 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs', 'codeop', 'collections',
+ 'colorsys', 'commands', 'compileall', 'compiler', 'concurrent', 'configparser',
+ 'contextlib', 'contextvars', 'cookielib', 'copy', 'copy_reg', 'copyreg', 'crypt', 'csv',
+ 'ctypes', 'curses', 'dataclasses', 'datetime', 'dbhash', 'dbm', 'decimal', 'difflib',
+ 'dircache', 'dis', 'distutils', 'dl', 'doctest', 'dumbdbm', 'dummy_thread',
+ 'dummy_threading', 'email', 'encodings', 'ensurepip', 'enum', 'errno',
+ 'exceptions', 'faulthandler', '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', 'html', 'htmlentitydefs', 'htmllib', 'http',
+ 'httplib', 'ic', 'icopen', 'imageop', 'imaplib', 'imgfile', 'imghdr', 'imp',
+ 'importlib', 'imputil', 'inspect', 'io', 'ipaddress', 'itertools', 'jpeg',
+ 'json', 'keyword', 'lib2to3', 'linecache', 'locale', 'logging', 'lzma',
+ 'macerrors', 'macostools', 'macpath', 'macresource', 'mailbox', 'mailcap',
+ 'marshal', 'math', 'md5', 'mhlib', 'mimetools', 'mimetypes', 'mimify', 'mmap',
+ 'modulefinder', 'msilib', 'msvcrt', 'multifile', 'multiprocessing', 'mutex',
+ 'netrc', 'new', 'nis', 'nntplib', 'numbers', 'operator', 'optparse', 'os',
+ 'ossaudiodev', 'parser', 'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes',
+ 'pkgutil', 'platform', 'plistlib', 'popen2', 'poplib', 'posix', 'posixfile',
+ 'pprint', 'profile', 'pstats', 'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc',
+ 'queue', 'quopri', 'random', 're', 'readline', 'reprlib', 'resource', 'rexec',
+ 'rfc822', 'rlcompleter', 'robotparser', 'runpy', 'sched', 'secrets', 'select',
+ 'selectors', 'sets', 'sgmllib', 'sha', 'shelve', 'shlex', 'shutil', 'signal',
+ 'site', 'sitecustomize', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver',
+ 'spwd', 'sqlite3', 'ssl', 'stat', 'statistics', 'statvfs', 'string', 'stringprep',
+ 'struct', 'subprocess', 'sunau', 'sunaudiodev', 'symbol', 'symtable', 'sys',
+ 'sysconfig', 'syslog', 'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios',
+ 'test', 'textwrap', 'this', 'thread', 'threading', 'time', 'timeit', 'tkinter',
+ 'token', 'tokenize', 'trace', 'traceback', 'tracemalloc', 'ttk', 'tty', 'turtle',
+ 'turtledemo', 'types', 'typing', 'unicodedata', 'unittest', 'urllib', 'urllib2',
+ 'urlparse', 'usercustomize', 'uu', 'uuid', 'venv', 'videoreader',
+ 'warnings', 'wave', 'weakref', 'webbrowser', 'whichdb', 'winreg', 'winsound',
+ 'wsgiref', 'xdrlib', 'xml', 'xmlrpc', 'xmlrpclib', 'zipapp', 'zipfile',
+ 'zipimport', 'zlib'],
+ 'known_third_party': ['google.appengine.api'],
+ 'known_first_party': [],
+ 'multi_line_output': WrapModes.GRID,
+ 'forced_separate': [],
+ 'indent': ' ' * 4,
+ 'comment_prefix': ' #',
+ 'length_sort': False,
+ 'add_imports': [],
+ 'remove_imports': [],
+ 'reverse_relative': False,
+ 'force_single_line': False,
+ 'default_section': 'FIRSTPARTY',
+ 'import_heading_future': '',
+ 'import_heading_stdlib': '',
+ 'import_heading_thirdparty': '',
+ 'import_heading_firstparty': '',
+ 'import_heading_localfolder': '',
+ 'balanced_wrapping': False,
+ 'use_parentheses': False,
+ 'order_by_type': True,
+ 'atomic': False,
+ 'lines_after_imports': -1,
+ 'lines_between_sections': 1,
+ 'lines_between_types': 0,
+ 'combine_as_imports': False,
+ 'combine_star': False,
+ 'keep_direct_and_as_imports': False,
+ 'include_trailing_comma': False,
+ 'from_first': False,
+ 'verbose': False,
+ 'quiet': False,
+ 'force_adds': False,
+ 'force_alphabetical_sort_within_sections': False,
+ 'force_alphabetical_sort': False,
+ 'force_grid_wrap': 0,
+ 'force_sort_within_sections': False,
+ 'show_diff': False,
+ 'ignore_whitespace': False,
+ 'no_lines_before': [],
+ 'no_inline_sort': False,
+ 'ignore_comments': False,
+ 'safety_excludes': True,
+ 'case_sensitive': False}
+
+
+@lru_cache()
+def from_path(path):
+ computed_settings = default.copy()
+ isort_defaults = ['~/.isort.cfg']
+ if appdirs:
+ isort_defaults = [appdirs.user_config_dir('isort.cfg')] + isort_defaults
+
+ _update_settings_with_config(path, '.editorconfig', ['~/.editorconfig'], ('*', '*.py', '**.py'), computed_settings)
+ _update_settings_with_config(path, 'pyproject.toml', [], ('tool.isort', ), computed_settings)
+ _update_settings_with_config(path, '.isort.cfg', isort_defaults, ('settings', 'isort'), computed_settings)
+ _update_settings_with_config(path, 'setup.cfg', [], ('isort', 'tool:isort'), computed_settings)
+ _update_settings_with_config(path, 'tox.ini', [], ('isort', 'tool:isort'), computed_settings)
+ return computed_settings
+
+
+def _update_settings_with_config(path, name, default, sections, computed_settings):
+ editor_config_file = None
+ for potential_settings_path in default:
+ expanded = os.path.expanduser(potential_settings_path)
+ if os.path.exists(expanded):
+ editor_config_file = expanded
+ break
+
+ tries = 0
+ current_directory = path
+ while current_directory and tries < MAX_CONFIG_SEARCH_DEPTH:
+ potential_path = os.path.join(current_directory, str(name))
+ if os.path.exists(potential_path):
+ editor_config_file = potential_path
+ break
+
+ new_directory = os.path.split(current_directory)[0]
+ if current_directory == new_directory:
+ break
+ current_directory = new_directory
+ tries += 1
+
+ if editor_config_file and os.path.exists(editor_config_file):
+ _update_with_config_file(editor_config_file, sections, computed_settings)
+
+
+def _update_with_config_file(file_path, sections, computed_settings):
+ cwd = os.path.dirname(file_path)
+ settings = _get_config_data(file_path, sections).copy()
+ if not settings:
+ return
+
+ 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':
+ computed_settings['indent'] = ' ' * (indent_size and int(indent_size) or 4)
+ elif indent_style == 'tab':
+ computed_settings['indent'] = '\t' * (indent_size and int(indent_size) or 1)
+
+ max_line_length = settings.pop('max_line_length', '').strip()
+ if max_line_length:
+ computed_settings['line_length'] = float('inf') if max_line_length == 'off' else int(max_line_length)
+
+ for key, value in settings.items():
+ access_key = key.replace('not_', '').lower()
+ existing_value_type = type(default.get(access_key, ''))
+ if existing_value_type in (list, tuple):
+ # sections has fixed order values; no adding or substraction from any set
+ if access_key == 'sections':
+ computed_settings[access_key] = tuple(_as_list(value))
+ else:
+ existing_data = set(computed_settings.get(access_key, default.get(access_key)))
+ if key.startswith('not_'):
+ computed_settings[access_key] = difference(existing_data, _as_list(value))
+ elif key.startswith('known_'):
+ computed_settings[access_key] = union(existing_data, _abspaths(cwd, _as_list(value)))
+ else:
+ computed_settings[access_key] = union(existing_data, _as_list(value))
+ elif existing_value_type == bool:
+ # Only some configuration formats support native boolean values.
+ if not isinstance(value, bool):
+ value = bool(strtobool(value))
+ computed_settings[access_key] = value
+ elif key.startswith('known_'):
+ computed_settings[access_key] = list(_abspaths(cwd, _as_list(value)))
+ elif key == 'force_grid_wrap':
+ try:
+ result = existing_value_type(value)
+ except ValueError:
+ # backwards compat
+ result = default.get(access_key) if value.lower().strip() == 'false' else 2
+ computed_settings[access_key] = result
+ else:
+ computed_settings[access_key] = existing_value_type(value)
+
+
+def _as_list(value):
+ if not isinstance(value, list):
+ value = value.replace('\n', ',').split(',')
+
+ return filter(bool, [item.strip() for item in value])
+
+
+def _abspaths(cwd, values):
+ 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
+
+
+@lru_cache()
+def _get_config_data(file_path, sections):
+ settings = {}
+
+ with io.open(file_path) as config_file:
+ if file_path.endswith('.toml'):
+ if toml:
+ config = toml.load(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:
+ if '[tool.isort]' in config_file.read():
+ warnings.warn("Found {} with [tool.isort] section, but toml package is not installed. "
+ "To configure isort with {}, install with 'isort[pyproject]'.".format(file_path,
+ file_path))
+ else:
+ 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()
+
+ if sys.version_info >= (3, 2):
+ config = configparser.ConfigParser(strict=False)
+ config.read_file(config_file)
+ else:
+ config = configparser.SafeConfigParser()
+ config.readfp(config_file)
+
+ for section in sections:
+ if config.has_section(section):
+ settings.update(config.items(section))
+
+ return settings
+
+
+def should_skip(filename, config, path=''):
+ """Returns True if the file and/or folder should be skipped based on the passed in settings."""
+ os_path = os.path.join(path, filename)
+
+ normalized_path = os_path.replace('\\', '/')
+ if normalized_path[1:2] == ':':
+ normalized_path = normalized_path[2:]
+
+ if path and config['safety_excludes']:
+ check_exclude = '/' + filename.replace('\\', '/') + '/'
+ if path and os.path.basename(path) in ('lib', ):
+ check_exclude = '/' + os.path.basename(path) + check_exclude
+ if safety_exclude_re.search(check_exclude):
+ return True
+
+ for skip_path in config['skip']:
+ if posixpath.abspath(normalized_path) == posixpath.abspath(skip_path.replace('\\', '/')):
+ return True
+
+ position = os.path.split(filename)
+ while position[1]:
+ if position[1] in config['skip']:
+ return True
+ position = os.path.split(position[0])
+
+ for glob in config['skip_glob']:
+ if fnmatch.fnmatch(filename, glob) or fnmatch.fnmatch('/' + filename, glob):
+ return True
+
+ if not (os.path.isfile(os_path) or os.path.isdir(os_path) or os.path.islink(os_path)):
+ return True
+
+ return False
diff --git a/venv/Lib/site-packages/isort/utils.py b/venv/Lib/site-packages/isort/utils.py
new file mode 100644
index 0000000..ce4e588
--- /dev/null
+++ b/venv/Lib/site-packages/isort/utils.py
@@ -0,0 +1,53 @@
+import os
+import sys
+from contextlib import contextmanager
+
+
+def exists_case_sensitive(path):
+ """
+ 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 (sys.platform.startswith('win') or sys.platform == 'darwin') and result:
+ directory, basename = os.path.split(path)
+ result = basename in os.listdir(directory)
+ return result
+
+
+@contextmanager
+def chdir(path):
+ """Context manager for changing dir and restoring previous workdir after exit.
+ """
+ curdir = os.getcwd()
+ os.chdir(path)
+ try:
+ yield
+ finally:
+ os.chdir(curdir)
+
+
+def union(a, b):
+ """ Return a list of items that are in `a` or `b`
+ """
+ u = []
+ for item in a:
+ if item not in u:
+ u.append(item)
+ for item in b:
+ if item not in u:
+ u.append(item)
+ return u
+
+
+def difference(a, b):
+ """ Return a list of items from `a` that are not in `b`.
+ """
+ d = []
+ for item in a:
+ if item not in b:
+ d.append(item)
+ return d
diff --git a/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/AUTHORS.rst b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/AUTHORS.rst
new file mode 100644
index 0000000..dbc0324
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/AUTHORS.rst
@@ -0,0 +1,10 @@
+
+Authors
+=======
+
+* Ionel Cristian Mărieș - https://blog.ionelmc.ro
+* Alvin Chow - https://github.com/alvinchow86
+* Astrum Kuo - https://github.com/xowenx
+* Erik M. Bray - http://iguananaut.net
+* Ran Benita - https://github.com/bluetech
+* "hugovk" - https://github.com/hugovk
diff --git a/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/INSTALLER b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/LICENSE b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/LICENSE
new file mode 100644
index 0000000..de39b84
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/LICENSE
@@ -0,0 +1,21 @@
+BSD 2-Clause License
+
+Copyright (c) 2014-2019, Ionel Cristian Mărieș
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+disclaimer.
+
+2. 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.
+
+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 HOLDER 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/site-packages/lazy_object_proxy-1.4.3.dist-info/METADATA b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/METADATA
new file mode 100644
index 0000000..6b4b830
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/METADATA
@@ -0,0 +1,166 @@
+Metadata-Version: 2.1
+Name: lazy-object-proxy
+Version: 1.4.3
+Summary: A fast and thorough lazy object proxy.
+Home-page: https://github.com/ionelmc/python-lazy-object-proxy
+Author: Ionel Cristian Mărieș
+Author-email: contact@ionelmc.ro
+License: BSD-2-Clause
+Project-URL: Documentation, https://python-lazy-object-proxy.readthedocs.io/
+Project-URL: Changelog, https://python-lazy-object-proxy.readthedocs.io/en/latest/changelog.html
+Project-URL: Issue Tracker, https://github.com/ionelmc/python-lazy-object-proxy/issues
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: Unix
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Utilities
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
+
+========
+Overview
+========
+
+
+
+A fast and thorough lazy object proxy.
+
+* Free software: BSD 2-Clause License
+
+Note that this is based on `wrapt`_'s ObjectProxy with one big change: it calls a function the first time the proxy object is
+used, while `wrapt.ObjectProxy` just forwards the method calls to the target object.
+
+In other words, you use `lazy-object-proxy` when you only have the object way later and you use `wrapt.ObjectProxy` when you
+want to override few methods (by subclassing) and forward everything else to the target object.
+
+Example::
+
+ import lazy_object_proxy
+
+ def expensive_func():
+ from time import sleep
+ print('starting calculation')
+ # just as example for a very slow computation
+ sleep(2)
+ print('finished calculation')
+ # return the result of the calculation
+ return 10
+
+ obj = lazy_object_proxy.Proxy(expensive_func)
+ # function is called only when object is actually used
+ print(obj) # now expensive_func is called
+
+ print(obj) # the result without calling the expensive_func
+
+Installation
+============
+
+::
+
+ pip install lazy-object-proxy
+
+Documentation
+=============
+
+https://python-lazy-object-proxy.readthedocs.io/
+
+Development
+===========
+
+To run the all tests run::
+
+ tox
+
+Acknowledgements
+================
+
+This project is based on some code from `wrapt`_ as you can see in the git history.
+
+.. _wrapt: https://github.com/GrahamDumpleton/wrapt
+
+
+Changelog
+=========
+
+1.4.3 (2019-10-26)
+------------------
+
+* Added binary wheels for Python 3.8.
+* Fixed license metadata.
+
+1.4.2 (2019-08-22)
+------------------
+
+* Included a ``pyproject.toml`` to allow users install the sdist with old python/setuptools, as the
+ setuptools-scm dep will be fetched by pip instead of setuptools.
+ Fixes `#30 <https://github.com/ionelmc/python-lazy-object-proxy/issues/30>`_.
+
+1.4.1 (2019-05-10)
+------------------
+
+* Fixed wheels being built with ``-coverage`` cflags. No more issues about bogus ``cext.gcda`` files.
+* Removed useless C file from wheels.
+* Changed ``setup.py`` to use setuptools-scm.
+
+1.4.0 (2019-05-05)
+------------------
+
+* Fixed ``__mod__`` for the slots backend. Contributed by Ran Benita in
+ `#28 <https://github.com/ionelmc/python-lazy-object-proxy/pull/28>`_.
+* Dropped support for Python 2.6 and 3.3. Contributed by "hugovk" in
+ `#24 <https://github.com/ionelmc/python-lazy-object-proxy/pull/24>`_.
+
+1.3.1 (2017-05-05)
+------------------
+
+* Fix broken release (``sdist`` had a broken ``MANIFEST.in``).
+
+1.3.0 (2017-05-02)
+------------------
+
+* Speed up arithmetic operations involving ``cext.Proxy`` subclasses.
+
+1.2.2 (2016-04-14)
+------------------
+
+* Added `manylinux <https://www.python.org/dev/peps/pep-0513/>`_ wheels.
+* Minor cleanup in readme.
+
+1.2.1 (2015-08-18)
+------------------
+
+* Fix a memory leak (the wrapped object would get bogus references). Contributed by Astrum Kuo in
+ `#10 <https://github.com/ionelmc/python-lazy-object-proxy/pull/10>`_.
+
+1.2.0 (2015-07-06)
+------------------
+
+* Don't instantiate the object when __repr__ is called. This aids with debugging (allows one to see exactly in
+ what state the proxy is).
+
+1.1.0 (2015-07-05)
+------------------
+
+* Added support for pickling. The pickled value is going to be the wrapped object *without* any Proxy container.
+* Fixed a memory management issue in the C extension (reference cycles weren't garbage collected due to improper
+ handling in the C extension). Contributed by Alvin Chow in
+ `#8 <https://github.com/ionelmc/python-lazy-object-proxy/pull/8>`_.
+
+1.0.2 (2015-04-11)
+-----------------------------------------
+
+* First release on PyPI.
+
+
diff --git a/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/RECORD b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/RECORD
new file mode 100644
index 0000000..e07e94d
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/RECORD
@@ -0,0 +1,20 @@
+lazy_object_proxy-1.4.3.dist-info/AUTHORS.rst,sha256=8CeCjODba0S8UczLyZBPhpO_J6NMZ9Hz_fE1A1uNe9Y,278
+lazy_object_proxy-1.4.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+lazy_object_proxy-1.4.3.dist-info/LICENSE,sha256=W-1KNkH2bsSNuN7SNqKV8z2H0CkxXzYXZVhUzw1wxUA,1329
+lazy_object_proxy-1.4.3.dist-info/METADATA,sha256=Y2X63wcFbQT4yI3zKpRFwfpA_TCpB6U79MPmRGCMJT0,5088
+lazy_object_proxy-1.4.3.dist-info/RECORD,,
+lazy_object_proxy-1.4.3.dist-info/WHEEL,sha256=uaZe_9gV-4T_d4AskuIQkCgcY8wMc0UXsVFnf0_mBGs,106
+lazy_object_proxy-1.4.3.dist-info/top_level.txt,sha256=UNH-FQB-j_8bYqPz3gD90kHvaC42TQqY0thHSnbaa0k,18
+lazy_object_proxy/__init__.py,sha256=pMqxzToF24DuzOltm-Q8nZ3jNDXOaSQcJmiNArdUrlU,410
+lazy_object_proxy/__pycache__/__init__.cpython-37.pyc,,
+lazy_object_proxy/__pycache__/_version.cpython-37.pyc,,
+lazy_object_proxy/__pycache__/compat.cpython-37.pyc,,
+lazy_object_proxy/__pycache__/simple.cpython-37.pyc,,
+lazy_object_proxy/__pycache__/slots.cpython-37.pyc,,
+lazy_object_proxy/__pycache__/utils.cpython-37.pyc,,
+lazy_object_proxy/_version.py,sha256=KsdHOInxnNuaG_C69nBloPsRbSiniucSfNG8eBgw8yc,120
+lazy_object_proxy/cext.cp37-win_amd64.pyd,sha256=W0Ps706HlNLprmldUashETxgGaQoq6_A5J4w0PPuoJw,31744
+lazy_object_proxy/compat.py,sha256=DY3HbKwbrbeKY6tkXRNRLJ1go6HJb8HUwWqyw3T5g_g,196
+lazy_object_proxy/simple.py,sha256=guacy8_QbJeBs7vXpPPVoDVkDNXOZ86xyS1dtAeKvOs,8216
+lazy_object_proxy/slots.py,sha256=9DilWUINScpZN26NwmRtscTtaqaEwtCfIoriLq5Nz24,11359
+lazy_object_proxy/utils.py,sha256=x4XTrtlp_mDTWO_EOq_ILIOv2Qol8RLMnRm5M8l3OfU,291
diff --git a/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/WHEEL b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/WHEEL
new file mode 100644
index 0000000..c4dd0f9
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.6)
+Root-Is-Purelib: false
+Tag: cp37-cp37m-win_amd64
+
diff --git a/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/top_level.txt b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/top_level.txt
new file mode 100644
index 0000000..bdf032e
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy-1.4.3.dist-info/top_level.txt
@@ -0,0 +1 @@
+lazy_object_proxy
diff --git a/venv/Lib/site-packages/lazy_object_proxy/__init__.py b/venv/Lib/site-packages/lazy_object_proxy/__init__.py
new file mode 100644
index 0000000..e9a9a76
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/__init__.py
@@ -0,0 +1,23 @@
+try:
+ import copy_reg as copyreg
+except ImportError:
+ import copyreg
+
+from .utils import identity
+
+copyreg.constructor(identity)
+
+try:
+ from .cext import Proxy
+ from .cext import identity
+except ImportError:
+ from .slots import Proxy
+else:
+ copyreg.constructor(identity)
+
+try:
+ from ._version import version as __version__
+except ImportError:
+ __version__ = '1.4.3'
+
+__all__ = "Proxy",
diff --git a/venv/Lib/site-packages/lazy_object_proxy/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..0e3505d
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/lazy_object_proxy/__pycache__/_version.cpython-37.pyc b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/_version.cpython-37.pyc
new file mode 100644
index 0000000..5774aa3
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/_version.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/lazy_object_proxy/__pycache__/compat.cpython-37.pyc b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/compat.cpython-37.pyc
new file mode 100644
index 0000000..efb5ab0
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/compat.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/lazy_object_proxy/__pycache__/simple.cpython-37.pyc b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/simple.cpython-37.pyc
new file mode 100644
index 0000000..261981a
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/simple.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/lazy_object_proxy/__pycache__/slots.cpython-37.pyc b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/slots.cpython-37.pyc
new file mode 100644
index 0000000..9b76e90
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/slots.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/lazy_object_proxy/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/utils.cpython-37.pyc
new file mode 100644
index 0000000..6958b01
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/__pycache__/utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/lazy_object_proxy/_version.py b/venv/Lib/site-packages/lazy_object_proxy/_version.py
new file mode 100644
index 0000000..3136771
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/_version.py
@@ -0,0 +1,4 @@
+# coding: utf-8
+# file generated by setuptools_scm
+# don't change, don't track in version control
+version = '1.4.3'
diff --git a/venv/Lib/site-packages/lazy_object_proxy/cext.cp37-win_amd64.pyd b/venv/Lib/site-packages/lazy_object_proxy/cext.cp37-win_amd64.pyd
new file mode 100644
index 0000000..516e3f8
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/cext.cp37-win_amd64.pyd
Binary files differ
diff --git a/venv/Lib/site-packages/lazy_object_proxy/compat.py b/venv/Lib/site-packages/lazy_object_proxy/compat.py
new file mode 100644
index 0000000..dc6edfa
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/compat.py
@@ -0,0 +1,9 @@
+import sys
+
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+
+def with_metaclass(meta, *bases):
+ """Create a base class with a metaclass."""
+ return meta("NewBase", bases, {})
diff --git a/venv/Lib/site-packages/lazy_object_proxy/simple.py b/venv/Lib/site-packages/lazy_object_proxy/simple.py
new file mode 100644
index 0000000..24b1339
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/simple.py
@@ -0,0 +1,246 @@
+import operator
+
+from .compat import PY2
+from .compat import PY3
+from .compat import with_metaclass
+from .utils import cached_property
+from .utils import identity
+
+
+def make_proxy_method(code):
+ def proxy_wrapper(self, *args):
+ return code(self.__wrapped__, *args)
+
+ return proxy_wrapper
+
+
+class _ProxyMethods(object):
+ # We use properties to override the values of __module__ and
+ # __doc__. If we add these in ObjectProxy, the derived class
+ # __dict__ will still be setup to have string variants of these
+ # attributes and the rules of descriptors means that they appear to
+ # take precedence over the properties in the base class. To avoid
+ # that, we copy the properties into the derived class type itself
+ # via a meta class. In that way the properties will always take
+ # precedence.
+
+ @property
+ def __module__(self):
+ return self.__wrapped__.__module__
+
+ @__module__.setter
+ def __module__(self, value):
+ self.__wrapped__.__module__ = value
+
+ @property
+ def __doc__(self):
+ return self.__wrapped__.__doc__
+
+ @__doc__.setter
+ def __doc__(self, value):
+ self.__wrapped__.__doc__ = value
+
+ # Need to also propagate the special __weakref__ attribute for case
+ # where decorating classes which will define this. If do not define
+ # it and use a function like inspect.getmembers() on a decorator
+ # class it will fail. This can't be in the derived classes.
+
+ @property
+ def __weakref__(self):
+ return self.__wrapped__.__weakref__
+
+
+class _ProxyMetaType(type):
+ def __new__(cls, name, bases, dictionary):
+ # Copy our special properties into the class so that they
+ # always take precedence over attributes of the same name added
+ # during construction of a derived class. This is to save
+ # duplicating the implementation for them in all derived classes.
+
+ dictionary.update(vars(_ProxyMethods))
+ dictionary.pop('__dict__')
+
+ return type.__new__(cls, name, bases, dictionary)
+
+
+class Proxy(with_metaclass(_ProxyMetaType)):
+ __factory__ = None
+
+ def __init__(self, factory):
+ self.__dict__['__factory__'] = factory
+
+ @cached_property
+ def __wrapped__(self):
+ self = self.__dict__
+ if '__factory__' in self:
+ factory = self['__factory__']
+ return factory()
+ else:
+ raise ValueError("Proxy hasn't been initiated: __factory__ is missing.")
+
+ __name__ = property(make_proxy_method(operator.attrgetter('__name__')))
+ __class__ = property(make_proxy_method(operator.attrgetter('__class__')))
+ __annotations__ = property(make_proxy_method(operator.attrgetter('__anotations__')))
+ __dir__ = make_proxy_method(dir)
+ __str__ = make_proxy_method(str)
+
+ if PY3:
+ __bytes__ = make_proxy_method(bytes)
+
+ def __repr__(self, __getattr__=object.__getattribute__):
+ if '__wrapped__' in self.__dict__:
+ return '<{} at 0x{:x} wrapping {!r} at 0x{:x} with factory {!r}>'.format(
+ type(self).__name__, id(self),
+ self.__wrapped__, id(self.__wrapped__),
+ self.__factory__
+ )
+ else:
+ return '<{} at 0x{:x} with factory {!r}>'.format(
+ type(self).__name__, id(self),
+ self.__factory__
+ )
+
+ __reversed__ = make_proxy_method(reversed)
+
+ if PY3:
+ __round__ = make_proxy_method(round)
+
+ __lt__ = make_proxy_method(operator.lt)
+ __le__ = make_proxy_method(operator.le)
+ __eq__ = make_proxy_method(operator.eq)
+ __ne__ = make_proxy_method(operator.ne)
+ __gt__ = make_proxy_method(operator.gt)
+ __ge__ = make_proxy_method(operator.ge)
+ __hash__ = make_proxy_method(hash)
+ __nonzero__ = make_proxy_method(bool)
+ __bool__ = make_proxy_method(bool)
+
+ def __setattr__(self, name, value):
+ if hasattr(type(self), name):
+ self.__dict__[name] = value
+ else:
+ setattr(self.__wrapped__, name, value)
+
+ def __getattr__(self, name):
+ if name in ('__wrapped__', '__factory__'):
+ raise AttributeError(name)
+ else:
+ return getattr(self.__wrapped__, name)
+
+ def __delattr__(self, name):
+ if hasattr(type(self), name):
+ del self.__dict__[name]
+ else:
+ delattr(self.__wrapped__, name)
+
+ __add__ = make_proxy_method(operator.add)
+ __sub__ = make_proxy_method(operator.sub)
+ __mul__ = make_proxy_method(operator.mul)
+ __div__ = make_proxy_method(operator.div if PY2 else operator.truediv)
+ __truediv__ = make_proxy_method(operator.truediv)
+ __floordiv__ = make_proxy_method(operator.floordiv)
+ __mod__ = make_proxy_method(operator.mod)
+ __divmod__ = make_proxy_method(divmod)
+ __pow__ = make_proxy_method(pow)
+ __lshift__ = make_proxy_method(operator.lshift)
+ __rshift__ = make_proxy_method(operator.rshift)
+ __and__ = make_proxy_method(operator.and_)
+ __xor__ = make_proxy_method(operator.xor)
+ __or__ = make_proxy_method(operator.or_)
+
+ def __radd__(self, other):
+ return other + self.__wrapped__
+
+ def __rsub__(self, other):
+ return other - self.__wrapped__
+
+ def __rmul__(self, other):
+ return other * self.__wrapped__
+
+ def __rdiv__(self, other):
+ return operator.div(other, self.__wrapped__)
+
+ def __rtruediv__(self, other):
+ return operator.truediv(other, self.__wrapped__)
+
+ def __rfloordiv__(self, other):
+ return other // self.__wrapped__
+
+ def __rmod__(self, other):
+ return other % self.__wrapped__
+
+ def __rdivmod__(self, other):
+ return divmod(other, self.__wrapped__)
+
+ def __rpow__(self, other, *args):
+ return pow(other, self.__wrapped__, *args)
+
+ def __rlshift__(self, other):
+ return other << self.__wrapped__
+
+ def __rrshift__(self, other):
+ return other >> self.__wrapped__
+
+ def __rand__(self, other):
+ return other & self.__wrapped__
+
+ def __rxor__(self, other):
+ return other ^ self.__wrapped__
+
+ def __ror__(self, other):
+ return other | self.__wrapped__
+
+ __iadd__ = make_proxy_method(operator.iadd)
+ __isub__ = make_proxy_method(operator.isub)
+ __imul__ = make_proxy_method(operator.imul)
+ __idiv__ = make_proxy_method(operator.idiv if PY2 else operator.itruediv)
+ __itruediv__ = make_proxy_method(operator.itruediv)
+ __ifloordiv__ = make_proxy_method(operator.ifloordiv)
+ __imod__ = make_proxy_method(operator.imod)
+ __ipow__ = make_proxy_method(operator.ipow)
+ __ilshift__ = make_proxy_method(operator.ilshift)
+ __irshift__ = make_proxy_method(operator.irshift)
+ __iand__ = make_proxy_method(operator.iand)
+ __ixor__ = make_proxy_method(operator.ixor)
+ __ior__ = make_proxy_method(operator.ior)
+ __neg__ = make_proxy_method(operator.neg)
+ __pos__ = make_proxy_method(operator.pos)
+ __abs__ = make_proxy_method(operator.abs)
+ __invert__ = make_proxy_method(operator.invert)
+
+ __int__ = make_proxy_method(int)
+
+ if PY2:
+ __long__ = make_proxy_method(long) # noqa
+
+ __float__ = make_proxy_method(float)
+ __oct__ = make_proxy_method(oct)
+ __hex__ = make_proxy_method(hex)
+ __index__ = make_proxy_method(operator.index)
+ __len__ = make_proxy_method(len)
+ __contains__ = make_proxy_method(operator.contains)
+ __getitem__ = make_proxy_method(operator.getitem)
+ __setitem__ = make_proxy_method(operator.setitem)
+ __delitem__ = make_proxy_method(operator.delitem)
+
+ if PY2:
+ __getslice__ = make_proxy_method(operator.getslice)
+ __setslice__ = make_proxy_method(operator.setslice)
+ __delslice__ = make_proxy_method(operator.delslice)
+
+ def __enter__(self):
+ return self.__wrapped__.__enter__()
+
+ def __exit__(self, *args, **kwargs):
+ return self.__wrapped__.__exit__(*args, **kwargs)
+
+ __iter__ = make_proxy_method(iter)
+
+ def __call__(self, *args, **kwargs):
+ return self.__wrapped__(*args, **kwargs)
+
+ def __reduce__(self):
+ return identity, (self.__wrapped__,)
+
+ def __reduce_ex__(self, protocol):
+ return identity, (self.__wrapped__,)
diff --git a/venv/Lib/site-packages/lazy_object_proxy/slots.py b/venv/Lib/site-packages/lazy_object_proxy/slots.py
new file mode 100644
index 0000000..efb08db
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/slots.py
@@ -0,0 +1,414 @@
+import operator
+
+from .compat import PY2
+from .compat import PY3
+from .compat import with_metaclass
+from .utils import identity
+
+
+class _ProxyMethods(object):
+ # We use properties to override the values of __module__ and
+ # __doc__. If we add these in ObjectProxy, the derived class
+ # __dict__ will still be setup to have string variants of these
+ # attributes and the rules of descriptors means that they appear to
+ # take precedence over the properties in the base class. To avoid
+ # that, we copy the properties into the derived class type itself
+ # via a meta class. In that way the properties will always take
+ # precedence.
+
+ @property
+ def __module__(self):
+ return self.__wrapped__.__module__
+
+ @__module__.setter
+ def __module__(self, value):
+ self.__wrapped__.__module__ = value
+
+ @property
+ def __doc__(self):
+ return self.__wrapped__.__doc__
+
+ @__doc__.setter
+ def __doc__(self, value):
+ self.__wrapped__.__doc__ = value
+
+ # We similar use a property for __dict__. We need __dict__ to be
+ # explicit to ensure that vars() works as expected.
+
+ @property
+ def __dict__(self):
+ return self.__wrapped__.__dict__
+
+ # Need to also propagate the special __weakref__ attribute for case
+ # where decorating classes which will define this. If do not define
+ # it and use a function like inspect.getmembers() on a decorator
+ # class it will fail. This can't be in the derived classes.
+
+ @property
+ def __weakref__(self):
+ return self.__wrapped__.__weakref__
+
+
+class _ProxyMetaType(type):
+ def __new__(cls, name, bases, dictionary):
+ # Copy our special properties into the class so that they
+ # always take precedence over attributes of the same name added
+ # during construction of a derived class. This is to save
+ # duplicating the implementation for them in all derived classes.
+
+ dictionary.update(vars(_ProxyMethods))
+
+ return type.__new__(cls, name, bases, dictionary)
+
+
+class Proxy(with_metaclass(_ProxyMetaType)):
+ """
+ A proxy implementation in pure Python, using slots. You can subclass this to add
+ local methods or attributes, or enable __dict__.
+
+ The most important internals:
+
+ * ``__factory__`` is the callback that "materializes" the object we proxy to.
+ * ``__target__`` will contain the object we proxy to, once it's "materialized".
+ * ``__wrapped__`` is a property that does either:
+
+ * return ``__target__`` if it's set.
+ * calls ``__factory__``, saves result to ``__target__`` and returns said result.
+ """
+
+ __slots__ = '__target__', '__factory__'
+
+ def __init__(self, factory):
+ object.__setattr__(self, '__factory__', factory)
+
+ @property
+ def __wrapped__(self, __getattr__=object.__getattribute__, __setattr__=object.__setattr__,
+ __delattr__=object.__delattr__):
+ try:
+ return __getattr__(self, '__target__')
+ except AttributeError:
+ try:
+ factory = __getattr__(self, '__factory__')
+ except AttributeError:
+ raise ValueError("Proxy hasn't been initiated: __factory__ is missing.")
+ target = factory()
+ __setattr__(self, '__target__', target)
+ return target
+
+ @__wrapped__.deleter
+ def __wrapped__(self, __delattr__=object.__delattr__):
+ __delattr__(self, '__target__')
+
+ @__wrapped__.setter
+ def __wrapped__(self, target, __setattr__=object.__setattr__):
+ __setattr__(self, '__target__', target)
+
+ @property
+ def __name__(self):
+ return self.__wrapped__.__name__
+
+ @__name__.setter
+ def __name__(self, value):
+ self.__wrapped__.__name__ = value
+
+ @property
+ def __class__(self):
+ return self.__wrapped__.__class__
+
+ @__class__.setter # noqa
+ def __class__(self, value):
+ self.__wrapped__.__class__ = value
+
+ @property
+ def __annotations__(self):
+ return self.__wrapped__.__anotations__
+
+ @__annotations__.setter
+ def __annotations__(self, value):
+ self.__wrapped__.__annotations__ = value
+
+ def __dir__(self):
+ return dir(self.__wrapped__)
+
+ def __str__(self):
+ return str(self.__wrapped__)
+
+ if PY3:
+ def __bytes__(self):
+ return bytes(self.__wrapped__)
+
+ def __repr__(self, __getattr__=object.__getattribute__):
+ try:
+ target = __getattr__(self, '__target__')
+ except AttributeError:
+ return '<{} at 0x{:x} with factory {!r}>'.format(
+ type(self).__name__, id(self),
+ self.__factory__
+ )
+ else:
+ return '<{} at 0x{:x} wrapping {!r} at 0x{:x} with factory {!r}>'.format(
+ type(self).__name__, id(self),
+ target, id(target),
+ self.__factory__
+ )
+
+ def __reversed__(self):
+ return reversed(self.__wrapped__)
+
+ if PY3:
+ def __round__(self):
+ return round(self.__wrapped__)
+
+ def __lt__(self, other):
+ return self.__wrapped__ < other
+
+ def __le__(self, other):
+ return self.__wrapped__ <= other
+
+ def __eq__(self, other):
+ return self.__wrapped__ == other
+
+ def __ne__(self, other):
+ return self.__wrapped__ != other
+
+ def __gt__(self, other):
+ return self.__wrapped__ > other
+
+ def __ge__(self, other):
+ return self.__wrapped__ >= other
+
+ def __hash__(self):
+ return hash(self.__wrapped__)
+
+ def __nonzero__(self):
+ return bool(self.__wrapped__)
+
+ def __bool__(self):
+ return bool(self.__wrapped__)
+
+ def __setattr__(self, name, value, __setattr__=object.__setattr__):
+ if hasattr(type(self), name):
+ __setattr__(self, name, value)
+ else:
+ setattr(self.__wrapped__, name, value)
+
+ def __getattr__(self, name):
+ if name in ('__wrapped__', '__factory__'):
+ raise AttributeError(name)
+ else:
+ return getattr(self.__wrapped__, name)
+
+ def __delattr__(self, name, __delattr__=object.__delattr__):
+ if hasattr(type(self), name):
+ __delattr__(self, name)
+ else:
+ delattr(self.__wrapped__, name)
+
+ def __add__(self, other):
+ return self.__wrapped__ + other
+
+ def __sub__(self, other):
+ return self.__wrapped__ - other
+
+ def __mul__(self, other):
+ return self.__wrapped__ * other
+
+ def __div__(self, other):
+ return operator.div(self.__wrapped__, other)
+
+ def __truediv__(self, other):
+ return operator.truediv(self.__wrapped__, other)
+
+ def __floordiv__(self, other):
+ return self.__wrapped__ // other
+
+ def __mod__(self, other):
+ return self.__wrapped__ % other
+
+ def __divmod__(self, other):
+ return divmod(self.__wrapped__, other)
+
+ def __pow__(self, other, *args):
+ return pow(self.__wrapped__, other, *args)
+
+ def __lshift__(self, other):
+ return self.__wrapped__ << other
+
+ def __rshift__(self, other):
+ return self.__wrapped__ >> other
+
+ def __and__(self, other):
+ return self.__wrapped__ & other
+
+ def __xor__(self, other):
+ return self.__wrapped__ ^ other
+
+ def __or__(self, other):
+ return self.__wrapped__ | other
+
+ def __radd__(self, other):
+ return other + self.__wrapped__
+
+ def __rsub__(self, other):
+ return other - self.__wrapped__
+
+ def __rmul__(self, other):
+ return other * self.__wrapped__
+
+ def __rdiv__(self, other):
+ return operator.div(other, self.__wrapped__)
+
+ def __rtruediv__(self, other):
+ return operator.truediv(other, self.__wrapped__)
+
+ def __rfloordiv__(self, other):
+ return other // self.__wrapped__
+
+ def __rmod__(self, other):
+ return other % self.__wrapped__
+
+ def __rdivmod__(self, other):
+ return divmod(other, self.__wrapped__)
+
+ def __rpow__(self, other, *args):
+ return pow(other, self.__wrapped__, *args)
+
+ def __rlshift__(self, other):
+ return other << self.__wrapped__
+
+ def __rrshift__(self, other):
+ return other >> self.__wrapped__
+
+ def __rand__(self, other):
+ return other & self.__wrapped__
+
+ def __rxor__(self, other):
+ return other ^ self.__wrapped__
+
+ def __ror__(self, other):
+ return other | self.__wrapped__
+
+ def __iadd__(self, other):
+ self.__wrapped__ += other
+ return self
+
+ def __isub__(self, other):
+ self.__wrapped__ -= other
+ return self
+
+ def __imul__(self, other):
+ self.__wrapped__ *= other
+ return self
+
+ def __idiv__(self, other):
+ self.__wrapped__ = operator.idiv(self.__wrapped__, other)
+ return self
+
+ def __itruediv__(self, other):
+ self.__wrapped__ = operator.itruediv(self.__wrapped__, other)
+ return self
+
+ def __ifloordiv__(self, other):
+ self.__wrapped__ //= other
+ return self
+
+ def __imod__(self, other):
+ self.__wrapped__ %= other
+ return self
+
+ def __ipow__(self, other):
+ self.__wrapped__ **= other
+ return self
+
+ def __ilshift__(self, other):
+ self.__wrapped__ <<= other
+ return self
+
+ def __irshift__(self, other):
+ self.__wrapped__ >>= other
+ return self
+
+ def __iand__(self, other):
+ self.__wrapped__ &= other
+ return self
+
+ def __ixor__(self, other):
+ self.__wrapped__ ^= other
+ return self
+
+ def __ior__(self, other):
+ self.__wrapped__ |= other
+ return self
+
+ def __neg__(self):
+ return -self.__wrapped__
+
+ def __pos__(self):
+ return +self.__wrapped__
+
+ def __abs__(self):
+ return abs(self.__wrapped__)
+
+ def __invert__(self):
+ return ~self.__wrapped__
+
+ def __int__(self):
+ return int(self.__wrapped__)
+
+ if PY2:
+ def __long__(self):
+ return long(self.__wrapped__) # noqa
+
+ def __float__(self):
+ return float(self.__wrapped__)
+
+ def __oct__(self):
+ return oct(self.__wrapped__)
+
+ def __hex__(self):
+ return hex(self.__wrapped__)
+
+ def __index__(self):
+ return operator.index(self.__wrapped__)
+
+ def __len__(self):
+ return len(self.__wrapped__)
+
+ def __contains__(self, value):
+ return value in self.__wrapped__
+
+ def __getitem__(self, key):
+ return self.__wrapped__[key]
+
+ def __setitem__(self, key, value):
+ self.__wrapped__[key] = value
+
+ def __delitem__(self, key):
+ del self.__wrapped__[key]
+
+ def __getslice__(self, i, j):
+ return self.__wrapped__[i:j]
+
+ def __setslice__(self, i, j, value):
+ self.__wrapped__[i:j] = value
+
+ def __delslice__(self, i, j):
+ del self.__wrapped__[i:j]
+
+ def __enter__(self):
+ return self.__wrapped__.__enter__()
+
+ def __exit__(self, *args, **kwargs):
+ return self.__wrapped__.__exit__(*args, **kwargs)
+
+ def __iter__(self):
+ return iter(self.__wrapped__)
+
+ def __call__(self, *args, **kwargs):
+ return self.__wrapped__(*args, **kwargs)
+
+ def __reduce__(self):
+ return identity, (self.__wrapped__,)
+
+ def __reduce_ex__(self, protocol):
+ return identity, (self.__wrapped__,)
diff --git a/venv/Lib/site-packages/lazy_object_proxy/utils.py b/venv/Lib/site-packages/lazy_object_proxy/utils.py
new file mode 100644
index 0000000..ceb3050
--- /dev/null
+++ b/venv/Lib/site-packages/lazy_object_proxy/utils.py
@@ -0,0 +1,13 @@
+def identity(obj):
+ return obj
+
+
+class cached_property(object):
+ def __init__(self, func):
+ self.func = func
+
+ def __get__(self, obj, cls):
+ if obj is None:
+ return self
+ value = obj.__dict__[self.func.__name__] = self.func(obj)
+ return value
diff --git a/venv/Lib/site-packages/mccabe-0.6.1.dist-info/DESCRIPTION.rst b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/DESCRIPTION.rst
new file mode 100644
index 0000000..de61068
--- /dev/null
+++ b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/DESCRIPTION.rst
@@ -0,0 +1,152 @@
+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, 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.0`` 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 that the value::
+
+ $ flake8 --max-complexity 10 coolproject
+ ...
+ coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14)
+
+This feature is quite useful to detect over-complex code. According to McCabe,
+anything that goes beyond 10 is too complex.
+
+
+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
+
+
+Changes
+-------
+
+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/site-packages/mccabe-0.6.1.dist-info/INSTALLER b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/Lib/site-packages/mccabe-0.6.1.dist-info/METADATA b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/METADATA
new file mode 100644
index 0000000..f22645f
--- /dev/null
+++ b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/METADATA
@@ -0,0 +1,178 @@
+Metadata-Version: 2.0
+Name: mccabe
+Version: 0.6.1
+Summary: McCabe checker, plugin for flake8
+Home-page: https://github.com/pycqa/mccabe
+Author: Ian Cordasco
+Author-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 :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Software Development :: Quality Assurance
+
+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, 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.0`` 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 that the value::
+
+ $ flake8 --max-complexity 10 coolproject
+ ...
+ coolproject/mod.py:1204:1: C901 'CoolFactory.prepare' is too complex (14)
+
+This feature is quite useful to detect over-complex code. According to McCabe,
+anything that goes beyond 10 is too complex.
+
+
+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
+
+
+Changes
+-------
+
+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/site-packages/mccabe-0.6.1.dist-info/RECORD b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/RECORD
new file mode 100644
index 0000000..91abd1a
--- /dev/null
+++ b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/RECORD
@@ -0,0 +1,10 @@
+__pycache__/mccabe.cpython-37.pyc,,
+mccabe-0.6.1.dist-info/DESCRIPTION.rst,sha256=lGHJ-Y3IviuP3DRqLL_TXPUr3wJ2GZ8XJkAV6ve3O58,3302
+mccabe-0.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+mccabe-0.6.1.dist-info/METADATA,sha256=jawnTfTVrlzBSmeI-cTXSRIwIlijODtZdj-padBqIv0,4324
+mccabe-0.6.1.dist-info/RECORD,,
+mccabe-0.6.1.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
+mccabe-0.6.1.dist-info/entry_points.txt,sha256=N2NH182GXTUyTm8r8XMgadb9C-CRa5dUr1k8OC91uGE,47
+mccabe-0.6.1.dist-info/metadata.json,sha256=e508OR4t6_G7h7eO2C6NHlHQqVpPZZH1_DlAPrVECYM,1218
+mccabe-0.6.1.dist-info/top_level.txt,sha256=21cXuqZE-lpcfAqqANvX9EjI1ED1p8zcViv064u3RKA,7
+mccabe.py,sha256=XPMywdQshG_5nSjckb-OzNqnCQuXQvy3FTClspKwGQA,10693
diff --git a/venv/Lib/site-packages/mccabe-0.6.1.dist-info/WHEEL b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/WHEEL
new file mode 100644
index 0000000..8b6dd1b
--- /dev/null
+++ b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.29.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/Lib/site-packages/mccabe-0.6.1.dist-info/entry_points.txt b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/entry_points.txt
new file mode 100644
index 0000000..cc6645b
--- /dev/null
+++ b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/entry_points.txt
@@ -0,0 +1,3 @@
+[flake8.extension]
+C90 = mccabe:McCabeChecker
+
diff --git a/venv/Lib/site-packages/mccabe-0.6.1.dist-info/metadata.json b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/metadata.json
new file mode 100644
index 0000000..ae04d8f
--- /dev/null
+++ b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Quality Assurance"], "extensions": {"python.details": {"contacts": [{"email": "graffatcolmingov@gmail.com", "name": "Ian Cordasco", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/pycqa/mccabe"}}, "python.exports": {"flake8.extension": {"C90": "mccabe:McCabeChecker"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["flake8", "mccabe"], "license": "Expat license", "metadata_version": "2.0", "name": "mccabe", "summary": "McCabe checker, plugin for flake8", "test_requires": [{"requires": ["pytest"]}], "version": "0.6.1"} \ No newline at end of file
diff --git a/venv/Lib/site-packages/mccabe-0.6.1.dist-info/top_level.txt b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/top_level.txt
new file mode 100644
index 0000000..8831b36
--- /dev/null
+++ b/venv/Lib/site-packages/mccabe-0.6.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+mccabe
diff --git a/venv/Lib/site-packages/mccabe.py b/venv/Lib/site-packages/mccabe.py
new file mode 100644
index 0000000..c0cda75
--- /dev/null
+++ b/venv/Lib/site-packages/mccabe.py
@@ -0,0 +1,347 @@
+""" 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.6.1'
+
+
+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"""
+ with open(module_path, "rU") as mod:
+ code = mod.read()
+ 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/site-packages/pip-19.0.3-py3.7.egg/pip/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..e942a3d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/__pycache__/__main__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/__pycache__/__main__.cpython-37.pyc
new file mode 100644
index 0000000..3ab0d17
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/__pycache__/__main__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..652b82d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/build_env.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/build_env.cpython-37.pyc
new file mode 100644
index 0000000..9265cac
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/build_env.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/cache.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/cache.cpython-37.pyc
new file mode 100644
index 0000000..317369b
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/cache.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/configuration.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/configuration.cpython-37.pyc
new file mode 100644
index 0000000..3663edd
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/configuration.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/download.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/download.cpython-37.pyc
new file mode 100644
index 0000000..b325759
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/download.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/exceptions.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/exceptions.cpython-37.pyc
new file mode 100644
index 0000000..1d80026
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/exceptions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/index.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/index.cpython-37.pyc
new file mode 100644
index 0000000..33e8429
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/index.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/locations.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/locations.cpython-37.pyc
new file mode 100644
index 0000000..a1dc40a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/locations.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/pep425tags.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/pep425tags.cpython-37.pyc
new file mode 100644
index 0000000..0d4bd32
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/pep425tags.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/pyproject.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/pyproject.cpython-37.pyc
new file mode 100644
index 0000000..e0bed54
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/pyproject.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/resolve.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/resolve.cpython-37.pyc
new file mode 100644
index 0000000..ca401eb
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/resolve.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/wheel.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/wheel.cpython-37.pyc
new file mode 100644
index 0000000..037cf93
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/__pycache__/wheel.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..2a9f1c0
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/autocompletion.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/autocompletion.cpython-37.pyc
new file mode 100644
index 0000000..5368708
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/autocompletion.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/base_command.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/base_command.cpython-37.pyc
new file mode 100644
index 0000000..0a13ac6
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/base_command.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/cmdoptions.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/cmdoptions.cpython-37.pyc
new file mode 100644
index 0000000..79e406d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/cmdoptions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/main_parser.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/main_parser.cpython-37.pyc
new file mode 100644
index 0000000..b4248f2
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/main_parser.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/parser.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/parser.cpython-37.pyc
new file mode 100644
index 0000000..cb35c86
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/parser.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/status_codes.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/status_codes.cpython-37.pyc
new file mode 100644
index 0000000..217d9ca
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/cli/__pycache__/status_codes.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..81f2d6a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/check.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/check.cpython-37.pyc
new file mode 100644
index 0000000..4cf332a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/check.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/completion.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/completion.cpython-37.pyc
new file mode 100644
index 0000000..2f10e9a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/completion.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/configuration.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/configuration.cpython-37.pyc
new file mode 100644
index 0000000..b33e6c9
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/configuration.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/download.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/download.cpython-37.pyc
new file mode 100644
index 0000000..8c02ceb
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/download.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/freeze.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/freeze.cpython-37.pyc
new file mode 100644
index 0000000..351ab9f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/freeze.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/hash.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/hash.cpython-37.pyc
new file mode 100644
index 0000000..2b6183f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/hash.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/help.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/help.cpython-37.pyc
new file mode 100644
index 0000000..0eaefaf
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/help.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/install.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/install.cpython-37.pyc
new file mode 100644
index 0000000..4587dd5
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/install.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/list.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/list.cpython-37.pyc
new file mode 100644
index 0000000..219febe
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/list.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/search.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/search.cpython-37.pyc
new file mode 100644
index 0000000..7809bf0
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/search.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/show.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/show.cpython-37.pyc
new file mode 100644
index 0000000..bb07126
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/show.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/uninstall.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/uninstall.cpython-37.pyc
new file mode 100644
index 0000000..b528ae4
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/uninstall.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/wheel.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/wheel.cpython-37.pyc
new file mode 100644
index 0000000..8198357
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/commands/__pycache__/wheel.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..8b92b6b
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/candidate.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/candidate.cpython-37.pyc
new file mode 100644
index 0000000..445bffe
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/candidate.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/format_control.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/format_control.cpython-37.pyc
new file mode 100644
index 0000000..304897a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/format_control.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/index.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/index.cpython-37.pyc
new file mode 100644
index 0000000..cad3ad0
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/index.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/link.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/link.cpython-37.pyc
new file mode 100644
index 0000000..e74697d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/models/__pycache__/link.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..07a1eb7
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/check.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/check.cpython-37.pyc
new file mode 100644
index 0000000..a85fd1b
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/check.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/freeze.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/freeze.cpython-37.pyc
new file mode 100644
index 0000000..7d8bed4
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/freeze.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/prepare.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/prepare.cpython-37.pyc
new file mode 100644
index 0000000..952f933
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/operations/__pycache__/prepare.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..3d591d3
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/constructors.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/constructors.cpython-37.pyc
new file mode 100644
index 0000000..b60d743
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/constructors.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_file.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_file.cpython-37.pyc
new file mode 100644
index 0000000..d5eb5fc
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_file.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_install.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_install.cpython-37.pyc
new file mode 100644
index 0000000..ef28d33
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_install.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_set.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_set.cpython-37.pyc
new file mode 100644
index 0000000..c6e6d28
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_set.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_tracker.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_tracker.cpython-37.pyc
new file mode 100644
index 0000000..f054d9f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_tracker.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_uninstall.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_uninstall.cpython-37.pyc
new file mode 100644
index 0000000..9aca886
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/req/__pycache__/req_uninstall.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..964b1b1
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/appdirs.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/appdirs.cpython-37.pyc
new file mode 100644
index 0000000..7357554
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/appdirs.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/compat.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/compat.cpython-37.pyc
new file mode 100644
index 0000000..8b19861
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/compat.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/deprecation.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/deprecation.cpython-37.pyc
new file mode 100644
index 0000000..80d7f26
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/deprecation.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/encoding.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/encoding.cpython-37.pyc
new file mode 100644
index 0000000..d09bef0
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/encoding.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/filesystem.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/filesystem.cpython-37.pyc
new file mode 100644
index 0000000..5fc3e1d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/filesystem.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/glibc.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/glibc.cpython-37.pyc
new file mode 100644
index 0000000..678cffb
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/glibc.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/hashes.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/hashes.cpython-37.pyc
new file mode 100644
index 0000000..e6fc295
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/hashes.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/logging.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/logging.cpython-37.pyc
new file mode 100644
index 0000000..20fa454
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/logging.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/misc.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/misc.cpython-37.pyc
new file mode 100644
index 0000000..6000d5f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/misc.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/models.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/models.cpython-37.pyc
new file mode 100644
index 0000000..8fb6ad9
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/models.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/outdated.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/outdated.cpython-37.pyc
new file mode 100644
index 0000000..43fdcf0
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/outdated.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/packaging.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/packaging.cpython-37.pyc
new file mode 100644
index 0000000..af6c672
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/packaging.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/setuptools_build.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/setuptools_build.cpython-37.pyc
new file mode 100644
index 0000000..96efb71
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/setuptools_build.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/temp_dir.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/temp_dir.cpython-37.pyc
new file mode 100644
index 0000000..f2795a2
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/temp_dir.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/typing.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/typing.cpython-37.pyc
new file mode 100644
index 0000000..c512b9f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/typing.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/ui.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/ui.cpython-37.pyc
new file mode 100644
index 0000000..d31671f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/utils/__pycache__/ui.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..96e1d11
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/bazaar.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/bazaar.cpython-37.pyc
new file mode 100644
index 0000000..1b9c28f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/bazaar.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/git.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/git.cpython-37.pyc
new file mode 100644
index 0000000..ce34c91
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/git.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/mercurial.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/mercurial.cpython-37.pyc
new file mode 100644
index 0000000..929ae09
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/mercurial.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/subversion.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/subversion.cpython-37.pyc
new file mode 100644
index 0000000..80e464e
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_internal/vcs/__pycache__/subversion.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..85c198f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/appdirs.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/appdirs.cpython-37.pyc
new file mode 100644
index 0000000..82a5e45
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/appdirs.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/pyparsing.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/pyparsing.cpython-37.pyc
new file mode 100644
index 0000000..0060b54
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/pyparsing.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/retrying.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/retrying.cpython-37.pyc
new file mode 100644
index 0000000..44d7fec
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/retrying.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/six.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/six.cpython-37.pyc
new file mode 100644
index 0000000..09f881b
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/__pycache__/six.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..32f3e2c
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-37.pyc
new file mode 100644
index 0000000..8ec155b
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/cache.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/cache.cpython-37.pyc
new file mode 100644
index 0000000..3b39486
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/cache.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/compat.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/compat.cpython-37.pyc
new file mode 100644
index 0000000..0cd12fb
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/compat.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/controller.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/controller.cpython-37.pyc
new file mode 100644
index 0000000..07de623
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/controller.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-37.pyc
new file mode 100644
index 0000000..50fda96
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-37.pyc
new file mode 100644
index 0000000..ec38444
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-37.pyc
new file mode 100644
index 0000000..c7db3b0
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..204f42d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-37.pyc
new file mode 100644
index 0000000..3015df0
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-37.pyc
new file mode 100644
index 0000000..4ae5794
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/certifi/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/certifi/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..0a7b8cb
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/certifi/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/certifi/__pycache__/core.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/certifi/__pycache__/core.cpython-37.pyc
new file mode 100644
index 0000000..8a3c613
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/certifi/__pycache__/core.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..ae5b615
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/big5freq.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/big5freq.cpython-37.pyc
new file mode 100644
index 0000000..d09c3b9
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/big5freq.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/big5prober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/big5prober.cpython-37.pyc
new file mode 100644
index 0000000..79fd058
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/big5prober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/chardistribution.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/chardistribution.cpython-37.pyc
new file mode 100644
index 0000000..245b4ca
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/chardistribution.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-37.pyc
new file mode 100644
index 0000000..e4ba863
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/charsetprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/charsetprober.cpython-37.pyc
new file mode 100644
index 0000000..db34adc
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/charsetprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-37.pyc
new file mode 100644
index 0000000..e73da8d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/compat.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/compat.cpython-37.pyc
new file mode 100644
index 0000000..f3cd010
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/compat.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/cp949prober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/cp949prober.cpython-37.pyc
new file mode 100644
index 0000000..9a8ba33
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/cp949prober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/enums.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/enums.cpython-37.pyc
new file mode 100644
index 0000000..ca0ef53
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/enums.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/escprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/escprober.cpython-37.pyc
new file mode 100644
index 0000000..f13269c
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/escprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/escsm.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/escsm.cpython-37.pyc
new file mode 100644
index 0000000..1df7589
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/escsm.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-37.pyc
new file mode 100644
index 0000000..b24802b
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-37.pyc
new file mode 100644
index 0000000..bd8df2f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euckrprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euckrprober.cpython-37.pyc
new file mode 100644
index 0000000..7f5a090
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euckrprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-37.pyc
new file mode 100644
index 0000000..2e4e530
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euctwprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euctwprober.cpython-37.pyc
new file mode 100644
index 0000000..76147f8
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/euctwprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-37.pyc
new file mode 100644
index 0000000..cbc7ce7
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-37.pyc
new file mode 100644
index 0000000..4bd0d7a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-37.pyc
new file mode 100644
index 0000000..813719f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/jisfreq.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/jisfreq.cpython-37.pyc
new file mode 100644
index 0000000..9307a6b
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/jisfreq.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/jpcntx.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/jpcntx.cpython-37.pyc
new file mode 100644
index 0000000..04d624f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/jpcntx.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-37.pyc
new file mode 100644
index 0000000..5abfb10
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-37.pyc
new file mode 100644
index 0000000..b7c7d1a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-37.pyc
new file mode 100644
index 0000000..c0f9892
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-37.pyc
new file mode 100644
index 0000000..1c8b29c
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-37.pyc
new file mode 100644
index 0000000..094800a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-37.pyc
new file mode 100644
index 0000000..07a1579
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/latin1prober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/latin1prober.cpython-37.pyc
new file mode 100644
index 0000000..c68d0c1
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/latin1prober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-37.pyc
new file mode 100644
index 0000000..008e325
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-37.pyc
new file mode 100644
index 0000000..d56ae5a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcssm.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcssm.cpython-37.pyc
new file mode 100644
index 0000000..67d668c
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/mbcssm.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-37.pyc
new file mode 100644
index 0000000..00ee52e
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-37.pyc
new file mode 100644
index 0000000..666fd1a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sjisprober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sjisprober.cpython-37.pyc
new file mode 100644
index 0000000..c1df549
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/sjisprober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/universaldetector.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/universaldetector.cpython-37.pyc
new file mode 100644
index 0000000..d9b70a8
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/universaldetector.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/utf8prober.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/utf8prober.cpython-37.pyc
new file mode 100644
index 0000000..abc8403
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/utf8prober.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/version.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/version.cpython-37.pyc
new file mode 100644
index 0000000..8495965
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/chardet/__pycache__/version.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..56fd83c
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/ansi.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/ansi.cpython-37.pyc
new file mode 100644
index 0000000..bf6f1c5
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/ansi.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-37.pyc
new file mode 100644
index 0000000..d21905b
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/initialise.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/initialise.cpython-37.pyc
new file mode 100644
index 0000000..a79392f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/initialise.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/win32.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/win32.cpython-37.pyc
new file mode 100644
index 0000000..b1b73f9
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/win32.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/winterm.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/winterm.cpython-37.pyc
new file mode 100644
index 0000000..a9c9641
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/colorama/__pycache__/winterm.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..d988eb4
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/compat.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/compat.cpython-37.pyc
new file mode 100644
index 0000000..86a7b46
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/compat.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/resources.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/resources.cpython-37.pyc
new file mode 100644
index 0000000..736c226
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/resources.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/scripts.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/scripts.cpython-37.pyc
new file mode 100644
index 0000000..2e01f4a
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/scripts.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/util.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/util.cpython-37.pyc
new file mode 100644
index 0000000..99bedb1
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/distlib/__pycache__/util.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..5f745b2
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-37.pyc
new file mode 100644
index 0000000..8342445
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-37.pyc
new file mode 100644
index 0000000..2ae54df
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-37.pyc
new file mode 100644
index 0000000..cf3f4ca
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_utils.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_utils.cpython-37.pyc
new file mode 100644
index 0000000..734558e
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/_utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/constants.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/constants.cpython-37.pyc
new file mode 100644
index 0000000..43efcea
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/constants.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/html5parser.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/html5parser.cpython-37.pyc
new file mode 100644
index 0000000..51dd003
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/html5parser.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/serializer.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/serializer.cpython-37.pyc
new file mode 100644
index 0000000..e27c531
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/__pycache__/serializer.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..dc6dd17
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-37.pyc
new file mode 100644
index 0000000..0ac68ce
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/datrie.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/datrie.cpython-37.pyc
new file mode 100644
index 0000000..e19e983
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/datrie.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-37.pyc
new file mode 100644
index 0000000..a5e09f2
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..dad249d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-37.pyc
new file mode 100644
index 0000000..6262068
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-37.pyc
new file mode 100644
index 0000000..160968d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..7330be3
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..a1d0411
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/core.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/core.cpython-37.pyc
new file mode 100644
index 0000000..963fb5f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/core.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/idnadata.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/idnadata.cpython-37.pyc
new file mode 100644
index 0000000..c83ef17
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/idnadata.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/intranges.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/intranges.cpython-37.pyc
new file mode 100644
index 0000000..0d54126
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/intranges.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/package_data.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/package_data.cpython-37.pyc
new file mode 100644
index 0000000..5b4d3cc
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/idna/__pycache__/package_data.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..418c6af
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/linklockfile.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/linklockfile.cpython-37.pyc
new file mode 100644
index 0000000..7c5c820
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/linklockfile.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/mkdirlockfile.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/mkdirlockfile.cpython-37.pyc
new file mode 100644
index 0000000..1984697
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/lockfile/__pycache__/mkdirlockfile.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..5a04b77
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/_version.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/_version.cpython-37.pyc
new file mode 100644
index 0000000..db58f9d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/_version.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/exceptions.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/exceptions.cpython-37.pyc
new file mode 100644
index 0000000..f5f0908
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/exceptions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/fallback.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/fallback.cpython-37.pyc
new file mode 100644
index 0000000..b0d2505
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/msgpack/__pycache__/fallback.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/__about__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/__about__.cpython-37.pyc
new file mode 100644
index 0000000..29ea00f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/__about__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..887ce3f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/_compat.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/_compat.cpython-37.pyc
new file mode 100644
index 0000000..a9d0d37
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/_compat.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/_structures.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/_structures.cpython-37.pyc
new file mode 100644
index 0000000..9136b76
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/_structures.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/markers.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/markers.cpython-37.pyc
new file mode 100644
index 0000000..7b78e61
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/markers.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/requirements.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/requirements.cpython-37.pyc
new file mode 100644
index 0000000..1fccbed
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/requirements.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc
new file mode 100644
index 0000000..5b7bd84
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/specifiers.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/utils.cpython-37.pyc
new file mode 100644
index 0000000..d68ec3e
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/version.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/version.cpython-37.pyc
new file mode 100644
index 0000000..c7b9e23
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/packaging/__pycache__/version.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..40d05aa
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/compat.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/compat.cpython-37.pyc
new file mode 100644
index 0000000..a1d9d4f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/compat.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/wrappers.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/wrappers.cpython-37.pyc
new file mode 100644
index 0000000..b56c6e4
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pep517/__pycache__/wrappers.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..132792d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-37.pyc
new file mode 100644
index 0000000..f17a185
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..59b3fbe
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/bar.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/bar.cpython-37.pyc
new file mode 100644
index 0000000..e3c2e89
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/bar.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/helpers.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/helpers.cpython-37.pyc
new file mode 100644
index 0000000..89d26eb
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/helpers.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/spinner.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/spinner.cpython-37.pyc
new file mode 100644
index 0000000..3f01f3b
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/progress/__pycache__/spinner.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..2e43497
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/core.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/core.cpython-37.pyc
new file mode 100644
index 0000000..f908525
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/core.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/parser.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/parser.cpython-37.pyc
new file mode 100644
index 0000000..aca22e2
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/parser.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/test.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/test.cpython-37.pyc
new file mode 100644
index 0000000..7f9ef83
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/test.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/utils.cpython-37.pyc
new file mode 100644
index 0000000..1e22576
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/writer.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/writer.cpython-37.pyc
new file mode 100644
index 0000000..f5c3ea7
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/pytoml/__pycache__/writer.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..65262d8
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/__version__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/__version__.cpython-37.pyc
new file mode 100644
index 0000000..7320fdb
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/__version__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/_internal_utils.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/_internal_utils.cpython-37.pyc
new file mode 100644
index 0000000..e1a4e7d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/_internal_utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/adapters.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/adapters.cpython-37.pyc
new file mode 100644
index 0000000..027fdbd
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/adapters.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/api.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/api.cpython-37.pyc
new file mode 100644
index 0000000..d391e14
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/api.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/auth.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/auth.cpython-37.pyc
new file mode 100644
index 0000000..b076636
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/auth.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/certs.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/certs.cpython-37.pyc
new file mode 100644
index 0000000..553e341
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/certs.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/compat.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/compat.cpython-37.pyc
new file mode 100644
index 0000000..47d10c7
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/compat.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/cookies.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/cookies.cpython-37.pyc
new file mode 100644
index 0000000..4e74914
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/cookies.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/exceptions.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/exceptions.cpython-37.pyc
new file mode 100644
index 0000000..b401152
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/exceptions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/hooks.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/hooks.cpython-37.pyc
new file mode 100644
index 0000000..f717b31
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/hooks.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/models.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/models.cpython-37.pyc
new file mode 100644
index 0000000..1410aaf
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/models.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/packages.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/packages.cpython-37.pyc
new file mode 100644
index 0000000..777bea6
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/packages.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/sessions.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/sessions.cpython-37.pyc
new file mode 100644
index 0000000..0b3539e
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/sessions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/status_codes.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/status_codes.cpython-37.pyc
new file mode 100644
index 0000000..7db68be
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/status_codes.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/structures.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/structures.cpython-37.pyc
new file mode 100644
index 0000000..fea3c8c
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/structures.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/utils.cpython-37.pyc
new file mode 100644
index 0000000..4d3b060
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/requests/__pycache__/utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..0c50378
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/_collections.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/_collections.cpython-37.pyc
new file mode 100644
index 0000000..96c67f0
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/_collections.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/connection.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/connection.cpython-37.pyc
new file mode 100644
index 0000000..72edb3c
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/connection.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-37.pyc
new file mode 100644
index 0000000..b90ca7c
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/exceptions.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/exceptions.cpython-37.pyc
new file mode 100644
index 0000000..2b66920
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/exceptions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/fields.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/fields.cpython-37.pyc
new file mode 100644
index 0000000..414b4ca
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/fields.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/filepost.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/filepost.cpython-37.pyc
new file mode 100644
index 0000000..418b90d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/filepost.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-37.pyc
new file mode 100644
index 0000000..59a3835
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/request.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/request.cpython-37.pyc
new file mode 100644
index 0000000..ebe9d09
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/request.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/response.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/response.cpython-37.pyc
new file mode 100644
index 0000000..ce00e7d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/__pycache__/response.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..560347f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-37.pyc
new file mode 100644
index 0000000..e23a2e0
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-37.pyc
new file mode 100644
index 0000000..73a6831
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..eb1d7d1
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/__pycache__/six.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/__pycache__/six.cpython-37.pyc
new file mode 100644
index 0000000..1447ede
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/__pycache__/six.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..247f178
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..bd7877f
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/connection.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/connection.cpython-37.pyc
new file mode 100644
index 0000000..c580ddc
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/connection.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/queue.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/queue.cpython-37.pyc
new file mode 100644
index 0000000..373e38e
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/queue.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/request.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/request.cpython-37.pyc
new file mode 100644
index 0000000..557c15d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/request.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/response.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/response.cpython-37.pyc
new file mode 100644
index 0000000..1359375
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/response.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/retry.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/retry.cpython-37.pyc
new file mode 100644
index 0000000..f9eba95
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/retry.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-37.pyc
new file mode 100644
index 0000000..5cfb7de
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-37.pyc
new file mode 100644
index 0000000..2710b82
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/url.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/url.cpython-37.pyc
new file mode 100644
index 0000000..dd79999
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/url.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/wait.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/wait.cpython-37.pyc
new file mode 100644
index 0000000..64d3721
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/urllib3/util/__pycache__/wait.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/webencodings/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/webencodings/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..f2a122d
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/webencodings/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/webencodings/__pycache__/labels.cpython-37.pyc b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/webencodings/__pycache__/labels.cpython-37.pyc
new file mode 100644
index 0000000..5530901
--- /dev/null
+++ b/venv/Lib/site-packages/pip-19.0.3-py3.7.egg/pip/_vendor/webencodings/__pycache__/labels.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint-2.4.4.dist-info/COPYING b/venv/Lib/site-packages/pylint-2.4.4.dist-info/COPYING
new file mode 100644
index 0000000..b7b5f53
--- /dev/null
+++ b/venv/Lib/site-packages/pylint-2.4.4.dist-info/COPYING
@@ -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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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/site-packages/pylint-2.4.4.dist-info/INSTALLER b/venv/Lib/site-packages/pylint-2.4.4.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/Lib/site-packages/pylint-2.4.4.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/Lib/site-packages/pylint-2.4.4.dist-info/METADATA b/venv/Lib/site-packages/pylint-2.4.4.dist-info/METADATA
new file mode 100644
index 0000000..993f92b
--- /dev/null
+++ b/venv/Lib/site-packages/pylint-2.4.4.dist-info/METADATA
@@ -0,0 +1,202 @@
+Metadata-Version: 2.1
+Name: pylint
+Version: 2.4.4
+Summary: python code static checker
+Home-page: https://github.com/PyCQA/pylint
+Author: Python Code Quality Authority
+Author-email: code-quality@python.org
+License: GPL
+Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: GNU General Public License (GPL)
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3 :: Only
+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
+Requires-Python: >=3.5.*
+Requires-Dist: astroid (<2.4,>=2.3.0)
+Requires-Dist: isort (<5,>=4.2.5)
+Requires-Dist: mccabe (<0.7,>=0.6)
+Requires-Dist: colorama ; sys_platform=="win32"
+
+
+README for Pylint - http://pylint.pycqa.org/
+============================================
+
+.. image:: https://travis-ci.org/PyCQA/pylint.svg?branch=master
+ :target: https://travis-ci.org/PyCQA/pylint
+
+.. image:: https://ci.appveyor.com/api/projects/status/rbvwhakyj1y09atb/branch/master?svg=true
+ :alt: AppVeyor Build Status
+ :target: https://ci.appveyor.com/project/PCManticore/pylint
+
+.. image:: https://coveralls.io/repos/github/PyCQA/pylint/badge.svg?branch=master
+ :target: https://coveralls.io/github/PyCQA/pylint?branch=master
+
+
+.. 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: http://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
+
+.. |tideliftlogo| image:: doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White_small.png
+ :width: 75
+ :height: 60
+ :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
+
+
+======
+Pylint
+======
+
+**It's not just a linter that annoys you!**
+
+Pylint is a Python static code analysis tool which looks for programming errors,
+helps enforcing a coding standard, sniffs for code smells and offers simple refactoring
+suggestions.
+
+It's highly configurable, having special pragmas to control its errors and warnings
+from within your code, as well as from an extensive configuration file.
+It is also possible to write your own plugins for adding your own checks or for
+extending pylint in one way or another.
+
+It's a free software distributed under the GNU General Public Licence unless
+otherwise specified.
+
+Development is hosted on GitHub: https://github.com/PyCQA/pylint/
+
+You can use the code-quality@python.org mailing list to discuss about
+Pylint. Subscribe at https://mail.python.org/mailman/listinfo/code-quality/
+or read the archives at https://mail.python.org/pipermail/code-quality/
+
+Pull requests are amazing and most welcome.
+
+Install
+-------
+
+Pylint can be simply installed by running::
+
+ pip install pylint
+
+If you are using Python 3.6+, upgrade to get full support for your version::
+
+ pip install pylint --upgrade
+
+If you want to install from a source distribution, extract the tarball and run
+the following command ::
+
+ python setup.py install
+
+
+Do make sure to do the same for astroid, which is used internally by pylint.
+
+For debian and rpm packages, use your usual tools according to your Linux distribution.
+
+More information about installation and available distribution format
+can be found here_.
+
+Documentation
+-------------
+
+The documentation lives at http://pylint.pycqa.org/.
+
+Pylint is shipped with following additional commands:
+
+* pyreverse: an UML diagram generator
+* symilar: an independent similarities checker
+* epylint: Emacs and Flymake compatible Pylint
+
+
+Testing
+-------
+
+We use tox_ for running the test suite. You should be able to install it with::
+
+ pip install tox pytest
+
+
+To run the test suite for a particular Python version, you can do::
+
+ tox -e py37
+
+
+To run individual tests with ``tox``, you can do::
+
+ tox -e py37 -- -k name_of_the_test
+
+
+We use pytest_ for testing ``pylint``, which you can use without using ``tox`` for a faster development cycle.
+
+If you want to run tests on a specific portion of the code with pytest_, (pytest-cov_) and your local python version::
+
+ # ( pip install pytest-cov )
+ # Everything:
+ python3 -m pytest tests/
+ # Everything in tests/message with coverage for the relevant code:
+ python3 -m pytest tests/message/ --cov=pylint.message
+ coverage html
+ # Only the functional test "missing_kwoa_py3":
+ python3 -m pytest "tests/test_functional.py::test_functional[missing_kwoa_py3]"
+
+
+Do not forget to clone astroid_ and install the last version::
+
+
+ git clone https://github.com/PyCQA/astroid.git
+
+ # From source
+ python3 astroid/setup.py build sdist
+ pip3 install astroid/dist/astroid*.tar.gz
+
+ # Using an editable installation
+ cd astroid
+ python3 -m pip install -e .
+
+
+For more detailed information, check the documentation.
+
+.. _here: http://pylint.pycqa.org/en/latest/user_guide/installation.html
+.. _tox: https://tox.readthedocs.io/en/latest/
+.. _pytest: https://docs.pytest.org/en/latest/
+.. _pytest-cov: https://pypi.org/project/pytest-cov/
+.. _astroid: https://github.com/PyCQA/astroid
+
+License
+-------
+
+pylint is, with a few exceptions listed below, `GPLv2 <COPYING>`_.
+
+The icon files are licensed under the `CC BY-SA 4.0 <https://creativecommons.org/licenses/by-sa/4.0/>`_ license:
+
+- `doc/logo.png <doc/logo.png>`_
+- `doc/logo.svg <doc/logo.svg>`_
+
+
diff --git a/venv/Lib/site-packages/pylint-2.4.4.dist-info/RECORD b/venv/Lib/site-packages/pylint-2.4.4.dist-info/RECORD
new file mode 100644
index 0000000..f35fd61
--- /dev/null
+++ b/venv/Lib/site-packages/pylint-2.4.4.dist-info/RECORD
@@ -0,0 +1,161 @@
+../../Scripts/epylint.exe,sha256=IVTqWxqMOvlJ9zcdZWsui4X5jbN-3_FC8R_NgS_rlXQ,102800
+../../Scripts/pylint.exe,sha256=G1_p5-RPnqvYkXT9XqTZw-dlKg5V25zz7_ESJXJDKEk,102798
+../../Scripts/pyreverse.exe,sha256=s0kRApzPy-mLHHLzDwKeCVAaEqjaHOamzyoeLGhSGIs,102804
+../../Scripts/symilar.exe,sha256=da7da6dG6_65KS3ouxsgrDbNzO4mJyVO79jt-RJrQu4,102800
+pylint-2.4.4.data/scripts/epylint,sha256=ebDphNeMoKus049k5MQbxN1JYsHUsOXZxws0Do6gCG0,51
+pylint-2.4.4.data/scripts/pylint,sha256=wXf1V2_-AB_S1uuYztSS90GiTeCkJ4eBOGEQ7CO2Nmc,53
+pylint-2.4.4.data/scripts/pyreverse,sha256=4UQf7-hfOAx6Ux8d5g0d2KIjpUPRMwFhBdsKsu0gWg0,59
+pylint-2.4.4.data/scripts/symilar,sha256=iz6DGtePyfs0haoFobDfsRsMjaFOizh7E3vsevB2Ipw,55
+pylint-2.4.4.dist-info/COPYING,sha256=w4runjyMTV1ZT_VIob4FRTAjAW1ihpMfZRLbIV7B_UI,17989
+pylint-2.4.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pylint-2.4.4.dist-info/METADATA,sha256=RNy4cq2r2CQ1GQdc8lt_Ds-gFkFKSR2GbllMSCa4B1c,6549
+pylint-2.4.4.dist-info/RECORD,,
+pylint-2.4.4.dist-info/WHEEL,sha256=p46_5Uhzqz6AzeSosiOnxK-zmFja1i22CrQCjmYe8ec,92
+pylint-2.4.4.dist-info/entry_points.txt,sha256=WUTwHM2ZcExO-VSvss18AMFsQL-XcWg6O3_MwobWfmw,137
+pylint-2.4.4.dist-info/top_level.txt,sha256=j6Z9i__pIuaiCka6Ul9YIy6yI5aw5QbCtldLvZlMksE,7
+pylint/__init__.py,sha256=4q6rdl1balJqnrIL-PvmRQjhUVZudPskFR_FCmyXCNI,1075
+pylint/__main__.py,sha256=8vKyCk6C5CcmdnhB7enBUDvHlzfPw3kCOg1NDMYT2f8,206
+pylint/__pkginfo__.py,sha256=DZsRD_0SjytZvXFo8vddIUj4IHkHu2u9wkslchWzPsY,3416
+pylint/__pycache__/__init__.cpython-37.pyc,,
+pylint/__pycache__/__main__.cpython-37.pyc,,
+pylint/__pycache__/__pkginfo__.cpython-37.pyc,,
+pylint/__pycache__/config.cpython-37.pyc,,
+pylint/__pycache__/constants.cpython-37.pyc,,
+pylint/__pycache__/epylint.cpython-37.pyc,,
+pylint/__pycache__/exceptions.cpython-37.pyc,,
+pylint/__pycache__/graph.cpython-37.pyc,,
+pylint/__pycache__/interfaces.cpython-37.pyc,,
+pylint/__pycache__/lint.cpython-37.pyc,,
+pylint/__pycache__/testutils.cpython-37.pyc,,
+pylint/checkers/__init__.py,sha256=awT2wQ7SPNGZteIaz1tOX59KbiY2UFy5r-NPU5c7N9w,1993
+pylint/checkers/__pycache__/__init__.cpython-37.pyc,,
+pylint/checkers/__pycache__/async.cpython-37.pyc,,
+pylint/checkers/__pycache__/base.cpython-37.pyc,,
+pylint/checkers/__pycache__/base_checker.cpython-37.pyc,,
+pylint/checkers/__pycache__/classes.cpython-37.pyc,,
+pylint/checkers/__pycache__/design_analysis.cpython-37.pyc,,
+pylint/checkers/__pycache__/exceptions.cpython-37.pyc,,
+pylint/checkers/__pycache__/format.cpython-37.pyc,,
+pylint/checkers/__pycache__/imports.cpython-37.pyc,,
+pylint/checkers/__pycache__/logging.cpython-37.pyc,,
+pylint/checkers/__pycache__/misc.cpython-37.pyc,,
+pylint/checkers/__pycache__/newstyle.cpython-37.pyc,,
+pylint/checkers/__pycache__/python3.cpython-37.pyc,,
+pylint/checkers/__pycache__/raw_metrics.cpython-37.pyc,,
+pylint/checkers/__pycache__/refactoring.cpython-37.pyc,,
+pylint/checkers/__pycache__/similar.cpython-37.pyc,,
+pylint/checkers/__pycache__/spelling.cpython-37.pyc,,
+pylint/checkers/__pycache__/stdlib.cpython-37.pyc,,
+pylint/checkers/__pycache__/strings.cpython-37.pyc,,
+pylint/checkers/__pycache__/typecheck.cpython-37.pyc,,
+pylint/checkers/__pycache__/utils.cpython-37.pyc,,
+pylint/checkers/__pycache__/variables.cpython-37.pyc,,
+pylint/checkers/async.py,sha256=y9-K0c8cNsy6YMKu_88wPeu61uYRaheInKJIGHMLHUw,3451
+pylint/checkers/base.py,sha256=rQsQSof2wx7wD5-HpygwUY74QXA26qcX2zLOANQxwVw,89595
+pylint/checkers/base_checker.py,sha256=ewLRP3fZ_tjzFzEmfugLfTeIU0TvZtjvF8m5Q3lABrY,7460
+pylint/checkers/classes.py,sha256=cvn-fYFdAURANVTE963Z3T3MKC_vE2lXatMyzRr6y08,69645
+pylint/checkers/design_analysis.py,sha256=vxEyEwUj_ends_cPB6e9Tc6Tv3HpYErskaCEby4_OI0,16621
+pylint/checkers/exceptions.py,sha256=MOO_5W854m0INx8usht1R5MvkpkbkZ2Khz1xZTu9uzg,20898
+pylint/checkers/format.py,sha256=Mm9tE37dkFH_1fuqccoe2x5vVQ9SOg8sUmLE3e2yRew,48349
+pylint/checkers/imports.py,sha256=kSfacz6r859NyafODJR6fVkffEHX8PmjrFdocETtJac,37704
+pylint/checkers/logging.py,sha256=mrl6jhnuBxRYHNZO-KkZbZP9UHxfEthRYus2YPBzEyo,14596
+pylint/checkers/misc.py,sha256=vGQmutXZcpz5hOx8SDcXtAzo7B_4I5wvjRgZhDli6vM,6033
+pylint/checkers/newstyle.py,sha256=zdip8yMkBW4E7LdQP6-p4Y6c62i7k_6KN49x39hJsGA,4655
+pylint/checkers/python3.py,sha256=gFjjlIxNnF12R4gQgWA4c0ltdtBX9yYuB4t1wrm7-Yo,51909
+pylint/checkers/raw_metrics.py,sha256=NeQi3tFoEMovJaEyV1eChxfN42IceADMKDSjj7UZP0M,3833
+pylint/checkers/refactoring.py,sha256=dM0Mtbp09U_lwNdohT877Boa7gt7DtMP3MHuSnuKtaA,60684
+pylint/checkers/similar.py,sha256=ED6DDyTVIZCGJ44ttoaAEInC0xoH4kNchclUSdHw8ro,15011
+pylint/checkers/spelling.py,sha256=ljuWf-rfYsZikKrg_ysVFax1n2hwhNVY5j6dFUEOZCw,13561
+pylint/checkers/stdlib.py,sha256=ksyf2XBkrct6DjS_sSKNW4-83klY29kTZPtwSgxH_eQ,17226
+pylint/checkers/strings.py,sha256=lKn_5Z4ImMNvHMVZRlJSEV11rsi8oucGKR55ascOchc,31064
+pylint/checkers/typecheck.py,sha256=NDNbCmQhOVNW1Vy13a3GIG2B9bGVtUfh86ms-lGHEUw,67752
+pylint/checkers/utils.py,sha256=D5i2uNXaWi4xWb4_B4ti91Hcc2bTokQ0mJ-rfuh6510,42482
+pylint/checkers/variables.py,sha256=dO7mZ9fpUBpIYZD8I6rv4NT2kYrBQhkEbCYmxo9s59w,76008
+pylint/config.py,sha256=2KHwQ9_Dzo8vYx9AecJF8qR7kZt1fq-Euw_777-G_sk,32626
+pylint/constants.py,sha256=rrW4Rztp75h0RIvy6jRc-zdDIWd8_M2d7OyoLmEz6u4,1185
+pylint/epylint.py,sha256=hSBM9-wetxUHvjIqvTyooImc8WV02RdV9vpt4zj2ZrM,6972
+pylint/exceptions.py,sha256=UxbRwVONB4q1Q8UX8-Ywc5u_APVXcHD1Lxi_b4JSLx0,1040
+pylint/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pylint/extensions/__pycache__/__init__.cpython-37.pyc,,
+pylint/extensions/__pycache__/_check_docs_utils.cpython-37.pyc,,
+pylint/extensions/__pycache__/bad_builtin.cpython-37.pyc,,
+pylint/extensions/__pycache__/broad_try_clause.cpython-37.pyc,,
+pylint/extensions/__pycache__/check_docs.cpython-37.pyc,,
+pylint/extensions/__pycache__/check_elif.cpython-37.pyc,,
+pylint/extensions/__pycache__/comparetozero.cpython-37.pyc,,
+pylint/extensions/__pycache__/docparams.cpython-37.pyc,,
+pylint/extensions/__pycache__/docstyle.cpython-37.pyc,,
+pylint/extensions/__pycache__/emptystring.cpython-37.pyc,,
+pylint/extensions/__pycache__/mccabe.cpython-37.pyc,,
+pylint/extensions/__pycache__/overlapping_exceptions.cpython-37.pyc,,
+pylint/extensions/__pycache__/redefined_variable_type.cpython-37.pyc,,
+pylint/extensions/_check_docs_utils.py,sha256=4ma2xqZKDqwB8jJskxUwdQkeG-ebO4P124qrw32E7XY,22777
+pylint/extensions/bad_builtin.py,sha256=6Via-KVRtuDry55wEhS5va4xeavB86IorvbWe5u7uG8,2358
+pylint/extensions/broad_try_clause.py,sha256=YD2E10bCOwzTzAdPMj45t_CI8BfFhWz-nUevvAjqubc,1799
+pylint/extensions/check_docs.py,sha256=PUXI8A1742UTxcoBAQ-dE88e_lFXlUYxfmkIS1z5eq8,784
+pylint/extensions/check_elif.py,sha256=qm_W2MJ5RBR2lt3K-E8JfXIYJTRuuFdDYeslO2UsG5s,2494
+pylint/extensions/comparetozero.py,sha256=_cKM2ulCv7Ig12gjQEANKOUvR0J4kp0TtaVqe16tEkE,2357
+pylint/extensions/docparams.py,sha256=uc2OfCYmGgAJxtBupEeiGPyjhlBCqRvEvGgik7teNB0,20161
+pylint/extensions/docstyle.py,sha256=ZlzRRXn0_b0a_AIHr-gTXtyIOm7pAVjVC_RvTLxBMSg,2904
+pylint/extensions/emptystring.py,sha256=BlYgMnaL0-N6We1cqQStYpQcyoYfoB9zbF6gM-UxPko,2456
+pylint/extensions/mccabe.py,sha256=1LYOmLdtiF_dyf1CKdixVBHeqWjfGHR6F1clKQiEmJM,6123
+pylint/extensions/overlapping_exceptions.py,sha256=xI8wsuncRzEW1Lf4xOhOLqjeWMF1qbYo91m2IQLBUi0,3291
+pylint/extensions/redefined_variable_type.py,sha256=RhDFCUUzkh8maonooUYDiq3GILUWjgkMtd7CLcy3o_o,4207
+pylint/graph.py,sha256=rvwe1_uYAX4f2BKTBdVxt4dQoOCUbEPg6YMG7AnTA6c,6545
+pylint/interfaces.py,sha256=_DndrjiOJTix3PR4tfJAfMZFPZ69IOBsbZMBzs-yX4k,3176
+pylint/lint.py,sha256=UPSGMHFDaO-Qim7HuyEqFNzv4-HNVYvR1FY3JLInQQY,70721
+pylint/message/__init__.py,sha256=mF-jL0vStH9kGJolQ11BHWpUfWx4hy1MeuaHOWTML2c,2654
+pylint/message/__pycache__/__init__.cpython-37.pyc,,
+pylint/message/__pycache__/message.cpython-37.pyc,,
+pylint/message/__pycache__/message_definition.cpython-37.pyc,,
+pylint/message/__pycache__/message_definition_store.cpython-37.pyc,,
+pylint/message/__pycache__/message_handler_mix_in.cpython-37.pyc,,
+pylint/message/__pycache__/message_id_store.cpython-37.pyc,,
+pylint/message/message.py,sha256=E-2yX4WtE0543P_z72ds3aH-mdg52pQKvdh0rk2k_JE,1324
+pylint/message/message_definition.py,sha256=NihHjlvXHhws0TbpRK-qE7YFvOdRhHYrHCdRh87tGc8,3008
+pylint/message/message_definition_store.py,sha256=OIXHLV153vuEpoup8QqUxy7Z0J4g63afjJXNuwvoZhg,3560
+pylint/message/message_handler_mix_in.py,sha256=FGyDLUBLHfoyryMHlBkbo3qbzpabtVgwcfsspLmlKDA,15264
+pylint/message/message_id_store.py,sha256=hn5KuWPh1FbzxuAkVxOsz3KYTWUPemVW7-DhlRbPEQo,5316
+pylint/pyreverse/__init__.py,sha256=runafCn0veg0di-i8TztMGlKEJO3Qg01MICGqDgZ0c0,202
+pylint/pyreverse/__pycache__/__init__.cpython-37.pyc,,
+pylint/pyreverse/__pycache__/diadefslib.cpython-37.pyc,,
+pylint/pyreverse/__pycache__/diagrams.cpython-37.pyc,,
+pylint/pyreverse/__pycache__/inspector.cpython-37.pyc,,
+pylint/pyreverse/__pycache__/main.cpython-37.pyc,,
+pylint/pyreverse/__pycache__/utils.cpython-37.pyc,,
+pylint/pyreverse/__pycache__/vcgutils.cpython-37.pyc,,
+pylint/pyreverse/__pycache__/writer.cpython-37.pyc,,
+pylint/pyreverse/diadefslib.py,sha256=K29JXIglMxnU4qunTajs6Ehh1_lzeO3-pwqlFBkuWz4,8628
+pylint/pyreverse/diagrams.py,sha256=fQ36OWK0XnGtCSAdOxithCkoTt-ZuYwDmf2k7apTchM,8962
+pylint/pyreverse/inspector.py,sha256=FF8GZh_sexjveki7CsJUVaQZV-9zlAj20fUfVcMaVkk,11996
+pylint/pyreverse/main.py,sha256=-uIdvJxlEeh2gte5ioGTYQC2gMUbcnAesAUAH9Cx21g,6329
+pylint/pyreverse/utils.py,sha256=4GmoTztZ19Fy7Qr7q38OGaAib-QkdQQUbfc4kVFDFRk,6183
+pylint/pyreverse/vcgutils.py,sha256=-aMHC9LhVsE4kmHrSyC_qowfxpKKLnBHj3Q84vFOwlc,6408
+pylint/pyreverse/writer.py,sha256=ouwvhkec8FAg1FzhtEnnrEXSH1e7khXdM6J4F8O6YqY,7803
+pylint/reporters/__init__.py,sha256=Wy75bdMIrvt6vpXnAmR86uyofWlSpk-bDrqMv3dk0ls,1530
+pylint/reporters/__pycache__/__init__.cpython-37.pyc,,
+pylint/reporters/__pycache__/base_reporter.cpython-37.pyc,,
+pylint/reporters/__pycache__/collecting_reporter.cpython-37.pyc,,
+pylint/reporters/__pycache__/json_reporter.cpython-37.pyc,,
+pylint/reporters/__pycache__/reports_handler_mix_in.cpython-37.pyc,,
+pylint/reporters/__pycache__/text.cpython-37.pyc,,
+pylint/reporters/base_reporter.py,sha256=xzI1J2Be-i43Dbik7ei_4SmzXmmw9vTQqMZZWI4BVsg,2056
+pylint/reporters/collecting_reporter.py,sha256=swn9TgqlYF9pIM_ZY5gdE_oh4l1I5MTfqt6ao_l4_SQ,503
+pylint/reporters/json_reporter.py,sha256=-rumpEwEWgoeKf48a0Y5M8mWAaM1uU3uMbR2dYCT56A,1775
+pylint/reporters/reports_handler_mix_in.py,sha256=dtPqqs1EmzQgi8dlZ5nrLYrOc4PqgHwWa_3GWkqcfPM,2729
+pylint/reporters/text.py,sha256=3lpsa3jTI-EaUmGHk3cFHRHmiW-GdpUvRBdVOywNsiw,7930
+pylint/reporters/ureports/__init__.py,sha256=L-azj4EDF4DEtR9bIU8C5F2y_fgtxCySP_UzKK-4G1M,3156
+pylint/reporters/ureports/__pycache__/__init__.cpython-37.pyc,,
+pylint/reporters/ureports/__pycache__/nodes.cpython-37.pyc,,
+pylint/reporters/ureports/__pycache__/text_writer.cpython-37.pyc,,
+pylint/reporters/ureports/nodes.py,sha256=RJj-HfDAyGg7TbplWMOODsqM96q_pc0PfCx2v4PZ3no,5196
+pylint/reporters/ureports/text_writer.py,sha256=f5UWHO343n417BgVX8HrrMJaQTPrFdyHP3QmWQz_HRY,3232
+pylint/testutils.py,sha256=BL2lyuXIrSHEO-ScTEIdmPRvRqGiVmUMClInsA17kpo,9657
+pylint/utils/__init__.py,sha256=Recd7HK7QOwdxExYLWWf0mQxLPQVMC3Ih0AgH6P17wE,2735
+pylint/utils/__pycache__/__init__.cpython-37.pyc,,
+pylint/utils/__pycache__/ast_walker.cpython-37.pyc,,
+pylint/utils/__pycache__/file_state.cpython-37.pyc,,
+pylint/utils/__pycache__/utils.cpython-37.pyc,,
+pylint/utils/ast_walker.py,sha256=Hx9TIbHaWjlIVdQSuotLVgrf-noGGc2-9XsGJABLLBs,2932
+pylint/utils/file_state.py,sha256=lCuwlKTZefCDWjSu6Hyqt2TjKaLpm0FWSFfSPZMSIag,5987
+pylint/utils/utils.py,sha256=_lyD_iGf3bLW-AWaaEDaMQZPWA3zZ1mUfhCUCFA_rq4,12392
diff --git a/venv/Lib/site-packages/pylint-2.4.4.dist-info/WHEEL b/venv/Lib/site-packages/pylint-2.4.4.dist-info/WHEEL
new file mode 100644
index 0000000..3b5c403
--- /dev/null
+++ b/venv/Lib/site-packages/pylint-2.4.4.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.6)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/venv/Lib/site-packages/pylint-2.4.4.dist-info/entry_points.txt b/venv/Lib/site-packages/pylint-2.4.4.dist-info/entry_points.txt
new file mode 100644
index 0000000..063b5e4
--- /dev/null
+++ b/venv/Lib/site-packages/pylint-2.4.4.dist-info/entry_points.txt
@@ -0,0 +1,6 @@
+[console_scripts]
+epylint = pylint:run_epylint
+pylint = pylint:run_pylint
+pyreverse = pylint:run_pyreverse
+symilar = pylint:run_symilar
+
diff --git a/venv/Lib/site-packages/pylint-2.4.4.dist-info/top_level.txt b/venv/Lib/site-packages/pylint-2.4.4.dist-info/top_level.txt
new file mode 100644
index 0000000..7fb0ea1
--- /dev/null
+++ b/venv/Lib/site-packages/pylint-2.4.4.dist-info/top_level.txt
@@ -0,0 +1 @@
+pylint
diff --git a/venv/Lib/site-packages/pylint/__init__.py b/venv/Lib/site-packages/pylint/__init__.py
new file mode 100644
index 0000000..8980938
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__init__.py
@@ -0,0 +1,43 @@
+# Copyright (c) 2008, 2012 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014, 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import sys
+
+from pylint.__pkginfo__ import version as __version__
+from pylint.checkers.similar import Run as SimilarRun
+from pylint.epylint import Run as EpylintRun
+from pylint.lint import Run as PylintRun
+from pylint.pyreverse.main import Run as PyreverseRun
+
+
+def run_pylint():
+ """run pylint"""
+
+ try:
+ PylintRun(sys.argv[1:])
+ except KeyboardInterrupt:
+ sys.exit(1)
+
+
+def run_epylint():
+ """run pylint"""
+
+ EpylintRun()
+
+
+def run_pyreverse():
+ """run pyreverse"""
+
+ PyreverseRun(sys.argv[1:])
+
+
+def run_symilar():
+ """run symilar"""
+
+ SimilarRun(sys.argv[1:])
diff --git a/venv/Lib/site-packages/pylint/__main__.py b/venv/Lib/site-packages/pylint/__main__.py
new file mode 100644
index 0000000..e12309b
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__main__.py
@@ -0,0 +1,7 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+#!/usr/bin/env python
+import pylint
+
+pylint.run_pylint()
diff --git a/venv/Lib/site-packages/pylint/__pkginfo__.py b/venv/Lib/site-packages/pylint/__pkginfo__.py
new file mode 100644
index 0000000..68702f4
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pkginfo__.py
@@ -0,0 +1,85 @@
+# Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2010 Julien Jehannet <julien.jehannet@logilab.fr>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Ricardo Gemignani <ricardo.gemignani@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2016 Florian Bruhin <git@the-compiler.org>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2017-2018 Hugo <hugovk@users.noreply.github.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+# pylint: disable=redefined-builtin,invalid-name
+"""pylint packaging information"""
+
+from os.path import join
+
+# For an official release, use dev_version = None
+numversion = (2, 4, 4)
+dev_version = None
+
+version = ".".join(str(num) for num in numversion)
+if dev_version is not None:
+ version += "-dev" + str(dev_version)
+
+install_requires = ["astroid>=2.3.0,<2.4", "isort>=4.2.5,<5", "mccabe>=0.6,<0.7"]
+
+dependency_links = [] # type: ignore
+
+extras_require = {}
+extras_require[':sys_platform=="win32"'] = ["colorama"]
+
+license = "GPL"
+description = "python code static checker"
+web = "https://github.com/PyCQA/pylint"
+mailinglist = "mailto:code-quality@python.org"
+author = "Python Code Quality Authority"
+author_email = "code-quality@python.org"
+
+classifiers = [
+ "Development Status :: 6 - Mature",
+ "Environment :: Console",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: GNU General Public License (GPL)",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3 :: Only",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+ "Topic :: Software Development :: Debuggers",
+ "Topic :: Software Development :: Quality Assurance",
+ "Topic :: Software Development :: Testing",
+]
+
+
+long_desc = """\
+ Pylint is a Python source code analyzer which looks for programming
+ errors, helps enforcing a coding standard and sniffs for some code
+ smells (as defined in Martin Fowler's Refactoring book)
+ .
+ Pylint can be seen as another PyChecker since nearly all tests you
+ can do with PyChecker can also be done with Pylint. However, Pylint
+ offers some more features, like checking length of lines of code,
+ checking if variable names are well-formed according to your coding
+ standard, or checking if declared interfaces are truly implemented,
+ and much more.
+ .
+ Additionally, it is possible to write plugins to add your own checks.
+ .
+ Pylint is shipped with "pyreverse" (UML diagram generator)
+ and "symilar" (an independent similarities checker)."""
+
+scripts = [
+ join("bin", filename) for filename in ("pylint", "symilar", "epylint", "pyreverse")
+]
diff --git a/venv/Lib/site-packages/pylint/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..4a5176d
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/__main__.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/__main__.cpython-37.pyc
new file mode 100644
index 0000000..06de374
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/__main__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/__pkginfo__.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/__pkginfo__.cpython-37.pyc
new file mode 100644
index 0000000..41da823
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/__pkginfo__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/config.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/config.cpython-37.pyc
new file mode 100644
index 0000000..0d3fde9
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/config.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/constants.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/constants.cpython-37.pyc
new file mode 100644
index 0000000..1d96028
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/constants.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/epylint.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/epylint.cpython-37.pyc
new file mode 100644
index 0000000..1b8630e
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/epylint.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/exceptions.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/exceptions.cpython-37.pyc
new file mode 100644
index 0000000..9766c27
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/exceptions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/graph.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/graph.cpython-37.pyc
new file mode 100644
index 0000000..3a0dd39
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/graph.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/interfaces.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/interfaces.cpython-37.pyc
new file mode 100644
index 0000000..53b4224
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/interfaces.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/lint.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/lint.cpython-37.pyc
new file mode 100644
index 0000000..ed84248
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/lint.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/__pycache__/testutils.cpython-37.pyc b/venv/Lib/site-packages/pylint/__pycache__/testutils.cpython-37.pyc
new file mode 100644
index 0000000..8db991c
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/__pycache__/testutils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__init__.py b/venv/Lib/site-packages/pylint/checkers/__init__.py
new file mode 100644
index 0000000..9c6306f
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__init__.py
@@ -0,0 +1,64 @@
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2013 buck@yelp.com <buck@yelp.com>
+# Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""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
+17: refactoring
+18-50: not yet used: reserved for future internal checkers.
+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 pylint.checkers.base_checker import BaseChecker, BaseTokenChecker
+from pylint.utils import register_plugins
+
+
+def table_lines_from_stats(stats, _, columns):
+ """get values listed in <columns> from <stats> and <old_stats>,
+ and return a formated list of values, designed to be given to a
+ ureport.Table object
+ """
+ lines = []
+ for m_type in columns:
+ new = stats[m_type]
+ new = "%.3f" % new if isinstance(new, float) else str(new)
+ lines += (m_type.replace("_", " "), new, "NC", "NC")
+ return lines
+
+
+def initialize(linter):
+ """initialize linter with checkers in this package """
+ register_plugins(linter, __path__[0])
+
+
+__all__ = ("BaseChecker", "BaseTokenChecker", "initialize")
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..3782086
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/async.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/async.cpython-37.pyc
new file mode 100644
index 0000000..ea14658
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/async.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/base.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/base.cpython-37.pyc
new file mode 100644
index 0000000..aaa3e51
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/base.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/base_checker.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/base_checker.cpython-37.pyc
new file mode 100644
index 0000000..e4f8221
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/base_checker.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/classes.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/classes.cpython-37.pyc
new file mode 100644
index 0000000..d0f58b4
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/classes.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-37.pyc
new file mode 100644
index 0000000..647b5aa
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/exceptions.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/exceptions.cpython-37.pyc
new file mode 100644
index 0000000..5371c29
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/exceptions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/format.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/format.cpython-37.pyc
new file mode 100644
index 0000000..8a6a0c0
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/format.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/imports.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/imports.cpython-37.pyc
new file mode 100644
index 0000000..f8b924d
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/imports.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/logging.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/logging.cpython-37.pyc
new file mode 100644
index 0000000..90cc06e
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/logging.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/misc.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/misc.cpython-37.pyc
new file mode 100644
index 0000000..9f449d4
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/misc.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/newstyle.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/newstyle.cpython-37.pyc
new file mode 100644
index 0000000..e409591
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/newstyle.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/python3.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/python3.cpython-37.pyc
new file mode 100644
index 0000000..b405dd3
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/python3.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-37.pyc
new file mode 100644
index 0000000..fdf16f6
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/refactoring.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/refactoring.cpython-37.pyc
new file mode 100644
index 0000000..f65c6b5
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/refactoring.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/similar.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/similar.cpython-37.pyc
new file mode 100644
index 0000000..09b77e5
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/similar.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/spelling.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/spelling.cpython-37.pyc
new file mode 100644
index 0000000..dbf748c
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/spelling.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/stdlib.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/stdlib.cpython-37.pyc
new file mode 100644
index 0000000..97576df
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/stdlib.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/strings.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/strings.cpython-37.pyc
new file mode 100644
index 0000000..0aab77c
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/strings.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/typecheck.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/typecheck.cpython-37.pyc
new file mode 100644
index 0000000..cc0c9b4
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/typecheck.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/utils.cpython-37.pyc
new file mode 100644
index 0000000..90e8ff1
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/__pycache__/variables.cpython-37.pyc b/venv/Lib/site-packages/pylint/checkers/__pycache__/variables.cpython-37.pyc
new file mode 100644
index 0000000..943ffbd
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/__pycache__/variables.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/checkers/async.py b/venv/Lib/site-packages/pylint/checkers/async.py
new file mode 100644
index 0000000..c33071e
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/async.py
@@ -0,0 +1,89 @@
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Checker for anything related to the async protocol (PEP 492)."""
+
+import sys
+
+import astroid
+from astroid import bases, exceptions
+
+from pylint import checkers, interfaces, utils
+from pylint.checkers import utils as checker_utils
+from pylint.checkers.utils import decorated_with
+
+
+class AsyncChecker(checkers.BaseChecker):
+ __implements__ = interfaces.IAstroidChecker
+ 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):
+ self._ignore_mixin_members = utils.get_global_option(
+ self, "ignore-mixin-members"
+ )
+ self._async_generators = ["contextlib.asynccontextmanager"]
+
+ @checker_utils.check_messages("yield-inside-async-function")
+ def visit_asyncfunctiondef(self, node):
+ for child in node.nodes_of_class(astroid.Yield):
+ if child.scope() is node and (
+ sys.version_info[:2] == (3, 5) or isinstance(child, astroid.YieldFrom)
+ ):
+ self.add_message("yield-inside-async-function", node=child)
+
+ @checker_utils.check_messages("not-async-context-manager")
+ def visit_asyncwith(self, node):
+ for ctx_mgr, _ in node.items:
+ inferred = checker_utils.safe_infer(ctx_mgr)
+ if inferred is None or inferred is astroid.Uninferable:
+ continue
+
+ if isinstance(inferred, bases.AsyncGenerator):
+ # Check if we are dealing with a function decorated
+ # with contextlib.asynccontextmanager.
+ if decorated_with(inferred.parent, self._async_generators):
+ continue
+ else:
+ try:
+ inferred.getattr("__aenter__")
+ inferred.getattr("__aexit__")
+ except 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
+ # Just ignore mixin classes.
+ if self._ignore_mixin_members:
+ if inferred.name[-5:].lower() == "mixin":
+ continue
+ else:
+ continue
+
+ self.add_message(
+ "not-async-context-manager", node=node, args=(inferred.name,)
+ )
+
+
+def register(linter):
+ """required method to auto register this checker"""
+ linter.register_checker(AsyncChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/base.py b/venv/Lib/site-packages/pylint/checkers/base.py
new file mode 100644
index 0000000..c94676e
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/base.py
@@ -0,0 +1,2333 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2016 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Nick Bastin <nick.bastin@gmail.com>
+# Copyright (c) 2015 Michael Kefeder <oss@multiwave.ch>
+# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
+# Copyright (c) 2015 Stephane Wirtel <stephane@wirtel.be>
+# Copyright (c) 2015 Cosmin Poieana <cmin@ropython.org>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016, 2018 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
+# Copyright (c) 2016 Elias Dorneles <eliasdorneles@gmail.com>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2016 Yannack <yannack@users.noreply.github.com>
+# Copyright (c) 2016 Alex Jurkiewicz <alex@jurkiewi.cz>
+# Copyright (c) 2017 Jacques Kvam <jwkvam@gmail.com>
+# Copyright (c) 2017 ttenhoeve-aa <ttenhoeve@appannie.com>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Steven M. Vascellaro <svascellaro@gmail.com>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Chris Lamb <chris@chris-lamb.co.uk>
+# Copyright (c) 2018 glmdgrielson <32415403+glmdgrielson@users.noreply.github.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""basic checker for Python code"""
+
+import builtins
+import collections
+import itertools
+import re
+import sys
+from typing import Pattern
+
+import astroid
+import astroid.bases
+import astroid.scoped_nodes
+from astroid.arguments import CallSite
+
+import pylint.utils as lint_utils
+from pylint import checkers, exceptions, interfaces
+from pylint.checkers import utils
+from pylint.checkers.utils import is_property_setter_or_deleter
+from pylint.reporters.ureports import nodes as reporter_nodes
+
+
+class NamingStyle:
+ # It may seem counterintuitive that single naming style
+ # has multiple "accepted" forms of regular expressions,
+ # but we need to special-case stuff like dunder names
+ # in method names.
+ CLASS_NAME_RGX = None # type: Pattern[str]
+ MOD_NAME_RGX = None # type: Pattern[str]
+ CONST_NAME_RGX = None # type: Pattern[str]
+ COMP_VAR_RGX = None # type: Pattern[str]
+ DEFAULT_NAME_RGX = None # type: Pattern[str]
+ CLASS_ATTRIBUTE_RGX = None # type: Pattern[str]
+
+ @classmethod
+ def get_regex(cls, name_type):
+ 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,
+ "inlinevar": cls.COMP_VAR_RGX,
+ }[name_type]
+
+
+class SnakeCaseStyle(NamingStyle):
+ """Regex rules for snake_case naming style."""
+
+ CLASS_NAME_RGX = re.compile("[a-z_][a-z0-9_]+$")
+ MOD_NAME_RGX = re.compile("([a-z_][a-z0-9_]*)$")
+ CONST_NAME_RGX = re.compile("(([a-z_][a-z0-9_]*)|(__.*__))$")
+ COMP_VAR_RGX = re.compile("[a-z_][a-z0-9_]*$")
+ DEFAULT_NAME_RGX = re.compile(
+ "(([a-z_][a-z0-9_]{2,})|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$"
+ )
+ CLASS_ATTRIBUTE_RGX = re.compile(r"(([a-z_][a-z0-9_]{2,}|(__.*__)))$")
+
+
+class CamelCaseStyle(NamingStyle):
+ """Regex rules for camelCase naming style."""
+
+ CLASS_NAME_RGX = re.compile("[a-z_][a-zA-Z0-9]+$")
+ MOD_NAME_RGX = re.compile("([a-z_][a-zA-Z0-9]*)$")
+ CONST_NAME_RGX = re.compile("(([a-z_][A-Za-z0-9]*)|(__.*__))$")
+ COMP_VAR_RGX = re.compile("[a-z_][A-Za-z0-9]*$")
+ DEFAULT_NAME_RGX = re.compile("(([a-z_][a-zA-Z0-9]{2,})|(__[a-z][a-zA-Z0-9_]+__))$")
+ CLASS_ATTRIBUTE_RGX = re.compile(r"([a-z_][A-Za-z0-9]{2,}|(__.*__))$")
+
+
+class PascalCaseStyle(NamingStyle):
+ """Regex rules for PascalCase naming style."""
+
+ CLASS_NAME_RGX = re.compile("[A-Z_][a-zA-Z0-9]+$")
+ MOD_NAME_RGX = re.compile("[A-Z_][a-zA-Z0-9]+$")
+ CONST_NAME_RGX = re.compile("(([A-Z_][A-Za-z0-9]*)|(__.*__))$")
+ COMP_VAR_RGX = re.compile("[A-Z_][a-zA-Z0-9]+$")
+ DEFAULT_NAME_RGX = re.compile("[A-Z_][a-zA-Z0-9]{2,}$|(__[a-z][a-zA-Z0-9_]+__)$")
+ CLASS_ATTRIBUTE_RGX = re.compile("[A-Z_][a-zA-Z0-9]{2,}$")
+
+
+class UpperCaseStyle(NamingStyle):
+ """Regex rules for UPPER_CASE naming style."""
+
+ CLASS_NAME_RGX = re.compile("[A-Z_][A-Z0-9_]+$")
+ MOD_NAME_RGX = re.compile("[A-Z_][A-Z0-9_]+$")
+ CONST_NAME_RGX = re.compile("(([A-Z_][A-Z0-9_]*)|(__.*__))$")
+ COMP_VAR_RGX = re.compile("[A-Z_][A-Z0-9_]+$")
+ DEFAULT_NAME_RGX = re.compile("([A-Z_][A-Z0-9_]{2,})|(__[a-z][a-zA-Z0-9_]+__)$")
+ CLASS_ATTRIBUTE_RGX = re.compile("[A-Z_][A-Z0-9_]{2,}$")
+
+
+class AnyStyle(NamingStyle):
+ @classmethod
+ def get_regex(cls, name_type):
+ return re.compile(".*")
+
+
+NAMING_STYLES = {
+ "snake_case": SnakeCaseStyle,
+ "camelCase": CamelCaseStyle,
+ "PascalCase": PascalCaseStyle,
+ "UPPER_CASE": UpperCaseStyle,
+ "any": AnyStyle,
+}
+
+# do not require a doc string on private/system methods
+NO_REQUIRED_DOC_RGX = re.compile("^_")
+REVERSED_PROTOCOL_METHOD = "__reversed__"
+SEQUENCE_PROTOCOL_METHODS = ("__getitem__", "__len__")
+REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS, (REVERSED_PROTOCOL_METHOD,))
+TYPECHECK_COMPARISON_OPERATORS = frozenset(("is", "is not", "==", "!=", "in", "not in"))
+LITERAL_NODE_TYPES = (astroid.Const, astroid.Dict, astroid.List, astroid.Set)
+UNITTEST_CASE = "unittest.case"
+BUILTINS = builtins.__name__
+TYPE_QNAME = "%s.type" % BUILTINS
+ABC_METACLASSES = {"_py_abc.ABCMeta", "abc.ABCMeta"} # Python 3.7+,
+
+# Name categories that are always consistent with all naming conventions.
+EXEMPT_NAME_CATEGORIES = {"exempt", "ignore"}
+
+# A mapping from builtin-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()", "{}", "[]"],
+ )
+)
+REVERSED_COMPS = {"<": ">", "<=": ">=", ">": "<", ">=": "<="}
+COMPARISON_OPERATORS = frozenset(("==", "!=", "<", ">", "<=", ">="))
+# List of methods which can be redefined
+REDEFINABLE_METHODS = frozenset(("__module__",))
+TYPING_FORWARD_REF_QNAME = "typing.ForwardRef"
+
+
+def _redefines_import(node):
+ """ 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, astroid.ExceptHandler):
+ current = current.parent
+ if not current or not utils.error_of_type(current.parent, ImportError):
+ return False
+ try_block = current.parent.parent
+ for import_node in try_block.nodes_of_class((astroid.ImportFrom, astroid.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 in_loop(node):
+ """return True if the node is inside a kind of for loop"""
+ parent = node.parent
+ while parent is not None:
+ if isinstance(
+ parent,
+ (
+ astroid.For,
+ astroid.ListComp,
+ astroid.SetComp,
+ astroid.DictComp,
+ astroid.GeneratorExp,
+ ),
+ ):
+ return True
+ parent = parent.parent
+ return False
+
+
+def in_nested_list(nested_list, obj):
+ """return true if the object is an element of <nested_list> or of a nested
+ list
+ """
+ for elmt in nested_list:
+ if isinstance(elmt, (list, tuple)):
+ if in_nested_list(elmt, obj):
+ return True
+ elif elmt == obj:
+ return True
+ return False
+
+
+def _get_break_loop_node(break_node):
+ """
+ Returns the loop node that holds the break node in arguments.
+
+ Args:
+ break_node (astroid.Break): the break node of interest.
+
+ Returns:
+ astroid.For or astroid.While: the loop node holding the break node.
+ """
+ loop_nodes = (astroid.For, astroid.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):
+ """
+ Returns true if a loop may ends up in a break statement.
+
+ Args:
+ loop (astroid.For, astroid.While): the loop node inspected.
+
+ Returns:
+ bool: True if the loop may ends up in a break statement, False otherwise.
+ """
+ loop_nodes = (astroid.For, astroid.While)
+ definition_nodes = (astroid.FunctionDef, astroid.ClassDef)
+ inner_loop_nodes = [
+ _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(astroid.Break, skip_klass=definition_nodes)
+ if _get_break_loop_node(_node) not in inner_loop_nodes
+ )
+
+
+def _is_multi_naming_match(match, node_type, confidence):
+ return (
+ match is not None
+ and match.lastgroup is not None
+ and match.lastgroup not in EXEMPT_NAME_CATEGORIES
+ and (node_type != "method" or confidence != interfaces.INFERENCE_FAILURE)
+ )
+
+
+BUILTIN_PROPERTY = "builtins.property"
+
+
+def _get_properties(config):
+ """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() # 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 _determine_function_name_type(node, config=None):
+ """Determine the name type whose regex the a function's name should match.
+
+ :param node: A function node.
+ :type node: astroid.node_classes.NodeNG
+ :param config: Configuration from which to pull additional property classes.
+ :type config: :class:`optparse.Values`
+
+ :returns: One of ('function', 'method', 'attr')
+ :rtype: str
+ """
+ property_classes, property_names = _get_properties(config)
+ if not node.is_method():
+ return "function"
+
+ if is_property_setter_or_deleter(node):
+ # If the function is decorated using the prop_method.{setter,getter}
+ # form, treat it like an attribute as well.
+ return "attr"
+
+ if node.decorators:
+ decorators = node.decorators.nodes
+ else:
+ decorators = []
+ for decorator in decorators:
+ # If the function is a property (decorated with @property
+ # or @abc.abstractproperty), the name type is 'attr'.
+ if isinstance(decorator, astroid.Name) or (
+ isinstance(decorator, astroid.Attribute)
+ and decorator.attrname in property_names
+ ):
+ inferred = utils.safe_infer(decorator)
+ if inferred and inferred.qname() in property_classes:
+ return "attr"
+ return "method"
+
+
+def _has_abstract_methods(node):
+ """
+ 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 report_by_type_stats(sect, stats, _):
+ """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 = {}
+ for node_type in ("module", "class", "method", "function"):
+ try:
+ total = stats[node_type]
+ except KeyError:
+ raise exceptions.EmptyReportError()
+ nice_stats[node_type] = {}
+ if total != 0:
+ try:
+ documented = total - stats["undocumented_" + node_type]
+ percent = (documented * 100.0) / total
+ nice_stats[node_type]["percent_documented"] = "%.2f" % percent
+ except KeyError:
+ nice_stats[node_type]["percent_documented"] = "NC"
+ try:
+ percent = (stats["badname_" + node_type] * 100.0) / total
+ nice_stats[node_type]["percent_badname"] = "%.2f" % percent
+ except KeyError:
+ nice_stats[node_type]["percent_badname"] = "NC"
+ lines = ("type", "number", "old number", "difference", "%documented", "%badname")
+ for node_type in ("module", "class", "method", "function"):
+ new = stats[node_type]
+ lines += (
+ node_type,
+ str(new),
+ "NC",
+ "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))
+
+
+def redefined_by_decorator(node):
+ """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, astroid.Attribute)
+ and getattr(decorator.expr, "name", None) == node.name
+ ):
+ return True
+ return False
+
+
+class _BasicChecker(checkers.BaseChecker):
+ __implements__ = interfaces.IAstroidChecker
+ name = "basic"
+
+
+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 '
+ "outside in a generator function or method (e.g. with some "
+ '"yield" statements).',
+ {"maxversion": (3, 3)},
+ ),
+ "E0107": (
+ "Use of the non-existent %s operator",
+ "nonexistent-operator",
+ "Used when you attempt to use the C-style pre-increment or "
+ "pre-decrement operator -- and ++, which doesn't exist in Python.",
+ ),
+ "E0108": (
+ "Duplicate argument name %s in function definition",
+ "duplicate-argument-name",
+ "Duplicate argument names in function definitions are syntax errors.",
+ ),
+ "E0110": (
+ "Abstract class %r with abstract methods instantiated",
+ "abstract-class-instantiated",
+ "Used when an abstract class with `abc.ABCMeta` as metaclass "
+ "has abstract methods and is instantiated.",
+ ),
+ "W0120": (
+ "Else clause on loop without a break statement",
+ "useless-else-on-loop",
+ "Loops should only have an else clause if they can exit early "
+ "with a break statement, otherwise the statements under else "
+ "should be on the same scope as the loop itself.",
+ ),
+ "E0112": (
+ "More than one starred expression in assignment",
+ "too-many-star-expressions",
+ "Emitted when there are more than one starred "
+ "expressions (`*x`) in an assignment. This is a SyntaxError.",
+ ),
+ "E0113": (
+ "Starred assignment target must be in a list or tuple",
+ "invalid-star-assignment-target",
+ "Emitted when a star expression is used as a starred assignment target.",
+ ),
+ "E0114": (
+ "Can use starred expression only in assignment target",
+ "star-needs-assignment-target",
+ "Emitted when a star expression is not used in an assignment target.",
+ ),
+ "E0115": (
+ "Name %r is nonlocal and global",
+ "nonlocal-and-global",
+ "Emitted when a name is both nonlocal and global.",
+ ),
+ "E0116": (
+ "'continue' not supported inside 'finally' clause",
+ "continue-in-finally",
+ "Emitted when the `continue` keyword is found "
+ "inside a finally clause, which is a SyntaxError.",
+ ),
+ "E0117": (
+ "nonlocal name %s found without binding",
+ "nonlocal-without-binding",
+ "Emitted when a nonlocal variable does not have an attached "
+ "name somewhere in the parent scopes",
+ ),
+ "E0118": (
+ "Name %r is used prior to global declaration",
+ "used-prior-global-declaration",
+ "Emitted when a name is used prior a global declaration, "
+ "which results in an error since Python 3.6.",
+ {"minversion": (3, 6)},
+ ),
+ }
+
+ @utils.check_messages("function-redefined")
+ def visit_classdef(self, node):
+ self._check_redefinition("class", node)
+
+ def _too_many_starred_for_tuple(self, assign_tuple):
+ starred_count = 0
+ for elem in assign_tuple.itered():
+ if isinstance(elem, astroid.Tuple):
+ return self._too_many_starred_for_tuple(elem)
+ if isinstance(elem, astroid.Starred):
+ starred_count += 1
+ return starred_count > 1
+
+ @utils.check_messages("too-many-star-expressions", "invalid-star-assignment-target")
+ def visit_assign(self, node):
+ # Check *a, *b = ...
+ assign_target = node.targets[0]
+ # Check *a = b
+ if isinstance(node.targets[0], astroid.Starred):
+ self.add_message("invalid-star-assignment-target", node=node)
+
+ if not isinstance(assign_target, astroid.Tuple):
+ return
+ if self._too_many_starred_for_tuple(assign_target):
+ self.add_message("too-many-star-expressions", node=node)
+
+ @utils.check_messages("star-needs-assignment-target")
+ def visit_starred(self, node):
+ """Check that a Starred expression is used in an assignment target."""
+ if isinstance(node.parent, astroid.Call):
+ # f(*args) is converted to Call(args=[Starred]), so ignore
+ # them for this check.
+ return
+ if isinstance(
+ node.parent, (astroid.List, astroid.Tuple, astroid.Set, astroid.Dict)
+ ):
+ # PEP 448 unpacking.
+ return
+
+ stmt = node.statement()
+ if not isinstance(stmt, astroid.Assign):
+ return
+
+ if stmt.value is node or stmt.value.parent_of(node):
+ self.add_message("star-needs-assignment-target", node=node)
+
+ @utils.check_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):
+ 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(
+ astroid.Return, skip_klass=(astroid.FunctionDef, astroid.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 = collections.defaultdict(list)
+ arguments = filter(None, [node.args.args, node.args.kwonlyargs])
+
+ for arg in itertools.chain.from_iterable(arguments):
+ arg_clusters[arg.name].append(arg)
+
+ # provide detailed report about each repeated argument
+ for argument_duplicates in arg_clusters.values():
+ if len(argument_duplicates) != 1:
+ for argument in argument_duplicates:
+ self.add_message(
+ "duplicate-argument-name",
+ line=argument.lineno,
+ node=argument,
+ args=(argument.name,),
+ )
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ def _check_name_used_prior_global(self, node):
+
+ scope_globals = {
+ name: child
+ for child in node.nodes_of_class(astroid.Global)
+ for name in child.names
+ if child.scope() is node
+ }
+
+ if not scope_globals:
+ return
+
+ for node_name in node.nodes_of_class(astroid.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):
+ """Check that a name is both nonlocal and global."""
+
+ def same_scope(current):
+ return current.scope() is node
+
+ from_iter = itertools.chain.from_iterable
+ nonlocals = set(
+ from_iter(
+ child.names
+ for child in node.nodes_of_class(astroid.Nonlocal)
+ if same_scope(child)
+ )
+ )
+
+ if not nonlocals:
+ return
+
+ global_vars = set(
+ from_iter(
+ child.names
+ for child in node.nodes_of_class(astroid.Global)
+ if same_scope(child)
+ )
+ )
+ for name in nonlocals.intersection(global_vars):
+ self.add_message("nonlocal-and-global", args=(name,), node=node)
+
+ @utils.check_messages("return-outside-function")
+ def visit_return(self, node):
+ if not isinstance(node.frame(), astroid.FunctionDef):
+ self.add_message("return-outside-function", node=node)
+
+ @utils.check_messages("yield-outside-function")
+ def visit_yield(self, node):
+ self._check_yield_outside_func(node)
+
+ @utils.check_messages("yield-outside-function")
+ def visit_yieldfrom(self, node):
+ self._check_yield_outside_func(node)
+
+ @utils.check_messages("not-in-loop", "continue-in-finally")
+ def visit_continue(self, node):
+ self._check_in_loop(node, "continue")
+
+ @utils.check_messages("not-in-loop")
+ def visit_break(self, node):
+ self._check_in_loop(node, "break")
+
+ @utils.check_messages("useless-else-on-loop")
+ def visit_for(self, node):
+ self._check_else_on_loop(node)
+
+ @utils.check_messages("useless-else-on-loop")
+ def visit_while(self, node):
+ self._check_else_on_loop(node)
+
+ @utils.check_messages("nonexistent-operator")
+ def visit_unaryop(self, node):
+ """check use of the non-existent ++ and -- operator operator"""
+ if (
+ (node.op in "+-")
+ and isinstance(node.operand, astroid.UnaryOp)
+ and (node.operand.op == node.op)
+ ):
+ self.add_message("nonexistent-operator", node=node, args=node.op * 2)
+
+ def _check_nonlocal_without_binding(self, node, name):
+ current_scope = node.scope()
+ while True:
+ if current_scope.parent is None:
+ break
+
+ if not isinstance(current_scope, (astroid.ClassDef, astroid.FunctionDef)):
+ self.add_message("nonlocal-without-binding", args=(name,), node=node)
+ return
+
+ if name not in current_scope.locals:
+ current_scope = current_scope.parent.scope()
+ continue
+
+ # Okay, found it.
+ return
+
+ if not isinstance(current_scope, astroid.FunctionDef):
+ self.add_message("nonlocal-without-binding", args=(name,), node=node)
+
+ @utils.check_messages("nonlocal-without-binding")
+ def visit_nonlocal(self, node):
+ for name in node.names:
+ self._check_nonlocal_without_binding(node, name)
+
+ @utils.check_messages("abstract-class-instantiated")
+ def visit_call(self, node):
+ """ Check instantiating abstract class with
+ abc.ABCMeta as metaclass.
+ """
+ try:
+ for inferred in node.func.infer():
+ self._check_inferred_class_is_abstract(inferred, node)
+ except astroid.InferenceError:
+ return
+
+ def _check_inferred_class_is_abstract(self, inferred, node):
+ if not isinstance(inferred, astroid.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):
+ if not isinstance(node.frame(), (astroid.FunctionDef, astroid.Lambda)):
+ self.add_message("yield-outside-function", node=node)
+
+ def _check_else_on_loop(self, node):
+ """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, node_name):
+ """check that a node is inside a for or while loop"""
+ _node = node.parent
+ while _node:
+ if isinstance(_node, (astroid.For, astroid.While)):
+ if node not in _node.orelse:
+ return
+
+ if isinstance(_node, (astroid.ClassDef, astroid.FunctionDef)):
+ break
+ if (
+ isinstance(_node, astroid.TryFinally)
+ and node in _node.finalbody
+ and isinstance(node, astroid.Continue)
+ ):
+ self.add_message("continue-in-finally", node=node)
+
+ _node = _node.parent
+
+ self.add_message("not-in-loop", node=node, args=node_name)
+
+ def _check_redefinition(self, redeftype, node):
+ """check for redefinition of a function / method / class name"""
+ parent_frame = node.parent.frame()
+
+ # Ignore function stubs created for type information
+ redefinitions = parent_frame.locals[node.name]
+ 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, astroid.ClassDef)
+ and node.name in REDEFINABLE_METHODS
+ ):
+ return
+
+ if utils.is_overload_stub(node):
+ return
+
+ # Check if we have forward references for this node.
+ try:
+ redefinition_index = redefinitions.index(node)
+ except ValueError:
+ pass
+ else:
+ for redefinition in redefinitions[:redefinition_index]:
+ inferred = utils.safe_infer(redefinition)
+ if (
+ inferred
+ and isinstance(inferred, astroid.Instance)
+ and inferred.qname() == TYPING_FORWARD_REF_QNAME
+ ):
+ return
+
+ dummy_variables_rgx = lint_utils.get_global_option(
+ self, "dummy-variables-rgx", default=None
+ )
+ if dummy_variables_rgx and dummy_variables_rgx.match(node.name):
+ return
+ self.add_message(
+ "function-redefined",
+ node=node,
+ args=(redeftype, defined_self.fromlineno),
+ )
+
+
+class BasicChecker(_BasicChecker):
+ """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
+ """
+
+ __implements__ = interfaces.IAstroidChecker
+
+ 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",
+ 'Used when you use the "exec" statement (function for Python '
+ "3), to discourage its usage. That doesn't "
+ "mean you cannot use it !",
+ ),
+ "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 2-item-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.",
+ ),
+ }
+
+ reports = (("RP0101", "Statistics by type", report_by_type_stats),)
+
+ def __init__(self, linter):
+ _BasicChecker.__init__(self, linter)
+ self.stats = None
+ self._tryfinallys = None
+
+ def open(self):
+ """initialize visit variables and statistics
+ """
+ self._tryfinallys = []
+ self.stats = self.linter.add_stats(module=0, function=0, method=0, class_=0)
+
+ @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test")
+ def visit_if(self, node):
+ self._check_using_constant_test(node, node.test)
+
+ @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test")
+ def visit_ifexp(self, node):
+ self._check_using_constant_test(node, node.test)
+
+ @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test")
+ def visit_comprehension(self, node):
+ if node.ifs:
+ for if_test in node.ifs:
+ self._check_using_constant_test(node, if_test)
+
+ def _check_using_constant_test(self, node, test):
+ const_nodes = (
+ astroid.Module,
+ astroid.scoped_nodes.GeneratorExp,
+ astroid.Lambda,
+ astroid.FunctionDef,
+ astroid.ClassDef,
+ astroid.bases.Generator,
+ astroid.UnboundMethod,
+ astroid.BoundMethod,
+ astroid.Module,
+ )
+ structs = (astroid.Dict, astroid.Tuple, astroid.Set)
+
+ # These nodes are excepted, since they are not constant
+ # values, requiring a computation to happen.
+ except_nodes = (
+ astroid.Call,
+ astroid.BinOp,
+ astroid.BoolOp,
+ astroid.UnaryOp,
+ astroid.Subscript,
+ )
+ inferred = None
+ emit = isinstance(test, (astroid.Const,) + structs + const_nodes)
+ if not isinstance(test, except_nodes):
+ inferred = utils.safe_infer(test)
+
+ if emit:
+ self.add_message("using-constant-test", node=node)
+ elif isinstance(inferred, const_nodes):
+ # If the constant node is a FunctionDef or Lambda then
+ #  it may be a illicit function call due to missing parentheses
+ call_inferred = None
+ if isinstance(inferred, astroid.FunctionDef):
+ call_inferred = inferred.infer_call_result()
+ elif isinstance(inferred, astroid.Lambda):
+ call_inferred = inferred.infer_call_result(node)
+ if call_inferred:
+ try:
+ for inf_call in call_inferred:
+ if inf_call != astroid.Uninferable:
+ self.add_message(
+ "missing-parentheses-for-call-in-test", node=node
+ )
+ break
+ except astroid.InferenceError:
+ pass
+ self.add_message("using-constant-test", node=node)
+
+ def visit_module(self, _):
+ """check module name, docstring and required arguments
+ """
+ self.stats["module"] += 1
+
+ def visit_classdef(self, node): # pylint: disable=unused-argument
+ """check module name, docstring and redefinition
+ increment branch counter
+ """
+ self.stats["class"] += 1
+
+ @utils.check_messages(
+ "pointless-statement", "pointless-string-statement", "expression-not-assigned"
+ )
+ def visit_expr(self, node):
+ """Check for various kind of statements without effect"""
+ expr = node.value
+ if isinstance(expr, astroid.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, (astroid.ClassDef, astroid.Module, astroid.FunctionDef)
+ ):
+ if isinstance(scope, astroid.FunctionDef) and scope.name != "__init__":
+ pass
+ else:
+ sibling = expr.previous_sibling()
+ if (
+ sibling is not None
+ and sibling.scope() is scope
+ and isinstance(sibling, (astroid.Assign, astroid.AnnAssign))
+ ):
+ return
+ self.add_message("pointless-string-statement", node=node)
+ return
+
+ # Ignore if this is :
+ # * a direct function call
+ # * 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, (astroid.Yield, astroid.Await, astroid.Ellipsis, astroid.Call)
+ )
+ or (
+ isinstance(node.parent, astroid.TryExcept)
+ and node.parent.body == [node]
+ )
+ or (isinstance(expr, astroid.Const) and expr.value is Ellipsis)
+ ):
+ return
+ if any(expr.nodes_of_class(astroid.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, call_args):
+ # Return the arguments for the given call which are
+ # not passed as vararg.
+ for arg in call_args:
+ if isinstance(arg, astroid.Starred):
+ if (
+ isinstance(arg.value, astroid.Name)
+ and arg.value.name != node.args.vararg
+ ):
+ yield arg
+ else:
+ yield arg
+
+ @staticmethod
+ def _has_variadic_argument(args, variadic_name):
+ if not args:
+ return True
+ for arg in args:
+ if isinstance(arg.value, astroid.Name):
+ if arg.value.name != variadic_name:
+ return True
+ else:
+ return True
+ return False
+
+ @utils.check_messages("unnecessary-lambda")
+ def visit_lambda(self, node):
+ """check whether or not 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, astroid.Call):
+ # The body of the lambda must be a function call expression
+ # for the lambda to be unnecessary.
+ return
+ if isinstance(node.body.func, astroid.Attribute) and isinstance(
+ node.body.func.expr, astroid.Call
+ ):
+ # Chained call, the intermediate call might
+ # return something else (but we don't check that, yet).
+ return
+
+ call_site = CallSite.from_call(call)
+ ordinary_args = list(node.args.args)
+ new_call_args = list(self._filter_vararg(node, call.args))
+ if node.args.kwarg:
+ if self._has_variadic_argument(call.kwargs, node.args.kwarg):
+ return
+
+ if node.args.vararg:
+ if self._has_variadic_argument(call.starargs, node.args.vararg):
+ return
+ elif call.starargs:
+ return
+
+ if call.keywords:
+ # Look for additional keyword arguments that are not part
+ # of the lambda's signature
+ lambda_kwargs = {keyword.name for keyword in node.args.defaults}
+ if len(lambda_kwargs) != len(call_site.keyword_arguments):
+ # Different lengths, so probably not identical
+ return
+ if set(call_site.keyword_arguments).difference(lambda_kwargs):
+ return
+
+ # The "ordinary" arguments must be in a correspondence such that:
+ # ordinary_args[i].name == call.args[i].name.
+ if len(ordinary_args) != len(new_call_args):
+ return
+ for arg, passed_arg in zip(ordinary_args, new_call_args):
+ if not isinstance(passed_arg, astroid.Name):
+ return
+ if arg.name != passed_arg.name:
+ return
+
+ self.add_message("unnecessary-lambda", line=node.fromlineno, node=node)
+
+ @utils.check_messages("dangerous-default-value")
+ def visit_functiondef(self, node):
+ """check function name, docstring, arguments, redefinition,
+ variable names, max locals
+ """
+ self.stats["method" if node.is_method() else "function"] += 1
+ self._check_dangerous_default(node)
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ def _check_dangerous_default(self, node):
+ # check for dangerous default values as arguments
+ is_iterable = lambda n: isinstance(n, (astroid.List, astroid.Set, astroid.Dict))
+ for default in node.args.defaults:
+ 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, astroid.Call):
+ msg = "%s() (%s)" % (value.name, value.qname())
+ else:
+ msg = "%s (%s)" % (default.as_string(), value.qname())
+ else:
+ # this argument is a name
+ msg = "%s (%s)" % (
+ default.as_string(),
+ DEFAULT_ARGUMENT_SYMBOLS[value.qname()],
+ )
+ self.add_message("dangerous-default-value", node=node, args=(msg,))
+
+ @utils.check_messages("unreachable", "lost-exception")
+ def visit_return(self, node):
+ """1 - check is the node has a right sibling (if so, that's some
+ unreachable code)
+ 2 - check is 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 bloc ?
+ self._check_not_in_finally(node, "return", (astroid.FunctionDef,))
+
+ @utils.check_messages("unreachable")
+ def visit_continue(self, node):
+ """check is the node has a right sibling (if so, that's some unreachable
+ code)
+ """
+ self._check_unreachable(node)
+
+ @utils.check_messages("unreachable", "lost-exception")
+ def visit_break(self, node):
+ """1 - check is the node has a right sibling (if so, that's some
+ unreachable code)
+ 2 - check is 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 bloc ?
+ self._check_not_in_finally(node, "break", (astroid.For, astroid.While))
+
+ @utils.check_messages("unreachable")
+ def visit_raise(self, node):
+ """check if the node has a right sibling (if so, that's some unreachable
+ code)
+ """
+ self._check_unreachable(node)
+
+ @utils.check_messages("exec-used")
+ def visit_exec(self, node):
+ """just print a warning on exec statements"""
+ self.add_message("exec-used", node=node)
+
+ def _check_misplaced_format_function(self, call_node):
+ if not isinstance(call_node.func, astroid.Attribute):
+ return
+ if call_node.func.attrname != "format":
+ return
+
+ expr = utils.safe_infer(call_node.func.expr)
+ if expr is astroid.Uninferable:
+ return
+ if not expr:
+ # we are doubtful on inferred type of node, so here just check if format
+ # was called on print()
+ call_expr = call_node.func.expr
+ if not isinstance(call_expr, astroid.Call):
+ return
+ if (
+ isinstance(call_expr.func, astroid.Name)
+ and call_expr.func.name == "print"
+ ):
+ self.add_message("misplaced-format-function", node=call_node)
+
+ @utils.check_messages(
+ "eval-used", "exec-used", "bad-reversed-sequence", "misplaced-format-function"
+ )
+ def visit_call(self, node):
+ """visit a Call node -> check if this is not a blacklisted builtin
+ call and check for * or ** use
+ """
+ self._check_misplaced_format_function(node)
+ if isinstance(node.func, astroid.Name):
+ name = node.func.name
+ # ignore the name if it's not a builtin (i.e. not defined in the
+ # locals nor globals scope)
+ if not (name in node.frame() or name in node.root()):
+ if name == "exec":
+ self.add_message("exec-used", node=node)
+ elif name == "reversed":
+ self._check_reversed(node)
+ elif name == "eval":
+ self.add_message("eval-used", node=node)
+
+ @utils.check_messages("assert-on-tuple")
+ def visit_assert(self, node):
+ """check the use of an assert statement on a tuple."""
+ if (
+ node.fail is None
+ and isinstance(node.test, astroid.Tuple)
+ and len(node.test.elts) == 2
+ ):
+ self.add_message("assert-on-tuple", node=node)
+
+ @utils.check_messages("duplicate-key")
+ def visit_dict(self, node):
+ """check duplicate key in dictionary"""
+ keys = set()
+ for k, _ in node.items:
+ if isinstance(k, astroid.Const):
+ key = k.value
+ if key in keys:
+ self.add_message("duplicate-key", node=node, args=key)
+ keys.add(key)
+
+ def visit_tryfinally(self, node):
+ """update try...finally flag"""
+ self._tryfinallys.append(node)
+
+ def leave_tryfinally(self, node): # pylint: disable=unused-argument
+ """update try...finally flag"""
+ self._tryfinallys.pop()
+
+ def _check_unreachable(self, node):
+ """check unreachable code"""
+ unreach_stmt = node.next_sibling()
+ if unreach_stmt is not None:
+ self.add_message("unreachable", node=unreach_stmt)
+
+ def _check_not_in_finally(self, node, node_name, breaker_classes=()):
+ """check that a node is not inside a finally clause of a
+ try...finally statement.
+ If we found before a try...finally bloc a parent which its type is
+ in breaker_classes, we skip the whole check."""
+ # if self._tryfinallys is empty, we're not an in try...finally block
+ if not self._tryfinallys:
+ return
+ # the node could be a grand-grand...-children 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):
+ """ check that the argument to `reversed` is a sequence """
+ try:
+ argument = utils.safe_infer(utils.get_argument_from_call(node, position=0))
+ except utils.NoSuchArgumentError:
+ pass
+ else:
+ if argument is astroid.Uninferable:
+ return
+ if argument is None:
+ # Nothing was inferred.
+ # Try to see if we have iter().
+ if isinstance(node.args[0], astroid.Call):
+ try:
+ func = next(node.args[0].func.infer())
+ except astroid.InferenceError:
+ return
+ if getattr(
+ func, "name", None
+ ) == "iter" and utils.is_builtin_object(func):
+ self.add_message("bad-reversed-sequence", node=node)
+ return
+
+ if isinstance(argument, (astroid.List, astroid.Tuple)):
+ return
+
+ if isinstance(argument, astroid.Instance):
+ if argument._proxied.name == "dict" and utils.is_builtin_object(
+ argument._proxied
+ ):
+ self.add_message("bad-reversed-sequence", node=node)
+ return
+ if any(
+ ancestor.name == "dict" and utils.is_builtin_object(ancestor)
+ for ancestor in argument._proxied.ancestors()
+ ):
+ # Mappings aren't accepted by reversed(), unless
+ # they provide explicitly a __reversed__ method.
+ 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.check_messages("confusing-with-statement")
+ def visit_with(self, node):
+ # a "with" statement with multiple managers coresponds
+ # to one AST "With" node with multiple items
+ pairs = node.items
+ if pairs:
+ for prev_pair, pair in zip(pairs, pairs[1:]):
+ if isinstance(prev_pair[1], astroid.AssignName) and (
+ pair[1] is None and not isinstance(pair[0], astroid.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):
+ # Detect assigning to the same variable.
+
+ scope = node.scope()
+ scope_locals = scope.locals
+
+ rhs_names = []
+ targets = node.targets
+ if isinstance(targets[0], astroid.Tuple):
+ if len(targets) != 1:
+ # A complex assignment, so bail out early.
+ return
+ targets = targets[0].elts
+
+ if isinstance(node.value, astroid.Name):
+ if len(targets) != 1:
+ return
+ rhs_names = [node.value]
+ elif isinstance(node.value, astroid.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, astroid.Name):
+ continue
+ if not isinstance(target, astroid.AssignName):
+ continue
+ if isinstance(scope, astroid.ClassDef) and target.name in scope_locals:
+ # Check that the scope is different than a class level, which is usually
+ # a pattern to expose module level attributes as class level ones.
+ 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):
+ for target in targets:
+ if not isinstance(target, astroid.Tuple):
+ continue
+
+ found_names = []
+ for element in target.elts:
+ if isinstance(element, astroid.Tuple):
+ self._check_redeclared_assign_name([element])
+ elif isinstance(element, astroid.AssignName) and element.name != "_":
+ 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.check_messages("self-assigning-variable", "redeclared-assigned-name")
+ def visit_assign(self, node):
+ self._check_self_assigning_variable(node)
+ self._check_redeclared_assign_name(node.targets)
+
+ @utils.check_messages("redeclared-assigned-name")
+ def visit_for(self, node):
+ self._check_redeclared_assign_name([node.target])
+
+
+KNOWN_NAME_TYPES = {
+ "module",
+ "const",
+ "class",
+ "function",
+ "method",
+ "attr",
+ "argument",
+ "variable",
+ "class_attribute",
+ "inlinevar",
+}
+
+
+HUMAN_READABLE_TYPES = {
+ "module": "module",
+ "const": "constant",
+ "class": "class",
+ "function": "function",
+ "method": "method",
+ "attr": "attribute",
+ "argument": "argument",
+ "variable": "variable",
+ "class_attribute": "class attribute",
+ "inlinevar": "inline iteration",
+}
+
+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",
+ "inlinevar": "any",
+}
+
+
+def _create_naming_options():
+ name_options = []
+ for name_type in sorted(KNOWN_NAME_TYPES):
+ human_readable_name = HUMAN_READABLE_TYPES[name_type]
+ default_style = DEFAULT_NAMING_STYLES[name_type]
+ name_type = name_type.replace("_", "-")
+ name_options.append(
+ (
+ "%s-naming-style" % (name_type,),
+ {
+ "default": default_style,
+ "type": "choice",
+ "choices": list(NAMING_STYLES.keys()),
+ "metavar": "<style>",
+ "help": "Naming style matching correct %s names."
+ % (human_readable_name,),
+ },
+ )
+ )
+ name_options.append(
+ (
+ "%s-rgx" % (name_type,),
+ {
+ "default": None,
+ "type": "regexp",
+ "metavar": "<regexp>",
+ "help": "Regular expression matching correct %s names. Overrides %s-naming-style."
+ % (human_readable_name, name_type),
+ },
+ )
+ )
+ return tuple(name_options)
+
+
+class NameChecker(_BasicChecker):
+
+ msgs = {
+ "C0102": (
+ 'Black listed name "%s"',
+ "blacklisted-name",
+ "Used when the name is listed in the black list (unauthorized names).",
+ ),
+ "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...).",
+ ),
+ "W0111": (
+ "Name %s will become a keyword in Python %s",
+ "assign-to-new-keyword",
+ "Used when assignment will become invalid in future "
+ "Python release due to introducing new keyword.",
+ ),
+ }
+
+ options = (
+ (
+ "good-names",
+ {
+ "default": ("i", "j", "k", "ex", "Run", "_"),
+ "type": "csv",
+ "metavar": "<names>",
+ "help": "Good variable names which should always be accepted,"
+ " separated by a comma.",
+ },
+ ),
+ (
+ "bad-names",
+ {
+ "default": ("foo", "bar", "baz", "toto", "tutu", "tata"),
+ "type": "csv",
+ "metavar": "<names>",
+ "help": "Bad variable names which should always be refused, "
+ "separated by a comma.",
+ },
+ ),
+ (
+ "name-group",
+ {
+ "default": (),
+ "type": "csv",
+ "metavar": "<name1:name2>",
+ "help": (
+ "Colon-delimited sets of names that determine each"
+ " other's naming style when the name regexes"
+ " allow several styles."
+ ),
+ },
+ ),
+ (
+ "include-naming-hint",
+ {
+ "default": False,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": "Include a hint for the correct naming format with invalid-name.",
+ },
+ ),
+ (
+ "property-classes",
+ {
+ "default": ("abc.abstractproperty",),
+ "type": "csv",
+ "metavar": "<decorator names>",
+ "help": "List of decorators that produce properties, such as "
+ "abc.abstractproperty. Add to this list to register "
+ "other decorators that produce valid properties. "
+ "These decorators are taken in consideration only for invalid-name.",
+ },
+ ),
+ ) + _create_naming_options()
+
+ KEYWORD_ONSET = {(3, 7): {"async", "await"}}
+
+ def __init__(self, linter):
+ _BasicChecker.__init__(self, linter)
+ self._name_category = {}
+ self._name_group = {}
+ self._bad_names = {}
+ self._name_regexps = {}
+ self._name_hints = {}
+
+ def open(self):
+ self.stats = self.linter.add_stats(
+ badname_module=0,
+ badname_class=0,
+ badname_function=0,
+ badname_method=0,
+ badname_attr=0,
+ badname_const=0,
+ badname_variable=0,
+ badname_inlinevar=0,
+ badname_argument=0,
+ badname_class_attribute=0,
+ )
+ for group in self.config.name_group:
+ for name_type in group.split(":"):
+ self._name_group[name_type] = "group_%s" % (group,)
+
+ regexps, hints = self._create_naming_rules()
+ self._name_regexps = regexps
+ self._name_hints = hints
+
+ def _create_naming_rules(self):
+ regexps = {}
+ hints = {}
+
+ for name_type in KNOWN_NAME_TYPES:
+ naming_style_option_name = "%s_naming_style" % (name_type,)
+ naming_style_name = getattr(self.config, naming_style_option_name)
+
+ regexps[name_type] = NAMING_STYLES[naming_style_name].get_regex(name_type)
+
+ custom_regex_setting_name = "%s_rgx" % (name_type,)
+ custom_regex = getattr(self.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] = "%r pattern" % custom_regex.pattern
+ else:
+ hints[name_type] = "%s naming style" % naming_style_name
+
+ return regexps, hints
+
+ @utils.check_messages("blacklisted-name", "invalid-name")
+ def visit_module(self, node):
+ self._check_name("module", node.name.split(".")[-1], node)
+ self._bad_names = {}
+
+ def leave_module(self, node): # pylint: disable=unused-argument
+ for all_groups in self._bad_names.values():
+ if len(all_groups) < 2:
+ continue
+ groups = collections.defaultdict(list)
+ min_warnings = sys.maxsize
+ 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),
+ )
+ warnings = itertools.chain(*by_line[1:])
+ else:
+ warnings = groups[min_warnings][0]
+ for args in warnings:
+ self._raise_name_warning(*args)
+
+ @utils.check_messages("blacklisted-name", "invalid-name", "assign-to-new-keyword")
+ def visit_classdef(self, node):
+ self._check_assign_to_new_keyword_violation(node.name, node)
+ self._check_name("class", node.name, node)
+ for attr, anodes in node.instance_attrs.items():
+ if not any(node.instance_attr_ancestors(attr)):
+ self._check_name("attr", attr, anodes[0])
+
+ @utils.check_messages("blacklisted-name", "invalid-name", "assign-to-new-keyword")
+ def visit_functiondef(self, node):
+ # Do not emit any warnings if the method is just an implementation
+ # of a base class method.
+ self._check_assign_to_new_keyword_violation(node.name, node)
+ 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.config),
+ node.name,
+ node,
+ confidence,
+ )
+ # Check argument names
+ args = node.args.args
+ if args is not None:
+ self._recursive_check_names(args, node)
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ @utils.check_messages("blacklisted-name", "invalid-name")
+ def visit_global(self, node):
+ for name in node.names:
+ self._check_name("const", name, node)
+
+ @utils.check_messages("blacklisted-name", "invalid-name", "assign-to-new-keyword")
+ def visit_assignname(self, node):
+ """check module level assigned names"""
+ self._check_assign_to_new_keyword_violation(node.name, node)
+ frame = node.frame()
+ assign_type = node.assign_type()
+ if isinstance(assign_type, astroid.Comprehension):
+ self._check_name("inlinevar", node.name, node)
+ elif isinstance(frame, astroid.Module):
+ if isinstance(assign_type, astroid.Assign) and not in_loop(assign_type):
+ if isinstance(utils.safe_infer(assign_type.value), astroid.ClassDef):
+ self._check_name("class", node.name, node)
+ else:
+ if not _redefines_import(node):
+ # Don't emit if the name redefines an import
+ # in an ImportError except handler.
+ self._check_name("const", node.name, node)
+ elif isinstance(assign_type, astroid.ExceptHandler):
+ self._check_name("variable", node.name, node)
+ elif isinstance(frame, astroid.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):
+ self._check_name("variable", node.name, node)
+ elif isinstance(frame, astroid.ClassDef):
+ if not list(frame.local_attr_ancestors(node.name)):
+ self._check_name("class_attribute", node.name, node)
+
+ def _recursive_check_names(self, args, node):
+ """check names in a possibly recursive list <arg>"""
+ for arg in args:
+ if isinstance(arg, astroid.AssignName):
+ self._check_name("argument", arg.name, node)
+ else:
+ self._recursive_check_names(arg.elts, node)
+
+ def _find_name_group(self, node_type):
+ return self._name_group.get(node_type, node_type)
+
+ def _raise_name_warning(self, node, node_type, name, confidence):
+ type_label = HUMAN_READABLE_TYPES[node_type]
+ hint = self._name_hints[node_type]
+ if self.config.include_naming_hint:
+ hint += " (%r pattern)" % self._name_regexps[node_type].pattern
+ args = (type_label.capitalize(), name, hint)
+
+ self.add_message("invalid-name", node=node, args=args, confidence=confidence)
+ self.stats["badname_" + node_type] += 1
+
+ def _check_name(self, node_type, name, node, confidence=interfaces.HIGH):
+ """check for a name using the type's regexp"""
+
+ def _should_exempt_from_invalid_name(node):
+ if node_type == "variable":
+ inferred = utils.safe_infer(node)
+ if isinstance(inferred, astroid.ClassDef):
+ return True
+ return False
+
+ if utils.is_inside_except(node):
+ clobbering, _ = utils.clobber_in_except(node)
+ if clobbering:
+ return
+ if name in self.config.good_names:
+ return
+ if name in self.config.bad_names:
+ self.stats["badname_" + node_type] += 1
+ self.add_message("blacklisted-name", node=node, args=name)
+ 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, {})
+ warnings = bad_name_group.setdefault(match.lastgroup, [])
+ warnings.append((node, node_type, name, confidence))
+
+ if match is None and not _should_exempt_from_invalid_name(node):
+ self._raise_name_warning(node, node_type, name, confidence)
+
+ def _check_assign_to_new_keyword_violation(self, name, node):
+ keyword_first_version = self._name_became_keyword_in_version(
+ name, self.KEYWORD_ONSET
+ )
+ if keyword_first_version is not None:
+ self.add_message(
+ "assign-to-new-keyword",
+ node=node,
+ args=(name, keyword_first_version),
+ confidence=interfaces.HIGH,
+ )
+
+ @staticmethod
+ def _name_became_keyword_in_version(name, rules):
+ for version, keywords in rules.items():
+ if name in keywords and sys.version_info < version:
+ return ".".join(map(str, version))
+ 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": "<regexp>",
+ "help": "Regular expression which should only match "
+ "function or class names that do not require a "
+ "docstring.",
+ },
+ ),
+ (
+ "docstring-min-length",
+ {
+ "default": -1,
+ "type": "int",
+ "metavar": "<int>",
+ "help": (
+ "Minimum line length for functions/classes that"
+ " require docstrings, shorter ones are exempt."
+ ),
+ },
+ ),
+ )
+
+ def open(self):
+ self.stats = self.linter.add_stats(
+ undocumented_module=0,
+ undocumented_function=0,
+ undocumented_method=0,
+ undocumented_class=0,
+ )
+
+ @utils.check_messages("missing-docstring", "empty-docstring")
+ def visit_module(self, node):
+ self._check_docstring("module", node)
+
+ @utils.check_messages("missing-docstring", "empty-docstring")
+ def visit_classdef(self, node):
+ if self.config.no_docstring_rgx.match(node.name) is None:
+ self._check_docstring("class", node)
+
+ @utils.check_messages("missing-docstring", "empty-docstring")
+ def visit_functiondef(self, node):
+ if self.config.no_docstring_rgx.match(node.name) is None:
+ ftype = "method" if node.is_method() else "function"
+ if is_property_setter_or_deleter(node):
+ return
+
+ if isinstance(node.parent.frame(), astroid.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 node.name in ancestor and isinstance(
+ ancestor[node.name], astroid.FunctionDef
+ ):
+ overridden = True
+ break
+ self._check_docstring(
+ ftype, node, report_missing=not overridden, confidence=confidence
+ )
+ elif isinstance(node.parent.frame(), astroid.Module):
+ self._check_docstring(ftype, node)
+ else:
+ return
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ def _check_docstring(
+ self, node_type, node, report_missing=True, confidence=interfaces.HIGH
+ ):
+ """check the node has a non empty docstring"""
+ docstring = node.doc
+ 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 has no body, there's no reason
+ # to require a docstring.
+ return
+ max_lines = self.config.docstring_min_length
+
+ if node_type != "module" and max_lines > -1 and lines < max_lines:
+ return
+ self.stats["undocumented_" + node_type] += 1
+ if (
+ node.body
+ and isinstance(node.body[0], astroid.Expr)
+ and isinstance(node.body[0].value, astroid.Call)
+ ):
+ # Most likely a string with a format call. Let's see.
+ func = utils.safe_infer(node.body[0].value.func)
+ if isinstance(func, astroid.BoundMethod) and isinstance(
+ func.bound, astroid.Instance
+ ):
+ # Strings.
+ if func.bound.name == "str":
+ return
+ if func.bound.name in ("str", "unicode", "bytes"):
+ return
+ if node_type == "module":
+ message = "missing-module-docstring"
+ elif node_type == "class":
+ message = "missing-class-docstring"
+ else:
+ message = "missing-function-docstring"
+ self.add_message(message, node=node, confidence=confidence)
+ elif not docstring.strip():
+ self.stats["undocumented_" + node_type] += 1
+ self.add_message(
+ "empty-docstring", node=node, args=(node_type,), confidence=confidence
+ )
+
+
+class PassChecker(_BasicChecker):
+ """check if the pass statement is really necessary"""
+
+ msgs = {
+ "W0107": (
+ "Unnecessary pass statement",
+ "unnecessary-pass",
+ 'Used when a "pass" statement that can be avoided is encountered.',
+ )
+ }
+
+ @utils.check_messages("unnecessary-pass")
+ def visit_pass(self, node):
+ if len(node.parent.child_sequence(node)) > 1 or (
+ isinstance(node.parent, (astroid.ClassDef, astroid.FunctionDef))
+ and (node.parent.doc is not None)
+ ):
+ self.add_message("unnecessary-pass", node=node)
+
+
+def _is_one_arg_pos_call(call):
+ """Is this a call with exactly 1 argument,
+ where that argument is positional?
+ """
+ return isinstance(call, astroid.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 to %s should be %s",
+ "singleton-comparison",
+ "Used when an expression is compared to singleton "
+ "values like True, False or None.",
+ ),
+ "C0122": (
+ "Comparison should be %s",
+ "misplaced-comparison-constant",
+ "Used when the constant is placed on the left side "
+ "of a comparison. It is usually clearer in intent to "
+ "place it in the right hand side of the comparison.",
+ ),
+ "C0123": (
+ "Using type() instead of isinstance() 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": (
+ "Comparison to literal",
+ "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.",
+ ),
+ "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.",
+ ),
+ }
+
+ def _check_singleton_comparison(self, singleton, root_node, negative_check=False):
+ if singleton.value is True:
+ if not negative_check:
+ suggestion = "just 'expr'"
+ else:
+ suggestion = "just 'not expr'"
+ self.add_message(
+ "singleton-comparison", node=root_node, args=(True, suggestion)
+ )
+ elif singleton.value is False:
+ if not negative_check:
+ suggestion = "'not expr'"
+ else:
+ suggestion = "'expr'"
+ self.add_message(
+ "singleton-comparison", node=root_node, args=(False, suggestion)
+ )
+ elif singleton.value is None:
+ if not negative_check:
+ suggestion = "'expr is None'"
+ else:
+ suggestion = "'expr is not None'"
+ self.add_message(
+ "singleton-comparison", node=root_node, args=(None, suggestion)
+ )
+
+ def _check_literal_comparison(self, literal, node):
+ """Check if we compare to a literal, which is usually what we do not want to do."""
+ nodes = (astroid.List, astroid.Tuple, astroid.Dict, astroid.Set)
+ is_other_literal = isinstance(literal, nodes)
+ is_const = False
+ if isinstance(literal, astroid.Const):
+ if isinstance(literal.value, bool) or literal.value is None:
+ # Not interested in this values.
+ return
+ is_const = isinstance(literal.value, (bytes, str, int, float))
+
+ if is_const or is_other_literal:
+ self.add_message("literal-comparison", node=node)
+
+ def _check_misplaced_constant(self, node, left, right, operator):
+ if isinstance(right, astroid.Const):
+ return
+ operator = REVERSED_COMPS.get(operator, operator)
+ suggestion = "%s %s %r" % (right.as_string(), operator, left.value)
+ self.add_message("misplaced-comparison-constant", node=node, args=(suggestion,))
+
+ def _check_logical_tautology(self, node):
+ """Check if identifier is compared against itself.
+ :param node: Compare node
+ :type node: astroid.node_classes.Compare
+ :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, astroid.Const) and isinstance(
+ right_operand, astroid.Const
+ ):
+ left_operand = left_operand.value
+ right_operand = right_operand.value
+ elif isinstance(left_operand, astroid.Name) and isinstance(
+ right_operand, astroid.Name
+ ):
+ left_operand = left_operand.name
+ right_operand = right_operand.name
+
+ if left_operand == right_operand:
+ suggestion = "%s %s %s" % (left_operand, operator, right_operand)
+ self.add_message("comparison-with-itself", node=node, args=(suggestion,))
+
+ def _check_callable_comparison(self, node):
+ operator = node.ops[0][0]
+ if operator not in COMPARISON_OPERATORS:
+ return
+
+ bare_callables = (astroid.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.
+ if (
+ sum(
+ 1
+ for operand in (left_operand, right_operand)
+ if isinstance(utils.safe_infer(operand), bare_callables)
+ )
+ == 1
+ ):
+ self.add_message("comparison-with-callable", node=node)
+
+ @utils.check_messages(
+ "singleton-comparison",
+ "misplaced-comparison-constant",
+ "unidiomatic-typecheck",
+ "literal-comparison",
+ "comparison-with-itself",
+ "comparison-with-callable",
+ )
+ def visit_compare(self, node):
+ self._check_callable_comparison(node)
+ self._check_logical_tautology(node)
+ self._check_unidiomatic_typecheck(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 COMPARISON_OPERATORS and isinstance(left, astroid.Const):
+ self._check_misplaced_constant(node, left, right, operator)
+
+ if operator == "==":
+ if isinstance(left, astroid.Const):
+ self._check_singleton_comparison(left, node)
+ elif isinstance(right, astroid.Const):
+ self._check_singleton_comparison(right, node)
+ if operator == "!=":
+ if isinstance(right, astroid.Const):
+ self._check_singleton_comparison(right, node, negative_check=True)
+ if operator in ("is", "is not"):
+ self._check_literal_comparison(right, node)
+
+ def _check_unidiomatic_typecheck(self, node):
+ operator, right = node.ops[0]
+ if operator in TYPECHECK_COMPARISON_OPERATORS:
+ left = node.left
+ if _is_one_arg_pos_call(left):
+ self._check_type_x_is_y(node, left, operator, right)
+
+ def _check_type_x_is_y(self, node, left, operator, right):
+ """Check for expressions like type(x) == Y."""
+ left_func = utils.safe_infer(left.func)
+ if not (
+ isinstance(left_func, astroid.ClassDef) and left_func.qname() == TYPE_QNAME
+ ):
+ return
+
+ if operator in ("is", "is not") and _is_one_arg_pos_call(right):
+ right_func = utils.safe_infer(right.func)
+ if (
+ isinstance(right_func, astroid.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)
+
+
+def register(linter):
+ """required method to auto register this checker"""
+ 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))
diff --git a/venv/Lib/site-packages/pylint/checkers/base_checker.py b/venv/Lib/site-packages/pylint/checkers/base_checker.py
new file mode 100644
index 0000000..f2ae4e5
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/base_checker.py
@@ -0,0 +1,187 @@
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2013 buck@yelp.com <buck@yelp.com>
+# Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+from inspect import cleandoc
+from typing import Any
+
+from pylint.config import OptionsProviderMixIn
+from pylint.constants import _MSG_ORDER, WarningScope
+from pylint.exceptions import InvalidMessageError
+from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements
+from pylint.message.message_definition import MessageDefinition
+from pylint.utils import get_rst_section, get_rst_title
+
+
+class BaseChecker(OptionsProviderMixIn):
+
+ # checker name (you may reuse an existing one)
+ name = None # type: str
+ # options level (0 will be displaying in --help, 1 in --long-help)
+ level = 1
+ # ordered list of options to control the checker behaviour
+ options = () # type: Any
+ # messages issued by this checker
+ msgs = {} # type: Any
+ # reports issued by this checker
+ reports = () # type: Any
+ # mark this checker as enabled or not.
+ enabled = True
+
+ def __init__(self, linter=None):
+ """checker instances should have the linter as argument
+
+ :param ILinter linter: is an object implementing ILinter."""
+ if self.name is not None:
+ self.name = self.name.lower()
+ OptionsProviderMixIn.__init__(self)
+ self.linter = linter
+
+ def __gt__(self, other):
+ """Permit to sort a list of Checker by name."""
+ return "{}{}".format(self.name, self.msgs).__gt__(
+ "{}{}".format(other.name, other.msgs)
+ )
+
+ def __repr__(self):
+ status = "Checker" if self.enabled else "Disabled checker"
+ return "{} '{}' (responsible for '{}')".format(
+ status, self.name, "', '".join(self.msgs.keys())
+ )
+
+ def __str__(self):
+ """This might be incomplete because multiple class inheriting BaseChecker
+ can have the same name. Cf MessageHandlerMixIn.get_full_documentation()"""
+ return self.get_full_documentation(
+ msgs=self.msgs, options=self.options_and_values(), reports=self.reports
+ )
+
+ def get_full_documentation(self, msgs, options, reports, doc=None, module=None):
+ result = ""
+ checker_title = "%s checker" % (self.name.replace("_", " ").title())
+ if module:
+ # Provide anchor to link against
+ result += ".. _%s:\n\n" % module
+ result += "%s\n" % get_rst_title(checker_title, "~")
+ if module:
+ result += "This checker is provided by ``%s``.\n" % module
+ result += "Verbatim name of the checker is ``%s``.\n\n" % self.name
+ if doc:
+ # Provide anchor to link against
+ result += get_rst_title("{} Documentation".format(checker_title), "^")
+ result += "%s\n\n" % cleandoc(doc)
+ # options might be an empty generator and not be False when casted to boolean
+ options = list(options)
+ if options:
+ result += get_rst_title("{} Options".format(checker_title), "^")
+ result += "%s\n" % get_rst_section(None, options)
+ if msgs:
+ result += get_rst_title("{} Messages".format(checker_title), "^")
+ for msgid, msg in sorted(
+ msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1])
+ ):
+ msg = self.create_message_definition_from_tuple(msgid, msg)
+ result += "%s\n" % msg.format_help(checkerref=False)
+ result += "\n"
+ if reports:
+ result += get_rst_title("{} Reports".format(checker_title), "^")
+ for report in reports:
+ result += ":%s: %s\n" % report[:2]
+ result += "\n"
+ result += "\n"
+ return result
+
+ def add_message(
+ self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None
+ ):
+ if not confidence:
+ confidence = UNDEFINED
+ self.linter.add_message(msgid, line, node, args, confidence, col_offset)
+
+ def check_consistency(self):
+ """Check the consistency of msgid.
+
+ msg ids for a checker should be a string of len 4, where the two first
+ characters are the checker id and the two last the msg id in this
+ checker.
+
+ :raises InvalidMessageError: If the checker id in the messages are not
+ always the same. """
+ checker_id = None
+ existing_ids = []
+ for message in self.messages:
+ if checker_id is not None and checker_id != message.msgid[1:3]:
+ error_msg = "Inconsistent checker part in message id "
+ error_msg += "'{}' (expected 'x{checker_id}xx' ".format(
+ message.msgid, checker_id=checker_id
+ )
+ error_msg += "because we already had {existing_ids}).".format(
+ existing_ids=existing_ids
+ )
+ raise InvalidMessageError(error_msg)
+ checker_id = message.msgid[1:3]
+ existing_ids.append(message.msgid)
+
+ def create_message_definition_from_tuple(self, msgid, msg_tuple):
+ if implements(self, (IRawChecker, ITokenChecker)):
+ default_scope = WarningScope.LINE
+ else:
+ default_scope = WarningScope.NODE
+ options = {}
+ if len(msg_tuple) > 3:
+ (msg, symbol, descr, options) = msg_tuple
+ elif len(msg_tuple) > 2:
+ (msg, symbol, descr) = msg_tuple
+ else:
+ error_msg = """Messages should have a msgid and a symbol. Something like this :
+
+"W1234": (
+ "message",
+ "message-symbol",
+ "Message description with detail.",
+ ...
+),
+"""
+ raise InvalidMessageError(error_msg)
+ options.setdefault("scope", default_scope)
+ return MessageDefinition(self, msgid, msg, descr, symbol, **options)
+
+ @property
+ def messages(self) -> list:
+ return [
+ self.create_message_definition_from_tuple(msgid, msg_tuple)
+ for msgid, msg_tuple in sorted(self.msgs.items())
+ ]
+
+ # dummy methods implementing the IChecker interface
+
+ def get_message_definition(self, msgid):
+ for message_definition in self.messages:
+ if message_definition.msgid == msgid:
+ return message_definition
+ error_msg = "MessageDefinition for '{}' does not exists. ".format(msgid)
+ error_msg += "Choose from {}.".format([m.msgid for m in self.messages])
+ raise InvalidMessageError(error_msg)
+
+ def open(self):
+ """called before visiting project (i.e set of modules)"""
+
+ def close(self):
+ """called after visiting project (i.e set of modules)"""
+
+
+class BaseTokenChecker(BaseChecker):
+ """Base class for checkers that want to have access to the token stream."""
+
+ def process_tokens(self, tokens):
+ """Should be overridden by subclasses."""
+ raise NotImplementedError()
diff --git a/venv/Lib/site-packages/pylint/checkers/classes.py b/venv/Lib/site-packages/pylint/checkers/classes.py
new file mode 100644
index 0000000..9f5d099
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/classes.py
@@ -0,0 +1,1844 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2016 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2010 Maarten ter Huurne <maarten@treewalker.org>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2014 David Pursehouse <david.pursehouse@gmail.com>
+# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
+# Copyright (c) 2016 Anthony Foglia <afoglia@users.noreply.github.com>
+# Copyright (c) 2016 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2018 Ben Green <benhgreen@icloud.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""classes checker for Python code
+"""
+import collections
+from itertools import chain, zip_longest
+
+import astroid
+from astroid import decorators, objects
+from astroid.bases import BUILTINS, Generator
+from astroid.exceptions import DuplicateBasesError, InconsistentMroError
+from astroid.scoped_nodes import function_to_method
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import (
+ PYMETHODS,
+ SPECIAL_METHODS_PARAMS,
+ check_messages,
+ class_is_abstract,
+ decorated_with,
+ decorated_with_property,
+ has_known_bases,
+ is_attr_private,
+ is_attr_protected,
+ is_builtin_object,
+ is_comprehension,
+ is_iterable,
+ is_property_setter,
+ is_property_setter_or_deleter,
+ is_protocol_class,
+ node_frame_class,
+ overrides_a_method,
+ safe_infer,
+ unimplemented_abstract_methods,
+)
+from pylint.interfaces import IAstroidChecker
+from pylint.utils import get_global_option
+
+NEXT_METHOD = "__next__"
+INVALID_BASE_CLASSES = {"bool", "range", "slice", "memoryview"}
+BUILTIN_DECORATORS = {"builtins.property", "builtins.classmethod"}
+
+# Dealing with useless override detection, with regard
+# to parameters vs arguments
+
+_CallSignature = collections.namedtuple(
+ "_CallSignature", "args kws starred_args starred_kws"
+)
+_ParameterSignature = collections.namedtuple(
+ "_ParameterSignature", "args kwonlyargs varargs kwargs"
+)
+
+
+def _signature_from_call(call):
+ kws = {}
+ args = []
+ starred_kws = []
+ starred_args = []
+ for keyword in call.keywords or []:
+ arg, value = keyword.arg, keyword.value
+ if arg is None and isinstance(value, astroid.Name):
+ # Starred node and we are interested only in names,
+ # otherwise some transformation might occur for the parameter.
+ starred_kws.append(value.name)
+ elif isinstance(value, astroid.Name):
+ kws[arg] = value.name
+ else:
+ kws[arg] = None
+
+ for arg in call.args:
+ if isinstance(arg, astroid.Starred) and isinstance(arg.value, astroid.Name):
+ # Positional variadic and a name, otherwise some transformation
+ # might have occurred.
+ starred_args.append(arg.value.name)
+ elif isinstance(arg, astroid.Name):
+ args.append(arg.name)
+ else:
+ args.append(None)
+
+ return _CallSignature(args, kws, starred_args, starred_kws)
+
+
+def _signature_from_arguments(arguments):
+ kwarg = arguments.kwarg
+ vararg = arguments.vararg
+ args = [arg.name for arg in arguments.args if arg.name != "self"]
+ kwonlyargs = [arg.name for arg in arguments.kwonlyargs]
+ return _ParameterSignature(args, kwonlyargs, vararg, kwarg)
+
+
+def _definition_equivalent_to_call(definition, call):
+ """Check if a definition signature is equivalent to a call."""
+ if definition.kwargs:
+ same_kw_variadics = definition.kwargs in call.starred_kws
+ else:
+ same_kw_variadics = not call.starred_kws
+ if definition.varargs:
+ same_args_variadics = definition.varargs in call.starred_args
+ else:
+ same_args_variadics = not call.starred_args
+ same_kwonlyargs = all(kw in call.kws for kw in definition.kwonlyargs)
+ same_args = definition.args == call.args
+
+ no_additional_kwarg_arguments = True
+ if call.kws:
+ for keyword in call.kws:
+ is_arg = keyword in call.args
+ is_kwonly = keyword in definition.kwonlyargs
+ if not is_arg and not is_kwonly:
+ # Maybe this argument goes into **kwargs,
+ # or it is an extraneous argument.
+ # In any case, the signature is different than
+ # the call site, which stops our search.
+ no_additional_kwarg_arguments = False
+ break
+
+ return all(
+ (
+ same_args,
+ same_kwonlyargs,
+ same_args_variadics,
+ same_kw_variadics,
+ no_additional_kwarg_arguments,
+ )
+ )
+
+
+# Deal with parameters overridding in two methods.
+
+
+def _positional_parameters(method):
+ positional = method.args.args
+ if method.type in ("classmethod", "method"):
+ positional = positional[1:]
+ return positional
+
+
+def _get_node_type(node, potential_types):
+ """
+ Return the type of the node if it exists in potential_types.
+
+ Args:
+ node (astroid.node): node to get the type of.
+ potential_types (tuple): potential types of the node.
+
+ Returns:
+ type: type of the node or None.
+ """
+ for potential_type in potential_types:
+ if isinstance(node, potential_type):
+ return potential_type
+ return None
+
+
+def _check_arg_equality(node_a, node_b, attr_name):
+ """
+ Check equality of nodes based on the comparison of their attributes named attr_name.
+
+ Args:
+ node_a (astroid.node): first node to compare.
+ node_b (astroid.node): second node to compare.
+ attr_name (str): name of the nodes attribute to use for comparison.
+
+ Returns:
+ bool: True if node_a.attr_name == node_b.attr_name, False otherwise.
+ """
+ return getattr(node_a, attr_name) == getattr(node_b, attr_name)
+
+
+def _has_different_parameters_default_value(original, overridden):
+ """
+ Check if original and overridden methods arguments have different default values
+
+ Return True if one of the overridden arguments has a default
+ value different from the default value of the original argument
+ If one of the method doesn't have argument (.args is None)
+ return False
+ """
+ if original.args is None or overridden.args is None:
+ return False
+
+ all_args = chain(original.args, original.kwonlyargs)
+ original_param_names = [param.name for param in all_args]
+ default_missing = object()
+ for param_name in original_param_names:
+ try:
+ original_default = original.default_value(param_name)
+ except astroid.exceptions.NoDefault:
+ original_default = default_missing
+ try:
+ overridden_default = overridden.default_value(param_name)
+ except astroid.exceptions.NoDefault:
+ overridden_default = default_missing
+
+ default_list = [
+ arg == default_missing for arg in (original_default, overridden_default)
+ ]
+ if any(default_list) and not all(default_list):
+ # Only one arg has no default value
+ return True
+
+ astroid_type_compared_attr = {
+ astroid.Const: "value",
+ astroid.ClassDef: "name",
+ astroid.Tuple: "elts",
+ astroid.List: "elts",
+ }
+ handled_types = tuple(
+ astroid_type for astroid_type in astroid_type_compared_attr
+ )
+ original_type = _get_node_type(original_default, handled_types)
+ if original_type:
+ #  We handle only astroid types that are inside the dict astroid_type_compared_attr
+ if not isinstance(overridden_default, original_type):
+ #  Two args with same name but different types
+ return True
+ if not _check_arg_equality(
+ original_default,
+ overridden_default,
+ astroid_type_compared_attr[original_type],
+ ):
+ # Two args with same type but different values
+ return True
+ return False
+
+
+def _has_different_parameters(original, overridden, dummy_parameter_regex):
+ zipped = zip_longest(original, overridden)
+ for original_param, overridden_param in zipped:
+ params = (original_param, overridden_param)
+ if not all(params):
+ return True
+
+ names = [param.name for param in params]
+ if any(map(dummy_parameter_regex.match, names)):
+ continue
+ if original_param.name != overridden_param.name:
+ return True
+ return False
+
+
+def _different_parameters(original, overridden, dummy_parameter_regex):
+ """Determine if the two methods have different parameters
+
+ They are considered to have different parameters if:
+
+ * they have different positional parameters, including different names
+
+ * one of the methods is having variadics, while the other is not
+
+ * they have different keyword only parameters.
+
+ """
+ original_parameters = _positional_parameters(original)
+ overridden_parameters = _positional_parameters(overridden)
+
+ different_positional = _has_different_parameters(
+ original_parameters, overridden_parameters, dummy_parameter_regex
+ )
+ different_kwonly = _has_different_parameters(
+ original.args.kwonlyargs, overridden.args.kwonlyargs, dummy_parameter_regex
+ )
+ if original.name in PYMETHODS:
+ # Ignore the difference for special methods. If the parameter
+ # numbers are different, then that is going to be caught by
+ # unexpected-special-method-signature.
+ # If the names are different, it doesn't matter, since they can't
+ # be used as keyword arguments anyway.
+ different_positional = different_kwonly = False
+
+ # Both or none should have extra variadics, otherwise the method
+ # loses or gains capabilities that are not reflected into the parent method,
+ # leading to potential inconsistencies in the code.
+ different_kwarg = (
+ sum(1 for param in (original.args.kwarg, overridden.args.kwarg) if not param)
+ == 1
+ )
+ different_vararg = (
+ sum(1 for param in (original.args.vararg, overridden.args.vararg) if not param)
+ == 1
+ )
+
+ return any(
+ (different_positional, different_kwarg, different_vararg, different_kwonly)
+ )
+
+
+def _is_invalid_base_class(cls):
+ return cls.name in INVALID_BASE_CLASSES and is_builtin_object(cls)
+
+
+def _has_data_descriptor(cls, attr):
+ attributes = cls.getattr(attr)
+ for attribute in attributes:
+ try:
+ for inferred in attribute.infer():
+ if isinstance(inferred, astroid.Instance):
+ try:
+ inferred.getattr("__get__")
+ inferred.getattr("__set__")
+ except astroid.NotFoundError:
+ continue
+ else:
+ return True
+ except astroid.InferenceError:
+ # Can't infer, avoid emitting a false positive in this case.
+ return True
+ return False
+
+
+def _called_in_methods(func, klass, methods):
+ """ Check if the func was called in any of the given methods,
+ belonging to the *klass*. Returns True if so, False otherwise.
+ """
+ if not isinstance(func, astroid.FunctionDef):
+ return False
+ for method in methods:
+ try:
+ inferred = klass.getattr(method)
+ except astroid.NotFoundError:
+ continue
+ for infer_method in inferred:
+ for call in infer_method.nodes_of_class(astroid.Call):
+ try:
+ bound = next(call.func.infer())
+ except (astroid.InferenceError, StopIteration):
+ continue
+ if not isinstance(bound, astroid.BoundMethod):
+ continue
+ func_obj = bound._proxied
+ if isinstance(func_obj, astroid.UnboundMethod):
+ func_obj = func_obj._proxied
+ if func_obj.name == func.name:
+ return True
+ return False
+
+
+def _is_attribute_property(name, klass):
+ """ Check if the given attribute *name* is a property
+ in the given *klass*.
+
+ It will look for `property` calls or for functions
+ with the given name, decorated by `property` or `property`
+ subclasses.
+ Returns ``True`` if the name is a property in the given klass,
+ ``False`` otherwise.
+ """
+
+ try:
+ attributes = klass.getattr(name)
+ except astroid.NotFoundError:
+ return False
+ property_name = "{}.property".format(BUILTINS)
+ for attr in attributes:
+ if attr is astroid.Uninferable:
+ continue
+ try:
+ inferred = next(attr.infer())
+ except astroid.InferenceError:
+ continue
+ if isinstance(inferred, astroid.FunctionDef) and decorated_with_property(
+ inferred
+ ):
+ return True
+ if inferred.pytype() == property_name:
+ return True
+ return False
+
+
+def _has_bare_super_call(fundef_node):
+ for call in fundef_node.nodes_of_class(astroid.Call):
+ func = call.func
+ if isinstance(func, astroid.Name) and func.name == "super" and not call.args:
+ return True
+ return False
+
+
+def _safe_infer_call_result(node, caller, context=None):
+ """
+ Safely infer the return value of a function.
+
+ Returns None if inference failed or if there is some ambiguity (more than
+ one node has been inferred). Otherwise returns inferred value.
+ """
+ try:
+ inferit = node.infer_call_result(caller, context=context)
+ value = next(inferit)
+ except astroid.InferenceError:
+ return None # inference failed
+ except StopIteration:
+ return None # no values inferred
+ try:
+ next(inferit)
+ return None # there is ambiguity on the inferred node
+ except astroid.InferenceError:
+ return None # there is some kind of ambiguity
+ except StopIteration:
+ return value
+
+
+def _has_same_layout_slots(slots, assigned_value):
+ inferred = next(assigned_value.infer())
+ if isinstance(inferred, astroid.ClassDef):
+ other_slots = inferred.slots()
+ if all(
+ first_slot and second_slot and first_slot.value == second_slot.value
+ for (first_slot, second_slot) in zip_longest(slots, other_slots)
+ ):
+ return True
+ return False
+
+
+MSGS = {
+ "F0202": (
+ "Unable to check methods signature (%s / %s)",
+ "method-check-failed",
+ "Used when Pylint has been unable to check methods signature "
+ "compatibility for an unexpected reason. Please report this kind "
+ "if you don't make sense of it.",
+ ),
+ "E0202": (
+ "An attribute defined in %s line %s hides this method",
+ "method-hidden",
+ "Used when a class defines a method which is hidden by an "
+ "instance attribute from an ancestor class or set by some "
+ "client code.",
+ ),
+ "E0203": (
+ "Access to member %r before its definition line %s",
+ "access-member-before-definition",
+ "Used when an instance member is accessed before it's actually assigned.",
+ ),
+ "W0201": (
+ "Attribute %r defined outside __init__",
+ "attribute-defined-outside-init",
+ "Used when an instance attribute is defined outside the __init__ method.",
+ ),
+ "W0212": (
+ "Access to a protected member %s of a client class", # E0214
+ "protected-access",
+ "Used when a protected member (i.e. class member with a name "
+ "beginning with an underscore) is access outside the class or a "
+ "descendant of the class where it's defined.",
+ ),
+ "E0211": (
+ "Method has no argument",
+ "no-method-argument",
+ "Used when a method which should have the bound instance as "
+ "first argument has no argument defined.",
+ ),
+ "E0213": (
+ 'Method should have "self" as first argument',
+ "no-self-argument",
+ 'Used when a method has an attribute different the "self" as '
+ "first argument. This is considered as an error since this is "
+ "a so common convention that you shouldn't break it!",
+ ),
+ "C0202": (
+ "Class method %s should have %s as first argument",
+ "bad-classmethod-argument",
+ "Used when a class method has a first argument named differently "
+ "than the value specified in valid-classmethod-first-arg option "
+ '(default to "cls"), recommended to easily differentiate them '
+ "from regular instance methods.",
+ ),
+ "C0203": (
+ "Metaclass method %s should have %s as first argument",
+ "bad-mcs-method-argument",
+ "Used when a metaclass method has a first argument named "
+ "differently than the value specified in valid-classmethod-first"
+ '-arg option (default to "cls"), recommended to easily '
+ "differentiate them from regular instance methods.",
+ ),
+ "C0204": (
+ "Metaclass class method %s should have %s as first argument",
+ "bad-mcs-classmethod-argument",
+ "Used when a metaclass class method has a first argument named "
+ "differently than the value specified in valid-metaclass-"
+ 'classmethod-first-arg option (default to "mcs"), recommended to '
+ "easily differentiate them from regular instance methods.",
+ ),
+ "W0211": (
+ "Static method with %r as first argument",
+ "bad-staticmethod-argument",
+ 'Used when a static method has "self" or a value specified in '
+ "valid-classmethod-first-arg option or "
+ "valid-metaclass-classmethod-first-arg option as first argument.",
+ ),
+ "R0201": (
+ "Method could be a function",
+ "no-self-use",
+ "Used when a method doesn't use its bound instance, and so could "
+ "be written as a function.",
+ ),
+ "W0221": (
+ "Parameters differ from %s %r method",
+ "arguments-differ",
+ "Used when a method has a different number of arguments than in "
+ "the implemented interface or in an overridden method.",
+ ),
+ "W0222": (
+ "Signature differs from %s %r method",
+ "signature-differs",
+ "Used when a method signature is different than in the "
+ "implemented interface or in an overridden method.",
+ ),
+ "W0223": (
+ "Method %r is abstract in class %r but is not overridden",
+ "abstract-method",
+ "Used when an abstract method (i.e. raise NotImplementedError) is "
+ "not overridden in concrete class.",
+ ),
+ "W0231": (
+ "__init__ method from base class %r is not called",
+ "super-init-not-called",
+ "Used when an ancestor class method has an __init__ method "
+ "which is not called by a derived class.",
+ ),
+ "W0232": (
+ "Class has no __init__ method",
+ "no-init",
+ "Used when a class has no __init__ method, neither its parent classes.",
+ ),
+ "W0233": (
+ "__init__ method from a non direct base class %r is called",
+ "non-parent-init-called",
+ "Used when an __init__ method is called on a class which is not "
+ "in the direct ancestors for the analysed class.",
+ ),
+ "W0235": (
+ "Useless super delegation in method %r",
+ "useless-super-delegation",
+ "Used whenever we can detect that an overridden method is useless, "
+ "relying on super() delegation to do the same thing as another method "
+ "from the MRO.",
+ ),
+ "W0236": (
+ "Method %r was expected to be %r, found it instead as %r",
+ "invalid-overridden-method",
+ "Used when we detect that a method was overridden as a property "
+ "or the other way around, which could result in potential bugs at "
+ "runtime.",
+ ),
+ "E0236": (
+ "Invalid object %r in __slots__, must contain only non empty strings",
+ "invalid-slots-object",
+ "Used when an invalid (non-string) object occurs in __slots__.",
+ ),
+ "E0237": (
+ "Assigning to attribute %r not defined in class slots",
+ "assigning-non-slot",
+ "Used when assigning to an attribute not defined in the class slots.",
+ ),
+ "E0238": (
+ "Invalid __slots__ object",
+ "invalid-slots",
+ "Used when an invalid __slots__ is found in class. "
+ "Only a string, an iterable or a sequence is permitted.",
+ ),
+ "E0239": (
+ "Inheriting %r, which is not a class.",
+ "inherit-non-class",
+ "Used when a class inherits from something which is not a class.",
+ ),
+ "E0240": (
+ "Inconsistent method resolution order for class %r",
+ "inconsistent-mro",
+ "Used when a class has an inconsistent method resolution order.",
+ ),
+ "E0241": (
+ "Duplicate bases for class %r",
+ "duplicate-bases",
+ "Used when a class has duplicate bases.",
+ ),
+ "E0242": (
+ "Value %r in slots conflicts with class variable",
+ "class-variable-slots-conflict",
+ "Used when a value in __slots__ conflicts with a class variable, property or method.",
+ ),
+ "R0202": (
+ "Consider using a decorator instead of calling classmethod",
+ "no-classmethod-decorator",
+ "Used when a class method is defined without using the decorator syntax.",
+ ),
+ "R0203": (
+ "Consider using a decorator instead of calling staticmethod",
+ "no-staticmethod-decorator",
+ "Used when a static method is defined without using the decorator syntax.",
+ ),
+ "C0205": (
+ "Class __slots__ should be a non-string iterable",
+ "single-string-used-for-slots",
+ "Used when a class __slots__ is a simple string, rather than an iterable.",
+ ),
+ "R0205": (
+ "Class %r inherits from object, can be safely removed from bases in python3",
+ "useless-object-inheritance",
+ "Used when a class inherit from object, which under python3 is implicit, "
+ "hence can be safely removed from bases.",
+ ),
+ "R0206": (
+ "Cannot have defined parameters for properties",
+ "property-with-parameters",
+ "Used when we detect that a property also has parameters, which are useless, "
+ "given that properties cannot be called with additional arguments.",
+ ),
+}
+
+
+class ScopeAccessMap:
+ """Store the accessed variables per scope."""
+
+ def __init__(self):
+ self._scopes = collections.defaultdict(lambda: collections.defaultdict(list))
+
+ def set_accessed(self, node):
+ """Set the given node as accessed."""
+
+ frame = node_frame_class(node)
+ if frame is None:
+ # The node does not live in a class.
+ return
+ self._scopes[frame][node.attrname].append(node)
+
+ def accessed(self, scope):
+ """Get the accessed variables for the given scope."""
+ return self._scopes.get(scope, {})
+
+
+class ClassChecker(BaseChecker):
+ """checks for :
+ * methods without self as first argument
+ * overridden methods signature
+ * access only to existent members via self
+ * attributes not defined in the __init__ method
+ * unreachable code
+ """
+
+ __implements__ = (IAstroidChecker,)
+
+ # configuration section name
+ name = "classes"
+ # messages
+ msgs = MSGS
+ priority = -2
+ # configuration options
+ options = (
+ (
+ "defining-attr-methods",
+ {
+ "default": ("__init__", "__new__", "setUp", "__post_init__"),
+ "type": "csv",
+ "metavar": "<method names>",
+ "help": "List of method names used to declare (i.e. assign) \
+instance attributes.",
+ },
+ ),
+ (
+ "valid-classmethod-first-arg",
+ {
+ "default": ("cls",),
+ "type": "csv",
+ "metavar": "<argument names>",
+ "help": "List of valid names for the first argument in \
+a class method.",
+ },
+ ),
+ (
+ "valid-metaclass-classmethod-first-arg",
+ {
+ "default": ("cls",),
+ "type": "csv",
+ "metavar": "<argument names>",
+ "help": "List of valid names for the first argument in \
+a metaclass class method.",
+ },
+ ),
+ (
+ "exclude-protected",
+ {
+ "default": (
+ # namedtuple public API.
+ "_asdict",
+ "_fields",
+ "_replace",
+ "_source",
+ "_make",
+ ),
+ "type": "csv",
+ "metavar": "<protected access exclusions>",
+ "help": (
+ "List of member names, which should be excluded "
+ "from the protected access warning."
+ ),
+ },
+ ),
+ )
+
+ def __init__(self, linter=None):
+ BaseChecker.__init__(self, linter)
+ self._accessed = ScopeAccessMap()
+ self._first_attrs = []
+ self._meth_could_be_func = None
+
+ @decorators.cachedproperty
+ def _dummy_rgx(self):
+ return get_global_option(self, "dummy-variables-rgx", default=None)
+
+ @decorators.cachedproperty
+ def _ignore_mixin(self):
+ return get_global_option(self, "ignore-mixin-members", default=True)
+
+ @check_messages(
+ "abstract-method",
+ "no-init",
+ "invalid-slots",
+ "single-string-used-for-slots",
+ "invalid-slots-object",
+ "class-variable-slots-conflict",
+ "inherit-non-class",
+ "useless-object-inheritance",
+ "inconsistent-mro",
+ "duplicate-bases",
+ )
+ def visit_classdef(self, node):
+ """init visit variable _accessed
+ """
+ self._check_bases_classes(node)
+ # if not an exception or a metaclass
+ if node.type == "class" and has_known_bases(node):
+ try:
+ node.local_attr("__init__")
+ except astroid.NotFoundError:
+ self.add_message("no-init", args=node, node=node)
+ self._check_slots(node)
+ self._check_proper_bases(node)
+ self._check_consistent_mro(node)
+
+ def _check_consistent_mro(self, node):
+ """Detect that a class has a consistent mro or duplicate bases."""
+ try:
+ node.mro()
+ except InconsistentMroError:
+ self.add_message("inconsistent-mro", args=node.name, node=node)
+ except DuplicateBasesError:
+ self.add_message("duplicate-bases", args=node.name, node=node)
+ except NotImplementedError:
+ # Old style class, there's no mro so don't do anything.
+ pass
+
+ def _check_proper_bases(self, node):
+ """
+ Detect that a class inherits something which is not
+ a class or a type.
+ """
+ for base in node.bases:
+ ancestor = safe_infer(base)
+ if ancestor in (astroid.Uninferable, None):
+ continue
+ if isinstance(ancestor, astroid.Instance) and ancestor.is_subtype_of(
+ "%s.type" % (BUILTINS,)
+ ):
+ continue
+
+ if not isinstance(ancestor, astroid.ClassDef) or _is_invalid_base_class(
+ ancestor
+ ):
+ self.add_message("inherit-non-class", args=base.as_string(), node=node)
+
+ if ancestor.name == object.__name__:
+ self.add_message(
+ "useless-object-inheritance", args=node.name, node=node
+ )
+
+ def leave_classdef(self, cnode):
+ """close a class node:
+ check that instance attributes are defined in __init__ and check
+ access to existent members
+ """
+ # check access to existent members on non metaclass classes
+ if self._ignore_mixin and cnode.name[-5:].lower() == "mixin":
+ # We are in a mixin class. No need to try to figure out if
+ # something is missing, since it is most likely that it will
+ # miss.
+ return
+
+ accessed = self._accessed.accessed(cnode)
+ if cnode.type != "metaclass":
+ self._check_accessed_members(cnode, accessed)
+ # checks attributes are defined in an allowed method such as __init__
+ if not self.linter.is_message_enabled("attribute-defined-outside-init"):
+ return
+ defining_methods = self.config.defining_attr_methods
+ current_module = cnode.root()
+ for attr, nodes in cnode.instance_attrs.items():
+ # Exclude `__dict__` as it is already defined.
+ if attr == "__dict__":
+ continue
+
+ # Skip nodes which are not in the current module and it may screw up
+ # the output, while it's not worth it
+ nodes = [
+ n
+ for n in nodes
+ if not isinstance(n.statement(), (astroid.Delete, astroid.AugAssign))
+ and n.root() is current_module
+ ]
+ if not nodes:
+ continue # error detected by typechecking
+
+ # Check if any method attr is defined in is a defining method
+ # or if we have the attribute defined in a setter.
+ frames = (node.frame() for node in nodes)
+ if any(
+ frame.name in defining_methods or is_property_setter(frame)
+ for frame in frames
+ ):
+ continue
+
+ # check attribute is defined in a parent's __init__
+ for parent in cnode.instance_attr_ancestors(attr):
+ attr_defined = False
+ # check if any parent method attr is defined in is a defining method
+ for node in parent.instance_attrs[attr]:
+ if node.frame().name in defining_methods:
+ attr_defined = True
+ if attr_defined:
+ # we're done :)
+ break
+ else:
+ # check attribute is defined as a class attribute
+ try:
+ cnode.local_attr(attr)
+ except astroid.NotFoundError:
+ for node in nodes:
+ if node.frame().name not in defining_methods:
+ # If the attribute was set by a call in any
+ # of the defining methods, then don't emit
+ # the warning.
+ if _called_in_methods(
+ node.frame(), cnode, defining_methods
+ ):
+ continue
+ self.add_message(
+ "attribute-defined-outside-init", args=attr, node=node
+ )
+
+ def visit_functiondef(self, node):
+ """check method arguments, overriding"""
+ # ignore actual functions
+ if not node.is_method():
+ return
+
+ self._check_useless_super_delegation(node)
+ self._check_property_with_parameters(node)
+
+ klass = node.parent.frame()
+ self._meth_could_be_func = True
+ # check first argument is self if this is actually a method
+ self._check_first_arg_for_type(node, klass.type == "metaclass")
+ if node.name == "__init__":
+ self._check_init(node)
+ return
+ # check signature if the method overloads inherited method
+ for overridden in klass.local_attr_ancestors(node.name):
+ # get astroid for the searched method
+ try:
+ parent_function = overridden[node.name]
+ except KeyError:
+ # we have found the method but it's not in the local
+ # dictionary.
+ # This may happen with astroid build from living objects
+ continue
+ if not isinstance(parent_function, astroid.FunctionDef):
+ continue
+ self._check_signature(node, parent_function, "overridden", klass)
+ self._check_invalid_overridden_method(node, parent_function)
+ break
+
+ if node.decorators:
+ for decorator in node.decorators.nodes:
+ if isinstance(decorator, astroid.Attribute) and decorator.attrname in (
+ "getter",
+ "setter",
+ "deleter",
+ ):
+ # attribute affectation will call this method, not hiding it
+ return
+ if isinstance(decorator, astroid.Name):
+ if decorator.name == "property":
+ # attribute affectation will either call a setter or raise
+ # an attribute error, anyway not hiding the function
+ return
+
+ # Infer the decorator and see if it returns something useful
+ inferred = safe_infer(decorator)
+ if not inferred:
+ return
+ if isinstance(inferred, astroid.FunctionDef):
+ # Okay, it's a decorator, let's see what it can infer.
+ try:
+ inferred = next(inferred.infer_call_result(inferred))
+ except astroid.InferenceError:
+ return
+ try:
+ if (
+ isinstance(inferred, (astroid.Instance, astroid.ClassDef))
+ and inferred.getattr("__get__")
+ and inferred.getattr("__set__")
+ ):
+ return
+ except astroid.AttributeInferenceError:
+ pass
+
+ # check if the method is hidden by an attribute
+ try:
+ overridden = klass.instance_attr(node.name)[0]
+ overridden_frame = overridden.frame()
+ if (
+ isinstance(overridden_frame, astroid.FunctionDef)
+ and overridden_frame.type == "method"
+ ):
+ overridden_frame = overridden_frame.parent.frame()
+ if isinstance(overridden_frame, astroid.ClassDef) and klass.is_subtype_of(
+ overridden_frame.qname()
+ ):
+ args = (overridden.root().name, overridden.fromlineno)
+ self.add_message("method-hidden", args=args, node=node)
+ except astroid.NotFoundError:
+ pass
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ def _check_useless_super_delegation(self, function):
+ """Check if the given function node is an useless method override
+
+ We consider it *useless* if it uses the super() builtin, but having
+ nothing additional whatsoever than not implementing the method at all.
+ If the method uses super() to delegate an operation to the rest of the MRO,
+ and if the method called is the same as the current one, the arguments
+ passed to super() are the same as the parameters that were passed to
+ this method, then the method could be removed altogether, by letting
+ other implementation to take precedence.
+ """
+
+ if (
+ not function.is_method()
+ # With decorators is a change of use
+ or function.decorators
+ ):
+ return
+
+ body = function.body
+ if len(body) != 1:
+ # Multiple statements, which means this overridden method
+ # could do multiple things we are not aware of.
+ return
+
+ statement = body[0]
+ if not isinstance(statement, (astroid.Expr, astroid.Return)):
+ # Doing something else than what we are interested into.
+ return
+
+ call = statement.value
+ if (
+ not isinstance(call, astroid.Call)
+ # Not a super() attribute access.
+ or not isinstance(call.func, astroid.Attribute)
+ ):
+ return
+
+ # Should be a super call.
+ try:
+ super_call = next(call.func.expr.infer())
+ except astroid.InferenceError:
+ return
+ else:
+ if not isinstance(super_call, objects.Super):
+ return
+
+ # The name should be the same.
+ if call.func.attrname != function.name:
+ return
+
+ # Should be a super call with the MRO pointer being the
+ # current class and the type being the current instance.
+ current_scope = function.parent.scope()
+ if (
+ super_call.mro_pointer != current_scope
+ or not isinstance(super_call.type, astroid.Instance)
+ or super_call.type.name != current_scope.name
+ ):
+ return
+
+ #  Check values of default args
+ klass = function.parent.frame()
+ meth_node = None
+ for overridden in klass.local_attr_ancestors(function.name):
+ # get astroid for the searched method
+ try:
+ meth_node = overridden[function.name]
+ except KeyError:
+ # we have found the method but it's not in the local
+ # dictionary.
+ # This may happen with astroid build from living objects
+ continue
+ if (
+ not isinstance(meth_node, astroid.FunctionDef)
+ # If the method have an ancestor which is not a
+ # function then it is legitimate to redefine it
+ or _has_different_parameters_default_value(
+ meth_node.args, function.args
+ )
+ ):
+ return
+ break
+
+ # Detect if the parameters are the same as the call's arguments.
+ params = _signature_from_arguments(function.args)
+ args = _signature_from_call(call)
+
+ if meth_node is not None:
+
+ def form_annotations(annotations):
+ return [
+ annotation.as_string() for annotation in filter(None, annotations)
+ ]
+
+ called_annotations = form_annotations(function.args.annotations)
+ overridden_annotations = form_annotations(meth_node.args.annotations)
+ if called_annotations and overridden_annotations:
+ if called_annotations != overridden_annotations:
+ return
+
+ if _definition_equivalent_to_call(params, args):
+ self.add_message(
+ "useless-super-delegation", node=function, args=(function.name,)
+ )
+
+ def _check_property_with_parameters(self, node):
+ if node.args.args and len(node.args.args) > 1 and decorated_with_property(node):
+ self.add_message("property-with-parameters", node=node)
+
+ def _check_invalid_overridden_method(self, function_node, parent_function_node):
+ parent_is_property = decorated_with_property(
+ parent_function_node
+ ) or is_property_setter_or_deleter(parent_function_node)
+ current_is_property = decorated_with_property(
+ function_node
+ ) or is_property_setter_or_deleter(function_node)
+ if parent_is_property and not current_is_property:
+ self.add_message(
+ "invalid-overridden-method",
+ args=(function_node.name, "property", function_node.type),
+ node=function_node,
+ )
+ elif not parent_is_property and current_is_property:
+ self.add_message(
+ "invalid-overridden-method",
+ args=(function_node.name, "method", "property"),
+ node=function_node,
+ )
+
+ def _check_slots(self, node):
+ if "__slots__" not in node.locals:
+ return
+ for slots in node.igetattr("__slots__"):
+ # check if __slots__ is a valid type
+ if slots is astroid.Uninferable:
+ continue
+ if not is_iterable(slots) and not is_comprehension(slots):
+ self.add_message("invalid-slots", node=node)
+ continue
+
+ if isinstance(slots, astroid.Const):
+ # a string, ignore the following checks
+ self.add_message("single-string-used-for-slots", node=node)
+ continue
+ if not hasattr(slots, "itered"):
+ # we can't obtain the values, maybe a .deque?
+ continue
+
+ if isinstance(slots, astroid.Dict):
+ values = [item[0] for item in slots.items]
+ else:
+ values = slots.itered()
+ if values is astroid.Uninferable:
+ return
+ for elt in values:
+ try:
+ self._check_slots_elt(elt, node)
+ except astroid.InferenceError:
+ continue
+
+ def _check_slots_elt(self, elt, node):
+ for inferred in elt.infer():
+ if inferred is astroid.Uninferable:
+ continue
+ if not isinstance(inferred, astroid.Const) or not isinstance(
+ inferred.value, str
+ ):
+ self.add_message(
+ "invalid-slots-object", args=inferred.as_string(), node=elt
+ )
+ continue
+ if not inferred.value:
+ self.add_message(
+ "invalid-slots-object", args=inferred.as_string(), node=elt
+ )
+
+ # Check if we have a conflict with a class variable.
+ class_variable = node.locals.get(inferred.value)
+ if class_variable:
+ # Skip annotated assignments which don't conflict at all with slots.
+ if len(class_variable) == 1:
+ parent = class_variable[0].parent
+ if isinstance(parent, astroid.AnnAssign) and parent.value is None:
+ return
+ self.add_message(
+ "class-variable-slots-conflict", args=(inferred.value,), node=elt
+ )
+
+ def leave_functiondef(self, node):
+ """on method node, check if this method couldn't be a function
+
+ ignore class, static and abstract methods, initializer,
+ methods overridden from a parent class.
+ """
+ if node.is_method():
+ if node.args.args is not None:
+ self._first_attrs.pop()
+ if not self.linter.is_message_enabled("no-self-use"):
+ return
+ class_node = node.parent.frame()
+ if (
+ self._meth_could_be_func
+ and node.type == "method"
+ and node.name not in PYMETHODS
+ and not (
+ node.is_abstract()
+ or overrides_a_method(class_node, node.name)
+ or decorated_with_property(node)
+ or _has_bare_super_call(node)
+ or is_protocol_class(class_node)
+ )
+ ):
+ self.add_message("no-self-use", node=node)
+
+ def visit_attribute(self, node):
+ """check if the getattr is an access to a class member
+ if so, register it. Also check for access to protected
+ class member from outside its class (but ignore __special__
+ methods)
+ """
+ # Check self
+ if self._uses_mandatory_method_param(node):
+ self._accessed.set_accessed(node)
+ return
+ if not self.linter.is_message_enabled("protected-access"):
+ return
+
+ self._check_protected_attribute_access(node)
+
+ def visit_assignattr(self, node):
+ if isinstance(
+ node.assign_type(), astroid.AugAssign
+ ) and self._uses_mandatory_method_param(node):
+ self._accessed.set_accessed(node)
+ self._check_in_slots(node)
+
+ def _check_in_slots(self, node):
+ """ Check that the given AssignAttr node
+ is defined in the class slots.
+ """
+ inferred = safe_infer(node.expr)
+ if not isinstance(inferred, astroid.Instance):
+ return
+
+ klass = inferred._proxied
+ if not has_known_bases(klass):
+ return
+ if "__slots__" not in klass.locals or not klass.newstyle:
+ return
+
+ slots = klass.slots()
+ if slots is None:
+ return
+ # If any ancestor doesn't use slots, the slots
+ # defined for this class are superfluous.
+ if any(
+ "__slots__" not in ancestor.locals and ancestor.name != "object"
+ for ancestor in klass.ancestors()
+ ):
+ return
+
+ if not any(slot.value == node.attrname for slot in slots):
+ # If we have a '__dict__' in slots, then
+ # assigning any name is valid.
+ if not any(slot.value == "__dict__" for slot in slots):
+ if _is_attribute_property(node.attrname, klass):
+ # Properties circumvent the slots mechanism,
+ # so we should not emit a warning for them.
+ return
+ if node.attrname in klass.locals and _has_data_descriptor(
+ klass, node.attrname
+ ):
+ # Descriptors circumvent the slots mechanism as well.
+ return
+ if node.attrname == "__class__" and _has_same_layout_slots(
+ slots, node.parent.value
+ ):
+ return
+ self.add_message("assigning-non-slot", args=(node.attrname,), node=node)
+
+ @check_messages(
+ "protected-access", "no-classmethod-decorator", "no-staticmethod-decorator"
+ )
+ def visit_assign(self, assign_node):
+ self._check_classmethod_declaration(assign_node)
+ node = assign_node.targets[0]
+ if not isinstance(node, astroid.AssignAttr):
+ return
+
+ if self._uses_mandatory_method_param(node):
+ return
+ self._check_protected_attribute_access(node)
+
+ def _check_classmethod_declaration(self, node):
+ """Checks for uses of classmethod() or staticmethod()
+
+ When a @classmethod or @staticmethod decorator should be used instead.
+ A message will be emitted only if the assignment is at a class scope
+ and only if the classmethod's argument belongs to the class where it
+ is defined.
+ `node` is an assign node.
+ """
+ if not isinstance(node.value, astroid.Call):
+ return
+
+ # check the function called is "classmethod" or "staticmethod"
+ func = node.value.func
+ if not isinstance(func, astroid.Name) or func.name not in (
+ "classmethod",
+ "staticmethod",
+ ):
+ return
+
+ msg = (
+ "no-classmethod-decorator"
+ if func.name == "classmethod"
+ else "no-staticmethod-decorator"
+ )
+ # assignment must be at a class scope
+ parent_class = node.scope()
+ if not isinstance(parent_class, astroid.ClassDef):
+ return
+
+ # Check if the arg passed to classmethod is a class member
+ classmeth_arg = node.value.args[0]
+ if not isinstance(classmeth_arg, astroid.Name):
+ return
+
+ method_name = classmeth_arg.name
+ if any(method_name == member.name for member in parent_class.mymethods()):
+ self.add_message(msg, node=node.targets[0])
+
+ def _check_protected_attribute_access(self, node):
+ """Given an attribute access node (set or get), check if attribute
+ access is legitimate. Call _check_first_attr with node before calling
+ this method. Valid cases are:
+ * self._attr in a method or cls._attr in a classmethod. Checked by
+ _check_first_attr.
+ * Klass._attr inside "Klass" class.
+ * Klass2._attr inside "Klass" class when Klass2 is a base class of
+ Klass.
+ """
+ attrname = node.attrname
+
+ if (
+ is_attr_protected(attrname)
+ and attrname not in self.config.exclude_protected
+ ):
+
+ klass = node_frame_class(node)
+
+ # In classes, check we are not getting a parent method
+ # through the class object or through super
+ callee = node.expr.as_string()
+
+ # We are not in a class, no remaining valid case
+ if klass is None:
+ self.add_message("protected-access", node=node, args=attrname)
+ return
+
+ # If the expression begins with a call to super, that's ok.
+ if (
+ isinstance(node.expr, astroid.Call)
+ and isinstance(node.expr.func, astroid.Name)
+ and node.expr.func.name == "super"
+ ):
+ return
+
+ # If the expression begins with a call to type(self), that's ok.
+ if self._is_type_self_call(node.expr):
+ return
+
+ # We are in a class, one remaining valid cases, Klass._attr inside
+ # Klass
+ if not (callee == klass.name or callee in klass.basenames):
+ # Detect property assignments in the body of the class.
+ # This is acceptable:
+ #
+ # class A:
+ # b = property(lambda: self._b)
+
+ stmt = node.parent.statement()
+ if (
+ isinstance(stmt, astroid.Assign)
+ and len(stmt.targets) == 1
+ and isinstance(stmt.targets[0], astroid.AssignName)
+ ):
+ name = stmt.targets[0].name
+ if _is_attribute_property(name, klass):
+ return
+
+ #  A licit use of protected member is inside a special method
+ if not attrname.startswith(
+ "__"
+ ) and self._is_called_inside_special_method(node):
+ return
+
+ self.add_message("protected-access", node=node, args=attrname)
+
+ @staticmethod
+ def _is_called_inside_special_method(node: astroid.node_classes.NodeNG) -> bool:
+ """
+ Returns true if the node is located inside a special (aka dunder) method
+ """
+ try:
+ frame_name = node.frame().name
+ except AttributeError:
+ return False
+ return frame_name and frame_name in PYMETHODS
+
+ def _is_type_self_call(self, expr):
+ return (
+ isinstance(expr, astroid.Call)
+ and isinstance(expr.func, astroid.Name)
+ and expr.func.name == "type"
+ and len(expr.args) == 1
+ and self._is_mandatory_method_param(expr.args[0])
+ )
+
+ def visit_name(self, node):
+ """check if the name handle an access to a class member
+ if so, register it
+ """
+ if self._first_attrs and (
+ node.name == self._first_attrs[-1] or not self._first_attrs[-1]
+ ):
+ self._meth_could_be_func = False
+
+ def _check_accessed_members(self, node, accessed):
+ """check that accessed members are defined"""
+ excs = ("AttributeError", "Exception", "BaseException")
+ for attr, nodes in accessed.items():
+ try:
+ # is it a class attribute ?
+ node.local_attr(attr)
+ # yes, stop here
+ continue
+ except astroid.NotFoundError:
+ pass
+ # is it an instance attribute of a parent class ?
+ try:
+ next(node.instance_attr_ancestors(attr))
+ # yes, stop here
+ continue
+ except StopIteration:
+ pass
+ # is it an instance attribute ?
+ try:
+ defstmts = node.instance_attr(attr)
+ except astroid.NotFoundError:
+ pass
+ else:
+ # filter out augment assignment nodes
+ defstmts = [stmt for stmt in defstmts if stmt not in nodes]
+ if not defstmts:
+ # only augment assignment for this node, no-member should be
+ # triggered by the typecheck checker
+ continue
+ # filter defstmts to only pick the first one when there are
+ # several assignments in the same scope
+ scope = defstmts[0].scope()
+ defstmts = [
+ stmt
+ for i, stmt in enumerate(defstmts)
+ if i == 0 or stmt.scope() is not scope
+ ]
+ # if there are still more than one, don't attempt to be smarter
+ # than we can be
+ if len(defstmts) == 1:
+ defstmt = defstmts[0]
+ # check that if the node is accessed in the same method as
+ # it's defined, it's accessed after the initial assignment
+ frame = defstmt.frame()
+ lno = defstmt.fromlineno
+ for _node in nodes:
+ if (
+ _node.frame() is frame
+ and _node.fromlineno < lno
+ and not astroid.are_exclusive(
+ _node.statement(), defstmt, excs
+ )
+ ):
+ self.add_message(
+ "access-member-before-definition",
+ node=_node,
+ args=(attr, lno),
+ )
+
+ def _check_first_arg_for_type(self, node, metaclass=0):
+ """check the name of first argument, expect:
+
+ * 'self' for a regular method
+ * 'cls' for a class method or a metaclass regular method (actually
+ valid-classmethod-first-arg value)
+ * 'mcs' for a metaclass class method (actually
+ valid-metaclass-classmethod-first-arg)
+ * not one of the above for a static method
+ """
+ # don't care about functions with unknown argument (builtins)
+ if node.args.args is None:
+ return
+ if node.args.args:
+ first_arg = node.argnames()[0]
+ elif node.args.posonlyargs:
+ first_arg = node.args.posonlyargs[0].name
+ else:
+ first_arg = None
+ self._first_attrs.append(first_arg)
+ first = self._first_attrs[-1]
+ # static method
+ if node.type == "staticmethod":
+ if (
+ first_arg == "self"
+ or first_arg in self.config.valid_classmethod_first_arg
+ or first_arg in self.config.valid_metaclass_classmethod_first_arg
+ ):
+ self.add_message("bad-staticmethod-argument", args=first, node=node)
+ return
+ self._first_attrs[-1] = None
+ # class / regular method with no args
+ elif not node.args.args and not node.args.posonlyargs:
+ self.add_message("no-method-argument", node=node)
+ # metaclass
+ elif metaclass:
+ # metaclass __new__ or classmethod
+ if node.type == "classmethod":
+ self._check_first_arg_config(
+ first,
+ self.config.valid_metaclass_classmethod_first_arg,
+ node,
+ "bad-mcs-classmethod-argument",
+ node.name,
+ )
+ # metaclass regular method
+ else:
+ self._check_first_arg_config(
+ first,
+ self.config.valid_classmethod_first_arg,
+ node,
+ "bad-mcs-method-argument",
+ node.name,
+ )
+ # regular class
+ else:
+ # class method
+ if node.type == "classmethod" or node.name == "__class_getitem__":
+ self._check_first_arg_config(
+ first,
+ self.config.valid_classmethod_first_arg,
+ node,
+ "bad-classmethod-argument",
+ node.name,
+ )
+ # regular method without self as argument
+ elif first != "self":
+ self.add_message("no-self-argument", node=node)
+
+ def _check_first_arg_config(self, first, config, node, message, method_name):
+ if first not in config:
+ if len(config) == 1:
+ valid = repr(config[0])
+ else:
+ valid = ", ".join(repr(v) for v in config[:-1])
+ valid = "%s or %r" % (valid, config[-1])
+ self.add_message(message, args=(method_name, valid), node=node)
+
+ def _check_bases_classes(self, node):
+ """check that the given class node implements abstract methods from
+ base classes
+ """
+
+ def is_abstract(method):
+ return method.is_abstract(pass_is_abstract=False)
+
+ # check if this class abstract
+ if class_is_abstract(node):
+ return
+
+ methods = sorted(
+ unimplemented_abstract_methods(node, is_abstract).items(),
+ key=lambda item: item[0],
+ )
+ for name, method in methods:
+ owner = method.parent.frame()
+ if owner is node:
+ continue
+ # owner is not this class, it must be a parent class
+ # check that the ancestor's method is not abstract
+ if name in node.locals:
+ # it is redefined as an attribute or with a descriptor
+ continue
+ self.add_message("abstract-method", node=node, args=(name, owner.name))
+
+ def _check_init(self, node):
+ """check that the __init__ method call super or ancestors'__init__
+ method (unless it is used for type hinting with `typing.overload`)
+ """
+ if not self.linter.is_message_enabled(
+ "super-init-not-called"
+ ) and not self.linter.is_message_enabled("non-parent-init-called"):
+ return
+ klass_node = node.parent.frame()
+ to_call = _ancestors_to_call(klass_node)
+ not_called_yet = dict(to_call)
+ for stmt in node.nodes_of_class(astroid.Call):
+ expr = stmt.func
+ if not isinstance(expr, astroid.Attribute) or expr.attrname != "__init__":
+ continue
+ # skip the test if using super
+ if (
+ isinstance(expr.expr, astroid.Call)
+ and isinstance(expr.expr.func, astroid.Name)
+ and expr.expr.func.name == "super"
+ ):
+ return
+ try:
+ for klass in expr.expr.infer():
+ if klass is astroid.Uninferable:
+ continue
+ # The inferred klass can be super(), which was
+ # assigned to a variable and the `__init__`
+ # was called later.
+ #
+ # base = super()
+ # base.__init__(...)
+
+ if (
+ isinstance(klass, astroid.Instance)
+ and isinstance(klass._proxied, astroid.ClassDef)
+ and is_builtin_object(klass._proxied)
+ and klass._proxied.name == "super"
+ ):
+ return
+ if isinstance(klass, objects.Super):
+ return
+ try:
+ del not_called_yet[klass]
+ except KeyError:
+ if klass not in to_call:
+ self.add_message(
+ "non-parent-init-called", node=expr, args=klass.name
+ )
+ except astroid.InferenceError:
+ continue
+ for klass, method in not_called_yet.items():
+ if decorated_with(node, ["typing.overload"]):
+ continue
+ cls = node_frame_class(method)
+ if klass.name == "object" or (cls and cls.name == "object"):
+ continue
+ self.add_message("super-init-not-called", args=klass.name, node=node)
+
+ def _check_signature(self, method1, refmethod, class_type, cls):
+ """check that the signature of the two given methods match
+ """
+ if not (
+ isinstance(method1, astroid.FunctionDef)
+ and isinstance(refmethod, astroid.FunctionDef)
+ ):
+ self.add_message(
+ "method-check-failed", args=(method1, refmethod), node=method1
+ )
+ return
+
+ instance = cls.instantiate_class()
+ method1 = function_to_method(method1, instance)
+ refmethod = function_to_method(refmethod, instance)
+
+ # Don't care about functions with unknown argument (builtins).
+ if method1.args.args is None or refmethod.args.args is None:
+ return
+
+ # Ignore private to class methods.
+ if is_attr_private(method1.name):
+ return
+ # Ignore setters, they have an implicit extra argument,
+ # which shouldn't be taken in consideration.
+ if is_property_setter(method1):
+ return
+
+ if _different_parameters(
+ refmethod, method1, dummy_parameter_regex=self._dummy_rgx
+ ):
+ self.add_message(
+ "arguments-differ", args=(class_type, method1.name), node=method1
+ )
+ elif len(method1.args.defaults) < len(refmethod.args.defaults):
+ self.add_message(
+ "signature-differs", args=(class_type, method1.name), node=method1
+ )
+
+ def _uses_mandatory_method_param(self, node):
+ """Check that attribute lookup name use first attribute variable name
+
+ Name is `self` for method, `cls` for classmethod and `mcs` for metaclass.
+ """
+ return self._is_mandatory_method_param(node.expr)
+
+ def _is_mandatory_method_param(self, node):
+ """Check if astroid.Name corresponds to first attribute variable name
+
+ Name is `self` for method, `cls` for classmethod and `mcs` for metaclass.
+ """
+ return (
+ self._first_attrs
+ and isinstance(node, astroid.Name)
+ and node.name == self._first_attrs[-1]
+ )
+
+
+class SpecialMethodsChecker(BaseChecker):
+ """Checker which verifies that special methods
+ are implemented correctly.
+ """
+
+ __implements__ = (IAstroidChecker,)
+ name = "classes"
+ msgs = {
+ "E0301": (
+ "__iter__ returns non-iterator",
+ "non-iterator-returned",
+ "Used when an __iter__ method returns something which is not an "
+ "iterable (i.e. has no `%s` method)" % NEXT_METHOD,
+ {
+ "old_names": [
+ ("W0234", "old-non-iterator-returned-1"),
+ ("E0234", "old-non-iterator-returned-2"),
+ ]
+ },
+ ),
+ "E0302": (
+ "The special method %r expects %s param(s), %d %s given",
+ "unexpected-special-method-signature",
+ "Emitted when a special method was defined with an "
+ "invalid number of parameters. If it has too few or "
+ "too many, it might not work at all.",
+ {"old_names": [("E0235", "bad-context-manager")]},
+ ),
+ "E0303": (
+ "__len__ does not return non-negative integer",
+ "invalid-length-returned",
+ "Used when a __len__ method returns something which is not a "
+ "non-negative integer",
+ {},
+ ),
+ }
+ priority = -2
+
+ @check_messages(
+ "unexpected-special-method-signature",
+ "non-iterator-returned",
+ "invalid-length-returned",
+ )
+ def visit_functiondef(self, node):
+ if not node.is_method():
+ return
+ if node.name == "__iter__":
+ self._check_iter(node)
+ if node.name == "__len__":
+ self._check_len(node)
+ if node.name in PYMETHODS:
+ self._check_unexpected_method_signature(node)
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ def _check_unexpected_method_signature(self, node):
+ expected_params = SPECIAL_METHODS_PARAMS[node.name]
+
+ if expected_params is None:
+ # This can support a variable number of parameters.
+ return
+ if not node.args.args and not node.args.vararg:
+ # Method has no parameter, will be caught
+ # by no-method-argument.
+ return
+
+ if decorated_with(node, [BUILTINS + ".staticmethod"]):
+ # We expect to not take in consideration self.
+ all_args = node.args.args
+ else:
+ all_args = node.args.args[1:]
+ mandatory = len(all_args) - len(node.args.defaults)
+ optional = len(node.args.defaults)
+ current_params = mandatory + optional
+
+ if isinstance(expected_params, tuple):
+ # The expected number of parameters can be any value from this
+ # tuple, although the user should implement the method
+ # to take all of them in consideration.
+ emit = mandatory not in expected_params
+ expected_params = "between %d or %d" % expected_params
+ else:
+ # If the number of mandatory parameters doesn't
+ # suffice, the expected parameters for this
+ # function will be deduced from the optional
+ # parameters.
+ rest = expected_params - mandatory
+ if rest == 0:
+ emit = False
+ elif rest < 0:
+ emit = True
+ elif rest > 0:
+ emit = not ((optional - rest) >= 0 or node.args.vararg)
+
+ if emit:
+ verb = "was" if current_params <= 1 else "were"
+ self.add_message(
+ "unexpected-special-method-signature",
+ args=(node.name, expected_params, current_params, verb),
+ node=node,
+ )
+
+ @staticmethod
+ def _is_iterator(node):
+ if node is astroid.Uninferable:
+ # Just ignore Uninferable objects.
+ return True
+ if isinstance(node, Generator):
+ # Generators can be itered.
+ return True
+
+ if isinstance(node, astroid.Instance):
+ try:
+ node.local_attr(NEXT_METHOD)
+ return True
+ except astroid.NotFoundError:
+ pass
+ elif isinstance(node, astroid.ClassDef):
+ metaclass = node.metaclass()
+ if metaclass and isinstance(metaclass, astroid.ClassDef):
+ try:
+ metaclass.local_attr(NEXT_METHOD)
+ return True
+ except astroid.NotFoundError:
+ pass
+ return False
+
+ def _check_iter(self, node):
+ inferred = _safe_infer_call_result(node, node)
+ if inferred is not None:
+ if not self._is_iterator(inferred):
+ self.add_message("non-iterator-returned", node=node)
+
+ def _check_len(self, node):
+ inferred = _safe_infer_call_result(node, node)
+ if not inferred or inferred is astroid.Uninferable:
+ return
+
+ if (
+ isinstance(inferred, astroid.Instance)
+ and inferred.name == "int"
+ and not isinstance(inferred, astroid.Const)
+ ):
+ # Assume it's good enough, since the int() call might wrap
+ # something that's uninferable for us
+ return
+
+ if not isinstance(inferred, astroid.Const):
+ self.add_message("invalid-length-returned", node=node)
+ return
+
+ value = inferred.value
+ if not isinstance(value, int) or value < 0:
+ self.add_message("invalid-length-returned", node=node)
+
+
+def _ancestors_to_call(klass_node, method="__init__"):
+ """return a dictionary where keys are the list of base classes providing
+ the queried method, and so that should/may be called from the method node
+ """
+ to_call = {}
+ for base_node in klass_node.ancestors(recurs=False):
+ try:
+ to_call[base_node] = next(base_node.igetattr(method))
+ except astroid.InferenceError:
+ continue
+ return to_call
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(ClassChecker(linter))
+ linter.register_checker(SpecialMethodsChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/design_analysis.py b/venv/Lib/site-packages/pylint/checkers/design_analysis.py
new file mode 100644
index 0000000..50d8eaa
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/design_analysis.py
@@ -0,0 +1,496 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006, 2009-2010, 2012-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012, 2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 ahirnish <ahirnish@gmail.com>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 Mark Miller <725mrm@gmail.com>
+# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+# Copyright (c) 2018 Jakub Wilk <jwilk@jwilk.net>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""check for signs of poor design"""
+
+import re
+from collections import defaultdict
+
+import astroid
+from astroid import BoolOp, If, decorators
+
+from pylint import utils
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import check_messages
+from pylint.interfaces import IAstroidChecker
+
+MSGS = {
+ "R0901": (
+ "Too many ancestors (%s/%s)",
+ "too-many-ancestors",
+ "Used when class has too many parent classes, try to reduce "
+ "this to get a simpler (and so easier to use) class.",
+ ),
+ "R0902": (
+ "Too many instance attributes (%s/%s)",
+ "too-many-instance-attributes",
+ "Used when class has too many instance attributes, try to reduce "
+ "this to get a simpler (and so easier to use) class.",
+ ),
+ "R0903": (
+ "Too few public methods (%s/%s)",
+ "too-few-public-methods",
+ "Used when class has too few public methods, so be sure it's "
+ "really worth it.",
+ ),
+ "R0904": (
+ "Too many public methods (%s/%s)",
+ "too-many-public-methods",
+ "Used when class has too many public methods, try to reduce "
+ "this to get a simpler (and so easier to use) class.",
+ ),
+ "R0911": (
+ "Too many return statements (%s/%s)",
+ "too-many-return-statements",
+ "Used when a function or method has too many return statement, "
+ "making it hard to follow.",
+ ),
+ "R0912": (
+ "Too many branches (%s/%s)",
+ "too-many-branches",
+ "Used when a function or method has too many branches, "
+ "making it hard to follow.",
+ ),
+ "R0913": (
+ "Too many arguments (%s/%s)",
+ "too-many-arguments",
+ "Used when a function or method takes too many arguments.",
+ ),
+ "R0914": (
+ "Too many local variables (%s/%s)",
+ "too-many-locals",
+ "Used when a function or method has too many local variables.",
+ ),
+ "R0915": (
+ "Too many statements (%s/%s)",
+ "too-many-statements",
+ "Used when a function or method has too many statements. You "
+ "should then split it in smaller functions / methods.",
+ ),
+ "R0916": (
+ "Too many boolean expressions in if statement (%s/%s)",
+ "too-many-boolean-expressions",
+ "Used when an if statement contains too many boolean expressions.",
+ ),
+}
+SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
+DATACLASSES_DECORATORS = frozenset({"dataclass", "attrs"})
+DATACLASS_IMPORT = "dataclasses"
+TYPING_NAMEDTUPLE = "typing.NamedTuple"
+
+
+def _is_exempt_from_public_methods(node: astroid.ClassDef) -> bool:
+ """Check if a class is exempt from too-few-public-methods"""
+
+ # If it's a typing.Namedtuple or an Enum
+ for ancestor in node.ancestors():
+ if ancestor.name == "Enum" and ancestor.root().name == "enum":
+ return True
+ if ancestor.qname() == TYPING_NAMEDTUPLE:
+ return True
+
+ # Or if it's a dataclass
+ if not node.decorators:
+ return False
+
+ root_locals = set(node.root().locals)
+ for decorator in node.decorators.nodes:
+ if isinstance(decorator, astroid.Call):
+ decorator = decorator.func
+ if not isinstance(decorator, (astroid.Name, astroid.Attribute)):
+ continue
+ if isinstance(decorator, astroid.Name):
+ name = decorator.name
+ else:
+ name = decorator.attrname
+ if name in DATACLASSES_DECORATORS and (
+ root_locals.intersection(DATACLASSES_DECORATORS)
+ or DATACLASS_IMPORT in root_locals
+ ):
+ return True
+ return False
+
+
+def _count_boolean_expressions(bool_op):
+ """Counts the number of boolean expressions in BoolOp `bool_op` (recursive)
+
+ example: a and (b or c or (d and e)) ==> 5 boolean expressions
+ """
+ nb_bool_expr = 0
+ for bool_expr in bool_op.get_children():
+ if isinstance(bool_expr, BoolOp):
+ nb_bool_expr += _count_boolean_expressions(bool_expr)
+ else:
+ nb_bool_expr += 1
+ return nb_bool_expr
+
+
+def _count_methods_in_class(node):
+ all_methods = sum(1 for method in node.methods() if not method.name.startswith("_"))
+ # Special methods count towards the number of public methods,
+ # but don't count towards there being too many methods.
+ for method in node.mymethods():
+ if SPECIAL_OBJ.search(method.name) and method.name != "__init__":
+ all_methods += 1
+ return all_methods
+
+
+class MisdesignChecker(BaseChecker):
+ """checks for sign of poor/misdesign:
+ * number of methods, attributes, local variables...
+ * size, complexity of functions, methods
+ """
+
+ __implements__ = (IAstroidChecker,)
+
+ # configuration section name
+ name = "design"
+ # messages
+ msgs = MSGS
+ priority = -2
+ # configuration options
+ options = (
+ (
+ "max-args",
+ {
+ "default": 5,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Maximum number of arguments for function / method.",
+ },
+ ),
+ (
+ "max-locals",
+ {
+ "default": 15,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Maximum number of locals for function / method body.",
+ },
+ ),
+ (
+ "max-returns",
+ {
+ "default": 6,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Maximum number of return / yield for function / "
+ "method body.",
+ },
+ ),
+ (
+ "max-branches",
+ {
+ "default": 12,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Maximum number of branch for function / method body.",
+ },
+ ),
+ (
+ "max-statements",
+ {
+ "default": 50,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Maximum number of statements in function / method " "body.",
+ },
+ ),
+ (
+ "max-parents",
+ {
+ "default": 7,
+ "type": "int",
+ "metavar": "<num>",
+ "help": "Maximum number of parents for a class (see R0901).",
+ },
+ ),
+ (
+ "max-attributes",
+ {
+ "default": 7,
+ "type": "int",
+ "metavar": "<num>",
+ "help": "Maximum number of attributes for a class \
+(see R0902).",
+ },
+ ),
+ (
+ "min-public-methods",
+ {
+ "default": 2,
+ "type": "int",
+ "metavar": "<num>",
+ "help": "Minimum number of public methods for a class \
+(see R0903).",
+ },
+ ),
+ (
+ "max-public-methods",
+ {
+ "default": 20,
+ "type": "int",
+ "metavar": "<num>",
+ "help": "Maximum number of public methods for a class \
+(see R0904).",
+ },
+ ),
+ (
+ "max-bool-expr",
+ {
+ "default": 5,
+ "type": "int",
+ "metavar": "<num>",
+ "help": "Maximum number of boolean expressions in an if "
+ "statement (see R0916).",
+ },
+ ),
+ )
+
+ def __init__(self, linter=None):
+ BaseChecker.__init__(self, linter)
+ self.stats = None
+ self._returns = None
+ self._branches = None
+ self._stmts = None
+
+ def open(self):
+ """initialize visit variables"""
+ self.stats = self.linter.add_stats()
+ self._returns = []
+ self._branches = defaultdict(int)
+ self._stmts = []
+
+ def _inc_all_stmts(self, amount):
+ for i in range(len(self._stmts)):
+ self._stmts[i] += amount
+
+ @decorators.cachedproperty
+ def _ignored_argument_names(self):
+ return utils.get_global_option(self, "ignored-argument-names", default=None)
+
+ @check_messages(
+ "too-many-ancestors",
+ "too-many-instance-attributes",
+ "too-few-public-methods",
+ "too-many-public-methods",
+ )
+ def visit_classdef(self, node):
+ """check size of inheritance hierarchy and number of instance attributes
+ """
+ nb_parents = len(list(node.ancestors()))
+ if nb_parents > self.config.max_parents:
+ self.add_message(
+ "too-many-ancestors",
+ node=node,
+ args=(nb_parents, self.config.max_parents),
+ )
+
+ if len(node.instance_attrs) > self.config.max_attributes:
+ self.add_message(
+ "too-many-instance-attributes",
+ node=node,
+ args=(len(node.instance_attrs), self.config.max_attributes),
+ )
+
+ @check_messages("too-few-public-methods", "too-many-public-methods")
+ def leave_classdef(self, node):
+ """check number of public methods"""
+ my_methods = sum(
+ 1 for method in node.mymethods() if not method.name.startswith("_")
+ )
+
+ # Does the class contain less than n public methods ?
+ # This checks only the methods defined in the current class,
+ # since the user might not have control over the classes
+ # from the ancestors. It avoids some false positives
+ # for classes such as unittest.TestCase, which provides
+ # a lot of assert methods. It doesn't make sense to warn
+ # when the user subclasses TestCase to add his own tests.
+ if my_methods > self.config.max_public_methods:
+ self.add_message(
+ "too-many-public-methods",
+ node=node,
+ args=(my_methods, self.config.max_public_methods),
+ )
+
+ # Stop here for exception, metaclass, interface classes and other
+ # classes for which we don't need to count the methods.
+ if node.type != "class" or _is_exempt_from_public_methods(node):
+ return
+
+ # Does the class contain more than n public methods ?
+ # This checks all the methods defined by ancestors and
+ # by the current class.
+ all_methods = _count_methods_in_class(node)
+ if all_methods < self.config.min_public_methods:
+ self.add_message(
+ "too-few-public-methods",
+ node=node,
+ args=(all_methods, self.config.min_public_methods),
+ )
+
+ @check_messages(
+ "too-many-return-statements",
+ "too-many-branches",
+ "too-many-arguments",
+ "too-many-locals",
+ "too-many-statements",
+ "keyword-arg-before-vararg",
+ )
+ def visit_functiondef(self, node):
+ """check function name, docstring, arguments, redefinition,
+ variable names, max locals
+ """
+ # init branch and returns counters
+ self._returns.append(0)
+ # check number of arguments
+ args = node.args.args
+ ignored_argument_names = self._ignored_argument_names
+ if args is not None:
+ ignored_args_num = 0
+ if ignored_argument_names:
+ ignored_args_num = sum(
+ 1 for arg in args if ignored_argument_names.match(arg.name)
+ )
+
+ argnum = len(args) - ignored_args_num
+ if argnum > self.config.max_args:
+ self.add_message(
+ "too-many-arguments",
+ node=node,
+ args=(len(args), self.config.max_args),
+ )
+ else:
+ ignored_args_num = 0
+ # check number of local variables
+ locnum = len(node.locals) - ignored_args_num
+ if locnum > self.config.max_locals:
+ self.add_message(
+ "too-many-locals", node=node, args=(locnum, self.config.max_locals)
+ )
+ # init new statements counter
+ self._stmts.append(1)
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ @check_messages(
+ "too-many-return-statements",
+ "too-many-branches",
+ "too-many-arguments",
+ "too-many-locals",
+ "too-many-statements",
+ )
+ def leave_functiondef(self, node):
+ """most of the work is done here on close:
+ checks for max returns, branch, return in __init__
+ """
+ returns = self._returns.pop()
+ if returns > self.config.max_returns:
+ self.add_message(
+ "too-many-return-statements",
+ node=node,
+ args=(returns, self.config.max_returns),
+ )
+ branches = self._branches[node]
+ if branches > self.config.max_branches:
+ self.add_message(
+ "too-many-branches",
+ node=node,
+ args=(branches, self.config.max_branches),
+ )
+ # check number of statements
+ stmts = self._stmts.pop()
+ if stmts > self.config.max_statements:
+ self.add_message(
+ "too-many-statements",
+ node=node,
+ args=(stmts, self.config.max_statements),
+ )
+
+ leave_asyncfunctiondef = leave_functiondef
+
+ def visit_return(self, _):
+ """count number of returns"""
+ if not self._returns:
+ return # return outside function, reported by the base checker
+ self._returns[-1] += 1
+
+ def visit_default(self, node):
+ """default visit method -> increments the statements counter if
+ necessary
+ """
+ if node.is_statement:
+ self._inc_all_stmts(1)
+
+ def visit_tryexcept(self, node):
+ """increments the branches counter"""
+ branches = len(node.handlers)
+ if node.orelse:
+ branches += 1
+ self._inc_branch(node, branches)
+ self._inc_all_stmts(branches)
+
+ def visit_tryfinally(self, node):
+ """increments the branches counter"""
+ self._inc_branch(node, 2)
+ self._inc_all_stmts(2)
+
+ @check_messages("too-many-boolean-expressions")
+ def visit_if(self, node):
+ """increments the branches counter and checks boolean expressions"""
+ self._check_boolean_expressions(node)
+ branches = 1
+ # don't double count If nodes coming from some 'elif'
+ if node.orelse and (len(node.orelse) > 1 or not isinstance(node.orelse[0], If)):
+ branches += 1
+ self._inc_branch(node, branches)
+ self._inc_all_stmts(branches)
+
+ def _check_boolean_expressions(self, node):
+ """Go through "if" node `node` and counts its boolean expressions
+
+ if the "if" node test is a BoolOp node
+ """
+ condition = node.test
+ if not isinstance(condition, BoolOp):
+ return
+ nb_bool_expr = _count_boolean_expressions(condition)
+ if nb_bool_expr > self.config.max_bool_expr:
+ self.add_message(
+ "too-many-boolean-expressions",
+ node=condition,
+ args=(nb_bool_expr, self.config.max_bool_expr),
+ )
+
+ def visit_while(self, node):
+ """increments the branches counter"""
+ branches = 1
+ if node.orelse:
+ branches += 1
+ self._inc_branch(node, branches)
+
+ visit_for = visit_while
+
+ def _inc_branch(self, node, branchesnum=1):
+ """increments the branches counter"""
+ self._branches[node.scope()] += branchesnum
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(MisdesignChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/exceptions.py b/venv/Lib/site-packages/pylint/checkers/exceptions.py
new file mode 100644
index 0000000..360e1d1
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/exceptions.py
@@ -0,0 +1,546 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2011-2014 Google, Inc.
+# Copyright (c) 2012 Tim Hatch <tim@timhatch.com>
+# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Rene Zhang <rz99@cornell.edu>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Steven Myint <hg@stevenmyint.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Erik <erik.eriksson@yahoo.com>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 Martin von Gagern <gagern@google.com>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Alexander Todorov <atodorov@otb.bg>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Checks for various exception related errors."""
+import builtins
+import inspect
+import typing
+
+import astroid
+from astroid.node_classes import NodeNG
+
+from pylint import checkers, interfaces
+from pylint.checkers import utils
+
+
+def _builtin_exceptions():
+ def predicate(obj):
+ return isinstance(obj, type) and issubclass(obj, BaseException)
+
+ members = inspect.getmembers(builtins, predicate)
+ return {exc.__name__ for (_, exc) in members}
+
+
+def _annotated_unpack_infer(stmt, context=None):
+ """
+ Recursively generate nodes inferred by the given statement.
+ If the inferred value is a list or a tuple, recurse on the elements.
+ Returns an iterator which yields tuples in the format
+ ('original node', 'inferred node').
+ """
+ if isinstance(stmt, (astroid.List, astroid.Tuple)):
+ for elt in stmt.elts:
+ inferred = utils.safe_infer(elt)
+ if inferred and inferred is not astroid.Uninferable:
+ yield elt, inferred
+ return
+ for inferred in stmt.infer(context):
+ if inferred is astroid.Uninferable:
+ continue
+ yield stmt, inferred
+
+
+def _is_raising(body: typing.List) -> bool:
+ """Return true if the given statement node raise an exception"""
+ for node in body:
+ if isinstance(node, astroid.Raise):
+ return True
+ return False
+
+
+OVERGENERAL_EXCEPTIONS = ("BaseException", "Exception")
+BUILTINS_NAME = builtins.__name__
+
+MSGS = {
+ "E0701": (
+ "Bad except clauses order (%s)",
+ "bad-except-order",
+ "Used when except clauses are not in the correct order (from the "
+ "more specific to the more generic). If you don't fix the order, "
+ "some exceptions may not be caught by the most specific handler.",
+ ),
+ "E0702": (
+ "Raising %s while only classes or instances are allowed",
+ "raising-bad-type",
+ "Used when something which is neither a class, an instance or a "
+ "string is raised (i.e. a `TypeError` will be raised).",
+ ),
+ "E0703": (
+ "Exception context set to something which is not an exception, nor None",
+ "bad-exception-context",
+ 'Used when using the syntax "raise ... from ...", '
+ "where the exception context is not an exception, "
+ "nor None.",
+ ),
+ "E0704": (
+ "The raise statement is not inside an except clause",
+ "misplaced-bare-raise",
+ "Used when a bare raise is not used inside an except clause. "
+ "This generates an error, since there are no active exceptions "
+ "to be reraised. An exception to this rule is represented by "
+ "a bare raise inside a finally clause, which might work, as long "
+ "as an exception is raised inside the try block, but it is "
+ "nevertheless a code smell that must not be relied upon.",
+ ),
+ "E0710": (
+ "Raising a new style class which doesn't inherit from BaseException",
+ "raising-non-exception",
+ "Used when a new style class which doesn't inherit from "
+ "BaseException is raised.",
+ ),
+ "E0711": (
+ "NotImplemented raised - should raise NotImplementedError",
+ "notimplemented-raised",
+ "Used when NotImplemented is raised instead of NotImplementedError",
+ ),
+ "E0712": (
+ "Catching an exception which doesn't inherit from Exception: %s",
+ "catching-non-exception",
+ "Used when a class which doesn't inherit from "
+ "Exception is used as an exception in an except clause.",
+ ),
+ "W0702": (
+ "No exception type(s) specified",
+ "bare-except",
+ "Used when an except clause doesn't specify exceptions type to catch.",
+ ),
+ "W0703": (
+ "Catching too general exception %s",
+ "broad-except",
+ "Used when an except catches a too general exception, "
+ "possibly burying unrelated errors.",
+ ),
+ "W0705": (
+ "Catching previously caught exception type %s",
+ "duplicate-except",
+ "Used when an except catches a type that was already caught by "
+ "a previous handler.",
+ ),
+ "W0706": (
+ "The except handler raises immediately",
+ "try-except-raise",
+ "Used when an except handler uses raise as its first or only "
+ "operator. This is useless because it raises back the exception "
+ "immediately. Remove the raise operator or the entire "
+ "try-except-raise block!",
+ ),
+ "W0711": (
+ 'Exception to catch is the result of a binary "%s" operation',
+ "binary-op-exception",
+ "Used when the exception to catch is of the form "
+ '"except A or B:". If intending to catch multiple, '
+ 'rewrite as "except (A, B):"',
+ ),
+ "W0715": (
+ "Exception arguments suggest string formatting might be intended",
+ "raising-format-tuple",
+ "Used when passing multiple arguments to an exception "
+ "constructor, the first of them a string literal containing what "
+ "appears to be placeholders intended for formatting",
+ ),
+ "W0716": (
+ "Invalid exception operation. %s",
+ "wrong-exception-operation",
+ "Used when an operation is done against an exception, but the operation "
+ "is not valid for the exception in question. Usually emitted when having "
+ "binary operations between exceptions in except handlers.",
+ ),
+}
+
+
+class BaseVisitor:
+ """Base class for visitors defined in this module."""
+
+ def __init__(self, checker, node):
+ self._checker = checker
+ self._node = node
+
+ def visit(self, node):
+ name = node.__class__.__name__.lower()
+ dispatch_meth = getattr(self, "visit_" + name, None)
+ if dispatch_meth:
+ dispatch_meth(node)
+ else:
+ self.visit_default(node)
+
+ def visit_default(self, node): # pylint: disable=unused-argument
+ """Default implementation for all the nodes."""
+
+
+class ExceptionRaiseRefVisitor(BaseVisitor):
+ """Visit references (anything that is not an AST leaf)."""
+
+ def visit_name(self, name):
+ if name.name == "NotImplemented":
+ self._checker.add_message("notimplemented-raised", node=self._node)
+
+ def visit_call(self, call):
+ if isinstance(call.func, astroid.Name):
+ self.visit_name(call.func)
+ if (
+ len(call.args) > 1
+ and isinstance(call.args[0], astroid.Const)
+ and isinstance(call.args[0].value, str)
+ ):
+ msg = call.args[0].value
+ if "%" in msg or ("{" in msg and "}" in msg):
+ self._checker.add_message("raising-format-tuple", node=self._node)
+
+
+class ExceptionRaiseLeafVisitor(BaseVisitor):
+ """Visitor for handling leaf kinds of a raise value."""
+
+ def visit_const(self, const):
+ if not isinstance(const.value, str):
+ # raising-string will be emitted from python3 porting checker.
+ self._checker.add_message(
+ "raising-bad-type", node=self._node, args=const.value.__class__.__name__
+ )
+
+ def visit_instance(self, instance):
+ # pylint: disable=protected-access
+ cls = instance._proxied
+ self.visit_classdef(cls)
+
+ # Exception instances have a particular class type
+ visit_exceptioninstance = visit_instance
+
+ def visit_classdef(self, cls):
+ if not utils.inherit_from_std_ex(cls) and utils.has_known_bases(cls):
+ if cls.newstyle:
+ self._checker.add_message("raising-non-exception", node=self._node)
+
+ def visit_tuple(self, _):
+ self._checker.add_message("raising-bad-type", node=self._node, args="tuple")
+
+ def visit_default(self, node):
+ name = getattr(node, "name", node.__class__.__name__)
+ self._checker.add_message("raising-bad-type", node=self._node, args=name)
+
+
+class ExceptionsChecker(checkers.BaseChecker):
+ """Exception related checks."""
+
+ __implements__ = interfaces.IAstroidChecker
+
+ name = "exceptions"
+ msgs = MSGS
+ priority = -4
+ options = (
+ (
+ "overgeneral-exceptions",
+ {
+ "default": OVERGENERAL_EXCEPTIONS,
+ "type": "csv",
+ "metavar": "<comma-separated class names>",
+ "help": "Exceptions that will emit a warning "
+ 'when being caught. Defaults to "%s".'
+ % (", ".join(OVERGENERAL_EXCEPTIONS),),
+ },
+ ),
+ )
+
+ def open(self):
+ self._builtin_exceptions = _builtin_exceptions()
+ super(ExceptionsChecker, self).open()
+
+ @utils.check_messages(
+ "misplaced-bare-raise",
+ "raising-bad-type",
+ "raising-non-exception",
+ "notimplemented-raised",
+ "bad-exception-context",
+ "raising-format-tuple",
+ )
+ def visit_raise(self, node):
+ if node.exc is None:
+ self._check_misplaced_bare_raise(node)
+ return
+
+ if node.cause:
+ self._check_bad_exception_context(node)
+
+ expr = node.exc
+ ExceptionRaiseRefVisitor(self, node).visit(expr)
+
+ try:
+ inferred_value = expr.inferred()[-1]
+ except astroid.InferenceError:
+ pass
+ else:
+ if inferred_value:
+ ExceptionRaiseLeafVisitor(self, node).visit(inferred_value)
+
+ def _check_misplaced_bare_raise(self, node):
+ # Filter out if it's present in __exit__.
+ scope = node.scope()
+ if (
+ isinstance(scope, astroid.FunctionDef)
+ and scope.is_method()
+ and scope.name == "__exit__"
+ ):
+ return
+
+ current = node
+ # Stop when a new scope is generated or when the raise
+ # statement is found inside a TryFinally.
+ ignores = (astroid.ExceptHandler, astroid.FunctionDef)
+ while current and not isinstance(current.parent, ignores):
+ current = current.parent
+
+ expected = (astroid.ExceptHandler,)
+ if not current or not isinstance(current.parent, expected):
+ self.add_message("misplaced-bare-raise", node=node)
+
+ def _check_bad_exception_context(self, node):
+ """Verify that the exception context is properly set.
+
+ An exception context can be only `None` or an exception.
+ """
+ cause = utils.safe_infer(node.cause)
+ if cause in (astroid.Uninferable, None):
+ return
+
+ if isinstance(cause, astroid.Const):
+ if cause.value is not None:
+ self.add_message("bad-exception-context", node=node)
+ elif not isinstance(cause, astroid.ClassDef) and not utils.inherit_from_std_ex(
+ cause
+ ):
+ self.add_message("bad-exception-context", node=node)
+
+ def _check_catching_non_exception(self, handler, exc, part):
+ if isinstance(exc, astroid.Tuple):
+ # Check if it is a tuple of exceptions.
+ inferred = [utils.safe_infer(elt) for elt in exc.elts]
+ if any(node is astroid.Uninferable for node in inferred):
+ # Don't emit if we don't know every component.
+ return
+ if all(
+ node
+ and (utils.inherit_from_std_ex(node) or not utils.has_known_bases(node))
+ for node in inferred
+ ):
+ return
+
+ if not isinstance(exc, astroid.ClassDef):
+ # Don't emit the warning if the inferred stmt
+ # is None, but the exception handler is something else,
+ # maybe it was redefined.
+ if isinstance(exc, astroid.Const) and exc.value is None:
+ if (
+ isinstance(handler.type, astroid.Const)
+ and handler.type.value is None
+ ) or handler.type.parent_of(exc):
+ # If the exception handler catches None or
+ # the exception component, which is None, is
+ # defined by the entire exception handler, then
+ # emit a warning.
+ self.add_message(
+ "catching-non-exception",
+ node=handler.type,
+ args=(part.as_string(),),
+ )
+ else:
+ self.add_message(
+ "catching-non-exception",
+ node=handler.type,
+ args=(part.as_string(),),
+ )
+ return
+
+ if (
+ not utils.inherit_from_std_ex(exc)
+ and exc.name not in self._builtin_exceptions
+ ):
+ if utils.has_known_bases(exc):
+ self.add_message(
+ "catching-non-exception", node=handler.type, args=(exc.name,)
+ )
+
+ def _check_try_except_raise(self, node):
+ def gather_exceptions_from_handler(
+ handler
+ ) -> typing.Optional[typing.List[NodeNG]]:
+ exceptions = [] # type: typing.List[NodeNG]
+ if handler.type:
+ exceptions_in_handler = utils.safe_infer(handler.type)
+ if isinstance(exceptions_in_handler, astroid.Tuple):
+ exceptions = list(
+ {
+ exception
+ for exception in exceptions_in_handler.elts
+ if isinstance(exception, astroid.Name)
+ }
+ )
+ elif exceptions_in_handler:
+ exceptions = [exceptions_in_handler]
+ else:
+ # Break when we cannot infer anything reliably.
+ return None
+ return exceptions
+
+ bare_raise = False
+ handler_having_bare_raise = None
+ excs_in_bare_handler = []
+ for handler in node.handlers:
+ if bare_raise:
+ # check that subsequent handler is not parent of handler which had bare raise.
+ # since utils.safe_infer can fail for bare except, check it before.
+ # also break early if bare except is followed by bare except.
+
+ excs_in_current_handler = gather_exceptions_from_handler(handler)
+
+ if not excs_in_current_handler:
+ bare_raise = False
+ break
+ if excs_in_bare_handler is None:
+ # It can be `None` when the inference failed
+ break
+
+ for exc_in_current_handler in excs_in_current_handler:
+ inferred_current = utils.safe_infer(exc_in_current_handler)
+ if any(
+ utils.is_subclass_of(
+ utils.safe_infer(exc_in_bare_handler), inferred_current
+ )
+ for exc_in_bare_handler in excs_in_bare_handler
+ ):
+ bare_raise = False
+ break
+
+ # `raise` as the first operator inside the except handler
+ if _is_raising([handler.body[0]]):
+ # flags when there is a bare raise
+ if handler.body[0].exc is None:
+ bare_raise = True
+ handler_having_bare_raise = handler
+ excs_in_bare_handler = gather_exceptions_from_handler(handler)
+ else:
+ if bare_raise:
+ self.add_message("try-except-raise", node=handler_having_bare_raise)
+
+ @utils.check_messages("wrong-exception-operation")
+ def visit_binop(self, node):
+ if isinstance(node.parent, astroid.ExceptHandler):
+ # except (V | A)
+ suggestion = "Did you mean '(%s, %s)' instead?" % (
+ node.left.as_string(),
+ node.right.as_string(),
+ )
+ self.add_message("wrong-exception-operation", node=node, args=(suggestion,))
+
+ @utils.check_messages("wrong-exception-operation")
+ def visit_compare(self, node):
+ if isinstance(node.parent, astroid.ExceptHandler):
+ # except (V < A)
+ suggestion = "Did you mean '(%s, %s)' instead?" % (
+ node.left.as_string(),
+ ", ".join(operand.as_string() for _, operand in node.ops),
+ )
+ self.add_message("wrong-exception-operation", node=node, args=(suggestion,))
+
+ @utils.check_messages(
+ "bare-except",
+ "broad-except",
+ "try-except-raise",
+ "binary-op-exception",
+ "bad-except-order",
+ "catching-non-exception",
+ "duplicate-except",
+ )
+ def visit_tryexcept(self, node):
+ """check for empty except"""
+ self._check_try_except_raise(node)
+ exceptions_classes = []
+ nb_handlers = len(node.handlers)
+ for index, handler in enumerate(node.handlers):
+ if handler.type is None:
+ if not _is_raising(handler.body):
+ self.add_message("bare-except", node=handler)
+
+ # check if an "except:" is followed by some other
+ # except
+ if index < (nb_handlers - 1):
+ msg = "empty except clause should always appear last"
+ self.add_message("bad-except-order", node=node, args=msg)
+
+ elif isinstance(handler.type, astroid.BoolOp):
+ self.add_message(
+ "binary-op-exception", node=handler, args=handler.type.op
+ )
+ else:
+ try:
+ excs = list(_annotated_unpack_infer(handler.type))
+ except astroid.InferenceError:
+ continue
+
+ for part, exc in excs:
+ if exc is astroid.Uninferable:
+ continue
+ if isinstance(exc, astroid.Instance) and utils.inherit_from_std_ex(
+ exc
+ ):
+ # pylint: disable=protected-access
+ exc = exc._proxied
+
+ self._check_catching_non_exception(handler, exc, part)
+
+ if not isinstance(exc, astroid.ClassDef):
+ continue
+
+ exc_ancestors = [
+ anc
+ for anc in exc.ancestors()
+ if isinstance(anc, astroid.ClassDef)
+ ]
+
+ for previous_exc in exceptions_classes:
+ if previous_exc in exc_ancestors:
+ msg = "%s is an ancestor class of %s" % (
+ previous_exc.name,
+ exc.name,
+ )
+ self.add_message(
+ "bad-except-order", node=handler.type, args=msg
+ )
+ if (
+ exc.name in self.config.overgeneral_exceptions
+ and exc.root().name == utils.EXCEPTIONS_MODULE
+ and not _is_raising(handler.body)
+ ):
+ self.add_message(
+ "broad-except", args=exc.name, node=handler.type
+ )
+
+ if exc in exceptions_classes:
+ self.add_message(
+ "duplicate-except", args=exc.name, node=handler.type
+ )
+
+ exceptions_classes += [exc for _, exc in excs]
+
+
+def register(linter):
+ """required method to auto register this checker"""
+ linter.register_checker(ExceptionsChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/format.py b/venv/Lib/site-packages/pylint/checkers/format.py
new file mode 100644
index 0000000..c4cad31
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/format.py
@@ -0,0 +1,1332 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012-2015 Google, Inc.
+# Copyright (c) 2013 moxian <aleftmail@inbox.ru>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 frost-nzcr4 <frost.nzcr4@jagmort.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Mike Frysinger <vapier@gentoo.org>
+# Copyright (c) 2015 Fabio Natali <me@fabionatali.com>
+# Copyright (c) 2015 Harut <yes@harutune.name>
+# Copyright (c) 2015 Mihai Balint <balint.mihai@gmail.com>
+# Copyright (c) 2015 Pavel Roskin <proski@gnu.org>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Petr Pulc <petrpulc@gmail.com>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Krzysztof Czapla <k.czapla68@gmail.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 James M. Allen <james.m.allen@gmail.com>
+# Copyright (c) 2017 vinnyrose <vinnyrose@users.noreply.github.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.guinta@protonmail.com>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2018 Fureigh <rhys.fureigh@gsa.gov>
+# Copyright (c) 2018 Pierre Sassoulas <pierre.sassoulas@wisebim.fr>
+# Copyright (c) 2018 Andreas Freimuth <andreas.freimuth@united-bits.de>
+# Copyright (c) 2018 Jakub Wilk <jwilk@jwilk.net>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Python code format's checker.
+
+By default try to follow Guido's style guide :
+
+https://www.python.org/doc/essays/styleguide/
+
+Some parts of the process_token method is based from The Tab Nanny std module.
+"""
+
+import keyword
+import tokenize
+from functools import reduce # pylint: disable=redefined-builtin
+
+from astroid import nodes
+
+from pylint.checkers import BaseTokenChecker
+from pylint.checkers.utils import check_messages
+from pylint.constants import OPTION_RGX, WarningScope
+from pylint.interfaces import IAstroidChecker, IRawChecker, ITokenChecker
+
+_ASYNC_TOKEN = "async"
+_CONTINUATION_BLOCK_OPENERS = [
+ "elif",
+ "except",
+ "for",
+ "if",
+ "while",
+ "def",
+ "class",
+ "with",
+]
+_KEYWORD_TOKENS = [
+ "assert",
+ "del",
+ "elif",
+ "except",
+ "for",
+ "if",
+ "in",
+ "not",
+ "raise",
+ "return",
+ "while",
+ "yield",
+ "with",
+]
+
+_SPACED_OPERATORS = [
+ "==",
+ "<",
+ ">",
+ "!=",
+ "<>",
+ "<=",
+ ">=",
+ "+=",
+ "-=",
+ "*=",
+ "**=",
+ "/=",
+ "//=",
+ "&=",
+ "|=",
+ "^=",
+ "%=",
+ ">>=",
+ "<<=",
+]
+_OPENING_BRACKETS = ["(", "[", "{"]
+_CLOSING_BRACKETS = [")", "]", "}"]
+_TAB_LENGTH = 8
+
+_EOL = frozenset([tokenize.NEWLINE, tokenize.NL, tokenize.COMMENT])
+_JUNK_TOKENS = (tokenize.COMMENT, tokenize.NL)
+
+# Whitespace checking policy constants
+_MUST = 0
+_MUST_NOT = 1
+_IGNORE = 2
+
+# Whitespace checking config constants
+_DICT_SEPARATOR = "dict-separator"
+_TRAILING_COMMA = "trailing-comma"
+_EMPTY_LINE = "empty-line"
+_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR, _EMPTY_LINE]
+_DEFAULT_NO_SPACE_CHECK_CHOICES = [_TRAILING_COMMA, _DICT_SEPARATOR]
+
+MSGS = {
+ "C0301": (
+ "Line too long (%s/%s)",
+ "line-too-long",
+ "Used when a line is longer than a given number of characters.",
+ ),
+ "C0302": (
+ "Too many lines in module (%s/%s)", # was W0302
+ "too-many-lines",
+ "Used when a module has too many lines, reducing its readability.",
+ ),
+ "C0303": (
+ "Trailing whitespace",
+ "trailing-whitespace",
+ "Used when there is whitespace between the end of a line and the newline.",
+ ),
+ "C0304": (
+ "Final newline missing",
+ "missing-final-newline",
+ "Used when the last line in a file is missing a newline.",
+ ),
+ "C0305": (
+ "Trailing newlines",
+ "trailing-newlines",
+ "Used when there are trailing blank lines in a file.",
+ ),
+ "W0311": (
+ "Bad indentation. Found %s %s, expected %s",
+ "bad-indentation",
+ "Used when an unexpected number of indentation's tabulations or "
+ "spaces has been found.",
+ ),
+ "C0330": ("Wrong %s indentation%s%s.\n%s%s", "bad-continuation", "TODO"),
+ "W0312": (
+ "Found indentation with %ss instead of %ss",
+ "mixed-indentation",
+ "Used when there are some mixed tabs and spaces in a module.",
+ ),
+ "W0301": (
+ "Unnecessary semicolon", # was W0106
+ "unnecessary-semicolon",
+ 'Used when a statement is ended by a semi-colon (";"), which '
+ "isn't necessary (that's python, not C ;).",
+ ),
+ "C0321": (
+ "More than one statement on a single line",
+ "multiple-statements",
+ "Used when more than on statement are found on the same line.",
+ {"scope": WarningScope.NODE},
+ ),
+ "C0325": (
+ "Unnecessary parens after %r keyword",
+ "superfluous-parens",
+ "Used when a single item in parentheses follows an if, for, or "
+ "other keyword.",
+ ),
+ "C0326": (
+ "%s space %s %s %s\n%s",
+ "bad-whitespace",
+ (
+ "Used when a wrong number of spaces is used around an operator, "
+ "bracket or block opener."
+ ),
+ {
+ "old_names": [
+ ("C0323", "no-space-after-operator"),
+ ("C0324", "no-space-after-comma"),
+ ("C0322", "no-space-before-operator"),
+ ]
+ },
+ ),
+ "C0327": (
+ "Mixed line endings LF and CRLF",
+ "mixed-line-endings",
+ "Used when there are mixed (LF and CRLF) newline signs in a file.",
+ ),
+ "C0328": (
+ "Unexpected line ending format. There is '%s' while it should be '%s'.",
+ "unexpected-line-ending-format",
+ "Used when there is different newline than expected.",
+ ),
+}
+
+
+def _underline_token(token):
+ length = token[3][1] - token[2][1]
+ offset = token[2][1]
+ referenced_line = token[4]
+ # If the referenced line does not end with a newline char, fix it
+ if referenced_line[-1] != "\n":
+ referenced_line += "\n"
+ return referenced_line + (" " * offset) + ("^" * length)
+
+
+def _column_distance(token1, token2):
+ if token1 == token2:
+ return 0
+ if token2[3] < token1[3]:
+ token1, token2 = token2, token1
+ if token1[3][0] != token2[2][0]:
+ return None
+ return token2[2][1] - token1[3][1]
+
+
+def _last_token_on_line_is(tokens, line_end, token):
+ return (
+ line_end > 0
+ and tokens.token(line_end - 1) == token
+ or line_end > 1
+ and tokens.token(line_end - 2) == token
+ and tokens.type(line_end - 1) == tokenize.COMMENT
+ )
+
+
+def _token_followed_by_eol(tokens, position):
+ return (
+ tokens.type(position + 1) == tokenize.NL
+ or tokens.type(position + 1) == tokenize.COMMENT
+ and tokens.type(position + 2) == tokenize.NL
+ )
+
+
+def _get_indent_string(line):
+ """Return the indention string of the given line."""
+ result = ""
+ for char in line:
+ if char in " \t":
+ result += char
+ else:
+ break
+ return result
+
+
+def _get_indent_length(line):
+ """Return the length of the indentation on the given token's line."""
+ result = 0
+ for char in line:
+ if char == " ":
+ result += 1
+ elif char == "\t":
+ result += _TAB_LENGTH
+ else:
+ break
+ return result
+
+
+def _get_indent_hint_line(bar_positions, bad_position):
+ """Return a line with |s for each of the positions in the given lists."""
+ if not bar_positions:
+ return "", ""
+
+ bar_positions = [_get_indent_length(indent) for indent in bar_positions]
+ bad_position = _get_indent_length(bad_position)
+ delta_message = ""
+ markers = [(pos, "|") for pos in bar_positions]
+ if len(markers) == 1:
+ # if we have only one marker we'll provide an extra hint on how to fix
+ expected_position = markers[0][0]
+ delta = abs(expected_position - bad_position)
+ direction = "add" if expected_position > bad_position else "remove"
+ delta_message = _CONTINUATION_HINT_MESSAGE % (
+ direction,
+ delta,
+ "s" if delta > 1 else "",
+ )
+ markers.append((bad_position, "^"))
+ markers.sort()
+ line = [" "] * (markers[-1][0] + 1)
+ for position, marker in markers:
+ line[position] = marker
+ return "".join(line), delta_message
+
+
+class _ContinuedIndent:
+ __slots__ = (
+ "valid_outdent_strings",
+ "valid_continuation_strings",
+ "context_type",
+ "token",
+ "position",
+ )
+
+ def __init__(
+ self,
+ context_type,
+ token,
+ position,
+ valid_outdent_strings,
+ valid_continuation_strings,
+ ):
+ self.valid_outdent_strings = valid_outdent_strings
+ self.valid_continuation_strings = valid_continuation_strings
+ self.context_type = context_type
+ self.position = position
+ self.token = token
+
+
+# The contexts for hanging indents.
+# A hanging indented dictionary value after :
+HANGING_DICT_VALUE = "dict-value"
+# Hanging indentation in an expression.
+HANGING = "hanging"
+# Hanging indentation in a block header.
+HANGING_BLOCK = "hanging-block"
+# Continued indentation inside an expression.
+CONTINUED = "continued"
+# Continued indentation in a block header.
+CONTINUED_BLOCK = "continued-block"
+
+SINGLE_LINE = "single"
+WITH_BODY = "multi"
+
+_CONTINUATION_MSG_PARTS = {
+ HANGING_DICT_VALUE: ("hanging", " in dict value"),
+ HANGING: ("hanging", ""),
+ HANGING_BLOCK: ("hanging", " before block"),
+ CONTINUED: ("continued", ""),
+ CONTINUED_BLOCK: ("continued", " before block"),
+}
+
+_CONTINUATION_HINT_MESSAGE = " (%s %d space%s)" # Ex: (remove 2 spaces)
+
+
+def _Indentations(*args):
+ """Valid indentation strings for a continued line."""
+ return {a: None for a in args}
+
+
+def _BeforeBlockIndentations(single, with_body):
+ """Valid alternative indentation strings for continued lines before blocks.
+
+ :param int single: Valid indentation string for statements on a single logical line.
+ :param int with_body: Valid indentation string for statements on several lines.
+
+ :returns: A dictionary mapping indent offsets to a string representing
+ whether the indent if for a line or block.
+ :rtype: dict
+ """
+ return {single: SINGLE_LINE, with_body: WITH_BODY}
+
+
+class TokenWrapper:
+ """A wrapper for readable access to token information."""
+
+ def __init__(self, tokens):
+ self._tokens = tokens
+
+ def token(self, idx):
+ return self._tokens[idx][1]
+
+ def type(self, idx):
+ return self._tokens[idx][0]
+
+ def start_line(self, idx):
+ return self._tokens[idx][2][0]
+
+ def start_col(self, idx):
+ return self._tokens[idx][2][1]
+
+ def line(self, idx):
+ return self._tokens[idx][4]
+
+ def line_indent(self, idx):
+ """Get the string of TABs and Spaces used for indentation of the line of this token"""
+ return _get_indent_string(self.line(idx))
+
+ def token_indent(self, idx):
+ """Get an indentation string for hanging indentation, consisting of the line-indent plus
+ a number of spaces to fill up to the column of this token.
+
+ e.g. the token indent for foo
+ in "<TAB><TAB>print(foo)"
+ is "<TAB><TAB> "
+ """
+ line_indent = self.line_indent(idx)
+ return line_indent + " " * (self.start_col(idx) - len(line_indent))
+
+
+class ContinuedLineState:
+ """Tracker for continued indentation inside a logical line."""
+
+ def __init__(self, tokens, config):
+ self._line_start = -1
+ self._cont_stack = []
+ self._is_block_opener = False
+ self.retained_warnings = []
+ self._config = config
+ self._tokens = TokenWrapper(tokens)
+
+ @property
+ def has_content(self):
+ return bool(self._cont_stack)
+
+ @property
+ def _block_indent_string(self):
+ return self._config.indent_string.replace("\\t", "\t")
+
+ @property
+ def _continuation_string(self):
+ return self._block_indent_string[0] * self._config.indent_after_paren
+
+ @property
+ def _continuation_size(self):
+ return self._config.indent_after_paren
+
+ def handle_line_start(self, pos):
+ """Record the first non-junk token at the start of a line."""
+ if self._line_start > -1:
+ return
+
+ check_token_position = pos
+ if self._tokens.token(pos) == _ASYNC_TOKEN:
+ check_token_position += 1
+ self._is_block_opener = (
+ self._tokens.token(check_token_position) in _CONTINUATION_BLOCK_OPENERS
+ )
+ self._line_start = pos
+
+ def next_physical_line(self):
+ """Prepares the tracker for a new physical line (NL)."""
+ self._line_start = -1
+ self._is_block_opener = False
+
+ def next_logical_line(self):
+ """Prepares the tracker for a new logical line (NEWLINE).
+
+ A new logical line only starts with block indentation.
+ """
+ self.next_physical_line()
+ self.retained_warnings = []
+ self._cont_stack = []
+
+ def add_block_warning(self, token_position, state, valid_indentations):
+ self.retained_warnings.append((token_position, state, valid_indentations))
+
+ def get_valid_indentations(self, idx):
+ """Returns the valid offsets for the token at the given position."""
+ # The closing brace on a dict or the 'for' in a dict comprehension may
+ # reset two indent levels because the dict value is ended implicitly
+ stack_top = -1
+ if (
+ self._tokens.token(idx) in ("}", "for")
+ and self._cont_stack[-1].token == ":"
+ ):
+ stack_top = -2
+ indent = self._cont_stack[stack_top]
+ if self._tokens.token(idx) in _CLOSING_BRACKETS:
+ valid_indentations = indent.valid_outdent_strings
+ else:
+ valid_indentations = indent.valid_continuation_strings
+ return indent, valid_indentations.copy()
+
+ def _hanging_indent_after_bracket(self, bracket, position):
+ """Extracts indentation information for a hanging indent
+
+ Case of hanging indent after a bracket (including parenthesis)
+
+ :param str bracket: bracket in question
+ :param int position: Position of bracket in self._tokens
+
+ :returns: the state and valid positions for hanging indentation
+ :rtype: _ContinuedIndent
+ """
+ indentation = self._tokens.line_indent(position)
+ if (
+ self._is_block_opener
+ and self._continuation_string == self._block_indent_string
+ ):
+ return _ContinuedIndent(
+ HANGING_BLOCK,
+ bracket,
+ position,
+ _Indentations(indentation + self._continuation_string, indentation),
+ _BeforeBlockIndentations(
+ indentation + self._continuation_string,
+ indentation + self._continuation_string * 2,
+ ),
+ )
+ if bracket == ":":
+ # If the dict key was on the same line as the open brace, the new
+ # correct indent should be relative to the key instead of the
+ # current indent level
+ paren_align = self._cont_stack[-1].valid_outdent_strings
+ next_align = self._cont_stack[-1].valid_continuation_strings.copy()
+ next_align_keys = list(next_align.keys())
+ next_align[next_align_keys[0] + self._continuation_string] = True
+ # Note that the continuation of
+ # d = {
+ # 'a': 'b'
+ # 'c'
+ # }
+ # is handled by the special-casing for hanging continued string indents.
+ return _ContinuedIndent(
+ HANGING_DICT_VALUE, bracket, position, paren_align, next_align
+ )
+ return _ContinuedIndent(
+ HANGING,
+ bracket,
+ position,
+ _Indentations(indentation, indentation + self._continuation_string),
+ _Indentations(indentation + self._continuation_string),
+ )
+
+ def _continuation_inside_bracket(self, bracket, position):
+ """Extracts indentation information for a continued indent."""
+ indentation = self._tokens.line_indent(position)
+ token_indent = self._tokens.token_indent(position)
+ next_token_indent = self._tokens.token_indent(position + 1)
+ if (
+ self._is_block_opener
+ and next_token_indent == indentation + self._block_indent_string
+ ):
+ return _ContinuedIndent(
+ CONTINUED_BLOCK,
+ bracket,
+ position,
+ _Indentations(token_indent),
+ _BeforeBlockIndentations(
+ next_token_indent, next_token_indent + self._continuation_string
+ ),
+ )
+ return _ContinuedIndent(
+ CONTINUED,
+ bracket,
+ position,
+ _Indentations(token_indent, next_token_indent),
+ _Indentations(next_token_indent),
+ )
+
+ def pop_token(self):
+ self._cont_stack.pop()
+
+ def push_token(self, token, position):
+ """Pushes a new token for continued indentation on the stack.
+
+ Tokens that can modify continued indentation offsets are:
+ * opening brackets
+ * 'lambda'
+ * : inside dictionaries
+
+ push_token relies on the caller to filter out those
+ interesting tokens.
+
+ :param int token: The concrete token
+ :param int position: The position of the token in the stream.
+ """
+ if _token_followed_by_eol(self._tokens, position):
+ self._cont_stack.append(self._hanging_indent_after_bracket(token, position))
+ else:
+ self._cont_stack.append(self._continuation_inside_bracket(token, position))
+
+
+class FormatChecker(BaseTokenChecker):
+ """checks for :
+ * unauthorized constructions
+ * strict indentation
+ * line length
+ """
+
+ __implements__ = (ITokenChecker, IAstroidChecker, IRawChecker)
+
+ # configuration section name
+ name = "format"
+ # messages
+ msgs = MSGS
+ # configuration options
+ # for available dict keys/values see the optik parser 'add_option' method
+ options = (
+ (
+ "max-line-length",
+ {
+ "default": 100,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Maximum number of characters on a single line.",
+ },
+ ),
+ (
+ "ignore-long-lines",
+ {
+ "type": "regexp",
+ "metavar": "<regexp>",
+ "default": r"^\s*(# )?<?https?://\S+>?$",
+ "help": (
+ "Regexp for a line that is allowed to be longer than " "the limit."
+ ),
+ },
+ ),
+ (
+ "single-line-if-stmt",
+ {
+ "default": False,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": (
+ "Allow the body of an if to be on the same "
+ "line as the test if there is no else."
+ ),
+ },
+ ),
+ (
+ "single-line-class-stmt",
+ {
+ "default": False,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": (
+ "Allow the body of a class to be on the same "
+ "line as the declaration if body contains "
+ "single statement."
+ ),
+ },
+ ),
+ (
+ "no-space-check",
+ {
+ "default": ",".join(_DEFAULT_NO_SPACE_CHECK_CHOICES),
+ "metavar": ",".join(_NO_SPACE_CHECK_CHOICES),
+ "type": "multiple_choice",
+ "choices": _NO_SPACE_CHECK_CHOICES,
+ "help": (
+ "List of optional constructs for which whitespace "
+ "checking is disabled. "
+ "`" + _DICT_SEPARATOR + "` is used to allow tabulation "
+ "in dicts, etc.: {1 : 1,\\n222: 2}. "
+ "`" + _TRAILING_COMMA + "` allows a space between comma "
+ "and closing bracket: (a, ). "
+ "`" + _EMPTY_LINE + "` allows space-only lines."
+ ),
+ },
+ ),
+ (
+ "max-module-lines",
+ {
+ "default": 1000,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Maximum number of lines in a module.",
+ },
+ ),
+ (
+ "indent-string",
+ {
+ "default": " ",
+ "type": "non_empty_string",
+ "metavar": "<string>",
+ "help": "String used as indentation unit. This is usually "
+ '" " (4 spaces) or "\\t" (1 tab).',
+ },
+ ),
+ (
+ "indent-after-paren",
+ {
+ "type": "int",
+ "metavar": "<int>",
+ "default": 4,
+ "help": "Number of spaces of indent required inside a hanging "
+ "or continued line.",
+ },
+ ),
+ (
+ "expected-line-ending-format",
+ {
+ "type": "choice",
+ "metavar": "<empty or LF or CRLF>",
+ "default": "",
+ "choices": ["", "LF", "CRLF"],
+ "help": (
+ "Expected format of line ending, "
+ "e.g. empty (any line ending), LF or CRLF."
+ ),
+ },
+ ),
+ )
+
+ def __init__(self, linter=None):
+ BaseTokenChecker.__init__(self, linter)
+ self._lines = None
+ self._visited_lines = None
+ self._bracket_stack = [None]
+
+ def _pop_token(self):
+ self._bracket_stack.pop()
+ self._current_line.pop_token()
+
+ def _push_token(self, token, idx):
+ self._bracket_stack.append(token)
+ self._current_line.push_token(token, idx)
+
+ def new_line(self, tokens, line_end, line_start):
+ """a new line has been encountered, process it if necessary"""
+ if _last_token_on_line_is(tokens, line_end, ";"):
+ self.add_message("unnecessary-semicolon", line=tokens.start_line(line_end))
+
+ line_num = tokens.start_line(line_start)
+ line = tokens.line(line_start)
+ if tokens.type(line_start) not in _JUNK_TOKENS:
+ self._lines[line_num] = line.split("\n")[0]
+ self.check_lines(line, line_num)
+
+ def process_module(self, _module):
+ self._keywords_with_parens = set()
+
+ def _check_keyword_parentheses(self, tokens, start):
+ """Check that there are not unnecessary parens after a keyword.
+
+ Parens are unnecessary if there is exactly one balanced outer pair on a
+ line, and it is followed by a colon, and contains no commas (i.e. is not a
+ tuple).
+
+ Args:
+ tokens: list of Tokens; the entire list of Tokens.
+ start: int; the position of the keyword in the token list.
+ """
+ # If the next token is not a paren, we're fine.
+ if self._inside_brackets(":") and tokens[start][1] == "for":
+ self._pop_token()
+ if tokens[start + 1][1] != "(":
+ return
+
+ found_and_or = False
+ depth = 0
+ keyword_token = str(tokens[start][1])
+ line_num = tokens[start][2][0]
+
+ for i in range(start, len(tokens) - 1):
+ token = tokens[i]
+
+ # If we hit a newline, then assume any parens were for continuation.
+ if token[0] == tokenize.NL:
+ return
+
+ if token[1] == "(":
+ depth += 1
+ elif token[1] == ")":
+ depth -= 1
+ if depth:
+ continue
+ # ')' can't happen after if (foo), since it would be a syntax error.
+ if tokens[i + 1][1] in (":", ")", "]", "}", "in") or tokens[i + 1][
+ 0
+ ] in (tokenize.NEWLINE, tokenize.ENDMARKER, tokenize.COMMENT):
+ # The empty tuple () is always accepted.
+ if i == start + 2:
+ return
+ if keyword_token == "not":
+ if not found_and_or:
+ self.add_message(
+ "superfluous-parens", line=line_num, args=keyword_token
+ )
+ elif keyword_token in ("return", "yield"):
+ self.add_message(
+ "superfluous-parens", line=line_num, args=keyword_token
+ )
+ elif keyword_token not in self._keywords_with_parens:
+ if not found_and_or:
+ self.add_message(
+ "superfluous-parens", line=line_num, args=keyword_token
+ )
+ return
+ elif depth == 1:
+ # This is a tuple, which is always acceptable.
+ if token[1] == ",":
+ return
+ # 'and' and 'or' are the only boolean operators with lower precedence
+ # than 'not', so parens are only required when they are found.
+ if token[1] in ("and", "or"):
+ found_and_or = True
+ # A yield inside an expression must always be in parentheses,
+ # quit early without error.
+ elif token[1] == "yield":
+ return
+ # A generator expression always has a 'for' token in it, and
+ # the 'for' token is only legal inside parens when it is in a
+ # generator expression. The parens are necessary here, so bail
+ # without an error.
+ elif token[1] == "for":
+ return
+
+ def _opening_bracket(self, tokens, i):
+ self._push_token(tokens[i][1], i)
+ # Special case: ignore slices
+ if tokens[i][1] == "[" and tokens[i + 1][1] == ":":
+ return
+
+ if i > 0 and (
+ tokens[i - 1][0] == tokenize.NAME
+ and not (keyword.iskeyword(tokens[i - 1][1]))
+ or tokens[i - 1][1] in _CLOSING_BRACKETS
+ ):
+ self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
+ else:
+ self._check_space(tokens, i, (_IGNORE, _MUST_NOT))
+
+ def _closing_bracket(self, tokens, i):
+ if self._inside_brackets(":"):
+ self._pop_token()
+ self._pop_token()
+ # Special case: ignore slices
+ if tokens[i - 1][1] == ":" and tokens[i][1] == "]":
+ return
+ policy_before = _MUST_NOT
+ if tokens[i][1] in _CLOSING_BRACKETS and tokens[i - 1][1] == ",":
+ if _TRAILING_COMMA in self.config.no_space_check:
+ policy_before = _IGNORE
+
+ self._check_space(tokens, i, (policy_before, _IGNORE))
+
+ def _has_valid_type_annotation(self, tokens, i):
+ """Extended check of PEP-484 type hint presence"""
+ if not self._inside_brackets("("):
+ return False
+ # token_info
+ # type string start end line
+ # 0 1 2 3 4
+ bracket_level = 0
+ for token in tokens[i - 1 :: -1]:
+ if token[1] == ":":
+ return True
+ if token[1] == "(":
+ return False
+ if token[1] == "]":
+ bracket_level += 1
+ elif token[1] == "[":
+ bracket_level -= 1
+ elif token[1] == ",":
+ if not bracket_level:
+ return False
+ elif token[1] in (".", "..."):
+ continue
+ elif token[0] not in (tokenize.NAME, tokenize.STRING, tokenize.NL):
+ return False
+ return False
+
+ def _check_equals_spacing(self, tokens, i):
+ """Check the spacing of a single equals sign."""
+ if self._has_valid_type_annotation(tokens, i):
+ self._check_space(tokens, i, (_MUST, _MUST))
+ elif self._inside_brackets("(") or self._inside_brackets("lambda"):
+ self._check_space(tokens, i, (_MUST_NOT, _MUST_NOT))
+ else:
+ self._check_space(tokens, i, (_MUST, _MUST))
+
+ def _open_lambda(self, tokens, i): # pylint:disable=unused-argument
+ self._push_token("lambda", i)
+
+ def _handle_colon(self, tokens, i):
+ # Special case: ignore slices
+ if self._inside_brackets("["):
+ return
+ if self._inside_brackets("{") and _DICT_SEPARATOR in self.config.no_space_check:
+ policy = (_IGNORE, _IGNORE)
+ else:
+ policy = (_MUST_NOT, _MUST)
+ self._check_space(tokens, i, policy)
+
+ if self._inside_brackets("lambda"):
+ self._pop_token()
+ elif self._inside_brackets("{"):
+ self._push_token(":", i)
+
+ def _handle_comma(self, tokens, i):
+ # Only require a following whitespace if this is
+ # not a hanging comma before a closing bracket.
+ if tokens[i + 1][1] in _CLOSING_BRACKETS:
+ self._check_space(tokens, i, (_MUST_NOT, _IGNORE))
+ else:
+ self._check_space(tokens, i, (_MUST_NOT, _MUST))
+ if self._inside_brackets(":"):
+ self._pop_token()
+
+ def _check_surrounded_by_space(self, tokens, i):
+ """Check that a binary operator is surrounded by exactly one space."""
+ self._check_space(tokens, i, (_MUST, _MUST))
+
+ def _check_space(self, tokens, i, policies):
+ def _policy_string(policy):
+ if policy == _MUST:
+ return "Exactly one", "required"
+ return "No", "allowed"
+
+ def _name_construct(token):
+ if token[1] == ",":
+ return "comma"
+ if token[1] == ":":
+ return ":"
+ if token[1] in "()[]{}":
+ return "bracket"
+ if token[1] in ("<", ">", "<=", ">=", "!=", "=="):
+ return "comparison"
+ if self._inside_brackets("("):
+ return "keyword argument assignment"
+ return "assignment"
+
+ good_space = [True, True]
+ token = tokens[i]
+ pairs = [(tokens[i - 1], token), (token, tokens[i + 1])]
+
+ for other_idx, (policy, token_pair) in enumerate(zip(policies, pairs)):
+ if token_pair[other_idx][0] in _EOL or policy == _IGNORE:
+ continue
+
+ distance = _column_distance(*token_pair)
+ if distance is None:
+ continue
+ good_space[other_idx] = (policy == _MUST and distance == 1) or (
+ policy == _MUST_NOT and distance == 0
+ )
+
+ warnings = []
+ if not any(good_space) and policies[0] == policies[1]:
+ warnings.append((policies[0], "around"))
+ else:
+ for ok, policy, position in zip(good_space, policies, ("before", "after")):
+ if not ok:
+ warnings.append((policy, position))
+ for policy, position in warnings:
+ construct = _name_construct(token)
+ count, state = _policy_string(policy)
+ self.add_message(
+ "bad-whitespace",
+ line=token[2][0],
+ args=(count, state, position, construct, _underline_token(token)),
+ col_offset=token[2][1],
+ )
+
+ def _inside_brackets(self, left):
+ return self._bracket_stack[-1] == left
+
+ def _prepare_token_dispatcher(self):
+ raw = [
+ (_KEYWORD_TOKENS, self._check_keyword_parentheses),
+ (_OPENING_BRACKETS, self._opening_bracket),
+ (_CLOSING_BRACKETS, self._closing_bracket),
+ (["="], self._check_equals_spacing),
+ (_SPACED_OPERATORS, self._check_surrounded_by_space),
+ ([","], self._handle_comma),
+ ([":"], self._handle_colon),
+ (["lambda"], self._open_lambda),
+ ]
+
+ dispatch = {}
+ for tokens, handler in raw:
+ for token in tokens:
+ dispatch[token] = handler
+ return dispatch
+
+ def process_tokens(self, tokens):
+ """process tokens and search for :
+
+ _ non strict indentation (i.e. not always using the <indent> parameter as
+ indent unit)
+ _ too long lines (i.e. longer than <max_chars>)
+ _ optionally bad construct (if given, bad_construct must be a compiled
+ regular expression).
+ """
+ self._bracket_stack = [None]
+ indents = [0]
+ check_equal = False
+ line_num = 0
+ self._lines = {}
+ self._visited_lines = {}
+ token_handlers = self._prepare_token_dispatcher()
+ self._last_line_ending = None
+ last_blank_line_num = 0
+
+ self._current_line = ContinuedLineState(tokens, self.config)
+ for idx, (tok_type, token, start, _, line) in enumerate(tokens):
+ if start[0] != line_num:
+ line_num = start[0]
+ # A tokenizer oddity: if an indented line contains a multi-line
+ # docstring, the line member of the INDENT token does not contain
+ # the full line; therefore we check the next token on the line.
+ if tok_type == tokenize.INDENT:
+ self.new_line(TokenWrapper(tokens), idx - 1, idx + 1)
+ else:
+ self.new_line(TokenWrapper(tokens), idx - 1, idx)
+
+ if tok_type == tokenize.NEWLINE:
+ # a program statement, or ENDMARKER, will eventually follow,
+ # after some (possibly empty) run of tokens of the form
+ # (NL | COMMENT)* (INDENT | DEDENT+)?
+ # If an INDENT appears, setting check_equal is wrong, and will
+ # be undone when we see the INDENT.
+ check_equal = True
+ self._process_retained_warnings(TokenWrapper(tokens), idx)
+ self._current_line.next_logical_line()
+ self._check_line_ending(token, line_num)
+ elif tok_type == tokenize.INDENT:
+ check_equal = False
+ self.check_indent_level(token, indents[-1] + 1, line_num)
+ indents.append(indents[-1] + 1)
+ elif tok_type == tokenize.DEDENT:
+ # there's nothing we need to check here! what's important is
+ # that when the run of DEDENTs ends, the indentation of the
+ # program statement (or ENDMARKER) that triggered the run is
+ # equal to what's left at the top of the indents stack
+ check_equal = True
+ if len(indents) > 1:
+ del indents[-1]
+ elif tok_type == tokenize.NL:
+ if not line.strip("\r\n"):
+ last_blank_line_num = line_num
+ self._check_continued_indentation(TokenWrapper(tokens), idx + 1)
+ self._current_line.next_physical_line()
+ elif tok_type not in (tokenize.COMMENT, tokenize.ENCODING):
+ self._current_line.handle_line_start(idx)
+ # This is the first concrete token following a NEWLINE, so it
+ # must be the first token of the next program statement, or an
+ # ENDMARKER; the "line" argument exposes the leading whitespace
+ # for this statement; in the case of ENDMARKER, line is an empty
+ # string, so will properly match the empty string with which the
+ # "indents" stack was seeded
+ if check_equal:
+ check_equal = False
+ self.check_indent_level(line, indents[-1], line_num)
+
+ if tok_type == tokenize.NUMBER and token.endswith("l"):
+ self.add_message("lowercase-l-suffix", line=line_num)
+
+ try:
+ handler = token_handlers[token]
+ except KeyError:
+ pass
+ else:
+ handler(tokens, idx)
+
+ line_num -= 1 # to be ok with "wc -l"
+ if line_num > self.config.max_module_lines:
+ # Get the line where the too-many-lines (or its message id)
+ # was disabled or default to 1.
+ message_definition = self.linter.msgs_store.get_message_definitions(
+ "too-many-lines"
+ )[0]
+ names = (message_definition.msgid, "too-many-lines")
+ line = next(filter(None, map(self.linter._pragma_lineno.get, names)), 1)
+ self.add_message(
+ "too-many-lines",
+ args=(line_num, self.config.max_module_lines),
+ line=line,
+ )
+
+ # See if there are any trailing lines. Do not complain about empty
+ # files like __init__.py markers.
+ if line_num == last_blank_line_num and line_num > 0:
+ self.add_message("trailing-newlines", line=line_num)
+
+ def _check_line_ending(self, line_ending, line_num):
+ # check if line endings are mixed
+ if self._last_line_ending is not None:
+ # line_ending == "" indicates a synthetic newline added at
+ # the end of a file that does not, in fact, end with a
+ # newline.
+ if line_ending and line_ending != self._last_line_ending:
+ self.add_message("mixed-line-endings", line=line_num)
+
+ self._last_line_ending = line_ending
+
+ # check if line ending is as expected
+ expected = self.config.expected_line_ending_format
+ if expected:
+ # reduce multiple \n\n\n\n to one \n
+ line_ending = reduce(lambda x, y: x + y if x != y else x, line_ending, "")
+ line_ending = "LF" if line_ending == "\n" else "CRLF"
+ if line_ending != expected:
+ self.add_message(
+ "unexpected-line-ending-format",
+ args=(line_ending, expected),
+ line=line_num,
+ )
+
+ def _process_retained_warnings(self, tokens, current_pos):
+ single_line_block_stmt = not _last_token_on_line_is(tokens, current_pos, ":")
+
+ for indent_pos, state, indentations in self._current_line.retained_warnings:
+ block_type = indentations[tokens.token_indent(indent_pos)]
+ hints = {k: v for k, v in indentations.items() if v != block_type}
+ if single_line_block_stmt and block_type == WITH_BODY:
+ self._add_continuation_message(state, hints, tokens, indent_pos)
+ elif not single_line_block_stmt and block_type == SINGLE_LINE:
+ self._add_continuation_message(state, hints, tokens, indent_pos)
+
+ def _check_continued_indentation(self, tokens, next_idx):
+ def same_token_around_nl(token_type):
+ return (
+ tokens.type(next_idx) == token_type
+ and tokens.type(next_idx - 2) == token_type
+ )
+
+ # Do not issue any warnings if the next line is empty.
+ if not self._current_line.has_content or tokens.type(next_idx) == tokenize.NL:
+ return
+
+ state, valid_indentations = self._current_line.get_valid_indentations(next_idx)
+ # Special handling for hanging comments and strings. If the last line ended
+ # with a comment (string) and the new line contains only a comment, the line
+ # may also be indented to the start of the previous token.
+ if same_token_around_nl(tokenize.COMMENT) or same_token_around_nl(
+ tokenize.STRING
+ ):
+ valid_indentations[tokens.token_indent(next_idx - 2)] = True
+
+ # We can only decide if the indentation of a continued line before opening
+ # a new block is valid once we know of the body of the block is on the
+ # same line as the block opener. Since the token processing is single-pass,
+ # emitting those warnings is delayed until the block opener is processed.
+ if (
+ state.context_type in (HANGING_BLOCK, CONTINUED_BLOCK)
+ and tokens.token_indent(next_idx) in valid_indentations
+ ):
+ self._current_line.add_block_warning(next_idx, state, valid_indentations)
+ elif tokens.token_indent(next_idx) not in valid_indentations:
+ length_indentation = len(tokens.token_indent(next_idx))
+ if not any(
+ length_indentation == 2 * len(indentation)
+ for indentation in valid_indentations
+ ):
+ self._add_continuation_message(
+ state, valid_indentations, tokens, next_idx
+ )
+
+ def _add_continuation_message(self, state, indentations, tokens, position):
+ readable_type, readable_position = _CONTINUATION_MSG_PARTS[state.context_type]
+ hint_line, delta_message = _get_indent_hint_line(
+ indentations, tokens.token_indent(position)
+ )
+ self.add_message(
+ "bad-continuation",
+ line=tokens.start_line(position),
+ args=(
+ readable_type,
+ readable_position,
+ delta_message,
+ tokens.line(position),
+ hint_line,
+ ),
+ )
+
+ @check_messages("multiple-statements")
+ def visit_default(self, node):
+ """check the node line number and check it if not yet done"""
+ if not node.is_statement:
+ return
+ if not node.root().pure_python:
+ return
+ prev_sibl = node.previous_sibling()
+ if prev_sibl is not None:
+ prev_line = prev_sibl.fromlineno
+ else:
+ # The line on which a finally: occurs in a try/finally
+ # is not directly represented in the AST. We infer it
+ # by taking the last line of the body and adding 1, which
+ # should be the line of finally:
+ if (
+ isinstance(node.parent, nodes.TryFinally)
+ and node in node.parent.finalbody
+ ):
+ prev_line = node.parent.body[0].tolineno + 1
+ else:
+ prev_line = node.parent.statement().fromlineno
+ line = node.fromlineno
+ assert line, node
+ if prev_line == line and self._visited_lines.get(line) != 2:
+ self._check_multi_statement_line(node, line)
+ return
+ if line in self._visited_lines:
+ return
+ try:
+ tolineno = node.blockstart_tolineno
+ except AttributeError:
+ tolineno = node.tolineno
+ assert tolineno, node
+ lines = []
+ for line in range(line, tolineno + 1):
+ self._visited_lines[line] = 1
+ try:
+ lines.append(self._lines[line].rstrip())
+ except KeyError:
+ lines.append("")
+
+ def _check_multi_statement_line(self, node, line):
+ """Check for lines containing multiple statements."""
+ # Do not warn about multiple nested context managers
+ # in with statements.
+ if isinstance(node, nodes.With):
+ return
+ # For try... except... finally..., the two nodes
+ # appear to be on the same line due to how the AST is built.
+ if isinstance(node, nodes.TryExcept) and isinstance(
+ node.parent, nodes.TryFinally
+ ):
+ return
+ if (
+ isinstance(node.parent, nodes.If)
+ and not node.parent.orelse
+ and self.config.single_line_if_stmt
+ ):
+ return
+ if (
+ isinstance(node.parent, nodes.ClassDef)
+ and len(node.parent.body) == 1
+ and self.config.single_line_class_stmt
+ ):
+ return
+ self.add_message("multiple-statements", node=node)
+ self._visited_lines[line] = 2
+
+ def check_lines(self, lines, i):
+ """check lines have less than a maximum number of characters
+ """
+ max_chars = self.config.max_line_length
+ ignore_long_line = self.config.ignore_long_lines
+
+ def check_line(line, i):
+ if not line.endswith("\n"):
+ self.add_message("missing-final-newline", line=i)
+ else:
+ # exclude \f (formfeed) from the rstrip
+ stripped_line = line.rstrip("\t\n\r\v ")
+ if not stripped_line and _EMPTY_LINE in self.config.no_space_check:
+ # allow empty lines
+ pass
+ elif line[len(stripped_line) :] not in ("\n", "\r\n"):
+ self.add_message(
+ "trailing-whitespace", line=i, col_offset=len(stripped_line)
+ )
+ # Don't count excess whitespace in the line length.
+ line = stripped_line
+ mobj = OPTION_RGX.search(line)
+ if mobj and "=" in line:
+ front_of_equal, _, back_of_equal = mobj.group(1).partition("=")
+ if front_of_equal.strip() == "disable":
+ if "line-too-long" in {
+ _msg_id.strip() for _msg_id in back_of_equal.split(",")
+ }:
+ return None
+ line = line.rsplit("#", 1)[0].rstrip()
+
+ if len(line) > max_chars and not ignore_long_line.search(line):
+ self.add_message("line-too-long", line=i, args=(len(line), max_chars))
+ return i + 1
+
+ unsplit_ends = {
+ "\v",
+ "\x0b",
+ "\f",
+ "\x0c",
+ "\x1c",
+ "\x1d",
+ "\x1e",
+ "\x85",
+ "\u2028",
+ "\u2029",
+ }
+ unsplit = []
+ for line in lines.splitlines(True):
+ if line[-1] in unsplit_ends:
+ unsplit.append(line)
+ continue
+
+ if unsplit:
+ unsplit.append(line)
+ line = "".join(unsplit)
+ unsplit = []
+
+ i = check_line(line, i)
+ if i is None:
+ break
+
+ if unsplit:
+ check_line("".join(unsplit), i)
+
+ def check_indent_level(self, string, expected, line_num):
+ """return the indent level of the string
+ """
+ indent = self.config.indent_string
+ if indent == "\\t": # \t is not interpreted in the configuration file
+ indent = "\t"
+ level = 0
+ unit_size = len(indent)
+ while string[:unit_size] == indent:
+ string = string[unit_size:]
+ level += 1
+ suppl = ""
+ while string and string[0] in " \t":
+ if string[0] != indent[0]:
+ if string[0] == "\t":
+ args = ("tab", "space")
+ else:
+ args = ("space", "tab")
+ self.add_message("mixed-indentation", args=args, line=line_num)
+ return level
+ suppl += string[0]
+ string = string[1:]
+ if level != expected or suppl:
+ i_type = "spaces"
+ if indent[0] == "\t":
+ i_type = "tabs"
+ self.add_message(
+ "bad-indentation",
+ line=line_num,
+ args=(level * unit_size + len(suppl), i_type, expected * unit_size),
+ )
+ return None
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(FormatChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/imports.py b/venv/Lib/site-packages/pylint/checkers/imports.py
new file mode 100644
index 0000000..42d4362
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/imports.py
@@ -0,0 +1,981 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2013 buck@yelp.com <buck@yelp.com>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
+# Copyright (c) 2015 Cezar <celnazli@bitdefender.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Noam Yorav-Raphael <noamraph@gmail.com>
+# Copyright (c) 2015 James Morgensen <james.morgensen@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Jared Garst <cultofjared@gmail.com>
+# Copyright (c) 2016 Maik Röder <maikroeder@gmail.com>
+# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Michka Popoff <michkapopoff@gmail.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 Erik Wright <erik.wright@shopify.com>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Marianna Polatoglou <mpolatoglou@bloomberg.net>
+# Copyright (c) 2019 Paul Renvoise <renvoisepaul@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""imports checkers for Python code"""
+
+import collections
+import copy
+import os
+import sys
+from distutils import sysconfig
+
+import astroid
+import isort
+from astroid import modutils
+from astroid.decorators import cached
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import (
+ check_messages,
+ is_from_fallback_block,
+ node_ignores_exception,
+)
+from pylint.exceptions import EmptyReportError
+from pylint.graph import DotBackend, get_cycles
+from pylint.interfaces import IAstroidChecker
+from pylint.reporters.ureports.nodes import Paragraph, VerbatimText
+from pylint.utils import get_global_option
+
+
+def _qualified_names(modname):
+ """Split the names of the given module into subparts
+
+ For example,
+ _qualified_names('pylint.checkers.ImportsChecker')
+ returns
+ ['pylint', 'pylint.checkers', 'pylint.checkers.ImportsChecker']
+ """
+ names = modname.split(".")
+ return [".".join(names[0 : i + 1]) for i in range(len(names))]
+
+
+def _get_import_name(importnode, modname):
+ """Get a prepared module name from the given import node
+
+ In the case of relative imports, this will return the
+ absolute qualified module name, which might be useful
+ for debugging. Otherwise, the initial module name
+ is returned unchanged.
+ """
+ if isinstance(importnode, astroid.ImportFrom):
+ if importnode.level:
+ root = importnode.root()
+ if isinstance(root, astroid.Module):
+ modname = root.relative_to_absolute_name(
+ modname, level=importnode.level
+ )
+ return modname
+
+
+def _get_first_import(node, context, name, base, level, alias):
+ """return the node where [base.]<name> is imported or None if not found
+ """
+ fullname = "%s.%s" % (base, name) if base else name
+
+ first = None
+ found = False
+ for first in context.body:
+ if first is node:
+ continue
+ if first.scope() is node.scope() and first.fromlineno > node.fromlineno:
+ continue
+ if isinstance(first, astroid.Import):
+ if any(fullname == iname[0] for iname in first.names):
+ found = True
+ break
+ elif isinstance(first, astroid.ImportFrom):
+ if level == first.level:
+ for imported_name, imported_alias in first.names:
+ if fullname == "%s.%s" % (first.modname, imported_name):
+ found = True
+ break
+ if (
+ name != "*"
+ and name == imported_name
+ and not (alias or imported_alias)
+ ):
+ found = True
+ break
+ if found:
+ break
+ if found and not astroid.are_exclusive(first, node):
+ return first
+ return None
+
+
+def _ignore_import_failure(node, modname, ignored_modules):
+ for submodule in _qualified_names(modname):
+ if submodule in ignored_modules:
+ return True
+
+ return node_ignores_exception(node, ImportError)
+
+
+# utilities to represents import dependencies as tree and dot graph ###########
+
+
+def _make_tree_defs(mod_files_list):
+ """get a list of 2-uple (module, list_of_files_which_import_this_module),
+ it will return a dictionary to represent this as a tree
+ """
+ tree_defs = {}
+ for mod, files in mod_files_list:
+ node = (tree_defs, ())
+ for prefix in mod.split("."):
+ node = node[0].setdefault(prefix, [{}, []])
+ node[1] += files
+ return tree_defs
+
+
+def _repr_tree_defs(data, indent_str=None):
+ """return a string which represents imports as a tree"""
+ lines = []
+ nodes = data.items()
+ for i, (mod, (sub, files)) in enumerate(sorted(nodes, key=lambda x: x[0])):
+ if not files:
+ files = ""
+ else:
+ files = "(%s)" % ",".join(sorted(files))
+ if indent_str is None:
+ lines.append("%s %s" % (mod, files))
+ sub_indent_str = " "
+ else:
+ lines.append(r"%s\-%s %s" % (indent_str, mod, files))
+ if i == len(nodes) - 1:
+ sub_indent_str = "%s " % indent_str
+ else:
+ sub_indent_str = "%s| " % indent_str
+ if sub:
+ lines.append(_repr_tree_defs(sub, sub_indent_str))
+ return "\n".join(lines)
+
+
+def _dependencies_graph(filename, dep_info):
+ """write dependencies as a dot (graphviz) file
+ """
+ done = {}
+ printer = DotBackend(filename[:-4], rankdir="LR")
+ printer.emit('URL="." node[shape="box"]')
+ for modname, dependencies in sorted(dep_info.items()):
+ done[modname] = 1
+ printer.emit_node(modname)
+ for depmodname in dependencies:
+ if depmodname not in done:
+ done[depmodname] = 1
+ printer.emit_node(depmodname)
+ for depmodname, dependencies in sorted(dep_info.items()):
+ for modname in dependencies:
+ printer.emit_edge(modname, depmodname)
+ printer.generate(filename)
+
+
+def _make_graph(filename, dep_info, sect, gtype):
+ """generate a dependencies graph and add some information about it in the
+ report's section
+ """
+ _dependencies_graph(filename, dep_info)
+ sect.append(Paragraph("%simports graph has been written to %s" % (gtype, filename)))
+
+
+# the import checker itself ###################################################
+
+MSGS = {
+ "E0401": (
+ "Unable to import %s",
+ "import-error",
+ "Used when pylint has been unable to import a module.",
+ {"old_names": [("F0401", "old-import-error")]},
+ ),
+ "E0402": (
+ "Attempted relative import beyond top-level package",
+ "relative-beyond-top-level",
+ "Used when a relative import tries to access too many levels "
+ "in the current package.",
+ ),
+ "R0401": (
+ "Cyclic import (%s)",
+ "cyclic-import",
+ "Used when a cyclic import between two or more modules is detected.",
+ ),
+ "W0401": (
+ "Wildcard import %s",
+ "wildcard-import",
+ "Used when `from module import *` is detected.",
+ ),
+ "W0402": (
+ "Uses of a deprecated module %r",
+ "deprecated-module",
+ "Used a module marked as deprecated is imported.",
+ ),
+ "W0404": (
+ "Reimport %r (imported line %s)",
+ "reimported",
+ "Used when a module is reimported multiple times.",
+ ),
+ "W0406": (
+ "Module import itself",
+ "import-self",
+ "Used when a module is importing itself.",
+ ),
+ "W0407": (
+ "Prefer importing %r instead of %r",
+ "preferred-module",
+ "Used when a module imported has a preferred replacement module.",
+ ),
+ "W0410": (
+ "__future__ import is not the first non docstring statement",
+ "misplaced-future",
+ "Python 2.5 and greater require __future__ import to be the "
+ "first non docstring statement in the module.",
+ ),
+ "C0410": (
+ "Multiple imports on one line (%s)",
+ "multiple-imports",
+ "Used when import statement importing multiple modules is detected.",
+ ),
+ "C0411": (
+ "%s should be placed before %s",
+ "wrong-import-order",
+ "Used when PEP8 import order is not respected (standard imports "
+ "first, then third-party libraries, then local imports)",
+ ),
+ "C0412": (
+ "Imports from package %s are not grouped",
+ "ungrouped-imports",
+ "Used when imports are not grouped by packages",
+ ),
+ "C0413": (
+ 'Import "%s" should be placed at the top of the module',
+ "wrong-import-position",
+ "Used when code and imports are mixed",
+ ),
+ "C0414": (
+ "Import alias does not rename original package",
+ "useless-import-alias",
+ "Used when an import alias is same as original package."
+ "e.g using import numpy as numpy instead of import numpy as np",
+ ),
+ "C0415": (
+ "Import outside toplevel (%s)",
+ "import-outside-toplevel",
+ "Used when an import statement is used anywhere other than the module "
+ "toplevel. Move this import to the top of the file.",
+ ),
+}
+
+
+DEFAULT_STANDARD_LIBRARY = ()
+DEFAULT_KNOWN_THIRD_PARTY = ("enchant",)
+DEFAULT_PREFERRED_MODULES = ()
+
+
+class ImportsChecker(BaseChecker):
+ """checks for
+ * external modules dependencies
+ * relative / wildcard imports
+ * cyclic imports
+ * uses of deprecated modules
+ * uses of modules instead of preferred modules
+ """
+
+ __implements__ = IAstroidChecker
+
+ name = "imports"
+ msgs = MSGS
+ priority = -2
+ deprecated_modules = ("optparse", "tkinter.tix")
+
+ options = (
+ (
+ "deprecated-modules",
+ {
+ "default": deprecated_modules,
+ "type": "csv",
+ "metavar": "<modules>",
+ "help": "Deprecated modules which should not be used,"
+ " separated by a comma.",
+ },
+ ),
+ (
+ "preferred-modules",
+ {
+ "default": DEFAULT_PREFERRED_MODULES,
+ "type": "csv",
+ "metavar": "<module:preferred-module>",
+ "help": "Couples of modules and preferred modules,"
+ " separated by a comma.",
+ },
+ ),
+ (
+ "import-graph",
+ {
+ "default": "",
+ "type": "string",
+ "metavar": "<file.dot>",
+ "help": "Create a graph of every (i.e. internal and"
+ " external) dependencies in the given file"
+ " (report RP0402 must not be disabled).",
+ },
+ ),
+ (
+ "ext-import-graph",
+ {
+ "default": "",
+ "type": "string",
+ "metavar": "<file.dot>",
+ "help": "Create a graph of external dependencies in the"
+ " given file (report RP0402 must not be disabled).",
+ },
+ ),
+ (
+ "int-import-graph",
+ {
+ "default": "",
+ "type": "string",
+ "metavar": "<file.dot>",
+ "help": "Create a graph of internal dependencies in the"
+ " given file (report RP0402 must not be disabled).",
+ },
+ ),
+ (
+ "known-standard-library",
+ {
+ "default": DEFAULT_STANDARD_LIBRARY,
+ "type": "csv",
+ "metavar": "<modules>",
+ "help": "Force import order to recognize a module as part of "
+ "the standard compatibility libraries.",
+ },
+ ),
+ (
+ "known-third-party",
+ {
+ "default": DEFAULT_KNOWN_THIRD_PARTY,
+ "type": "csv",
+ "metavar": "<modules>",
+ "help": "Force import order to recognize a module as part of "
+ "a third party library.",
+ },
+ ),
+ (
+ "allow-any-import-level",
+ {
+ "default": (),
+ "type": "csv",
+ "metavar": "<modules>",
+ "help": (
+ "List of modules that can be imported at any level, not just "
+ "the top level one."
+ ),
+ },
+ ),
+ (
+ "analyse-fallback-blocks",
+ {
+ "default": False,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": "Analyse import fallback blocks. This can be used to "
+ "support both Python 2 and 3 compatible code, which "
+ "means that the block might have code that exists "
+ "only in one or another interpreter, leading to false "
+ "positives when analysed.",
+ },
+ ),
+ (
+ "allow-wildcard-with-all",
+ {
+ "default": False,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": "Allow wildcard imports from modules that define __all__.",
+ },
+ ),
+ )
+
+ def __init__(self, linter=None):
+ BaseChecker.__init__(self, linter)
+ self.stats = None
+ self.import_graph = None
+ self._imports_stack = []
+ self._first_non_import_node = None
+ self._module_pkg = {} # mapping of modules to the pkg they belong in
+ self._allow_any_import_level = set()
+ self.reports = (
+ ("RP0401", "External dependencies", self._report_external_dependencies),
+ ("RP0402", "Modules dependencies graph", self._report_dependencies_graph),
+ )
+
+ self._site_packages = self._compute_site_packages()
+
+ @staticmethod
+ def _compute_site_packages():
+ def _normalized_path(path):
+ return os.path.normcase(os.path.abspath(path))
+
+ paths = set()
+ real_prefix = getattr(sys, "real_prefix", None)
+ for prefix in filter(None, (real_prefix, sys.prefix)):
+ path = sysconfig.get_python_lib(prefix=prefix)
+ path = _normalized_path(path)
+ paths.add(path)
+
+ # Handle Debian's derivatives /usr/local.
+ if os.path.isfile("/etc/debian_version"):
+ for prefix in filter(None, (real_prefix, sys.prefix)):
+ libpython = os.path.join(
+ prefix,
+ "local",
+ "lib",
+ "python" + sysconfig.get_python_version(),
+ "dist-packages",
+ )
+ paths.add(libpython)
+ return paths
+
+ def open(self):
+ """called before visiting project (i.e set of modules)"""
+ self.linter.add_stats(dependencies={})
+ self.linter.add_stats(cycles=[])
+ self.stats = self.linter.stats
+ self.import_graph = collections.defaultdict(set)
+ self._module_pkg = {} # mapping of modules to the pkg they belong in
+ self._excluded_edges = collections.defaultdict(set)
+ self._ignored_modules = get_global_option(self, "ignored-modules", default=[])
+ # Build a mapping {'module': 'preferred-module'}
+ self.preferred_modules = dict(
+ module.split(":")
+ for module in self.config.preferred_modules
+ if ":" in module
+ )
+ self._allow_any_import_level = set(self.config.allow_any_import_level)
+
+ def _import_graph_without_ignored_edges(self):
+ filtered_graph = copy.deepcopy(self.import_graph)
+ for node in filtered_graph:
+ filtered_graph[node].difference_update(self._excluded_edges[node])
+ return filtered_graph
+
+ def close(self):
+ """called before visiting project (i.e set of modules)"""
+ if self.linter.is_message_enabled("cyclic-import"):
+ graph = self._import_graph_without_ignored_edges()
+ vertices = list(graph)
+ for cycle in get_cycles(graph, vertices=vertices):
+ self.add_message("cyclic-import", args=" -> ".join(cycle))
+
+ @check_messages(*MSGS)
+ def visit_import(self, node):
+ """triggered when an import statement is seen"""
+ self._check_reimport(node)
+ self._check_import_as_rename(node)
+ self._check_toplevel(node)
+
+ names = [name for name, _ in node.names]
+ if len(names) >= 2:
+ self.add_message("multiple-imports", args=", ".join(names), node=node)
+
+ for name in names:
+ self._check_deprecated_module(node, name)
+ self._check_preferred_module(node, name)
+ imported_module = self._get_imported_module(node, name)
+ if isinstance(node.parent, astroid.Module):
+ # Allow imports nested
+ self._check_position(node)
+ if isinstance(node.scope(), astroid.Module):
+ self._record_import(node, imported_module)
+
+ if imported_module is None:
+ continue
+
+ self._add_imported_module(node, imported_module.name)
+
+ @check_messages(*MSGS)
+ def visit_importfrom(self, node):
+ """triggered when a from statement is seen"""
+ basename = node.modname
+ imported_module = self._get_imported_module(node, basename)
+
+ self._check_import_as_rename(node)
+ self._check_misplaced_future(node)
+ self._check_deprecated_module(node, basename)
+ self._check_preferred_module(node, basename)
+ self._check_wildcard_imports(node, imported_module)
+ self._check_same_line_imports(node)
+ self._check_reimport(node, basename=basename, level=node.level)
+ self._check_toplevel(node)
+
+ if isinstance(node.parent, astroid.Module):
+ # Allow imports nested
+ self._check_position(node)
+ if isinstance(node.scope(), astroid.Module):
+ self._record_import(node, imported_module)
+ if imported_module is None:
+ return
+ for name, _ in node.names:
+ if name != "*":
+ self._add_imported_module(node, "%s.%s" % (imported_module.name, name))
+ else:
+ self._add_imported_module(node, imported_module.name)
+
+ @check_messages(*MSGS)
+ def leave_module(self, node):
+ # Check imports are grouped by category (standard, 3rd party, local)
+ std_imports, ext_imports, loc_imports = self._check_imports_order(node)
+
+ # Check that imports are grouped by package within a given category
+ met_import = set() #  set for 'import x' style
+ met_from = set() #  set for 'from x import y' style
+ current_package = None
+ for import_node, import_name in std_imports + ext_imports + loc_imports:
+ if not self.linter.is_message_enabled(
+ "ungrouped-imports", import_node.fromlineno
+ ):
+ continue
+ if isinstance(import_node, astroid.node_classes.ImportFrom):
+ met = met_from
+ else:
+ met = met_import
+ package, _, _ = import_name.partition(".")
+ if current_package and current_package != package and package in met:
+ self.add_message("ungrouped-imports", node=import_node, args=package)
+ current_package = package
+ met.add(package)
+
+ self._imports_stack = []
+ self._first_non_import_node = None
+
+ def compute_first_non_import_node(self, node):
+ if not self.linter.is_message_enabled("wrong-import-position", node.fromlineno):
+ return
+ # if the node does not contain an import instruction, and if it is the
+ # first node of the module, keep a track of it (all the import positions
+ # of the module will be compared to the position of this first
+ # instruction)
+ if self._first_non_import_node:
+ return
+ if not isinstance(node.parent, astroid.Module):
+ return
+ nested_allowed = [astroid.TryExcept, astroid.TryFinally]
+ is_nested_allowed = [
+ allowed for allowed in nested_allowed if isinstance(node, allowed)
+ ]
+ if is_nested_allowed and any(
+ node.nodes_of_class((astroid.Import, astroid.ImportFrom))
+ ):
+ return
+ if isinstance(node, astroid.Assign):
+ # Add compatibility for module level dunder names
+ # https://www.python.org/dev/peps/pep-0008/#module-level-dunder-names
+ valid_targets = [
+ isinstance(target, astroid.AssignName)
+ and target.name.startswith("__")
+ and target.name.endswith("__")
+ for target in node.targets
+ ]
+ if all(valid_targets):
+ return
+ self._first_non_import_node = node
+
+ visit_tryfinally = (
+ visit_tryexcept
+ ) = (
+ visit_assignattr
+ ) = (
+ visit_assign
+ ) = (
+ visit_ifexp
+ ) = visit_comprehension = visit_expr = visit_if = compute_first_non_import_node
+
+ def visit_functiondef(self, node):
+ if not self.linter.is_message_enabled("wrong-import-position", node.fromlineno):
+ return
+ # If it is the first non import instruction of the module, record it.
+ if self._first_non_import_node:
+ return
+
+ # Check if the node belongs to an `If` or a `Try` block. If they
+ # contain imports, skip recording this node.
+ if not isinstance(node.parent.scope(), astroid.Module):
+ return
+
+ root = node
+ while not isinstance(root.parent, astroid.Module):
+ root = root.parent
+
+ if isinstance(root, (astroid.If, astroid.TryFinally, astroid.TryExcept)):
+ if any(root.nodes_of_class((astroid.Import, astroid.ImportFrom))):
+ return
+
+ self._first_non_import_node = node
+
+ visit_classdef = visit_for = visit_while = visit_functiondef
+
+ def _check_misplaced_future(self, node):
+ basename = node.modname
+ if basename == "__future__":
+ # check if this is the first non-docstring statement in the module
+ prev = node.previous_sibling()
+ if prev:
+ # consecutive future statements are possible
+ if not (
+ isinstance(prev, astroid.ImportFrom)
+ and prev.modname == "__future__"
+ ):
+ self.add_message("misplaced-future", node=node)
+ return
+
+ def _check_same_line_imports(self, node):
+ # Detect duplicate imports on the same line.
+ names = (name for name, _ in node.names)
+ counter = collections.Counter(names)
+ for name, count in counter.items():
+ if count > 1:
+ self.add_message("reimported", node=node, args=(name, node.fromlineno))
+
+ def _check_position(self, node):
+ """Check `node` import or importfrom node position is correct
+
+ Send a message if `node` comes before another instruction
+ """
+ # if a first non-import instruction has already been encountered,
+ # it means the import comes after it and therefore is not well placed
+ if self._first_non_import_node:
+ self.add_message("wrong-import-position", node=node, args=node.as_string())
+
+ def _record_import(self, node, importedmodnode):
+ """Record the package `node` imports from"""
+ if isinstance(node, astroid.ImportFrom):
+ importedname = node.modname
+ else:
+ importedname = importedmodnode.name if importedmodnode else None
+ if not importedname:
+ importedname = node.names[0][0].split(".")[0]
+
+ if isinstance(node, astroid.ImportFrom) and (node.level or 0) >= 1:
+ # We need the importedname with first point to detect local package
+ # Example of node:
+ # 'from .my_package1 import MyClass1'
+ # the output should be '.my_package1' instead of 'my_package1'
+ # Example of node:
+ # 'from . import my_package2'
+ # the output should be '.my_package2' instead of '{pyfile}'
+ importedname = "." + importedname
+
+ self._imports_stack.append((node, importedname))
+
+ @staticmethod
+ def _is_fallback_import(node, imports):
+ imports = [import_node for (import_node, _) in imports]
+ return any(astroid.are_exclusive(import_node, node) for import_node in imports)
+
+ def _check_imports_order(self, _module_node):
+ """Checks imports of module `node` are grouped by category
+
+ Imports must follow this order: standard, 3rd party, local
+ """
+ std_imports = []
+ third_party_imports = []
+ first_party_imports = []
+ # need of a list that holds third or first party ordered import
+ external_imports = []
+ local_imports = []
+ third_party_not_ignored = []
+ first_party_not_ignored = []
+ local_not_ignored = []
+ isort_obj = isort.SortImports(
+ file_contents="",
+ known_third_party=self.config.known_third_party,
+ known_standard_library=self.config.known_standard_library,
+ )
+ for node, modname in self._imports_stack:
+ if modname.startswith("."):
+ package = "." + modname.split(".")[1]
+ else:
+ package = modname.split(".")[0]
+ nested = not isinstance(node.parent, astroid.Module)
+ ignore_for_import_order = not self.linter.is_message_enabled(
+ "wrong-import-order", node.fromlineno
+ )
+ import_category = isort_obj.place_module(package)
+ node_and_package_import = (node, package)
+ if import_category in ("FUTURE", "STDLIB"):
+ std_imports.append(node_and_package_import)
+ wrong_import = (
+ third_party_not_ignored
+ or first_party_not_ignored
+ or local_not_ignored
+ )
+ if self._is_fallback_import(node, wrong_import):
+ continue
+ if wrong_import and not nested:
+ self.add_message(
+ "wrong-import-order",
+ node=node,
+ args=(
+ 'standard import "%s"' % node.as_string(),
+ '"%s"' % wrong_import[0][0].as_string(),
+ ),
+ )
+ elif import_category == "THIRDPARTY":
+ third_party_imports.append(node_and_package_import)
+ external_imports.append(node_and_package_import)
+ if not nested and not ignore_for_import_order:
+ third_party_not_ignored.append(node_and_package_import)
+ wrong_import = first_party_not_ignored or local_not_ignored
+ if wrong_import and not nested:
+ self.add_message(
+ "wrong-import-order",
+ node=node,
+ args=(
+ 'third party import "%s"' % node.as_string(),
+ '"%s"' % wrong_import[0][0].as_string(),
+ ),
+ )
+ elif import_category == "FIRSTPARTY":
+ first_party_imports.append(node_and_package_import)
+ external_imports.append(node_and_package_import)
+ if not nested and not ignore_for_import_order:
+ first_party_not_ignored.append(node_and_package_import)
+ wrong_import = local_not_ignored
+ if wrong_import and not nested:
+ self.add_message(
+ "wrong-import-order",
+ node=node,
+ args=(
+ 'first party import "%s"' % node.as_string(),
+ '"%s"' % wrong_import[0][0].as_string(),
+ ),
+ )
+ elif import_category == "LOCALFOLDER":
+ local_imports.append((node, package))
+ if not nested and not ignore_for_import_order:
+ local_not_ignored.append((node, package))
+ return std_imports, external_imports, local_imports
+
+ def _get_imported_module(self, importnode, modname):
+ try:
+ return importnode.do_import_module(modname)
+ except astroid.TooManyLevelsError:
+ if _ignore_import_failure(importnode, modname, self._ignored_modules):
+ return None
+
+ self.add_message("relative-beyond-top-level", node=importnode)
+ except astroid.AstroidSyntaxError as exc:
+ message = "Cannot import {!r} due to syntax error {!r}".format(
+ modname, str(exc.error) # pylint: disable=no-member; false positive
+ )
+ self.add_message("syntax-error", line=importnode.lineno, args=message)
+
+ except astroid.AstroidBuildingException:
+ if not self.linter.is_message_enabled("import-error"):
+ return None
+ if _ignore_import_failure(importnode, modname, self._ignored_modules):
+ return None
+ if not self.config.analyse_fallback_blocks and is_from_fallback_block(
+ importnode
+ ):
+ return None
+
+ dotted_modname = _get_import_name(importnode, modname)
+ self.add_message("import-error", args=repr(dotted_modname), node=importnode)
+
+ def _add_imported_module(self, node, importedmodname):
+ """notify an imported module, used to analyze dependencies"""
+ module_file = node.root().file
+ context_name = node.root().name
+ base = os.path.splitext(os.path.basename(module_file))[0]
+
+ try:
+ importedmodname = modutils.get_module_part(importedmodname, module_file)
+ except ImportError:
+ pass
+
+ if context_name == importedmodname:
+ self.add_message("import-self", node=node)
+
+ elif not modutils.is_standard_module(importedmodname):
+ # if this is not a package __init__ module
+ if base != "__init__" and context_name not in self._module_pkg:
+ # record the module's parent, or the module itself if this is
+ # a top level module, as the package it belongs to
+ self._module_pkg[context_name] = context_name.rsplit(".", 1)[0]
+
+ # handle dependencies
+ importedmodnames = self.stats["dependencies"].setdefault(
+ importedmodname, set()
+ )
+ if context_name not in importedmodnames:
+ importedmodnames.add(context_name)
+
+ # update import graph
+ self.import_graph[context_name].add(importedmodname)
+ if not self.linter.is_message_enabled("cyclic-import", line=node.lineno):
+ self._excluded_edges[context_name].add(importedmodname)
+
+ def _check_deprecated_module(self, node, mod_path):
+ """check if the module is deprecated"""
+ for mod_name in self.config.deprecated_modules:
+ if mod_path == mod_name or mod_path.startswith(mod_name + "."):
+ self.add_message("deprecated-module", node=node, args=mod_path)
+
+ def _check_preferred_module(self, node, mod_path):
+ """check if the module has a preferred replacement"""
+ if mod_path in self.preferred_modules:
+ self.add_message(
+ "preferred-module",
+ node=node,
+ args=(self.preferred_modules[mod_path], mod_path),
+ )
+
+ def _check_import_as_rename(self, node):
+ names = node.names
+ for name in names:
+ if not all(name):
+ return
+
+ real_name = name[0]
+ splitted_packages = real_name.rsplit(".")
+ real_name = splitted_packages[-1]
+ imported_name = name[1]
+ # consider only following cases
+ # import x as x
+ # and ignore following
+ # import x.y.z as z
+ if real_name == imported_name and len(splitted_packages) == 1:
+ self.add_message("useless-import-alias", node=node)
+
+ def _check_reimport(self, node, basename=None, level=None):
+ """check if the import is necessary (i.e. not already done)"""
+ if not self.linter.is_message_enabled("reimported"):
+ return
+
+ frame = node.frame()
+ root = node.root()
+ contexts = [(frame, level)]
+ if root is not frame:
+ contexts.append((root, None))
+
+ for known_context, known_level in contexts:
+ for name, alias in node.names:
+ first = _get_first_import(
+ node, known_context, name, basename, known_level, alias
+ )
+ if first is not None:
+ self.add_message(
+ "reimported", node=node, args=(name, first.fromlineno)
+ )
+
+ def _report_external_dependencies(self, sect, _, _dummy):
+ """return a verbatim layout for displaying dependencies"""
+ dep_info = _make_tree_defs(self._external_dependencies_info().items())
+ if not dep_info:
+ raise EmptyReportError()
+ tree_str = _repr_tree_defs(dep_info)
+ sect.append(VerbatimText(tree_str))
+
+ def _report_dependencies_graph(self, sect, _, _dummy):
+ """write dependencies as a dot (graphviz) file"""
+ dep_info = self.stats["dependencies"]
+ if not dep_info or not (
+ self.config.import_graph
+ or self.config.ext_import_graph
+ or self.config.int_import_graph
+ ):
+ raise EmptyReportError()
+ filename = self.config.import_graph
+ if filename:
+ _make_graph(filename, dep_info, sect, "")
+ filename = self.config.ext_import_graph
+ if filename:
+ _make_graph(filename, self._external_dependencies_info(), sect, "external ")
+ filename = self.config.int_import_graph
+ if filename:
+ _make_graph(filename, self._internal_dependencies_info(), sect, "internal ")
+
+ def _filter_dependencies_graph(self, internal):
+ """build the internal or the external dependency graph"""
+ graph = collections.defaultdict(set)
+ for importee, importers in self.stats["dependencies"].items():
+ for importer in importers:
+ package = self._module_pkg.get(importer, importer)
+ is_inside = importee.startswith(package)
+ if is_inside and internal or not is_inside and not internal:
+ graph[importee].add(importer)
+ return graph
+
+ @cached
+ def _external_dependencies_info(self):
+ """return cached external dependencies information or build and
+ cache them
+ """
+ return self._filter_dependencies_graph(internal=False)
+
+ @cached
+ def _internal_dependencies_info(self):
+ """return cached internal dependencies information or build and
+ cache them
+ """
+ return self._filter_dependencies_graph(internal=True)
+
+ def _check_wildcard_imports(self, node, imported_module):
+ if node.root().package:
+ # Skip the check if in __init__.py issue #2026
+ return
+
+ wildcard_import_is_allowed = self._wildcard_import_is_allowed(imported_module)
+ for name, _ in node.names:
+ if name == "*" and not wildcard_import_is_allowed:
+ self.add_message("wildcard-import", args=node.modname, node=node)
+
+ def _wildcard_import_is_allowed(self, imported_module):
+ return (
+ self.config.allow_wildcard_with_all
+ and imported_module is not None
+ and "__all__" in imported_module.locals
+ )
+
+ def _check_toplevel(self, node):
+ """Check whether the import is made outside the module toplevel.
+ """
+ # If the scope of the import is a module, then obviously it is
+ # not outside the module toplevel.
+ if isinstance(node.scope(), astroid.Module):
+ return
+
+ if isinstance(node, astroid.ImportFrom):
+ module_names = [node.modname]
+ else:
+ module_names = [name[0] for name in node.names]
+
+ # Get the full names of all the imports that are not whitelisted.
+ scoped_imports = [
+ name for name in module_names if name not in self._allow_any_import_level
+ ]
+
+ if scoped_imports:
+ self.add_message(
+ "import-outside-toplevel", args=", ".join(scoped_imports), node=node
+ )
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(ImportsChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/logging.py b/venv/Lib/site-packages/pylint/checkers/logging.py
new file mode 100644
index 0000000..5ad0e76
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/logging.py
@@ -0,0 +1,384 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2009, 2012, 2014 Google, Inc.
+# Copyright (c) 2012 Mike Bryant <leachim@leachim.info>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Chris Murray <chris@chrismurray.scot>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2017 guillaume2 <guillaume.peillex@gmail.col>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 Mariatta Wijaya <mariatta@python.org>
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""checker for use of Python logging
+"""
+import string
+
+import astroid
+
+from pylint import checkers, interfaces
+from pylint.checkers import utils
+from pylint.checkers.utils import check_messages
+
+MSGS = {
+ "W1201": (
+ "Specify string format arguments as logging function parameters",
+ "logging-not-lazy",
+ "Used when a logging statement has a call form of "
+ '"logging.<logging method>(format_string % (format_args...))". '
+ "Such calls should leave string interpolation to the logging "
+ "method itself and be written "
+ '"logging.<logging method>(format_string, format_args...)" '
+ "so that the program may avoid incurring the cost of the "
+ "interpolation in those cases in which no message will be "
+ "logged. For more, see "
+ "http://www.python.org/dev/peps/pep-0282/.",
+ ),
+ "W1202": (
+ "Use %s formatting in logging functions%s",
+ "logging-format-interpolation",
+ "Used when a logging statement has a call form of "
+ '"logging.<logging method>(<string formatting>)".'
+ " with invalid string formatting. "
+ "Use another way for format the string instead.",
+ ),
+ "E1200": (
+ "Unsupported logging format character %r (%#02x) at index %d",
+ "logging-unsupported-format",
+ "Used when an unsupported format character is used in a logging "
+ "statement format string.",
+ ),
+ "E1201": (
+ "Logging format string ends in middle of conversion specifier",
+ "logging-format-truncated",
+ "Used when a logging statement format string terminates before "
+ "the end of a conversion specifier.",
+ ),
+ "E1205": (
+ "Too many arguments for logging format string",
+ "logging-too-many-args",
+ "Used when a logging format string is given too many arguments.",
+ ),
+ "E1206": (
+ "Not enough arguments for logging format string",
+ "logging-too-few-args",
+ "Used when a logging format string is given too few arguments.",
+ ),
+}
+
+
+CHECKED_CONVENIENCE_FUNCTIONS = {
+ "critical",
+ "debug",
+ "error",
+ "exception",
+ "fatal",
+ "info",
+ "warn",
+ "warning",
+}
+
+
+def is_method_call(func, types=(), methods=()):
+ """Determines if a BoundMethod node represents a method call.
+
+ Args:
+ func (astroid.BoundMethod): The BoundMethod AST node to check.
+ types (Optional[String]): Optional sequence of caller type names to restrict check.
+ methods (Optional[String]): Optional sequence of method names to restrict check.
+
+ Returns:
+ bool: true if the node represents a method call for the given type and
+ method names, False otherwise.
+ """
+ return (
+ isinstance(func, astroid.BoundMethod)
+ and isinstance(func.bound, astroid.Instance)
+ and (func.bound.name in types if types else True)
+ and (func.name in methods if methods else True)
+ )
+
+
+class LoggingChecker(checkers.BaseChecker):
+ """Checks use of the logging module."""
+
+ __implements__ = interfaces.IAstroidChecker
+ name = "logging"
+ msgs = MSGS
+
+ options = (
+ (
+ "logging-modules",
+ {
+ "default": ("logging",),
+ "type": "csv",
+ "metavar": "<comma separated list>",
+ "help": "Logging modules to check that the string format "
+ "arguments are in logging function parameter format.",
+ },
+ ),
+ (
+ "logging-format-style",
+ {
+ "default": "old",
+ "type": "choice",
+ "metavar": "<old (%) or new ({) or fstr (f'')>",
+ "choices": ["old", "new", "fstr"],
+ "help": "Format style used to check logging format string. "
+ "`old` means using % formatting, `new` is for `{}` formatting,"
+ "and `fstr` is for f-strings.",
+ },
+ ),
+ )
+
+ def visit_module(self, node): # pylint: disable=unused-argument
+ """Clears any state left in this checker from last module checked."""
+ # The code being checked can just as easily "import logging as foo",
+ # so it is necessary to process the imports and store in this field
+ # what name the logging module is actually given.
+ self._logging_names = set()
+ logging_mods = self.config.logging_modules
+
+ self._format_style = self.config.logging_format_style
+ format_styles = {"old": "%", "new": "{", "fstr": "f-string"}
+ format_style_help = ""
+ if self._format_style == "old":
+ format_style_help = " and pass the % parameters as arguments"
+
+ self._format_style_args = (format_styles[self._format_style], format_style_help)
+
+ self._logging_modules = set(logging_mods)
+ self._from_imports = {}
+ for logging_mod in logging_mods:
+ parts = logging_mod.rsplit(".", 1)
+ if len(parts) > 1:
+ self._from_imports[parts[0]] = parts[1]
+
+ def visit_importfrom(self, node):
+ """Checks to see if a module uses a non-Python logging module."""
+ try:
+ logging_name = self._from_imports[node.modname]
+ for module, as_name in node.names:
+ if module == logging_name:
+ self._logging_names.add(as_name or module)
+ except KeyError:
+ pass
+
+ def visit_import(self, node):
+ """Checks to see if this module uses Python's built-in logging."""
+ for module, as_name in node.names:
+ if module in self._logging_modules:
+ self._logging_names.add(as_name or module)
+
+ @check_messages(*MSGS)
+ def visit_call(self, node):
+ """Checks calls to logging methods."""
+
+ def is_logging_name():
+ return (
+ isinstance(node.func, astroid.Attribute)
+ and isinstance(node.func.expr, astroid.Name)
+ and node.func.expr.name in self._logging_names
+ )
+
+ def is_logger_class():
+ try:
+ for inferred in node.func.infer():
+ if isinstance(inferred, astroid.BoundMethod):
+ parent = inferred._proxied.parent
+ if isinstance(parent, astroid.ClassDef) and (
+ parent.qname() == "logging.Logger"
+ or any(
+ ancestor.qname() == "logging.Logger"
+ for ancestor in parent.ancestors()
+ )
+ ):
+ return True, inferred._proxied.name
+ except astroid.exceptions.InferenceError:
+ pass
+ return False, None
+
+ if is_logging_name():
+ name = node.func.attrname
+ else:
+ result, name = is_logger_class()
+ if not result:
+ return
+ self._check_log_method(node, name)
+
+ def _check_log_method(self, node, name):
+ """Checks calls to logging.log(level, format, *format_args)."""
+ if name == "log":
+ if node.starargs or node.kwargs or len(node.args) < 2:
+ # Either a malformed call, star args, or double-star args. Beyond
+ # the scope of this checker.
+ return
+ format_pos = 1
+ elif name in CHECKED_CONVENIENCE_FUNCTIONS:
+ if node.starargs or node.kwargs or not node.args:
+ # Either no args, star args, or double-star args. Beyond the
+ # scope of this checker.
+ return
+ format_pos = 0
+ else:
+ return
+
+ if isinstance(node.args[format_pos], astroid.BinOp):
+ binop = node.args[format_pos]
+ emit = binop.op == "%"
+ if binop.op == "+":
+ total_number_of_strings = sum(
+ 1
+ for operand in (binop.left, binop.right)
+ if self._is_operand_literal_str(utils.safe_infer(operand))
+ )
+ emit = total_number_of_strings > 0
+ if emit:
+ self.add_message("logging-not-lazy", node=node)
+ elif isinstance(node.args[format_pos], astroid.Call):
+ self._check_call_func(node.args[format_pos])
+ elif isinstance(node.args[format_pos], astroid.Const):
+ self._check_format_string(node, format_pos)
+ elif isinstance(
+ node.args[format_pos], (astroid.FormattedValue, astroid.JoinedStr)
+ ):
+ if self._format_style != "fstr":
+ self.add_message(
+ "logging-format-interpolation",
+ node=node,
+ args=self._format_style_args,
+ )
+
+ @staticmethod
+ def _is_operand_literal_str(operand):
+ """
+ Return True if the operand in argument is a literal string
+ """
+ return isinstance(operand, astroid.Const) and operand.name == "str"
+
+ def _check_call_func(self, node):
+ """Checks that function call is not format_string.format().
+
+ Args:
+ node (astroid.node_classes.Call):
+ Call AST node to be checked.
+ """
+ func = utils.safe_infer(node.func)
+ types = ("str", "unicode")
+ methods = ("format",)
+ if is_method_call(func, types, methods) and not is_complex_format_str(
+ func.bound
+ ):
+ self.add_message(
+ "logging-format-interpolation", node=node, args=self._format_style_args
+ )
+
+ def _check_format_string(self, node, format_arg):
+ """Checks that format string tokens match the supplied arguments.
+
+ Args:
+ node (astroid.node_classes.NodeNG): AST node to be checked.
+ format_arg (int): Index of the format string in the node arguments.
+ """
+ num_args = _count_supplied_tokens(node.args[format_arg + 1 :])
+ if not num_args:
+ # If no args were supplied the string is not interpolated and can contain
+ # formatting characters - it's used verbatim. Don't check any further.
+ return
+
+ format_string = node.args[format_arg].value
+ required_num_args = 0
+ if isinstance(format_string, bytes):
+ format_string = format_string.decode()
+ if isinstance(format_string, str):
+ try:
+ if self._format_style == "old":
+ keyword_args, required_num_args, _, _ = utils.parse_format_string(
+ format_string
+ )
+ if keyword_args:
+ # Keyword checking on logging strings is complicated by
+ # special keywords - out of scope.
+ return
+ elif self._format_style == "new":
+ keyword_arguments, implicit_pos_args, explicit_pos_args = utils.parse_format_method_string(
+ format_string
+ )
+
+ keyword_args_cnt = len(
+ set(k for k, l in keyword_arguments if not isinstance(k, int))
+ )
+ required_num_args = (
+ keyword_args_cnt + implicit_pos_args + explicit_pos_args
+ )
+ else:
+ self.add_message(
+ "logging-format-interpolation",
+ node=node,
+ args=self._format_style_args,
+ )
+ except utils.UnsupportedFormatCharacter as ex:
+ char = format_string[ex.index]
+ self.add_message(
+ "logging-unsupported-format",
+ node=node,
+ args=(char, ord(char), ex.index),
+ )
+ return
+ except utils.IncompleteFormatString:
+ self.add_message("logging-format-truncated", node=node)
+ return
+ if num_args > required_num_args:
+ self.add_message("logging-too-many-args", node=node)
+ elif num_args < required_num_args:
+ self.add_message("logging-too-few-args", node=node)
+
+
+def is_complex_format_str(node):
+ """Checks if node represents a string with complex formatting specs.
+
+ Args:
+ node (astroid.node_classes.NodeNG): AST node to check
+ Returns:
+ bool: True if inferred string uses complex formatting, False otherwise
+ """
+ inferred = utils.safe_infer(node)
+ if inferred is None or not (
+ isinstance(inferred, astroid.Const) and isinstance(inferred.value, str)
+ ):
+ return True
+ try:
+ parsed = list(string.Formatter().parse(inferred.value))
+ except ValueError:
+ # This format string is invalid
+ return False
+ for _, _, format_spec, _ in parsed:
+ if format_spec:
+ return True
+ return False
+
+
+def _count_supplied_tokens(args):
+ """Counts the number of tokens in an args list.
+
+ The Python log functions allow for special keyword arguments: func,
+ exc_info and extra. To handle these cases correctly, we only count
+ arguments that aren't keywords.
+
+ Args:
+ args (list): AST nodes that are arguments for a log format string.
+
+ Returns:
+ int: Number of AST nodes that aren't keywords.
+ """
+ return sum(1 for arg in args if not isinstance(arg, astroid.Keyword))
+
+
+def register(linter):
+ """Required method to auto-register this checker."""
+ linter.register_checker(LoggingChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/misc.py b/venv/Lib/site-packages/pylint/checkers/misc.py
new file mode 100644
index 0000000..dcf7a3e
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/misc.py
@@ -0,0 +1,171 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006, 2009-2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Alexandru Coman <fcoman@bitdefender.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 glegoux <gilles.legoux@gmail.com>
+# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+
+"""Check source code is ascii only or has an encoding declaration (PEP 263)"""
+
+import re
+import tokenize
+
+from pylint.checkers import BaseChecker
+from pylint.constants import OPTION_RGX
+from pylint.interfaces import IRawChecker, ITokenChecker
+from pylint.message import MessagesHandlerMixIn
+
+
+class ByIdManagedMessagesChecker(BaseChecker):
+
+ """checks for messages that are enabled or disabled by id instead of symbol."""
+
+ __implements__ = IRawChecker
+
+ # configuration section name
+ name = "miscellaneous"
+ msgs = {
+ "I0023": (
+ "%s",
+ "use-symbolic-message-instead",
+ "Used when a message is enabled or disabled by id.",
+ )
+ }
+
+ options = ()
+
+ def process_module(self, module):
+ """inspect the source file to find messages activated or deactivated by id."""
+ managed_msgs = MessagesHandlerMixIn.get_by_id_managed_msgs()
+ for (mod_name, msg_id, msg_symbol, lineno, is_disabled) in managed_msgs:
+ if mod_name == module.name:
+ if is_disabled:
+ txt = "Id '{ident}' is used to disable '{symbol}' message emission".format(
+ ident=msg_id, symbol=msg_symbol
+ )
+ else:
+ txt = "Id '{ident}' is used to enable '{symbol}' message emission".format(
+ ident=msg_id, symbol=msg_symbol
+ )
+ self.add_message("use-symbolic-message-instead", line=lineno, args=txt)
+ MessagesHandlerMixIn.clear_by_id_managed_msgs()
+
+
+class EncodingChecker(BaseChecker):
+
+ """checks for:
+ * warning notes in the code like FIXME, XXX
+ * encoding issues.
+ """
+
+ __implements__ = (IRawChecker, ITokenChecker)
+
+ # configuration section name
+ name = "miscellaneous"
+ msgs = {
+ "W0511": (
+ "%s",
+ "fixme",
+ "Used when a warning note as FIXME or XXX is detected.",
+ )
+ }
+
+ options = (
+ (
+ "notes",
+ {
+ "type": "csv",
+ "metavar": "<comma separated values>",
+ "default": ("FIXME", "XXX", "TODO"),
+ "help": (
+ "List of note tags to take in consideration, "
+ "separated by a comma."
+ ),
+ },
+ ),
+ )
+
+ def open(self):
+ super().open()
+ self._fixme_pattern = re.compile(
+ r"#\s*(%s)\b" % "|".join(map(re.escape, self.config.notes)), re.I
+ )
+
+ def _check_encoding(self, lineno, line, file_encoding):
+ try:
+ return line.decode(file_encoding)
+ except UnicodeDecodeError:
+ pass
+ except LookupError:
+ if line.startswith("#") and "coding" in line and file_encoding in line:
+ self.add_message(
+ "syntax-error",
+ line=lineno,
+ args='Cannot decode using encoding "{}",'
+ " bad encoding".format(file_encoding),
+ )
+
+ def process_module(self, module):
+ """inspect the source file to find encoding problem"""
+ if module.file_encoding:
+ encoding = module.file_encoding
+ else:
+ encoding = "ascii"
+
+ with module.stream() as stream:
+ for lineno, line in enumerate(stream):
+ self._check_encoding(lineno + 1, line, encoding)
+
+ def process_tokens(self, tokens):
+ """inspect the source to find fixme problems"""
+ if not self.config.notes:
+ return
+ comments = (
+ token_info for token_info in tokens if token_info.type == tokenize.COMMENT
+ )
+ for comment in comments:
+ comment_text = comment.string[1:].lstrip() # trim '#' and whitespaces
+
+ # handle pylint disable clauses
+ disable_option_match = OPTION_RGX.search(comment_text)
+ if disable_option_match:
+ try:
+ _, value = disable_option_match.group(1).split("=", 1)
+ values = [_val.strip().upper() for _val in value.split(",")]
+ if set(values) & set(self.config.notes):
+ continue
+ except ValueError:
+ self.add_message(
+ "bad-inline-option",
+ args=disable_option_match.group(1).strip(),
+ line=comment.start[0],
+ )
+ continue
+
+ # emit warnings if necessary
+ match = self._fixme_pattern.search("#" + comment_text.lower())
+ if match:
+ note = match.group(1)
+ self.add_message(
+ "fixme",
+ col_offset=comment.string.lower().index(note.lower()),
+ args=comment_text,
+ line=comment.start[0],
+ )
+
+
+def register(linter):
+ """required method to auto register this checker"""
+ linter.register_checker(EncodingChecker(linter))
+ linter.register_checker(ByIdManagedMessagesChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/newstyle.py b/venv/Lib/site-packages/pylint/checkers/newstyle.py
new file mode 100644
index 0000000..46f4e4e
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/newstyle.py
@@ -0,0 +1,127 @@
+# Copyright (c) 2006, 2008-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""check for new / old style related problems
+"""
+import astroid
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import check_messages, has_known_bases, node_frame_class
+from pylint.interfaces import IAstroidChecker
+
+MSGS = {
+ "E1003": (
+ "Bad first argument %r given to super()",
+ "bad-super-call",
+ "Used when another argument than the current class is given as "
+ "first argument of the super builtin.",
+ )
+}
+
+
+class NewStyleConflictChecker(BaseChecker):
+ """checks for usage of new style capabilities on old style classes and
+ other new/old styles conflicts problems
+ * use of property, __slots__, super
+ * "super" usage
+ """
+
+ __implements__ = (IAstroidChecker,)
+
+ # configuration section name
+ name = "newstyle"
+ # messages
+ msgs = MSGS
+ priority = -2
+ # configuration options
+ options = ()
+
+ @check_messages("bad-super-call")
+ def visit_functiondef(self, node):
+ """check use of super"""
+ # ignore actual functions or method within a new style class
+ if not node.is_method():
+ return
+ klass = node.parent.frame()
+ for stmt in node.nodes_of_class(astroid.Call):
+ if node_frame_class(stmt) != node_frame_class(node):
+ # Don't look down in other scopes.
+ continue
+
+ expr = stmt.func
+ if not isinstance(expr, astroid.Attribute):
+ continue
+
+ call = expr.expr
+ # skip the test if using super
+ if not (
+ isinstance(call, astroid.Call)
+ and isinstance(call.func, astroid.Name)
+ and call.func.name == "super"
+ ):
+ continue
+
+ # super should not be used on an old style class
+ if klass.newstyle or not has_known_bases(klass):
+ # super first arg should not be the class
+ if not call.args:
+ continue
+
+ # calling super(type(self), self) can lead to recursion loop
+ # in derived classes
+ arg0 = call.args[0]
+ if (
+ isinstance(arg0, astroid.Call)
+ and isinstance(arg0.func, astroid.Name)
+ and arg0.func.name == "type"
+ ):
+ self.add_message("bad-super-call", node=call, args=("type",))
+ continue
+
+ # calling super(self.__class__, self) can lead to recursion loop
+ # in derived classes
+ if (
+ len(call.args) >= 2
+ and isinstance(call.args[1], astroid.Name)
+ and call.args[1].name == "self"
+ and isinstance(arg0, astroid.Attribute)
+ and arg0.attrname == "__class__"
+ ):
+ self.add_message(
+ "bad-super-call", node=call, args=("self.__class__",)
+ )
+ continue
+
+ try:
+ supcls = call.args and next(call.args[0].infer(), None)
+ except astroid.InferenceError:
+ continue
+
+ if klass is not supcls:
+ name = None
+ # if supcls is not Uninferable, then supcls was inferred
+ # and use its name. Otherwise, try to look
+ # for call.args[0].name
+ if supcls:
+ name = supcls.name
+ elif call.args and hasattr(call.args[0], "name"):
+ name = call.args[0].name
+ if name:
+ self.add_message("bad-super-call", node=call, args=(name,))
+
+ visit_asyncfunctiondef = visit_functiondef
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(NewStyleConflictChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/python3.py b/venv/Lib/site-packages/pylint/checkers/python3.py
new file mode 100644
index 0000000..583b1c2
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/python3.py
@@ -0,0 +1,1398 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2015 Brett Cannon <brett@python.org>
+# Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
+# Copyright (c) 2015 Pavel Roskin <proski@gnu.org>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2015 Cosmin Poieana <cmin@ropython.org>
+# Copyright (c) 2015 Viorel Stirbu <viorels@gmail.com>
+# Copyright (c) 2016, 2018 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2016-2017 Roy Williams <roy.williams.iii@gmail.com>
+# Copyright (c) 2016 Roy Williams <rwilliams@lyft.com>
+# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 Erik <erik.eriksson@yahoo.com>
+# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2017 Daniel Miller <millerdev@gmail.com>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 ahirnish <ahirnish@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+# Copyright (c) 2018 gaurikholkar <f2013002@goa.bits-pilani.ac.in>
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Check Python 2 code for Python 2/3 source-compatible issues."""
+import re
+import tokenize
+from collections import namedtuple
+
+import astroid
+from astroid import bases
+
+from pylint import checkers, interfaces
+from pylint.checkers import utils
+from pylint.checkers.utils import find_try_except_wrapper_node, node_ignores_exception
+from pylint.constants import WarningScope
+from pylint.interfaces import INFERENCE, INFERENCE_FAILURE
+
+_ZERO = re.compile("^0+$")
+
+
+def _is_old_octal(literal):
+ if _ZERO.match(literal):
+ return False
+ if re.match(r"0\d+", literal):
+ try:
+ int(literal, 8)
+ except ValueError:
+ return False
+ return True
+ return None
+
+
+def _inferred_value_is_dict(value):
+ if isinstance(value, astroid.Dict):
+ return True
+ return isinstance(value, astroid.Instance) and "dict" in value.basenames
+
+
+def _is_builtin(node):
+ return getattr(node, "name", None) in ("__builtin__", "builtins")
+
+
+_ACCEPTS_ITERATOR = {
+ "iter",
+ "list",
+ "tuple",
+ "sorted",
+ "set",
+ "sum",
+ "any",
+ "all",
+ "enumerate",
+ "dict",
+ "filter",
+ "reversed",
+ "max",
+ "min",
+ "frozenset",
+ "OrderedDict",
+}
+ATTRIBUTES_ACCEPTS_ITERATOR = {"join", "from_iterable"}
+_BUILTIN_METHOD_ACCEPTS_ITERATOR = {
+ "builtins.list.extend",
+ "builtins.dict.update",
+ "builtins.set.update",
+}
+DICT_METHODS = {"items", "keys", "values"}
+
+
+def _in_iterating_context(node):
+ """Check if the node is being used as an iterator.
+
+ Definition is taken from lib2to3.fixer_util.in_special_context().
+ """
+ parent = node.parent
+ # Since a call can't be the loop variant we only need to know if the node's
+ # parent is a 'for' loop to know it's being used as the iterator for the
+ # loop.
+ if isinstance(parent, astroid.For):
+ return True
+ # Need to make sure the use of the node is in the iterator part of the
+ # comprehension.
+ if isinstance(parent, astroid.Comprehension):
+ if parent.iter == node:
+ return True
+ # Various built-ins can take in an iterable or list and lead to the same
+ # value.
+ elif isinstance(parent, astroid.Call):
+ if isinstance(parent.func, astroid.Name):
+ if parent.func.name in _ACCEPTS_ITERATOR:
+ return True
+ elif isinstance(parent.func, astroid.Attribute):
+ if parent.func.attrname in ATTRIBUTES_ACCEPTS_ITERATOR:
+ return True
+
+ inferred = utils.safe_infer(parent.func)
+ if inferred:
+ if inferred.qname() in _BUILTIN_METHOD_ACCEPTS_ITERATOR:
+ return True
+ root = inferred.root()
+ if root and root.name == "itertools":
+ return True
+ # If the call is in an unpacking, there's no need to warn,
+ # since it can be considered iterating.
+ elif isinstance(parent, astroid.Assign) and isinstance(
+ parent.targets[0], (astroid.List, astroid.Tuple)
+ ):
+ if len(parent.targets[0].elts) > 1:
+ return True
+ # If the call is in a containment check, we consider that to
+ # be an iterating context
+ elif (
+ isinstance(parent, astroid.Compare)
+ and len(parent.ops) == 1
+ and parent.ops[0][0] == "in"
+ ):
+ return True
+ # Also if it's an `yield from`, that's fair
+ elif isinstance(parent, astroid.YieldFrom):
+ return True
+ if isinstance(parent, astroid.Starred):
+ return True
+ return False
+
+
+def _is_conditional_import(node):
+ """Checks if an import node is in the context of a conditional.
+ """
+ parent = node.parent
+ return isinstance(
+ parent, (astroid.TryExcept, astroid.ExceptHandler, astroid.If, astroid.IfExp)
+ )
+
+
+Branch = namedtuple("Branch", ["node", "is_py2_only"])
+
+
+class Python3Checker(checkers.BaseChecker):
+
+ __implements__ = interfaces.IAstroidChecker
+ enabled = False
+ name = "python3"
+
+ msgs = {
+ # Errors for what will syntactically break in Python 3, warnings for
+ # everything else.
+ "E1601": (
+ "print statement used",
+ "print-statement",
+ "Used when a print statement is used "
+ "(`print` is a function in Python 3)",
+ ),
+ "E1602": (
+ "Parameter unpacking specified",
+ "parameter-unpacking",
+ "Used when parameter unpacking is specified for a function"
+ "(Python 3 doesn't allow it)",
+ ),
+ "E1603": (
+ "Implicit unpacking of exceptions is not supported in Python 3",
+ "unpacking-in-except",
+ "Python3 will not allow implicit unpacking of "
+ "exceptions in except clauses. "
+ "See http://www.python.org/dev/peps/pep-3110/",
+ {"old_names": [("W0712", "old-unpacking-in-except")]},
+ ),
+ "E1604": (
+ "Use raise ErrorClass(args) instead of raise ErrorClass, args.",
+ "old-raise-syntax",
+ "Used when the alternate raise syntax "
+ "'raise foo, bar' is used "
+ "instead of 'raise foo(bar)'.",
+ {"old_names": [("W0121", "old-old-raise-syntax")]},
+ ),
+ "E1605": (
+ "Use of the `` operator",
+ "backtick",
+ 'Used when the deprecated "``" (backtick) operator is used '
+ "instead of the str() function.",
+ {"scope": WarningScope.NODE, "old_names": [("W0333", "old-backtick")]},
+ ),
+ "E1609": (
+ "Import * only allowed at module level",
+ "import-star-module-level",
+ "Used when the import star syntax is used somewhere "
+ "else than the module level.",
+ {"maxversion": (3, 0)},
+ ),
+ "W1601": (
+ "apply built-in referenced",
+ "apply-builtin",
+ "Used when the apply built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1602": (
+ "basestring built-in referenced",
+ "basestring-builtin",
+ "Used when the basestring built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1603": (
+ "buffer built-in referenced",
+ "buffer-builtin",
+ "Used when the buffer built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1604": (
+ "cmp built-in referenced",
+ "cmp-builtin",
+ "Used when the cmp built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1605": (
+ "coerce built-in referenced",
+ "coerce-builtin",
+ "Used when the coerce built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1606": (
+ "execfile built-in referenced",
+ "execfile-builtin",
+ "Used when the execfile built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1607": (
+ "file built-in referenced",
+ "file-builtin",
+ "Used when the file built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1608": (
+ "long built-in referenced",
+ "long-builtin",
+ "Used when the long built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1609": (
+ "raw_input built-in referenced",
+ "raw_input-builtin",
+ "Used when the raw_input built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1610": (
+ "reduce built-in referenced",
+ "reduce-builtin",
+ "Used when the reduce built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1611": (
+ "StandardError built-in referenced",
+ "standarderror-builtin",
+ "Used when the StandardError built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1612": (
+ "unicode built-in referenced",
+ "unicode-builtin",
+ "Used when the unicode built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1613": (
+ "xrange built-in referenced",
+ "xrange-builtin",
+ "Used when the xrange built-in function is referenced "
+ "(missing from Python 3)",
+ ),
+ "W1614": (
+ "__coerce__ method defined",
+ "coerce-method",
+ "Used when a __coerce__ method is defined "
+ "(method is not used by Python 3)",
+ ),
+ "W1615": (
+ "__delslice__ method defined",
+ "delslice-method",
+ "Used when a __delslice__ method is defined "
+ "(method is not used by Python 3)",
+ ),
+ "W1616": (
+ "__getslice__ method defined",
+ "getslice-method",
+ "Used when a __getslice__ method is defined "
+ "(method is not used by Python 3)",
+ ),
+ "W1617": (
+ "__setslice__ method defined",
+ "setslice-method",
+ "Used when a __setslice__ method is defined "
+ "(method is not used by Python 3)",
+ ),
+ "W1618": (
+ "import missing `from __future__ import absolute_import`",
+ "no-absolute-import",
+ "Used when an import is not accompanied by "
+ "``from __future__ import absolute_import`` "
+ "(default behaviour in Python 3)",
+ ),
+ "W1619": (
+ "division w/o __future__ statement",
+ "old-division",
+ "Used for non-floor division w/o a float literal or "
+ "``from __future__ import division`` "
+ "(Python 3 returns a float for int division unconditionally)",
+ ),
+ "W1620": (
+ "Calling a dict.iter*() method",
+ "dict-iter-method",
+ "Used for calls to dict.iterkeys(), itervalues() or iteritems() "
+ "(Python 3 lacks these methods)",
+ ),
+ "W1621": (
+ "Calling a dict.view*() method",
+ "dict-view-method",
+ "Used for calls to dict.viewkeys(), viewvalues() or viewitems() "
+ "(Python 3 lacks these methods)",
+ ),
+ "W1622": (
+ "Called a next() method on an object",
+ "next-method-called",
+ "Used when an object's next() method is called "
+ "(Python 3 uses the next() built-in function)",
+ ),
+ "W1623": (
+ "Assigning to a class's __metaclass__ attribute",
+ "metaclass-assignment",
+ "Used when a metaclass is specified by assigning to __metaclass__ "
+ "(Python 3 specifies the metaclass as a class statement argument)",
+ ),
+ "W1624": (
+ "Indexing exceptions will not work on Python 3",
+ "indexing-exception",
+ "Indexing exceptions will not work on Python 3. Use "
+ "`exception.args[index]` instead.",
+ {"old_names": [("W0713", "old-indexing-exception")]},
+ ),
+ "W1625": (
+ "Raising a string exception",
+ "raising-string",
+ "Used when a string exception is raised. This will not "
+ "work on Python 3.",
+ {"old_names": [("W0701", "old-raising-string")]},
+ ),
+ "W1626": (
+ "reload built-in referenced",
+ "reload-builtin",
+ "Used when the reload built-in function is referenced "
+ "(missing from Python 3). You can use instead imp.reload "
+ "or importlib.reload.",
+ ),
+ "W1627": (
+ "__oct__ method defined",
+ "oct-method",
+ "Used when an __oct__ method is defined "
+ "(method is not used by Python 3)",
+ ),
+ "W1628": (
+ "__hex__ method defined",
+ "hex-method",
+ "Used when a __hex__ method is defined (method is not used by Python 3)",
+ ),
+ "W1629": (
+ "__nonzero__ method defined",
+ "nonzero-method",
+ "Used when a __nonzero__ method is defined "
+ "(method is not used by Python 3)",
+ ),
+ "W1630": (
+ "__cmp__ method defined",
+ "cmp-method",
+ "Used when a __cmp__ method is defined (method is not used by Python 3)",
+ ),
+ # 'W1631': replaced by W1636
+ "W1632": (
+ "input built-in referenced",
+ "input-builtin",
+ "Used when the input built-in is referenced "
+ "(backwards-incompatible semantics in Python 3)",
+ ),
+ "W1633": (
+ "round built-in referenced",
+ "round-builtin",
+ "Used when the round built-in is referenced "
+ "(backwards-incompatible semantics in Python 3)",
+ ),
+ "W1634": (
+ "intern built-in referenced",
+ "intern-builtin",
+ "Used when the intern built-in is referenced "
+ "(Moved to sys.intern in Python 3)",
+ ),
+ "W1635": (
+ "unichr built-in referenced",
+ "unichr-builtin",
+ "Used when the unichr built-in is referenced (Use chr in Python 3)",
+ ),
+ "W1636": (
+ "map built-in referenced when not iterating",
+ "map-builtin-not-iterating",
+ "Used when the map built-in is referenced in a non-iterating "
+ "context (returns an iterator in Python 3)",
+ {"old_names": [("W1631", "implicit-map-evaluation")]},
+ ),
+ "W1637": (
+ "zip built-in referenced when not iterating",
+ "zip-builtin-not-iterating",
+ "Used when the zip built-in is referenced in a non-iterating "
+ "context (returns an iterator in Python 3)",
+ ),
+ "W1638": (
+ "range built-in referenced when not iterating",
+ "range-builtin-not-iterating",
+ "Used when the range built-in is referenced in a non-iterating "
+ "context (returns a range in Python 3)",
+ ),
+ "W1639": (
+ "filter built-in referenced when not iterating",
+ "filter-builtin-not-iterating",
+ "Used when the filter built-in is referenced in a non-iterating "
+ "context (returns an iterator in Python 3)",
+ ),
+ "W1640": (
+ "Using the cmp argument for list.sort / sorted",
+ "using-cmp-argument",
+ "Using the cmp argument for list.sort or the sorted "
+ "builtin should be avoided, since it was removed in "
+ "Python 3. Using either `key` or `functools.cmp_to_key` "
+ "should be preferred.",
+ ),
+ "W1641": (
+ "Implementing __eq__ without also implementing __hash__",
+ "eq-without-hash",
+ "Used when a class implements __eq__ but not __hash__. In Python 2, objects "
+ "get object.__hash__ as the default implementation, in Python 3 objects get "
+ "None as their default __hash__ implementation if they also implement __eq__.",
+ ),
+ "W1642": (
+ "__div__ method defined",
+ "div-method",
+ "Used when a __div__ method is defined. Using `__truediv__` and setting"
+ "__div__ = __truediv__ should be preferred."
+ "(method is not used by Python 3)",
+ ),
+ "W1643": (
+ "__idiv__ method defined",
+ "idiv-method",
+ "Used when an __idiv__ method is defined. Using `__itruediv__` and setting"
+ "__idiv__ = __itruediv__ should be preferred."
+ "(method is not used by Python 3)",
+ ),
+ "W1644": (
+ "__rdiv__ method defined",
+ "rdiv-method",
+ "Used when a __rdiv__ method is defined. Using `__rtruediv__` and setting"
+ "__rdiv__ = __rtruediv__ should be preferred."
+ "(method is not used by Python 3)",
+ ),
+ "W1645": (
+ "Exception.message removed in Python 3",
+ "exception-message-attribute",
+ "Used when the message attribute is accessed on an Exception. Use "
+ "str(exception) instead.",
+ ),
+ "W1646": (
+ "non-text encoding used in str.decode",
+ "invalid-str-codec",
+ "Used when using str.encode or str.decode with a non-text encoding. Use "
+ "codecs module to handle arbitrary codecs.",
+ ),
+ "W1647": (
+ "sys.maxint removed in Python 3",
+ "sys-max-int",
+ "Used when accessing sys.maxint. Use sys.maxsize instead.",
+ ),
+ "W1648": (
+ "Module moved in Python 3",
+ "bad-python3-import",
+ "Used when importing a module that no longer exists in Python 3.",
+ ),
+ "W1649": (
+ "Accessing a deprecated function on the string module",
+ "deprecated-string-function",
+ "Used when accessing a string function that has been deprecated in Python 3.",
+ ),
+ "W1650": (
+ "Using str.translate with deprecated deletechars parameters",
+ "deprecated-str-translate-call",
+ "Used when using the deprecated deletechars parameters from str.translate. Use "
+ "re.sub to remove the desired characters ",
+ ),
+ "W1651": (
+ "Accessing a deprecated function on the itertools module",
+ "deprecated-itertools-function",
+ "Used when accessing a function on itertools that has been removed in Python 3.",
+ ),
+ "W1652": (
+ "Accessing a deprecated fields on the types module",
+ "deprecated-types-field",
+ "Used when accessing a field on types that has been removed in Python 3.",
+ ),
+ "W1653": (
+ "next method defined",
+ "next-method-defined",
+ "Used when a next method is defined that would be an iterator in Python 2 but "
+ "is treated as a normal function in Python 3.",
+ ),
+ "W1654": (
+ "dict.items referenced when not iterating",
+ "dict-items-not-iterating",
+ "Used when dict.items is referenced in a non-iterating "
+ "context (returns an iterator in Python 3)",
+ ),
+ "W1655": (
+ "dict.keys referenced when not iterating",
+ "dict-keys-not-iterating",
+ "Used when dict.keys is referenced in a non-iterating "
+ "context (returns an iterator in Python 3)",
+ ),
+ "W1656": (
+ "dict.values referenced when not iterating",
+ "dict-values-not-iterating",
+ "Used when dict.values is referenced in a non-iterating "
+ "context (returns an iterator in Python 3)",
+ ),
+ "W1657": (
+ "Accessing a removed attribute on the operator module",
+ "deprecated-operator-function",
+ "Used when accessing a field on operator module that has been "
+ "removed in Python 3.",
+ ),
+ "W1658": (
+ "Accessing a removed attribute on the urllib module",
+ "deprecated-urllib-function",
+ "Used when accessing a field on urllib module that has been "
+ "removed or moved in Python 3.",
+ ),
+ "W1659": (
+ "Accessing a removed xreadlines attribute",
+ "xreadlines-attribute",
+ "Used when accessing the xreadlines() function on a file stream, "
+ "removed in Python 3.",
+ ),
+ "W1660": (
+ "Accessing a removed attribute on the sys module",
+ "deprecated-sys-function",
+ "Used when accessing a field on sys module that has been "
+ "removed in Python 3.",
+ ),
+ "W1661": (
+ "Using an exception object that was bound by an except handler",
+ "exception-escape",
+ "Emitted when using an exception, that was bound in an except "
+ "handler, outside of the except handler. On Python 3 these "
+ "exceptions will be deleted once they get out "
+ "of the except handler.",
+ ),
+ "W1662": (
+ "Using a variable that was bound inside a comprehension",
+ "comprehension-escape",
+ "Emitted when using a variable, that was bound in a comprehension "
+ "handler, outside of the comprehension itself. On Python 3 these "
+ "variables will be deleted outside of the "
+ "comprehension.",
+ ),
+ }
+
+ _bad_builtins = frozenset(
+ [
+ "apply",
+ "basestring",
+ "buffer",
+ "cmp",
+ "coerce",
+ "execfile",
+ "file",
+ "input", # Not missing, but incompatible semantics
+ "intern",
+ "long",
+ "raw_input",
+ "reduce",
+ "round", # Not missing, but incompatible semantics
+ "StandardError",
+ "unichr",
+ "unicode",
+ "xrange",
+ "reload",
+ ]
+ )
+
+ _unused_magic_methods = frozenset(
+ [
+ "__coerce__",
+ "__delslice__",
+ "__getslice__",
+ "__setslice__",
+ "__oct__",
+ "__hex__",
+ "__nonzero__",
+ "__cmp__",
+ "__div__",
+ "__idiv__",
+ "__rdiv__",
+ ]
+ )
+
+ _invalid_encodings = frozenset(
+ [
+ "base64_codec",
+ "base64",
+ "base_64",
+ "bz2_codec",
+ "bz2",
+ "hex_codec",
+ "hex",
+ "quopri_codec",
+ "quopri",
+ "quotedprintable",
+ "quoted_printable",
+ "uu_codec",
+ "uu",
+ "zlib_codec",
+ "zlib",
+ "zip",
+ "rot13",
+ "rot_13",
+ ]
+ )
+
+ _bad_python3_module_map = {
+ "sys-max-int": {"sys": frozenset(["maxint"])},
+ "deprecated-itertools-function": {
+ "itertools": frozenset(
+ ["izip", "ifilter", "imap", "izip_longest", "ifilterfalse"]
+ )
+ },
+ "deprecated-types-field": {
+ "types": frozenset(
+ [
+ "EllipsisType",
+ "XRangeType",
+ "ComplexType",
+ "StringType",
+ "TypeType",
+ "LongType",
+ "UnicodeType",
+ "ClassType",
+ "BufferType",
+ "StringTypes",
+ "NotImplementedType",
+ "NoneType",
+ "InstanceType",
+ "FloatType",
+ "SliceType",
+ "UnboundMethodType",
+ "ObjectType",
+ "IntType",
+ "TupleType",
+ "ListType",
+ "DictType",
+ "FileType",
+ "DictionaryType",
+ "BooleanType",
+ "DictProxyType",
+ ]
+ )
+ },
+ "bad-python3-import": frozenset(
+ [
+ "anydbm",
+ "BaseHTTPServer",
+ "__builtin__",
+ "CGIHTTPServer",
+ "ConfigParser",
+ "copy_reg",
+ "cPickle",
+ "cStringIO",
+ "Cookie",
+ "cookielib",
+ "dbhash",
+ "dumbdbm",
+ "dumbdb",
+ "Dialog",
+ "DocXMLRPCServer",
+ "FileDialog",
+ "FixTk",
+ "gdbm",
+ "htmlentitydefs",
+ "HTMLParser",
+ "httplib",
+ "markupbase",
+ "Queue",
+ "repr",
+ "robotparser",
+ "ScrolledText",
+ "SimpleDialog",
+ "SimpleHTTPServer",
+ "SimpleXMLRPCServer",
+ "StringIO",
+ "dummy_thread",
+ "SocketServer",
+ "test.test_support",
+ "Tkinter",
+ "Tix",
+ "Tkconstants",
+ "tkColorChooser",
+ "tkCommonDialog",
+ "Tkdnd",
+ "tkFileDialog",
+ "tkFont",
+ "tkMessageBox",
+ "tkSimpleDialog",
+ "UserList",
+ "UserString",
+ "whichdb",
+ "_winreg",
+ "xmlrpclib",
+ "audiodev",
+ "Bastion",
+ "bsddb185",
+ "bsddb3",
+ "Canvas",
+ "cfmfile",
+ "cl",
+ "commands",
+ "compiler",
+ "dircache",
+ "dl",
+ "exception",
+ "fpformat",
+ "htmllib",
+ "ihooks",
+ "imageop",
+ "imputil",
+ "linuxaudiodev",
+ "md5",
+ "mhlib",
+ "mimetools",
+ "MimeWriter",
+ "mimify",
+ "multifile",
+ "mutex",
+ "new",
+ "popen2",
+ "posixfile",
+ "pure",
+ "rexec",
+ "rfc822",
+ "sets",
+ "sha",
+ "sgmllib",
+ "sre",
+ "stringold",
+ "sunaudio",
+ "sv",
+ "test.testall",
+ "thread",
+ "timing",
+ "toaiff",
+ "user",
+ "urllib2",
+ "urlparse",
+ ]
+ ),
+ "deprecated-string-function": {
+ "string": frozenset(
+ [
+ "maketrans",
+ "atof",
+ "atoi",
+ "atol",
+ "capitalize",
+ "expandtabs",
+ "find",
+ "rfind",
+ "index",
+ "rindex",
+ "count",
+ "lower",
+ "letters",
+ "split",
+ "rsplit",
+ "splitfields",
+ "join",
+ "joinfields",
+ "lstrip",
+ "rstrip",
+ "strip",
+ "swapcase",
+ "translate",
+ "upper",
+ "ljust",
+ "rjust",
+ "center",
+ "zfill",
+ "replace",
+ "lowercase",
+ "letters",
+ "uppercase",
+ "atol_error",
+ "atof_error",
+ "atoi_error",
+ "index_error",
+ ]
+ )
+ },
+ "deprecated-operator-function": {"operator": frozenset({"div"})},
+ "deprecated-urllib-function": {
+ "urllib": frozenset(
+ {
+ "addbase",
+ "addclosehook",
+ "addinfo",
+ "addinfourl",
+ "always_safe",
+ "basejoin",
+ "ftpcache",
+ "ftperrors",
+ "ftpwrapper",
+ "getproxies",
+ "getproxies_environment",
+ "getproxies_macosx_sysconf",
+ "main",
+ "noheaders",
+ "pathname2url",
+ "proxy_bypass",
+ "proxy_bypass_environment",
+ "proxy_bypass_macosx_sysconf",
+ "quote",
+ "quote_plus",
+ "reporthook",
+ "splitattr",
+ "splithost",
+ "splitnport",
+ "splitpasswd",
+ "splitport",
+ "splitquery",
+ "splittag",
+ "splittype",
+ "splituser",
+ "splitvalue",
+ "unquote",
+ "unquote_plus",
+ "unwrap",
+ "url2pathname",
+ "urlcleanup",
+ "urlencode",
+ "urlopen",
+ "urlretrieve",
+ }
+ )
+ },
+ "deprecated-sys-function": {"sys": frozenset({"exc_clear"})},
+ }
+
+ _python_2_tests = frozenset(
+ [
+ astroid.extract_node(x).repr_tree()
+ for x in [
+ "sys.version_info[0] == 2",
+ "sys.version_info[0] < 3",
+ "sys.version_info == (2, 7)",
+ "sys.version_info <= (2, 7)",
+ "sys.version_info < (3, 0)",
+ ]
+ ]
+ )
+
+ def __init__(self, *args, **kwargs):
+ self._future_division = False
+ self._future_absolute_import = False
+ self._modules_warned_about = set()
+ self._branch_stack = []
+ super(Python3Checker, self).__init__(*args, **kwargs)
+
+ # pylint: disable=keyword-arg-before-vararg, arguments-differ
+ def add_message(self, msg_id, always_warn=False, *args, **kwargs):
+ if always_warn or not (
+ self._branch_stack and self._branch_stack[-1].is_py2_only
+ ):
+ super(Python3Checker, self).add_message(msg_id, *args, **kwargs)
+
+ def _is_py2_test(self, node):
+ if isinstance(node.test, astroid.Attribute) and isinstance(
+ node.test.expr, astroid.Name
+ ):
+ if node.test.expr.name == "six" and node.test.attrname == "PY2":
+ return True
+ elif (
+ isinstance(node.test, astroid.Compare)
+ and node.test.repr_tree() in self._python_2_tests
+ ):
+ return True
+ return False
+
+ def visit_if(self, node):
+ self._branch_stack.append(Branch(node, self._is_py2_test(node)))
+
+ def leave_if(self, node):
+ assert self._branch_stack.pop().node == node
+
+ def visit_ifexp(self, node):
+ self._branch_stack.append(Branch(node, self._is_py2_test(node)))
+
+ def leave_ifexp(self, node):
+ assert self._branch_stack.pop().node == node
+
+ def visit_module(self, node): # pylint: disable=unused-argument
+ """Clear checker state after previous module."""
+ self._future_division = False
+ self._future_absolute_import = False
+
+ def visit_functiondef(self, node):
+ if node.is_method():
+ if node.name in self._unused_magic_methods:
+ method_name = node.name
+ if node.name.startswith("__"):
+ method_name = node.name[2:-2]
+ self.add_message(method_name + "-method", node=node)
+ elif node.name == "next":
+ # If there is a method named `next` declared, if it is invokable
+ # with zero arguments then it implements the Iterator protocol.
+ # This means if the method is an instance method or a
+ # classmethod 1 argument should cause a failure, if it is a
+ # staticmethod 0 arguments should cause a failure.
+ failing_arg_count = 1
+ if utils.decorated_with(node, [bases.BUILTINS + ".staticmethod"]):
+ failing_arg_count = 0
+ if len(node.args.args) == failing_arg_count:
+ self.add_message("next-method-defined", node=node)
+
+ @utils.check_messages("parameter-unpacking")
+ def visit_arguments(self, node):
+ for arg in node.args:
+ if isinstance(arg, astroid.Tuple):
+ self.add_message("parameter-unpacking", node=arg)
+
+ @utils.check_messages("comprehension-escape")
+ def visit_listcomp(self, node):
+ names = {
+ generator.target.name
+ for generator in node.generators
+ if isinstance(generator.target, astroid.AssignName)
+ }
+ scope = node.parent.scope()
+ scope_names = scope.nodes_of_class(astroid.Name, skip_klass=astroid.FunctionDef)
+ has_redefined_assign_name = any(
+ assign_name
+ for assign_name in scope.nodes_of_class(
+ astroid.AssignName, skip_klass=astroid.FunctionDef
+ )
+ if assign_name.name in names and assign_name.lineno > node.lineno
+ )
+ if has_redefined_assign_name:
+ return
+
+ emitted_for_names = set()
+ scope_names = list(scope_names)
+ for scope_name in scope_names:
+ if (
+ scope_name.name not in names
+ or scope_name.lineno <= node.lineno
+ or scope_name.name in emitted_for_names
+ or scope_name.scope() == node
+ ):
+ continue
+
+ emitted_for_names.add(scope_name.name)
+ self.add_message("comprehension-escape", node=scope_name)
+
+ def visit_name(self, node):
+ """Detect when a "bad" built-in is referenced."""
+ found_node, _ = node.lookup(node.name)
+ if not _is_builtin(found_node):
+ return
+ if node.name not in self._bad_builtins:
+ return
+ if node_ignores_exception(node) or isinstance(
+ find_try_except_wrapper_node(node), astroid.ExceptHandler
+ ):
+ return
+
+ message = node.name.lower() + "-builtin"
+ self.add_message(message, node=node)
+
+ @utils.check_messages("print-statement")
+ def visit_print(self, node):
+ self.add_message("print-statement", node=node, always_warn=True)
+
+ def _warn_if_deprecated(self, node, module, attributes, report_on_modules=True):
+ for message, module_map in self._bad_python3_module_map.items():
+ if module in module_map and module not in self._modules_warned_about:
+ if isinstance(module_map, frozenset):
+ if report_on_modules:
+ self._modules_warned_about.add(module)
+ self.add_message(message, node=node)
+ elif attributes and module_map[module].intersection(attributes):
+ self.add_message(message, node=node)
+
+ def visit_importfrom(self, node):
+ if node.modname == "__future__":
+ for name, _ in node.names:
+ if name == "division":
+ self._future_division = True
+ elif name == "absolute_import":
+ self._future_absolute_import = True
+ else:
+ if not self._future_absolute_import:
+ if self.linter.is_message_enabled("no-absolute-import"):
+ self.add_message("no-absolute-import", node=node)
+ self._future_absolute_import = True
+ if not _is_conditional_import(node) and not node.level:
+ self._warn_if_deprecated(node, node.modname, {x[0] for x in node.names})
+
+ if node.names[0][0] == "*":
+ if self.linter.is_message_enabled("import-star-module-level"):
+ if not isinstance(node.scope(), astroid.Module):
+ self.add_message("import-star-module-level", node=node)
+
+ def visit_import(self, node):
+ if not self._future_absolute_import:
+ if self.linter.is_message_enabled("no-absolute-import"):
+ self.add_message("no-absolute-import", node=node)
+ self._future_absolute_import = True
+ if not _is_conditional_import(node):
+ for name, _ in node.names:
+ self._warn_if_deprecated(node, name, None)
+
+ @utils.check_messages("metaclass-assignment")
+ def visit_classdef(self, node):
+ if "__metaclass__" in node.locals:
+ self.add_message("metaclass-assignment", node=node)
+ locals_and_methods = set(node.locals).union(x.name for x in node.mymethods())
+ if "__eq__" in locals_and_methods and "__hash__" not in locals_and_methods:
+ self.add_message("eq-without-hash", node=node)
+
+ @utils.check_messages("old-division")
+ def visit_binop(self, node):
+ if not self._future_division and node.op == "/":
+ for arg in (node.left, node.right):
+ inferred = utils.safe_infer(arg)
+ # If we can infer the object and that object is not an int, bail out.
+ if inferred and not (
+ (
+ isinstance(inferred, astroid.Const)
+ and isinstance(inferred.value, int)
+ )
+ or (
+ isinstance(inferred, astroid.Instance)
+ and inferred.name == "int"
+ )
+ ):
+ break
+ else:
+ self.add_message("old-division", node=node)
+
+ def _check_cmp_argument(self, node):
+ # Check that the `cmp` argument is used
+ kwargs = []
+ if isinstance(node.func, astroid.Attribute) and node.func.attrname == "sort":
+ inferred = utils.safe_infer(node.func.expr)
+ if not inferred:
+ return
+
+ builtins_list = "{}.list".format(bases.BUILTINS)
+ if isinstance(inferred, astroid.List) or inferred.qname() == builtins_list:
+ kwargs = node.keywords
+
+ elif isinstance(node.func, astroid.Name) and node.func.name == "sorted":
+ inferred = utils.safe_infer(node.func)
+ if not inferred:
+ return
+
+ builtins_sorted = "{}.sorted".format(bases.BUILTINS)
+ if inferred.qname() == builtins_sorted:
+ kwargs = node.keywords
+
+ for kwarg in kwargs or []:
+ if kwarg.arg == "cmp":
+ self.add_message("using-cmp-argument", node=node)
+ return
+
+ @staticmethod
+ def _is_constant_string_or_name(node):
+ if isinstance(node, astroid.Const):
+ return isinstance(node.value, str)
+ return isinstance(node, astroid.Name)
+
+ @staticmethod
+ def _is_none(node):
+ return isinstance(node, astroid.Const) and node.value is None
+
+ @staticmethod
+ def _has_only_n_positional_args(node, number_of_args):
+ return len(node.args) == number_of_args and all(node.args) and not node.keywords
+
+ @staticmethod
+ def _could_be_string(inferred_types):
+ confidence = INFERENCE if inferred_types else INFERENCE_FAILURE
+ for inferred_type in inferred_types:
+ if inferred_type is astroid.Uninferable:
+ confidence = INFERENCE_FAILURE
+ elif not (
+ isinstance(inferred_type, astroid.Const)
+ and isinstance(inferred_type.value, str)
+ ):
+ return None
+ return confidence
+
+ def visit_call(self, node):
+ self._check_cmp_argument(node)
+
+ if isinstance(node.func, astroid.Attribute):
+ inferred_types = set()
+ try:
+ for inferred_receiver in node.func.expr.infer():
+ if inferred_receiver is astroid.Uninferable:
+ continue
+ inferred_types.add(inferred_receiver)
+ if isinstance(inferred_receiver, astroid.Module):
+ self._warn_if_deprecated(
+ node,
+ inferred_receiver.name,
+ {node.func.attrname},
+ report_on_modules=False,
+ )
+ if (
+ _inferred_value_is_dict(inferred_receiver)
+ and node.func.attrname in DICT_METHODS
+ ):
+ if not _in_iterating_context(node):
+ checker = "dict-{}-not-iterating".format(node.func.attrname)
+ self.add_message(checker, node=node)
+ except astroid.InferenceError:
+ pass
+ if node.args:
+ is_str_confidence = self._could_be_string(inferred_types)
+ if is_str_confidence:
+ if (
+ node.func.attrname in ("encode", "decode")
+ and len(node.args) >= 1
+ and node.args[0]
+ ):
+ first_arg = node.args[0]
+ self._validate_encoding(first_arg, node)
+ if (
+ node.func.attrname == "translate"
+ and self._has_only_n_positional_args(node, 2)
+ and self._is_none(node.args[0])
+ and self._is_constant_string_or_name(node.args[1])
+ ):
+ # The above statement looking for calls of the form:
+ #
+ # foo.translate(None, 'abc123')
+ #
+ # or
+ #
+ # foo.translate(None, some_variable)
+ #
+ # This check is somewhat broad and _may_ have some false positives, but
+ # after checking several large codebases it did not have any false
+ # positives while finding several real issues. This call pattern seems
+ # rare enough that the trade off is worth it.
+ self.add_message(
+ "deprecated-str-translate-call",
+ node=node,
+ confidence=is_str_confidence,
+ )
+ return
+ if node.keywords:
+ return
+ if node.func.attrname == "next":
+ self.add_message("next-method-called", node=node)
+ else:
+ if node.func.attrname in ("iterkeys", "itervalues", "iteritems"):
+ self.add_message("dict-iter-method", node=node)
+ elif node.func.attrname in ("viewkeys", "viewvalues", "viewitems"):
+ self.add_message("dict-view-method", node=node)
+ elif isinstance(node.func, astroid.Name):
+ found_node = node.func.lookup(node.func.name)[0]
+ if _is_builtin(found_node):
+ if node.func.name in ("filter", "map", "range", "zip"):
+ if not _in_iterating_context(node):
+ checker = "{}-builtin-not-iterating".format(node.func.name)
+ self.add_message(checker, node=node)
+ if node.func.name == "open" and node.keywords:
+ kwargs = node.keywords
+ for kwarg in kwargs or []:
+ if kwarg.arg == "encoding":
+ self._validate_encoding(kwarg.value, node)
+ break
+
+ def _validate_encoding(self, encoding, node):
+ if isinstance(encoding, astroid.Const):
+ value = encoding.value
+ if value in self._invalid_encodings:
+ self.add_message("invalid-str-codec", node=node)
+
+ @utils.check_messages("indexing-exception")
+ def visit_subscript(self, node):
+ """ Look for indexing exceptions. """
+ try:
+ for inferred in node.value.infer():
+ if not isinstance(inferred, astroid.Instance):
+ continue
+ if utils.inherit_from_std_ex(inferred):
+ self.add_message("indexing-exception", node=node)
+ except astroid.InferenceError:
+ return
+
+ def visit_assignattr(self, node):
+ if isinstance(node.assign_type(), astroid.AugAssign):
+ self.visit_attribute(node)
+
+ def visit_delattr(self, node):
+ self.visit_attribute(node)
+
+ @utils.check_messages("exception-message-attribute", "xreadlines-attribute")
+ def visit_attribute(self, node):
+ """Look for removed attributes"""
+ if node.attrname == "xreadlines":
+ self.add_message("xreadlines-attribute", node=node)
+ return
+
+ exception_message = "message"
+ try:
+ for inferred in node.expr.infer():
+ if isinstance(inferred, astroid.Instance) and utils.inherit_from_std_ex(
+ inferred
+ ):
+ if node.attrname == exception_message:
+
+ # Exceptions with .message clearly defined are an exception
+ if exception_message in inferred.instance_attrs:
+ continue
+ self.add_message("exception-message-attribute", node=node)
+ if isinstance(inferred, astroid.Module):
+ self._warn_if_deprecated(
+ node, inferred.name, {node.attrname}, report_on_modules=False
+ )
+ except astroid.InferenceError:
+ return
+
+ @utils.check_messages("unpacking-in-except", "comprehension-escape")
+ def visit_excepthandler(self, node):
+ """Visit an except handler block and check for exception unpacking."""
+
+ def _is_used_in_except_block(node):
+ scope = node.scope()
+ current = node
+ while (
+ current
+ and current != scope
+ and not isinstance(current, astroid.ExceptHandler)
+ ):
+ current = current.parent
+ return isinstance(current, astroid.ExceptHandler) and current.type != node
+
+ if isinstance(node.name, (astroid.Tuple, astroid.List)):
+ self.add_message("unpacking-in-except", node=node)
+ return
+
+ if not node.name:
+ return
+
+ # Find any names
+ scope = node.parent.scope()
+ scope_names = scope.nodes_of_class(astroid.Name, skip_klass=astroid.FunctionDef)
+ scope_names = list(scope_names)
+ potential_leaked_names = [
+ scope_name
+ for scope_name in scope_names
+ if scope_name.name == node.name.name
+ and scope_name.lineno > node.lineno
+ and not _is_used_in_except_block(scope_name)
+ ]
+ reassignments_for_same_name = {
+ assign_name.lineno
+ for assign_name in scope.nodes_of_class(
+ astroid.AssignName, skip_klass=astroid.FunctionDef
+ )
+ if assign_name.name == node.name.name
+ }
+ for leaked_name in potential_leaked_names:
+ if any(
+ node.lineno < elem < leaked_name.lineno
+ for elem in reassignments_for_same_name
+ ):
+ continue
+ self.add_message("exception-escape", node=leaked_name)
+
+ @utils.check_messages("backtick")
+ def visit_repr(self, node):
+ self.add_message("backtick", node=node)
+
+ @utils.check_messages("raising-string", "old-raise-syntax")
+ def visit_raise(self, node):
+ """Visit a raise statement and check for raising
+ strings or old-raise-syntax.
+ """
+
+ # Ignore empty raise.
+ if node.exc is None:
+ return
+ expr = node.exc
+ if self._check_raise_value(node, expr):
+ return
+ try:
+ value = next(astroid.unpack_infer(expr))
+ except astroid.InferenceError:
+ return
+ self._check_raise_value(node, value)
+
+ def _check_raise_value(self, node, expr):
+ if isinstance(expr, astroid.Const):
+ value = expr.value
+ if isinstance(value, str):
+ self.add_message("raising-string", node=node)
+ return True
+ return None
+
+
+class Python3TokenChecker(checkers.BaseTokenChecker):
+ __implements__ = interfaces.ITokenChecker
+ name = "python3"
+ enabled = False
+
+ msgs = {
+ "E1606": (
+ "Use of long suffix",
+ "long-suffix",
+ 'Used when "l" or "L" is used to mark a long integer. '
+ "This will not work in Python 3, since `int` and `long` "
+ "types have merged.",
+ {"maxversion": (3, 0)},
+ ),
+ "E1607": (
+ "Use of the <> operator",
+ "old-ne-operator",
+ 'Used when the deprecated "<>" operator is used instead '
+ 'of "!=". This is removed in Python 3.',
+ {"maxversion": (3, 0), "old_names": [("W0331", "old-old-ne-operator")]},
+ ),
+ "E1608": (
+ "Use of old octal literal",
+ "old-octal-literal",
+ "Used when encountering the old octal syntax, "
+ "removed in Python 3. To use the new syntax, "
+ "prepend 0o on the number.",
+ {"maxversion": (3, 0)},
+ ),
+ "E1610": (
+ "Non-ascii bytes literals not supported in 3.x",
+ "non-ascii-bytes-literal",
+ "Used when non-ascii bytes literals are found in a program. "
+ "They are no longer supported in Python 3.",
+ {"maxversion": (3, 0)},
+ ),
+ }
+
+ def process_tokens(self, tokens):
+ for idx, (tok_type, token, start, _, _) in enumerate(tokens):
+ if tok_type == tokenize.NUMBER:
+ if token.lower().endswith("l"):
+ # This has a different semantic than lowercase-l-suffix.
+ self.add_message("long-suffix", line=start[0])
+ elif _is_old_octal(token):
+ self.add_message("old-octal-literal", line=start[0])
+ if tokens[idx][1] == "<>":
+ self.add_message("old-ne-operator", line=tokens[idx][2][0])
+ if tok_type == tokenize.STRING and token.startswith("b"):
+ if any(elem for elem in token if ord(elem) > 127):
+ self.add_message("non-ascii-bytes-literal", line=start[0])
+
+
+def register(linter):
+ linter.register_checker(Python3Checker(linter))
+ linter.register_checker(Python3TokenChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/raw_metrics.py b/venv/Lib/site-packages/pylint/checkers/raw_metrics.py
new file mode 100644
index 0000000..0564398
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/raw_metrics.py
@@ -0,0 +1,119 @@
+# Copyright (c) 2007, 2010, 2013, 2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2013 Google, Inc.
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Mike Frysinger <vapier@gentoo.org>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+""" Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
+ http://www.logilab.fr/ -- mailto:contact@logilab.fr
+
+Raw metrics checker
+"""
+
+import tokenize
+from typing import Any
+
+from pylint.checkers import BaseTokenChecker
+from pylint.exceptions import EmptyReportError
+from pylint.interfaces import ITokenChecker
+from pylint.reporters.ureports.nodes import Table
+
+
+def report_raw_stats(sect, stats, _):
+ """calculate percentage of code / doc / comment / empty
+ """
+ total_lines = stats["total_lines"]
+ if not total_lines:
+ raise EmptyReportError()
+ sect.description = "%s lines have been analyzed" % total_lines
+ lines = ("type", "number", "%", "previous", "difference")
+ for node_type in ("code", "docstring", "comment", "empty"):
+ key = node_type + "_lines"
+ total = stats[key]
+ percent = float(total * 100) / total_lines
+ lines += (node_type, str(total), "%.2f" % percent, "NC", "NC")
+ sect.append(Table(children=lines, cols=5, rheaders=1))
+
+
+class RawMetricsChecker(BaseTokenChecker):
+ """does not check anything but gives some raw metrics :
+ * total number of lines
+ * total number of code lines
+ * total number of docstring lines
+ * total number of comments lines
+ * total number of empty lines
+ """
+
+ __implements__ = (ITokenChecker,)
+
+ # configuration section name
+ name = "metrics"
+ # configuration options
+ options = ()
+ # messages
+ msgs = {} # type: Any
+ # reports
+ reports = (("RP0701", "Raw metrics", report_raw_stats),)
+
+ def __init__(self, linter):
+ BaseTokenChecker.__init__(self, linter)
+ self.stats = None
+
+ def open(self):
+ """init statistics"""
+ self.stats = self.linter.add_stats(
+ total_lines=0,
+ code_lines=0,
+ empty_lines=0,
+ docstring_lines=0,
+ comment_lines=0,
+ )
+
+ def process_tokens(self, tokens):
+ """update stats"""
+ i = 0
+ tokens = list(tokens)
+ while i < len(tokens):
+ i, lines_number, line_type = get_type(tokens, i)
+ self.stats["total_lines"] += lines_number
+ self.stats[line_type] += lines_number
+
+
+JUNK = (tokenize.NL, tokenize.INDENT, tokenize.NEWLINE, tokenize.ENDMARKER)
+
+
+def get_type(tokens, start_index):
+ """return the line type : docstring, comment, code, empty"""
+ i = start_index
+ tok_type = tokens[i][0]
+ start = tokens[i][2]
+ pos = start
+ line_type = None
+ while i < len(tokens) and tokens[i][2][0] == start[0]:
+ tok_type = tokens[i][0]
+ pos = tokens[i][3]
+ if line_type is None:
+ if tok_type == tokenize.STRING:
+ line_type = "docstring_lines"
+ elif tok_type == tokenize.COMMENT:
+ line_type = "comment_lines"
+ elif tok_type in JUNK:
+ pass
+ else:
+ line_type = "code_lines"
+ i += 1
+ if line_type is None:
+ line_type = "empty_lines"
+ elif i < len(tokens) and tokens[i][0] == tokenize.NEWLINE:
+ i += 1
+ return i, pos[0] - start[0] + 1, line_type
+
+
+def register(linter):
+ """ required method to auto register this checker """
+ linter.register_checker(RawMetricsChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/refactoring.py b/venv/Lib/site-packages/pylint/checkers/refactoring.py
new file mode 100644
index 0000000..2831343
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/refactoring.py
@@ -0,0 +1,1510 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2016-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
+# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
+# Copyright (c) 2017 Łukasz Sznuk <ls@rdprojekt.pl>
+# Copyright (c) 2017 Alex Hearn <alex.d.hearn@gmail.com>
+# Copyright (c) 2017 Antonio Ossa <aaossa@uc.cl>
+# Copyright (c) 2018 Konstantin Manna <Konstantin@Manna.uno>
+# Copyright (c) 2018 Konstantin <Github@pheanex.de>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Matej Marušák <marusak.matej@gmail.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+# Copyright (c) 2018 Mr. Senko <atodorov@mrsenko.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Looks for code which can be refactored."""
+import builtins
+import collections
+import itertools
+import tokenize
+from functools import reduce
+
+import astroid
+from astroid import decorators
+
+from pylint import checkers, interfaces
+from pylint import utils as lint_utils
+from pylint.checkers import utils
+
+KNOWN_INFINITE_ITERATORS = {"itertools.count"}
+BUILTIN_EXIT_FUNCS = frozenset(("quit", "exit"))
+
+
+def _if_statement_is_always_returning(if_node, returning_node_class):
+ for node in if_node.body:
+ if isinstance(node, returning_node_class):
+ return True
+ return False
+
+
+def _is_len_call(node):
+ """Checks if node is len(SOMETHING)."""
+ return (
+ isinstance(node, astroid.Call)
+ and isinstance(node.func, astroid.Name)
+ and node.func.name == "len"
+ )
+
+
+def _is_constant_zero(node):
+ return isinstance(node, astroid.Const) and node.value == 0
+
+
+def _node_is_test_condition(node):
+ """ Checks if node is an if, while, assert or if expression statement."""
+ return isinstance(node, (astroid.If, astroid.While, astroid.Assert, astroid.IfExp))
+
+
+def _is_trailing_comma(tokens, index):
+ """Check if the given token is a trailing comma
+
+ :param tokens: Sequence of modules tokens
+ :type tokens: list[tokenize.TokenInfo]
+ :param int index: Index of token under check in tokens
+ :returns: True if the token is a comma which trails an expression
+ :rtype: bool
+ """
+ token = tokens[index]
+ if token.exact_type != tokenize.COMMA:
+ return False
+ # Must have remaining tokens on the same line such as NEWLINE
+ left_tokens = itertools.islice(tokens, index + 1, None)
+ same_line_remaining_tokens = list(
+ itertools.takewhile(
+ lambda other_token, _token=token: other_token.start[0] == _token.start[0],
+ left_tokens,
+ )
+ )
+ # Note: If the newline is tokenize.NEWLINE and not tokenize.NL
+ # then the newline denotes the end of expression
+ is_last_element = all(
+ other_token.type in (tokenize.NEWLINE, tokenize.COMMENT)
+ for other_token in same_line_remaining_tokens
+ )
+ if not same_line_remaining_tokens or not is_last_element:
+ return False
+
+ def get_curline_index_start():
+ """Get the index denoting the start of the current line"""
+ for subindex, token in enumerate(reversed(tokens[:index])):
+ # See Lib/tokenize.py and Lib/token.py in cpython for more info
+ if token.type in (tokenize.NEWLINE, tokenize.NL):
+ return index - subindex
+ return 0
+
+ curline_start = get_curline_index_start()
+ expected_tokens = {"return", "yield"}
+ for prevtoken in tokens[curline_start:index]:
+ if "=" in prevtoken.string or prevtoken.string in expected_tokens:
+ return True
+ return False
+
+
+class RefactoringChecker(checkers.BaseTokenChecker):
+ """Looks for code which can be refactored
+
+ This checker also mixes the astroid and the token approaches
+ in order to create knowledge about whether an "else if" node
+ is a true "else if" node, or an "elif" node.
+ """
+
+ __implements__ = (interfaces.ITokenChecker, interfaces.IAstroidChecker)
+
+ name = "refactoring"
+
+ msgs = {
+ "R1701": (
+ "Consider merging these isinstance calls to isinstance(%s, (%s))",
+ "consider-merging-isinstance",
+ "Used when multiple consecutive isinstance calls can be merged into one.",
+ ),
+ "R1706": (
+ "Consider using ternary (%s)",
+ "consider-using-ternary",
+ "Used when one of known pre-python 2.5 ternary syntax is used.",
+ ),
+ "R1709": (
+ "Boolean expression may be simplified to %s",
+ "simplify-boolean-expression",
+ "Emitted when redundant pre-python 2.5 ternary syntax is used.",
+ ),
+ "R1702": (
+ "Too many nested blocks (%s/%s)",
+ "too-many-nested-blocks",
+ "Used when a function or a method has too many nested "
+ "blocks. This makes the code less understandable and "
+ "maintainable.",
+ {"old_names": [("R0101", "old-too-many-nested-blocks")]},
+ ),
+ "R1703": (
+ "The if statement can be replaced with %s",
+ "simplifiable-if-statement",
+ "Used when an if statement can be replaced with 'bool(test)'. ",
+ {"old_names": [("R0102", "old-simplifiable-if-statement")]},
+ ),
+ "R1704": (
+ "Redefining argument with the local name %r",
+ "redefined-argument-from-local",
+ "Used when a local name is redefining an argument, which might "
+ "suggest a potential error. This is taken in account only for "
+ "a handful of name binding operations, such as for iteration, "
+ "with statement assignment and exception handler assignment.",
+ ),
+ "R1705": (
+ 'Unnecessary "%s" after "return"',
+ "no-else-return",
+ "Used in order to highlight an unnecessary block of "
+ "code following an if containing a return statement. "
+ "As such, it will warn when it encounters an else "
+ "following a chain of ifs, all of them containing a "
+ "return statement.",
+ ),
+ "R1707": (
+ "Disallow trailing comma tuple",
+ "trailing-comma-tuple",
+ "In Python, a tuple is actually created by the comma symbol, "
+ "not by the parentheses. Unfortunately, one can actually create a "
+ "tuple by misplacing a trailing comma, which can lead to potential "
+ "weird bugs in your code. You should always use parentheses "
+ "explicitly for creating a tuple.",
+ ),
+ "R1708": (
+ "Do not raise StopIteration in generator, use return statement instead",
+ "stop-iteration-return",
+ "According to PEP479, the raise of StopIteration to end the loop of "
+ "a generator may lead to hard to find bugs. This PEP specify that "
+ "raise StopIteration has to be replaced by a simple return statement",
+ ),
+ "R1710": (
+ "Either all return statements in a function should return an expression, "
+ "or none of them should.",
+ "inconsistent-return-statements",
+ "According to PEP8, if any return statement returns an expression, "
+ "any return statements where no value is returned should explicitly "
+ "state this as return None, and an explicit return statement "
+ "should be present at the end of the function (if reachable)",
+ ),
+ "R1711": (
+ "Useless return at end of function or method",
+ "useless-return",
+ 'Emitted when a single "return" or "return None" statement is found '
+ "at the end of function or method definition. This statement can safely be "
+ "removed because Python will implicitly return None",
+ ),
+ "R1712": (
+ "Consider using tuple unpacking for swapping variables",
+ "consider-swap-variables",
+ "You do not have to use a temporary variable in order to "
+ 'swap variables. Using "tuple unpacking" to directly swap '
+ "variables makes the intention more clear.",
+ ),
+ "R1713": (
+ "Consider using str.join(sequence) for concatenating "
+ "strings from an iterable",
+ "consider-using-join",
+ "Using str.join(sequence) is faster, uses less memory "
+ "and increases readability compared to for-loop iteration.",
+ ),
+ "R1714": (
+ 'Consider merging these comparisons with "in" to %r',
+ "consider-using-in",
+ "To check if a variable is equal to one of many values,"
+ 'combine the values into a tuple and check if the variable is contained "in" it '
+ "instead of checking for equality against each of the values."
+ "This is faster and less verbose.",
+ ),
+ "R1715": (
+ "Consider using dict.get for getting values from a dict "
+ "if a key is present or a default if not",
+ "consider-using-get",
+ "Using the builtin dict.get for getting a value from a dictionary "
+ "if a key is present or a default if not, is simpler and considered "
+ "more idiomatic, although sometimes a bit slower",
+ ),
+ "R1716": (
+ "Simplify chained comparison between the operands",
+ "chained-comparison",
+ "This message is emitted when pylint encounters boolean operation like"
+ '"a < b and b < c", suggesting instead to refactor it to "a < b < c"',
+ ),
+ "R1717": (
+ "Consider using a dictionary comprehension",
+ "consider-using-dict-comprehension",
+ "Emitted when we detect the creation of a dictionary "
+ "using the dict() callable and a transient list. "
+ "Although there is nothing syntactically wrong with this code, "
+ "it is hard to read and can be simplified to a dict comprehension."
+ "Also it is faster since you don't need to create another "
+ "transient list",
+ ),
+ "R1718": (
+ "Consider using a set comprehension",
+ "consider-using-set-comprehension",
+ "Although there is nothing syntactically wrong with this code, "
+ "it is hard to read and can be simplified to a set comprehension."
+ "Also it is faster since you don't need to create another "
+ "transient list",
+ ),
+ "R1719": (
+ "The if expression can be replaced with %s",
+ "simplifiable-if-expression",
+ "Used when an if expression can be replaced with 'bool(test)'. ",
+ ),
+ "R1720": (
+ 'Unnecessary "%s" after "raise"',
+ "no-else-raise",
+ "Used in order to highlight an unnecessary block of "
+ "code following an if containing a raise statement. "
+ "As such, it will warn when it encounters an else "
+ "following a chain of ifs, all of them containing a "
+ "raise statement.",
+ ),
+ "R1721": (
+ "Unnecessary use of a comprehension",
+ "unnecessary-comprehension",
+ "Instead of using an identitiy comprehension, "
+ "consider using the list, dict or set constructor. "
+ "It is faster and simpler.",
+ ),
+ "R1722": (
+ "Consider using sys.exit()",
+ "consider-using-sys-exit",
+ "Instead of using exit() or quit(), consider using the sys.exit().",
+ ),
+ "R1723": (
+ 'Unnecessary "%s" after "break"',
+ "no-else-break",
+ "Used in order to highlight an unnecessary block of "
+ "code following an if containing a break statement. "
+ "As such, it will warn when it encounters an else "
+ "following a chain of ifs, all of them containing a "
+ "break statement.",
+ ),
+ "R1724": (
+ 'Unnecessary "%s" after "continue"',
+ "no-else-continue",
+ "Used in order to highlight an unnecessary block of "
+ "code following an if containing a continue statement. "
+ "As such, it will warn when it encounters an else "
+ "following a chain of ifs, all of them containing a "
+ "continue statement.",
+ ),
+ }
+ options = (
+ (
+ "max-nested-blocks",
+ {
+ "default": 5,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Maximum number of nested blocks for function / method body",
+ },
+ ),
+ (
+ "never-returning-functions",
+ {
+ "default": ("sys.exit",),
+ "type": "csv",
+ "help": "Complete name of functions that never returns. When checking "
+ "for inconsistent-return-statements if a never returning function is "
+ "called then it will be considered as an explicit return statement "
+ "and no message will be printed.",
+ },
+ ),
+ )
+
+ priority = 0
+
+ def __init__(self, linter=None):
+ checkers.BaseTokenChecker.__init__(self, linter)
+ self._return_nodes = {}
+ self._init()
+ self._never_returning_functions = None
+
+ def _init(self):
+ self._nested_blocks = []
+ self._elifs = []
+ self._nested_blocks_msg = None
+ self._reported_swap_nodes = set()
+
+ def open(self):
+ # do this in open since config not fully initialized in __init__
+ self._never_returning_functions = set(self.config.never_returning_functions)
+
+ @decorators.cachedproperty
+ def _dummy_rgx(self):
+ return lint_utils.get_global_option(self, "dummy-variables-rgx", default=None)
+
+ @staticmethod
+ def _is_bool_const(node):
+ return isinstance(node.value, astroid.Const) and isinstance(
+ node.value.value, bool
+ )
+
+ def _is_actual_elif(self, node):
+ """Check if the given node is an actual elif
+
+ This is a problem we're having with the builtin ast module,
+ which splits `elif` branches into a separate if statement.
+ Unfortunately we need to know the exact type in certain
+ cases.
+ """
+ if isinstance(node.parent, astroid.If):
+ orelse = node.parent.orelse
+ # current if node must directly follow an "else"
+ if orelse and orelse == [node]:
+ if (node.lineno, node.col_offset) in self._elifs:
+ return True
+ return False
+
+ def _check_simplifiable_if(self, node):
+ """Check if the given if node can be simplified.
+
+ The if statement can be reduced to a boolean expression
+ in some cases. For instance, if there are two branches
+ and both of them return a boolean value that depends on
+ the result of the statement's test, then this can be reduced
+ to `bool(test)` without losing any functionality.
+ """
+
+ if self._is_actual_elif(node):
+ # Not interested in if statements with multiple branches.
+ return
+ if len(node.orelse) != 1 or len(node.body) != 1:
+ return
+
+ # Check if both branches can be reduced.
+ first_branch = node.body[0]
+ else_branch = node.orelse[0]
+ if isinstance(first_branch, astroid.Return):
+ if not isinstance(else_branch, astroid.Return):
+ return
+ first_branch_is_bool = self._is_bool_const(first_branch)
+ else_branch_is_bool = self._is_bool_const(else_branch)
+ reduced_to = "'return bool(test)'"
+ elif isinstance(first_branch, astroid.Assign):
+ if not isinstance(else_branch, astroid.Assign):
+ return
+
+ # Check if we assign to the same value
+ first_branch_targets = [
+ target.name
+ for target in first_branch.targets
+ if isinstance(target, astroid.AssignName)
+ ]
+ else_branch_targets = [
+ target.name
+ for target in else_branch.targets
+ if isinstance(target, astroid.AssignName)
+ ]
+ if not first_branch_targets or not else_branch_targets:
+ return
+ if sorted(first_branch_targets) != sorted(else_branch_targets):
+ return
+
+ first_branch_is_bool = self._is_bool_const(first_branch)
+ else_branch_is_bool = self._is_bool_const(else_branch)
+ reduced_to = "'var = bool(test)'"
+ else:
+ return
+
+ if not first_branch_is_bool or not else_branch_is_bool:
+ return
+ if not first_branch.value.value:
+ # This is a case that can't be easily simplified and
+ # if it can be simplified, it will usually result in a
+ # code that's harder to understand and comprehend.
+ # Let's take for instance `arg and arg <= 3`. This could theoretically be
+ # reduced to `not arg or arg > 3`, but the net result is that now the
+ # condition is harder to understand, because it requires understanding of
+ # an extra clause:
+ # * first, there is the negation of truthness with `not arg`
+ # * the second clause is `arg > 3`, which occurs when arg has a
+ # a truth value, but it implies that `arg > 3` is equivalent
+ # with `arg and arg > 3`, which means that the user must
+ # think about this assumption when evaluating `arg > 3`.
+ # The original form is easier to grasp.
+ return
+
+ self.add_message("simplifiable-if-statement", node=node, args=(reduced_to,))
+
+ def process_tokens(self, tokens):
+ # Process tokens and look for 'if' or 'elif'
+ for index, token in enumerate(tokens):
+ token_string = token[1]
+ if token_string == "elif":
+ # AST exists by the time process_tokens is called, so
+ # it's safe to assume tokens[index+1]
+ # exists. tokens[index+1][2] is the elif's position as
+ # reported by CPython and PyPy,
+ # tokens[index][2] is the actual position and also is
+ # reported by IronPython.
+ self._elifs.extend([tokens[index][2], tokens[index + 1][2]])
+ elif _is_trailing_comma(tokens, index):
+ if self.linter.is_message_enabled("trailing-comma-tuple"):
+ self.add_message("trailing-comma-tuple", line=token.start[0])
+
+ def leave_module(self, _):
+ self._init()
+
+ @utils.check_messages("too-many-nested-blocks")
+ def visit_tryexcept(self, node):
+ self._check_nested_blocks(node)
+
+ visit_tryfinally = visit_tryexcept
+ visit_while = visit_tryexcept
+
+ def _check_redefined_argument_from_local(self, name_node):
+ if self._dummy_rgx and self._dummy_rgx.match(name_node.name):
+ return
+ if not name_node.lineno:
+ # Unknown position, maybe it is a manually built AST?
+ return
+
+ scope = name_node.scope()
+ if not isinstance(scope, astroid.FunctionDef):
+ return
+
+ for defined_argument in scope.args.nodes_of_class(
+ astroid.AssignName, skip_klass=(astroid.Lambda,)
+ ):
+ if defined_argument.name == name_node.name:
+ self.add_message(
+ "redefined-argument-from-local",
+ node=name_node,
+ args=(name_node.name,),
+ )
+
+ @utils.check_messages("redefined-argument-from-local", "too-many-nested-blocks")
+ def visit_for(self, node):
+ self._check_nested_blocks(node)
+
+ for name in node.target.nodes_of_class(astroid.AssignName):
+ self._check_redefined_argument_from_local(name)
+
+ @utils.check_messages("redefined-argument-from-local")
+ def visit_excepthandler(self, node):
+ if node.name and isinstance(node.name, astroid.AssignName):
+ self._check_redefined_argument_from_local(node.name)
+
+ @utils.check_messages("redefined-argument-from-local")
+ def visit_with(self, node):
+ for _, names in node.items:
+ if not names:
+ continue
+ for name in names.nodes_of_class(astroid.AssignName):
+ self._check_redefined_argument_from_local(name)
+
+ def _check_superfluous_else(self, node, msg_id, returning_node_class):
+ if not node.orelse:
+ # Not interested in if statements without else.
+ return
+
+ if self._is_actual_elif(node):
+ # Not interested in elif nodes; only if
+ return
+
+ if _if_statement_is_always_returning(node, returning_node_class):
+ orelse = node.orelse[0]
+ followed_by_elif = (orelse.lineno, orelse.col_offset) in self._elifs
+ self.add_message(
+ msg_id, node=node, args="elif" if followed_by_elif else "else"
+ )
+
+ def _check_superfluous_else_return(self, node):
+ return self._check_superfluous_else(
+ node, msg_id="no-else-return", returning_node_class=astroid.Return
+ )
+
+ def _check_superfluous_else_raise(self, node):
+ return self._check_superfluous_else(
+ node, msg_id="no-else-raise", returning_node_class=astroid.Raise
+ )
+
+ def _check_superfluous_else_break(self, node):
+ return self._check_superfluous_else(
+ node, msg_id="no-else-break", returning_node_class=astroid.Break
+ )
+
+ def _check_superfluous_else_continue(self, node):
+ return self._check_superfluous_else(
+ node, msg_id="no-else-continue", returning_node_class=astroid.Continue
+ )
+
+ def _check_consider_get(self, node):
+ def type_and_name_are_equal(node_a, node_b):
+ for _type in [astroid.Name, astroid.AssignName]:
+ if all(isinstance(_node, _type) for _node in [node_a, node_b]):
+ return node_a.name == node_b.name
+ if all(isinstance(_node, astroid.Const) for _node in [node_a, node_b]):
+ return node_a.value == node_b.value
+ return False
+
+ if_block_ok = (
+ isinstance(node.test, astroid.Compare)
+ and len(node.body) == 1
+ and isinstance(node.body[0], astroid.Assign)
+ and isinstance(node.body[0].value, astroid.Subscript)
+ and type_and_name_are_equal(node.body[0].value.value, node.test.ops[0][1])
+ and isinstance(node.body[0].value.slice, astroid.Index)
+ and type_and_name_are_equal(node.body[0].value.slice.value, node.test.left)
+ and len(node.body[0].targets) == 1
+ and isinstance(node.body[0].targets[0], astroid.AssignName)
+ and isinstance(utils.safe_infer(node.test.ops[0][1]), astroid.Dict)
+ )
+
+ if if_block_ok and not node.orelse:
+ self.add_message("consider-using-get", node=node)
+ elif (
+ if_block_ok
+ and len(node.orelse) == 1
+ and isinstance(node.orelse[0], astroid.Assign)
+ and type_and_name_are_equal(
+ node.orelse[0].targets[0], node.body[0].targets[0]
+ )
+ and len(node.orelse[0].targets) == 1
+ ):
+ self.add_message("consider-using-get", node=node)
+
+ @utils.check_messages(
+ "too-many-nested-blocks",
+ "simplifiable-if-statement",
+ "no-else-return",
+ "no-else-raise",
+ "no-else-break",
+ "no-else-continue",
+ "consider-using-get",
+ )
+ def visit_if(self, node):
+ self._check_simplifiable_if(node)
+ self._check_nested_blocks(node)
+ self._check_superfluous_else_return(node)
+ self._check_superfluous_else_raise(node)
+ self._check_superfluous_else_break(node)
+ self._check_superfluous_else_continue(node)
+ self._check_consider_get(node)
+
+ @utils.check_messages("simplifiable-if-expression")
+ def visit_ifexp(self, node):
+ self._check_simplifiable_ifexp(node)
+
+ def _check_simplifiable_ifexp(self, node):
+ if not isinstance(node.body, astroid.Const) or not isinstance(
+ node.orelse, astroid.Const
+ ):
+ return
+
+ if not isinstance(node.body.value, bool) or not isinstance(
+ node.orelse.value, bool
+ ):
+ return
+
+ if isinstance(node.test, astroid.Compare):
+ test_reduced_to = "test"
+ else:
+ test_reduced_to = "bool(test)"
+
+ if (node.body.value, node.orelse.value) == (True, False):
+ reduced_to = "'{}'".format(test_reduced_to)
+ elif (node.body.value, node.orelse.value) == (False, True):
+ reduced_to = "'not test'"
+ else:
+ return
+
+ self.add_message("simplifiable-if-expression", node=node, args=(reduced_to,))
+
+ @utils.check_messages(
+ "too-many-nested-blocks", "inconsistent-return-statements", "useless-return"
+ )
+ def leave_functiondef(self, node):
+ # check left-over nested blocks stack
+ self._emit_nested_blocks_message_if_needed(self._nested_blocks)
+ # new scope = reinitialize the stack of nested blocks
+ self._nested_blocks = []
+ #  check consistent return statements
+ self._check_consistent_returns(node)
+ # check for single return or return None at the end
+ self._check_return_at_the_end(node)
+ self._return_nodes[node.name] = []
+
+ @utils.check_messages("stop-iteration-return")
+ def visit_raise(self, node):
+ self._check_stop_iteration_inside_generator(node)
+
+ def _check_stop_iteration_inside_generator(self, node):
+ """Check if an exception of type StopIteration is raised inside a generator"""
+ frame = node.frame()
+ if not isinstance(frame, astroid.FunctionDef) or not frame.is_generator():
+ return
+ if utils.node_ignores_exception(node, StopIteration):
+ return
+ if not node.exc:
+ return
+ exc = utils.safe_infer(node.exc)
+ if exc is None or exc is astroid.Uninferable:
+ return
+ if self._check_exception_inherit_from_stopiteration(exc):
+ self.add_message("stop-iteration-return", node=node)
+
+ @staticmethod
+ def _check_exception_inherit_from_stopiteration(exc):
+ """Return True if the exception node in argument inherit from StopIteration"""
+ stopiteration_qname = "{}.StopIteration".format(utils.EXCEPTIONS_MODULE)
+ return any(_class.qname() == stopiteration_qname for _class in exc.mro())
+
+ def _check_consider_using_comprehension_constructor(self, node):
+ if (
+ isinstance(node.func, astroid.Name)
+ and node.args
+ and isinstance(node.args[0], astroid.ListComp)
+ ):
+ if node.func.name == "dict" and not isinstance(
+ node.args[0].elt, astroid.Call
+ ):
+ message_name = "consider-using-dict-comprehension"
+ self.add_message(message_name, node=node)
+ elif node.func.name == "set":
+ message_name = "consider-using-set-comprehension"
+ self.add_message(message_name, node=node)
+
+ @utils.check_messages(
+ "stop-iteration-return",
+ "consider-using-dict-comprehension",
+ "consider-using-set-comprehension",
+ "consider-using-sys-exit",
+ )
+ def visit_call(self, node):
+ self._check_raising_stopiteration_in_generator_next_call(node)
+ self._check_consider_using_comprehension_constructor(node)
+ self._check_quit_exit_call(node)
+
+ @staticmethod
+ def _has_exit_in_scope(scope):
+ exit_func = scope.locals.get("exit")
+ return bool(
+ exit_func and isinstance(exit_func[0], (astroid.ImportFrom, astroid.Import))
+ )
+
+ def _check_quit_exit_call(self, node):
+
+ if isinstance(node.func, astroid.Name) and node.func.name in BUILTIN_EXIT_FUNCS:
+ # If we have `exit` imported from `sys` in the current or global scope, exempt this instance.
+ local_scope = node.scope()
+ if self._has_exit_in_scope(local_scope) or self._has_exit_in_scope(
+ node.root()
+ ):
+ return
+ self.add_message("consider-using-sys-exit", node=node)
+
+ def _check_raising_stopiteration_in_generator_next_call(self, node):
+ """Check if a StopIteration exception is raised by the call to next function
+
+ If the next value has a default value, then do not add message.
+
+ :param node: Check to see if this Call node is a next function
+ :type node: :class:`astroid.node_classes.Call`
+ """
+
+ def _looks_like_infinite_iterator(param):
+ inferred = utils.safe_infer(param)
+ if inferred:
+ return inferred.qname() in KNOWN_INFINITE_ITERATORS
+ return False
+
+ if isinstance(node.func, astroid.Attribute):
+ # A next() method, which is now what we want.
+ return
+
+ inferred = utils.safe_infer(node.func)
+ if getattr(inferred, "name", "") == "next":
+ frame = node.frame()
+ # The next builtin can only have up to two
+ # positional arguments and no keyword arguments
+ has_sentinel_value = len(node.args) > 1
+ if (
+ isinstance(frame, astroid.FunctionDef)
+ and frame.is_generator()
+ and not has_sentinel_value
+ and not utils.node_ignores_exception(node, StopIteration)
+ and not _looks_like_infinite_iterator(node.args[0])
+ ):
+ self.add_message("stop-iteration-return", node=node)
+
+ def _check_nested_blocks(self, node):
+ """Update and check the number of nested blocks
+ """
+ # only check block levels inside functions or methods
+ if not isinstance(node.scope(), astroid.FunctionDef):
+ return
+ # messages are triggered on leaving the nested block. Here we save the
+ # stack in case the current node isn't nested in the previous one
+ nested_blocks = self._nested_blocks[:]
+ if node.parent == node.scope():
+ self._nested_blocks = [node]
+ else:
+ # go through ancestors from the most nested to the less
+ for ancestor_node in reversed(self._nested_blocks):
+ if ancestor_node == node.parent:
+ break
+ self._nested_blocks.pop()
+ # if the node is an elif, this should not be another nesting level
+ if isinstance(node, astroid.If) and self._is_actual_elif(node):
+ if self._nested_blocks:
+ self._nested_blocks.pop()
+ self._nested_blocks.append(node)
+
+ # send message only once per group of nested blocks
+ if len(nested_blocks) > len(self._nested_blocks):
+ self._emit_nested_blocks_message_if_needed(nested_blocks)
+
+ def _emit_nested_blocks_message_if_needed(self, nested_blocks):
+ if len(nested_blocks) > self.config.max_nested_blocks:
+ self.add_message(
+ "too-many-nested-blocks",
+ node=nested_blocks[0],
+ args=(len(nested_blocks), self.config.max_nested_blocks),
+ )
+
+ @staticmethod
+ def _duplicated_isinstance_types(node):
+ """Get the duplicated types from the underlying isinstance calls.
+
+ :param astroid.BoolOp node: Node which should contain a bunch of isinstance calls.
+ :returns: Dictionary of the comparison objects from the isinstance calls,
+ to duplicate values from consecutive calls.
+ :rtype: dict
+ """
+ duplicated_objects = set()
+ all_types = collections.defaultdict(set)
+
+ for call in node.values:
+ if not isinstance(call, astroid.Call) or len(call.args) != 2:
+ continue
+
+ inferred = utils.safe_infer(call.func)
+ if not inferred or not utils.is_builtin_object(inferred):
+ continue
+
+ if inferred.name != "isinstance":
+ continue
+
+ isinstance_object = call.args[0].as_string()
+ isinstance_types = call.args[1]
+
+ if isinstance_object in all_types:
+ duplicated_objects.add(isinstance_object)
+
+ if isinstance(isinstance_types, astroid.Tuple):
+ elems = [
+ class_type.as_string() for class_type in isinstance_types.itered()
+ ]
+ else:
+ elems = [isinstance_types.as_string()]
+ all_types[isinstance_object].update(elems)
+
+ # Remove all keys which not duplicated
+ return {
+ key: value for key, value in all_types.items() if key in duplicated_objects
+ }
+
+ def _check_consider_merging_isinstance(self, node):
+ """Check isinstance calls which can be merged together."""
+ if node.op != "or":
+ return
+
+ first_args = self._duplicated_isinstance_types(node)
+ for duplicated_name, class_names in first_args.items():
+ names = sorted(name for name in class_names)
+ self.add_message(
+ "consider-merging-isinstance",
+ node=node,
+ args=(duplicated_name, ", ".join(names)),
+ )
+
+ def _check_consider_using_in(self, node):
+ allowed_ops = {"or": "==", "and": "!="}
+
+ if node.op not in allowed_ops or len(node.values) < 2:
+ return
+
+ for value in node.values:
+ if (
+ not isinstance(value, astroid.Compare)
+ or len(value.ops) != 1
+ or value.ops[0][0] not in allowed_ops[node.op]
+ ):
+ return
+ for comparable in value.left, value.ops[0][1]:
+ if isinstance(comparable, astroid.Call):
+ return
+
+ # Gather variables and values from comparisons
+ variables, values = [], []
+ for value in node.values:
+ variable_set = set()
+ for comparable in value.left, value.ops[0][1]:
+ if isinstance(comparable, astroid.Name):
+ variable_set.add(comparable.as_string())
+ values.append(comparable.as_string())
+ variables.append(variable_set)
+
+ # Look for (common-)variables that occur in all comparisons
+ common_variables = reduce(lambda a, b: a.intersection(b), variables)
+
+ if not common_variables:
+ return
+
+ # Gather information for the suggestion
+ common_variable = sorted(list(common_variables))[0]
+ comprehension = "in" if node.op == "or" else "not in"
+ values = list(collections.OrderedDict.fromkeys(values))
+ values.remove(common_variable)
+ values_string = ", ".join(values) if len(values) != 1 else values[0] + ","
+ suggestion = "%s %s (%s)" % (common_variable, comprehension, values_string)
+
+ self.add_message("consider-using-in", node=node, args=(suggestion,))
+
+ def _check_chained_comparison(self, node):
+ """Check if there is any chained comparison in the expression.
+
+ Add a refactoring message if a boolOp contains comparison like a < b and b < c,
+ which can be chained as a < b < c.
+
+ Care is taken to avoid simplifying a < b < c and b < d.
+ """
+ if node.op != "and" or len(node.values) < 2:
+ return
+
+ def _find_lower_upper_bounds(comparison_node, uses):
+ left_operand = comparison_node.left
+ for operator, right_operand in comparison_node.ops:
+ for operand in (left_operand, right_operand):
+ value = None
+ if isinstance(operand, astroid.Name):
+ value = operand.name
+ elif isinstance(operand, astroid.Const):
+ value = operand.value
+
+ if value is None:
+ continue
+
+ if operator in ("<", "<="):
+ if operand is left_operand:
+ uses[value]["lower_bound"].add(comparison_node)
+ elif operand is right_operand:
+ uses[value]["upper_bound"].add(comparison_node)
+ elif operator in (">", ">="):
+ if operand is left_operand:
+ uses[value]["upper_bound"].add(comparison_node)
+ elif operand is right_operand:
+ uses[value]["lower_bound"].add(comparison_node)
+ left_operand = right_operand
+
+ uses = collections.defaultdict(
+ lambda: {"lower_bound": set(), "upper_bound": set()}
+ )
+ for comparison_node in node.values:
+ if isinstance(comparison_node, astroid.Compare):
+ _find_lower_upper_bounds(comparison_node, uses)
+
+ for _, bounds in uses.items():
+ num_shared = len(bounds["lower_bound"].intersection(bounds["upper_bound"]))
+ num_lower_bounds = len(bounds["lower_bound"])
+ num_upper_bounds = len(bounds["upper_bound"])
+ if num_shared < num_lower_bounds and num_shared < num_upper_bounds:
+ self.add_message("chained-comparison", node=node)
+ break
+
+ @utils.check_messages(
+ "consider-merging-isinstance", "consider-using-in", "chained-comparison"
+ )
+ def visit_boolop(self, node):
+ self._check_consider_merging_isinstance(node)
+ self._check_consider_using_in(node)
+ self._check_chained_comparison(node)
+
+ @staticmethod
+ def _is_simple_assignment(node):
+ return (
+ isinstance(node, astroid.Assign)
+ and len(node.targets) == 1
+ and isinstance(node.targets[0], astroid.node_classes.AssignName)
+ and isinstance(node.value, astroid.node_classes.Name)
+ )
+
+ def _check_swap_variables(self, node):
+ if not node.next_sibling() or not node.next_sibling().next_sibling():
+ return
+ assignments = [node, node.next_sibling(), node.next_sibling().next_sibling()]
+ if not all(self._is_simple_assignment(node) for node in assignments):
+ return
+ if any(node in self._reported_swap_nodes for node in assignments):
+ return
+ left = [node.targets[0].name for node in assignments]
+ right = [node.value.name for node in assignments]
+ if left[0] == right[-1] and left[1:] == right[:-1]:
+ self._reported_swap_nodes.update(assignments)
+ message = "consider-swap-variables"
+ self.add_message(message, node=node)
+
+ @utils.check_messages(
+ "simplify-boolean-expression",
+ "consider-using-ternary",
+ "consider-swap-variables",
+ )
+ def visit_assign(self, node):
+ self._check_swap_variables(node)
+ if self._is_and_or_ternary(node.value):
+ cond, truth_value, false_value = self._and_or_ternary_arguments(node.value)
+ else:
+ return
+
+ if all(
+ isinstance(value, astroid.Compare) for value in (truth_value, false_value)
+ ):
+ return
+
+ inferred_truth_value = utils.safe_infer(truth_value)
+ if inferred_truth_value in (None, astroid.Uninferable):
+ truth_boolean_value = True
+ else:
+ truth_boolean_value = truth_value.bool_value()
+
+ if truth_boolean_value is False:
+ message = "simplify-boolean-expression"
+ suggestion = false_value.as_string()
+ else:
+ message = "consider-using-ternary"
+ suggestion = "{truth} if {cond} else {false}".format(
+ truth=truth_value.as_string(),
+ cond=cond.as_string(),
+ false=false_value.as_string(),
+ )
+ self.add_message(message, node=node, args=(suggestion,))
+
+ visit_return = visit_assign
+
+ def _check_consider_using_join(self, aug_assign):
+ """
+ We start with the augmented assignment and work our way upwards.
+ Names of variables for nodes if match successful:
+ result = '' # assign
+ for number in ['1', '2', '3'] # for_loop
+ result += number # aug_assign
+ """
+ for_loop = aug_assign.parent
+ if not isinstance(for_loop, astroid.For) or len(for_loop.body) > 1:
+ return
+ assign = for_loop.previous_sibling()
+ if not isinstance(assign, astroid.Assign):
+ return
+ result_assign_names = {
+ target.name
+ for target in assign.targets
+ if isinstance(target, astroid.AssignName)
+ }
+
+ is_concat_loop = (
+ aug_assign.op == "+="
+ and isinstance(aug_assign.target, astroid.AssignName)
+ and len(for_loop.body) == 1
+ and aug_assign.target.name in result_assign_names
+ and isinstance(assign.value, astroid.Const)
+ and isinstance(assign.value.value, str)
+ and isinstance(aug_assign.value, astroid.Name)
+ and aug_assign.value.name == for_loop.target.name
+ )
+ if is_concat_loop:
+ self.add_message("consider-using-join", node=aug_assign)
+
+ @utils.check_messages("consider-using-join")
+ def visit_augassign(self, node):
+ self._check_consider_using_join(node)
+
+ @utils.check_messages("unnecessary-comprehension")
+ def visit_comprehension(self, node):
+ self._check_unnecessary_comprehension(node)
+
+ def _check_unnecessary_comprehension(self, node):
+ if (
+ isinstance(node.parent, astroid.GeneratorExp)
+ or len(node.ifs) != 0
+ or len(node.parent.generators) != 1
+ or node.is_async
+ ):
+ return
+
+ if (
+ isinstance(node.parent, astroid.DictComp)
+ and isinstance(node.parent.key, astroid.Name)
+ and isinstance(node.parent.value, astroid.Name)
+ and isinstance(node.target, astroid.Tuple)
+ and all(isinstance(elt, astroid.AssignName) for elt in node.target.elts)
+ ):
+ expr_list = [node.parent.key.name, node.parent.value.name]
+ target_list = [elt.name for elt in node.target.elts]
+
+ elif isinstance(node.parent, (astroid.ListComp, astroid.SetComp)):
+ expr = node.parent.elt
+ if isinstance(expr, astroid.Name):
+ expr_list = expr.name
+ elif isinstance(expr, astroid.Tuple):
+ if any(not isinstance(elt, astroid.Name) for elt in expr.elts):
+ return
+ expr_list = [elt.name for elt in expr.elts]
+ else:
+ expr_list = []
+ target = node.parent.generators[0].target
+ target_list = (
+ target.name
+ if isinstance(target, astroid.AssignName)
+ else (
+ [
+ elt.name
+ for elt in target.elts
+ if isinstance(elt, astroid.AssignName)
+ ]
+ if isinstance(target, astroid.Tuple)
+ else []
+ )
+ )
+ else:
+ return
+ if expr_list == target_list != []:
+ self.add_message("unnecessary-comprehension", node=node)
+
+ @staticmethod
+ def _is_and_or_ternary(node):
+ """
+ Returns true if node is 'condition and true_value or false_value' form.
+
+ All of: condition, true_value and false_value should not be a complex boolean expression
+ """
+ return (
+ isinstance(node, astroid.BoolOp)
+ and node.op == "or"
+ and len(node.values) == 2
+ and isinstance(node.values[0], astroid.BoolOp)
+ and not isinstance(node.values[1], astroid.BoolOp)
+ and node.values[0].op == "and"
+ and not isinstance(node.values[0].values[1], astroid.BoolOp)
+ and len(node.values[0].values) == 2
+ )
+
+ @staticmethod
+ def _and_or_ternary_arguments(node):
+ false_value = node.values[1]
+ condition, true_value = node.values[0].values
+ return condition, true_value, false_value
+
+ def visit_functiondef(self, node):
+ self._return_nodes[node.name] = list(
+ node.nodes_of_class(astroid.Return, skip_klass=astroid.FunctionDef)
+ )
+
+ def _check_consistent_returns(self, node):
+ """Check that all return statements inside a function are consistent.
+
+ Return statements are consistent if:
+ - all returns are explicit and if there is no implicit return;
+ - all returns are empty and if there is, possibly, an implicit return.
+
+ Args:
+ node (astroid.FunctionDef): the function holding the return statements.
+
+ """
+ # explicit return statements are those with a not None value
+ explicit_returns = [
+ _node for _node in self._return_nodes[node.name] if _node.value is not None
+ ]
+ if not explicit_returns:
+ return
+ if len(explicit_returns) == len(
+ self._return_nodes[node.name]
+ ) and self._is_node_return_ended(node):
+ return
+ self.add_message("inconsistent-return-statements", node=node)
+
+ def _is_node_return_ended(self, node):
+ """Check if the node ends with an explicit return statement.
+
+ Args:
+ node (astroid.NodeNG): node to be checked.
+
+ Returns:
+ bool: True if the node ends with an explicit statement, False otherwise.
+
+ """
+ #  Recursion base case
+ if isinstance(node, astroid.Return):
+ return True
+ if isinstance(node, astroid.Call):
+ try:
+ funcdef_node = node.func.inferred()[0]
+ if self._is_function_def_never_returning(funcdef_node):
+ return True
+ except astroid.InferenceError:
+ pass
+ # Avoid the check inside while loop as we don't know
+ #  if they will be completed
+ if isinstance(node, astroid.While):
+ return True
+ if isinstance(node, astroid.Raise):
+ # a Raise statement doesn't need to end with a return statement
+ # but if the exception raised is handled, then the handler has to
+ # ends with a return statement
+ if not node.exc:
+ # Ignore bare raises
+ return True
+ if not utils.is_node_inside_try_except(node):
+ # If the raise statement is not inside a try/except statement
+ #  then the exception is raised and cannot be caught. No need
+ #  to infer it.
+ return True
+ exc = utils.safe_infer(node.exc)
+ if exc is None or exc is astroid.Uninferable:
+ return False
+ exc_name = exc.pytype().split(".")[-1]
+ handlers = utils.get_exception_handlers(node, exc_name)
+ handlers = list(handlers) if handlers is not None else []
+ if handlers:
+ # among all the handlers handling the exception at least one
+ # must end with a return statement
+ return any(
+ self._is_node_return_ended(_handler) for _handler in handlers
+ )
+ # if no handlers handle the exception then it's ok
+ return True
+ if isinstance(node, astroid.If):
+ # if statement is returning if there are exactly two return statements in its
+ #  children : one for the body part, the other for the orelse part
+ # Do not check if inner function definition are return ended.
+ is_orelse_returning = any(
+ self._is_node_return_ended(_ore)
+ for _ore in node.orelse
+ if not isinstance(_ore, astroid.FunctionDef)
+ )
+ is_if_returning = any(
+ self._is_node_return_ended(_ifn)
+ for _ifn in node.body
+ if not isinstance(_ifn, astroid.FunctionDef)
+ )
+ return is_if_returning and is_orelse_returning
+ #  recurses on the children of the node except for those which are except handler
+ # because one cannot be sure that the handler will really be used
+ return any(
+ self._is_node_return_ended(_child)
+ for _child in node.get_children()
+ if not isinstance(_child, astroid.ExceptHandler)
+ )
+
+ def _is_function_def_never_returning(self, node):
+ """Return True if the function never returns. False otherwise.
+
+ Args:
+ node (astroid.FunctionDef): function definition node to be analyzed.
+
+ Returns:
+ bool: True if the function never returns, False otherwise.
+ """
+ try:
+ return node.qname() in self._never_returning_functions
+ except TypeError:
+ return False
+
+ def _check_return_at_the_end(self, node):
+ """Check for presence of a *single* return statement at the end of a
+ function. "return" or "return None" are useless because None is the
+ default return type if they are missing.
+
+ NOTE: produces a message only if there is a single return statement
+ in the function body. Otherwise _check_consistent_returns() is called!
+ Per its implementation and PEP8 we can have a "return None" at the end
+ of the function body if there are other return statements before that!
+ """
+ if len(self._return_nodes[node.name]) > 1:
+ return
+ if len(node.body) <= 1:
+ return
+
+ last = node.body[-1]
+ if isinstance(last, astroid.Return):
+ # e.g. "return"
+ if last.value is None:
+ self.add_message("useless-return", node=node)
+ # return None"
+ elif isinstance(last.value, astroid.Const) and (last.value.value is None):
+ self.add_message("useless-return", node=node)
+
+
+class RecommandationChecker(checkers.BaseChecker):
+ __implements__ = (interfaces.IAstroidChecker,)
+ name = "refactoring"
+ msgs = {
+ "C0200": (
+ "Consider using enumerate instead of iterating with range and len",
+ "consider-using-enumerate",
+ "Emitted when code that iterates with range and len is "
+ "encountered. Such code can be simplified by using the "
+ "enumerate builtin.",
+ ),
+ "C0201": (
+ "Consider iterating the dictionary directly instead of calling .keys()",
+ "consider-iterating-dictionary",
+ "Emitted when the keys of a dictionary are iterated through the .keys() "
+ "method. It is enough to just iterate through the dictionary itself, as "
+ 'in "for key in dictionary".',
+ ),
+ }
+
+ @staticmethod
+ def _is_builtin(node, function):
+ inferred = utils.safe_infer(node)
+ if not inferred:
+ return False
+ return utils.is_builtin_object(inferred) and inferred.name == function
+
+ @utils.check_messages("consider-iterating-dictionary")
+ def visit_call(self, node):
+ if not isinstance(node.func, astroid.Attribute):
+ return
+ if node.func.attrname != "keys":
+ return
+ if not isinstance(node.parent, (astroid.For, astroid.Comprehension)):
+ return
+
+ inferred = utils.safe_infer(node.func)
+ if not isinstance(inferred, astroid.BoundMethod) or not isinstance(
+ inferred.bound, astroid.Dict
+ ):
+ return
+
+ if isinstance(node.parent, (astroid.For, astroid.Comprehension)):
+ self.add_message("consider-iterating-dictionary", node=node)
+
+ @utils.check_messages("consider-using-enumerate")
+ def visit_for(self, node):
+ """Emit a convention whenever range and len are used for indexing."""
+ # Verify that we have a `range([start], len(...), [stop])` call and
+ # that the object which is iterated is used as a subscript in the
+ # body of the for.
+
+ # Is it a proper range call?
+ if not isinstance(node.iter, astroid.Call):
+ return
+ if not self._is_builtin(node.iter.func, "range"):
+ return
+ if len(node.iter.args) == 2 and not _is_constant_zero(node.iter.args[0]):
+ return
+ if len(node.iter.args) > 2:
+ return
+
+ # Is it a proper len call?
+ if not isinstance(node.iter.args[-1], astroid.Call):
+ return
+ second_func = node.iter.args[-1].func
+ if not self._is_builtin(second_func, "len"):
+ return
+ len_args = node.iter.args[-1].args
+ if not len_args or len(len_args) != 1:
+ return
+ iterating_object = len_args[0]
+ if not isinstance(iterating_object, astroid.Name):
+ return
+ # If we're defining __iter__ on self, enumerate won't work
+ scope = node.scope()
+ if iterating_object.name == "self" and scope.name == "__iter__":
+ return
+
+ # Verify that the body of the for loop uses a subscript
+ # with the object that was iterated. This uses some heuristics
+ # in order to make sure that the same object is used in the
+ # for body.
+ for child in node.body:
+ for subscript in child.nodes_of_class(astroid.Subscript):
+ if not isinstance(subscript.value, astroid.Name):
+ continue
+ if not isinstance(subscript.slice, astroid.Index):
+ continue
+ if not isinstance(subscript.slice.value, astroid.Name):
+ continue
+ if subscript.slice.value.name != node.target.name:
+ continue
+ if iterating_object.name != subscript.value.name:
+ continue
+ if subscript.value.scope() != node.scope():
+ # Ignore this subscript if it's not in the same
+ # scope. This means that in the body of the for
+ # loop, another scope was created, where the same
+ # name for the iterating object was used.
+ continue
+ self.add_message("consider-using-enumerate", node=node)
+ return
+
+
+class NotChecker(checkers.BaseChecker):
+ """checks for too many not in comparison expressions
+
+ - "not not" should trigger a warning
+ - "not" followed by a comparison should trigger a warning
+ """
+
+ __implements__ = (interfaces.IAstroidChecker,)
+ msgs = {
+ "C0113": (
+ 'Consider changing "%s" to "%s"',
+ "unneeded-not",
+ "Used when a boolean expression contains an unneeded negation.",
+ )
+ }
+ name = "refactoring"
+ reverse_op = {
+ "<": ">=",
+ "<=": ">",
+ ">": "<=",
+ ">=": "<",
+ "==": "!=",
+ "!=": "==",
+ "in": "not in",
+ "is": "is not",
+ }
+ # sets are not ordered, so for example "not set(LEFT_VALS) <= set(RIGHT_VALS)" is
+ # not equivalent to "set(LEFT_VALS) > set(RIGHT_VALS)"
+ skipped_nodes = (astroid.Set,)
+ # 'builtins' py3, '__builtin__' py2
+ skipped_classnames = [
+ "%s.%s" % (builtins.__name__, qname) for qname in ("set", "frozenset")
+ ]
+
+ @utils.check_messages("unneeded-not")
+ def visit_unaryop(self, node):
+ if node.op != "not":
+ return
+ operand = node.operand
+
+ if isinstance(operand, astroid.UnaryOp) and operand.op == "not":
+ self.add_message(
+ "unneeded-not",
+ node=node,
+ args=(node.as_string(), operand.operand.as_string()),
+ )
+ elif isinstance(operand, astroid.Compare):
+ left = operand.left
+ # ignore multiple comparisons
+ if len(operand.ops) > 1:
+ return
+ operator, right = operand.ops[0]
+ if operator not in self.reverse_op:
+ return
+ # Ignore __ne__ as function of __eq__
+ frame = node.frame()
+ if frame.name == "__ne__" and operator == "==":
+ return
+ for _type in (utils.node_type(left), utils.node_type(right)):
+ if not _type:
+ return
+ if isinstance(_type, self.skipped_nodes):
+ return
+ if (
+ isinstance(_type, astroid.Instance)
+ and _type.qname() in self.skipped_classnames
+ ):
+ return
+ suggestion = "%s %s %s" % (
+ left.as_string(),
+ self.reverse_op[operator],
+ right.as_string(),
+ )
+ self.add_message(
+ "unneeded-not", node=node, args=(node.as_string(), suggestion)
+ )
+
+
+class LenChecker(checkers.BaseChecker):
+ """Checks for incorrect usage of len() inside conditions.
+ Pep8 states:
+ For sequences, (strings, lists, tuples), use the fact that empty sequences are false.
+
+ Yes: if not seq:
+ if seq:
+
+ No: if len(seq):
+ if not len(seq):
+
+ Problems detected:
+ * if len(sequence):
+ * if not len(sequence):
+ * elif len(sequence):
+ * elif not len(sequence):
+ * while len(sequence):
+ * while not len(sequence):
+ * assert len(sequence):
+ * assert not len(sequence):
+ """
+
+ __implements__ = (interfaces.IAstroidChecker,)
+
+ # configuration section name
+ name = "refactoring"
+ msgs = {
+ "C1801": (
+ "Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty",
+ "len-as-condition",
+ "Used when Pylint detects that len(sequence) is being used "
+ "without explicit comparison inside a condition to determine if a sequence is empty. "
+ "Instead of coercing the length to a boolean, either "
+ "rely on the fact that empty sequences are false or "
+ "compare the length against a scalar.",
+ )
+ }
+
+ priority = -2
+ options = ()
+
+ @utils.check_messages("len-as-condition")
+ def visit_call(self, node):
+ # a len(S) call is used inside a test condition
+ # could be if, while, assert or if expression statement
+ # e.g. `if len(S):`
+ if _is_len_call(node):
+ # the len() call could also be nested together with other
+ # boolean operations, e.g. `if z or len(x):`
+ parent = node.parent
+ while isinstance(parent, astroid.BoolOp):
+ parent = parent.parent
+
+ # we're finally out of any nested boolean operations so check if
+ # this len() call is part of a test condition
+ if not _node_is_test_condition(parent):
+ return
+ if not (node is parent.test or parent.test.parent_of(node)):
+ return
+ self.add_message("len-as-condition", node=node)
+
+ @utils.check_messages("len-as-condition")
+ def visit_unaryop(self, node):
+ """`not len(S)` must become `not S` regardless if the parent block
+ is a test condition or something else (boolean expression)
+ e.g. `if not len(S):`"""
+ if (
+ isinstance(node, astroid.UnaryOp)
+ and node.op == "not"
+ and _is_len_call(node.operand)
+ ):
+ self.add_message("len-as-condition", node=node)
+
+
+def register(linter):
+ """Required method to auto register this checker."""
+ linter.register_checker(RefactoringChecker(linter))
+ linter.register_checker(NotChecker(linter))
+ linter.register_checker(RecommandationChecker(linter))
+ linter.register_checker(LenChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/similar.py b/venv/Lib/site-packages/pylint/checkers/similar.py
new file mode 100644
index 0000000..019b55f
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/similar.py
@@ -0,0 +1,452 @@
+# Copyright (c) 2006, 2008-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012 Ry4an Brase <ry4an-hg@ry4an.org>
+# Copyright (c) 2012 Google, Inc.
+# Copyright (c) 2012 Anthony VEREZ <anthony.verez.external@cassidian.com>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2017 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+# pylint: disable=redefined-builtin
+"""a similarities / code duplication command line tool and pylint checker
+"""
+
+import sys
+from collections import defaultdict
+from getopt import getopt
+from itertools import groupby
+
+import astroid
+
+from pylint.checkers import BaseChecker, table_lines_from_stats
+from pylint.interfaces import IRawChecker
+from pylint.reporters.ureports.nodes import Table
+from pylint.utils import decoding_stream
+
+
+class Similar:
+ """finds copy-pasted lines of code in a project"""
+
+ def __init__(
+ self,
+ min_lines=4,
+ ignore_comments=False,
+ ignore_docstrings=False,
+ ignore_imports=False,
+ ):
+ self.min_lines = min_lines
+ self.ignore_comments = ignore_comments
+ self.ignore_docstrings = ignore_docstrings
+ self.ignore_imports = ignore_imports
+ self.linesets = []
+
+ def append_stream(self, streamid, stream, encoding=None):
+ """append a file to search for similarities"""
+ if encoding is None:
+ readlines = stream.readlines
+ else:
+ readlines = decoding_stream(stream, encoding).readlines
+ try:
+ self.linesets.append(
+ LineSet(
+ streamid,
+ readlines(),
+ self.ignore_comments,
+ self.ignore_docstrings,
+ self.ignore_imports,
+ )
+ )
+ except UnicodeDecodeError:
+ pass
+
+ def run(self):
+ """start looking for similarities and display results on stdout"""
+ self._display_sims(self._compute_sims())
+
+ def _compute_sims(self):
+ """compute similarities in appended files"""
+ no_duplicates = defaultdict(list)
+ for num, lineset1, idx1, lineset2, idx2 in self._iter_sims():
+ duplicate = no_duplicates[num]
+ for couples in duplicate:
+ if (lineset1, idx1) in couples or (lineset2, idx2) in couples:
+ couples.add((lineset1, idx1))
+ couples.add((lineset2, idx2))
+ break
+ else:
+ duplicate.append({(lineset1, idx1), (lineset2, idx2)})
+ sims = []
+ for num, ensembles in no_duplicates.items():
+ for couples in ensembles:
+ sims.append((num, couples))
+ sims.sort()
+ sims.reverse()
+ return sims
+
+ def _display_sims(self, sims):
+ """display computed similarities on stdout"""
+ nb_lignes_dupliquees = 0
+ for num, couples in sims:
+ print()
+ print(num, "similar lines in", len(couples), "files")
+ couples = sorted(couples)
+ lineset = idx = None
+ for lineset, idx in couples:
+ print("==%s:%s" % (lineset.name, idx))
+ if lineset:
+ for line in lineset._real_lines[idx : idx + num]:
+ print(" ", line.rstrip())
+ nb_lignes_dupliquees += num * (len(couples) - 1)
+ nb_total_lignes = sum([len(lineset) for lineset in self.linesets])
+ print(
+ "TOTAL lines=%s duplicates=%s percent=%.2f"
+ % (
+ nb_total_lignes,
+ nb_lignes_dupliquees,
+ nb_lignes_dupliquees * 100.0 / nb_total_lignes,
+ )
+ )
+
+ def _find_common(self, lineset1, lineset2):
+ """find similarities in the two given linesets"""
+ lines1 = lineset1.enumerate_stripped
+ lines2 = lineset2.enumerate_stripped
+ find = lineset2.find
+ index1 = 0
+ min_lines = self.min_lines
+ while index1 < len(lineset1):
+ skip = 1
+ num = 0
+ for index2 in find(lineset1[index1]):
+ non_blank = 0
+ for num, ((_, line1), (_, line2)) in enumerate(
+ zip(lines1(index1), lines2(index2))
+ ):
+ if line1 != line2:
+ if non_blank > min_lines:
+ yield num, lineset1, index1, lineset2, index2
+ skip = max(skip, num)
+ break
+ if line1:
+ non_blank += 1
+ else:
+ # we may have reach the end
+ num += 1
+ if non_blank > min_lines:
+ yield num, lineset1, index1, lineset2, index2
+ skip = max(skip, num)
+ index1 += skip
+
+ def _iter_sims(self):
+ """iterate on similarities among all files, by making a cartesian
+ product
+ """
+ for idx, lineset in enumerate(self.linesets[:-1]):
+ for lineset2 in self.linesets[idx + 1 :]:
+ for sim in self._find_common(lineset, lineset2):
+ yield sim
+
+
+def stripped_lines(lines, ignore_comments, ignore_docstrings, ignore_imports):
+ """return lines with leading/trailing whitespace and any ignored code
+ features removed
+ """
+ if ignore_imports:
+ tree = astroid.parse("".join(lines))
+ node_is_import_by_lineno = (
+ (node.lineno, isinstance(node, (astroid.Import, astroid.ImportFrom)))
+ for node in tree.body
+ )
+ line_begins_import = {
+ lineno: all(is_import for _, is_import in node_is_import_group)
+ for lineno, node_is_import_group in groupby(
+ node_is_import_by_lineno, key=lambda x: x[0]
+ )
+ }
+ current_line_is_import = False
+
+ strippedlines = []
+ docstring = None
+ for lineno, line in enumerate(lines, start=1):
+ line = line.strip()
+ if ignore_docstrings:
+ if not docstring and any(
+ line.startswith(i) for i in ['"""', "'''", 'r"""', "r'''"]
+ ):
+ docstring = line[:3]
+ line = line[3:]
+ if docstring:
+ if line.endswith(docstring):
+ docstring = None
+ line = ""
+ if ignore_imports:
+ current_line_is_import = line_begins_import.get(
+ lineno, current_line_is_import
+ )
+ if current_line_is_import:
+ line = ""
+ if ignore_comments:
+ line = line.split("#", 1)[0].strip()
+ strippedlines.append(line)
+ return strippedlines
+
+
+class LineSet:
+ """Holds and indexes all the lines of a single source file"""
+
+ def __init__(
+ self,
+ name,
+ lines,
+ ignore_comments=False,
+ ignore_docstrings=False,
+ ignore_imports=False,
+ ):
+ self.name = name
+ self._real_lines = lines
+ self._stripped_lines = stripped_lines(
+ lines, ignore_comments, ignore_docstrings, ignore_imports
+ )
+ self._index = self._mk_index()
+
+ def __str__(self):
+ return "<Lineset for %s>" % self.name
+
+ def __len__(self):
+ return len(self._real_lines)
+
+ def __getitem__(self, index):
+ return self._stripped_lines[index]
+
+ def __lt__(self, other):
+ return self.name < other.name
+
+ def __hash__(self):
+ return id(self)
+
+ def enumerate_stripped(self, start_at=0):
+ """return an iterator on stripped lines, starting from a given index
+ if specified, else 0
+ """
+ idx = start_at
+ if start_at:
+ lines = self._stripped_lines[start_at:]
+ else:
+ lines = self._stripped_lines
+ for line in lines:
+ # if line:
+ yield idx, line
+ idx += 1
+
+ def find(self, stripped_line):
+ """return positions of the given stripped line in this set"""
+ return self._index.get(stripped_line, ())
+
+ def _mk_index(self):
+ """create the index for this set"""
+ index = defaultdict(list)
+ for line_no, line in enumerate(self._stripped_lines):
+ if line:
+ index[line].append(line_no)
+ return index
+
+
+MSGS = {
+ "R0801": (
+ "Similar lines in %s files\n%s",
+ "duplicate-code",
+ "Indicates that a set of similar lines has been detected "
+ "among multiple file. This usually means that the code should "
+ "be refactored to avoid this duplication.",
+ )
+}
+
+
+def report_similarities(sect, stats, old_stats):
+ """make a layout with some stats about duplication"""
+ lines = ["", "now", "previous", "difference"]
+ lines += table_lines_from_stats(
+ stats, old_stats, ("nb_duplicated_lines", "percent_duplicated_lines")
+ )
+ sect.append(Table(children=lines, cols=4, rheaders=1, cheaders=1))
+
+
+# wrapper to get a pylint checker from the similar class
+class SimilarChecker(BaseChecker, Similar):
+ """checks for similarities and duplicated code. This computation may be
+ memory / CPU intensive, so you should disable it if you experiment some
+ problems.
+ """
+
+ __implements__ = (IRawChecker,)
+ # configuration section name
+ name = "similarities"
+ # messages
+ msgs = MSGS
+ # configuration options
+ # for available dict keys/values see the optik parser 'add_option' method
+ options = (
+ (
+ "min-similarity-lines", # type: ignore
+ {
+ "default": 4,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Minimum lines number of a similarity.",
+ },
+ ),
+ (
+ "ignore-comments",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y or n>",
+ "help": "Ignore comments when computing similarities.",
+ },
+ ),
+ (
+ "ignore-docstrings",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y or n>",
+ "help": "Ignore docstrings when computing similarities.",
+ },
+ ),
+ (
+ "ignore-imports",
+ {
+ "default": False,
+ "type": "yn",
+ "metavar": "<y or n>",
+ "help": "Ignore imports when computing similarities.",
+ },
+ ),
+ )
+ # reports
+ reports = (("RP0801", "Duplication", report_similarities),) # type: ignore
+
+ def __init__(self, linter=None):
+ BaseChecker.__init__(self, linter)
+ Similar.__init__(
+ self, min_lines=4, ignore_comments=True, ignore_docstrings=True
+ )
+ self.stats = None
+
+ def set_option(self, optname, value, action=None, optdict=None):
+ """method called to set an option (registered in the options list)
+
+ overridden to report options setting to Similar
+ """
+ BaseChecker.set_option(self, optname, value, action, optdict)
+ if optname == "min-similarity-lines":
+ self.min_lines = self.config.min_similarity_lines
+ elif optname == "ignore-comments":
+ self.ignore_comments = self.config.ignore_comments
+ elif optname == "ignore-docstrings":
+ self.ignore_docstrings = self.config.ignore_docstrings
+ elif optname == "ignore-imports":
+ self.ignore_imports = self.config.ignore_imports
+
+ def open(self):
+ """init the checkers: reset linesets and statistics information"""
+ self.linesets = []
+ self.stats = self.linter.add_stats(
+ nb_duplicated_lines=0, percent_duplicated_lines=0
+ )
+
+ def process_module(self, node):
+ """process a module
+
+ the module's content is accessible via the stream object
+
+ stream must implement the readlines method
+ """
+ with node.stream() as stream:
+ self.append_stream(self.linter.current_name, stream, node.file_encoding)
+
+ def close(self):
+ """compute and display similarities on closing (i.e. end of parsing)"""
+ total = sum(len(lineset) for lineset in self.linesets)
+ duplicated = 0
+ stats = self.stats
+ for num, couples in self._compute_sims():
+ msg = []
+ lineset = idx = None
+ for lineset, idx in couples:
+ msg.append("==%s:%s" % (lineset.name, idx))
+ msg.sort()
+
+ if lineset:
+ for line in lineset._real_lines[idx : idx + num]:
+ msg.append(line.rstrip())
+
+ self.add_message("R0801", args=(len(couples), "\n".join(msg)))
+ duplicated += num * (len(couples) - 1)
+ stats["nb_duplicated_lines"] = duplicated
+ stats["percent_duplicated_lines"] = total and duplicated * 100.0 / total
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(SimilarChecker(linter))
+
+
+def usage(status=0):
+ """display command line usage information"""
+ print("finds copy pasted blocks in a set of files")
+ print()
+ print(
+ "Usage: symilar [-d|--duplicates min_duplicated_lines] \
+[-i|--ignore-comments] [--ignore-docstrings] [--ignore-imports] file1..."
+ )
+ sys.exit(status)
+
+
+def Run(argv=None):
+ """standalone command line access point"""
+ if argv is None:
+ argv = sys.argv[1:]
+
+ s_opts = "hdi"
+ l_opts = (
+ "help",
+ "duplicates=",
+ "ignore-comments",
+ "ignore-imports",
+ "ignore-docstrings",
+ )
+ min_lines = 4
+ ignore_comments = False
+ ignore_docstrings = False
+ ignore_imports = False
+ opts, args = getopt(argv, s_opts, l_opts)
+ for opt, val in opts:
+ if opt in ("-d", "--duplicates"):
+ min_lines = int(val)
+ elif opt in ("-h", "--help"):
+ usage()
+ elif opt in ("-i", "--ignore-comments"):
+ ignore_comments = True
+ elif opt in ("--ignore-docstrings",):
+ ignore_docstrings = True
+ elif opt in ("--ignore-imports",):
+ ignore_imports = True
+ if not args:
+ usage(1)
+ sim = Similar(min_lines, ignore_comments, ignore_docstrings, ignore_imports)
+ for filename in args:
+ with open(filename) as stream:
+ sim.append_stream(filename, stream)
+ sim.run()
+ sys.exit(0)
+
+
+if __name__ == "__main__":
+ Run()
diff --git a/venv/Lib/site-packages/pylint/checkers/spelling.py b/venv/Lib/site-packages/pylint/checkers/spelling.py
new file mode 100644
index 0000000..b1a5334
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/spelling.py
@@ -0,0 +1,411 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
+# Copyright (c) 2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2015 Pavel Roskin <proski@gnu.org>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016-2017 Pedro Algarvio <pedro@algarvio.me>
+# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Checker for spelling errors in comments and docstrings.
+"""
+
+import os
+import re
+import tokenize
+
+from pylint.checkers import BaseTokenChecker
+from pylint.checkers.utils import check_messages
+from pylint.interfaces import IAstroidChecker, ITokenChecker
+
+try:
+ import enchant
+ from enchant.tokenize import ( # type: ignore
+ get_tokenizer,
+ Chunker,
+ Filter,
+ EmailFilter,
+ URLFilter,
+ WikiWordFilter,
+ )
+except ImportError:
+ enchant = None
+ # pylint: disable=no-init
+ class Filter: # type: ignore
+ def _skip(self, word):
+ raise NotImplementedError
+
+ class Chunker: # type: ignore
+ pass
+
+
+if enchant is not None:
+ br = enchant.Broker()
+ dicts = br.list_dicts()
+ dict_choices = [""] + [d[0] for d in dicts]
+ dicts = ["%s (%s)" % (d[0], d[1].name) for d in dicts]
+ dicts = ", ".join(dicts)
+ instr = ""
+else:
+ dicts = "none"
+ dict_choices = [""]
+ instr = " To make it work, install the python-enchant package."
+
+
+class WordsWithDigigtsFilter(Filter):
+ """Skips words with digits.
+ """
+
+ def _skip(self, word):
+ for char in word:
+ if char.isdigit():
+ return True
+ return False
+
+
+class WordsWithUnderscores(Filter):
+ """Skips words with underscores.
+
+ They are probably function parameter names.
+ """
+
+ def _skip(self, word):
+ return "_" in word
+
+
+class CamelCasedWord(Filter):
+ r"""Filter skipping over camelCasedWords.
+ This filter skips any words matching the following regular expression:
+
+ ^([a-z]\w+[A-Z]+\w+)
+
+ That is, any words that are camelCasedWords.
+ """
+ _pattern = re.compile(r"^([a-z]+([\d]|[A-Z])(?:\w+)?)")
+
+ def _skip(self, word):
+ return bool(self._pattern.match(word))
+
+
+class SphinxDirectives(Filter):
+ r"""Filter skipping over Sphinx Directives.
+ This filter skips any words matching the following regular expression:
+
+ ^:([a-z]+):`([^`]+)(`)?
+
+ That is, for example, :class:`BaseQuery`
+ """
+ # The final ` in the pattern is optional because enchant strips it out
+ _pattern = re.compile(r"^:([a-z]+):`([^`]+)(`)?")
+
+ def _skip(self, word):
+ return bool(self._pattern.match(word))
+
+
+class ForwardSlashChunkder(Chunker):
+ """
+ This chunker allows splitting words like 'before/after' into 'before' and 'after'
+ """
+
+ def next(self):
+ while True:
+ if not self._text:
+ raise StopIteration()
+ if "/" not in self._text:
+ text = self._text
+ self._offset = 0
+ self._text = ""
+ return (text, 0)
+ pre_text, post_text = self._text.split("/", 1)
+ self._text = post_text
+ self._offset = 0
+ if (
+ not pre_text
+ or not post_text
+ or not pre_text[-1].isalpha()
+ or not post_text[0].isalpha()
+ ):
+ self._text = ""
+ self._offset = 0
+ return (pre_text + "/" + post_text, 0)
+ return (pre_text, 0)
+
+ def _next(self):
+ while True:
+ if "/" not in self._text:
+ return (self._text, 0)
+ pre_text, post_text = self._text.split("/", 1)
+ if not pre_text or not post_text:
+ break
+ if not pre_text[-1].isalpha() or not post_text[0].isalpha():
+ raise StopIteration()
+ self._text = pre_text + " " + post_text
+ raise StopIteration()
+
+
+class SpellingChecker(BaseTokenChecker):
+ """Check spelling in comments and docstrings"""
+
+ __implements__ = (ITokenChecker, IAstroidChecker)
+ name = "spelling"
+ msgs = {
+ "C0401": (
+ "Wrong spelling of a word '%s' in a comment:\n%s\n"
+ "%s\nDid you mean: '%s'?",
+ "wrong-spelling-in-comment",
+ "Used when a word in comment is not spelled correctly.",
+ ),
+ "C0402": (
+ "Wrong spelling of a word '%s' in a docstring:\n%s\n"
+ "%s\nDid you mean: '%s'?",
+ "wrong-spelling-in-docstring",
+ "Used when a word in docstring is not spelled correctly.",
+ ),
+ "C0403": (
+ "Invalid characters %r in a docstring",
+ "invalid-characters-in-docstring",
+ "Used when a word in docstring cannot be checked by enchant.",
+ ),
+ }
+ options = (
+ (
+ "spelling-dict",
+ {
+ "default": "",
+ "type": "choice",
+ "metavar": "<dict name>",
+ "choices": dict_choices,
+ "help": "Spelling dictionary name. "
+ "Available dictionaries: %s.%s" % (dicts, instr),
+ },
+ ),
+ (
+ "spelling-ignore-words",
+ {
+ "default": "",
+ "type": "string",
+ "metavar": "<comma separated words>",
+ "help": "List of comma separated words that " "should not be checked.",
+ },
+ ),
+ (
+ "spelling-private-dict-file",
+ {
+ "default": "",
+ "type": "string",
+ "metavar": "<path to file>",
+ "help": "A path to a file that contains the private "
+ "dictionary; one word per line.",
+ },
+ ),
+ (
+ "spelling-store-unknown-words",
+ {
+ "default": "n",
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": "Tells whether to store unknown words to the "
+ "private dictionary (see the "
+ "--spelling-private-dict-file option) instead of "
+ "raising a message.",
+ },
+ ),
+ (
+ "max-spelling-suggestions",
+ {
+ "default": 4,
+ "type": "int",
+ "metavar": "N",
+ "help": "Limits count of emitted suggestions for " "spelling mistakes.",
+ },
+ ),
+ )
+
+ def open(self):
+ self.initialized = False
+ self.private_dict_file = None
+
+ if enchant is None:
+ return
+ dict_name = self.config.spelling_dict
+ if not dict_name:
+ return
+
+ self.ignore_list = [
+ w.strip() for w in self.config.spelling_ignore_words.split(",")
+ ]
+ # "param" appears in docstring in param description and
+ # "pylint" appears in comments in pylint pragmas.
+ self.ignore_list.extend(["param", "pylint"])
+
+ # Expand tilde to allow e.g. spelling-private-dict-file = ~/.pylintdict
+ if self.config.spelling_private_dict_file:
+ self.config.spelling_private_dict_file = os.path.expanduser(
+ self.config.spelling_private_dict_file
+ )
+
+ if self.config.spelling_private_dict_file:
+ self.spelling_dict = enchant.DictWithPWL(
+ dict_name, self.config.spelling_private_dict_file
+ )
+ self.private_dict_file = open(self.config.spelling_private_dict_file, "a")
+ else:
+ self.spelling_dict = enchant.Dict(dict_name)
+
+ if self.config.spelling_store_unknown_words:
+ self.unknown_words = set()
+
+ self.tokenizer = get_tokenizer(
+ dict_name,
+ chunkers=[ForwardSlashChunkder],
+ filters=[
+ EmailFilter,
+ URLFilter,
+ WikiWordFilter,
+ WordsWithDigigtsFilter,
+ WordsWithUnderscores,
+ CamelCasedWord,
+ SphinxDirectives,
+ ],
+ )
+ self.initialized = True
+
+ def close(self):
+ if self.private_dict_file:
+ self.private_dict_file.close()
+
+ def _check_spelling(self, msgid, line, line_num):
+ original_line = line
+ try:
+ initial_space = re.search(r"^[^\S]\s*", line).regs[0][1]
+ except (IndexError, AttributeError):
+ initial_space = 0
+ if line.strip().startswith("#"):
+ line = line.strip()[1:]
+ starts_with_comment = True
+ else:
+ starts_with_comment = False
+ for word, word_start_at in self.tokenizer(line.strip()):
+ word_start_at += initial_space
+ lower_cased_word = word.casefold()
+
+ # Skip words from ignore list.
+ if word in self.ignore_list or lower_cased_word in self.ignore_list:
+ continue
+
+ # Strip starting u' from unicode literals and r' from raw strings.
+ if word.startswith(("u'", 'u"', "r'", 'r"')) and len(word) > 2:
+ word = word[2:]
+ lower_cased_word = lower_cased_word[2:]
+
+ # If it is a known word, then continue.
+ try:
+ if self.spelling_dict.check(lower_cased_word):
+ # The lower cased version of word passed spell checking
+ continue
+
+ # If we reached this far, it means there was a spelling mistake.
+ # Let's retry with the original work because 'unicode' is a
+ # spelling mistake but 'Unicode' is not
+ if self.spelling_dict.check(word):
+ continue
+ except enchant.errors.Error:
+ self.add_message(
+ "invalid-characters-in-docstring", line=line_num, args=(word,)
+ )
+ continue
+
+ # Store word to private dict or raise a message.
+ if self.config.spelling_store_unknown_words:
+ if lower_cased_word not in self.unknown_words:
+ self.private_dict_file.write("%s\n" % lower_cased_word)
+ self.unknown_words.add(lower_cased_word)
+ else:
+ # Present up to N suggestions.
+ suggestions = self.spelling_dict.suggest(word)
+ del suggestions[self.config.max_spelling_suggestions :]
+
+ line_segment = line[word_start_at:]
+ match = re.search(r"(\W|^)(%s)(\W|$)" % word, line_segment)
+ if match:
+ # Start position of second group in regex.
+ col = match.regs[2][0]
+ else:
+ col = line_segment.index(word)
+
+ col += word_start_at
+
+ if starts_with_comment:
+ col += 1
+ indicator = (" " * col) + ("^" * len(word))
+
+ self.add_message(
+ msgid,
+ line=line_num,
+ args=(
+ word,
+ original_line,
+ indicator,
+ "'{}'".format("' or '".join(suggestions)),
+ ),
+ )
+
+ def process_tokens(self, tokens):
+ if not self.initialized:
+ return
+
+ # Process tokens and look for comments.
+ for (tok_type, token, (start_row, _), _, _) in tokens:
+ if tok_type == tokenize.COMMENT:
+ if start_row == 1 and token.startswith("#!/"):
+ # Skip shebang lines
+ continue
+ if token.startswith("# pylint:"):
+ # Skip pylint enable/disable comments
+ continue
+ self._check_spelling("wrong-spelling-in-comment", token, start_row)
+
+ @check_messages("wrong-spelling-in-docstring")
+ def visit_module(self, node):
+ if not self.initialized:
+ return
+ self._check_docstring(node)
+
+ @check_messages("wrong-spelling-in-docstring")
+ def visit_classdef(self, node):
+ if not self.initialized:
+ return
+ self._check_docstring(node)
+
+ @check_messages("wrong-spelling-in-docstring")
+ def visit_functiondef(self, node):
+ if not self.initialized:
+ return
+ self._check_docstring(node)
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ def _check_docstring(self, node):
+ """check the node has any spelling errors"""
+ docstring = node.doc
+ if not docstring:
+ return
+
+ start_line = node.lineno + 1
+
+ # Go through lines of docstring
+ for idx, line in enumerate(docstring.splitlines()):
+ self._check_spelling("wrong-spelling-in-docstring", line, start_line + idx)
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(SpellingChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/stdlib.py b/venv/Lib/site-packages/pylint/checkers/stdlib.py
new file mode 100644
index 0000000..a945107
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/stdlib.py
@@ -0,0 +1,452 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Cosmin Poieana <cmin@ropython.org>
+# Copyright (c) 2014 Vlad Temian <vladtemian@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Cezar <celnazli@bitdefender.com>
+# Copyright (c) 2015 Chris Rebert <code@rebertia.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Jared Garst <cultofjared@gmail.com>
+# Copyright (c) 2017 Renat Galimov <renat2017@gmail.com>
+# Copyright (c) 2017 Martin <MartinBasti@users.noreply.github.com>
+# Copyright (c) 2017 Christopher Zurcher <zurcher@users.noreply.github.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2018 Banjamin Freeman <befreeman@users.noreply.github.com>
+# Copyright (c) 2018 Ioana Tagirta <ioana.tagirta@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Checkers for various standard library functions."""
+
+import sys
+
+import astroid
+from astroid.bases import Instance
+from astroid.node_classes import Const
+
+from pylint.checkers import BaseChecker, utils
+from pylint.interfaces import IAstroidChecker
+
+OPEN_FILES = {"open", "file"}
+UNITTEST_CASE = "unittest.case"
+THREADING_THREAD = "threading.Thread"
+COPY_COPY = "copy.copy"
+OS_ENVIRON = "os._Environ"
+ENV_GETTERS = {"os.getenv"}
+SUBPROCESS_POPEN = "subprocess.Popen"
+SUBPROCESS_RUN = "subprocess.run"
+OPEN_MODULE = "_io"
+
+
+def _check_mode_str(mode):
+ # check type
+ if not isinstance(mode, str):
+ return False
+ # check syntax
+ modes = set(mode)
+ _mode = "rwatb+Ux"
+ creating = "x" in modes
+ if modes - set(_mode) or len(mode) > len(modes):
+ return False
+ # check logic
+ reading = "r" in modes
+ writing = "w" in modes
+ appending = "a" in modes
+ text = "t" in modes
+ binary = "b" in modes
+ if "U" in modes:
+ if writing or appending or creating:
+ return False
+ reading = True
+ if text and binary:
+ return False
+ total = reading + writing + appending + creating
+ if total > 1:
+ return False
+ if not (reading or writing or appending or creating):
+ return False
+ return True
+
+
+class StdlibChecker(BaseChecker):
+ __implements__ = (IAstroidChecker,)
+ name = "stdlib"
+
+ msgs = {
+ "W1501": (
+ '"%s" is not a valid mode for open.',
+ "bad-open-mode",
+ "Python supports: r, w, a[, x] modes with b, +, "
+ "and U (only with r) options. "
+ "See http://docs.python.org/2/library/functions.html#open",
+ ),
+ "W1502": (
+ "Using datetime.time in a boolean context.",
+ "boolean-datetime",
+ "Using datetime.time in a boolean context can hide "
+ "subtle bugs when the time they represent matches "
+ "midnight UTC. This behaviour was fixed in Python 3.5. "
+ "See http://bugs.python.org/issue13936 for reference.",
+ {"maxversion": (3, 5)},
+ ),
+ "W1503": (
+ "Redundant use of %s with constant value %r",
+ "redundant-unittest-assert",
+ "The first argument of assertTrue and assertFalse is "
+ "a condition. If a constant is passed as parameter, that "
+ "condition will be always true. In this case a warning "
+ "should be emitted.",
+ ),
+ "W1505": (
+ "Using deprecated method %s()",
+ "deprecated-method",
+ "The method is marked as deprecated and will be removed in "
+ "a future version of Python. Consider looking for an "
+ "alternative in the documentation.",
+ ),
+ "W1506": (
+ "threading.Thread needs the target function",
+ "bad-thread-instantiation",
+ "The warning is emitted when a threading.Thread class "
+ "is instantiated without the target function being passed. "
+ "By default, the first parameter is the group param, not the target param. ",
+ ),
+ "W1507": (
+ "Using copy.copy(os.environ). Use os.environ.copy() instead. ",
+ "shallow-copy-environ",
+ "os.environ is not a dict object but proxy object, so "
+ "shallow copy has still effects on original object. "
+ "See https://bugs.python.org/issue15373 for reference. ",
+ ),
+ "E1507": (
+ "%s does not support %s type argument",
+ "invalid-envvar-value",
+ "Env manipulation functions support only string type arguments. "
+ "See https://docs.python.org/3/library/os.html#os.getenv. ",
+ ),
+ "W1508": (
+ "%s default type is %s. Expected str or None.",
+ "invalid-envvar-default",
+ "Env manipulation functions return None or str values. "
+ "Supplying anything different as a default may cause bugs. "
+ "See https://docs.python.org/3/library/os.html#os.getenv. ",
+ ),
+ "W1509": (
+ "Using preexec_fn keyword which may be unsafe in the presence "
+ "of threads",
+ "subprocess-popen-preexec-fn",
+ "The preexec_fn parameter is not safe to use in the presence "
+ "of threads in your application. The child process could "
+ "deadlock before exec is called. If you must use it, keep it "
+ "trivial! Minimize the number of libraries you call into."
+ "https://docs.python.org/3/library/subprocess.html#popen-constructor",
+ ),
+ "W1510": (
+ "Using subprocess.run without explicitly set `check` is not recommended.",
+ "subprocess-run-check",
+ "The check parameter should always be used with explicitly set "
+ "`check` keyword to make clear what the error-handling behavior is."
+ "https://docs.python.org/3/library/subprocess.html#subprocess.runs",
+ ),
+ }
+
+ deprecated = {
+ 0: {
+ "cgi.parse_qs",
+ "cgi.parse_qsl",
+ "ctypes.c_buffer",
+ "distutils.command.register.register.check_metadata",
+ "distutils.command.sdist.sdist.check_metadata",
+ "tkinter.Misc.tk_menuBar",
+ "tkinter.Menu.tk_bindForTraversal",
+ },
+ 2: {
+ (2, 6, 0): {
+ "commands.getstatus",
+ "os.popen2",
+ "os.popen3",
+ "os.popen4",
+ "macostools.touched",
+ },
+ (2, 7, 0): {
+ "unittest.case.TestCase.assertEquals",
+ "unittest.case.TestCase.assertNotEquals",
+ "unittest.case.TestCase.assertAlmostEquals",
+ "unittest.case.TestCase.assertNotAlmostEquals",
+ "unittest.case.TestCase.assert_",
+ "xml.etree.ElementTree.Element.getchildren",
+ "xml.etree.ElementTree.Element.getiterator",
+ "xml.etree.ElementTree.XMLParser.getiterator",
+ "xml.etree.ElementTree.XMLParser.doctype",
+ },
+ },
+ 3: {
+ (3, 0, 0): {
+ "inspect.getargspec",
+ "failUnlessEqual",
+ "assertEquals",
+ "failIfEqual",
+ "assertNotEquals",
+ "failUnlessAlmostEqual",
+ "assertAlmostEquals",
+ "failIfAlmostEqual",
+ "assertNotAlmostEquals",
+ "failUnless",
+ "assert_",
+ "failUnlessRaises",
+ "failIf",
+ "assertRaisesRegexp",
+ "assertRegexpMatches",
+ "assertNotRegexpMatches",
+ },
+ (3, 1, 0): {
+ "base64.encodestring",
+ "base64.decodestring",
+ "ntpath.splitunc",
+ },
+ (3, 2, 0): {
+ "cgi.escape",
+ "configparser.RawConfigParser.readfp",
+ "xml.etree.ElementTree.Element.getchildren",
+ "xml.etree.ElementTree.Element.getiterator",
+ "xml.etree.ElementTree.XMLParser.getiterator",
+ "xml.etree.ElementTree.XMLParser.doctype",
+ },
+ (3, 3, 0): {
+ "inspect.getmoduleinfo",
+ "logging.warn",
+ "logging.Logger.warn",
+ "logging.LoggerAdapter.warn",
+ "nntplib._NNTPBase.xpath",
+ "platform.popen",
+ },
+ (3, 4, 0): {
+ "importlib.find_loader",
+ "plistlib.readPlist",
+ "plistlib.writePlist",
+ "plistlib.readPlistFromBytes",
+ "plistlib.writePlistToBytes",
+ },
+ (3, 4, 4): {"asyncio.tasks.async"},
+ (3, 5, 0): {
+ "fractions.gcd",
+ "inspect.getargvalues",
+ "inspect.formatargspec",
+ "inspect.formatargvalues",
+ "inspect.getcallargs",
+ "platform.linux_distribution",
+ "platform.dist",
+ },
+ (3, 6, 0): {"importlib._bootstrap_external.FileLoader.load_module"},
+ },
+ }
+
+ def _check_bad_thread_instantiation(self, node):
+ if not node.kwargs and not node.keywords and len(node.args) <= 1:
+ self.add_message("bad-thread-instantiation", node=node)
+
+ def _check_for_preexec_fn_in_popen(self, node):
+ if node.keywords:
+ for keyword in node.keywords:
+ if keyword.arg == "preexec_fn":
+ self.add_message("subprocess-popen-preexec-fn", node=node)
+
+ def _check_for_check_kw_in_run(self, node):
+ kwargs = {keyword.arg for keyword in (node.keywords or ())}
+ if "check" not in kwargs:
+ self.add_message("subprocess-run-check", node=node)
+
+ def _check_shallow_copy_environ(self, node):
+ arg = utils.get_argument_from_call(node, position=0)
+ for inferred in arg.inferred():
+ if inferred.qname() == OS_ENVIRON:
+ self.add_message("shallow-copy-environ", node=node)
+ break
+
+ @utils.check_messages(
+ "bad-open-mode",
+ "redundant-unittest-assert",
+ "deprecated-method",
+ "bad-thread-instantiation",
+ "shallow-copy-environ",
+ "invalid-envvar-value",
+ "invalid-envvar-default",
+ "subprocess-popen-preexec-fn",
+ "subprocess-run-check",
+ )
+ def visit_call(self, node):
+ """Visit a Call node."""
+ try:
+ for inferred in node.func.infer():
+ if inferred is astroid.Uninferable:
+ continue
+ if inferred.root().name == OPEN_MODULE:
+ if getattr(node.func, "name", None) in OPEN_FILES:
+ self._check_open_mode(node)
+ elif inferred.root().name == UNITTEST_CASE:
+ self._check_redundant_assert(node, inferred)
+ elif isinstance(inferred, astroid.ClassDef):
+ if inferred.qname() == THREADING_THREAD:
+ self._check_bad_thread_instantiation(node)
+ elif inferred.qname() == SUBPROCESS_POPEN:
+ self._check_for_preexec_fn_in_popen(node)
+ elif isinstance(inferred, astroid.FunctionDef):
+ name = inferred.qname()
+ if name == COPY_COPY:
+ self._check_shallow_copy_environ(node)
+ elif name in ENV_GETTERS:
+ self._check_env_function(node, inferred)
+ elif name == SUBPROCESS_RUN:
+ self._check_for_check_kw_in_run(node)
+ self._check_deprecated_method(node, inferred)
+ except astroid.InferenceError:
+ return
+
+ @utils.check_messages("boolean-datetime")
+ def visit_unaryop(self, node):
+ if node.op == "not":
+ self._check_datetime(node.operand)
+
+ @utils.check_messages("boolean-datetime")
+ def visit_if(self, node):
+ self._check_datetime(node.test)
+
+ @utils.check_messages("boolean-datetime")
+ def visit_ifexp(self, node):
+ self._check_datetime(node.test)
+
+ @utils.check_messages("boolean-datetime")
+ def visit_boolop(self, node):
+ for value in node.values:
+ self._check_datetime(value)
+
+ def _check_deprecated_method(self, node, inferred):
+ py_vers = sys.version_info[0]
+
+ if isinstance(node.func, astroid.Attribute):
+ func_name = node.func.attrname
+ elif isinstance(node.func, astroid.Name):
+ func_name = node.func.name
+ else:
+ # Not interested in other nodes.
+ return
+
+ # Reject nodes which aren't of interest to us.
+ acceptable_nodes = (
+ astroid.BoundMethod,
+ astroid.UnboundMethod,
+ astroid.FunctionDef,
+ )
+ if not isinstance(inferred, acceptable_nodes):
+ return
+
+ qname = inferred.qname()
+ if any(name in self.deprecated[0] for name in (qname, func_name)):
+ self.add_message("deprecated-method", node=node, args=(func_name,))
+ else:
+ for since_vers, func_list in self.deprecated[py_vers].items():
+ if since_vers <= sys.version_info and any(
+ name in func_list for name in (qname, func_name)
+ ):
+ self.add_message("deprecated-method", node=node, args=(func_name,))
+ break
+
+ def _check_redundant_assert(self, node, infer):
+ if (
+ isinstance(infer, astroid.BoundMethod)
+ and node.args
+ and isinstance(node.args[0], astroid.Const)
+ and infer.name in ["assertTrue", "assertFalse"]
+ ):
+ self.add_message(
+ "redundant-unittest-assert",
+ args=(infer.name, node.args[0].value),
+ node=node,
+ )
+
+ def _check_datetime(self, node):
+ """ Check that a datetime was inferred.
+ If so, emit boolean-datetime warning.
+ """
+ try:
+ inferred = next(node.infer())
+ except astroid.InferenceError:
+ return
+ if isinstance(inferred, Instance) and inferred.qname() == "datetime.time":
+ self.add_message("boolean-datetime", node=node)
+
+ def _check_open_mode(self, node):
+ """Check that the mode argument of an open or file call is valid."""
+ try:
+ mode_arg = utils.get_argument_from_call(node, position=1, keyword="mode")
+ except utils.NoSuchArgumentError:
+ return
+ if mode_arg:
+ mode_arg = utils.safe_infer(mode_arg)
+ if isinstance(mode_arg, astroid.Const) and not _check_mode_str(
+ mode_arg.value
+ ):
+ self.add_message("bad-open-mode", node=node, args=mode_arg.value)
+
+ def _check_env_function(self, node, infer):
+ env_name_kwarg = "key"
+ env_value_kwarg = "default"
+ if node.keywords:
+ kwargs = {keyword.arg: keyword.value for keyword in node.keywords}
+ else:
+ kwargs = None
+ if node.args:
+ env_name_arg = node.args[0]
+ elif kwargs and env_name_kwarg in kwargs:
+ env_name_arg = kwargs[env_name_kwarg]
+ else:
+ env_name_arg = None
+
+ if env_name_arg:
+ self._check_invalid_envvar_value(
+ node=node,
+ message="invalid-envvar-value",
+ call_arg=utils.safe_infer(env_name_arg),
+ infer=infer,
+ allow_none=False,
+ )
+
+ if len(node.args) == 2:
+ env_value_arg = node.args[1]
+ elif kwargs and env_value_kwarg in kwargs:
+ env_value_arg = kwargs[env_value_kwarg]
+ else:
+ env_value_arg = None
+
+ if env_value_arg:
+ self._check_invalid_envvar_value(
+ node=node,
+ infer=infer,
+ message="invalid-envvar-default",
+ call_arg=utils.safe_infer(env_value_arg),
+ allow_none=True,
+ )
+
+ def _check_invalid_envvar_value(self, node, infer, message, call_arg, allow_none):
+ if call_arg in (astroid.Uninferable, None):
+ return
+
+ name = infer.qname()
+ if isinstance(call_arg, Const):
+ emit = False
+ if call_arg.value is None:
+ emit = not allow_none
+ elif not isinstance(call_arg.value, str):
+ emit = True
+ if emit:
+ self.add_message(message, node=node, args=(name, call_arg.pytype()))
+ else:
+ self.add_message(message, node=node, args=(name, call_arg.pytype()))
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(StdlibChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/strings.py b/venv/Lib/site-packages/pylint/checkers/strings.py
new file mode 100644
index 0000000..9470f46
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/strings.py
@@ -0,0 +1,755 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009 Charles Hebert <charles.hebert@logilab.fr>
+# Copyright (c) 2010-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Rene Zhang <rz99@cornell.edu>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016, 2018 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2016 Peter Dawyndt <Peter.Dawyndt@UGent.be>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Checker for string formatting operations.
+"""
+
+import builtins
+import numbers
+import tokenize
+from collections import Counter
+
+import astroid
+from astroid.arguments import CallSite
+from astroid.node_classes import Const
+
+from pylint.checkers import BaseChecker, BaseTokenChecker, utils
+from pylint.checkers.utils import check_messages
+from pylint.interfaces import IAstroidChecker, IRawChecker, ITokenChecker
+
+_AST_NODE_STR_TYPES = ("__builtin__.unicode", "__builtin__.str", "builtins.str")
+
+MSGS = {
+ "E1300": (
+ "Unsupported format character %r (%#02x) at index %d",
+ "bad-format-character",
+ "Used when an unsupported format character is used in a format string.",
+ ),
+ "E1301": (
+ "Format string ends in middle of conversion specifier",
+ "truncated-format-string",
+ "Used when a format string terminates before the end of a "
+ "conversion specifier.",
+ ),
+ "E1302": (
+ "Mixing named and unnamed conversion specifiers in format string",
+ "mixed-format-string",
+ "Used when a format string contains both named (e.g. '%(foo)d') "
+ "and unnamed (e.g. '%d') conversion specifiers. This is also "
+ "used when a named conversion specifier contains * for the "
+ "minimum field width and/or precision.",
+ ),
+ "E1303": (
+ "Expected mapping for format string, not %s",
+ "format-needs-mapping",
+ "Used when a format string that uses named conversion specifiers "
+ "is used with an argument that is not a mapping.",
+ ),
+ "W1300": (
+ "Format string dictionary key should be a string, not %s",
+ "bad-format-string-key",
+ "Used when a format string that uses named conversion specifiers "
+ "is used with a dictionary whose keys are not all strings.",
+ ),
+ "W1301": (
+ "Unused key %r in format string dictionary",
+ "unused-format-string-key",
+ "Used when a format string that uses named conversion specifiers "
+ "is used with a dictionary that contains keys not required by the "
+ "format string.",
+ ),
+ "E1304": (
+ "Missing key %r in format string dictionary",
+ "missing-format-string-key",
+ "Used when a format string that uses named conversion specifiers "
+ "is used with a dictionary that doesn't contain all the keys "
+ "required by the format string.",
+ ),
+ "E1305": (
+ "Too many arguments for format string",
+ "too-many-format-args",
+ "Used when a format string that uses unnamed conversion "
+ "specifiers is given too many arguments.",
+ ),
+ "E1306": (
+ "Not enough arguments for format string",
+ "too-few-format-args",
+ "Used when a format string that uses unnamed conversion "
+ "specifiers is given too few arguments",
+ ),
+ "E1307": (
+ "Argument %r does not match format type %r",
+ "bad-string-format-type",
+ "Used when a type required by format string "
+ "is not suitable for actual argument type",
+ ),
+ "E1310": (
+ "Suspicious argument in %s.%s call",
+ "bad-str-strip-call",
+ "The argument to a str.{l,r,}strip call contains a duplicate character, ",
+ ),
+ "W1302": (
+ "Invalid format string",
+ "bad-format-string",
+ "Used when a PEP 3101 format string is invalid.",
+ ),
+ "W1303": (
+ "Missing keyword argument %r for format string",
+ "missing-format-argument-key",
+ "Used when a PEP 3101 format string that uses named fields "
+ "doesn't receive one or more required keywords.",
+ ),
+ "W1304": (
+ "Unused format argument %r",
+ "unused-format-string-argument",
+ "Used when a PEP 3101 format string that uses named "
+ "fields is used with an argument that "
+ "is not required by the format string.",
+ ),
+ "W1305": (
+ "Format string contains both automatic field numbering "
+ "and manual field specification",
+ "format-combined-specification",
+ "Used when a PEP 3101 format string contains both automatic "
+ "field numbering (e.g. '{}') and manual field "
+ "specification (e.g. '{0}').",
+ ),
+ "W1306": (
+ "Missing format attribute %r in format specifier %r",
+ "missing-format-attribute",
+ "Used when a PEP 3101 format string uses an "
+ "attribute specifier ({0.length}), but the argument "
+ "passed for formatting doesn't have that attribute.",
+ ),
+ "W1307": (
+ "Using invalid lookup key %r in format specifier %r",
+ "invalid-format-index",
+ "Used when a PEP 3101 format string uses a lookup specifier "
+ "({a[1]}), but the argument passed for formatting "
+ "doesn't contain or doesn't have that key as an attribute.",
+ ),
+ "W1308": (
+ "Duplicate string formatting argument %r, consider passing as named argument",
+ "duplicate-string-formatting-argument",
+ "Used when we detect that a string formatting is "
+ "repeating an argument instead of using named string arguments",
+ ),
+}
+
+OTHER_NODES = (
+ astroid.Const,
+ astroid.List,
+ astroid.Lambda,
+ astroid.FunctionDef,
+ astroid.ListComp,
+ astroid.SetComp,
+ astroid.GeneratorExp,
+)
+
+BUILTINS_STR = builtins.__name__ + ".str"
+BUILTINS_FLOAT = builtins.__name__ + ".float"
+BUILTINS_INT = builtins.__name__ + ".int"
+
+
+def get_access_path(key, parts):
+ """ Given a list of format specifiers, returns
+ the final access path (e.g. a.b.c[0][1]).
+ """
+ path = []
+ for is_attribute, specifier in parts:
+ if is_attribute:
+ path.append(".{}".format(specifier))
+ else:
+ path.append("[{!r}]".format(specifier))
+ return str(key) + "".join(path)
+
+
+def arg_matches_format_type(arg_type, format_type):
+ if format_type in "sr":
+ # All types can be printed with %s and %r
+ return True
+ if isinstance(arg_type, astroid.Instance):
+ arg_type = arg_type.pytype()
+ if arg_type == BUILTINS_STR:
+ return format_type == "c"
+ if arg_type == BUILTINS_FLOAT:
+ return format_type in "deEfFgGn%"
+ if arg_type == BUILTINS_INT:
+ # Integers allow all types
+ return True
+ return False
+ return True
+
+
+class StringFormatChecker(BaseChecker):
+ """Checks string formatting operations to ensure that the format string
+ is valid and the arguments match the format string.
+ """
+
+ __implements__ = (IAstroidChecker,)
+ name = "string"
+ msgs = MSGS
+
+ # pylint: disable=too-many-branches
+ @check_messages(*MSGS)
+ def visit_binop(self, node):
+ if node.op != "%":
+ return
+ left = node.left
+ args = node.right
+
+ if not (isinstance(left, astroid.Const) and isinstance(left.value, str)):
+ return
+ format_string = left.value
+ try:
+ required_keys, required_num_args, required_key_types, required_arg_types = utils.parse_format_string(
+ format_string
+ )
+ except utils.UnsupportedFormatCharacter as exc:
+ formatted = format_string[exc.index]
+ self.add_message(
+ "bad-format-character",
+ node=node,
+ args=(formatted, ord(formatted), exc.index),
+ )
+ return
+ except utils.IncompleteFormatString:
+ self.add_message("truncated-format-string", node=node)
+ return
+ if required_keys and required_num_args:
+ # The format string uses both named and unnamed format
+ # specifiers.
+ self.add_message("mixed-format-string", node=node)
+ elif required_keys:
+ # The format string uses only named format specifiers.
+ # Check that the RHS of the % operator is a mapping object
+ # that contains precisely the set of keys required by the
+ # format string.
+ if isinstance(args, astroid.Dict):
+ keys = set()
+ unknown_keys = False
+ for k, _ in args.items:
+ if isinstance(k, astroid.Const):
+ key = k.value
+ if isinstance(key, str):
+ keys.add(key)
+ else:
+ self.add_message(
+ "bad-format-string-key", node=node, args=key
+ )
+ else:
+ # One of the keys was something other than a
+ # constant. Since we can't tell what it is,
+ # suppress checks for missing keys in the
+ # dictionary.
+ unknown_keys = True
+ if not unknown_keys:
+ for key in required_keys:
+ if key not in keys:
+ self.add_message(
+ "missing-format-string-key", node=node, args=key
+ )
+ for key in keys:
+ if key not in required_keys:
+ self.add_message(
+ "unused-format-string-key", node=node, args=key
+ )
+ for key, arg in args.items:
+ if not isinstance(key, astroid.Const):
+ continue
+ format_type = required_key_types.get(key.value, None)
+ arg_type = utils.safe_infer(arg)
+ if (
+ format_type is not None
+ and arg_type not in (None, astroid.Uninferable)
+ and not arg_matches_format_type(arg_type, format_type)
+ ):
+ self.add_message(
+ "bad-string-format-type",
+ node=node,
+ args=(arg_type.pytype(), format_type),
+ )
+ elif isinstance(args, (OTHER_NODES, astroid.Tuple)):
+ type_name = type(args).__name__
+ self.add_message("format-needs-mapping", node=node, args=type_name)
+ # else:
+ # The RHS of the format specifier is a name or
+ # expression. It may be a mapping object, so
+ # there's nothing we can check.
+ else:
+ # The format string uses only unnamed format specifiers.
+ # Check that the number of arguments passed to the RHS of
+ # the % operator matches the number required by the format
+ # string.
+ args_elts = ()
+ if isinstance(args, astroid.Tuple):
+ rhs_tuple = utils.safe_infer(args)
+ num_args = None
+ if hasattr(rhs_tuple, "elts"):
+ args_elts = rhs_tuple.elts
+ num_args = len(args_elts)
+ elif isinstance(args, (OTHER_NODES, (astroid.Dict, astroid.DictComp))):
+ args_elts = [args]
+ num_args = 1
+ else:
+ # The RHS of the format specifier is a name or
+ # expression. It could be a tuple of unknown size, so
+ # there's nothing we can check.
+ num_args = None
+ if num_args is not None:
+ if num_args > required_num_args:
+ self.add_message("too-many-format-args", node=node)
+ elif num_args < required_num_args:
+ self.add_message("too-few-format-args", node=node)
+ for arg, format_type in zip(args_elts, required_arg_types):
+ if not arg:
+ continue
+ arg_type = utils.safe_infer(arg)
+ if arg_type not in (
+ None,
+ astroid.Uninferable,
+ ) and not arg_matches_format_type(arg_type, format_type):
+ self.add_message(
+ "bad-string-format-type",
+ node=node,
+ args=(arg_type.pytype(), format_type),
+ )
+
+ @check_messages(*MSGS)
+ def visit_call(self, node):
+ func = utils.safe_infer(node.func)
+ if (
+ isinstance(func, astroid.BoundMethod)
+ and isinstance(func.bound, astroid.Instance)
+ and func.bound.name in ("str", "unicode", "bytes")
+ ):
+ if func.name in ("strip", "lstrip", "rstrip") and node.args:
+ arg = utils.safe_infer(node.args[0])
+ if not isinstance(arg, astroid.Const) or not isinstance(arg.value, str):
+ return
+ if len(arg.value) != len(set(arg.value)):
+ self.add_message(
+ "bad-str-strip-call",
+ node=node,
+ args=(func.bound.name, func.name),
+ )
+ elif func.name == "format":
+ self._check_new_format(node, func)
+
+ def _detect_vacuous_formatting(self, node, positional_arguments):
+ counter = Counter(
+ arg.name for arg in positional_arguments if isinstance(arg, astroid.Name)
+ )
+ for name, count in counter.items():
+ if count == 1:
+ continue
+ self.add_message(
+ "duplicate-string-formatting-argument", node=node, args=(name,)
+ )
+
+ def _check_new_format(self, node, func):
+ """Check the new string formatting. """
+ # Skip ormat nodes which don't have an explicit string on the
+ # left side of the format operation.
+ # We do this because our inference engine can't properly handle
+ # redefinitions of the original string.
+ # Note that there may not be any left side at all, if the format method
+ # has been assigned to another variable. See issue 351. For example:
+ #
+ # fmt = 'some string {}'.format
+ # fmt('arg')
+ if isinstance(node.func, astroid.Attribute) and not isinstance(
+ node.func.expr, astroid.Const
+ ):
+ return
+ if node.starargs or node.kwargs:
+ return
+ try:
+ strnode = next(func.bound.infer())
+ except astroid.InferenceError:
+ return
+ if not (isinstance(strnode, astroid.Const) and isinstance(strnode.value, str)):
+ return
+ try:
+ call_site = CallSite.from_call(node)
+ except astroid.InferenceError:
+ return
+
+ try:
+ fields, num_args, manual_pos = utils.parse_format_method_string(
+ strnode.value
+ )
+ except utils.IncompleteFormatString:
+ self.add_message("bad-format-string", node=node)
+ return
+
+ positional_arguments = call_site.positional_arguments
+ named_arguments = call_site.keyword_arguments
+ named_fields = {field[0] for field in fields if isinstance(field[0], str)}
+ if num_args and manual_pos:
+ self.add_message("format-combined-specification", node=node)
+ return
+
+ check_args = False
+ # Consider "{[0]} {[1]}" as num_args.
+ num_args += sum(1 for field in named_fields if field == "")
+ if named_fields:
+ for field in named_fields:
+ if field and field not in named_arguments:
+ self.add_message(
+ "missing-format-argument-key", node=node, args=(field,)
+ )
+ for field in named_arguments:
+ if field not in named_fields:
+ self.add_message(
+ "unused-format-string-argument", node=node, args=(field,)
+ )
+ # num_args can be 0 if manual_pos is not.
+ num_args = num_args or manual_pos
+ if positional_arguments or num_args:
+ empty = any(True for field in named_fields if field == "")
+ if named_arguments or empty:
+ # Verify the required number of positional arguments
+ # only if the .format got at least one keyword argument.
+ # This means that the format strings accepts both
+ # positional and named fields and we should warn
+ # when one of the them is missing or is extra.
+ check_args = True
+ else:
+ check_args = True
+ if check_args:
+ # num_args can be 0 if manual_pos is not.
+ num_args = num_args or manual_pos
+ if len(positional_arguments) > num_args:
+ self.add_message("too-many-format-args", node=node)
+ elif len(positional_arguments) < num_args:
+ self.add_message("too-few-format-args", node=node)
+
+ self._detect_vacuous_formatting(node, positional_arguments)
+ self._check_new_format_specifiers(node, fields, named_arguments)
+
+ def _check_new_format_specifiers(self, node, fields, named):
+ """
+ Check attribute and index access in the format
+ string ("{0.a}" and "{0[a]}").
+ """
+ for key, specifiers in fields:
+ # Obtain the argument. If it can't be obtained
+ # or inferred, skip this check.
+ if key == "":
+ # {[0]} will have an unnamed argument, defaulting
+ # to 0. It will not be present in `named`, so use the value
+ # 0 for it.
+ key = 0
+ if isinstance(key, numbers.Number):
+ try:
+ argname = utils.get_argument_from_call(node, key)
+ except utils.NoSuchArgumentError:
+ continue
+ else:
+ if key not in named:
+ continue
+ argname = named[key]
+ if argname in (astroid.Uninferable, None):
+ continue
+ try:
+ argument = utils.safe_infer(argname)
+ except astroid.InferenceError:
+ continue
+ if not specifiers or not argument:
+ # No need to check this key if it doesn't
+ # use attribute / item access
+ continue
+ if argument.parent and isinstance(argument.parent, astroid.Arguments):
+ # Ignore any object coming from an argument,
+ # because we can't infer its value properly.
+ continue
+ previous = argument
+ parsed = []
+ for is_attribute, specifier in specifiers:
+ if previous is astroid.Uninferable:
+ break
+ parsed.append((is_attribute, specifier))
+ if is_attribute:
+ try:
+ previous = previous.getattr(specifier)[0]
+ except astroid.NotFoundError:
+ if (
+ hasattr(previous, "has_dynamic_getattr")
+ and previous.has_dynamic_getattr()
+ ):
+ # Don't warn if the object has a custom __getattr__
+ break
+ path = get_access_path(key, parsed)
+ self.add_message(
+ "missing-format-attribute",
+ args=(specifier, path),
+ node=node,
+ )
+ break
+ else:
+ warn_error = False
+ if hasattr(previous, "getitem"):
+ try:
+ previous = previous.getitem(astroid.Const(specifier))
+ except (
+ astroid.AstroidIndexError,
+ astroid.AstroidTypeError,
+ astroid.AttributeInferenceError,
+ ):
+ warn_error = True
+ except astroid.InferenceError:
+ break
+ if previous is astroid.Uninferable:
+ break
+ else:
+ try:
+ # Lookup __getitem__ in the current node,
+ # but skip further checks, because we can't
+ # retrieve the looked object
+ previous.getattr("__getitem__")
+ break
+ except astroid.NotFoundError:
+ warn_error = True
+ if warn_error:
+ path = get_access_path(key, parsed)
+ self.add_message(
+ "invalid-format-index", args=(specifier, path), node=node
+ )
+ break
+
+ try:
+ previous = next(previous.infer())
+ except astroid.InferenceError:
+ # can't check further if we can't infer it
+ break
+
+
+class StringConstantChecker(BaseTokenChecker):
+ """Check string literals"""
+
+ __implements__ = (IAstroidChecker, ITokenChecker, IRawChecker)
+ name = "string"
+ msgs = {
+ "W1401": (
+ "Anomalous backslash in string: '%s'. "
+ "String constant might be missing an r prefix.",
+ "anomalous-backslash-in-string",
+ "Used when a backslash is in a literal string but not as an escape.",
+ ),
+ "W1402": (
+ "Anomalous Unicode escape in byte string: '%s'. "
+ "String constant might be missing an r or u prefix.",
+ "anomalous-unicode-escape-in-string",
+ "Used when an escape like \\u is encountered in a byte "
+ "string where it has no effect.",
+ ),
+ "W1403": (
+ "Implicit string concatenation found in %s",
+ "implicit-str-concat-in-sequence",
+ "String literals are implicitly concatenated in a "
+ "literal iterable definition : "
+ "maybe a comma is missing ?",
+ ),
+ }
+ options = (
+ (
+ "check-str-concat-over-line-jumps",
+ {
+ "default": False,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": "This flag controls whether the "
+ "implicit-str-concat-in-sequence should generate a warning "
+ "on implicit string concatenation in sequences defined over "
+ "several lines.",
+ },
+ ),
+ )
+
+ # Characters that have a special meaning after a backslash in either
+ # Unicode or byte strings.
+ ESCAPE_CHARACTERS = "abfnrtvx\n\r\t\\'\"01234567"
+
+ # Characters that have a special meaning after a backslash but only in
+ # Unicode strings.
+ UNICODE_ESCAPE_CHARACTERS = "uUN"
+
+ def __init__(self, *args, **kwargs):
+ super(StringConstantChecker, self).__init__(*args, **kwargs)
+ self.string_tokens = {} # token position -> (token value, next token)
+
+ def process_module(self, module):
+ self._unicode_literals = "unicode_literals" in module.future_imports
+
+ def process_tokens(self, tokens):
+ encoding = "ascii"
+ for i, (tok_type, token, start, _, line) in enumerate(tokens):
+ if tok_type == tokenize.ENCODING:
+ # this is always the first token processed
+ encoding = token
+ elif tok_type == tokenize.STRING:
+ # 'token' is the whole un-parsed token; we can look at the start
+ # of it to see whether it's a raw or unicode string etc.
+ self.process_string_token(token, start[0])
+ # We figure the next token, ignoring comments & newlines:
+ j = i + 1
+ while j < len(tokens) and tokens[j].type in (
+ tokenize.NEWLINE,
+ tokenize.NL,
+ tokenize.COMMENT,
+ ):
+ j += 1
+ next_token = tokens[j] if j < len(tokens) else None
+ if encoding != "ascii":
+ # We convert `tokenize` character count into a byte count,
+ # to match with astroid `.col_offset`
+ start = (start[0], len(line[: start[1]].encode(encoding)))
+ self.string_tokens[start] = (str_eval(token), next_token)
+
+ @check_messages(*(msgs.keys()))
+ def visit_list(self, node):
+ self.check_for_concatenated_strings(node, "list")
+
+ @check_messages(*(msgs.keys()))
+ def visit_set(self, node):
+ self.check_for_concatenated_strings(node, "set")
+
+ @check_messages(*(msgs.keys()))
+ def visit_tuple(self, node):
+ self.check_for_concatenated_strings(node, "tuple")
+
+ def check_for_concatenated_strings(self, iterable_node, iterable_type):
+ for elt in iterable_node.elts:
+ if isinstance(elt, Const) and elt.pytype() in _AST_NODE_STR_TYPES:
+ if elt.col_offset < 0:
+ # This can happen in case of escaped newlines
+ continue
+ if (elt.lineno, elt.col_offset) not in self.string_tokens:
+ # This may happen with Latin1 encoding
+ # cf. https://github.com/PyCQA/pylint/issues/2610
+ continue
+ matching_token, next_token = self.string_tokens[
+ (elt.lineno, elt.col_offset)
+ ]
+ # We detect string concatenation: the AST Const is the
+ # combination of 2 string tokens
+ if matching_token != elt.value and next_token is not None:
+ if next_token.type == tokenize.STRING and (
+ next_token.start[0] == elt.lineno
+ or self.config.check_str_concat_over_line_jumps
+ ):
+ self.add_message(
+ "implicit-str-concat-in-sequence",
+ line=elt.lineno,
+ args=(iterable_type,),
+ )
+
+ def process_string_token(self, token, start_row):
+ quote_char = None
+ index = None
+ for index, char in enumerate(token):
+ if char in "'\"":
+ quote_char = char
+ break
+ if quote_char is None:
+ return
+
+ prefix = token[:index].lower() # markers like u, b, r.
+ after_prefix = token[index:]
+ if after_prefix[:3] == after_prefix[-3:] == 3 * quote_char:
+ string_body = after_prefix[3:-3]
+ else:
+ string_body = after_prefix[1:-1] # Chop off quotes
+ # No special checks on raw strings at the moment.
+ if "r" not in prefix:
+ self.process_non_raw_string_token(prefix, string_body, start_row)
+
+ def process_non_raw_string_token(self, prefix, string_body, start_row):
+ """check for bad escapes in a non-raw string.
+
+ prefix: lowercase string of eg 'ur' string prefix markers.
+ string_body: the un-parsed body of the string, not including the quote
+ marks.
+ start_row: integer line number in the source.
+ """
+ # Walk through the string; if we see a backslash then escape the next
+ # character, and skip over it. If we see a non-escaped character,
+ # alert, and continue.
+ #
+ # Accept a backslash when it escapes a backslash, or a quote, or
+ # end-of-line, or one of the letters that introduce a special escape
+ # sequence <http://docs.python.org/reference/lexical_analysis.html>
+ #
+ index = 0
+ while True:
+ index = string_body.find("\\", index)
+ if index == -1:
+ break
+ # There must be a next character; having a backslash at the end
+ # of the string would be a SyntaxError.
+ next_char = string_body[index + 1]
+ match = string_body[index : index + 2]
+ if next_char in self.UNICODE_ESCAPE_CHARACTERS:
+ if "u" in prefix:
+ pass
+ elif "b" not in prefix:
+ pass # unicode by default
+ else:
+ self.add_message(
+ "anomalous-unicode-escape-in-string",
+ line=start_row,
+ args=(match,),
+ col_offset=index,
+ )
+ elif next_char not in self.ESCAPE_CHARACTERS:
+ self.add_message(
+ "anomalous-backslash-in-string",
+ line=start_row,
+ args=(match,),
+ col_offset=index,
+ )
+ # Whether it was a valid escape or not, backslash followed by
+ # another character can always be consumed whole: the second
+ # character can never be the start of a new backslash escape.
+ index += 2
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(StringFormatChecker(linter))
+ linter.register_checker(StringConstantChecker(linter))
+
+
+def str_eval(token):
+ """
+ Mostly replicate `ast.literal_eval(token)` manually to avoid any performance hit.
+ This supports f-strings, contrary to `ast.literal_eval`.
+ We have to support all string literal notations:
+ https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals
+ """
+ if token[0:2].lower() in ("fr", "rf"):
+ token = token[2:]
+ elif token[0].lower() in ("r", "u", "f"):
+ token = token[1:]
+ if token[0:3] in ('"""', "'''"):
+ return token[3:-3]
+ return token[1:-1]
diff --git a/venv/Lib/site-packages/pylint/checkers/typecheck.py b/venv/Lib/site-packages/pylint/checkers/typecheck.py
new file mode 100644
index 0000000..a288f49
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/typecheck.py
@@ -0,0 +1,1770 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2009 James Lingard <jchl@aristanetworks.com>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 David Shea <dshea@redhat.com>
+# Copyright (c) 2014 Steven Myint <hg@stevenmyint.com>
+# Copyright (c) 2014 Holger Peters <email@holger-peters.de>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Anentropic <ego@anentropic.com>
+# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
+# Copyright (c) 2015 Rene Zhang <rz99@cornell.edu>
+# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2016 Jürgen Hermann <jh@web.de>
+# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2016 Filipe Brandenburger <filbranden@google.com>
+# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 Ben Green <benhgreen@icloud.com>
+# Copyright (c) 2018 Konstantin <Github@pheanex.de>
+# Copyright (c) 2018 Justin Li <justinnhli@users.noreply.github.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""try to find more bugs in the code using astroid inference capabilities
+"""
+
+import builtins
+import fnmatch
+import heapq
+import itertools
+import operator
+import re
+import shlex
+import sys
+import types
+from collections import deque
+from collections.abc import Sequence
+from functools import singledispatch
+
+import astroid
+import astroid.arguments
+import astroid.context
+import astroid.nodes
+from astroid import bases, decorators, exceptions, modutils, objects
+from astroid.interpreter import dunder_lookup
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import (
+ check_messages,
+ decorated_with,
+ decorated_with_property,
+ has_known_bases,
+ is_builtin_object,
+ is_comprehension,
+ is_inside_abstract_class,
+ is_iterable,
+ is_mapping,
+ is_overload_stub,
+ is_super,
+ node_ignores_exception,
+ safe_infer,
+ supports_delitem,
+ supports_getitem,
+ supports_membership_test,
+ supports_setitem,
+)
+from pylint.interfaces import INFERENCE, IAstroidChecker
+from pylint.utils import get_global_option
+
+BUILTINS = builtins.__name__
+STR_FORMAT = {"%s.str.format" % BUILTINS}
+ASYNCIO_COROUTINE = "asyncio.coroutines.coroutine"
+
+
+def _unflatten(iterable):
+ for index, elem in enumerate(iterable):
+ if isinstance(elem, Sequence) and not isinstance(elem, str):
+ for single_elem in _unflatten(elem):
+ yield single_elem
+ elif elem and not index:
+ # We're interested only in the first element.
+ yield elem
+
+
+def _flatten_container(iterable):
+ # Flatten nested containers into a single iterable
+ for item in iterable:
+ if isinstance(item, (list, tuple, types.GeneratorType)):
+ yield from _flatten_container(item)
+ else:
+ yield item
+
+
+def _is_owner_ignored(owner, attrname, ignored_classes, ignored_modules):
+ """Check if the given owner should be ignored
+
+ This will verify if the owner's module is in *ignored_modules*
+ or the owner's module fully qualified name is in *ignored_modules*
+ or if the *ignored_modules* contains a pattern which catches
+ the fully qualified name of the module.
+
+ Also, similar checks are done for the owner itself, if its name
+ matches any name from the *ignored_classes* or if its qualified
+ name can be found in *ignored_classes*.
+ """
+ ignored_modules = set(ignored_modules)
+ module_name = owner.root().name
+ module_qname = owner.root().qname()
+
+ for ignore in ignored_modules:
+ # Try to match the module name / fully qualified name directly
+ if module_qname in ignored_modules or module_name in ignored_modules:
+ return True
+
+ # Try to see if the ignores pattern match against the module name.
+ if fnmatch.fnmatch(module_qname, ignore):
+ return True
+
+ # Otherwise we might have a root module name being ignored,
+ # and the qualified owner has more levels of depth.
+ parts = deque(module_name.split("."))
+ current_module = ""
+
+ while parts:
+ part = parts.popleft()
+ if not current_module:
+ current_module = part
+ else:
+ current_module += ".{}".format(part)
+ if current_module in ignored_modules:
+ return True
+
+ # Match against ignored classes.
+ ignored_classes = set(ignored_classes)
+ if hasattr(owner, "qname"):
+ qname = owner.qname()
+ else:
+ qname = ""
+ return any(ignore in (attrname, qname) for ignore in ignored_classes)
+
+
+@singledispatch
+def _node_names(node):
+ if not hasattr(node, "locals"):
+ return []
+ return node.locals.keys()
+
+
+@_node_names.register(astroid.ClassDef)
+@_node_names.register(astroid.Instance)
+def _(node):
+ values = itertools.chain(node.instance_attrs.keys(), node.locals.keys())
+
+ try:
+ mro = node.mro()[1:]
+ except (NotImplementedError, TypeError):
+ mro = node.ancestors()
+
+ other_values = [value for cls in mro for value in _node_names(cls)]
+ return itertools.chain(values, other_values)
+
+
+def _string_distance(seq1, seq2):
+ seq2_length = len(seq2)
+
+ row = list(range(1, seq2_length + 1)) + [0]
+ for seq1_index, seq1_char in enumerate(seq1):
+ last_row = row
+ row = [0] * seq2_length + [seq1_index + 1]
+
+ for seq2_index, seq2_char in enumerate(seq2):
+ row[seq2_index] = min(
+ last_row[seq2_index] + 1,
+ row[seq2_index - 1] + 1,
+ last_row[seq2_index - 1] + (seq1_char != seq2_char),
+ )
+
+ return row[seq2_length - 1]
+
+
+def _similar_names(owner, attrname, distance_threshold, max_choices):
+ """Given an owner and a name, try to find similar names
+
+ The similar names are searched given a distance metric and only
+ a given number of choices will be returned.
+ """
+ possible_names = []
+ names = _node_names(owner)
+
+ for name in names:
+ if name == attrname:
+ continue
+
+ distance = _string_distance(attrname, name)
+ if distance <= distance_threshold:
+ possible_names.append((name, distance))
+
+ # Now get back the values with a minimum, up to the given
+ # limit or choices.
+ picked = [
+ name
+ for (name, _) in heapq.nsmallest(
+ max_choices, possible_names, key=operator.itemgetter(1)
+ )
+ ]
+ return sorted(picked)
+
+
+def _missing_member_hint(owner, attrname, distance_threshold, max_choices):
+ names = _similar_names(owner, attrname, distance_threshold, max_choices)
+ if not names:
+ # No similar name.
+ return ""
+
+ names = list(map(repr, names))
+ if len(names) == 1:
+ names = ", ".join(names)
+ else:
+ names = "one of {} or {}".format(", ".join(names[:-1]), names[-1])
+
+ return "; maybe {}?".format(names)
+
+
+MSGS = {
+ "E1101": (
+ "%s %r has no %r member%s",
+ "no-member",
+ "Used when a variable is accessed for an unexistent member.",
+ {"old_names": [("E1103", "maybe-no-member")]},
+ ),
+ "I1101": (
+ "%s %r has no %r member%s, but source is unavailable. Consider "
+ "adding this module to extension-pkg-whitelist if you want "
+ "to perform analysis based on run-time introspection of living objects.",
+ "c-extension-no-member",
+ "Used when a variable is accessed for non-existent member of C "
+ "extension. Due to unavailability of source static analysis is impossible, "
+ "but it may be performed by introspecting living objects in run-time.",
+ ),
+ "E1102": (
+ "%s is not callable",
+ "not-callable",
+ "Used when an object being called has been inferred to a non "
+ "callable object.",
+ ),
+ "E1111": (
+ "Assigning result of a function call, where the function has no return",
+ "assignment-from-no-return",
+ "Used when an assignment is done on a function call but the "
+ "inferred function doesn't return anything.",
+ ),
+ "E1120": (
+ "No value for argument %s in %s call",
+ "no-value-for-parameter",
+ "Used when a function call passes too few arguments.",
+ ),
+ "E1121": (
+ "Too many positional arguments for %s call",
+ "too-many-function-args",
+ "Used when a function call passes too many positional arguments.",
+ ),
+ "E1123": (
+ "Unexpected keyword argument %r in %s call",
+ "unexpected-keyword-arg",
+ "Used when a function call passes a keyword argument that "
+ "doesn't correspond to one of the function's parameter names.",
+ ),
+ "E1124": (
+ "Argument %r passed by position and keyword in %s call",
+ "redundant-keyword-arg",
+ "Used when a function call would result in assigning multiple "
+ "values to a function parameter, one value from a positional "
+ "argument and one from a keyword argument.",
+ ),
+ "E1125": (
+ "Missing mandatory keyword argument %r in %s call",
+ "missing-kwoa",
+ (
+ "Used when a function call does not pass a mandatory"
+ " keyword-only argument."
+ ),
+ ),
+ "E1126": (
+ "Sequence index is not an int, slice, or instance with __index__",
+ "invalid-sequence-index",
+ "Used when a sequence type is indexed with an invalid type. "
+ "Valid types are ints, slices, and objects with an __index__ "
+ "method.",
+ ),
+ "E1127": (
+ "Slice index is not an int, None, or instance with __index__",
+ "invalid-slice-index",
+ "Used when a slice index is not an integer, None, or an object "
+ "with an __index__ method.",
+ ),
+ "E1128": (
+ "Assigning result of a function call, where the function returns None",
+ "assignment-from-none",
+ "Used when an assignment is done on a function call but the "
+ "inferred function returns nothing but None.",
+ {"old_names": [("W1111", "old-assignment-from-none")]},
+ ),
+ "E1129": (
+ "Context manager '%s' doesn't implement __enter__ and __exit__.",
+ "not-context-manager",
+ "Used when an instance in a with statement doesn't implement "
+ "the context manager protocol(__enter__/__exit__).",
+ ),
+ "E1130": (
+ "%s",
+ "invalid-unary-operand-type",
+ "Emitted when a unary operand is used on an object which does not "
+ "support this type of operation.",
+ ),
+ "E1131": (
+ "%s",
+ "unsupported-binary-operation",
+ "Emitted when a binary arithmetic operation between two "
+ "operands is not supported.",
+ ),
+ "E1132": (
+ "Got multiple values for keyword argument %r in function call",
+ "repeated-keyword",
+ "Emitted when a function call got multiple values for a keyword.",
+ ),
+ "E1135": (
+ "Value '%s' doesn't support membership test",
+ "unsupported-membership-test",
+ "Emitted when an instance in membership test expression doesn't "
+ "implement membership protocol (__contains__/__iter__/__getitem__).",
+ ),
+ "E1136": (
+ "Value '%s' is unsubscriptable",
+ "unsubscriptable-object",
+ "Emitted when a subscripted value doesn't support subscription "
+ "(i.e. doesn't define __getitem__ method or __class_getitem__ for a class).",
+ ),
+ "E1137": (
+ "%r does not support item assignment",
+ "unsupported-assignment-operation",
+ "Emitted when an object does not support item assignment "
+ "(i.e. doesn't define __setitem__ method).",
+ ),
+ "E1138": (
+ "%r does not support item deletion",
+ "unsupported-delete-operation",
+ "Emitted when an object does not support item deletion "
+ "(i.e. doesn't define __delitem__ method).",
+ ),
+ "E1139": (
+ "Invalid metaclass %r used",
+ "invalid-metaclass",
+ "Emitted whenever we can detect that a class is using, "
+ "as a metaclass, something which might be invalid for using as "
+ "a metaclass.",
+ ),
+ "E1140": (
+ "Dict key is unhashable",
+ "unhashable-dict-key",
+ "Emitted when a dict key is not hashable "
+ "(i.e. doesn't define __hash__ method).",
+ ),
+ "E1141": (
+ "Unpacking a dictionary in iteration without calling .items()",
+ "dict-iter-missing-items",
+ "Emitted when trying to iterate through a dict without calling .items()",
+ ),
+ "W1113": (
+ "Keyword argument before variable positional arguments list "
+ "in the definition of %s function",
+ "keyword-arg-before-vararg",
+ "When defining a keyword argument before variable positional arguments, one can "
+ "end up in having multiple values passed for the aforementioned parameter in "
+ "case the method is called with keyword arguments.",
+ ),
+ "W1114": (
+ "Positional arguments appear to be out of order",
+ "arguments-out-of-order",
+ "Emitted when the caller's argument names fully match the parameter "
+ "names in the function signature but do not have the same order.",
+ ),
+}
+
+# builtin sequence types in Python 2 and 3.
+SEQUENCE_TYPES = {
+ "str",
+ "unicode",
+ "list",
+ "tuple",
+ "bytearray",
+ "xrange",
+ "range",
+ "bytes",
+ "memoryview",
+}
+
+
+def _emit_no_member(node, owner, owner_name, ignored_mixins=True, ignored_none=True):
+ """Try to see if no-member should be emitted for the given owner.
+
+ The following cases are ignored:
+
+ * the owner is a function and it has decorators.
+ * the owner is an instance and it has __getattr__, __getattribute__ implemented
+ * the module is explicitly ignored from no-member checks
+ * the owner is a class and the name can be found in its metaclass.
+ * The access node is protected by an except handler, which handles
+ AttributeError, Exception or bare except.
+ """
+ # pylint: disable=too-many-return-statements
+ if node_ignores_exception(node, AttributeError):
+ return False
+ if ignored_none and isinstance(owner, astroid.Const) and owner.value is None:
+ return False
+ if is_super(owner) or getattr(owner, "type", None) == "metaclass":
+ return False
+ if owner_name and ignored_mixins and owner_name[-5:].lower() == "mixin":
+ return False
+ if isinstance(owner, astroid.FunctionDef) and owner.decorators:
+ return False
+ if isinstance(owner, (astroid.Instance, astroid.ClassDef)):
+ if owner.has_dynamic_getattr():
+ # Issue #2565: Don't ignore enums, as they have a `__getattr__` but it's not
+ # invoked at this point.
+ try:
+ metaclass = owner.metaclass()
+ except exceptions.MroError:
+ return False
+ if metaclass:
+ return metaclass.qname() == "enum.EnumMeta"
+ return False
+ if not has_known_bases(owner):
+ return False
+
+ # Exclude typed annotations, since these might actually exist
+ # at some point during the runtime of the program.
+ attribute = owner.locals.get(node.attrname, [None])[0]
+ if (
+ attribute
+ and isinstance(attribute, astroid.AssignName)
+ and isinstance(attribute.parent, astroid.AnnAssign)
+ ):
+ return False
+ if isinstance(owner, objects.Super):
+ # Verify if we are dealing with an invalid Super object.
+ # If it is invalid, then there's no point in checking that
+ # it has the required attribute. Also, don't fail if the
+ # MRO is invalid.
+ try:
+ owner.super_mro()
+ except (exceptions.MroError, exceptions.SuperError):
+ return False
+ if not all(map(has_known_bases, owner.type.mro())):
+ return False
+ if isinstance(owner, astroid.Module):
+ try:
+ owner.getattr("__getattr__")
+ return False
+ except astroid.NotFoundError:
+ pass
+ if owner_name and node.attrname.startswith("_" + owner_name):
+ # Test if an attribute has been mangled ('private' attribute)
+ unmangled_name = node.attrname.split("_" + owner_name)[-1]
+ try:
+ if owner.getattr(unmangled_name, context=None) is not None:
+ return False
+ except astroid.NotFoundError:
+ return True
+ return True
+
+
+def _determine_callable(callable_obj):
+ # Ordering is important, since BoundMethod is a subclass of UnboundMethod,
+ # and Function inherits Lambda.
+ parameters = 0
+ if hasattr(callable_obj, "implicit_parameters"):
+ parameters = callable_obj.implicit_parameters()
+ if isinstance(callable_obj, astroid.BoundMethod):
+ # Bound methods have an extra implicit 'self' argument.
+ return callable_obj, parameters, callable_obj.type
+ if isinstance(callable_obj, astroid.UnboundMethod):
+ return callable_obj, parameters, "unbound method"
+ if isinstance(callable_obj, astroid.FunctionDef):
+ return callable_obj, parameters, callable_obj.type
+ if isinstance(callable_obj, astroid.Lambda):
+ return callable_obj, parameters, "lambda"
+ if isinstance(callable_obj, astroid.ClassDef):
+ # Class instantiation, lookup __new__ instead.
+ # If we only find object.__new__, we can safely check __init__
+ # instead. If __new__ belongs to builtins, then we look
+ # again for __init__ in the locals, since we won't have
+ # argument information for the builtin __new__ function.
+ try:
+ # Use the last definition of __new__.
+ new = callable_obj.local_attr("__new__")[-1]
+ except exceptions.NotFoundError:
+ new = None
+
+ from_object = new and new.parent.scope().name == "object"
+ from_builtins = new and new.root().name in sys.builtin_module_names
+
+ if not new or from_object or from_builtins:
+ try:
+ # Use the last definition of __init__.
+ callable_obj = callable_obj.local_attr("__init__")[-1]
+ except exceptions.NotFoundError:
+ # do nothing, covered by no-init.
+ raise ValueError
+ else:
+ callable_obj = new
+
+ if not isinstance(callable_obj, astroid.FunctionDef):
+ raise ValueError
+ # both have an extra implicit 'cls'/'self' argument.
+ return callable_obj, parameters, "constructor"
+
+ raise ValueError
+
+
+def _has_parent_of_type(node, node_type, statement):
+ """Check if the given node has a parent of the given type."""
+ parent = node.parent
+ while not isinstance(parent, node_type) and statement.parent_of(parent):
+ parent = parent.parent
+ return isinstance(parent, node_type)
+
+
+def _no_context_variadic_keywords(node, scope):
+ statement = node.statement()
+ variadics = ()
+
+ if isinstance(scope, astroid.Lambda) and not isinstance(scope, astroid.FunctionDef):
+ variadics = list(node.keywords or []) + node.kwargs
+ else:
+ if isinstance(statement, (astroid.Return, astroid.Expr)) and isinstance(
+ statement.value, astroid.Call
+ ):
+ call = statement.value
+ variadics = list(call.keywords or []) + call.kwargs
+
+ return _no_context_variadic(node, scope.args.kwarg, astroid.Keyword, variadics)
+
+
+def _no_context_variadic_positional(node, scope):
+ variadics = ()
+ if isinstance(scope, astroid.Lambda) and not isinstance(scope, astroid.FunctionDef):
+ variadics = node.starargs + node.kwargs
+ else:
+ statement = node.statement()
+ if isinstance(statement, (astroid.Expr, astroid.Return)) and isinstance(
+ statement.value, astroid.Call
+ ):
+ call = statement.value
+ variadics = call.starargs + call.kwargs
+
+ return _no_context_variadic(node, scope.args.vararg, astroid.Starred, variadics)
+
+
+def _no_context_variadic(node, variadic_name, variadic_type, variadics):
+ """Verify if the given call node has variadic nodes without context
+
+ This is a workaround for handling cases of nested call functions
+ which don't have the specific call context at hand.
+ Variadic arguments (variable positional arguments and variable
+ keyword arguments) are inferred, inherently wrong, by astroid
+ as a Tuple, respectively a Dict with empty elements.
+ This can lead pylint to believe that a function call receives
+ too few arguments.
+ """
+ scope = node.scope()
+ is_in_lambda_scope = not isinstance(scope, astroid.FunctionDef) and isinstance(
+ scope, astroid.Lambda
+ )
+ statement = node.statement()
+ for name in statement.nodes_of_class(astroid.Name):
+ if name.name != variadic_name:
+ continue
+
+ inferred = safe_infer(name)
+ if isinstance(inferred, (astroid.List, astroid.Tuple)):
+ length = len(inferred.elts)
+ elif isinstance(inferred, astroid.Dict):
+ length = len(inferred.items)
+ else:
+ continue
+
+ if is_in_lambda_scope and isinstance(inferred.parent, astroid.Arguments):
+ # The statement of the variadic will be the assignment itself,
+ # so we need to go the lambda instead
+ inferred_statement = inferred.parent.parent
+ else:
+ inferred_statement = inferred.statement()
+
+ if not length and isinstance(inferred_statement, astroid.Lambda):
+ is_in_starred_context = _has_parent_of_type(node, variadic_type, statement)
+ used_as_starred_argument = any(
+ variadic.value == name or variadic.value.parent_of(name)
+ for variadic in variadics
+ )
+ if is_in_starred_context or used_as_starred_argument:
+ return True
+ return False
+
+
+def _is_invalid_metaclass(metaclass):
+ try:
+ mro = metaclass.mro()
+ except NotImplementedError:
+ # Cannot have a metaclass which is not a newstyle class.
+ return True
+ else:
+ if not any(is_builtin_object(cls) and cls.name == "type" for cls in mro):
+ return True
+ return False
+
+
+def _infer_from_metaclass_constructor(cls, func):
+ """Try to infer what the given *func* constructor is building
+
+ :param astroid.FunctionDef func:
+ A metaclass constructor. Metaclass definitions can be
+ functions, which should accept three arguments, the name of
+ the class, the bases of the class and the attributes.
+ The function could return anything, but usually it should
+ be a proper metaclass.
+ :param astroid.ClassDef cls:
+ The class for which the *func* parameter should generate
+ a metaclass.
+ :returns:
+ The class generated by the function or None,
+ if we couldn't infer it.
+ :rtype: astroid.ClassDef
+ """
+ context = astroid.context.InferenceContext()
+
+ class_bases = astroid.List()
+ class_bases.postinit(elts=cls.bases)
+
+ attrs = astroid.Dict()
+ local_names = [(name, values[-1]) for name, values in cls.locals.items()]
+ attrs.postinit(local_names)
+
+ builder_args = astroid.Tuple()
+ builder_args.postinit([cls.name, class_bases, attrs])
+
+ context.callcontext = astroid.context.CallContext(builder_args)
+ try:
+ inferred = next(func.infer_call_result(func, context), None)
+ except astroid.InferenceError:
+ return None
+ return inferred or None
+
+
+def _is_c_extension(module_node):
+ return (
+ not modutils.is_standard_module(module_node.name)
+ and not module_node.fully_defined()
+ )
+
+
+class TypeChecker(BaseChecker):
+ """try to find bugs in the code using type inference
+ """
+
+ __implements__ = (IAstroidChecker,)
+
+ # configuration section name
+ name = "typecheck"
+ # messages
+ msgs = MSGS
+ priority = -1
+ # configuration options
+ options = (
+ (
+ "ignore-on-opaque-inference",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": "This flag controls whether pylint should warn about "
+ "no-member and similar checks whenever an opaque object "
+ "is returned when inferring. The inference can return "
+ "multiple potential results while evaluating a Python object, "
+ "but some branches might not be evaluated, which results in "
+ "partial inference. In that case, it might be useful to still emit "
+ "no-member and other checks for the rest of the inferred objects.",
+ },
+ ),
+ (
+ "ignore-mixin-members",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": 'Tells whether missing members accessed in mixin \
+class should be ignored. A mixin class is detected if its name ends with \
+"mixin" (case insensitive).',
+ },
+ ),
+ (
+ "ignore-none",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": "Tells whether to warn about missing members when the owner "
+ "of the attribute is inferred to be None.",
+ },
+ ),
+ (
+ "ignored-modules",
+ {
+ "default": (),
+ "type": "csv",
+ "metavar": "<module names>",
+ "help": "List of module names for which member attributes "
+ "should not be checked (useful for modules/projects "
+ "where namespaces are manipulated during runtime and "
+ "thus existing member attributes cannot be "
+ "deduced by static analysis). It supports qualified "
+ "module names, as well as Unix pattern matching.",
+ },
+ ),
+ # the defaults here are *stdlib* names that (almost) always
+ # lead to false positives, since their idiomatic use is
+ # 'too dynamic' for pylint to grok.
+ (
+ "ignored-classes",
+ {
+ "default": ("optparse.Values", "thread._local", "_thread._local"),
+ "type": "csv",
+ "metavar": "<members names>",
+ "help": "List of class names for which member attributes "
+ "should not be checked (useful for classes with "
+ "dynamically set attributes). This supports "
+ "the use of qualified names.",
+ },
+ ),
+ (
+ "generated-members",
+ {
+ "default": (),
+ "type": "string",
+ "metavar": "<members names>",
+ "help": "List of members which are set dynamically and \
+missed by pylint inference system, and so shouldn't trigger E1101 when \
+accessed. Python regular expressions are accepted.",
+ },
+ ),
+ (
+ "contextmanager-decorators",
+ {
+ "default": ["contextlib.contextmanager"],
+ "type": "csv",
+ "metavar": "<decorator names>",
+ "help": "List of decorators that produce context managers, "
+ "such as contextlib.contextmanager. Add to this list "
+ "to register other decorators that produce valid "
+ "context managers.",
+ },
+ ),
+ (
+ "missing-member-hint-distance",
+ {
+ "default": 1,
+ "type": "int",
+ "metavar": "<member hint edit distance>",
+ "help": "The minimum edit distance a name should have in order "
+ "to be considered a similar match for a missing member name.",
+ },
+ ),
+ (
+ "missing-member-max-choices",
+ {
+ "default": 1,
+ "type": "int",
+ "metavar": "<member hint max choices>",
+ "help": "The total number of similar names that should be taken in "
+ "consideration when showing a hint for a missing member.",
+ },
+ ),
+ (
+ "missing-member-hint",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<missing member hint>",
+ "help": "Show a hint with possible names when a member name was not "
+ "found. The aspect of finding the hint is based on edit distance.",
+ },
+ ),
+ (
+ "signature-mutators",
+ {
+ "default": [],
+ "type": "csv",
+ "metavar": "<decorator names>",
+ "help": "List of decorators that change the signature of "
+ "a decorated function.",
+ },
+ ),
+ )
+
+ @decorators.cachedproperty
+ def _suggestion_mode(self):
+ return get_global_option(self, "suggestion-mode", default=True)
+
+ def open(self):
+ # do this in open since config not fully initialized in __init__
+ # generated_members may contain regular expressions
+ # (surrounded by quote `"` and followed by a comma `,`)
+ # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' =>
+ # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}')
+ if isinstance(self.config.generated_members, str):
+ gen = shlex.shlex(self.config.generated_members)
+ gen.whitespace += ","
+ gen.wordchars += r"[]-+\.*?()|"
+ self.config.generated_members = tuple(tok.strip('"') for tok in gen)
+
+ @check_messages("keyword-arg-before-vararg")
+ def visit_functiondef(self, node):
+ # check for keyword arg before varargs
+ if node.args.vararg and node.args.defaults:
+ self.add_message("keyword-arg-before-vararg", node=node, args=(node.name))
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ @check_messages("invalid-metaclass")
+ def visit_classdef(self, node):
+ def _metaclass_name(metaclass):
+ if isinstance(metaclass, (astroid.ClassDef, astroid.FunctionDef)):
+ return metaclass.name
+ return metaclass.as_string()
+
+ metaclass = node.declared_metaclass()
+ if not metaclass:
+ return
+
+ if isinstance(metaclass, astroid.FunctionDef):
+ # Try to infer the result.
+ metaclass = _infer_from_metaclass_constructor(node, metaclass)
+ if not metaclass:
+ # Don't do anything if we cannot infer the result.
+ return
+
+ if isinstance(metaclass, astroid.ClassDef):
+ if _is_invalid_metaclass(metaclass):
+ self.add_message(
+ "invalid-metaclass", node=node, args=(_metaclass_name(metaclass),)
+ )
+ else:
+ self.add_message(
+ "invalid-metaclass", node=node, args=(_metaclass_name(metaclass),)
+ )
+
+ def visit_assignattr(self, node):
+ if isinstance(node.assign_type(), astroid.AugAssign):
+ self.visit_attribute(node)
+
+ def visit_delattr(self, node):
+ self.visit_attribute(node)
+
+ @check_messages("no-member", "c-extension-no-member")
+ def visit_attribute(self, node):
+ """check that the accessed attribute exists
+
+ to avoid too much false positives for now, we'll consider the code as
+ correct if a single of the inferred nodes has the accessed attribute.
+
+ function/method, super call and metaclasses are ignored
+ """
+ for pattern in self.config.generated_members:
+ # attribute is marked as generated, stop here
+ if re.match(pattern, node.attrname):
+ return
+ if re.match(pattern, node.as_string()):
+ return
+
+ try:
+ inferred = list(node.expr.infer())
+ except exceptions.InferenceError:
+ return
+
+ # list of (node, nodename) which are missing the attribute
+ missingattr = set()
+
+ non_opaque_inference_results = [
+ owner
+ for owner in inferred
+ if owner is not astroid.Uninferable
+ and not isinstance(owner, astroid.nodes.Unknown)
+ ]
+ if (
+ len(non_opaque_inference_results) != len(inferred)
+ and self.config.ignore_on_opaque_inference
+ ):
+ # There is an ambiguity in the inference. Since we can't
+ # make sure that we won't emit a false positive, we just stop
+ # whenever the inference returns an opaque inference object.
+ return
+ for owner in non_opaque_inference_results:
+ name = getattr(owner, "name", None)
+ if _is_owner_ignored(
+ owner, name, self.config.ignored_classes, self.config.ignored_modules
+ ):
+ continue
+
+ try:
+ if not [
+ n
+ for n in owner.getattr(node.attrname)
+ if not isinstance(n.statement(), astroid.AugAssign)
+ ]:
+ missingattr.add((owner, name))
+ continue
+ except AttributeError:
+ continue
+ except exceptions.NotFoundError:
+ # This can't be moved before the actual .getattr call,
+ # because there can be more values inferred and we are
+ # stopping after the first one which has the attribute in question.
+ # The problem is that if the first one has the attribute,
+ # but we continue to the next values which doesn't have the
+ # attribute, then we'll have a false positive.
+ # So call this only after the call has been made.
+ if not _emit_no_member(
+ node,
+ owner,
+ name,
+ ignored_mixins=self.config.ignore_mixin_members,
+ ignored_none=self.config.ignore_none,
+ ):
+ continue
+ missingattr.add((owner, name))
+ continue
+ # stop on the first found
+ break
+ else:
+ # we have not found any node with the attributes, display the
+ # message for inferred nodes
+ done = set()
+ for owner, name in missingattr:
+ if isinstance(owner, astroid.Instance):
+ actual = owner._proxied
+ else:
+ actual = owner
+ if actual in done:
+ continue
+ done.add(actual)
+
+ msg, hint = self._get_nomember_msgid_hint(node, owner)
+ self.add_message(
+ msg,
+ node=node,
+ args=(owner.display_type(), name, node.attrname, hint),
+ confidence=INFERENCE,
+ )
+
+ def _get_nomember_msgid_hint(self, node, owner):
+ suggestions_are_possible = self._suggestion_mode and isinstance(
+ owner, astroid.Module
+ )
+ if suggestions_are_possible and _is_c_extension(owner):
+ msg = "c-extension-no-member"
+ hint = ""
+ else:
+ msg = "no-member"
+ if self.config.missing_member_hint:
+ hint = _missing_member_hint(
+ owner,
+ node.attrname,
+ self.config.missing_member_hint_distance,
+ self.config.missing_member_max_choices,
+ )
+ else:
+ hint = ""
+ return msg, hint
+
+ @check_messages("assignment-from-no-return", "assignment-from-none")
+ def visit_assign(self, node):
+ """check that if assigning to a function call, the function is
+ possibly returning something valuable
+ """
+ if not isinstance(node.value, astroid.Call):
+ return
+
+ function_node = safe_infer(node.value.func)
+ funcs = (astroid.FunctionDef, astroid.UnboundMethod, astroid.BoundMethod)
+ if not isinstance(function_node, funcs):
+ return
+
+ # Unwrap to get the actual function object
+ if isinstance(function_node, astroid.BoundMethod) and isinstance(
+ function_node._proxied, astroid.UnboundMethod
+ ):
+ function_node = function_node._proxied._proxied
+
+ # Make sure that it's a valid function that we can analyze.
+ # Ordered from less expensive to more expensive checks.
+ # pylint: disable=too-many-boolean-expressions
+ if (
+ not function_node.is_function
+ or isinstance(function_node, astroid.AsyncFunctionDef)
+ or function_node.decorators
+ or function_node.is_generator()
+ or function_node.is_abstract(pass_is_abstract=False)
+ or not function_node.root().fully_defined()
+ ):
+ return
+
+ returns = list(
+ function_node.nodes_of_class(astroid.Return, skip_klass=astroid.FunctionDef)
+ )
+ if not returns:
+ self.add_message("assignment-from-no-return", node=node)
+ else:
+ for rnode in returns:
+ if not (
+ isinstance(rnode.value, astroid.Const)
+ and rnode.value.value is None
+ or rnode.value is None
+ ):
+ break
+ else:
+ self.add_message("assignment-from-none", node=node)
+
+ def _check_uninferable_call(self, node):
+ """
+ Check that the given uninferable Call node does not
+ call an actual function.
+ """
+ if not isinstance(node.func, astroid.Attribute):
+ return
+
+ # Look for properties. First, obtain
+ # the lhs of the Attribute node and search the attribute
+ # there. If that attribute is a property or a subclass of properties,
+ # then most likely it's not callable.
+
+ expr = node.func.expr
+ klass = safe_infer(expr)
+ if (
+ klass is None
+ or klass is astroid.Uninferable
+ or not isinstance(klass, astroid.Instance)
+ ):
+ return
+
+ try:
+ attrs = klass._proxied.getattr(node.func.attrname)
+ except exceptions.NotFoundError:
+ return
+
+ for attr in attrs:
+ if attr is astroid.Uninferable:
+ continue
+ if not isinstance(attr, astroid.FunctionDef):
+ continue
+
+ # Decorated, see if it is decorated with a property.
+ # Also, check the returns and see if they are callable.
+ if decorated_with_property(attr):
+
+ try:
+ all_returns_are_callable = all(
+ return_node.callable() or return_node is astroid.Uninferable
+ for return_node in attr.infer_call_result(node)
+ )
+ except astroid.InferenceError:
+ continue
+
+ if not all_returns_are_callable:
+ self.add_message(
+ "not-callable", node=node, args=node.func.as_string()
+ )
+ break
+
+ def _check_argument_order(self, node, call_site, called, called_param_names):
+ """Match the supplied argument names against the function parameters.
+ Warn if some argument names are not in the same order as they are in
+ the function signature.
+ """
+ # Check for called function being an object instance function
+ # If so, ignore the initial 'self' argument in the signature
+ try:
+ is_classdef = isinstance(called.parent, astroid.scoped_nodes.ClassDef)
+ if is_classdef and called_param_names[0] == "self":
+ called_param_names = called_param_names[1:]
+ except IndexError:
+ return
+
+ try:
+ # extract argument names, if they have names
+ calling_parg_names = [p.name for p in call_site.positional_arguments]
+
+ # Additionally get names of keyword arguments to use in a full match
+ # against parameters
+ calling_kwarg_names = [
+ arg.name for arg in call_site.keyword_arguments.values()
+ ]
+ except AttributeError:
+ # the type of arg does not provide a `.name`. In this case we
+ # stop checking for out-of-order arguments because it is only relevant
+ # for named variables.
+ return
+
+ # Don't check for ordering if there is an unmatched arg or param
+ arg_set = set(calling_parg_names) | set(calling_kwarg_names)
+ param_set = set(called_param_names)
+ if arg_set != param_set:
+ return
+
+ # Warn based on the equality of argument ordering
+ if calling_parg_names != called_param_names[: len(calling_parg_names)]:
+ self.add_message("arguments-out-of-order", node=node, args=())
+
+ # pylint: disable=too-many-branches,too-many-locals
+ @check_messages(*(list(MSGS.keys())))
+ def visit_call(self, node):
+ """check that called functions/methods are inferred to callable objects,
+ and that the arguments passed to the function match the parameters in
+ the inferred function's definition
+ """
+ called = safe_infer(node.func)
+ # only function, generator and object defining __call__ are allowed
+ # Ignore instances of descriptors since astroid cannot properly handle them
+ # yet
+ if called and not called.callable():
+ if isinstance(called, astroid.Instance) and (
+ not has_known_bases(called)
+ or (
+ called.parent is not None
+ and isinstance(called.scope(), astroid.ClassDef)
+ and "__get__" in called.locals
+ )
+ ):
+ # Don't emit if we can't make sure this object is callable.
+ pass
+ else:
+ self.add_message("not-callable", node=node, args=node.func.as_string())
+
+ self._check_uninferable_call(node)
+ try:
+ called, implicit_args, callable_name = _determine_callable(called)
+ except ValueError:
+ # Any error occurred during determining the function type, most of
+ # those errors are handled by different warnings.
+ return
+
+ if called.args.args is None:
+ # Built-in functions have no argument information.
+ return
+
+ if len(called.argnames()) != len(set(called.argnames())):
+ # Duplicate parameter name (see duplicate-argument). We can't really
+ # make sense of the function call in this case, so just return.
+ return
+
+ # Build the set of keyword arguments, checking for duplicate keywords,
+ # and count the positional arguments.
+ call_site = astroid.arguments.CallSite.from_call(node)
+
+ # Warn about duplicated keyword arguments, such as `f=24, **{'f': 24}`
+ for keyword in call_site.duplicated_keywords:
+ self.add_message("repeated-keyword", node=node, args=(keyword,))
+
+ if call_site.has_invalid_arguments() or call_site.has_invalid_keywords():
+ # Can't make sense of this.
+ return
+
+ # Has the function signature changed in ways we cannot reliably detect?
+ if hasattr(called, "decorators") and decorated_with(
+ called, self.config.signature_mutators
+ ):
+ return
+
+ num_positional_args = len(call_site.positional_arguments)
+ keyword_args = list(call_site.keyword_arguments.keys())
+ overload_function = is_overload_stub(called)
+
+ # Determine if we don't have a context for our call and we use variadics.
+ node_scope = node.scope()
+ if isinstance(node_scope, (astroid.Lambda, astroid.FunctionDef)):
+ has_no_context_positional_variadic = _no_context_variadic_positional(
+ node, node_scope
+ )
+ has_no_context_keywords_variadic = _no_context_variadic_keywords(
+ node, node_scope
+ )
+ else:
+ has_no_context_positional_variadic = (
+ has_no_context_keywords_variadic
+ ) = False
+
+ # These are coming from the functools.partial implementation in astroid
+ already_filled_positionals = getattr(called, "filled_positionals", 0)
+ already_filled_keywords = getattr(called, "filled_keywords", {})
+
+ keyword_args += list(already_filled_keywords)
+ num_positional_args += implicit_args + already_filled_positionals
+
+ # Analyze the list of formal parameters.
+ args = list(itertools.chain(called.args.posonlyargs or (), called.args.args))
+ num_mandatory_parameters = len(args) - len(called.args.defaults)
+ parameters = []
+ parameter_name_to_index = {}
+ for i, arg in enumerate(args):
+ if isinstance(arg, astroid.Tuple):
+ name = None
+ # Don't store any parameter names within the tuple, since those
+ # are not assignable from keyword arguments.
+ else:
+ assert isinstance(arg, astroid.AssignName)
+ # This occurs with:
+ # def f( (a), (b) ): pass
+ name = arg.name
+ parameter_name_to_index[name] = i
+ if i >= num_mandatory_parameters:
+ defval = called.args.defaults[i - num_mandatory_parameters]
+ else:
+ defval = None
+ parameters.append([(name, defval), False])
+
+ kwparams = {}
+ for i, arg in enumerate(called.args.kwonlyargs):
+ if isinstance(arg, astroid.Keyword):
+ name = arg.arg
+ else:
+ assert isinstance(arg, astroid.AssignName)
+ name = arg.name
+ kwparams[name] = [called.args.kw_defaults[i], False]
+
+ self._check_argument_order(
+ node, call_site, called, [p[0][0] for p in parameters]
+ )
+
+ # 1. Match the positional arguments.
+ for i in range(num_positional_args):
+ if i < len(parameters):
+ parameters[i][1] = True
+ elif called.args.vararg is not None:
+ # The remaining positional arguments get assigned to the *args
+ # parameter.
+ break
+ else:
+ if not overload_function:
+ # Too many positional arguments.
+ self.add_message(
+ "too-many-function-args", node=node, args=(callable_name,)
+ )
+ break
+
+ # 2. Match the keyword arguments.
+ for keyword in keyword_args:
+ if keyword in parameter_name_to_index:
+ i = parameter_name_to_index[keyword]
+ if parameters[i][1]:
+ # Duplicate definition of function parameter.
+
+ # Might be too hardcoded, but this can actually
+ # happen when using str.format and `self` is passed
+ # by keyword argument, as in `.format(self=self)`.
+ # It's perfectly valid to so, so we're just skipping
+ # it if that's the case.
+ if not (keyword == "self" and called.qname() in STR_FORMAT):
+ self.add_message(
+ "redundant-keyword-arg",
+ node=node,
+ args=(keyword, callable_name),
+ )
+ else:
+ parameters[i][1] = True
+ elif keyword in kwparams:
+ if kwparams[keyword][1]:
+ # Duplicate definition of function parameter.
+ self.add_message(
+ "redundant-keyword-arg",
+ node=node,
+ args=(keyword, callable_name),
+ )
+ else:
+ kwparams[keyword][1] = True
+ elif called.args.kwarg is not None:
+ # The keyword argument gets assigned to the **kwargs parameter.
+ pass
+ elif not overload_function:
+ # Unexpected keyword argument.
+ self.add_message(
+ "unexpected-keyword-arg", node=node, args=(keyword, callable_name)
+ )
+
+ # 3. Match the **kwargs, if any.
+ if node.kwargs:
+ for i, [(name, defval), assigned] in enumerate(parameters):
+ # Assume that *kwargs provides values for all remaining
+ # unassigned named parameters.
+ if name is not None:
+ parameters[i][1] = True
+ else:
+ # **kwargs can't assign to tuples.
+ pass
+
+ # Check that any parameters without a default have been assigned
+ # values.
+ for [(name, defval), assigned] in parameters:
+ if (defval is None) and not assigned:
+ if name is None:
+ display_name = "<tuple>"
+ else:
+ display_name = repr(name)
+ if not has_no_context_positional_variadic and not overload_function:
+ self.add_message(
+ "no-value-for-parameter",
+ node=node,
+ args=(display_name, callable_name),
+ )
+
+ for name in kwparams:
+ defval, assigned = kwparams[name]
+ if defval is None and not assigned and not has_no_context_keywords_variadic:
+ self.add_message("missing-kwoa", node=node, args=(name, callable_name))
+
+ @check_messages("invalid-sequence-index")
+ def visit_extslice(self, node):
+ # Check extended slice objects as if they were used as a sequence
+ # index to check if the object being sliced can support them
+ return self.visit_index(node)
+
+ @check_messages("invalid-sequence-index")
+ def visit_index(self, node):
+ if not node.parent or not hasattr(node.parent, "value"):
+ return None
+ # Look for index operations where the parent is a sequence type.
+ # If the types can be determined, only allow indices to be int,
+ # slice or instances with __index__.
+ parent_type = safe_infer(node.parent.value)
+ if not isinstance(
+ parent_type, (astroid.ClassDef, astroid.Instance)
+ ) or not has_known_bases(parent_type):
+ return None
+
+ # Determine what method on the parent this index will use
+ # The parent of this node will be a Subscript, and the parent of that
+ # node determines if the Subscript is a get, set, or delete operation.
+ if node.parent.ctx is astroid.Store:
+ methodname = "__setitem__"
+ elif node.parent.ctx is astroid.Del:
+ methodname = "__delitem__"
+ else:
+ methodname = "__getitem__"
+
+ # Check if this instance's __getitem__, __setitem__, or __delitem__, as
+ # appropriate to the statement, is implemented in a builtin sequence
+ # type. This way we catch subclasses of sequence types but skip classes
+ # that override __getitem__ and which may allow non-integer indices.
+ try:
+ methods = dunder_lookup.lookup(parent_type, methodname)
+ if methods is astroid.Uninferable:
+ return None
+ itemmethod = methods[0]
+ except (
+ exceptions.NotFoundError,
+ exceptions.AttributeInferenceError,
+ IndexError,
+ ):
+ return None
+
+ if (
+ not isinstance(itemmethod, astroid.FunctionDef)
+ or itemmethod.root().name != BUILTINS
+ or not itemmethod.parent
+ or itemmethod.parent.name not in SEQUENCE_TYPES
+ ):
+ return None
+
+ # For ExtSlice objects coming from visit_extslice, no further
+ # inference is necessary, since if we got this far the ExtSlice
+ # is an error.
+ if isinstance(node, astroid.ExtSlice):
+ index_type = node
+ else:
+ index_type = safe_infer(node)
+ if index_type is None or index_type is astroid.Uninferable:
+ return None
+ # Constants must be of type int
+ if isinstance(index_type, astroid.Const):
+ if isinstance(index_type.value, int):
+ return None
+ # Instance values must be int, slice, or have an __index__ method
+ elif isinstance(index_type, astroid.Instance):
+ if index_type.pytype() in (BUILTINS + ".int", BUILTINS + ".slice"):
+ return None
+ try:
+ index_type.getattr("__index__")
+ return None
+ except exceptions.NotFoundError:
+ pass
+ elif isinstance(index_type, astroid.Slice):
+ # Delegate to visit_slice. A slice can be present
+ # here after inferring the index node, which could
+ # be a `slice(...)` call for instance.
+ return self.visit_slice(index_type)
+
+ # Anything else is an error
+ self.add_message("invalid-sequence-index", node=node)
+ return None
+
+ @check_messages("invalid-slice-index")
+ def visit_slice(self, node):
+ # Check the type of each part of the slice
+ invalid_slices = 0
+ for index in (node.lower, node.upper, node.step):
+ if index is None:
+ continue
+
+ index_type = safe_infer(index)
+ if index_type is None or index_type is astroid.Uninferable:
+ continue
+
+ # Constants must of type int or None
+ if isinstance(index_type, astroid.Const):
+ if isinstance(index_type.value, (int, type(None))):
+ continue
+ # Instance values must be of type int, None or an object
+ # with __index__
+ elif isinstance(index_type, astroid.Instance):
+ if index_type.pytype() in (BUILTINS + ".int", BUILTINS + ".NoneType"):
+ continue
+
+ try:
+ index_type.getattr("__index__")
+ return
+ except exceptions.NotFoundError:
+ pass
+ invalid_slices += 1
+
+ if not invalid_slices:
+ return
+
+ # Anything else is an error, unless the object that is indexed
+ # is a custom object, which knows how to handle this kind of slices
+ parent = node.parent
+ if isinstance(parent, astroid.ExtSlice):
+ parent = parent.parent
+ if isinstance(parent, astroid.Subscript):
+ inferred = safe_infer(parent.value)
+ if inferred is None or inferred is astroid.Uninferable:
+ # Don't know what this is
+ return
+ known_objects = (
+ astroid.List,
+ astroid.Dict,
+ astroid.Tuple,
+ astroid.objects.FrozenSet,
+ astroid.Set,
+ )
+ if not isinstance(inferred, known_objects):
+ # Might be an instance that knows how to handle this slice object
+ return
+ for _ in range(invalid_slices):
+ self.add_message("invalid-slice-index", node=node)
+
+ @check_messages("not-context-manager")
+ def visit_with(self, node):
+ for ctx_mgr, _ in node.items:
+ context = astroid.context.InferenceContext()
+ inferred = safe_infer(ctx_mgr, context=context)
+ if inferred is None or inferred is astroid.Uninferable:
+ continue
+
+ if isinstance(inferred, bases.Generator):
+ # Check if we are dealing with a function decorated
+ # with contextlib.contextmanager.
+ if decorated_with(
+ inferred.parent, self.config.contextmanager_decorators
+ ):
+ continue
+ # If the parent of the generator is not the context manager itself,
+ # that means that it could have been returned from another
+ # function which was the real context manager.
+ # The following approach is more of a hack rather than a real
+ # solution: walk all the inferred statements for the
+ # given *ctx_mgr* and if you find one function scope
+ # which is decorated, consider it to be the real
+ # manager and give up, otherwise emit not-context-manager.
+ # See the test file for not_context_manager for a couple
+ # of self explaining tests.
+
+ # Retrieve node from all previusly visited nodes in the the inference history
+ context_path_names = filter(None, _unflatten(context.path))
+ inferred_paths = _flatten_container(
+ safe_infer(path) for path in context_path_names
+ )
+ for inferred_path in inferred_paths:
+ if not inferred_path:
+ continue
+ scope = inferred_path.scope()
+ if not isinstance(scope, astroid.FunctionDef):
+ continue
+ if decorated_with(scope, self.config.contextmanager_decorators):
+ break
+ else:
+ self.add_message(
+ "not-context-manager", node=node, args=(inferred.name,)
+ )
+ else:
+ try:
+ inferred.getattr("__enter__")
+ inferred.getattr("__exit__")
+ except exceptions.NotFoundError:
+ if isinstance(inferred, astroid.Instance):
+ # If we do not know the bases of this class,
+ # just skip it.
+ if not has_known_bases(inferred):
+ continue
+ # Just ignore mixin classes.
+ if self.config.ignore_mixin_members:
+ if inferred.name[-5:].lower() == "mixin":
+ continue
+
+ self.add_message(
+ "not-context-manager", node=node, args=(inferred.name,)
+ )
+
+ @check_messages("invalid-unary-operand-type")
+ def visit_unaryop(self, node):
+ """Detect TypeErrors for unary operands."""
+
+ for error in node.type_errors():
+ # Let the error customize its output.
+ self.add_message("invalid-unary-operand-type", args=str(error), node=node)
+
+ @check_messages("unsupported-binary-operation")
+ def _visit_binop(self, node):
+ """Detect TypeErrors for binary arithmetic operands."""
+ self._check_binop_errors(node)
+
+ @check_messages("unsupported-binary-operation")
+ def _visit_augassign(self, node):
+ """Detect TypeErrors for augmented binary arithmetic operands."""
+ self._check_binop_errors(node)
+
+ def _check_binop_errors(self, node):
+ for error in node.type_errors():
+ # Let the error customize its output.
+ if any(
+ isinstance(obj, astroid.ClassDef) and not has_known_bases(obj)
+ for obj in (error.left_type, error.right_type)
+ ):
+ continue
+ self.add_message("unsupported-binary-operation", args=str(error), node=node)
+
+ def _check_membership_test(self, node):
+ if is_inside_abstract_class(node):
+ return
+ if is_comprehension(node):
+ return
+ inferred = safe_infer(node)
+ if inferred is None or inferred is astroid.Uninferable:
+ return
+ if not supports_membership_test(inferred):
+ self.add_message(
+ "unsupported-membership-test", args=node.as_string(), node=node
+ )
+
+ @check_messages("unsupported-membership-test")
+ def visit_compare(self, node):
+ if len(node.ops) != 1:
+ return
+
+ op, right = node.ops[0]
+ if op in ["in", "not in"]:
+ self._check_membership_test(right)
+
+ @check_messages(
+ "unsubscriptable-object",
+ "unsupported-assignment-operation",
+ "unsupported-delete-operation",
+ "unhashable-dict-key",
+ )
+ def visit_subscript(self, node):
+ supported_protocol = None
+ if isinstance(node.value, (astroid.ListComp, astroid.DictComp)):
+ return
+
+ if isinstance(node.value, astroid.Dict):
+ # Assert dict key is hashable
+ inferred = safe_infer(node.slice.value)
+ if inferred not in (None, astroid.Uninferable):
+ try:
+ hash_fn = next(inferred.igetattr("__hash__"))
+ except astroid.InferenceError:
+ pass
+ else:
+ if getattr(hash_fn, "value", True) is None:
+ self.add_message("unhashable-dict-key", node=node.value)
+
+ if node.ctx == astroid.Load:
+ supported_protocol = supports_getitem
+ msg = "unsubscriptable-object"
+ elif node.ctx == astroid.Store:
+ supported_protocol = supports_setitem
+ msg = "unsupported-assignment-operation"
+ elif node.ctx == astroid.Del:
+ supported_protocol = supports_delitem
+ msg = "unsupported-delete-operation"
+
+ if isinstance(node.value, astroid.SetComp):
+ self.add_message(msg, args=node.value.as_string(), node=node.value)
+ return
+
+ if is_inside_abstract_class(node):
+ return
+
+ inferred = safe_infer(node.value)
+ if inferred is None or inferred is astroid.Uninferable:
+ return
+
+ if not supported_protocol(inferred):
+ self.add_message(msg, args=node.value.as_string(), node=node.value)
+
+ @check_messages("dict-items-missing-iter")
+ def visit_for(self, node):
+ if not isinstance(node.target, astroid.node_classes.Tuple):
+ # target is not a tuple
+ return
+ if not len(node.target.elts) == 2:
+ # target is not a tuple of two elements
+ return
+
+ iterable = node.iter
+ if not isinstance(iterable, astroid.node_classes.Name):
+ # it's not a bare variable
+ return
+
+ inferred = safe_infer(iterable)
+ if not inferred:
+ return
+ if not isinstance(inferred, astroid.node_classes.Dict):
+ # the iterable is not a dict
+ return
+
+ self.add_message("dict-iter-missing-items", node=node)
+
+
+class IterableChecker(BaseChecker):
+ """
+ Checks for non-iterables used in an iterable context.
+ Contexts include:
+ - for-statement
+ - starargs in function call
+ - `yield from`-statement
+ - list, dict and set comprehensions
+ - generator expressions
+ Also checks for non-mappings in function call kwargs.
+ """
+
+ __implements__ = (IAstroidChecker,)
+ name = "typecheck"
+
+ msgs = {
+ "E1133": (
+ "Non-iterable value %s is used in an iterating context",
+ "not-an-iterable",
+ "Used when a non-iterable value is used in place where "
+ "iterable is expected",
+ ),
+ "E1134": (
+ "Non-mapping value %s is used in a mapping context",
+ "not-a-mapping",
+ "Used when a non-mapping value is used in place where "
+ "mapping is expected",
+ ),
+ }
+
+ @staticmethod
+ def _is_asyncio_coroutine(node):
+ if not isinstance(node, astroid.Call):
+ return False
+
+ inferred_func = safe_infer(node.func)
+ if not isinstance(inferred_func, astroid.FunctionDef):
+ return False
+ if not inferred_func.decorators:
+ return False
+ for decorator in inferred_func.decorators.nodes:
+ inferred_decorator = safe_infer(decorator)
+ if not isinstance(inferred_decorator, astroid.FunctionDef):
+ continue
+ if inferred_decorator.qname() != ASYNCIO_COROUTINE:
+ continue
+ return True
+ return False
+
+ def _check_iterable(self, node, check_async=False):
+ if is_inside_abstract_class(node) or is_comprehension(node):
+ return
+ inferred = safe_infer(node)
+ if not inferred:
+ return
+ if not is_iterable(inferred, check_async=check_async):
+ self.add_message("not-an-iterable", args=node.as_string(), node=node)
+
+ def _check_mapping(self, node):
+ if is_inside_abstract_class(node):
+ return
+ if isinstance(node, astroid.DictComp):
+ return
+ inferred = safe_infer(node)
+ if inferred is None or inferred is astroid.Uninferable:
+ return
+ if not is_mapping(inferred):
+ self.add_message("not-a-mapping", args=node.as_string(), node=node)
+
+ @check_messages("not-an-iterable")
+ def visit_for(self, node):
+ self._check_iterable(node.iter)
+
+ @check_messages("not-an-iterable")
+ def visit_asyncfor(self, node):
+ self._check_iterable(node.iter, check_async=True)
+
+ @check_messages("not-an-iterable")
+ def visit_yieldfrom(self, node):
+ if self._is_asyncio_coroutine(node.value):
+ return
+ self._check_iterable(node.value)
+
+ @check_messages("not-an-iterable", "not-a-mapping")
+ def visit_call(self, node):
+ for stararg in node.starargs:
+ self._check_iterable(stararg.value)
+ for kwarg in node.kwargs:
+ self._check_mapping(kwarg.value)
+
+ @check_messages("not-an-iterable")
+ def visit_listcomp(self, node):
+ for gen in node.generators:
+ self._check_iterable(gen.iter, check_async=gen.is_async)
+
+ @check_messages("not-an-iterable")
+ def visit_dictcomp(self, node):
+ for gen in node.generators:
+ self._check_iterable(gen.iter, check_async=gen.is_async)
+
+ @check_messages("not-an-iterable")
+ def visit_setcomp(self, node):
+ for gen in node.generators:
+ self._check_iterable(gen.iter, check_async=gen.is_async)
+
+ @check_messages("not-an-iterable")
+ def visit_generatorexp(self, node):
+ for gen in node.generators:
+ self._check_iterable(gen.iter, check_async=gen.is_async)
+
+
+def register(linter):
+ """required method to auto register this checker """
+ linter.register_checker(TypeChecker(linter))
+ linter.register_checker(IterableChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/checkers/utils.py b/venv/Lib/site-packages/pylint/checkers/utils.py
new file mode 100644
index 0000000..2a6820a
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/utils.py
@@ -0,0 +1,1253 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2007, 2009-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
+# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Ricardo Gemignani <ricardo.gemignani@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016, 2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016-2017 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2016 Brian C. Lane <bcl@redhat.com>
+# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 ttenhoeve-aa <ttenhoeve@appannie.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.guinta@protonmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+# Copyright (c) 2018 Brian Shaginaw <brian.shaginaw@warbyparker.com>
+# Copyright (c) 2018 Caio Carrara <ccarrara@redhat.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""some functions that may be useful for various checkers
+"""
+import builtins
+import itertools
+import numbers
+import re
+import string
+from functools import lru_cache, partial
+from typing import Callable, Dict, Iterable, List, Match, Optional, Set, Tuple, Union
+
+import astroid
+from astroid import bases as _bases
+from astroid import helpers, scoped_nodes
+from astroid.exceptions import _NonDeducibleTypeHierarchy
+
+import _string # pylint: disable=wrong-import-position, wrong-import-order
+
+BUILTINS_NAME = builtins.__name__
+COMP_NODE_TYPES = (
+ astroid.ListComp,
+ astroid.SetComp,
+ astroid.DictComp,
+ astroid.GeneratorExp,
+)
+EXCEPTIONS_MODULE = "builtins"
+ABC_METHODS = {
+ "abc.abstractproperty",
+ "abc.abstractmethod",
+ "abc.abstractclassmethod",
+ "abc.abstractstaticmethod",
+}
+TYPING_PROTOCOLS = frozenset({"typing.Protocol", "typing_extensions.Protocol"})
+ITER_METHOD = "__iter__"
+AITER_METHOD = "__aiter__"
+NEXT_METHOD = "__next__"
+GETITEM_METHOD = "__getitem__"
+CLASS_GETITEM_METHOD = "__class_getitem__"
+SETITEM_METHOD = "__setitem__"
+DELITEM_METHOD = "__delitem__"
+CONTAINS_METHOD = "__contains__"
+KEYS_METHOD = "keys"
+
+# Dictionary which maps the number of expected parameters a
+# special method can have to a set of special methods.
+# The following keys are used to denote the parameters restrictions:
+#
+# * None: variable number of parameters
+# * number: exactly that number of parameters
+# * tuple: this are the odd ones. Basically it means that the function
+# can work with any number of arguments from that tuple,
+# although it's best to implement it in order to accept
+# all of them.
+_SPECIAL_METHODS_PARAMS = {
+ None: ("__new__", "__init__", "__call__"),
+ 0: (
+ "__del__",
+ "__repr__",
+ "__str__",
+ "__bytes__",
+ "__hash__",
+ "__bool__",
+ "__dir__",
+ "__len__",
+ "__length_hint__",
+ "__iter__",
+ "__reversed__",
+ "__neg__",
+ "__pos__",
+ "__abs__",
+ "__invert__",
+ "__complex__",
+ "__int__",
+ "__float__",
+ "__neg__",
+ "__pos__",
+ "__abs__",
+ "__complex__",
+ "__int__",
+ "__float__",
+ "__index__",
+ "__enter__",
+ "__aenter__",
+ "__getnewargs_ex__",
+ "__getnewargs__",
+ "__getstate__",
+ "__reduce__",
+ "__copy__",
+ "__unicode__",
+ "__nonzero__",
+ "__await__",
+ "__aiter__",
+ "__anext__",
+ "__fspath__",
+ ),
+ 1: (
+ "__format__",
+ "__lt__",
+ "__le__",
+ "__eq__",
+ "__ne__",
+ "__gt__",
+ "__ge__",
+ "__getattr__",
+ "__getattribute__",
+ "__delattr__",
+ "__delete__",
+ "__instancecheck__",
+ "__subclasscheck__",
+ "__getitem__",
+ "__missing__",
+ "__delitem__",
+ "__contains__",
+ "__add__",
+ "__sub__",
+ "__mul__",
+ "__truediv__",
+ "__floordiv__",
+ "__rfloordiv__",
+ "__mod__",
+ "__divmod__",
+ "__lshift__",
+ "__rshift__",
+ "__and__",
+ "__xor__",
+ "__or__",
+ "__radd__",
+ "__rsub__",
+ "__rmul__",
+ "__rtruediv__",
+ "__rmod__",
+ "__rdivmod__",
+ "__rpow__",
+ "__rlshift__",
+ "__rrshift__",
+ "__rand__",
+ "__rxor__",
+ "__ror__",
+ "__iadd__",
+ "__isub__",
+ "__imul__",
+ "__itruediv__",
+ "__ifloordiv__",
+ "__imod__",
+ "__ilshift__",
+ "__irshift__",
+ "__iand__",
+ "__ixor__",
+ "__ior__",
+ "__ipow__",
+ "__setstate__",
+ "__reduce_ex__",
+ "__deepcopy__",
+ "__cmp__",
+ "__matmul__",
+ "__rmatmul__",
+ "__div__",
+ ),
+ 2: ("__setattr__", "__get__", "__set__", "__setitem__", "__set_name__"),
+ 3: ("__exit__", "__aexit__"),
+ (0, 1): ("__round__",),
+}
+
+SPECIAL_METHODS_PARAMS = {
+ name: params
+ for params, methods in _SPECIAL_METHODS_PARAMS.items()
+ for name in methods # type: ignore
+}
+PYMETHODS = set(SPECIAL_METHODS_PARAMS)
+
+
+class NoSuchArgumentError(Exception):
+ pass
+
+
+def is_inside_except(node):
+ """Returns true if node is inside the name of an except handler."""
+ current = node
+ while current and not isinstance(current.parent, astroid.ExceptHandler):
+ current = current.parent
+
+ return current and current is current.parent.name
+
+
+def is_inside_lambda(node: astroid.node_classes.NodeNG) -> bool:
+ """Return true if given node is inside lambda"""
+ parent = node.parent
+ while parent is not None:
+ if isinstance(parent, astroid.Lambda):
+ return True
+ parent = parent.parent
+ return False
+
+
+def get_all_elements(
+ node: astroid.node_classes.NodeNG
+) -> Iterable[astroid.node_classes.NodeNG]:
+ """Recursively returns all atoms in nested lists and tuples."""
+ if isinstance(node, (astroid.Tuple, astroid.List)):
+ for child in node.elts:
+ yield from get_all_elements(child)
+ else:
+ yield node
+
+
+def clobber_in_except(
+ node: astroid.node_classes.NodeNG
+) -> Tuple[bool, Optional[Tuple[str, str]]]:
+ """Checks if an assignment node in an except handler clobbers an existing
+ variable.
+
+ Returns (True, args for W0623) if assignment clobbers an existing variable,
+ (False, None) otherwise.
+ """
+ if isinstance(node, astroid.AssignAttr):
+ return True, (node.attrname, "object %r" % (node.expr.as_string(),))
+ if isinstance(node, astroid.AssignName):
+ name = node.name
+ if is_builtin(name):
+ return True, (name, "builtins")
+
+ stmts = node.lookup(name)[1]
+ if stmts and not isinstance(
+ stmts[0].assign_type(),
+ (astroid.Assign, astroid.AugAssign, astroid.ExceptHandler),
+ ):
+ return True, (name, "outer scope (line %s)" % stmts[0].fromlineno)
+ return False, None
+
+
+def is_super(node: astroid.node_classes.NodeNG) -> bool:
+ """return True if the node is referencing the "super" builtin function
+ """
+ if getattr(node, "name", None) == "super" and node.root().name == BUILTINS_NAME:
+ return True
+ return False
+
+
+def is_error(node: astroid.node_classes.NodeNG) -> bool:
+ """return true if the function does nothing but raising an exception"""
+ raises = False
+ returns = False
+ for child_node in node.nodes_of_class((astroid.Raise, astroid.Return)):
+ if isinstance(child_node, astroid.Raise):
+ raises = True
+ if isinstance(child_node, astroid.Return):
+ returns = True
+ return raises and not returns
+
+
+builtins = builtins.__dict__.copy() # type: ignore
+SPECIAL_BUILTINS = ("__builtins__",) # '__path__', '__file__')
+
+
+def is_builtin_object(node: astroid.node_classes.NodeNG) -> bool:
+ """Returns True if the given node is an object from the __builtin__ module."""
+ return node and node.root().name == BUILTINS_NAME
+
+
+def is_builtin(name: str) -> bool:
+ """return true if <name> could be considered as a builtin defined by python
+ """
+ return name in builtins or name in SPECIAL_BUILTINS # type: ignore
+
+
+def is_defined_in_scope(
+ var_node: astroid.node_classes.NodeNG,
+ varname: str,
+ scope: astroid.node_classes.NodeNG,
+) -> bool:
+ if isinstance(scope, astroid.If):
+ for node in scope.body:
+ if (
+ isinstance(node, astroid.Assign)
+ and any(
+ isinstance(target, astroid.AssignName) and target.name == varname
+ for target in node.targets
+ )
+ ) or (isinstance(node, astroid.Nonlocal) and varname in node.names):
+ return True
+ elif isinstance(scope, (COMP_NODE_TYPES, astroid.For)):
+ for ass_node in scope.nodes_of_class(astroid.AssignName):
+ if ass_node.name == varname:
+ return True
+ elif isinstance(scope, astroid.With):
+ for expr, ids in scope.items:
+ if expr.parent_of(var_node):
+ break
+ if ids and isinstance(ids, astroid.AssignName) and ids.name == varname:
+ return True
+ elif isinstance(scope, (astroid.Lambda, astroid.FunctionDef)):
+ if scope.args.is_argument(varname):
+ # If the name is found inside a default value
+ # of a function, then let the search continue
+ # in the parent's tree.
+ if scope.args.parent_of(var_node):
+ try:
+ scope.args.default_value(varname)
+ scope = scope.parent
+ is_defined_in_scope(var_node, varname, scope)
+ except astroid.NoDefault:
+ pass
+ return True
+ if getattr(scope, "name", None) == varname:
+ return True
+ elif isinstance(scope, astroid.ExceptHandler):
+ if isinstance(scope.name, astroid.AssignName):
+ ass_node = scope.name
+ if ass_node.name == varname:
+ return True
+ return False
+
+
+def is_defined_before(var_node: astroid.node_classes.NodeNG) -> bool:
+ """return True if the variable node is defined by a parent node (list,
+ set, dict, or generator comprehension, lambda) or in a previous sibling
+ node on the same line (statement_defining ; statement_using)
+ """
+ varname = var_node.name
+ _node = var_node.parent
+ while _node:
+ if is_defined_in_scope(var_node, varname, _node):
+ return True
+ _node = _node.parent
+ # possibly multiple statements on the same line using semi colon separator
+ stmt = var_node.statement()
+ _node = stmt.previous_sibling()
+ lineno = stmt.fromlineno
+ while _node and _node.fromlineno == lineno:
+ for assign_node in _node.nodes_of_class(astroid.AssignName):
+ if assign_node.name == varname:
+ return True
+ for imp_node in _node.nodes_of_class((astroid.ImportFrom, astroid.Import)):
+ if varname in [name[1] or name[0] for name in imp_node.names]:
+ return True
+ _node = _node.previous_sibling()
+ return False
+
+
+def is_default_argument(node: astroid.node_classes.NodeNG) -> bool:
+ """return true if the given Name node is used in function or lambda
+ default argument's value
+ """
+ parent = node.scope()
+ if isinstance(parent, (astroid.FunctionDef, astroid.Lambda)):
+ for default_node in parent.args.defaults:
+ for default_name_node in default_node.nodes_of_class(astroid.Name):
+ if default_name_node is node:
+ return True
+ return False
+
+
+def is_func_decorator(node: astroid.node_classes.NodeNG) -> bool:
+ """return true if the name is used in function decorator"""
+ parent = node.parent
+ while parent is not None:
+ if isinstance(parent, astroid.Decorators):
+ return True
+ if parent.is_statement or isinstance(
+ parent,
+ (astroid.Lambda, scoped_nodes.ComprehensionScope, scoped_nodes.ListComp),
+ ):
+ break
+ parent = parent.parent
+ return False
+
+
+def is_ancestor_name(
+ frame: astroid.node_classes.NodeNG, node: astroid.node_classes.NodeNG
+) -> bool:
+ """return True if `frame` is an astroid.Class node with `node` in the
+ subtree of its bases attribute
+ """
+ try:
+ bases = frame.bases
+ except AttributeError:
+ return False
+ for base in bases:
+ if node in base.nodes_of_class(astroid.Name):
+ return True
+ return False
+
+
+def assign_parent(node: astroid.node_classes.NodeNG) -> astroid.node_classes.NodeNG:
+ """return the higher parent which is not an AssignName, Tuple or List node
+ """
+ while node and isinstance(node, (astroid.AssignName, astroid.Tuple, astroid.List)):
+ node = node.parent
+ return node
+
+
+def overrides_a_method(class_node: astroid.node_classes.NodeNG, name: str) -> bool:
+ """return True if <name> is a method overridden from an ancestor"""
+ for ancestor in class_node.ancestors():
+ if name in ancestor and isinstance(ancestor[name], astroid.FunctionDef):
+ return True
+ return False
+
+
+def check_messages(*messages: str) -> Callable:
+ """decorator to store messages that are handled by a checker method"""
+
+ def store_messages(func):
+ func.checks_msgs = messages
+ return func
+
+ return store_messages
+
+
+class IncompleteFormatString(Exception):
+ """A format string ended in the middle of a format specifier."""
+
+
+class UnsupportedFormatCharacter(Exception):
+ """A format character in a format string is not one of the supported
+ format characters."""
+
+ def __init__(self, index):
+ Exception.__init__(self, index)
+ self.index = index
+
+
+def parse_format_string(
+ format_string: str
+) -> Tuple[Set[str], int, Dict[str, str], List[str]]:
+ """Parses a format string, returning a tuple of (keys, num_args), where keys
+ is the set of mapping keys in the format string, and num_args is the number
+ of arguments required by the format string. Raises
+ IncompleteFormatString or UnsupportedFormatCharacter if a
+ parse error occurs."""
+ keys = set()
+ key_types = dict()
+ pos_types = []
+ num_args = 0
+
+ def next_char(i):
+ i += 1
+ if i == len(format_string):
+ raise IncompleteFormatString
+ return (i, format_string[i])
+
+ i = 0
+ while i < len(format_string):
+ char = format_string[i]
+ if char == "%":
+ i, char = next_char(i)
+ # Parse the mapping key (optional).
+ key = None
+ if char == "(":
+ depth = 1
+ i, char = next_char(i)
+ key_start = i
+ while depth != 0:
+ if char == "(":
+ depth += 1
+ elif char == ")":
+ depth -= 1
+ i, char = next_char(i)
+ key_end = i - 1
+ key = format_string[key_start:key_end]
+
+ # Parse the conversion flags (optional).
+ while char in "#0- +":
+ i, char = next_char(i)
+ # Parse the minimum field width (optional).
+ if char == "*":
+ num_args += 1
+ i, char = next_char(i)
+ else:
+ while char in string.digits:
+ i, char = next_char(i)
+ # Parse the precision (optional).
+ if char == ".":
+ i, char = next_char(i)
+ if char == "*":
+ num_args += 1
+ i, char = next_char(i)
+ else:
+ while char in string.digits:
+ i, char = next_char(i)
+ # Parse the length modifier (optional).
+ if char in "hlL":
+ i, char = next_char(i)
+ # Parse the conversion type (mandatory).
+ flags = "diouxXeEfFgGcrs%a"
+ if char not in flags:
+ raise UnsupportedFormatCharacter(i)
+ if key:
+ keys.add(key)
+ key_types[key] = char
+ elif char != "%":
+ num_args += 1
+ pos_types.append(char)
+ i += 1
+ return keys, num_args, key_types, pos_types
+
+
+def split_format_field_names(format_string) -> Tuple[str, Iterable[Tuple[bool, str]]]:
+ try:
+ return _string.formatter_field_name_split(format_string)
+ except ValueError:
+ raise IncompleteFormatString()
+
+
+def collect_string_fields(format_string) -> Iterable[Optional[str]]:
+ """ Given a format string, return an iterator
+ of all the valid format fields. It handles nested fields
+ as well.
+ """
+ formatter = string.Formatter()
+ try:
+ parseiterator = formatter.parse(format_string)
+ for result in parseiterator:
+ if all(item is None for item in result[1:]):
+ # not a replacement format
+ continue
+ name = result[1]
+ nested = result[2]
+ yield name
+ if nested:
+ for field in collect_string_fields(nested):
+ yield field
+ except ValueError as exc:
+ # Probably the format string is invalid.
+ if exc.args[0].startswith("cannot switch from manual"):
+ # On Jython, parsing a string with both manual
+ # and automatic positions will fail with a ValueError,
+ # while on CPython it will simply return the fields,
+ # the validation being done in the interpreter (?).
+ # We're just returning two mixed fields in order
+ # to trigger the format-combined-specification check.
+ yield ""
+ yield "1"
+ return
+ raise IncompleteFormatString(format_string)
+
+
+def parse_format_method_string(
+ format_string: str
+) -> Tuple[List[Tuple[str, List[Tuple[bool, str]]]], int, int]:
+ """
+ Parses a PEP 3101 format string, returning a tuple of
+ (keyword_arguments, implicit_pos_args_cnt, explicit_pos_args),
+ where keyword_arguments is the set of mapping keys in the format string, implicit_pos_args_cnt
+ is the number of arguments required by the format string and
+ explicit_pos_args is the number of arguments passed with the position.
+ """
+ keyword_arguments = []
+ implicit_pos_args_cnt = 0
+ explicit_pos_args = set()
+ for name in collect_string_fields(format_string):
+ if name and str(name).isdigit():
+ explicit_pos_args.add(str(name))
+ elif name:
+ keyname, fielditerator = split_format_field_names(name)
+ if isinstance(keyname, numbers.Number):
+ # In Python 2 it will return long which will lead
+ # to different output between 2 and 3
+ explicit_pos_args.add(str(keyname))
+ keyname = int(keyname)
+ try:
+ keyword_arguments.append((keyname, list(fielditerator)))
+ except ValueError:
+ raise IncompleteFormatString()
+ else:
+ implicit_pos_args_cnt += 1
+ return keyword_arguments, implicit_pos_args_cnt, len(explicit_pos_args)
+
+
+def is_attr_protected(attrname: str) -> bool:
+ """return True if attribute name is protected (start with _ and some other
+ details), False otherwise.
+ """
+ return (
+ attrname[0] == "_"
+ and attrname != "_"
+ and not (attrname.startswith("__") and attrname.endswith("__"))
+ )
+
+
+def node_frame_class(node: astroid.node_classes.NodeNG) -> Optional[astroid.ClassDef]:
+ """Return the class that is wrapping the given node
+
+ The function returns a class for a method node (or a staticmethod or a
+ classmethod), otherwise it returns `None`.
+ """
+ klass = node.frame()
+
+ while klass is not None and not isinstance(klass, astroid.ClassDef):
+ if klass.parent is None:
+ klass = None
+ else:
+ klass = klass.parent.frame()
+
+ return klass
+
+
+def is_attr_private(attrname: str) -> Optional[Match[str]]:
+ """Check that attribute name is private (at least two leading underscores,
+ at most one trailing underscore)
+ """
+ regex = re.compile("^_{2,}.*[^_]+_?$")
+ return regex.match(attrname)
+
+
+def get_argument_from_call(
+ call_node: astroid.Call, position: int = None, keyword: str = None
+) -> astroid.Name:
+ """Returns the specified argument from a function call.
+
+ :param astroid.Call call_node: Node representing a function call to check.
+ :param int position: position of the argument.
+ :param str keyword: the keyword of the argument.
+
+ :returns: The node representing the argument, None if the argument is not found.
+ :rtype: astroid.Name
+ :raises ValueError: if both position and keyword are None.
+ :raises NoSuchArgumentError: if no argument at the provided position or with
+ the provided keyword.
+ """
+ if position is None and keyword is None:
+ raise ValueError("Must specify at least one of: position or keyword.")
+ if position is not None:
+ try:
+ return call_node.args[position]
+ except IndexError:
+ pass
+ if keyword and call_node.keywords:
+ for arg in call_node.keywords:
+ if arg.arg == keyword:
+ return arg.value
+
+ raise NoSuchArgumentError
+
+
+def inherit_from_std_ex(node: astroid.node_classes.NodeNG) -> bool:
+ """
+ Return true if the given class node is subclass of
+ exceptions.Exception.
+ """
+ ancestors = node.ancestors() if hasattr(node, "ancestors") else []
+ for ancestor in itertools.chain([node], ancestors):
+ if (
+ ancestor.name in ("Exception", "BaseException")
+ and ancestor.root().name == EXCEPTIONS_MODULE
+ ):
+ return True
+ return False
+
+
+def error_of_type(handler: astroid.ExceptHandler, error_type) -> bool:
+ """
+ Check if the given exception handler catches
+ the given error_type.
+
+ The *handler* parameter is a node, representing an ExceptHandler node.
+ The *error_type* can be an exception, such as AttributeError,
+ the name of an exception, or it can be a tuple of errors.
+ The function will return True if the handler catches any of the
+ given errors.
+ """
+
+ def stringify_error(error):
+ if not isinstance(error, str):
+ return error.__name__
+ return error
+
+ if not isinstance(error_type, tuple):
+ error_type = (error_type,) # type: ignore
+ expected_errors = {stringify_error(error) for error in error_type} # type: ignore
+ if not handler.type:
+ return True
+ return handler.catch(expected_errors)
+
+
+def decorated_with_property(node: astroid.FunctionDef) -> bool:
+ """Detect if the given function node is decorated with a property. """
+ if not node.decorators:
+ return False
+ for decorator in node.decorators.nodes:
+ try:
+ if _is_property_decorator(decorator):
+ return True
+ except astroid.InferenceError:
+ pass
+ return False
+
+
+def _is_property_kind(node, *kinds):
+ if not isinstance(node, (astroid.UnboundMethod, astroid.FunctionDef)):
+ return False
+ if node.decorators:
+ for decorator in node.decorators.nodes:
+ if isinstance(decorator, astroid.Attribute) and decorator.attrname in kinds:
+ return True
+ return False
+
+
+def is_property_setter(node: astroid.FunctionDef) -> bool:
+ """Check if the given node is a property setter"""
+ return _is_property_kind(node, "setter")
+
+
+def is_property_setter_or_deleter(node: astroid.FunctionDef) -> bool:
+ """Check if the given node is either a property setter or a deleter"""
+ return _is_property_kind(node, "setter", "deleter")
+
+
+def _is_property_decorator(decorator: astroid.Name) -> bool:
+ for inferred in decorator.infer():
+ if isinstance(inferred, astroid.ClassDef):
+ if inferred.root().name == BUILTINS_NAME and inferred.name == "property":
+ return True
+ for ancestor in inferred.ancestors():
+ if (
+ ancestor.name == "property"
+ and ancestor.root().name == BUILTINS_NAME
+ ):
+ return True
+ return False
+
+
+def decorated_with(
+ func: Union[astroid.FunctionDef, astroid.BoundMethod, astroid.UnboundMethod],
+ qnames: Iterable[str],
+) -> bool:
+ """Determine if the `func` node has a decorator with the qualified name `qname`."""
+ decorators = func.decorators.nodes if func.decorators else []
+ for decorator_node in decorators:
+ if isinstance(decorator_node, astroid.Call):
+ # We only want to infer the function name
+ decorator_node = decorator_node.func
+ try:
+ if any(
+ i is not None and i.qname() in qnames or i.name in qnames
+ for i in decorator_node.infer()
+ ):
+ return True
+ except astroid.InferenceError:
+ continue
+ return False
+
+
+@lru_cache(maxsize=1024)
+def unimplemented_abstract_methods(
+ node: astroid.node_classes.NodeNG, is_abstract_cb: astroid.FunctionDef = None
+) -> Dict[str, astroid.node_classes.NodeNG]:
+ """
+ Get the unimplemented abstract methods for the given *node*.
+
+ A method can be considered abstract if the callback *is_abstract_cb*
+ returns a ``True`` value. The check defaults to verifying that
+ a method is decorated with abstract methods.
+ The function will work only for new-style classes. For old-style
+ classes, it will simply return an empty dictionary.
+ For the rest of them, it will return a dictionary of abstract method
+ names and their inferred objects.
+ """
+ if is_abstract_cb is None:
+ is_abstract_cb = partial(decorated_with, qnames=ABC_METHODS)
+ visited = {} # type: Dict[str, astroid.node_classes.NodeNG]
+ try:
+ mro = reversed(node.mro())
+ except NotImplementedError:
+ # Old style class, it will not have a mro.
+ return {}
+ except astroid.ResolveError:
+ # Probably inconsistent hierarchy, don'try
+ # to figure this out here.
+ return {}
+ for ancestor in mro:
+ for obj in ancestor.values():
+ inferred = obj
+ if isinstance(obj, astroid.AssignName):
+ inferred = safe_infer(obj)
+ if not inferred:
+ # Might be an abstract function,
+ # but since we don't have enough information
+ # in order to take this decision, we're taking
+ # the *safe* decision instead.
+ if obj.name in visited:
+ del visited[obj.name]
+ continue
+ if not isinstance(inferred, astroid.FunctionDef):
+ if obj.name in visited:
+ del visited[obj.name]
+ if isinstance(inferred, astroid.FunctionDef):
+ # It's critical to use the original name,
+ # since after inferring, an object can be something
+ # else than expected, as in the case of the
+ # following assignment.
+ #
+ # class A:
+ # def keys(self): pass
+ # __iter__ = keys
+ abstract = is_abstract_cb(inferred)
+ if abstract:
+ visited[obj.name] = inferred
+ elif not abstract and obj.name in visited:
+ del visited[obj.name]
+ return visited
+
+
+def find_try_except_wrapper_node(
+ node: astroid.node_classes.NodeNG
+) -> Union[astroid.ExceptHandler, astroid.TryExcept]:
+ """Return the ExceptHandler or the TryExcept node in which the node is."""
+ current = node
+ ignores = (astroid.ExceptHandler, astroid.TryExcept)
+ while current and not isinstance(current.parent, ignores):
+ current = current.parent
+
+ if current and isinstance(current.parent, ignores):
+ return current.parent
+ return None
+
+
+def is_from_fallback_block(node: astroid.node_classes.NodeNG) -> bool:
+ """Check if the given node is from a fallback import block."""
+ context = find_try_except_wrapper_node(node)
+ if not context:
+ return False
+
+ if isinstance(context, astroid.ExceptHandler):
+ other_body = context.parent.body
+ handlers = context.parent.handlers
+ else:
+ other_body = itertools.chain.from_iterable(
+ handler.body for handler in context.handlers
+ )
+ handlers = context.handlers
+
+ has_fallback_imports = any(
+ isinstance(import_node, (astroid.ImportFrom, astroid.Import))
+ for import_node in other_body
+ )
+ ignores_import_error = _except_handlers_ignores_exception(handlers, ImportError)
+ return ignores_import_error or has_fallback_imports
+
+
+def _except_handlers_ignores_exception(
+ handlers: astroid.ExceptHandler, exception
+) -> bool:
+ func = partial(error_of_type, error_type=(exception,))
+ return any(map(func, handlers))
+
+
+def get_exception_handlers(
+ node: astroid.node_classes.NodeNG, exception=Exception
+) -> Optional[List[astroid.ExceptHandler]]:
+ """Return the collections of handlers handling the exception in arguments.
+
+ Args:
+ node (astroid.NodeNG): A node that is potentially wrapped in a try except.
+ exception (builtin.Exception or str): exception or name of the exception.
+
+ Returns:
+ list: the collection of handlers that are handling the exception or None.
+
+ """
+ context = find_try_except_wrapper_node(node)
+ if isinstance(context, astroid.TryExcept):
+ return [
+ handler for handler in context.handlers if error_of_type(handler, exception)
+ ]
+ return []
+
+
+def is_node_inside_try_except(node: astroid.Raise) -> bool:
+ """Check if the node is directly under a Try/Except statement.
+ (but not under an ExceptHandler!)
+
+ Args:
+ node (astroid.Raise): the node raising the exception.
+
+ Returns:
+ bool: True if the node is inside a try/except statement, False otherwise.
+ """
+ context = find_try_except_wrapper_node(node)
+ return isinstance(context, astroid.TryExcept)
+
+
+def node_ignores_exception(
+ node: astroid.node_classes.NodeNG, exception=Exception
+) -> bool:
+ """Check if the node is in a TryExcept which handles the given exception.
+
+ If the exception is not given, the function is going to look for bare
+ excepts.
+ """
+ managing_handlers = get_exception_handlers(node, exception)
+ if not managing_handlers:
+ return False
+ return any(managing_handlers)
+
+
+def class_is_abstract(node: astroid.ClassDef) -> bool:
+ """return true if the given class node should be considered as an abstract
+ class
+ """
+ for method in node.methods():
+ if method.parent.frame() is node:
+ if method.is_abstract(pass_is_abstract=False):
+ return True
+ return False
+
+
+def _supports_protocol_method(value: astroid.node_classes.NodeNG, attr: str) -> bool:
+ try:
+ attributes = value.getattr(attr)
+ except astroid.NotFoundError:
+ return False
+
+ first = attributes[0]
+ if isinstance(first, astroid.AssignName):
+ if isinstance(first.parent.value, astroid.Const):
+ return False
+ return True
+
+
+def is_comprehension(node: astroid.node_classes.NodeNG) -> bool:
+ comprehensions = (
+ astroid.ListComp,
+ astroid.SetComp,
+ astroid.DictComp,
+ astroid.GeneratorExp,
+ )
+ return isinstance(node, comprehensions)
+
+
+def _supports_mapping_protocol(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol_method(
+ value, GETITEM_METHOD
+ ) and _supports_protocol_method(value, KEYS_METHOD)
+
+
+def _supports_membership_test_protocol(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol_method(value, CONTAINS_METHOD)
+
+
+def _supports_iteration_protocol(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol_method(value, ITER_METHOD) or _supports_protocol_method(
+ value, GETITEM_METHOD
+ )
+
+
+def _supports_async_iteration_protocol(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol_method(value, AITER_METHOD)
+
+
+def _supports_getitem_protocol(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol_method(value, GETITEM_METHOD)
+
+
+def _supports_setitem_protocol(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol_method(value, SETITEM_METHOD)
+
+
+def _supports_delitem_protocol(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol_method(value, DELITEM_METHOD)
+
+
+def _is_abstract_class_name(name: str) -> bool:
+ lname = name.lower()
+ is_mixin = lname.endswith("mixin")
+ is_abstract = lname.startswith("abstract")
+ is_base = lname.startswith("base") or lname.endswith("base")
+ return is_mixin or is_abstract or is_base
+
+
+def is_inside_abstract_class(node: astroid.node_classes.NodeNG) -> bool:
+ while node is not None:
+ if isinstance(node, astroid.ClassDef):
+ if class_is_abstract(node):
+ return True
+ name = getattr(node, "name", None)
+ if name is not None and _is_abstract_class_name(name):
+ return True
+ node = node.parent
+ return False
+
+
+def _supports_protocol(
+ value: astroid.node_classes.NodeNG, protocol_callback: astroid.FunctionDef
+) -> bool:
+ if isinstance(value, astroid.ClassDef):
+ if not has_known_bases(value):
+ return True
+ # classobj can only be iterable if it has an iterable metaclass
+ meta = value.metaclass()
+ if meta is not None:
+ if protocol_callback(meta):
+ return True
+ if isinstance(value, astroid.BaseInstance):
+ if not has_known_bases(value):
+ return True
+ if value.has_dynamic_getattr():
+ return True
+ if protocol_callback(value):
+ return True
+
+ if (
+ isinstance(value, _bases.Proxy)
+ and isinstance(value._proxied, astroid.BaseInstance)
+ and has_known_bases(value._proxied)
+ ):
+ value = value._proxied
+ return protocol_callback(value)
+
+ return False
+
+
+def is_iterable(value: astroid.node_classes.NodeNG, check_async: bool = False) -> bool:
+ if check_async:
+ protocol_check = _supports_async_iteration_protocol
+ else:
+ protocol_check = _supports_iteration_protocol
+ return _supports_protocol(value, protocol_check)
+
+
+def is_mapping(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol(value, _supports_mapping_protocol)
+
+
+def supports_membership_test(value: astroid.node_classes.NodeNG) -> bool:
+ supported = _supports_protocol(value, _supports_membership_test_protocol)
+ return supported or is_iterable(value)
+
+
+def supports_getitem(value: astroid.node_classes.NodeNG) -> bool:
+ if isinstance(value, astroid.ClassDef):
+ if _supports_protocol_method(value, CLASS_GETITEM_METHOD):
+ return True
+ return _supports_protocol(value, _supports_getitem_protocol)
+
+
+def supports_setitem(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol(value, _supports_setitem_protocol)
+
+
+def supports_delitem(value: astroid.node_classes.NodeNG) -> bool:
+ return _supports_protocol(value, _supports_delitem_protocol)
+
+
+@lru_cache(maxsize=1024)
+def safe_infer(
+ node: astroid.node_classes.NodeNG, context=None
+) -> Optional[astroid.node_classes.NodeNG]:
+ """Return the inferred value for the given node.
+
+ Return None if inference failed or if there is some ambiguity (more than
+ one node has been inferred).
+ """
+ try:
+ inferit = node.infer(context=context)
+ value = next(inferit)
+ except astroid.InferenceError:
+ return None
+ try:
+ next(inferit)
+ return None # None if there is ambiguity on the inferred node
+ except astroid.InferenceError:
+ return None # there is some kind of ambiguity
+ except StopIteration:
+ return value
+
+
+def has_known_bases(klass: astroid.ClassDef, context=None) -> bool:
+ """Return true if all base classes of a class could be inferred."""
+ try:
+ return klass._all_bases_known
+ except AttributeError:
+ pass
+ for base in klass.bases:
+ result = safe_infer(base, context=context)
+ if (
+ not isinstance(result, astroid.ClassDef)
+ or result is klass
+ or not has_known_bases(result, context=context)
+ ):
+ klass._all_bases_known = False
+ return False
+ klass._all_bases_known = True
+ return True
+
+
+def is_none(node: astroid.node_classes.NodeNG) -> bool:
+ return (
+ node is None
+ or (isinstance(node, astroid.Const) and node.value is None)
+ or (isinstance(node, astroid.Name) and node.name == "None")
+ )
+
+
+def node_type(node: astroid.node_classes.NodeNG) -> Optional[type]:
+ """Return the inferred type for `node`
+
+ If there is more than one possible type, or if inferred type is Uninferable or None,
+ return None
+ """
+ # check there is only one possible type for the assign node. Else we
+ # don't handle it for now
+ types = set()
+ try:
+ for var_type in node.infer():
+ if var_type == astroid.Uninferable or is_none(var_type):
+ continue
+ types.add(var_type)
+ if len(types) > 1:
+ return None
+ except astroid.InferenceError:
+ return None
+ return types.pop() if types else None
+
+
+def is_registered_in_singledispatch_function(node: astroid.FunctionDef) -> bool:
+ """Check if the given function node is a singledispatch function."""
+
+ singledispatch_qnames = (
+ "functools.singledispatch",
+ "singledispatch.singledispatch",
+ )
+
+ if not isinstance(node, astroid.FunctionDef):
+ return False
+
+ decorators = node.decorators.nodes if node.decorators else []
+ for decorator in decorators:
+ # func.register are function calls
+ if not isinstance(decorator, astroid.Call):
+ continue
+
+ func = decorator.func
+ if not isinstance(func, astroid.Attribute) or func.attrname != "register":
+ continue
+
+ try:
+ func_def = next(func.expr.infer())
+ except astroid.InferenceError:
+ continue
+
+ if isinstance(func_def, astroid.FunctionDef):
+ # pylint: disable=redundant-keyword-arg; some flow inference goes wrong here
+ return decorated_with(func_def, singledispatch_qnames)
+
+ return False
+
+
+def get_node_last_lineno(node: astroid.node_classes.NodeNG) -> int:
+ """
+ Get the last lineno of the given node. For a simple statement this will just be node.lineno,
+ but for a node that has child statements (e.g. a method) this will be the lineno of the last
+ child statement recursively.
+ """
+ # 'finalbody' is always the last clause in a try statement, if present
+ if getattr(node, "finalbody", False):
+ return get_node_last_lineno(node.finalbody[-1])
+ # For if, while, and for statements 'orelse' is always the last clause.
+ # For try statements 'orelse' is the last in the absence of a 'finalbody'
+ if getattr(node, "orelse", False):
+ return get_node_last_lineno(node.orelse[-1])
+ # try statements have the 'handlers' last if there is no 'orelse' or 'finalbody'
+ if getattr(node, "handlers", False):
+ return get_node_last_lineno(node.handlers[-1])
+ # All compound statements have a 'body'
+ if getattr(node, "body", False):
+ return get_node_last_lineno(node.body[-1])
+ # Not a compound statement
+ return node.lineno
+
+
+def is_postponed_evaluation_enabled(node: astroid.node_classes.NodeNG) -> bool:
+ """Check if the postponed evaluation of annotations is enabled"""
+ name = "annotations"
+ module = node.root()
+ stmt = module.locals.get(name)
+ return (
+ stmt
+ and isinstance(stmt[0], astroid.ImportFrom)
+ and stmt[0].modname == "__future__"
+ )
+
+
+def is_subclass_of(child: astroid.ClassDef, parent: astroid.ClassDef) -> bool:
+ """
+ Check if first node is a subclass of second node.
+ :param child: Node to check for subclass.
+ :param parent: Node to check for superclass.
+ :returns: True if child is derived from parent. False otherwise.
+ """
+ if not all(isinstance(node, astroid.ClassDef) for node in (child, parent)):
+ return False
+
+ for ancestor in child.ancestors():
+ try:
+ if helpers.is_subtype(ancestor, parent):
+ return True
+ except _NonDeducibleTypeHierarchy:
+ continue
+ return False
+
+
+@lru_cache(maxsize=1024)
+def is_overload_stub(node: astroid.node_classes.NodeNG) -> bool:
+ """Check if a node if is a function stub decorated with typing.overload.
+
+ :param node: Node to check.
+ :returns: True if node is an overload function stub. False otherwise.
+ """
+ decorators = getattr(node, "decorators", None)
+ return bool(decorators and decorated_with(node, ["typing.overload", "overload"]))
+
+
+def is_protocol_class(cls: astroid.node_classes.NodeNG) -> bool:
+ """Check if the given node represents a protocol class
+
+ :param cls: The node to check
+ :returns: True if the node is a typing protocol class, false otherwise.
+ """
+ if not isinstance(cls, astroid.ClassDef):
+ return False
+
+ # Use .ancestors() since not all protocol classes can have
+ # their mro deduced.
+ return any(parent.qname() in TYPING_PROTOCOLS for parent in cls.ancestors())
diff --git a/venv/Lib/site-packages/pylint/checkers/variables.py b/venv/Lib/site-packages/pylint/checkers/variables.py
new file mode 100644
index 0000000..e13f9b5
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/checkers/variables.py
@@ -0,0 +1,1987 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
+# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
+# Copyright (c) 2011-2014, 2017 Google, Inc.
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Ricardo Gemignani <ricardo.gemignani@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
+# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
+# Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016, 2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2016, 2018 Jakub Wilk <jwilk@jwilk.net>
+# Copyright (c) 2016-2017 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 Grant Welch <gwelch925+github@gmail.com>
+# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Dan Garrette <dhgarrette@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.guinta@protonmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Marianna Polatoglou <mpolatoglou@bloomberg.net>
+# Copyright (c) 2018 mar-chi-pan <mar.polatoglou@gmail.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""variables checkers for Python code
+"""
+import collections
+import copy
+import itertools
+import os
+import re
+from functools import lru_cache
+
+import astroid
+from astroid import decorators, modutils, objects
+from astroid.context import InferenceContext
+
+from pylint.checkers import BaseChecker, utils
+from pylint.checkers.utils import is_postponed_evaluation_enabled
+from pylint.interfaces import HIGH, INFERENCE, INFERENCE_FAILURE, IAstroidChecker
+from pylint.utils import get_global_option
+
+SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
+FUTURE = "__future__"
+# regexp for ignored argument name
+IGNORED_ARGUMENT_NAMES = re.compile("_.*|^ignored_|^unused_")
+# In Python 3.7 abc has a Python implementation which is preferred
+# by astroid. Unfortunately this also messes up our explicit checks
+# for `abc`
+METACLASS_NAME_TRANSFORMS = {"_py_abc": "abc"}
+TYPING_TYPE_CHECKS_GUARDS = frozenset({"typing.TYPE_CHECKING", "TYPE_CHECKING"})
+BUILTIN_RANGE = "builtins.range"
+TYPING_MODULE = "typing"
+TYPING_NAMES = frozenset(
+ {
+ "Any",
+ "Callable",
+ "ClassVar",
+ "Generic",
+ "Optional",
+ "Tuple",
+ "Type",
+ "TypeVar",
+ "Union",
+ "AbstractSet",
+ "ByteString",
+ "Container",
+ "ContextManager",
+ "Hashable",
+ "ItemsView",
+ "Iterable",
+ "Iterator",
+ "KeysView",
+ "Mapping",
+ "MappingView",
+ "MutableMapping",
+ "MutableSequence",
+ "MutableSet",
+ "Sequence",
+ "Sized",
+ "ValuesView",
+ "Awaitable",
+ "AsyncIterator",
+ "AsyncIterable",
+ "Coroutine",
+ "Collection",
+ "AsyncGenerator",
+ "AsyncContextManager",
+ "Reversible",
+ "SupportsAbs",
+ "SupportsBytes",
+ "SupportsComplex",
+ "SupportsFloat",
+ "SupportsInt",
+ "SupportsRound",
+ "Counter",
+ "Deque",
+ "Dict",
+ "DefaultDict",
+ "List",
+ "Set",
+ "FrozenSet",
+ "NamedTuple",
+ "Generator",
+ "AnyStr",
+ "Text",
+ "Pattern",
+ }
+)
+
+
+def _is_from_future_import(stmt, name):
+ """Check if the name is a future import from another module."""
+ try:
+ module = stmt.do_import_module(stmt.modname)
+ except astroid.AstroidBuildingException:
+ return None
+
+ for local_node in module.locals.get(name, []):
+ if isinstance(local_node, astroid.ImportFrom) and local_node.modname == FUTURE:
+ return True
+ return None
+
+
+def in_for_else_branch(parent, stmt):
+ """Returns True if stmt in inside the else branch for a parent For stmt."""
+ return isinstance(parent, astroid.For) and any(
+ else_stmt.parent_of(stmt) or else_stmt == stmt for else_stmt in parent.orelse
+ )
+
+
+@lru_cache(maxsize=1000)
+def overridden_method(klass, name):
+ """get overridden method if any"""
+ try:
+ parent = next(klass.local_attr_ancestors(name))
+ except (StopIteration, KeyError):
+ return None
+ try:
+ meth_node = parent[name]
+ except KeyError:
+ # We have found an ancestor defining <name> but it's not in the local
+ # dictionary. This may happen with astroid built from living objects.
+ return None
+ if isinstance(meth_node, astroid.FunctionDef):
+ return meth_node
+ return None
+
+
+def _get_unpacking_extra_info(node, inferred):
+ """return extra information to add to the message for unpacking-non-sequence
+ and unbalanced-tuple-unpacking errors
+ """
+ more = ""
+ inferred_module = inferred.root().name
+ if node.root().name == inferred_module:
+ if node.lineno == inferred.lineno:
+ more = " %s" % inferred.as_string()
+ elif inferred.lineno:
+ more = " defined at line %s" % inferred.lineno
+ elif inferred.lineno:
+ more = " defined at line %s of %s" % (inferred.lineno, inferred_module)
+ return more
+
+
+def _detect_global_scope(node, frame, defframe):
+ """ Detect that the given frames shares a global
+ scope.
+
+ Two frames shares a global scope when neither
+ of them are hidden under a function scope, as well
+ as any of parent scope of them, until the root scope.
+ In this case, depending from something defined later on
+ will not work, because it is still undefined.
+
+ Example:
+ class A:
+ # B has the same global scope as `C`, leading to a NameError.
+ class B(C): ...
+ class C: ...
+
+ """
+ def_scope = scope = None
+ if frame and frame.parent:
+ scope = frame.parent.scope()
+ if defframe and defframe.parent:
+ def_scope = defframe.parent.scope()
+ if isinstance(frame, astroid.FunctionDef):
+ # If the parent of the current node is a
+ # function, then it can be under its scope
+ # (defined in, which doesn't concern us) or
+ # the `->` part of annotations. The same goes
+ # for annotations of function arguments, they'll have
+ # their parent the Arguments node.
+ if not isinstance(node.parent, (astroid.FunctionDef, astroid.Arguments)):
+ return False
+ elif any(
+ not isinstance(f, (astroid.ClassDef, astroid.Module)) for f in (frame, defframe)
+ ):
+ # Not interested in other frames, since they are already
+ # not in a global scope.
+ return False
+
+ break_scopes = []
+ for current_scope in (scope, def_scope):
+ # Look for parent scopes. If there is anything different
+ # than a module or a class scope, then they frames don't
+ # share a global scope.
+ parent_scope = current_scope
+ while parent_scope:
+ if not isinstance(parent_scope, (astroid.ClassDef, astroid.Module)):
+ break_scopes.append(parent_scope)
+ break
+ if parent_scope.parent:
+ parent_scope = parent_scope.parent.scope()
+ else:
+ break
+ if break_scopes and len(set(break_scopes)) != 1:
+ # Store different scopes than expected.
+ # If the stored scopes are, in fact, the very same, then it means
+ # that the two frames (frame and defframe) shares the same scope,
+ # and we could apply our lineno analysis over them.
+ # For instance, this works when they are inside a function, the node
+ # that uses a definition and the definition itself.
+ return False
+ # At this point, we are certain that frame and defframe shares a scope
+ # and the definition of the first depends on the second.
+ return frame.lineno < defframe.lineno
+
+
+def _infer_name_module(node, name):
+ context = InferenceContext()
+ context.lookupname = name
+ return node.infer(context, asname=False)
+
+
+def _fix_dot_imports(not_consumed):
+ """ Try to fix imports with multiple dots, by returning a dictionary
+ with the import names expanded. The function unflattens root imports,
+ like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree'
+ and 'xml.sax' respectively.
+ """
+ names = {}
+ for name, stmts in not_consumed.items():
+ if any(
+ isinstance(stmt, astroid.AssignName)
+ and isinstance(stmt.assign_type(), astroid.AugAssign)
+ for stmt in stmts
+ ):
+ continue
+ for stmt in stmts:
+ if not isinstance(stmt, (astroid.ImportFrom, astroid.Import)):
+ continue
+ for imports in stmt.names:
+ second_name = None
+ import_module_name = imports[0]
+ if import_module_name == "*":
+ # In case of wildcard imports,
+ # pick the name from inside the imported module.
+ second_name = name
+ else:
+ name_matches_dotted_import = False
+ if (
+ import_module_name.startswith(name)
+ and import_module_name.find(".") > -1
+ ):
+ name_matches_dotted_import = True
+
+ if name_matches_dotted_import or name in imports:
+ # Most likely something like 'xml.etree',
+ # which will appear in the .locals as 'xml'.
+ # Only pick the name if it wasn't consumed.
+ second_name = import_module_name
+ if second_name and second_name not in names:
+ names[second_name] = stmt
+ return sorted(names.items(), key=lambda a: a[1].fromlineno)
+
+
+def _find_frame_imports(name, frame):
+ """
+ Detect imports in the frame, with the required
+ *name*. Such imports can be considered assignments.
+ Returns True if an import for the given name was found.
+ """
+ imports = frame.nodes_of_class((astroid.Import, astroid.ImportFrom))
+ for import_node in imports:
+ for import_name, import_alias in import_node.names:
+ # If the import uses an alias, check only that.
+ # Otherwise, check only the import name.
+ if import_alias:
+ if import_alias == name:
+ return True
+ elif import_name and import_name == name:
+ return True
+ return None
+
+
+def _import_name_is_global(stmt, global_names):
+ for import_name, import_alias in stmt.names:
+ # If the import uses an alias, check only that.
+ # Otherwise, check only the import name.
+ if import_alias:
+ if import_alias in global_names:
+ return True
+ elif import_name in global_names:
+ return True
+ return False
+
+
+def _flattened_scope_names(iterator):
+ values = (set(stmt.names) for stmt in iterator)
+ return set(itertools.chain.from_iterable(values))
+
+
+def _assigned_locally(name_node):
+ """
+ Checks if name_node has corresponding assign statement in same scope
+ """
+ assign_stmts = name_node.scope().nodes_of_class(astroid.AssignName)
+ return any(a.name == name_node.name for a in assign_stmts)
+
+
+def _is_type_checking_import(node):
+ parent = node.parent
+ if not isinstance(parent, astroid.If):
+ return False
+ test = parent.test
+ return test.as_string() in TYPING_TYPE_CHECKS_GUARDS
+
+
+def _has_locals_call_after_node(stmt, scope):
+ skip_nodes = (
+ astroid.FunctionDef,
+ astroid.ClassDef,
+ astroid.Import,
+ astroid.ImportFrom,
+ )
+ for call in scope.nodes_of_class(astroid.Call, skip_klass=skip_nodes):
+ inferred = utils.safe_infer(call.func)
+ if (
+ utils.is_builtin_object(inferred)
+ and getattr(inferred, "name", None) == "locals"
+ ):
+ if stmt.lineno < call.lineno:
+ return True
+ return False
+
+
+MSGS = {
+ "E0601": (
+ "Using variable %r before assignment",
+ "used-before-assignment",
+ "Used when a local variable is accessed before its assignment.",
+ ),
+ "E0602": (
+ "Undefined variable %r",
+ "undefined-variable",
+ "Used when an undefined variable is accessed.",
+ ),
+ "E0603": (
+ "Undefined variable name %r in __all__",
+ "undefined-all-variable",
+ "Used when an undefined variable name is referenced in __all__.",
+ ),
+ "E0604": (
+ "Invalid object %r in __all__, must contain only strings",
+ "invalid-all-object",
+ "Used when an invalid (non-string) object occurs in __all__.",
+ ),
+ "E0611": (
+ "No name %r in module %r",
+ "no-name-in-module",
+ "Used when a name cannot be found in a module.",
+ ),
+ "W0601": (
+ "Global variable %r undefined at the module level",
+ "global-variable-undefined",
+ 'Used when a variable is defined through the "global" statement '
+ "but the variable is not defined in the module scope.",
+ ),
+ "W0602": (
+ "Using global for %r but no assignment is done",
+ "global-variable-not-assigned",
+ 'Used when a variable is defined through the "global" statement '
+ "but no assignment to this variable is done.",
+ ),
+ "W0603": (
+ "Using the global statement", # W0121
+ "global-statement",
+ 'Used when you use the "global" statement to update a global '
+ "variable. Pylint just try to discourage this "
+ "usage. That doesn't mean you cannot use it !",
+ ),
+ "W0604": (
+ "Using the global statement at the module level", # W0103
+ "global-at-module-level",
+ 'Used when you use the "global" statement at the module level '
+ "since it has no effect",
+ ),
+ "W0611": (
+ "Unused %s",
+ "unused-import",
+ "Used when an imported module or variable is not used.",
+ ),
+ "W0612": (
+ "Unused variable %r",
+ "unused-variable",
+ "Used when a variable is defined but not used.",
+ ),
+ "W0613": (
+ "Unused argument %r",
+ "unused-argument",
+ "Used when a function or method argument is not used.",
+ ),
+ "W0614": (
+ "Unused import %s from wildcard import",
+ "unused-wildcard-import",
+ "Used when an imported module or variable is not used from a "
+ "`'from X import *'` style import.",
+ ),
+ "W0621": (
+ "Redefining name %r from outer scope (line %s)",
+ "redefined-outer-name",
+ "Used when a variable's name hides a name defined in the outer scope.",
+ ),
+ "W0622": (
+ "Redefining built-in %r",
+ "redefined-builtin",
+ "Used when a variable or function override a built-in.",
+ ),
+ "W0623": (
+ "Redefining name %r from %s in exception handler",
+ "redefine-in-handler",
+ "Used when an exception handler assigns the exception to an existing name",
+ ),
+ "W0631": (
+ "Using possibly undefined loop variable %r",
+ "undefined-loop-variable",
+ "Used when a loop variable (i.e. defined by a for loop or "
+ "a list comprehension or a generator expression) is used outside "
+ "the loop.",
+ ),
+ "W0632": (
+ "Possible unbalanced tuple unpacking with "
+ "sequence%s: "
+ "left side has %d label(s), right side has %d value(s)",
+ "unbalanced-tuple-unpacking",
+ "Used when there is an unbalanced tuple unpacking in assignment",
+ {"old_names": [("E0632", "old-unbalanced-tuple-unpacking")]},
+ ),
+ "E0633": (
+ "Attempting to unpack a non-sequence%s",
+ "unpacking-non-sequence",
+ "Used when something which is not "
+ "a sequence is used in an unpack assignment",
+ {"old_names": [("W0633", "old-unpacking-non-sequence")]},
+ ),
+ "W0640": (
+ "Cell variable %s defined in loop",
+ "cell-var-from-loop",
+ "A variable used in a closure is defined in a loop. "
+ "This will result in all closures using the same value for "
+ "the closed-over variable.",
+ ),
+ "W0641": (
+ "Possibly unused variable %r",
+ "possibly-unused-variable",
+ "Used when a variable is defined but might not be used. "
+ "The possibility comes from the fact that locals() might be used, "
+ "which could consume or not the said variable",
+ ),
+ "W0642": (
+ "Invalid assignment to %s in method",
+ "self-cls-assignment",
+ "Invalid assignment to self or cls in instance or class method "
+ "respectively.",
+ ),
+}
+
+
+ScopeConsumer = collections.namedtuple(
+ "ScopeConsumer", "to_consume consumed scope_type"
+)
+
+
+class NamesConsumer:
+ """
+ A simple class to handle consumed, to consume and scope type info of node locals
+ """
+
+ def __init__(self, node, scope_type):
+ self._atomic = ScopeConsumer(copy.copy(node.locals), {}, scope_type)
+
+ def __repr__(self):
+ msg = "\nto_consume : {:s}\n".format(
+ ", ".join(
+ [
+ "{}->{}".format(key, val)
+ for key, val in self._atomic.to_consume.items()
+ ]
+ )
+ )
+ msg += "consumed : {:s}\n".format(
+ ", ".join(
+ [
+ "{}->{}".format(key, val)
+ for key, val in self._atomic.consumed.items()
+ ]
+ )
+ )
+ msg += "scope_type : {:s}\n".format(self._atomic.scope_type)
+ return msg
+
+ def __iter__(self):
+ return iter(self._atomic)
+
+ @property
+ def to_consume(self):
+ return self._atomic.to_consume
+
+ @property
+ def consumed(self):
+ return self._atomic.consumed
+
+ @property
+ def scope_type(self):
+ return self._atomic.scope_type
+
+ def mark_as_consumed(self, name, new_node):
+ """
+ Mark the name as consumed and delete it from
+ the to_consume dictionary
+ """
+ self.consumed[name] = new_node
+ del self.to_consume[name]
+
+ def get_next_to_consume(self, node):
+ # mark the name as consumed if it's defined in this scope
+ name = node.name
+ parent_node = node.parent
+ found_node = self.to_consume.get(name)
+ if (
+ found_node
+ and isinstance(parent_node, astroid.Assign)
+ and parent_node == found_node[0].parent
+ ):
+ lhs = found_node[0].parent.targets[0]
+ if lhs.name == name: # this name is defined in this very statement
+ found_node = None
+ return found_node
+
+
+# pylint: disable=too-many-public-methods
+class VariablesChecker(BaseChecker):
+ """checks for
+ * unused variables / imports
+ * undefined variables
+ * redefinition of variable from builtins or from an outer scope
+ * use of variable before assignment
+ * __all__ consistency
+ * self/cls assignment
+ """
+
+ __implements__ = IAstroidChecker
+
+ name = "variables"
+ msgs = MSGS
+ priority = -1
+ options = (
+ (
+ "init-import",
+ {
+ "default": 0,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": "Tells whether we should check for unused import in "
+ "__init__ files.",
+ },
+ ),
+ (
+ "dummy-variables-rgx",
+ {
+ "default": "_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_",
+ "type": "regexp",
+ "metavar": "<regexp>",
+ "help": "A regular expression matching the name of dummy "
+ "variables (i.e. expected to not be used).",
+ },
+ ),
+ (
+ "additional-builtins",
+ {
+ "default": (),
+ "type": "csv",
+ "metavar": "<comma separated list>",
+ "help": "List of additional names supposed to be defined in "
+ "builtins. Remember that you should avoid defining new builtins "
+ "when possible.",
+ },
+ ),
+ (
+ "callbacks",
+ {
+ "default": ("cb_", "_cb"),
+ "type": "csv",
+ "metavar": "<callbacks>",
+ "help": "List of strings which can identify a callback "
+ "function by name. A callback name must start or "
+ "end with one of those strings.",
+ },
+ ),
+ (
+ "redefining-builtins-modules",
+ {
+ "default": (
+ "six.moves",
+ "past.builtins",
+ "future.builtins",
+ "builtins",
+ "io",
+ ),
+ "type": "csv",
+ "metavar": "<comma separated list>",
+ "help": "List of qualified module names which can have objects "
+ "that can redefine builtins.",
+ },
+ ),
+ (
+ "ignored-argument-names",
+ {
+ "default": IGNORED_ARGUMENT_NAMES,
+ "type": "regexp",
+ "metavar": "<regexp>",
+ "help": "Argument names that match this expression will be "
+ "ignored. Default to name with leading underscore.",
+ },
+ ),
+ (
+ "allow-global-unused-variables",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "help": "Tells whether unused global variables should be treated as a violation.",
+ },
+ ),
+ )
+
+ def __init__(self, linter=None):
+ BaseChecker.__init__(self, linter)
+ self._to_consume = (
+ None
+ ) # list of tuples: (to_consume:dict, consumed:dict, scope_type:str)
+ self._checking_mod_attr = None
+ self._loop_variables = []
+ self._type_annotation_names = []
+ self._postponed_evaluation_enabled = False
+
+ @utils.check_messages("redefined-outer-name")
+ def visit_for(self, node):
+ assigned_to = [
+ var.name for var in node.target.nodes_of_class(astroid.AssignName)
+ ]
+
+ # Only check variables that are used
+ dummy_rgx = self.config.dummy_variables_rgx
+ assigned_to = [var for var in assigned_to if not dummy_rgx.match(var)]
+
+ for variable in assigned_to:
+ for outer_for, outer_variables in self._loop_variables:
+ if variable in outer_variables and not in_for_else_branch(
+ outer_for, node
+ ):
+ self.add_message(
+ "redefined-outer-name",
+ args=(variable, outer_for.fromlineno),
+ node=node,
+ )
+ break
+
+ self._loop_variables.append((node, assigned_to))
+
+ @utils.check_messages("redefined-outer-name")
+ def leave_for(self, node):
+ self._loop_variables.pop()
+ self._store_type_annotation_names(node)
+
+ def visit_module(self, node):
+ """visit module : update consumption analysis variable
+ checks globals doesn't overrides builtins
+ """
+ self._to_consume = [NamesConsumer(node, "module")]
+ self._postponed_evaluation_enabled = is_postponed_evaluation_enabled(node)
+
+ for name, stmts in node.locals.items():
+ if utils.is_builtin(name) and not utils.is_inside_except(stmts[0]):
+ if self._should_ignore_redefined_builtin(stmts[0]) or name == "__doc__":
+ continue
+ self.add_message("redefined-builtin", args=name, node=stmts[0])
+
+ @utils.check_messages(
+ "unused-import",
+ "unused-wildcard-import",
+ "redefined-builtin",
+ "undefined-all-variable",
+ "invalid-all-object",
+ "unused-variable",
+ )
+ def leave_module(self, node):
+ """leave module: check globals
+ """
+ assert len(self._to_consume) == 1
+
+ self._check_metaclasses(node)
+ not_consumed = self._to_consume.pop().to_consume
+ # attempt to check for __all__ if defined
+ if "__all__" in node.locals:
+ self._check_all(node, not_consumed)
+
+ # check for unused globals
+ self._check_globals(not_consumed)
+
+ # don't check unused imports in __init__ files
+ if not self.config.init_import and node.package:
+ return
+
+ self._check_imports(not_consumed)
+
+ def visit_classdef(self, node):
+ """visit class: update consumption analysis variable
+ """
+ self._to_consume.append(NamesConsumer(node, "class"))
+
+ def leave_classdef(self, _):
+ """leave class: update consumption analysis variable
+ """
+ # do not check for not used locals here (no sense)
+ self._to_consume.pop()
+
+ def visit_lambda(self, node):
+ """visit lambda: update consumption analysis variable
+ """
+ self._to_consume.append(NamesConsumer(node, "lambda"))
+
+ def leave_lambda(self, _):
+ """leave lambda: update consumption analysis variable
+ """
+ # do not check for not used locals here
+ self._to_consume.pop()
+
+ def visit_generatorexp(self, node):
+ """visit genexpr: update consumption analysis variable
+ """
+ self._to_consume.append(NamesConsumer(node, "comprehension"))
+
+ def leave_generatorexp(self, _):
+ """leave genexpr: update consumption analysis variable
+ """
+ # do not check for not used locals here
+ self._to_consume.pop()
+
+ def visit_dictcomp(self, node):
+ """visit dictcomp: update consumption analysis variable
+ """
+ self._to_consume.append(NamesConsumer(node, "comprehension"))
+
+ def leave_dictcomp(self, _):
+ """leave dictcomp: update consumption analysis variable
+ """
+ # do not check for not used locals here
+ self._to_consume.pop()
+
+ def visit_setcomp(self, node):
+ """visit setcomp: update consumption analysis variable
+ """
+ self._to_consume.append(NamesConsumer(node, "comprehension"))
+
+ def leave_setcomp(self, _):
+ """leave setcomp: update consumption analysis variable
+ """
+ # do not check for not used locals here
+ self._to_consume.pop()
+
+ def visit_functiondef(self, node):
+ """visit function: update consumption analysis variable and check locals
+ """
+ self._to_consume.append(NamesConsumer(node, "function"))
+ if not (
+ self.linter.is_message_enabled("redefined-outer-name")
+ or self.linter.is_message_enabled("redefined-builtin")
+ ):
+ return
+ globs = node.root().globals
+ for name, stmt in node.items():
+ if utils.is_inside_except(stmt):
+ continue
+ if name in globs and not isinstance(stmt, astroid.Global):
+ definition = globs[name][0]
+ if (
+ isinstance(definition, astroid.ImportFrom)
+ and definition.modname == FUTURE
+ ):
+ # It is a __future__ directive, not a symbol.
+ continue
+
+ # Do not take in account redefined names for the purpose
+ # of type checking.:
+ if any(
+ isinstance(definition.parent, astroid.If)
+ and definition.parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS
+ for definition in globs[name]
+ ):
+ continue
+
+ line = definition.fromlineno
+ if not self._is_name_ignored(stmt, name):
+ self.add_message(
+ "redefined-outer-name", args=(name, line), node=stmt
+ )
+
+ elif utils.is_builtin(name) and not self._should_ignore_redefined_builtin(
+ stmt
+ ):
+ # do not print Redefining builtin for additional builtins
+ self.add_message("redefined-builtin", args=name, node=stmt)
+
+ def leave_functiondef(self, node):
+ """leave function: check function's locals are consumed"""
+ self._check_metaclasses(node)
+
+ if node.type_comment_returns:
+ self._store_type_annotation_node(node.type_comment_returns)
+ if node.type_comment_args:
+ for argument_annotation in node.type_comment_args:
+ self._store_type_annotation_node(argument_annotation)
+
+ not_consumed = self._to_consume.pop().to_consume
+ if not (
+ self.linter.is_message_enabled("unused-variable")
+ or self.linter.is_message_enabled("possibly-unused-variable")
+ or self.linter.is_message_enabled("unused-argument")
+ ):
+ return
+
+ # Don't check arguments of function which are only raising an exception.
+ if utils.is_error(node):
+ return
+
+ # Don't check arguments of abstract methods or within an interface.
+ is_method = node.is_method()
+ if is_method and node.is_abstract():
+ return
+
+ global_names = _flattened_scope_names(node.nodes_of_class(astroid.Global))
+ nonlocal_names = _flattened_scope_names(node.nodes_of_class(astroid.Nonlocal))
+ for name, stmts in not_consumed.items():
+ self._check_is_unused(name, node, stmts[0], global_names, nonlocal_names)
+
+ visit_asyncfunctiondef = visit_functiondef
+ leave_asyncfunctiondef = leave_functiondef
+
+ @utils.check_messages(
+ "global-variable-undefined",
+ "global-variable-not-assigned",
+ "global-statement",
+ "global-at-module-level",
+ "redefined-builtin",
+ )
+ def visit_global(self, node):
+ """check names imported exists in the global scope"""
+ frame = node.frame()
+ if isinstance(frame, astroid.Module):
+ self.add_message("global-at-module-level", node=node)
+ return
+
+ module = frame.root()
+ default_message = True
+ locals_ = node.scope().locals
+ for name in node.names:
+ try:
+ assign_nodes = module.getattr(name)
+ except astroid.NotFoundError:
+ # unassigned global, skip
+ assign_nodes = []
+
+ not_defined_locally_by_import = not any(
+ isinstance(local, astroid.node_classes.Import)
+ for local in locals_.get(name, ())
+ )
+ if not assign_nodes and not_defined_locally_by_import:
+ self.add_message("global-variable-not-assigned", args=name, node=node)
+ default_message = False
+ continue
+
+ for anode in assign_nodes:
+ if (
+ isinstance(anode, astroid.AssignName)
+ and anode.name in module.special_attributes
+ ):
+ self.add_message("redefined-builtin", args=name, node=node)
+ break
+ if anode.frame() is module:
+ # module level assignment
+ break
+ else:
+ if not_defined_locally_by_import:
+ # global undefined at the module scope
+ self.add_message("global-variable-undefined", args=name, node=node)
+ default_message = False
+
+ if default_message:
+ self.add_message("global-statement", node=node)
+
+ def visit_assignname(self, node):
+ if isinstance(node.assign_type(), astroid.AugAssign):
+ self.visit_name(node)
+
+ def visit_delname(self, node):
+ self.visit_name(node)
+
+ @utils.check_messages(*MSGS)
+ def visit_name(self, node):
+ """check that a name is defined if the current scope and doesn't
+ redefine a built-in
+ """
+ stmt = node.statement()
+ if stmt.fromlineno is None:
+ # name node from an astroid built from live code, skip
+ assert not stmt.root().file.endswith(".py")
+ return
+
+ name = node.name
+ frame = stmt.scope()
+ # if the name node is used as a function default argument's value or as
+ # a decorator, then start from the parent frame of the function instead
+ # of the function frame - and thus open an inner class scope
+ if (
+ utils.is_default_argument(node)
+ or utils.is_func_decorator(node)
+ or utils.is_ancestor_name(frame, node)
+ ):
+ start_index = len(self._to_consume) - 2
+ else:
+ start_index = len(self._to_consume) - 1
+ # iterates through parent scopes, from the inner to the outer
+ base_scope_type = self._to_consume[start_index].scope_type
+ # pylint: disable=too-many-nested-blocks; refactoring this block is a pain.
+ for i in range(start_index, -1, -1):
+ current_consumer = self._to_consume[i]
+ # if the current scope is a class scope but it's not the inner
+ # scope, ignore it. This prevents to access this scope instead of
+ # the globals one in function members when there are some common
+ # names. The only exception is when the starting scope is a
+ # comprehension and its direct outer scope is a class
+ if (
+ current_consumer.scope_type == "class"
+ and i != start_index
+ and not (base_scope_type == "comprehension" and i == start_index - 1)
+ ):
+ if self._ignore_class_scope(node):
+ continue
+
+ # the name has already been consumed, only check it's not a loop
+ # variable used outside the loop
+ # avoid the case where there are homonyms inside function scope and
+ #  comprehension current scope (avoid bug #1731)
+ if name in current_consumer.consumed and not (
+ current_consumer.scope_type == "comprehension"
+ and self._has_homonym_in_upper_function_scope(node, i)
+ ):
+ defnode = utils.assign_parent(current_consumer.consumed[name][0])
+ self._check_late_binding_closure(node, defnode)
+ self._loopvar_name(node, name)
+ break
+
+ found_node = current_consumer.get_next_to_consume(node)
+ if found_node is None:
+ continue
+
+ # checks for use before assignment
+ defnode = utils.assign_parent(current_consumer.to_consume[name][0])
+
+ if defnode is not None:
+ self._check_late_binding_closure(node, defnode)
+ defstmt = defnode.statement()
+ defframe = defstmt.frame()
+ # The class reuses itself in the class scope.
+ recursive_klass = (
+ frame is defframe
+ and defframe.parent_of(node)
+ and isinstance(defframe, astroid.ClassDef)
+ and node.name == defframe.name
+ )
+
+ if (
+ recursive_klass
+ and utils.is_inside_lambda(node)
+ and (
+ not utils.is_default_argument(node)
+ or node.scope().parent.scope() is not defframe
+ )
+ ):
+ # Self-referential class references are fine in lambda's --
+ # As long as they are not part of the default argument directly
+ # under the scope of the parent self-referring class.
+ # Example of valid default argument:
+ # class MyName3:
+ # myattr = 1
+ # mylambda3 = lambda: lambda a=MyName3: a
+ # Example of invalid default argument:
+ # class MyName4:
+ # myattr = 1
+ # mylambda4 = lambda a=MyName4: lambda: a
+
+ # If the above conditional is True,
+ # there is no possibility of undefined-variable
+ # Also do not consume class name
+ # (since consuming blocks subsequent checks)
+ # -- quit
+ break
+
+ maybee0601, annotation_return, use_outer_definition = self._is_variable_violation(
+ node,
+ name,
+ defnode,
+ stmt,
+ defstmt,
+ frame,
+ defframe,
+ base_scope_type,
+ recursive_klass,
+ )
+
+ if use_outer_definition:
+ continue
+
+ if (
+ maybee0601
+ and not utils.is_defined_before(node)
+ and not astroid.are_exclusive(stmt, defstmt, ("NameError",))
+ ):
+
+ # Used and defined in the same place, e.g `x += 1` and `del x`
+ defined_by_stmt = defstmt is stmt and isinstance(
+ node, (astroid.DelName, astroid.AssignName)
+ )
+ if (
+ recursive_klass
+ or defined_by_stmt
+ or annotation_return
+ or isinstance(defstmt, astroid.Delete)
+ ):
+ if not utils.node_ignores_exception(node, NameError):
+
+ # Handle postponed evaluation of annotations
+ if not (
+ self._postponed_evaluation_enabled
+ and isinstance(
+ stmt,
+ (
+ astroid.AnnAssign,
+ astroid.FunctionDef,
+ astroid.Arguments,
+ ),
+ )
+ and name in node.root().locals
+ ):
+ self.add_message(
+ "undefined-variable", args=name, node=node
+ )
+ elif base_scope_type != "lambda":
+ # E0601 may *not* occurs in lambda scope.
+
+ # Handle postponed evaluation of annotations
+ if not (
+ self._postponed_evaluation_enabled
+ and isinstance(
+ stmt, (astroid.AnnAssign, astroid.FunctionDef)
+ )
+ ):
+ self.add_message(
+ "used-before-assignment", args=name, node=node
+ )
+ elif base_scope_type == "lambda":
+ # E0601 can occur in class-level scope in lambdas, as in
+ # the following example:
+ # class A:
+ # x = lambda attr: f + attr
+ # f = 42
+ if isinstance(frame, astroid.ClassDef) and name in frame.locals:
+ if isinstance(node.parent, astroid.Arguments):
+ if stmt.fromlineno <= defstmt.fromlineno:
+ # Doing the following is fine:
+ # class A:
+ # x = 42
+ # y = lambda attr=x: attr
+ self.add_message(
+ "used-before-assignment", args=name, node=node
+ )
+ else:
+ self.add_message(
+ "undefined-variable", args=name, node=node
+ )
+ elif current_consumer.scope_type == "lambda":
+ self.add_message("undefined-variable", node=node, args=name)
+
+ current_consumer.mark_as_consumed(name, found_node)
+ # check it's not a loop variable used outside the loop
+ self._loopvar_name(node, name)
+ break
+ else:
+ # we have not found the name, if it isn't a builtin, that's an
+ # undefined name !
+ if not (
+ name in astroid.Module.scope_attrs
+ or utils.is_builtin(name)
+ or name in self.config.additional_builtins
+ ):
+ if not utils.node_ignores_exception(node, NameError):
+ self.add_message("undefined-variable", args=name, node=node)
+
+ @utils.check_messages("no-name-in-module")
+ def visit_import(self, node):
+ """check modules attribute accesses"""
+ if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node):
+ # No need to verify this, since ImportError is already
+ # handled by the client code.
+ return
+
+ for name, _ in node.names:
+ parts = name.split(".")
+ try:
+ module = next(_infer_name_module(node, parts[0]))
+ except astroid.ResolveError:
+ continue
+ self._check_module_attrs(node, module, parts[1:])
+
+ @utils.check_messages("no-name-in-module")
+ def visit_importfrom(self, node):
+ """check modules attribute accesses"""
+ if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node):
+ # No need to verify this, since ImportError is already
+ # handled by the client code.
+ return
+
+ name_parts = node.modname.split(".")
+ try:
+ module = node.do_import_module(name_parts[0])
+ except astroid.AstroidBuildingException:
+ return
+ module = self._check_module_attrs(node, module, name_parts[1:])
+ if not module:
+ return
+ for name, _ in node.names:
+ if name == "*":
+ continue
+ self._check_module_attrs(node, module, name.split("."))
+
+ @utils.check_messages(
+ "unbalanced-tuple-unpacking", "unpacking-non-sequence", "self-cls-assignment"
+ )
+ def visit_assign(self, node):
+ """Check unbalanced tuple unpacking for assignments
+ and unpacking non-sequences as well as in case self/cls
+ get assigned.
+ """
+ self._check_self_cls_assign(node)
+ if not isinstance(node.targets[0], (astroid.Tuple, astroid.List)):
+ return
+
+ targets = node.targets[0].itered()
+ try:
+ inferred = utils.safe_infer(node.value)
+ if inferred is not None:
+ self._check_unpacking(inferred, node, targets)
+ except astroid.InferenceError:
+ return
+
+ # listcomp have now also their scope
+ def visit_listcomp(self, node):
+ """visit dictcomp: update consumption analysis variable
+ """
+ self._to_consume.append(NamesConsumer(node, "comprehension"))
+
+ def leave_listcomp(self, _):
+ """leave dictcomp: update consumption analysis variable
+ """
+ # do not check for not used locals here
+ self._to_consume.pop()
+
+ def leave_assign(self, node):
+ self._store_type_annotation_names(node)
+
+ def leave_with(self, node):
+ self._store_type_annotation_names(node)
+
+ def visit_arguments(self, node):
+ for annotation in node.type_comment_args:
+ self._store_type_annotation_node(annotation)
+
+ # Relying on other checker's options, which might not have been initialized yet.
+ @decorators.cachedproperty
+ def _analyse_fallback_blocks(self):
+ return get_global_option(self, "analyse-fallback-blocks", default=False)
+
+ @decorators.cachedproperty
+ def _ignored_modules(self):
+ return get_global_option(self, "ignored-modules", default=[])
+
+ @decorators.cachedproperty
+ def _allow_global_unused_variables(self):
+ return get_global_option(self, "allow-global-unused-variables", default=True)
+
+ @staticmethod
+ def _defined_in_function_definition(node, frame):
+ in_annotation_or_default = False
+ if isinstance(frame, astroid.FunctionDef) and node.statement() is frame:
+ in_annotation_or_default = (
+ node in frame.args.annotations
+ or node in frame.args.kwonlyargs_annotations
+ or node is frame.args.varargannotation
+ or node is frame.args.kwargannotation
+ ) or frame.args.parent_of(node)
+ return in_annotation_or_default
+
+ @staticmethod
+ def _is_variable_violation(
+ node,
+ name,
+ defnode,
+ stmt,
+ defstmt,
+ frame,
+ defframe,
+ base_scope_type,
+ recursive_klass,
+ ):
+ # pylint: disable=too-many-nested-blocks
+ # node: Node to check for violation
+ # name: name of node to check violation for
+ # frame: Scope of statement of node
+ # base_scope_type: local scope type
+ maybee0601 = True
+ annotation_return = False
+ use_outer_definition = False
+ if frame is not defframe:
+ maybee0601 = _detect_global_scope(node, frame, defframe)
+ elif defframe.parent is None:
+ # we are at the module level, check the name is not
+ # defined in builtins
+ if name in defframe.scope_attrs or astroid.builtin_lookup(name)[1]:
+ maybee0601 = False
+ else:
+ # we are in a local scope, check the name is not
+ # defined in global or builtin scope
+ # skip this lookup if name is assigned later in function scope/lambda
+ # Note: the node.frame() is not the same as the `frame` argument which is
+ # equivalent to frame.statement().scope()
+ forbid_lookup = (
+ isinstance(frame, astroid.FunctionDef)
+ or isinstance(node.frame(), astroid.Lambda)
+ ) and _assigned_locally(node)
+ if not forbid_lookup and defframe.root().lookup(name)[1]:
+ maybee0601 = False
+ use_outer_definition = stmt == defstmt and not isinstance(
+ defnode, astroid.node_classes.Comprehension
+ )
+ else:
+ # check if we have a nonlocal
+ if name in defframe.locals:
+ maybee0601 = not any(
+ isinstance(child, astroid.Nonlocal) and name in child.names
+ for child in defframe.get_children()
+ )
+
+ if (
+ base_scope_type == "lambda"
+ and isinstance(frame, astroid.ClassDef)
+ and name in frame.locals
+ ):
+
+ # This rule verifies that if the definition node of the
+ # checked name is an Arguments node and if the name
+ # is used a default value in the arguments defaults
+ # and the actual definition of the variable label
+ # is happening before the Arguments definition.
+ #
+ # bar = None
+ # foo = lambda bar=bar: bar
+ #
+ # In this case, maybee0601 should be False, otherwise
+ # it should be True.
+ maybee0601 = not (
+ isinstance(defnode, astroid.Arguments)
+ and node in defnode.defaults
+ and frame.locals[name][0].fromlineno < defstmt.fromlineno
+ )
+ elif isinstance(defframe, astroid.ClassDef) and isinstance(
+ frame, astroid.FunctionDef
+ ):
+ # Special rule for function return annotations,
+ # which uses the same name as the class where
+ # the function lives.
+ if node is frame.returns and defframe.parent_of(frame.returns):
+ maybee0601 = annotation_return = True
+
+ if (
+ maybee0601
+ and defframe.name in defframe.locals
+ and defframe.locals[name][0].lineno < frame.lineno
+ ):
+ # Detect class assignments with the same
+ # name as the class. In this case, no warning
+ # should be raised.
+ maybee0601 = False
+ if isinstance(node.parent, astroid.Arguments):
+ maybee0601 = stmt.fromlineno <= defstmt.fromlineno
+ elif recursive_klass:
+ maybee0601 = True
+ else:
+ maybee0601 = maybee0601 and stmt.fromlineno <= defstmt.fromlineno
+ if maybee0601 and stmt.fromlineno == defstmt.fromlineno:
+ if (
+ isinstance(defframe, astroid.FunctionDef)
+ and frame is defframe
+ and defframe.parent_of(node)
+ and stmt is not defstmt
+ ):
+ # Single statement function, with the statement on the
+ # same line as the function definition
+ maybee0601 = False
+
+ # Look for type checking definitions inside a type checking guard.
+ if isinstance(defstmt, (astroid.Import, astroid.ImportFrom)):
+ defstmt_parent = defstmt.parent
+
+ if (
+ isinstance(defstmt_parent, astroid.If)
+ and defstmt_parent.test.as_string() in TYPING_TYPE_CHECKS_GUARDS
+ ):
+ # Exempt those definitions that are used inside the type checking
+ # guard or that are defined in both type checking guard branches.
+ used_in_branch = defstmt_parent.parent_of(node)
+ defined_in_or_else = False
+
+ for definition in defstmt_parent.orelse:
+ if isinstance(definition, astroid.Assign):
+ defined_in_or_else = any(
+ target.name == name for target in definition.targets
+ )
+ if defined_in_or_else:
+ break
+
+ if not used_in_branch and not defined_in_or_else:
+ maybee0601 = True
+
+ return maybee0601, annotation_return, use_outer_definition
+
+ def _ignore_class_scope(self, node):
+ """
+ Return True if the node is in a local class scope, as an assignment.
+
+ :param node: Node considered
+ :type node: astroid.Node
+ :return: True if the node is in a local class scope, as an assignment. False otherwise.
+ :rtype: bool
+ """
+ # Detect if we are in a local class scope, as an assignment.
+ # For example, the following is fair game.
+ #
+ # class A:
+ # b = 1
+ # c = lambda b=b: b * b
+ #
+ # class B:
+ # tp = 1
+ # def func(self, arg: tp):
+ # ...
+ # class C:
+ # tp = 2
+ # def func(self, arg=tp):
+ # ...
+
+ name = node.name
+ frame = node.statement().scope()
+ in_annotation_or_default = self._defined_in_function_definition(node, frame)
+ if in_annotation_or_default:
+ frame_locals = frame.parent.scope().locals
+ else:
+ frame_locals = frame.locals
+ return not (
+ (isinstance(frame, astroid.ClassDef) or in_annotation_or_default)
+ and name in frame_locals
+ )
+
+ def _loopvar_name(self, node, name):
+ # filter variables according to node's scope
+ if not self.linter.is_message_enabled("undefined-loop-variable"):
+ return
+ astmts = [stmt for stmt in node.lookup(name)[1] if hasattr(stmt, "assign_type")]
+ # If this variable usage exists inside a function definition
+ # that exists in the same loop,
+ # the usage is safe because the function will not be defined either if
+ # the variable is not defined.
+ scope = node.scope()
+ if isinstance(scope, astroid.FunctionDef) and any(
+ asmt.statement().parent_of(scope) for asmt in astmts
+ ):
+ return
+
+ # filter variables according their respective scope test is_statement
+ # and parent to avoid #74747. This is not a total fix, which would
+ # introduce a mechanism similar to special attribute lookup in
+ # modules. Also, in order to get correct inference in this case, the
+ # scope lookup rules would need to be changed to return the initial
+ # assignment (which does not exist in code per se) as well as any later
+ # modifications.
+ if (
+ not astmts
+ or (astmts[0].is_statement or astmts[0].parent)
+ and astmts[0].statement().parent_of(node)
+ ):
+ _astmts = []
+ else:
+ _astmts = astmts[:1]
+ for i, stmt in enumerate(astmts[1:]):
+ if astmts[i].statement().parent_of(stmt) and not in_for_else_branch(
+ astmts[i].statement(), stmt
+ ):
+ continue
+ _astmts.append(stmt)
+ astmts = _astmts
+ if len(astmts) != 1:
+ return
+
+ assign = astmts[0].assign_type()
+ if not (
+ isinstance(
+ assign, (astroid.For, astroid.Comprehension, astroid.GeneratorExp)
+ )
+ and assign.statement() is not node.statement()
+ ):
+ return
+
+ # For functions we can do more by inferring the length of the itered object
+ if not isinstance(assign, astroid.For):
+ self.add_message("undefined-loop-variable", args=name, node=node)
+ return
+
+ try:
+ inferred = next(assign.iter.infer())
+ except astroid.InferenceError:
+ self.add_message("undefined-loop-variable", args=name, node=node)
+ else:
+ if (
+ isinstance(inferred, astroid.Instance)
+ and inferred.qname() == BUILTIN_RANGE
+ ):
+ # Consider range() objects safe, even if they might not yield any results.
+ return
+
+ # Consider sequences.
+ sequences = (
+ astroid.List,
+ astroid.Tuple,
+ astroid.Dict,
+ astroid.Set,
+ objects.FrozenSet,
+ )
+ if not isinstance(inferred, sequences):
+ self.add_message("undefined-loop-variable", args=name, node=node)
+ return
+
+ elements = getattr(inferred, "elts", getattr(inferred, "items", []))
+ if not elements:
+ self.add_message("undefined-loop-variable", args=name, node=node)
+
+ def _check_is_unused(self, name, node, stmt, global_names, nonlocal_names):
+ # pylint: disable=too-many-branches
+ # Ignore some special names specified by user configuration.
+ if self._is_name_ignored(stmt, name):
+ return
+ # Ignore names that were added dynamically to the Function scope
+ if (
+ isinstance(node, astroid.FunctionDef)
+ and name == "__class__"
+ and len(node.locals["__class__"]) == 1
+ and isinstance(node.locals["__class__"][0], astroid.ClassDef)
+ ):
+ return
+
+ # Ignore names imported by the global statement.
+ if isinstance(stmt, (astroid.Global, astroid.Import, astroid.ImportFrom)):
+ # Detect imports, assigned to global statements.
+ if global_names and _import_name_is_global(stmt, global_names):
+ return
+
+ argnames = list(
+ itertools.chain(node.argnames(), [arg.name for arg in node.args.kwonlyargs])
+ )
+ # Care about functions with unknown argument (builtins)
+ if name in argnames:
+ self._check_unused_arguments(name, node, stmt, argnames)
+ else:
+ if stmt.parent and isinstance(
+ stmt.parent, (astroid.Assign, astroid.AnnAssign)
+ ):
+ if name in nonlocal_names:
+ return
+
+ qname = asname = None
+ if isinstance(stmt, (astroid.Import, astroid.ImportFrom)):
+ # Need the complete name, which we don't have in .locals.
+ if len(stmt.names) > 1:
+ import_names = next(
+ (names for names in stmt.names if name in names), None
+ )
+ else:
+ import_names = stmt.names[0]
+ if import_names:
+ qname, asname = import_names
+ name = asname or qname
+
+ if _has_locals_call_after_node(stmt, node.scope()):
+ message_name = "possibly-unused-variable"
+ else:
+ if isinstance(stmt, astroid.Import):
+ if asname is not None:
+ msg = "%s imported as %s" % (qname, asname)
+ else:
+ msg = "import %s" % name
+ self.add_message("unused-import", args=msg, node=stmt)
+ return
+ if isinstance(stmt, astroid.ImportFrom):
+ if asname is not None:
+ msg = "%s imported from %s as %s" % (
+ qname,
+ stmt.modname,
+ asname,
+ )
+ else:
+ msg = "%s imported from %s" % (name, stmt.modname)
+ self.add_message("unused-import", args=msg, node=stmt)
+ return
+ message_name = "unused-variable"
+
+ # Don't check function stubs created only for type information
+ if utils.is_overload_stub(node):
+ return
+
+ self.add_message(message_name, args=name, node=stmt)
+
+ def _is_name_ignored(self, stmt, name):
+ authorized_rgx = self.config.dummy_variables_rgx
+ if (
+ isinstance(stmt, astroid.AssignName)
+ and isinstance(stmt.parent, astroid.Arguments)
+ or isinstance(stmt, astroid.Arguments)
+ ):
+ regex = self.config.ignored_argument_names
+ else:
+ regex = authorized_rgx
+ return regex and regex.match(name)
+
+ def _check_unused_arguments(self, name, node, stmt, argnames):
+ is_method = node.is_method()
+ klass = node.parent.frame()
+ if is_method and isinstance(klass, astroid.ClassDef):
+ confidence = (
+ INFERENCE if utils.has_known_bases(klass) else INFERENCE_FAILURE
+ )
+ else:
+ confidence = HIGH
+
+ if is_method:
+ # Don't warn for the first argument of a (non static) method
+ if node.type != "staticmethod" and name == argnames[0]:
+ return
+ # Don't warn for argument of an overridden method
+ overridden = overridden_method(klass, node.name)
+ if overridden is not None and name in overridden.argnames():
+ return
+ if node.name in utils.PYMETHODS and node.name not in (
+ "__init__",
+ "__new__",
+ ):
+ return
+ # Don't check callback arguments
+ if any(
+ node.name.startswith(cb) or node.name.endswith(cb)
+ for cb in self.config.callbacks
+ ):
+ return
+ # Don't check arguments of singledispatch.register function.
+ if utils.is_registered_in_singledispatch_function(node):
+ return
+
+ # Don't check function stubs created only for type information
+ if utils.is_overload_stub(node):
+ return
+
+ # Don't check protocol classes
+ if utils.is_protocol_class(klass):
+ return
+
+ self.add_message("unused-argument", args=name, node=stmt, confidence=confidence)
+
+ def _check_late_binding_closure(self, node, assignment_node):
+ def _is_direct_lambda_call():
+ return (
+ isinstance(node_scope.parent, astroid.Call)
+ and node_scope.parent.func is node_scope
+ )
+
+ node_scope = node.scope()
+ if not isinstance(node_scope, (astroid.Lambda, astroid.FunctionDef)):
+ return
+ if isinstance(node.parent, astroid.Arguments):
+ return
+
+ if isinstance(assignment_node, astroid.Comprehension):
+ if assignment_node.parent.parent_of(node.scope()):
+ self.add_message("cell-var-from-loop", node=node, args=node.name)
+ else:
+ assign_scope = assignment_node.scope()
+ maybe_for = assignment_node
+ while not isinstance(maybe_for, astroid.For):
+ if maybe_for is assign_scope:
+ break
+ maybe_for = maybe_for.parent
+ else:
+ if (
+ maybe_for.parent_of(node_scope)
+ and not _is_direct_lambda_call()
+ and not isinstance(node_scope.statement(), astroid.Return)
+ ):
+ self.add_message("cell-var-from-loop", node=node, args=node.name)
+
+ def _should_ignore_redefined_builtin(self, stmt):
+ if not isinstance(stmt, astroid.ImportFrom):
+ return False
+ return stmt.modname in self.config.redefining_builtins_modules
+
+ def _has_homonym_in_upper_function_scope(self, node, index):
+ """
+ Return True if there is a node with the same name in the to_consume dict of an upper scope
+ and if that scope is a function
+
+ :param node: node to check for
+ :type node: astroid.Node
+ :param index: index of the current consumer inside self._to_consume
+ :type index: int
+ :return: True if there is a node with the same name in the to_consume dict of an upper scope
+ and if that scope is a function
+ :rtype: bool
+ """
+ for _consumer in self._to_consume[index - 1 :: -1]:
+ if _consumer.scope_type == "function" and node.name in _consumer.to_consume:
+ return True
+ return False
+
+ def _store_type_annotation_node(self, type_annotation):
+ """Given a type annotation, store all the name nodes it refers to"""
+ if isinstance(type_annotation, astroid.Name):
+ self._type_annotation_names.append(type_annotation.name)
+ return
+
+ if not isinstance(type_annotation, astroid.Subscript):
+ return
+
+ if (
+ isinstance(type_annotation.value, astroid.Attribute)
+ and isinstance(type_annotation.value.expr, astroid.Name)
+ and type_annotation.value.expr.name == TYPING_MODULE
+ ):
+ self._type_annotation_names.append(TYPING_MODULE)
+ return
+
+ self._type_annotation_names.extend(
+ annotation.name
+ for annotation in type_annotation.nodes_of_class(astroid.Name)
+ )
+
+ def _store_type_annotation_names(self, node):
+ type_annotation = node.type_annotation
+ if not type_annotation:
+ return
+ self._store_type_annotation_node(node.type_annotation)
+
+ def _check_self_cls_assign(self, node):
+ """Check that self/cls don't get assigned"""
+ assign_names = {
+ target.name
+ for target in node.targets
+ if isinstance(target, astroid.AssignName)
+ }
+ scope = node.scope()
+ nonlocals_with_same_name = any(
+ child
+ for child in scope.body
+ if isinstance(child, astroid.Nonlocal) and assign_names & set(child.names)
+ )
+ if nonlocals_with_same_name:
+ scope = node.scope().parent.scope()
+
+ if not (
+ isinstance(scope, astroid.scoped_nodes.FunctionDef)
+ and scope.is_method()
+ and "builtins.staticmethod" not in scope.decoratornames()
+ ):
+ return
+ argument_names = scope.argnames()
+ if not argument_names:
+ return
+ self_cls_name = argument_names[0]
+ target_assign_names = (
+ target.name
+ for target in node.targets
+ if isinstance(target, astroid.node_classes.AssignName)
+ )
+ if self_cls_name in target_assign_names:
+ self.add_message("self-cls-assignment", node=node, args=(self_cls_name))
+
+ def _check_unpacking(self, inferred, node, targets):
+ """ Check for unbalanced tuple unpacking
+ and unpacking non sequences.
+ """
+ if utils.is_inside_abstract_class(node):
+ return
+ if utils.is_comprehension(node):
+ return
+ if inferred is astroid.Uninferable:
+ return
+ if (
+ isinstance(inferred.parent, astroid.Arguments)
+ and isinstance(node.value, astroid.Name)
+ and node.value.name == inferred.parent.vararg
+ ):
+ # Variable-length argument, we can't determine the length.
+ return
+ if isinstance(inferred, (astroid.Tuple, astroid.List)):
+ # attempt to check unpacking is properly balanced
+ values = inferred.itered()
+ if len(targets) != len(values):
+ # Check if we have starred nodes.
+ if any(isinstance(target, astroid.Starred) for target in targets):
+ return
+ self.add_message(
+ "unbalanced-tuple-unpacking",
+ node=node,
+ args=(
+ _get_unpacking_extra_info(node, inferred),
+ len(targets),
+ len(values),
+ ),
+ )
+ # attempt to check unpacking may be possible (ie RHS is iterable)
+ else:
+ if not utils.is_iterable(inferred):
+ self.add_message(
+ "unpacking-non-sequence",
+ node=node,
+ args=(_get_unpacking_extra_info(node, inferred),),
+ )
+
+ def _check_module_attrs(self, node, module, module_names):
+ """check that module_names (list of string) are accessible through the
+ given module
+ if the latest access name corresponds to a module, return it
+ """
+ assert isinstance(module, astroid.Module), module
+ while module_names:
+ name = module_names.pop(0)
+ if name == "__dict__":
+ module = None
+ break
+ try:
+ module = next(module.getattr(name)[0].infer())
+ if module is astroid.Uninferable:
+ return None
+ except astroid.NotFoundError:
+ if module.name in self._ignored_modules:
+ return None
+ self.add_message(
+ "no-name-in-module", args=(name, module.name), node=node
+ )
+ return None
+ except astroid.InferenceError:
+ return None
+ if module_names:
+ modname = module.name if module else "__dict__"
+ self.add_message(
+ "no-name-in-module", node=node, args=(".".join(module_names), modname)
+ )
+ return None
+ if isinstance(module, astroid.Module):
+ return module
+ return None
+
+ def _check_all(self, node, not_consumed):
+ assigned = next(node.igetattr("__all__"))
+ if assigned is astroid.Uninferable:
+ return
+
+ for elt in getattr(assigned, "elts", ()):
+ try:
+ elt_name = next(elt.infer())
+ except astroid.InferenceError:
+ continue
+ if elt_name is astroid.Uninferable:
+ continue
+ if not elt_name.parent:
+ continue
+
+ if not isinstance(elt_name, astroid.Const) or not isinstance(
+ elt_name.value, str
+ ):
+ self.add_message("invalid-all-object", args=elt.as_string(), node=elt)
+ continue
+
+ elt_name = elt_name.value
+ # If elt is in not_consumed, remove it from not_consumed
+ if elt_name in not_consumed:
+ del not_consumed[elt_name]
+ continue
+
+ if elt_name not in node.locals:
+ if not node.package:
+ self.add_message(
+ "undefined-all-variable", args=(elt_name,), node=elt
+ )
+ else:
+ basename = os.path.splitext(node.file)[0]
+ if os.path.basename(basename) == "__init__":
+ name = node.name + "." + elt_name
+ try:
+ modutils.file_from_modpath(name.split("."))
+ except ImportError:
+ self.add_message(
+ "undefined-all-variable", args=(elt_name,), node=elt
+ )
+ except SyntaxError:
+ # don't yield a syntax-error warning,
+ # because it will be later yielded
+ # when the file will be checked
+ pass
+
+ def _check_globals(self, not_consumed):
+ if self._allow_global_unused_variables:
+ return
+ for name, nodes in not_consumed.items():
+ for node in nodes:
+ self.add_message("unused-variable", args=(name,), node=node)
+
+ def _check_imports(self, not_consumed):
+ local_names = _fix_dot_imports(not_consumed)
+ checked = set()
+ for name, stmt in local_names:
+ for imports in stmt.names:
+ real_name = imported_name = imports[0]
+ if imported_name == "*":
+ real_name = name
+ as_name = imports[1]
+ if real_name in checked:
+ continue
+ if name not in (real_name, as_name):
+ continue
+ checked.add(real_name)
+
+ if isinstance(stmt, astroid.Import) or (
+ isinstance(stmt, astroid.ImportFrom) and not stmt.modname
+ ):
+ if isinstance(stmt, astroid.ImportFrom) and SPECIAL_OBJ.search(
+ imported_name
+ ):
+ # Filter special objects (__doc__, __all__) etc.,
+ # because they can be imported for exporting.
+ continue
+
+ if imported_name in self._type_annotation_names:
+ # Most likely a typing import if it wasn't used so far.
+ continue
+
+ if as_name == "_":
+ continue
+ if as_name is None:
+ msg = "import %s" % imported_name
+ else:
+ msg = "%s imported as %s" % (imported_name, as_name)
+ if not _is_type_checking_import(stmt):
+ self.add_message("unused-import", args=msg, node=stmt)
+ elif isinstance(stmt, astroid.ImportFrom) and stmt.modname != FUTURE:
+ if SPECIAL_OBJ.search(imported_name):
+ # Filter special objects (__doc__, __all__) etc.,
+ # because they can be imported for exporting.
+ continue
+
+ if _is_from_future_import(stmt, name):
+ # Check if the name is in fact loaded from a
+ # __future__ import in another module.
+ continue
+
+ if imported_name in self._type_annotation_names:
+ # Most likely a typing import if it wasn't used so far.
+ continue
+
+ if imported_name == "*":
+ self.add_message("unused-wildcard-import", args=name, node=stmt)
+ else:
+ if as_name is None:
+ msg = "%s imported from %s" % (imported_name, stmt.modname)
+ else:
+ fields = (imported_name, stmt.modname, as_name)
+ msg = "%s imported from %s as %s" % fields
+ if not _is_type_checking_import(stmt):
+ self.add_message("unused-import", args=msg, node=stmt)
+ del self._to_consume
+
+ def _check_metaclasses(self, node):
+ """ Update consumption analysis for metaclasses. """
+ consumed = [] # [(scope_locals, consumed_key)]
+
+ for child_node in node.get_children():
+ if isinstance(child_node, astroid.ClassDef):
+ consumed.extend(self._check_classdef_metaclasses(child_node, node))
+
+ # Pop the consumed items, in order to avoid having
+ # unused-import and unused-variable false positives
+ for scope_locals, name in consumed:
+ scope_locals.pop(name, None)
+
+ def _check_classdef_metaclasses(self, klass, parent_node):
+ if not klass._metaclass:
+ # Skip if this class doesn't use explicitly a metaclass, but inherits it from ancestors
+ return []
+
+ consumed = [] # [(scope_locals, consumed_key)]
+ metaclass = klass.metaclass()
+
+ name = None
+ if isinstance(klass._metaclass, astroid.Name):
+ name = klass._metaclass.name
+ elif metaclass:
+ name = metaclass.root().name
+
+ found = None
+ name = METACLASS_NAME_TRANSFORMS.get(name, name)
+ if name:
+ # check enclosing scopes starting from most local
+ for scope_locals, _, _ in self._to_consume[::-1]:
+ found = scope_locals.get(name)
+ if found:
+ consumed.append((scope_locals, name))
+ break
+
+ if found is None and not metaclass:
+ name = None
+ if isinstance(klass._metaclass, astroid.Name):
+ name = klass._metaclass.name
+ elif isinstance(klass._metaclass, astroid.Attribute):
+ name = klass._metaclass.as_string()
+
+ if name is not None:
+ if not (
+ name in astroid.Module.scope_attrs
+ or utils.is_builtin(name)
+ or name in self.config.additional_builtins
+ or name in parent_node.locals
+ ):
+ self.add_message("undefined-variable", node=klass, args=(name,))
+
+ return consumed
+
+
+def register(linter):
+ """required method to auto register this checker"""
+ linter.register_checker(VariablesChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/config.py b/venv/Lib/site-packages/pylint/config.py
new file mode 100644
index 0000000..0925575
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/config.py
@@ -0,0 +1,913 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2010, 2012-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2008 pyves@crater.logilab.fr <pyves@crater.logilab.fr>
+# Copyright (c) 2010 Julien Jehannet <julien.jehannet@logilab.fr>
+# Copyright (c) 2013 Google, Inc.
+# Copyright (c) 2013 John McGehee <jmcgehee@altera.com>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Aru Sahni <arusahni@gmail.com>
+# Copyright (c) 2015 John Kirkham <jakirkham@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Erik <erik.eriksson@yahoo.com>
+# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2017-2018 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 ahirnish <ahirnish@gmail.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2018 Gary Tyler McLeod <mail@garytyler.com>
+# Copyright (c) 2018 Konstantin <Github@pheanex.de>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""utilities for Pylint configuration :
+
+* pylintrc
+* pylint.d (PYLINTHOME)
+"""
+import collections
+import configparser
+import contextlib
+import copy
+import io
+import optparse
+import os
+import pickle
+import re
+import sys
+import time
+from typing import Any, Dict, Tuple
+
+from pylint import utils
+
+USER_HOME = os.path.expanduser("~")
+if "PYLINTHOME" in os.environ:
+ PYLINT_HOME = os.environ["PYLINTHOME"]
+ if USER_HOME == "~":
+ USER_HOME = os.path.dirname(PYLINT_HOME)
+elif USER_HOME == "~":
+ PYLINT_HOME = ".pylint.d"
+else:
+ PYLINT_HOME = os.path.join(USER_HOME, ".pylint.d")
+
+
+def _get_pdata_path(base_name, recurs):
+ base_name = base_name.replace(os.sep, "_")
+ return os.path.join(PYLINT_HOME, "%s%s%s" % (base_name, recurs, ".stats"))
+
+
+def load_results(base):
+ data_file = _get_pdata_path(base, 1)
+ try:
+ with open(data_file, "rb") as stream:
+ return pickle.load(stream)
+ except Exception: # pylint: disable=broad-except
+ return {}
+
+
+def save_results(results, base):
+ if not os.path.exists(PYLINT_HOME):
+ try:
+ os.mkdir(PYLINT_HOME)
+ except OSError:
+ print("Unable to create directory %s" % PYLINT_HOME, file=sys.stderr)
+ data_file = _get_pdata_path(base, 1)
+ try:
+ with open(data_file, "wb") as stream:
+ pickle.dump(results, stream)
+ except (IOError, OSError) as ex:
+ print("Unable to create file %s: %s" % (data_file, ex), file=sys.stderr)
+
+
+def find_pylintrc():
+ """search the pylint rc file and return its path if it find it, else None
+ """
+ # is there a pylint rc file in the current directory ?
+ if os.path.exists("pylintrc"):
+ return os.path.abspath("pylintrc")
+ if os.path.exists(".pylintrc"):
+ return os.path.abspath(".pylintrc")
+ if os.path.isfile("__init__.py"):
+ curdir = os.path.abspath(os.getcwd())
+ while os.path.isfile(os.path.join(curdir, "__init__.py")):
+ curdir = os.path.abspath(os.path.join(curdir, ".."))
+ if os.path.isfile(os.path.join(curdir, "pylintrc")):
+ return os.path.join(curdir, "pylintrc")
+ if os.path.isfile(os.path.join(curdir, ".pylintrc")):
+ return os.path.join(curdir, ".pylintrc")
+ if "PYLINTRC" in os.environ and os.path.exists(os.environ["PYLINTRC"]):
+ pylintrc = os.environ["PYLINTRC"]
+ else:
+ user_home = os.path.expanduser("~")
+ if user_home in ("~", "/root"):
+ pylintrc = ".pylintrc"
+ else:
+ pylintrc = os.path.join(user_home, ".pylintrc")
+ if not os.path.isfile(pylintrc):
+ pylintrc = os.path.join(user_home, ".config", "pylintrc")
+ if not os.path.isfile(pylintrc):
+ if os.path.isfile("/etc/pylintrc"):
+ pylintrc = "/etc/pylintrc"
+ else:
+ pylintrc = None
+ return pylintrc
+
+
+PYLINTRC = find_pylintrc()
+
+ENV_HELP = (
+ """
+The following environment variables are used:
+ * PYLINTHOME
+ Path to the directory where persistent data for the run will be stored. If
+not found, it defaults to ~/.pylint.d/ or .pylint.d (in the current working
+directory).
+ * PYLINTRC
+ Path to the configuration file. See the documentation for the method used
+to search for configuration file.
+"""
+ % globals() # type: ignore
+)
+
+
+class UnsupportedAction(Exception):
+ """raised by set_option when it doesn't know what to do for an action"""
+
+
+def _multiple_choice_validator(choices, name, value):
+ values = utils._check_csv(value)
+ for csv_value in values:
+ if csv_value not in choices:
+ msg = "option %s: invalid value: %r, should be in %s"
+ raise optparse.OptionValueError(msg % (name, csv_value, choices))
+ return values
+
+
+def _choice_validator(choices, name, value):
+ if value not in choices:
+ msg = "option %s: invalid value: %r, should be in %s"
+ raise optparse.OptionValueError(msg % (name, value, choices))
+ return value
+
+
+# pylint: disable=unused-argument
+def _csv_validator(_, name, value):
+ return utils._check_csv(value)
+
+
+# pylint: disable=unused-argument
+def _regexp_validator(_, name, value):
+ if hasattr(value, "pattern"):
+ return value
+ return re.compile(value)
+
+
+# pylint: disable=unused-argument
+def _regexp_csv_validator(_, name, value):
+ return [_regexp_validator(_, name, val) for val in _csv_validator(_, name, value)]
+
+
+def _yn_validator(opt, _, value):
+ if isinstance(value, int):
+ return bool(value)
+ if value in ("y", "yes"):
+ return True
+ if value in ("n", "no"):
+ return False
+ msg = "option %s: invalid yn value %r, should be in (y, yes, n, no)"
+ raise optparse.OptionValueError(msg % (opt, value))
+
+
+def _non_empty_string_validator(opt, _, value):
+ if not value:
+ msg = "indent string can't be empty."
+ raise optparse.OptionValueError(msg)
+ return utils._unquote(value)
+
+
+VALIDATORS = {
+ "string": utils._unquote,
+ "int": int,
+ "regexp": re.compile,
+ "regexp_csv": _regexp_csv_validator,
+ "csv": _csv_validator,
+ "yn": _yn_validator,
+ "choice": lambda opt, name, value: _choice_validator(opt["choices"], name, value),
+ "multiple_choice": lambda opt, name, value: _multiple_choice_validator(
+ opt["choices"], name, value
+ ),
+ "non_empty_string": _non_empty_string_validator,
+}
+
+
+def _call_validator(opttype, optdict, option, value):
+ if opttype not in VALIDATORS:
+ raise Exception('Unsupported type "%s"' % opttype)
+ try:
+ return VALIDATORS[opttype](optdict, option, value)
+ except TypeError:
+ try:
+ return VALIDATORS[opttype](value)
+ except Exception:
+ raise optparse.OptionValueError(
+ "%s value (%r) should be of type %s" % (option, value, opttype)
+ )
+
+
+def _validate(value, optdict, name=""):
+ """return a validated value for an option according to its type
+
+ optional argument name is only used for error message formatting
+ """
+ try:
+ _type = optdict["type"]
+ except KeyError:
+ return value
+ return _call_validator(_type, optdict, name, value)
+
+
+def _level_options(group, outputlevel):
+ return [
+ option
+ for option in group.option_list
+ if (getattr(option, "level", 0) or 0) <= outputlevel
+ and option.help is not optparse.SUPPRESS_HELP
+ ]
+
+
+def _expand_default(self, option):
+ """Patch OptionParser.expand_default with custom behaviour
+
+ This will handle defaults to avoid overriding values in the
+ configuration file.
+ """
+ if self.parser is None or not self.default_tag:
+ return option.help
+ optname = option._long_opts[0][2:]
+ try:
+ provider = self.parser.options_manager._all_options[optname]
+ except KeyError:
+ value = None
+ else:
+ optdict = provider.get_option_def(optname)
+ optname = provider.option_attrname(optname, optdict)
+ value = getattr(provider.config, optname, optdict)
+ value = utils._format_option_value(optdict, value)
+ if value is optparse.NO_DEFAULT or not value:
+ value = self.NO_DEFAULT_VALUE
+ return option.help.replace(self.default_tag, str(value))
+
+
+@contextlib.contextmanager
+def _patch_optparse():
+ orig_default = optparse.HelpFormatter
+ try:
+ optparse.HelpFormatter.expand_default = _expand_default
+ yield
+ finally:
+ optparse.HelpFormatter.expand_default = orig_default
+
+
+def _multiple_choices_validating_option(opt, name, value):
+ return _multiple_choice_validator(opt.choices, name, value)
+
+
+# pylint: disable=no-member
+class Option(optparse.Option):
+ TYPES = optparse.Option.TYPES + (
+ "regexp",
+ "regexp_csv",
+ "csv",
+ "yn",
+ "multiple_choice",
+ "non_empty_string",
+ )
+ ATTRS = optparse.Option.ATTRS + ["hide", "level"]
+ TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
+ TYPE_CHECKER["regexp"] = _regexp_validator
+ TYPE_CHECKER["regexp_csv"] = _regexp_csv_validator
+ TYPE_CHECKER["csv"] = _csv_validator
+ TYPE_CHECKER["yn"] = _yn_validator
+ TYPE_CHECKER["multiple_choice"] = _multiple_choices_validating_option
+ TYPE_CHECKER["non_empty_string"] = _non_empty_string_validator
+
+ def __init__(self, *opts, **attrs):
+ optparse.Option.__init__(self, *opts, **attrs)
+ if hasattr(self, "hide") and self.hide:
+ self.help = optparse.SUPPRESS_HELP
+
+ def _check_choice(self):
+ if self.type in ("choice", "multiple_choice"):
+ if self.choices is None:
+ raise optparse.OptionError(
+ "must supply a list of choices for type 'choice'", self
+ )
+ if not isinstance(self.choices, (tuple, list)):
+ raise optparse.OptionError(
+ "choices must be a list of strings ('%s' supplied)"
+ % str(type(self.choices)).split("'")[1],
+ self,
+ )
+ elif self.choices is not None:
+ raise optparse.OptionError(
+ "must not supply choices for type %r" % self.type, self
+ )
+
+ # pylint: disable=unsupported-assignment-operation
+ optparse.Option.CHECK_METHODS[2] = _check_choice # type: ignore
+
+ def process(self, opt, value, values, parser):
+ # First, convert the value(s) to the right type. Howl if any
+ # value(s) are bogus.
+ value = self.convert_value(opt, value)
+ if self.type == "named":
+ existent = getattr(values, self.dest)
+ if existent:
+ existent.update(value)
+ value = existent
+ # And then take whatever action is expected of us.
+ # This is a separate method to make life easier for
+ # subclasses to add new actions.
+ return self.take_action(self.action, self.dest, opt, value, values, parser)
+
+
+class OptionParser(optparse.OptionParser):
+ def __init__(self, option_class, *args, **kwargs):
+ optparse.OptionParser.__init__(self, option_class=Option, *args, **kwargs)
+
+ def format_option_help(self, formatter=None):
+ if formatter is None:
+ formatter = self.formatter
+ outputlevel = getattr(formatter, "output_level", 0)
+ formatter.store_option_strings(self)
+ result = []
+ result.append(formatter.format_heading("Options"))
+ formatter.indent()
+ if self.option_list:
+ result.append(optparse.OptionContainer.format_option_help(self, formatter))
+ result.append("\n")
+ for group in self.option_groups:
+ if group.level <= outputlevel and (
+ group.description or _level_options(group, outputlevel)
+ ):
+ result.append(group.format_help(formatter))
+ result.append("\n")
+ formatter.dedent()
+ # Drop the last "\n", or the header if no options or option groups:
+ return "".join(result[:-1])
+
+ def _match_long_opt(self, opt):
+ """Disable abbreviations."""
+ if opt not in self._long_opt:
+ raise optparse.BadOptionError(opt)
+ return opt
+
+
+# pylint: disable=abstract-method; by design?
+class _ManHelpFormatter(optparse.HelpFormatter):
+ def __init__(
+ self, indent_increment=0, max_help_position=24, width=79, short_first=0
+ ):
+ optparse.HelpFormatter.__init__(
+ self, indent_increment, max_help_position, width, short_first
+ )
+
+ def format_heading(self, heading):
+ return ".SH %s\n" % heading.upper()
+
+ def format_description(self, description):
+ return description
+
+ def format_option(self, option):
+ try:
+ optstring = option.option_strings
+ except AttributeError:
+ optstring = self.format_option_strings(option)
+ if option.help:
+ help_text = self.expand_default(option)
+ help_string = " ".join([l.strip() for l in help_text.splitlines()])
+ help_string = help_string.replace("\\", "\\\\")
+ help_string = help_string.replace("[current:", "[default:")
+ else:
+ help_string = ""
+ return """.IP "%s"
+%s
+""" % (
+ optstring,
+ help_string,
+ )
+
+ def format_head(self, optparser, pkginfo, section=1):
+ long_desc = ""
+ try:
+ pgm = optparser._get_prog_name()
+ except AttributeError:
+ # py >= 2.4.X (dunno which X exactly, at least 2)
+ pgm = optparser.get_prog_name()
+ short_desc = self.format_short_description(pgm, pkginfo.description)
+ if hasattr(pkginfo, "long_desc"):
+ long_desc = self.format_long_description(pgm, pkginfo.long_desc)
+ return "%s\n%s\n%s\n%s" % (
+ self.format_title(pgm, section),
+ short_desc,
+ self.format_synopsis(pgm),
+ long_desc,
+ )
+
+ @staticmethod
+ def format_title(pgm, section):
+ date = "%d-%02d-%02d" % time.localtime()[:3]
+ return '.TH %s %s "%s" %s' % (pgm, section, date, pgm)
+
+ @staticmethod
+ def format_short_description(pgm, short_desc):
+ return """.SH NAME
+.B %s
+\\- %s
+""" % (
+ pgm,
+ short_desc.strip(),
+ )
+
+ @staticmethod
+ def format_synopsis(pgm):
+ return (
+ """.SH SYNOPSIS
+.B %s
+[
+.I OPTIONS
+] [
+.I <arguments>
+]
+"""
+ % pgm
+ )
+
+ @staticmethod
+ def format_long_description(pgm, long_desc):
+ long_desc = "\n".join(line.lstrip() for line in long_desc.splitlines())
+ long_desc = long_desc.replace("\n.\n", "\n\n")
+ if long_desc.lower().startswith(pgm):
+ long_desc = long_desc[len(pgm) :]
+ return """.SH DESCRIPTION
+.B %s
+%s
+""" % (
+ pgm,
+ long_desc.strip(),
+ )
+
+ @staticmethod
+ def format_tail(pkginfo):
+ tail = """.SH SEE ALSO
+/usr/share/doc/pythonX.Y-%s/
+
+.SH BUGS
+Please report bugs on the project\'s mailing list:
+%s
+
+.SH AUTHOR
+%s <%s>
+""" % (
+ getattr(pkginfo, "debian_name", pkginfo.modname),
+ pkginfo.mailinglist,
+ pkginfo.author,
+ pkginfo.author_email,
+ )
+
+ if hasattr(pkginfo, "copyright"):
+ tail += (
+ """
+.SH COPYRIGHT
+%s
+"""
+ % pkginfo.copyright
+ )
+
+ return tail
+
+
+class OptionsManagerMixIn:
+ """Handle configuration from both a configuration file and command line options"""
+
+ def __init__(self, usage, config_file=None, version=None):
+ self.config_file = config_file
+ self.reset_parsers(usage, version=version)
+ # list of registered options providers
+ self.options_providers = []
+ # dictionary associating option name to checker
+ self._all_options = collections.OrderedDict()
+ self._short_options = {}
+ self._nocallback_options = {}
+ self._mygroups = {}
+ # verbosity
+ self._maxlevel = 0
+
+ def reset_parsers(self, usage="", version=None):
+ # configuration file parser
+ self.cfgfile_parser = configparser.ConfigParser(
+ inline_comment_prefixes=("#", ";")
+ )
+ # command line parser
+ self.cmdline_parser = OptionParser(Option, usage=usage, version=version)
+ self.cmdline_parser.options_manager = self
+ self._optik_option_attrs = set(self.cmdline_parser.option_class.ATTRS)
+
+ def register_options_provider(self, provider, own_group=True):
+ """register an options provider"""
+ assert provider.priority <= 0, "provider's priority can't be >= 0"
+ for i in range(len(self.options_providers)):
+ if provider.priority > self.options_providers[i].priority:
+ self.options_providers.insert(i, provider)
+ break
+ else:
+ self.options_providers.append(provider)
+ non_group_spec_options = [
+ option for option in provider.options if "group" not in option[1]
+ ]
+ groups = getattr(provider, "option_groups", ())
+ if own_group and non_group_spec_options:
+ self.add_option_group(
+ provider.name.upper(),
+ provider.__doc__,
+ non_group_spec_options,
+ provider,
+ )
+ else:
+ for opt, optdict in non_group_spec_options:
+ self.add_optik_option(provider, self.cmdline_parser, opt, optdict)
+ for gname, gdoc in groups:
+ gname = gname.upper()
+ goptions = [
+ option
+ for option in provider.options
+ if option[1].get("group", "").upper() == gname
+ ]
+ self.add_option_group(gname, gdoc, goptions, provider)
+
+ def add_option_group(self, group_name, _, options, provider):
+ # add option group to the command line parser
+ if group_name in self._mygroups:
+ group = self._mygroups[group_name]
+ else:
+ group = optparse.OptionGroup(
+ self.cmdline_parser, title=group_name.capitalize()
+ )
+ self.cmdline_parser.add_option_group(group)
+ group.level = provider.level
+ self._mygroups[group_name] = group
+ # add section to the config file
+ if (
+ group_name != "DEFAULT"
+ and group_name not in self.cfgfile_parser._sections
+ ):
+ self.cfgfile_parser.add_section(group_name)
+ # add provider's specific options
+ for opt, optdict in options:
+ self.add_optik_option(provider, group, opt, optdict)
+
+ def add_optik_option(self, provider, optikcontainer, opt, optdict):
+ args, optdict = self.optik_option(provider, opt, optdict)
+ option = optikcontainer.add_option(*args, **optdict)
+ self._all_options[opt] = provider
+ self._maxlevel = max(self._maxlevel, option.level or 0)
+
+ def optik_option(self, provider, opt, optdict):
+ """get our personal option definition and return a suitable form for
+ use with optik/optparse
+ """
+ optdict = copy.copy(optdict)
+ if "action" in optdict:
+ self._nocallback_options[provider] = opt
+ else:
+ optdict["action"] = "callback"
+ optdict["callback"] = self.cb_set_provider_option
+ # default is handled here and *must not* be given to optik if you
+ # want the whole machinery to work
+ if "default" in optdict:
+ if (
+ "help" in optdict
+ and optdict.get("default") is not None
+ and optdict["action"] not in ("store_true", "store_false")
+ ):
+ optdict["help"] += " [current: %default]"
+ del optdict["default"]
+ args = ["--" + str(opt)]
+ if "short" in optdict:
+ self._short_options[optdict["short"]] = opt
+ args.append("-" + optdict["short"])
+ del optdict["short"]
+ # cleanup option definition dict before giving it to optik
+ for key in list(optdict.keys()):
+ if key not in self._optik_option_attrs:
+ optdict.pop(key)
+ return args, optdict
+
+ def cb_set_provider_option(self, option, opt, value, parser):
+ """optik callback for option setting"""
+ if opt.startswith("--"):
+ # remove -- on long option
+ opt = opt[2:]
+ else:
+ # short option, get its long equivalent
+ opt = self._short_options[opt[1:]]
+ # trick since we can't set action='store_true' on options
+ if value is None:
+ value = 1
+ self.global_set_option(opt, value)
+
+ def global_set_option(self, opt, value):
+ """set option on the correct option provider"""
+ self._all_options[opt].set_option(opt, value)
+
+ def generate_config(self, stream=None, skipsections=(), encoding=None):
+ """write a configuration file according to the current configuration
+ into the given stream or stdout
+ """
+ options_by_section = {}
+ sections = []
+ for provider in self.options_providers:
+ for section, options in provider.options_by_section():
+ if section is None:
+ section = provider.name
+ if section in skipsections:
+ continue
+ options = [
+ (n, d, v)
+ for (n, d, v) in options
+ if d.get("type") is not None and not d.get("deprecated")
+ ]
+ if not options:
+ continue
+ if section not in sections:
+ sections.append(section)
+ alloptions = options_by_section.setdefault(section, [])
+ alloptions += options
+ stream = stream or sys.stdout
+ printed = False
+ for section in sections:
+ if printed:
+ print("\n", file=stream)
+ utils.format_section(
+ stream, section.upper(), sorted(options_by_section[section])
+ )
+ printed = True
+
+ def generate_manpage(self, pkginfo, section=1, stream=None):
+ with _patch_optparse():
+ _generate_manpage(
+ self.cmdline_parser,
+ pkginfo,
+ section,
+ stream=stream or sys.stdout,
+ level=self._maxlevel,
+ )
+
+ def load_provider_defaults(self):
+ """initialize configuration using default values"""
+ for provider in self.options_providers:
+ provider.load_defaults()
+
+ def read_config_file(self, config_file=None, verbose=None):
+ """read the configuration file but do not load it (i.e. dispatching
+ values to each options provider)
+ """
+ helplevel = 1
+ while helplevel <= self._maxlevel:
+ opt = "-".join(["long"] * helplevel) + "-help"
+ if opt in self._all_options:
+ break # already processed
+ # pylint: disable=unused-argument
+ def helpfunc(option, opt, val, p, level=helplevel):
+ print(self.help(level))
+ sys.exit(0)
+
+ helpmsg = "%s verbose help." % " ".join(["more"] * helplevel)
+ optdict = {"action": "callback", "callback": helpfunc, "help": helpmsg}
+ provider = self.options_providers[0]
+ self.add_optik_option(provider, self.cmdline_parser, opt, optdict)
+ provider.options += ((opt, optdict),)
+ helplevel += 1
+ if config_file is None:
+ config_file = self.config_file
+ if config_file is not None:
+ config_file = os.path.expanduser(config_file)
+ if not os.path.exists(config_file):
+ raise IOError("The config file {:s} doesn't exist!".format(config_file))
+
+ use_config_file = config_file and os.path.exists(config_file)
+ if use_config_file:
+ parser = self.cfgfile_parser
+
+ # Use this encoding in order to strip the BOM marker, if any.
+ with io.open(config_file, "r", encoding="utf_8_sig") as fp:
+ parser.read_file(fp)
+
+ # normalize sections'title
+ for sect, values in list(parser._sections.items()):
+ if not sect.isupper() and values:
+ parser._sections[sect.upper()] = values
+
+ if not verbose:
+ return
+
+ if use_config_file:
+ msg = "Using config file {}".format(os.path.abspath(config_file))
+ else:
+ msg = "No config file found, using default configuration"
+ print(msg, file=sys.stderr)
+
+ def load_config_file(self):
+ """dispatch values previously read from a configuration file to each
+ options provider)
+ """
+ parser = self.cfgfile_parser
+ for section in parser.sections():
+ for option, value in parser.items(section):
+ try:
+ self.global_set_option(option, value)
+ except (KeyError, optparse.OptionError):
+ continue
+
+ def load_configuration(self, **kwargs):
+ """override configuration according to given parameters"""
+ return self.load_configuration_from_config(kwargs)
+
+ def load_configuration_from_config(self, config):
+ for opt, opt_value in config.items():
+ opt = opt.replace("_", "-")
+ provider = self._all_options[opt]
+ provider.set_option(opt, opt_value)
+
+ def load_command_line_configuration(self, args=None):
+ """Override configuration according to command line parameters
+
+ return additional arguments
+ """
+ with _patch_optparse():
+ if args is None:
+ args = sys.argv[1:]
+ else:
+ args = list(args)
+ (options, args) = self.cmdline_parser.parse_args(args=args)
+ for provider in self._nocallback_options:
+ config = provider.config
+ for attr in config.__dict__.keys():
+ value = getattr(options, attr, None)
+ if value is None:
+ continue
+ setattr(config, attr, value)
+ return args
+
+ def add_help_section(self, title, description, level=0):
+ """add a dummy option section for help purpose """
+ group = optparse.OptionGroup(
+ self.cmdline_parser, title=title.capitalize(), description=description
+ )
+ group.level = level
+ self._maxlevel = max(self._maxlevel, level)
+ self.cmdline_parser.add_option_group(group)
+
+ def help(self, level=0):
+ """return the usage string for available options """
+ self.cmdline_parser.formatter.output_level = level
+ with _patch_optparse():
+ return self.cmdline_parser.format_help()
+
+
+class OptionsProviderMixIn:
+ """Mixin to provide options to an OptionsManager"""
+
+ # those attributes should be overridden
+ priority = -1
+ name = "default"
+ options = () # type: Tuple[Tuple[str, Dict[str, Any]], ...]
+ level = 0
+
+ def __init__(self):
+ self.config = optparse.Values()
+ self.load_defaults()
+
+ def load_defaults(self):
+ """initialize the provider using default values"""
+ for opt, optdict in self.options:
+ action = optdict.get("action")
+ if action != "callback":
+ # callback action have no default
+ if optdict is None:
+ optdict = self.get_option_def(opt)
+ default = optdict.get("default")
+ self.set_option(opt, default, action, optdict)
+
+ def option_attrname(self, opt, optdict=None):
+ """get the config attribute corresponding to opt"""
+ if optdict is None:
+ optdict = self.get_option_def(opt)
+ return optdict.get("dest", opt.replace("-", "_"))
+
+ def option_value(self, opt):
+ """get the current value for the given option"""
+ return getattr(self.config, self.option_attrname(opt), None)
+
+ def set_option(self, optname, value, action=None, optdict=None):
+ """method called to set an option (registered in the options list)"""
+ if optdict is None:
+ optdict = self.get_option_def(optname)
+ if value is not None:
+ value = _validate(value, optdict, optname)
+ if action is None:
+ action = optdict.get("action", "store")
+ if action == "store":
+ setattr(self.config, self.option_attrname(optname, optdict), value)
+ elif action in ("store_true", "count"):
+ setattr(self.config, self.option_attrname(optname, optdict), 0)
+ elif action == "store_false":
+ setattr(self.config, self.option_attrname(optname, optdict), 1)
+ elif action == "append":
+ optname = self.option_attrname(optname, optdict)
+ _list = getattr(self.config, optname, None)
+ if _list is None:
+ if isinstance(value, (list, tuple)):
+ _list = value
+ elif value is not None:
+ _list = []
+ _list.append(value)
+ setattr(self.config, optname, _list)
+ elif isinstance(_list, tuple):
+ setattr(self.config, optname, _list + (value,))
+ else:
+ _list.append(value)
+ elif action == "callback":
+ optdict["callback"](None, optname, value, None)
+ else:
+ raise UnsupportedAction(action)
+
+ def get_option_def(self, opt):
+ """return the dictionary defining an option given its name"""
+ assert self.options
+ for option in self.options:
+ if option[0] == opt:
+ return option[1]
+ raise optparse.OptionError(
+ "no such option %s in section %r" % (opt, self.name), opt
+ )
+
+ def options_by_section(self):
+ """return an iterator on options grouped by section
+
+ (section, [list of (optname, optdict, optvalue)])
+ """
+ sections = {}
+ for optname, optdict in self.options:
+ sections.setdefault(optdict.get("group"), []).append(
+ (optname, optdict, self.option_value(optname))
+ )
+ if None in sections:
+ yield None, sections.pop(None)
+ for section, options in sorted(sections.items()):
+ yield section.upper(), options
+
+ def options_and_values(self, options=None):
+ if options is None:
+ options = self.options
+ for optname, optdict in options:
+ yield (optname, optdict, self.option_value(optname))
+
+
+class ConfigurationMixIn(OptionsManagerMixIn, OptionsProviderMixIn):
+ """basic mixin for simple configurations which don't need the
+ manager / providers model
+ """
+
+ def __init__(self, *args, **kwargs):
+ if not args:
+ kwargs.setdefault("usage", "")
+ OptionsManagerMixIn.__init__(self, *args, **kwargs)
+ OptionsProviderMixIn.__init__(self)
+ if not getattr(self, "option_groups", None):
+ self.option_groups = []
+ for _, optdict in self.options:
+ try:
+ gdef = (optdict["group"].upper(), "")
+ except KeyError:
+ continue
+ if gdef not in self.option_groups:
+ self.option_groups.append(gdef)
+ self.register_options_provider(self, own_group=False)
+
+
+def _generate_manpage(optparser, pkginfo, section=1, stream=sys.stdout, level=0):
+ formatter = _ManHelpFormatter()
+ formatter.output_level = level
+ formatter.parser = optparser
+ print(formatter.format_head(optparser, pkginfo, section), file=stream)
+ print(optparser.format_option_help(formatter), file=stream)
+ print(formatter.format_tail(pkginfo), file=stream)
diff --git a/venv/Lib/site-packages/pylint/constants.py b/venv/Lib/site-packages/pylint/constants.py
new file mode 100644
index 0000000..852fc15
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/constants.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import re
+
+# Allow stopping after the first semicolon/hash encountered,
+# so that an option can be continued with the reasons
+# why it is active or disabled.
+OPTION_RGX = re.compile(r"\s*#.*\bpylint:\s*([^;#]+)[;#]{0,1}")
+
+PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll")
+
+MSG_STATE_CONFIDENCE = 2
+_MSG_ORDER = "EWRCIF"
+MSG_STATE_SCOPE_CONFIG = 0
+MSG_STATE_SCOPE_MODULE = 1
+
+# The line/node distinction does not apply to fatal errors and reports.
+_SCOPE_EXEMPT = "FR"
+
+MSG_TYPES = {
+ "I": "info",
+ "C": "convention",
+ "R": "refactor",
+ "W": "warning",
+ "E": "error",
+ "F": "fatal",
+}
+MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.items()}
+
+MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1}
+
+# You probably don't want to change the MAIN_CHECKER_NAME
+# This would affect rcfile generation and retro-compatibility
+# on all project using [MASTER] in their rcfile.
+MAIN_CHECKER_NAME = "master"
+
+
+class WarningScope:
+ LINE = "line-based-msg"
+ NODE = "node-based-msg"
diff --git a/venv/Lib/site-packages/pylint/epylint.py b/venv/Lib/site-packages/pylint/epylint.py
new file mode 100644
index 0000000..85f1c86
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/epylint.py
@@ -0,0 +1,197 @@
+# -*- coding: utf-8;
+# mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4
+# -*- vim:fenc=utf-8:ft=python:et:sw=4:ts=4:sts=4
+
+# Copyright (c) 2008-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Jakob Normark <jakobnormark@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Manuel Vázquez Acosta <mva.led@gmail.com>
+# Copyright (c) 2014 Derek Harland <derek.harland@finq.co.nz>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Mihai Balint <balint.mihai@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Daniela Plascencia <daplascen@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Ryan McGuire <ryan@enigmacurry.com>
+# Copyright (c) 2018 thernstig <30827238+thernstig@users.noreply.github.com>
+# Copyright (c) 2018 Radostin Stoyanov <rst0git@users.noreply.github.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Emacs and Flymake compatible Pylint.
+
+This script is for integration with emacs and is compatible with flymake mode.
+
+epylint walks out of python packages before invoking pylint. This avoids
+reporting import errors that occur when a module within a package uses the
+absolute import path to get another module within this package.
+
+For example:
+ - Suppose a package is structured as
+
+ a/__init__.py
+ a/b/x.py
+ a/c/y.py
+
+ - Then if y.py imports x as "from a.b import x" the following produces pylint
+ errors
+
+ cd a/c; pylint y.py
+
+ - The following obviously doesn't
+
+ pylint a/c/y.py
+
+ - As this script will be invoked by emacs within the directory of the file
+ we are checking we need to go out of it to avoid these false positives.
+
+
+You may also use py_run to run pylint with desired options and get back (or not)
+its output.
+"""
+import os
+import os.path as osp
+import shlex
+import sys
+from io import StringIO
+from subprocess import PIPE, Popen
+
+
+def _get_env():
+ """Extracts the environment PYTHONPATH and appends the current sys.path to
+ those."""
+ env = dict(os.environ)
+ env["PYTHONPATH"] = os.pathsep.join(sys.path)
+ return env
+
+
+def lint(filename, options=()):
+ """Pylint the given file.
+
+ When run from emacs we will be in the directory of a file, and passed its
+ filename. If this file is part of a package and is trying to import other
+ modules from within its own package or another package rooted in a directory
+ below it, pylint will classify it as a failed import.
+
+ To get around this, we traverse down the directory tree to find the root of
+ the package this module is in. We then invoke pylint from this directory.
+
+ Finally, we must correct the filenames in the output generated by pylint so
+ Emacs doesn't become confused (it will expect just the original filename,
+ while pylint may extend it with extra directories if we've traversed down
+ the tree)
+ """
+ # traverse downwards until we are out of a python package
+ full_path = osp.abspath(filename)
+ parent_path = osp.dirname(full_path)
+ child_path = osp.basename(full_path)
+
+ while parent_path != "/" and osp.exists(osp.join(parent_path, "__init__.py")):
+ child_path = osp.join(osp.basename(parent_path), child_path)
+ parent_path = osp.dirname(parent_path)
+
+ # Start pylint
+ # Ensure we use the python and pylint associated with the running epylint
+ run_cmd = "import sys; from pylint.lint import Run; Run(sys.argv[1:])"
+ cmd = (
+ [sys.executable, "-c", run_cmd]
+ + [
+ "--msg-template",
+ "{path}:{line}: {category} ({msg_id}, {symbol}, {obj}) {msg}",
+ "-r",
+ "n",
+ child_path,
+ ]
+ + list(options)
+ )
+ process = Popen(
+ cmd, stdout=PIPE, cwd=parent_path, env=_get_env(), universal_newlines=True
+ )
+
+ for line in process.stdout:
+ # remove pylintrc warning
+ if line.startswith("No config file found"):
+ continue
+
+ # modify the file name thats output to reverse the path traversal we made
+ parts = line.split(":")
+ if parts and parts[0] == child_path:
+ line = ":".join([filename] + parts[1:])
+ print(line, end=" ")
+
+ process.wait()
+ return process.returncode
+
+
+def py_run(command_options="", return_std=False, stdout=None, stderr=None):
+ """Run pylint from python
+
+ ``command_options`` is a string containing ``pylint`` command line options;
+ ``return_std`` (boolean) indicates return of created standard output
+ and error (see below);
+ ``stdout`` and ``stderr`` are 'file-like' objects in which standard output
+ could be written.
+
+ Calling agent is responsible for stdout/err management (creation, close).
+ Default standard output and error are those from sys,
+ or standalone ones (``subprocess.PIPE``) are used
+ if they are not set and ``return_std``.
+
+ If ``return_std`` is set to ``True``, this function returns a 2-uple
+ containing standard output and error related to created process,
+ as follows: ``(stdout, stderr)``.
+
+ To silently run Pylint on a module, and get its standard output and error:
+ >>> (pylint_stdout, pylint_stderr) = py_run( 'module_name.py', True)
+ """
+ # Detect if we use Python as executable or not, else default to `python`
+ executable = sys.executable if "python" in sys.executable else "python"
+
+ # Create command line to call pylint
+ epylint_part = [executable, "-c", "from pylint import epylint;epylint.Run()"]
+ options = shlex.split(command_options, posix=not sys.platform.startswith("win"))
+ cli = epylint_part + options
+
+ # Providing standard output and/or error if not set
+ if stdout is None:
+ if return_std:
+ stdout = PIPE
+ else:
+ stdout = sys.stdout
+ if stderr is None:
+ if return_std:
+ stderr = PIPE
+ else:
+ stderr = sys.stderr
+ # Call pylint in a subprocess
+ process = Popen(
+ cli,
+ shell=False,
+ stdout=stdout,
+ stderr=stderr,
+ env=_get_env(),
+ universal_newlines=True,
+ )
+ proc_stdout, proc_stderr = process.communicate()
+ # Return standard output and error
+ if return_std:
+ return StringIO(proc_stdout), StringIO(proc_stderr)
+ return None
+
+
+def Run():
+ if len(sys.argv) == 1:
+ print("Usage: %s <filename> [options]" % sys.argv[0])
+ sys.exit(1)
+ elif not osp.exists(sys.argv[1]):
+ print("%s does not exist" % sys.argv[1])
+ sys.exit(1)
+ else:
+ sys.exit(lint(sys.argv[1], sys.argv[2:]))
+
+
+if __name__ == "__main__":
+ Run()
diff --git a/venv/Lib/site-packages/pylint/exceptions.py b/venv/Lib/site-packages/pylint/exceptions.py
new file mode 100644
index 0000000..d5dd17f
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/exceptions.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Exception classes raised by various operations within pylint."""
+
+
+class InvalidMessageError(Exception):
+ """raised when a message creation, registration or addition is rejected"""
+
+
+class UnknownMessageError(Exception):
+ """raised when an unregistered message id is encountered"""
+
+
+class EmptyReportError(Exception):
+ """raised when a report is empty and so should not be displayed"""
+
+
+class InvalidReporterError(Exception):
+ """raised when selected reporter is invalid (e.g. not found)"""
+
+
+class InvalidArgsError(ValueError):
+ """raised when passed arguments are invalid, e.g., have the wrong length"""
diff --git a/venv/Lib/site-packages/pylint/extensions/__init__.py b/venv/Lib/site-packages/pylint/extensions/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__init__.py
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..03323e7
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/_check_docs_utils.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/_check_docs_utils.cpython-37.pyc
new file mode 100644
index 0000000..271e216
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/_check_docs_utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/bad_builtin.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/bad_builtin.cpython-37.pyc
new file mode 100644
index 0000000..bb50903
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/bad_builtin.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/broad_try_clause.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/broad_try_clause.cpython-37.pyc
new file mode 100644
index 0000000..cd3cd71
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/broad_try_clause.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/check_docs.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/check_docs.cpython-37.pyc
new file mode 100644
index 0000000..9730100
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/check_docs.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/check_elif.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/check_elif.cpython-37.pyc
new file mode 100644
index 0000000..030378b
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/check_elif.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/comparetozero.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/comparetozero.cpython-37.pyc
new file mode 100644
index 0000000..83eaae3
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/comparetozero.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/docparams.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/docparams.cpython-37.pyc
new file mode 100644
index 0000000..3d447e1
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/docparams.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/docstyle.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/docstyle.cpython-37.pyc
new file mode 100644
index 0000000..e6d0d7d
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/docstyle.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/emptystring.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/emptystring.cpython-37.pyc
new file mode 100644
index 0000000..f5f4892
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/emptystring.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/mccabe.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/mccabe.cpython-37.pyc
new file mode 100644
index 0000000..cb64a4d
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/mccabe.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/overlapping_exceptions.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/overlapping_exceptions.cpython-37.pyc
new file mode 100644
index 0000000..f099683
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/overlapping_exceptions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/__pycache__/redefined_variable_type.cpython-37.pyc b/venv/Lib/site-packages/pylint/extensions/__pycache__/redefined_variable_type.cpython-37.pyc
new file mode 100644
index 0000000..eb897a3
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/__pycache__/redefined_variable_type.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/extensions/_check_docs_utils.py b/venv/Lib/site-packages/pylint/extensions/_check_docs_utils.py
new file mode 100644
index 0000000..fe1603f
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/_check_docs_utils.py
@@ -0,0 +1,792 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2016-2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Yuri Bochkarev <baltazar.bz@gmail.com>
+# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Mitar <mitar.github@tnode.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2018 Mitchell T.H. Young <mitchelly@gmail.com>
+# Copyright (c) 2018 Adrian Chirieac <chirieacam@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Utility methods for docstring checking."""
+
+import re
+
+import astroid
+
+from pylint.checkers import utils
+
+
+def space_indentation(s):
+ """The number of leading spaces in a string
+
+ :param str s: input string
+
+ :rtype: int
+ :return: number of leading spaces
+ """
+ return len(s) - len(s.lstrip(" "))
+
+
+def get_setters_property_name(node):
+ """Get the name of the property that the given node is a setter for.
+
+ :param node: The node to get the property name for.
+ :type node: str
+
+ :rtype: str or None
+ :returns: The name of the property that the node is a setter for,
+ or None if one could not be found.
+ """
+ decorators = node.decorators.nodes if node.decorators else []
+ for decorator in decorators:
+ if (
+ isinstance(decorator, astroid.Attribute)
+ and decorator.attrname == "setter"
+ and isinstance(decorator.expr, astroid.Name)
+ ):
+ return decorator.expr.name
+ return None
+
+
+def get_setters_property(node):
+ """Get the property node for the given setter node.
+
+ :param node: The node to get the property for.
+ :type node: astroid.FunctionDef
+
+ :rtype: astroid.FunctionDef or None
+ :returns: The node relating to the property of the given setter node,
+ or None if one could not be found.
+ """
+ property_ = None
+
+ property_name = get_setters_property_name(node)
+ class_node = utils.node_frame_class(node)
+ if property_name and class_node:
+ class_attrs = class_node.getattr(node.name)
+ for attr in class_attrs:
+ if utils.decorated_with_property(attr):
+ property_ = attr
+ break
+
+ return property_
+
+
+def returns_something(return_node):
+ """Check if a return node returns a value other than None.
+
+ :param return_node: The return node to check.
+ :type return_node: astroid.Return
+
+ :rtype: bool
+ :return: True if the return node returns a value other than None,
+ False otherwise.
+ """
+ returns = return_node.value
+
+ if returns is None:
+ return False
+
+ return not (isinstance(returns, astroid.Const) and returns.value is None)
+
+
+def _get_raise_target(node):
+ if isinstance(node.exc, astroid.Call):
+ func = node.exc.func
+ if isinstance(func, (astroid.Name, astroid.Attribute)):
+ return utils.safe_infer(func)
+ return None
+
+
+def possible_exc_types(node):
+ """
+ Gets all of the possible raised exception types for the given raise node.
+
+ .. note::
+
+ Caught exception types are ignored.
+
+
+ :param node: The raise node to find exception types for.
+ :type node: astroid.node_classes.NodeNG
+
+ :returns: A list of exception types possibly raised by :param:`node`.
+ :rtype: set(str)
+ """
+ excs = []
+ if isinstance(node.exc, astroid.Name):
+ inferred = utils.safe_infer(node.exc)
+ if inferred:
+ excs = [inferred.name]
+ elif node.exc is None:
+ handler = node.parent
+ while handler and not isinstance(handler, astroid.ExceptHandler):
+ handler = handler.parent
+
+ if handler and handler.type:
+ inferred_excs = astroid.unpack_infer(handler.type)
+ excs = (exc.name for exc in inferred_excs if exc is not astroid.Uninferable)
+ else:
+ target = _get_raise_target(node)
+ if isinstance(target, astroid.ClassDef):
+ excs = [target.name]
+ elif isinstance(target, astroid.FunctionDef):
+ for ret in target.nodes_of_class(astroid.Return):
+ if ret.frame() != target:
+ # return from inner function - ignore it
+ continue
+
+ val = utils.safe_infer(ret.value)
+ if (
+ val
+ and isinstance(val, (astroid.Instance, astroid.ClassDef))
+ and utils.inherit_from_std_ex(val)
+ ):
+ excs.append(val.name)
+
+ try:
+ return {exc for exc in excs if not utils.node_ignores_exception(node, exc)}
+ except astroid.InferenceError:
+ return set()
+
+
+def docstringify(docstring, default_type="default"):
+ for docstring_type in [
+ SphinxDocstring,
+ EpytextDocstring,
+ GoogleDocstring,
+ NumpyDocstring,
+ ]:
+ instance = docstring_type(docstring)
+ if instance.is_valid():
+ return instance
+
+ docstring_type = DOCSTRING_TYPES.get(default_type, Docstring)
+ return docstring_type(docstring)
+
+
+class Docstring:
+ re_for_parameters_see = re.compile(
+ r"""
+ For\s+the\s+(other)?\s*parameters\s*,\s+see
+ """,
+ re.X | re.S,
+ )
+
+ supports_yields = None
+ """True if the docstring supports a "yield" section.
+
+ False if the docstring uses the returns section to document generators.
+ """
+
+ # These methods are designed to be overridden
+ # pylint: disable=no-self-use
+ def __init__(self, doc):
+ doc = doc or ""
+ self.doc = doc.expandtabs()
+
+ def is_valid(self):
+ return False
+
+ def exceptions(self):
+ return set()
+
+ def has_params(self):
+ return False
+
+ def has_returns(self):
+ return False
+
+ def has_rtype(self):
+ return False
+
+ def has_property_returns(self):
+ return False
+
+ def has_property_type(self):
+ return False
+
+ def has_yields(self):
+ return False
+
+ def has_yields_type(self):
+ return False
+
+ def match_param_docs(self):
+ return set(), set()
+
+ def params_documented_elsewhere(self):
+ return self.re_for_parameters_see.search(self.doc) is not None
+
+
+class SphinxDocstring(Docstring):
+ re_type = r"""
+ [~!.]? # Optional link style prefix
+ \w(?:\w|\.[^\.])* # Valid python name
+ """
+
+ re_simple_container_type = r"""
+ {type} # a container type
+ [\(\[] [^\n\s]+ [\)\]] # with the contents of the container
+ """.format(
+ type=re_type
+ )
+
+ re_xref = r"""
+ (?::\w+:)? # optional tag
+ `{}` # what to reference
+ """.format(
+ re_type
+ )
+
+ re_param_raw = r"""
+ : # initial colon
+ (?: # Sphinx keywords
+ param|parameter|
+ arg|argument|
+ key|keyword
+ )
+ \s+ # whitespace
+
+ (?: # optional type declaration
+ ({type}|{container_type})
+ \s+
+ )?
+
+ (\w+) # Parameter name
+ \s* # whitespace
+ : # final colon
+ """.format(
+ type=re_type, container_type=re_simple_container_type
+ )
+ re_param_in_docstring = re.compile(re_param_raw, re.X | re.S)
+
+ re_type_raw = r"""
+ :type # Sphinx keyword
+ \s+ # whitespace
+ ({type}) # Parameter name
+ \s* # whitespace
+ : # final colon
+ """.format(
+ type=re_type
+ )
+ re_type_in_docstring = re.compile(re_type_raw, re.X | re.S)
+
+ re_property_type_raw = r"""
+ :type: # Sphinx keyword
+ \s+ # whitespace
+ {type} # type declaration
+ """.format(
+ type=re_type
+ )
+ re_property_type_in_docstring = re.compile(re_property_type_raw, re.X | re.S)
+
+ re_raise_raw = r"""
+ : # initial colon
+ (?: # Sphinx keyword
+ raises?|
+ except|exception
+ )
+ \s+ # whitespace
+ ({type}) # exception type
+ \s* # whitespace
+ : # final colon
+ """.format(
+ type=re_type
+ )
+ re_raise_in_docstring = re.compile(re_raise_raw, re.X | re.S)
+
+ re_rtype_in_docstring = re.compile(r":rtype:")
+
+ re_returns_in_docstring = re.compile(r":returns?:")
+
+ supports_yields = False
+
+ def is_valid(self):
+ return bool(
+ self.re_param_in_docstring.search(self.doc)
+ or self.re_raise_in_docstring.search(self.doc)
+ or self.re_rtype_in_docstring.search(self.doc)
+ or self.re_returns_in_docstring.search(self.doc)
+ or self.re_property_type_in_docstring.search(self.doc)
+ )
+
+ def exceptions(self):
+ types = set()
+
+ for match in re.finditer(self.re_raise_in_docstring, self.doc):
+ raise_type = match.group(1)
+ types.add(raise_type)
+
+ return types
+
+ def has_params(self):
+ if not self.doc:
+ return False
+
+ return self.re_param_in_docstring.search(self.doc) is not None
+
+ def has_returns(self):
+ if not self.doc:
+ return False
+
+ return bool(self.re_returns_in_docstring.search(self.doc))
+
+ def has_rtype(self):
+ if not self.doc:
+ return False
+
+ return bool(self.re_rtype_in_docstring.search(self.doc))
+
+ def has_property_returns(self):
+ if not self.doc:
+ return False
+
+ # The summary line is the return doc,
+ # so the first line must not be a known directive.
+ return not self.doc.lstrip().startswith(":")
+
+ def has_property_type(self):
+ if not self.doc:
+ return False
+
+ return bool(self.re_property_type_in_docstring.search(self.doc))
+
+ def match_param_docs(self):
+ params_with_doc = set()
+ params_with_type = set()
+
+ for match in re.finditer(self.re_param_in_docstring, self.doc):
+ name = match.group(2)
+ params_with_doc.add(name)
+ param_type = match.group(1)
+ if param_type is not None:
+ params_with_type.add(name)
+
+ params_with_type.update(re.findall(self.re_type_in_docstring, self.doc))
+ return params_with_doc, params_with_type
+
+
+class EpytextDocstring(SphinxDocstring):
+ """
+ Epytext is similar to Sphinx. See the docs:
+ http://epydoc.sourceforge.net/epytext.html
+ http://epydoc.sourceforge.net/fields.html#fields
+
+ It's used in PyCharm:
+ https://www.jetbrains.com/help/pycharm/2016.1/creating-documentation-comments.html#d848203e314
+ https://www.jetbrains.com/help/pycharm/2016.1/using-docstrings-to-specify-types.html
+ """
+
+ re_param_in_docstring = re.compile(
+ SphinxDocstring.re_param_raw.replace(":", "@", 1), re.X | re.S
+ )
+
+ re_type_in_docstring = re.compile(
+ SphinxDocstring.re_type_raw.replace(":", "@", 1), re.X | re.S
+ )
+
+ re_property_type_in_docstring = re.compile(
+ SphinxDocstring.re_property_type_raw.replace(":", "@", 1), re.X | re.S
+ )
+
+ re_raise_in_docstring = re.compile(
+ SphinxDocstring.re_raise_raw.replace(":", "@", 1), re.X | re.S
+ )
+
+ re_rtype_in_docstring = re.compile(
+ r"""
+ @ # initial "at" symbol
+ (?: # Epytext keyword
+ rtype|returntype
+ )
+ : # final colon
+ """,
+ re.X | re.S,
+ )
+
+ re_returns_in_docstring = re.compile(r"@returns?:")
+
+ def has_property_returns(self):
+ if not self.doc:
+ return False
+
+ # If this is a property docstring, the summary is the return doc.
+ if self.has_property_type():
+ # The summary line is the return doc,
+ # so the first line must not be a known directive.
+ return not self.doc.lstrip().startswith("@")
+
+ return False
+
+
+class GoogleDocstring(Docstring):
+ re_type = SphinxDocstring.re_type
+
+ re_xref = SphinxDocstring.re_xref
+
+ re_container_type = r"""
+ (?:{type}|{xref}) # a container type
+ [\(\[] [^\n]+ [\)\]] # with the contents of the container
+ """.format(
+ type=re_type, xref=re_xref
+ )
+
+ re_multiple_type = r"""
+ (?:{container_type}|{type}|{xref})
+ (?:\s+(?:of|or)\s+(?:{container_type}|{type}|{xref}))*
+ """.format(
+ type=re_type, xref=re_xref, container_type=re_container_type
+ )
+
+ _re_section_template = r"""
+ ^([ ]*) {0} \s*: \s*$ # Google parameter header
+ ( .* ) # section
+ """
+
+ re_param_section = re.compile(
+ _re_section_template.format(r"(?:Args|Arguments|Parameters)"),
+ re.X | re.S | re.M,
+ )
+
+ re_keyword_param_section = re.compile(
+ _re_section_template.format(r"Keyword\s(?:Args|Arguments|Parameters)"),
+ re.X | re.S | re.M,
+ )
+
+ re_param_line = re.compile(
+ r"""
+ \s* \*{{0,2}}(\w+) # identifier potentially with asterisks
+ \s* ( [(]
+ {type}
+ (?:,\s+optional)?
+ [)] )? \s* : # optional type declaration
+ \s* (.*) # beginning of optional description
+ """.format(
+ type=re_multiple_type
+ ),
+ re.X | re.S | re.M,
+ )
+
+ re_raise_section = re.compile(
+ _re_section_template.format(r"Raises"), re.X | re.S | re.M
+ )
+
+ re_raise_line = re.compile(
+ r"""
+ \s* ({type}) \s* : # identifier
+ \s* (.*) # beginning of optional description
+ """.format(
+ type=re_type
+ ),
+ re.X | re.S | re.M,
+ )
+
+ re_returns_section = re.compile(
+ _re_section_template.format(r"Returns?"), re.X | re.S | re.M
+ )
+
+ re_returns_line = re.compile(
+ r"""
+ \s* ({type}:)? # identifier
+ \s* (.*) # beginning of description
+ """.format(
+ type=re_multiple_type
+ ),
+ re.X | re.S | re.M,
+ )
+
+ re_property_returns_line = re.compile(
+ r"""
+ ^{type}: # indentifier
+ \s* (.*) # Summary line / description
+ """.format(
+ type=re_multiple_type
+ ),
+ re.X | re.S | re.M,
+ )
+
+ re_yields_section = re.compile(
+ _re_section_template.format(r"Yields?"), re.X | re.S | re.M
+ )
+
+ re_yields_line = re_returns_line
+
+ supports_yields = True
+
+ def is_valid(self):
+ return bool(
+ self.re_param_section.search(self.doc)
+ or self.re_raise_section.search(self.doc)
+ or self.re_returns_section.search(self.doc)
+ or self.re_yields_section.search(self.doc)
+ or self.re_property_returns_line.search(self._first_line())
+ )
+
+ def has_params(self):
+ if not self.doc:
+ return False
+
+ return self.re_param_section.search(self.doc) is not None
+
+ def has_returns(self):
+ if not self.doc:
+ return False
+
+ entries = self._parse_section(self.re_returns_section)
+ for entry in entries:
+ match = self.re_returns_line.match(entry)
+ if not match:
+ continue
+
+ return_desc = match.group(2)
+ if return_desc:
+ return True
+
+ return False
+
+ def has_rtype(self):
+ if not self.doc:
+ return False
+
+ entries = self._parse_section(self.re_returns_section)
+ for entry in entries:
+ match = self.re_returns_line.match(entry)
+ if not match:
+ continue
+
+ return_type = match.group(1)
+ if return_type:
+ return True
+
+ return False
+
+ def has_property_returns(self):
+ # The summary line is the return doc,
+ # so the first line must not be a known directive.
+ first_line = self._first_line()
+ return not bool(
+ self.re_param_section.search(first_line)
+ or self.re_raise_section.search(first_line)
+ or self.re_returns_section.search(first_line)
+ or self.re_yields_section.search(first_line)
+ )
+
+ def has_property_type(self):
+ if not self.doc:
+ return False
+
+ return bool(self.re_property_returns_line.match(self._first_line()))
+
+ def has_yields(self):
+ if not self.doc:
+ return False
+
+ entries = self._parse_section(self.re_yields_section)
+ for entry in entries:
+ match = self.re_yields_line.match(entry)
+ if not match:
+ continue
+
+ yield_desc = match.group(2)
+ if yield_desc:
+ return True
+
+ return False
+
+ def has_yields_type(self):
+ if not self.doc:
+ return False
+
+ entries = self._parse_section(self.re_yields_section)
+ for entry in entries:
+ match = self.re_yields_line.match(entry)
+ if not match:
+ continue
+
+ yield_type = match.group(1)
+ if yield_type:
+ return True
+
+ return False
+
+ def exceptions(self):
+ types = set()
+
+ entries = self._parse_section(self.re_raise_section)
+ for entry in entries:
+ match = self.re_raise_line.match(entry)
+ if not match:
+ continue
+
+ exc_type = match.group(1)
+ exc_desc = match.group(2)
+ if exc_desc:
+ types.add(exc_type)
+
+ return types
+
+ def match_param_docs(self):
+ params_with_doc = set()
+ params_with_type = set()
+
+ entries = self._parse_section(self.re_param_section)
+ entries.extend(self._parse_section(self.re_keyword_param_section))
+ for entry in entries:
+ match = self.re_param_line.match(entry)
+ if not match:
+ continue
+
+ param_name = match.group(1)
+ param_type = match.group(2)
+ param_desc = match.group(3)
+ if param_type:
+ params_with_type.add(param_name)
+
+ if param_desc:
+ params_with_doc.add(param_name)
+
+ return params_with_doc, params_with_type
+
+ def _first_line(self):
+ return self.doc.lstrip().split("\n", 1)[0]
+
+ @staticmethod
+ def min_section_indent(section_match):
+ return len(section_match.group(1)) + 1
+
+ @staticmethod
+ def _is_section_header(_):
+ # Google parsing does not need to detect section headers,
+ # because it works off of indentation level only
+ return False
+
+ def _parse_section(self, section_re):
+ section_match = section_re.search(self.doc)
+ if section_match is None:
+ return []
+
+ min_indentation = self.min_section_indent(section_match)
+
+ entries = []
+ entry = []
+ is_first = True
+ for line in section_match.group(2).splitlines():
+ if not line.strip():
+ continue
+ indentation = space_indentation(line)
+ if indentation < min_indentation:
+ break
+
+ # The first line after the header defines the minimum
+ # indentation.
+ if is_first:
+ min_indentation = indentation
+ is_first = False
+
+ if indentation == min_indentation:
+ if self._is_section_header(line):
+ break
+ # Lines with minimum indentation must contain the beginning
+ # of a new parameter documentation.
+ if entry:
+ entries.append("\n".join(entry))
+ entry = []
+
+ entry.append(line)
+
+ if entry:
+ entries.append("\n".join(entry))
+
+ return entries
+
+
+class NumpyDocstring(GoogleDocstring):
+ _re_section_template = r"""
+ ^([ ]*) {0} \s*?$ # Numpy parameters header
+ \s* [-=]+ \s*?$ # underline
+ ( .* ) # section
+ """
+
+ re_param_section = re.compile(
+ _re_section_template.format(r"(?:Args|Arguments|Parameters)"),
+ re.X | re.S | re.M,
+ )
+
+ re_param_line = re.compile(
+ r"""
+ \s* (\w+) # identifier
+ \s* :
+ \s* (?:({type})(?:,\s+optional)?)? # optional type declaration
+ \n # description starts on a new line
+ \s* (.*) # description
+ """.format(
+ type=GoogleDocstring.re_multiple_type
+ ),
+ re.X | re.S,
+ )
+
+ re_raise_section = re.compile(
+ _re_section_template.format(r"Raises"), re.X | re.S | re.M
+ )
+
+ re_raise_line = re.compile(
+ r"""
+ \s* ({type})$ # type declaration
+ \s* (.*) # optional description
+ """.format(
+ type=GoogleDocstring.re_type
+ ),
+ re.X | re.S | re.M,
+ )
+
+ re_returns_section = re.compile(
+ _re_section_template.format(r"Returns?"), re.X | re.S | re.M
+ )
+
+ re_returns_line = re.compile(
+ r"""
+ \s* (?:\w+\s+:\s+)? # optional name
+ ({type})$ # type declaration
+ \s* (.*) # optional description
+ """.format(
+ type=GoogleDocstring.re_multiple_type
+ ),
+ re.X | re.S | re.M,
+ )
+
+ re_yields_section = re.compile(
+ _re_section_template.format(r"Yields?"), re.X | re.S | re.M
+ )
+
+ re_yields_line = re_returns_line
+
+ supports_yields = True
+
+ @staticmethod
+ def min_section_indent(section_match):
+ return len(section_match.group(1))
+
+ @staticmethod
+ def _is_section_header(line):
+ return bool(re.match(r"\s*-+$", line))
+
+
+DOCSTRING_TYPES = {
+ "sphinx": SphinxDocstring,
+ "epytext": EpytextDocstring,
+ "google": GoogleDocstring,
+ "numpy": NumpyDocstring,
+ "default": Docstring,
+}
+"""A map of the name of the docstring type to its class.
+
+:type: dict(str, type)
+"""
diff --git a/venv/Lib/site-packages/pylint/extensions/bad_builtin.py b/venv/Lib/site-packages/pylint/extensions/bad_builtin.py
new file mode 100644
index 0000000..754c409
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/bad_builtin.py
@@ -0,0 +1,69 @@
+# Copyright (c) 2016 Claudiu Popa <pcmanticore@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Checker for deprecated builtins."""
+import astroid
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import check_messages
+from pylint.interfaces import IAstroidChecker
+
+BAD_FUNCTIONS = ["map", "filter"]
+# Some hints regarding the use of bad builtins.
+BUILTIN_HINTS = {"map": "Using a list comprehension can be clearer."}
+BUILTIN_HINTS["filter"] = BUILTIN_HINTS["map"]
+
+
+class BadBuiltinChecker(BaseChecker):
+
+ __implements__ = (IAstroidChecker,)
+ name = "deprecated_builtins"
+ msgs = {
+ "W0141": (
+ "Used builtin function %s",
+ "bad-builtin",
+ "Used when a black listed builtin function is used (see the "
+ "bad-function option). Usual black listed functions are the ones "
+ "like map, or filter , where Python offers now some cleaner "
+ "alternative like list comprehension.",
+ )
+ }
+
+ options = (
+ (
+ "bad-functions",
+ {
+ "default": BAD_FUNCTIONS,
+ "type": "csv",
+ "metavar": "<builtin function names>",
+ "help": "List of builtins function names that should not be "
+ "used, separated by a comma",
+ },
+ ),
+ )
+
+ @check_messages("bad-builtin")
+ def visit_call(self, node):
+ if isinstance(node.func, astroid.Name):
+ name = node.func.name
+ # ignore the name if it's not a builtin (i.e. not defined in the
+ # locals nor globals scope)
+ if not (name in node.frame() or name in node.root()):
+ if name in self.config.bad_functions:
+ hint = BUILTIN_HINTS.get(name)
+ if hint:
+ args = "%r. %s" % (name, hint)
+ else:
+ args = repr(name)
+ self.add_message("bad-builtin", node=node, args=args)
+
+
+def register(linter):
+ """Required method to auto register this checker.
+
+ :param linter: Main interface object for Pylint plugins
+ :type linter: Pylint object
+ """
+ linter.register_checker(BadBuiltinChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/broad_try_clause.py b/venv/Lib/site-packages/pylint/extensions/broad_try_clause.py
new file mode 100644
index 0000000..9a61fb6
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/broad_try_clause.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019 Tyler N. Thieding <python@thieding.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Looks for try/except statements with too much code in the try clause."""
+
+from pylint import checkers, interfaces
+
+
+class BroadTryClauseChecker(checkers.BaseChecker):
+ """Checks for try clauses with too many lines.
+
+ According to PEP 8, ``try`` clauses shall contain the absolute minimum
+ amount of code. This checker enforces a maximum number of statements within
+ ``try`` clauses.
+
+ """
+
+ __implements__ = interfaces.IAstroidChecker
+
+ # configuration section name
+ name = "broad_try_clause"
+ msgs = {
+ "W0717": (
+ "%s",
+ "too-many-try-statements",
+ "Try clause contains too many statements.",
+ )
+ }
+
+ priority = -2
+ options = (
+ (
+ "max-try-statements",
+ {
+ "default": 1,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "Maximum number of statements allowed in a try clause",
+ },
+ ),
+ )
+
+ def visit_tryexcept(self, node):
+ try_clause_statements = len(node.body)
+ if try_clause_statements > self.config.max_try_statements:
+ msg = "try clause contains {0} statements, expected at most {1}".format(
+ try_clause_statements, self.config.max_try_statements
+ )
+ self.add_message(
+ "too-many-try-statements", node.lineno, node=node, args=msg
+ )
+
+
+def register(linter):
+ """Required method to auto register this checker."""
+ linter.register_checker(BroadTryClauseChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/check_docs.py b/venv/Lib/site-packages/pylint/extensions/check_docs.py
new file mode 100644
index 0000000..7f7f643
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/check_docs.py
@@ -0,0 +1,23 @@
+# Copyright (c) 2014-2015 Bruno Daniel <bruno.daniel@blue-yonder.com>
+# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import warnings
+
+from pylint.extensions import docparams
+
+
+def register(linter):
+ """Required method to auto register this checker.
+
+ :param linter: Main interface object for Pylint plugins
+ :type linter: Pylint object
+ """
+ warnings.warn(
+ "This plugin is deprecated, use pylint.extensions.docparams instead.",
+ DeprecationWarning,
+ )
+ linter.register_checker(docparams.DocstringParameterChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/check_elif.py b/venv/Lib/site-packages/pylint/extensions/check_elif.py
new file mode 100644
index 0000000..67555b1
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/check_elif.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Glenn Matthews <glmatthe@cisco.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import astroid
+
+from pylint.checkers import BaseTokenChecker
+from pylint.checkers.utils import check_messages
+from pylint.interfaces import IAstroidChecker, ITokenChecker
+
+
+class ElseifUsedChecker(BaseTokenChecker):
+ """Checks for use of "else if" when an "elif" could be used
+ """
+
+ __implements__ = (ITokenChecker, IAstroidChecker)
+ name = "else_if_used"
+ msgs = {
+ "R5501": (
+ 'Consider using "elif" instead of "else if"',
+ "else-if-used",
+ "Used when an else statement is immediately followed by "
+ "an if statement and does not contain statements that "
+ "would be unrelated to it.",
+ )
+ }
+
+ def __init__(self, linter=None):
+ BaseTokenChecker.__init__(self, linter)
+ self._init()
+
+ def _init(self):
+ self._elifs = []
+ self._if_counter = 0
+
+ def process_tokens(self, tokens):
+ # Process tokens and look for 'if' or 'elif'
+ for _, token, _, _, _ in tokens:
+ if token == "elif":
+ self._elifs.append(True)
+ elif token == "if":
+ self._elifs.append(False)
+
+ def leave_module(self, _):
+ self._init()
+
+ def visit_ifexp(self, node):
+ if isinstance(node.parent, astroid.FormattedValue):
+ return
+ self._if_counter += 1
+
+ def visit_comprehension(self, node):
+ self._if_counter += len(node.ifs)
+
+ @check_messages("else-if-used")
+ def visit_if(self, node):
+ if isinstance(node.parent, astroid.If):
+ orelse = node.parent.orelse
+ # current if node must directly follow an "else"
+ if orelse and orelse == [node]:
+ if not self._elifs[self._if_counter]:
+ self.add_message("else-if-used", node=node)
+ self._if_counter += 1
+
+
+def register(linter):
+ """Required method to auto register this checker.
+
+ :param linter: Main interface object for Pylint plugins
+ :type linter: Pylint object
+ """
+ linter.register_checker(ElseifUsedChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/comparetozero.py b/venv/Lib/site-packages/pylint/extensions/comparetozero.py
new file mode 100644
index 0000000..e31f488
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/comparetozero.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
+# Copyright (c) 2017 Claudiu Popa <pcmanticore@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Looks for comparisons to empty string."""
+
+import itertools
+
+import astroid
+
+from pylint import checkers, interfaces
+from pylint.checkers import utils
+
+
+def _is_constant_zero(node):
+ return isinstance(node, astroid.Const) and node.value == 0
+
+
+class CompareToZeroChecker(checkers.BaseChecker):
+ """Checks for comparisons to zero.
+ Most of the times you should use the fact that integers with a value of 0 are false.
+ An exception to this rule is when 0 is allowed in the program and has a
+ different meaning than None!
+ """
+
+ __implements__ = (interfaces.IAstroidChecker,)
+
+ # configuration section name
+ name = "compare-to-zero"
+ msgs = {
+ "C2001": (
+ "Avoid comparisons to zero",
+ "compare-to-zero",
+ "Used when Pylint detects comparison to a 0 constant.",
+ )
+ }
+
+ priority = -2
+ options = ()
+
+ @utils.check_messages("compare-to-zero")
+ def visit_compare(self, node):
+ _operators = ["!=", "==", "is not", "is"]
+ # note: astroid.Compare has the left most operand in node.left
+ # while the rest are a list of tuples in node.ops
+ # the format of the tuple is ('compare operator sign', node)
+ # here we squash everything into `ops` to make it easier for processing later
+ ops = [("", node.left)]
+ ops.extend(node.ops)
+ ops = list(itertools.chain(*ops))
+
+ for ops_idx in range(len(ops) - 2):
+ op_1 = ops[ops_idx]
+ op_2 = ops[ops_idx + 1]
+ op_3 = ops[ops_idx + 2]
+ error_detected = False
+
+ # 0 ?? X
+ if _is_constant_zero(op_1) and op_2 in _operators:
+ error_detected = True
+ # X ?? 0
+ elif op_2 in _operators and _is_constant_zero(op_3):
+ error_detected = True
+
+ if error_detected:
+ self.add_message("compare-to-zero", node=node)
+
+
+def register(linter):
+ """Required method to auto register this checker."""
+ linter.register_checker(CompareToZeroChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/docparams.py b/venv/Lib/site-packages/pylint/extensions/docparams.py
new file mode 100644
index 0000000..d5a15a4
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/docparams.py
@@ -0,0 +1,536 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2014-2015 Bruno Daniel <bruno.daniel@blue-yonder.com>
+# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016-2018 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
+# Copyright (c) 2016 Glenn Matthews <glmatthe@cisco.com>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2017 John Paraskevopoulos <io.paraskev@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Adam Dangoor <adamdangoor@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Pylint plugin for checking in Sphinx, Google, or Numpy style docstrings
+"""
+import astroid
+
+import pylint.extensions._check_docs_utils as utils
+from pylint.checkers import BaseChecker
+from pylint.checkers import utils as checker_utils
+from pylint.interfaces import IAstroidChecker
+
+
+class DocstringParameterChecker(BaseChecker):
+ """Checker for Sphinx, Google, or Numpy style docstrings
+
+ * Check that all function, method and constructor parameters are mentioned
+ in the params and types part of the docstring. Constructor parameters
+ can be documented in either the class docstring or ``__init__`` docstring,
+ but not both.
+ * Check that there are no naming inconsistencies between the signature and
+ the documentation, i.e. also report documented parameters that are missing
+ in the signature. This is important to find cases where parameters are
+ renamed only in the code, not in the documentation.
+ * Check that all explicitly raised exceptions in a function are documented
+ in the function docstring. Caught exceptions are ignored.
+
+ Activate this checker by adding the line::
+
+ load-plugins=pylint.extensions.docparams
+
+ to the ``MASTER`` section of your ``.pylintrc``.
+
+ :param linter: linter object
+ :type linter: :class:`pylint.lint.PyLinter`
+ """
+
+ __implements__ = IAstroidChecker
+
+ name = "parameter_documentation"
+ msgs = {
+ "W9005": (
+ '"%s" has constructor parameters documented in class and __init__',
+ "multiple-constructor-doc",
+ "Please remove parameter declarations in the class or constructor.",
+ ),
+ "W9006": (
+ '"%s" not documented as being raised',
+ "missing-raises-doc",
+ "Please document exceptions for all raised exception types.",
+ ),
+ "W9008": (
+ "Redundant returns documentation",
+ "redundant-returns-doc",
+ "Please remove the return/rtype documentation from this method.",
+ ),
+ "W9010": (
+ "Redundant yields documentation",
+ "redundant-yields-doc",
+ "Please remove the yields documentation from this method.",
+ ),
+ "W9011": (
+ "Missing return documentation",
+ "missing-return-doc",
+ "Please add documentation about what this method returns.",
+ {"old_names": [("W9007", "old-missing-returns-doc")]},
+ ),
+ "W9012": (
+ "Missing return type documentation",
+ "missing-return-type-doc",
+ "Please document the type returned by this method.",
+ # we can't use the same old_name for two different warnings
+ # {'old_names': [('W9007', 'missing-returns-doc')]},
+ ),
+ "W9013": (
+ "Missing yield documentation",
+ "missing-yield-doc",
+ "Please add documentation about what this generator yields.",
+ {"old_names": [("W9009", "old-missing-yields-doc")]},
+ ),
+ "W9014": (
+ "Missing yield type documentation",
+ "missing-yield-type-doc",
+ "Please document the type yielded by this method.",
+ # we can't use the same old_name for two different warnings
+ # {'old_names': [('W9009', 'missing-yields-doc')]},
+ ),
+ "W9015": (
+ '"%s" missing in parameter documentation',
+ "missing-param-doc",
+ "Please add parameter declarations for all parameters.",
+ {"old_names": [("W9003", "old-missing-param-doc")]},
+ ),
+ "W9016": (
+ '"%s" missing in parameter type documentation',
+ "missing-type-doc",
+ "Please add parameter type declarations for all parameters.",
+ {"old_names": [("W9004", "old-missing-type-doc")]},
+ ),
+ "W9017": (
+ '"%s" differing in parameter documentation',
+ "differing-param-doc",
+ "Please check parameter names in declarations.",
+ ),
+ "W9018": (
+ '"%s" differing in parameter type documentation',
+ "differing-type-doc",
+ "Please check parameter names in type declarations.",
+ ),
+ }
+
+ options = (
+ (
+ "accept-no-param-doc",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y or n>",
+ "help": "Whether to accept totally missing parameter "
+ "documentation in the docstring of a function that has "
+ "parameters.",
+ },
+ ),
+ (
+ "accept-no-raise-doc",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y or n>",
+ "help": "Whether to accept totally missing raises "
+ "documentation in the docstring of a function that "
+ "raises an exception.",
+ },
+ ),
+ (
+ "accept-no-return-doc",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y or n>",
+ "help": "Whether to accept totally missing return "
+ "documentation in the docstring of a function that "
+ "returns a statement.",
+ },
+ ),
+ (
+ "accept-no-yields-doc",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y or n>",
+ "help": "Whether to accept totally missing yields "
+ "documentation in the docstring of a generator.",
+ },
+ ),
+ (
+ "default-docstring-type",
+ {
+ "type": "choice",
+ "default": "default",
+ "choices": list(utils.DOCSTRING_TYPES),
+ "help": "If the docstring type cannot be guessed "
+ "the specified docstring type will be used.",
+ },
+ ),
+ )
+
+ priority = -2
+
+ constructor_names = {"__init__", "__new__"}
+ not_needed_param_in_docstring = {"self", "cls"}
+
+ def visit_functiondef(self, node):
+ """Called for function and method definitions (def).
+
+ :param node: Node for a function or method definition in the AST
+ :type node: :class:`astroid.scoped_nodes.Function`
+ """
+ node_doc = utils.docstringify(node.doc, self.config.default_docstring_type)
+ self.check_functiondef_params(node, node_doc)
+ self.check_functiondef_returns(node, node_doc)
+ self.check_functiondef_yields(node, node_doc)
+
+ def check_functiondef_params(self, node, node_doc):
+ node_allow_no_param = None
+ if node.name in self.constructor_names:
+ class_node = checker_utils.node_frame_class(node)
+ if class_node is not None:
+ class_doc = utils.docstringify(
+ class_node.doc, self.config.default_docstring_type
+ )
+ self.check_single_constructor_params(class_doc, node_doc, class_node)
+
+ # __init__ or class docstrings can have no parameters documented
+ # as long as the other documents them.
+ node_allow_no_param = (
+ class_doc.has_params()
+ or class_doc.params_documented_elsewhere()
+ or None
+ )
+ class_allow_no_param = (
+ node_doc.has_params()
+ or node_doc.params_documented_elsewhere()
+ or None
+ )
+
+ self.check_arguments_in_docstring(
+ class_doc, node.args, class_node, class_allow_no_param
+ )
+
+ self.check_arguments_in_docstring(
+ node_doc, node.args, node, node_allow_no_param
+ )
+
+ def check_functiondef_returns(self, node, node_doc):
+ if (not node_doc.supports_yields and node.is_generator()) or node.is_abstract():
+ return
+
+ return_nodes = node.nodes_of_class(astroid.Return)
+ if (node_doc.has_returns() or node_doc.has_rtype()) and not any(
+ utils.returns_something(ret_node) for ret_node in return_nodes
+ ):
+ self.add_message("redundant-returns-doc", node=node)
+
+ def check_functiondef_yields(self, node, node_doc):
+ if not node_doc.supports_yields or node.is_abstract():
+ return
+
+ if (
+ node_doc.has_yields() or node_doc.has_yields_type()
+ ) and not node.is_generator():
+ self.add_message("redundant-yields-doc", node=node)
+
+ def visit_raise(self, node):
+ func_node = node.frame()
+ if not isinstance(func_node, astroid.FunctionDef):
+ return
+
+ expected_excs = utils.possible_exc_types(node)
+
+ if not expected_excs:
+ return
+
+ if not func_node.doc:
+ # If this is a property setter,
+ # the property should have the docstring instead.
+ property_ = utils.get_setters_property(func_node)
+ if property_:
+ func_node = property_
+
+ doc = utils.docstringify(func_node.doc, self.config.default_docstring_type)
+ if not doc.is_valid():
+ if doc.doc:
+ self._handle_no_raise_doc(expected_excs, func_node)
+ return
+
+ found_excs_full_names = doc.exceptions()
+
+ # Extract just the class name, e.g. "error" from "re.error"
+ found_excs_class_names = {exc.split(".")[-1] for exc in found_excs_full_names}
+ missing_excs = expected_excs - found_excs_class_names
+ self._add_raise_message(missing_excs, func_node)
+
+ def visit_return(self, node):
+ if not utils.returns_something(node):
+ return
+
+ func_node = node.frame()
+ if not isinstance(func_node, astroid.FunctionDef):
+ return
+
+ doc = utils.docstringify(func_node.doc, self.config.default_docstring_type)
+ if not doc.is_valid() and self.config.accept_no_return_doc:
+ return
+
+ is_property = checker_utils.decorated_with_property(func_node)
+
+ if not (doc.has_returns() or (doc.has_property_returns() and is_property)):
+ self.add_message("missing-return-doc", node=func_node)
+
+ if func_node.returns:
+ return
+
+ if not (doc.has_rtype() or (doc.has_property_type() and is_property)):
+ self.add_message("missing-return-type-doc", node=func_node)
+
+ def visit_yield(self, node):
+ func_node = node.frame()
+ if not isinstance(func_node, astroid.FunctionDef):
+ return
+
+ doc = utils.docstringify(func_node.doc, self.config.default_docstring_type)
+ if not doc.is_valid() and self.config.accept_no_yields_doc:
+ return
+
+ if doc.supports_yields:
+ doc_has_yields = doc.has_yields()
+ doc_has_yields_type = doc.has_yields_type()
+ else:
+ doc_has_yields = doc.has_returns()
+ doc_has_yields_type = doc.has_rtype()
+
+ if not doc_has_yields:
+ self.add_message("missing-yield-doc", node=func_node)
+
+ if not (doc_has_yields_type or func_node.returns):
+ self.add_message("missing-yield-type-doc", node=func_node)
+
+ def visit_yieldfrom(self, node):
+ self.visit_yield(node)
+
+ def _compare_missing_args(
+ self,
+ found_argument_names,
+ message_id,
+ not_needed_names,
+ expected_argument_names,
+ warning_node,
+ ):
+ """Compare the found argument names with the expected ones and
+ generate a message if there are arguments missing.
+
+ :param set found_argument_names: argument names found in the
+ docstring
+
+ :param str message_id: pylint message id
+
+ :param not_needed_names: names that may be omitted
+ :type not_needed_names: set of str
+
+ :param set expected_argument_names: Expected argument names
+ :param NodeNG warning_node: The node to be analyzed
+ """
+ missing_argument_names = (
+ expected_argument_names - found_argument_names
+ ) - not_needed_names
+ if missing_argument_names:
+ self.add_message(
+ message_id,
+ args=(", ".join(sorted(missing_argument_names)),),
+ node=warning_node,
+ )
+
+ def _compare_different_args(
+ self,
+ found_argument_names,
+ message_id,
+ not_needed_names,
+ expected_argument_names,
+ warning_node,
+ ):
+ """Compare the found argument names with the expected ones and
+ generate a message if there are extra arguments found.
+
+ :param set found_argument_names: argument names found in the
+ docstring
+
+ :param str message_id: pylint message id
+
+ :param not_needed_names: names that may be omitted
+ :type not_needed_names: set of str
+
+ :param set expected_argument_names: Expected argument names
+ :param NodeNG warning_node: The node to be analyzed
+ """
+ differing_argument_names = (
+ (expected_argument_names ^ found_argument_names)
+ - not_needed_names
+ - expected_argument_names
+ )
+
+ if differing_argument_names:
+ self.add_message(
+ message_id,
+ args=(", ".join(sorted(differing_argument_names)),),
+ node=warning_node,
+ )
+
+ def check_arguments_in_docstring(
+ self, doc, arguments_node, warning_node, accept_no_param_doc=None
+ ):
+ """Check that all parameters in a function, method or class constructor
+ on the one hand and the parameters mentioned in the parameter
+ documentation (e.g. the Sphinx tags 'param' and 'type') on the other
+ hand are consistent with each other.
+
+ * Undocumented parameters except 'self' are noticed.
+ * Undocumented parameter types except for 'self' and the ``*<args>``
+ and ``**<kwargs>`` parameters are noticed.
+ * Parameters mentioned in the parameter documentation that don't or no
+ longer exist in the function parameter list are noticed.
+ * If the text "For the parameters, see" or "For the other parameters,
+ see" (ignoring additional whitespace) is mentioned in the docstring,
+ missing parameter documentation is tolerated.
+ * If there's no Sphinx style, Google style or NumPy style parameter
+ documentation at all, i.e. ``:param`` is never mentioned etc., the
+ checker assumes that the parameters are documented in another format
+ and the absence is tolerated.
+
+ :param doc: Docstring for the function, method or class.
+ :type doc: :class:`Docstring`
+
+ :param arguments_node: Arguments node for the function, method or
+ class constructor.
+ :type arguments_node: :class:`astroid.scoped_nodes.Arguments`
+
+ :param warning_node: The node to assign the warnings to
+ :type warning_node: :class:`astroid.scoped_nodes.Node`
+
+ :param accept_no_param_doc: Whether or not to allow no parameters
+ to be documented.
+ If None then this value is read from the configuration.
+ :type accept_no_param_doc: bool or None
+ """
+ # Tolerate missing param or type declarations if there is a link to
+ # another method carrying the same name.
+ if not doc.doc:
+ return
+
+ if accept_no_param_doc is None:
+ accept_no_param_doc = self.config.accept_no_param_doc
+ tolerate_missing_params = doc.params_documented_elsewhere()
+
+ # Collect the function arguments.
+ expected_argument_names = {arg.name for arg in arguments_node.args}
+ expected_argument_names.update(arg.name for arg in arguments_node.kwonlyargs)
+ not_needed_type_in_docstring = self.not_needed_param_in_docstring.copy()
+
+ if arguments_node.vararg is not None:
+ expected_argument_names.add(arguments_node.vararg)
+ not_needed_type_in_docstring.add(arguments_node.vararg)
+ if arguments_node.kwarg is not None:
+ expected_argument_names.add(arguments_node.kwarg)
+ not_needed_type_in_docstring.add(arguments_node.kwarg)
+ params_with_doc, params_with_type = doc.match_param_docs()
+
+ # Tolerate no parameter documentation at all.
+ if not params_with_doc and not params_with_type and accept_no_param_doc:
+ tolerate_missing_params = True
+
+ if not tolerate_missing_params:
+ self._compare_missing_args(
+ params_with_doc,
+ "missing-param-doc",
+ self.not_needed_param_in_docstring,
+ expected_argument_names,
+ warning_node,
+ )
+
+ for index, arg_name in enumerate(arguments_node.args):
+ if arguments_node.annotations[index]:
+ params_with_type.add(arg_name.name)
+ for index, arg_name in enumerate(arguments_node.kwonlyargs):
+ if arguments_node.kwonlyargs_annotations[index]:
+ params_with_type.add(arg_name.name)
+
+ if not tolerate_missing_params:
+ self._compare_missing_args(
+ params_with_type,
+ "missing-type-doc",
+ not_needed_type_in_docstring,
+ expected_argument_names,
+ warning_node,
+ )
+
+ self._compare_different_args(
+ params_with_doc,
+ "differing-param-doc",
+ self.not_needed_param_in_docstring,
+ expected_argument_names,
+ warning_node,
+ )
+ self._compare_different_args(
+ params_with_type,
+ "differing-type-doc",
+ not_needed_type_in_docstring,
+ expected_argument_names,
+ warning_node,
+ )
+
+ def check_single_constructor_params(self, class_doc, init_doc, class_node):
+ if class_doc.has_params() and init_doc.has_params():
+ self.add_message(
+ "multiple-constructor-doc", args=(class_node.name,), node=class_node
+ )
+
+ def _handle_no_raise_doc(self, excs, node):
+ if self.config.accept_no_raise_doc:
+ return
+
+ self._add_raise_message(excs, node)
+
+ def _add_raise_message(self, missing_excs, node):
+ """
+ Adds a message on :param:`node` for the missing exception type.
+
+ :param missing_excs: A list of missing exception types.
+ :type missing_excs: set(str)
+
+ :param node: The node show the message on.
+ :type node: astroid.node_classes.NodeNG
+ """
+ if node.is_abstract():
+ try:
+ missing_excs.remove("NotImplementedError")
+ except KeyError:
+ pass
+
+ if not missing_excs:
+ return
+
+ self.add_message(
+ "missing-raises-doc", args=(", ".join(sorted(missing_excs)),), node=node
+ )
+
+
+def register(linter):
+ """Required method to auto register this checker.
+
+ :param linter: Main interface object for Pylint plugins
+ :type linter: Pylint object
+ """
+ linter.register_checker(DocstringParameterChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/docstyle.py b/venv/Lib/site-packages/pylint/extensions/docstyle.py
new file mode 100644
index 0000000..36f506f
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/docstyle.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 Luis Escobar <lescobar@vauxoo.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import linecache
+
+from pylint import checkers
+from pylint.checkers.utils import check_messages
+from pylint.interfaces import HIGH, IAstroidChecker
+
+
+class DocStringStyleChecker(checkers.BaseChecker):
+ """Checks format of docstrings based on PEP 0257"""
+
+ __implements__ = IAstroidChecker
+ name = "docstyle"
+
+ msgs = {
+ "C0198": (
+ 'Bad docstring quotes in %s, expected """, given %s',
+ "bad-docstring-quotes",
+ "Used when a docstring does not have triple double quotes.",
+ ),
+ "C0199": (
+ "First line empty in %s docstring",
+ "docstring-first-line-empty",
+ "Used when a blank line is found at the beginning of a docstring.",
+ ),
+ }
+
+ @check_messages("docstring-first-line-empty", "bad-docstring-quotes")
+ def visit_module(self, node):
+ self._check_docstring("module", node)
+
+ def visit_classdef(self, node):
+ self._check_docstring("class", node)
+
+ def visit_functiondef(self, node):
+ ftype = "method" if node.is_method() else "function"
+ self._check_docstring(ftype, node)
+
+ visit_asyncfunctiondef = visit_functiondef
+
+ def _check_docstring(self, node_type, node):
+ docstring = node.doc
+ if docstring and docstring[0] == "\n":
+ self.add_message(
+ "docstring-first-line-empty",
+ node=node,
+ args=(node_type,),
+ confidence=HIGH,
+ )
+
+ # Use "linecache", instead of node.as_string(), because the latter
+ # looses the original form of the docstrings.
+
+ if docstring:
+ lineno = node.fromlineno + 1
+ line = linecache.getline(node.root().file, lineno).lstrip()
+ if line and line.find('"""') == 0:
+ return
+ if line and "'''" in line:
+ quotes = "'''"
+ elif line and line[0] == '"':
+ quotes = '"'
+ elif line and line[0] == "'":
+ quotes = "'"
+ else:
+ quotes = False
+ if quotes:
+ self.add_message(
+ "bad-docstring-quotes",
+ node=node,
+ args=(node_type, quotes),
+ confidence=HIGH,
+ )
+
+
+def register(linter):
+ """Required method to auto register this checker.
+
+ :param linter: Main interface object for Pylint plugins
+ :type linter: Pylint object
+ """
+ linter.register_checker(DocStringStyleChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/emptystring.py b/venv/Lib/site-packages/pylint/extensions/emptystring.py
new file mode 100644
index 0000000..04021d5
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/emptystring.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
+# Copyright (c) 2017 Claudiu Popa <pcmanticore@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Looks for comparisons to empty string."""
+
+import itertools
+
+import astroid
+
+from pylint import checkers, interfaces
+from pylint.checkers import utils
+
+
+def _is_constant_empty_str(node):
+ return isinstance(node, astroid.Const) and node.value == ""
+
+
+class CompareToEmptyStringChecker(checkers.BaseChecker):
+ """Checks for comparisons to empty string.
+ Most of the times you should use the fact that empty strings are false.
+ An exception to this rule is when an empty string value is allowed in the program
+ and has a different meaning than None!
+ """
+
+ __implements__ = (interfaces.IAstroidChecker,)
+
+ # configuration section name
+ name = "compare-to-empty-string"
+ msgs = {
+ "C1901": (
+ "Avoid comparisons to empty string",
+ "compare-to-empty-string",
+ "Used when Pylint detects comparison to an empty string constant.",
+ )
+ }
+
+ priority = -2
+ options = ()
+
+ @utils.check_messages("compare-to-empty-string")
+ def visit_compare(self, node):
+ _operators = ["!=", "==", "is not", "is"]
+ # note: astroid.Compare has the left most operand in node.left
+ # while the rest are a list of tuples in node.ops
+ # the format of the tuple is ('compare operator sign', node)
+ # here we squash everything into `ops` to make it easier for processing later
+ ops = [("", node.left)]
+ ops.extend(node.ops)
+ ops = list(itertools.chain(*ops))
+
+ for ops_idx in range(len(ops) - 2):
+ op_1 = ops[ops_idx]
+ op_2 = ops[ops_idx + 1]
+ op_3 = ops[ops_idx + 2]
+ error_detected = False
+
+ # x ?? ""
+ if _is_constant_empty_str(op_1) and op_2 in _operators:
+ error_detected = True
+ # '' ?? X
+ elif op_2 in _operators and _is_constant_empty_str(op_3):
+ error_detected = True
+
+ if error_detected:
+ self.add_message("compare-to-empty-string", node=node)
+
+
+def register(linter):
+ """Required method to auto register this checker."""
+ linter.register_checker(CompareToEmptyStringChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/mccabe.py b/venv/Lib/site-packages/pylint/extensions/mccabe.py
new file mode 100644
index 0000000..cafac97
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/mccabe.py
@@ -0,0 +1,196 @@
+# Copyright (c) 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Module to add McCabe checker class for pylint. """
+
+from mccabe import PathGraph as Mccabe_PathGraph
+from mccabe import PathGraphingAstVisitor as Mccabe_PathGraphingAstVisitor
+
+from pylint import checkers
+from pylint.checkers.utils import check_messages
+from pylint.interfaces import HIGH, IAstroidChecker
+
+
+class PathGraph(Mccabe_PathGraph):
+ def __init__(self, node):
+ super(PathGraph, self).__init__(name="", entity="", lineno=1)
+ self.root = node
+
+
+class PathGraphingAstVisitor(Mccabe_PathGraphingAstVisitor):
+ def __init__(self):
+ super(PathGraphingAstVisitor, self).__init__()
+ self._bottom_counter = 0
+
+ def default(self, node, *args):
+ for child in node.get_children():
+ self.dispatch(child, *args)
+
+ def dispatch(self, node, *args):
+ self.node = node
+ klass = node.__class__
+ meth = self._cache.get(klass)
+ if meth is None:
+ class_name = klass.__name__
+ meth = getattr(self.visitor, "visit" + class_name, self.default)
+ self._cache[klass] = meth
+ return meth(node, *args)
+
+ def visitFunctionDef(self, node):
+ if self.graph is not None:
+ # closure
+ pathnode = self._append_node(node)
+ self.tail = pathnode
+ self.dispatch_list(node.body)
+ bottom = "%s" % self._bottom_counter
+ self._bottom_counter += 1
+ self.graph.connect(self.tail, bottom)
+ self.graph.connect(node, bottom)
+ self.tail = bottom
+ else:
+ self.graph = PathGraph(node)
+ self.tail = node
+ self.dispatch_list(node.body)
+ self.graphs["%s%s" % (self.classname, node.name)] = self.graph
+ self.reset()
+
+ visitAsyncFunctionDef = visitFunctionDef
+
+ def visitSimpleStatement(self, node):
+ self._append_node(node)
+
+ visitAssert = (
+ visitAssign
+ ) = (
+ visitAugAssign
+ ) = (
+ visitDelete
+ ) = (
+ visitPrint
+ ) = (
+ visitRaise
+ ) = (
+ visitYield
+ ) = (
+ visitImport
+ ) = (
+ visitCall
+ ) = (
+ visitSubscript
+ ) = (
+ visitPass
+ ) = (
+ visitContinue
+ ) = (
+ visitBreak
+ ) = visitGlobal = visitReturn = visitExpr = visitAwait = visitSimpleStatement
+
+ def visitWith(self, node):
+ self._append_node(node)
+ self.dispatch_list(node.body)
+
+ visitAsyncWith = visitWith
+
+ def _append_node(self, node):
+ if not self.tail:
+ return None
+ self.graph.connect(self.tail, node)
+ self.tail = node
+ return node
+
+ 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(node)
+ self._subgraph_parse(node, node, extra_blocks)
+ self.graphs["%s%s" % (self.classname, name)] = self.graph
+ self.reset()
+ else:
+ self._append_node(node)
+ self._subgraph_parse(node, node, 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 = node
+ self.dispatch_list(node.body)
+ loose_ends.append(self.tail)
+ for extra in extra_blocks:
+ self.tail = node
+ self.dispatch_list(extra.body)
+ loose_ends.append(self.tail)
+ if node.orelse:
+ self.tail = node
+ self.dispatch_list(node.orelse)
+ loose_ends.append(self.tail)
+ else:
+ loose_ends.append(node)
+ if node:
+ bottom = "%s" % self._bottom_counter
+ self._bottom_counter += 1
+ for end in loose_ends:
+ self.graph.connect(end, bottom)
+ self.tail = bottom
+
+
+class McCabeMethodChecker(checkers.BaseChecker):
+ """Checks McCabe complexity cyclomatic threshold in methods and functions
+ to validate a too complex code.
+ """
+
+ __implements__ = IAstroidChecker
+ name = "design"
+
+ msgs = {
+ "R1260": (
+ "%s is too complex. The McCabe rating is %d",
+ "too-complex",
+ "Used when a method or function is too complex based on "
+ "McCabe Complexity Cyclomatic",
+ )
+ }
+ options = (
+ (
+ "max-complexity",
+ {
+ "default": 10,
+ "type": "int",
+ "metavar": "<int>",
+ "help": "McCabe complexity cyclomatic threshold",
+ },
+ ),
+ )
+
+ @check_messages("too-complex")
+ def visit_module(self, node):
+ """visit an astroid.Module node to check too complex rating and
+ add message if is greather than max_complexity stored from options"""
+ visitor = PathGraphingAstVisitor()
+ for child in node.body:
+ visitor.preorder(child, visitor)
+ for graph in visitor.graphs.values():
+ complexity = graph.complexity()
+ node = graph.root
+ if hasattr(node, "name"):
+ node_name = "'%s'" % node.name
+ else:
+ node_name = "This '%s'" % node.__class__.__name__.lower()
+ if complexity <= self.config.max_complexity:
+ continue
+ self.add_message(
+ "too-complex", node=node, confidence=HIGH, args=(node_name, complexity)
+ )
+
+
+def register(linter):
+ """Required method to auto register this checker.
+
+ :param linter: Main interface object for Pylint plugins
+ :type linter: Pylint object
+ """
+ linter.register_checker(McCabeMethodChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/overlapping_exceptions.py b/venv/Lib/site-packages/pylint/extensions/overlapping_exceptions.py
new file mode 100644
index 0000000..be2208c
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/overlapping_exceptions.py
@@ -0,0 +1,88 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Looks for overlapping exceptions."""
+
+import astroid
+
+from pylint import checkers, interfaces
+from pylint.checkers import utils
+from pylint.checkers.exceptions import _annotated_unpack_infer
+
+
+class OverlappingExceptionsChecker(checkers.BaseChecker):
+ """Checks for two or more exceptions in the same exception handler
+ clause that are identical or parts of the same inheritance hierarchy
+ (i.e. overlapping)."""
+
+ __implements__ = interfaces.IAstroidChecker
+
+ name = "overlap-except"
+ msgs = {
+ "W0714": (
+ "Overlapping exceptions (%s)",
+ "overlapping-except",
+ "Used when exceptions in handler overlap or are identical",
+ )
+ }
+ priority = -2
+ options = ()
+
+ @utils.check_messages("overlapping-except")
+ def visit_tryexcept(self, node):
+ """check for empty except"""
+ for handler in node.handlers:
+ if handler.type is None:
+ continue
+ if isinstance(handler.type, astroid.BoolOp):
+ continue
+ try:
+ excs = list(_annotated_unpack_infer(handler.type))
+ except astroid.InferenceError:
+ continue
+
+ handled_in_clause = []
+ for part, exc in excs:
+ if exc is astroid.Uninferable:
+ continue
+ if isinstance(exc, astroid.Instance) and utils.inherit_from_std_ex(exc):
+ # pylint: disable=protected-access
+ exc = exc._proxied
+
+ if not isinstance(exc, astroid.ClassDef):
+ continue
+
+ exc_ancestors = [
+ anc for anc in exc.ancestors() if isinstance(anc, astroid.ClassDef)
+ ]
+
+ for prev_part, prev_exc in handled_in_clause:
+ prev_exc_ancestors = [
+ anc
+ for anc in prev_exc.ancestors()
+ if isinstance(anc, astroid.ClassDef)
+ ]
+ if exc == prev_exc:
+ self.add_message(
+ "overlapping-except",
+ node=handler.type,
+ args="%s and %s are the same"
+ % (prev_part.as_string(), part.as_string()),
+ )
+ elif prev_exc in exc_ancestors or exc in prev_exc_ancestors:
+ ancestor = part if exc in prev_exc_ancestors else prev_part
+ descendant = part if prev_exc in exc_ancestors else prev_part
+ self.add_message(
+ "overlapping-except",
+ node=handler.type,
+ args="%s is an ancestor class of %s"
+ % (ancestor.as_string(), descendant.as_string()),
+ )
+ handled_in_clause += [(part, exc)]
+
+
+def register(linter):
+ """Required method to auto register this checker."""
+ linter.register_checker(OverlappingExceptionsChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/extensions/redefined_variable_type.py b/venv/Lib/site-packages/pylint/extensions/redefined_variable_type.py
new file mode 100644
index 0000000..cfe4754
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/extensions/redefined_variable_type.py
@@ -0,0 +1,116 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2016-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016 Glenn Matthews <glmatthe@cisco.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import astroid
+
+from pylint.checkers import BaseChecker
+from pylint.checkers.utils import check_messages, is_none, node_type
+from pylint.interfaces import IAstroidChecker
+
+BUILTINS = "builtins"
+
+
+class MultipleTypesChecker(BaseChecker):
+ """Checks for variable type redefinitions (NoneType excepted)
+
+ At a function, method, class or module scope
+
+ This rule could be improved:
+
+ - Currently, if an attribute is set to different types in 2 methods of a
+ same class, it won't be detected (see functional test)
+ - One could improve the support for inference on assignment with tuples,
+ ifexpr, etc. Also it would be great to have support for inference on
+ str.split()
+ """
+
+ __implements__ = IAstroidChecker
+
+ name = "multiple_types"
+ msgs = {
+ "R0204": (
+ "Redefinition of %s type from %s to %s",
+ "redefined-variable-type",
+ "Used when the type of a variable changes inside a "
+ "method or a function.",
+ )
+ }
+
+ def visit_classdef(self, _):
+ self._assigns.append({})
+
+ @check_messages("redefined-variable-type")
+ def leave_classdef(self, _):
+ self._check_and_add_messages()
+
+ visit_functiondef = visit_classdef
+ leave_functiondef = leave_module = leave_classdef
+
+ def visit_module(self, _):
+ self._assigns = [{}]
+
+ def _check_and_add_messages(self):
+ assigns = self._assigns.pop()
+ for name, args in assigns.items():
+ if len(args) <= 1:
+ continue
+ orig_node, orig_type = args[0]
+ # Check if there is a type in the following nodes that would be
+ # different from orig_type.
+ for redef_node, redef_type in args[1:]:
+ if redef_type == orig_type:
+ continue
+ # if a variable is defined to several types in an if node,
+ # this is not actually redefining.
+ orig_parent = orig_node.parent
+ redef_parent = redef_node.parent
+ if isinstance(orig_parent, astroid.If):
+ if orig_parent == redef_parent:
+ if (
+ redef_node in orig_parent.orelse
+ and orig_node not in orig_parent.orelse
+ ):
+ orig_node, orig_type = redef_node, redef_type
+ continue
+ elif isinstance(
+ redef_parent, astroid.If
+ ) and redef_parent in orig_parent.nodes_of_class(astroid.If):
+ orig_node, orig_type = redef_node, redef_type
+ continue
+ orig_type = orig_type.replace(BUILTINS + ".", "")
+ redef_type = redef_type.replace(BUILTINS + ".", "")
+ self.add_message(
+ "redefined-variable-type",
+ node=redef_node,
+ args=(name, orig_type, redef_type),
+ )
+ break
+
+ def visit_assign(self, node):
+ # we don't handle multiple assignment nor slice assignment
+ target = node.targets[0]
+ if isinstance(target, (astroid.Tuple, astroid.Subscript)):
+ return
+ # ignore NoneType
+ if is_none(node):
+ return
+ _type = node_type(node.value)
+ if _type:
+ self._assigns[-1].setdefault(target.as_string(), []).append(
+ (node, _type.pytype())
+ )
+
+
+def register(linter):
+ """Required method to auto register this checker.
+
+ :param linter: Main interface object for Pylint plugins
+ :type linter: Pylint object
+ """
+ linter.register_checker(MultipleTypesChecker(linter))
diff --git a/venv/Lib/site-packages/pylint/graph.py b/venv/Lib/site-packages/pylint/graph.py
new file mode 100644
index 0000000..0dc7a14
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/graph.py
@@ -0,0 +1,197 @@
+# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Graph manipulation utilities.
+
+(dot generation adapted from pypy/translator/tool/make_dot.py)
+"""
+
+import codecs
+import os
+import os.path as osp
+import subprocess
+import sys
+import tempfile
+
+
+def target_info_from_filename(filename):
+ """Transforms /some/path/foo.png into ('/some/path', 'foo.png', 'png')."""
+ basename = osp.basename(filename)
+ storedir = osp.dirname(osp.abspath(filename))
+ target = filename.split(".")[-1]
+ return storedir, basename, target
+
+
+class DotBackend:
+ """Dot File backend."""
+
+ def __init__(
+ self,
+ graphname,
+ rankdir=None,
+ size=None,
+ ratio=None,
+ charset="utf-8",
+ renderer="dot",
+ additional_param=None,
+ ):
+ if additional_param is None:
+ additional_param = {}
+ self.graphname = graphname
+ self.renderer = renderer
+ self.lines = []
+ self._source = None
+ self.emit("digraph %s {" % normalize_node_id(graphname))
+ if rankdir:
+ self.emit("rankdir=%s" % rankdir)
+ if ratio:
+ self.emit("ratio=%s" % ratio)
+ if size:
+ self.emit('size="%s"' % size)
+ if charset:
+ assert charset.lower() in ("utf-8", "iso-8859-1", "latin1"), (
+ "unsupported charset %s" % charset
+ )
+ self.emit('charset="%s"' % charset)
+ for param in additional_param.items():
+ self.emit("=".join(param))
+
+ def get_source(self):
+ """returns self._source"""
+ if self._source is None:
+ self.emit("}\n")
+ self._source = "\n".join(self.lines)
+ del self.lines
+ return self._source
+
+ source = property(get_source)
+
+ def generate(self, outputfile=None, dotfile=None, mapfile=None):
+ """Generates a graph file.
+
+ :param str outputfile: filename and path [defaults to graphname.png]
+ :param str dotfile: filename and path [defaults to graphname.dot]
+ :param str mapfile: filename and path
+
+ :rtype: str
+ :return: a path to the generated file
+ """
+ name = self.graphname
+ if not dotfile:
+ # if 'outputfile' is a dot file use it as 'dotfile'
+ if outputfile and outputfile.endswith(".dot"):
+ dotfile = outputfile
+ else:
+ dotfile = "%s.dot" % name
+ if outputfile is not None:
+ storedir, _, target = target_info_from_filename(outputfile)
+ if target != "dot":
+ pdot, dot_sourcepath = tempfile.mkstemp(".dot", name)
+ os.close(pdot)
+ else:
+ dot_sourcepath = osp.join(storedir, dotfile)
+ else:
+ target = "png"
+ pdot, dot_sourcepath = tempfile.mkstemp(".dot", name)
+ ppng, outputfile = tempfile.mkstemp(".png", name)
+ os.close(pdot)
+ os.close(ppng)
+ pdot = codecs.open(dot_sourcepath, "w", encoding="utf8")
+ pdot.write(self.source)
+ pdot.close()
+ if target != "dot":
+ use_shell = sys.platform == "win32"
+ if mapfile:
+ subprocess.call(
+ [
+ self.renderer,
+ "-Tcmapx",
+ "-o",
+ mapfile,
+ "-T",
+ target,
+ dot_sourcepath,
+ "-o",
+ outputfile,
+ ],
+ shell=use_shell,
+ )
+ else:
+ subprocess.call(
+ [self.renderer, "-T", target, dot_sourcepath, "-o", outputfile],
+ shell=use_shell,
+ )
+ os.unlink(dot_sourcepath)
+ return outputfile
+
+ def emit(self, line):
+ """Adds <line> to final output."""
+ self.lines.append(line)
+
+ def emit_edge(self, name1, name2, **props):
+ """emit an edge from <name1> to <name2>.
+ edge properties: see http://www.graphviz.org/doc/info/attrs.html
+ """
+ attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()]
+ n_from, n_to = normalize_node_id(name1), normalize_node_id(name2)
+ self.emit("%s -> %s [%s];" % (n_from, n_to, ", ".join(sorted(attrs))))
+
+ def emit_node(self, name, **props):
+ """emit a node with given properties.
+ node properties: see http://www.graphviz.org/doc/info/attrs.html
+ """
+ attrs = ['%s="%s"' % (prop, value) for prop, value in props.items()]
+ self.emit("%s [%s];" % (normalize_node_id(name), ", ".join(sorted(attrs))))
+
+
+def normalize_node_id(nid):
+ """Returns a suitable DOT node id for `nid`."""
+ return '"%s"' % nid
+
+
+def get_cycles(graph_dict, vertices=None):
+ """given a dictionary representing an ordered graph (i.e. key are vertices
+ and values is a list of destination vertices representing edges), return a
+ list of detected cycles
+ """
+ if not graph_dict:
+ return ()
+ result = []
+ if vertices is None:
+ vertices = graph_dict.keys()
+ for vertice in vertices:
+ _get_cycles(graph_dict, [], set(), result, vertice)
+ return result
+
+
+def _get_cycles(graph_dict, path, visited, result, vertice):
+ """recursive function doing the real work for get_cycles"""
+ if vertice in path:
+ cycle = [vertice]
+ for node in path[::-1]:
+ if node == vertice:
+ break
+ cycle.insert(0, node)
+ # make a canonical representation
+ start_from = min(cycle)
+ index = cycle.index(start_from)
+ cycle = cycle[index:] + cycle[0:index]
+ # append it to result if not already in
+ if cycle not in result:
+ result.append(cycle)
+ return
+ path.append(vertice)
+ try:
+ for node in graph_dict[vertice]:
+ # don't check already visited nodes again
+ if node not in visited:
+ _get_cycles(graph_dict, path, visited, result, node)
+ visited.add(node)
+ except KeyError:
+ pass
+ path.pop()
diff --git a/venv/Lib/site-packages/pylint/interfaces.py b/venv/Lib/site-packages/pylint/interfaces.py
new file mode 100644
index 0000000..378585c
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/interfaces.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009-2010, 2012-2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Interfaces for Pylint objects"""
+from collections import namedtuple
+
+Confidence = namedtuple("Confidence", ["name", "description"])
+# Warning Certainties
+HIGH = Confidence("HIGH", "No false positive possible.")
+INFERENCE = Confidence("INFERENCE", "Warning based on inference result.")
+INFERENCE_FAILURE = Confidence(
+ "INFERENCE_FAILURE", "Warning based on inference with failures."
+)
+UNDEFINED = Confidence("UNDEFINED", "Warning without any associated confidence level.")
+
+CONFIDENCE_LEVELS = [HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED]
+
+
+class Interface:
+ """Base class for interfaces."""
+
+ @classmethod
+ def is_implemented_by(cls, instance):
+ return implements(instance, cls)
+
+
+def implements(obj, interface):
+ """Return true if the give object (maybe an instance or class) implements
+ the interface.
+ """
+ kimplements = getattr(obj, "__implements__", ())
+ if not isinstance(kimplements, (list, tuple)):
+ kimplements = (kimplements,)
+ for implementedinterface in kimplements:
+ if issubclass(implementedinterface, interface):
+ return True
+ return False
+
+
+class IChecker(Interface):
+ """This is a base interface, not designed to be used elsewhere than for
+ sub interfaces definition.
+ """
+
+ def open(self):
+ """called before visiting project (i.e set of modules)"""
+
+ def close(self):
+ """called after visiting project (i.e set of modules)"""
+
+
+class IRawChecker(IChecker):
+ """interface for checker which need to parse the raw file
+ """
+
+ def process_module(self, astroid):
+ """ process a module
+
+ the module's content is accessible via astroid.stream
+ """
+
+
+class ITokenChecker(IChecker):
+ """Interface for checkers that need access to the token list."""
+
+ def process_tokens(self, tokens):
+ """Process a module.
+
+ tokens is a list of all source code tokens in the file.
+ """
+
+
+class IAstroidChecker(IChecker):
+ """ interface for checker which prefers receive events according to
+ statement type
+ """
+
+
+class IReporter(Interface):
+ """ reporter collect messages and display results encapsulated in a layout
+ """
+
+ def handle_message(self, msg):
+ """Handle the given message object."""
+
+ def display_reports(self, layout):
+ """display results encapsulated in the layout tree
+ """
+
+
+__all__ = ("IRawChecker", "IAstroidChecker", "ITokenChecker", "IReporter")
diff --git a/venv/Lib/site-packages/pylint/lint.py b/venv/Lib/site-packages/pylint/lint.py
new file mode 100644
index 0000000..a98970b
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/lint.py
@@ -0,0 +1,1817 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2008 Fabrice Douchant <Fabrice.Douchant@logilab.fr>
+# Copyright (c) 2009 Vincent
+# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
+# Copyright (c) 2011-2014 Google, Inc.
+# Copyright (c) 2012 David Pursehouse <david.pursehouse@sonymobile.com>
+# Copyright (c) 2012 Kevin Jing Qiu <kevin.jing.qiu@gmail.com>
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2012 JT Olds <jtolds@xnet5.com>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2015 Michal Nowikowski <godfryd@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Alexandru Coman <fcoman@bitdefender.com>
+# Copyright (c) 2014 Daniel Harding <dharding@living180.net>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2014 Dan Goldsmith <djgoldsmith@googlemail.com>
+# Copyright (c) 2015-2016 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Aru Sahni <arusahni@gmail.com>
+# Copyright (c) 2015 Steven Myint <hg@stevenmyint.com>
+# Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
+# Copyright (c) 2015 Mihai Balint <balint.mihai@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
+# Copyright (c) 2016 Alan Evangelista <alanoe@linux.vnet.ibm.com>
+# Copyright (c) 2017-2018 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Daniel Miller <millerdev@gmail.com>
+# Copyright (c) 2017 Roman Ivanov <me@roivanov.com>
+# Copyright (c) 2017 Ned Batchelder <ned@nedbatchelder.com>
+# Copyright (c) 2018 Randall Leeds <randall@bleeds.info>
+# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2018 Jason Owen <jason.a.owen@gmail.com>
+# Copyright (c) 2018 Gary Tyler McLeod <mail@garytyler.com>
+# Copyright (c) 2018 Yuval Langer <yuvallanger@mail.tau.ac.il>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2018 kapsh <kapsh@kap.sh>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+# pylint: disable=broad-except
+
+""" pylint [options] modules_or_packages
+
+ Check that module(s) satisfy a coding standard (and more !).
+
+ pylint --help
+
+ Display this help message and exit.
+
+ pylint --help-msg <msg-id>[,<msg-id>]
+
+ Display help messages about given message identifiers and exit.
+"""
+import collections
+import contextlib
+import operator
+import os
+import sys
+import tokenize
+import traceback
+import warnings
+from io import TextIOWrapper
+
+import astroid
+from astroid import modutils
+from astroid.__pkginfo__ import version as astroid_version
+from astroid.builder import AstroidBuilder
+
+from pylint import __pkginfo__, checkers, config, exceptions, interfaces, reporters
+from pylint.__pkginfo__ import version
+from pylint.constants import MAIN_CHECKER_NAME, MSG_TYPES, OPTION_RGX
+from pylint.message import Message, MessageDefinitionStore, MessagesHandlerMixIn
+from pylint.reporters.ureports import nodes as report_nodes
+from pylint.utils import ASTWalker, FileState, utils
+
+try:
+ import multiprocessing
+except ImportError:
+ multiprocessing = None # type: ignore
+
+
+MANAGER = astroid.MANAGER
+
+
+def _ast_from_string(data, filepath, modname):
+ cached = MANAGER.astroid_cache.get(modname)
+ if cached and cached.file == filepath:
+ return cached
+
+ return AstroidBuilder(MANAGER).string_build(data, modname, filepath)
+
+
+def _read_stdin():
+ # https://mail.python.org/pipermail/python-list/2012-November/634424.html
+ sys.stdin = TextIOWrapper(sys.stdin.detach(), encoding="utf-8")
+ return sys.stdin.read()
+
+
+def _get_new_args(message):
+ location = (
+ message.abspath,
+ message.path,
+ message.module,
+ message.obj,
+ message.line,
+ message.column,
+ )
+ return (message.msg_id, message.symbol, location, message.msg, message.confidence)
+
+
+def _get_python_path(filepath):
+ dirname = os.path.realpath(os.path.expanduser(filepath))
+ if not os.path.isdir(dirname):
+ dirname = os.path.dirname(dirname)
+ while True:
+ if not os.path.exists(os.path.join(dirname, "__init__.py")):
+ return dirname
+ old_dirname = dirname
+ dirname = os.path.dirname(dirname)
+ if old_dirname == dirname:
+ return os.getcwd()
+ return None
+
+
+def _merge_stats(stats):
+ merged = {}
+ by_msg = collections.Counter()
+ for stat in stats:
+ message_stats = stat.pop("by_msg", {})
+ by_msg.update(message_stats)
+
+ for key, item in stat.items():
+ if key not in merged:
+ merged[key] = item
+ else:
+ if isinstance(item, dict):
+ merged[key].update(item)
+ else:
+ merged[key] = merged[key] + item
+
+ merged["by_msg"] = by_msg
+ return merged
+
+
+# Python Linter class #########################################################
+
+MSGS = {
+ "F0001": (
+ "%s",
+ "fatal",
+ "Used when an error occurred preventing the analysis of a \
+ module (unable to find it for instance).",
+ ),
+ "F0002": (
+ "%s: %s",
+ "astroid-error",
+ "Used when an unexpected error occurred while building the "
+ "Astroid representation. This is usually accompanied by a "
+ "traceback. Please report such errors !",
+ ),
+ "F0010": (
+ "error while code parsing: %s",
+ "parse-error",
+ "Used when an exception occurred while building the Astroid "
+ "representation which could be handled by astroid.",
+ ),
+ "I0001": (
+ "Unable to run raw checkers on built-in module %s",
+ "raw-checker-failed",
+ "Used to inform that a built-in module has not been checked "
+ "using the raw checkers.",
+ ),
+ "I0010": (
+ "Unable to consider inline option %r",
+ "bad-inline-option",
+ "Used when an inline option is either badly formatted or can't "
+ "be used inside modules.",
+ ),
+ "I0011": (
+ "Locally disabling %s (%s)",
+ "locally-disabled",
+ "Used when an inline option disables a message or a messages category.",
+ ),
+ "I0013": (
+ "Ignoring entire file",
+ "file-ignored",
+ "Used to inform that the file will not be checked",
+ ),
+ "I0020": (
+ "Suppressed %s (from line %d)",
+ "suppressed-message",
+ "A message was triggered on a line, but suppressed explicitly "
+ "by a disable= comment in the file. This message is not "
+ "generated for messages that are ignored due to configuration "
+ "settings.",
+ ),
+ "I0021": (
+ "Useless suppression of %s",
+ "useless-suppression",
+ "Reported when a message is explicitly disabled for a line or "
+ "a block of code, but never triggered.",
+ ),
+ "I0022": (
+ 'Pragma "%s" is deprecated, use "%s" instead',
+ "deprecated-pragma",
+ "Some inline pylint options have been renamed or reworked, "
+ "only the most recent form should be used. "
+ "NOTE:skip-all is only available with pylint >= 0.26",
+ {"old_names": [("I0014", "deprecated-disable-all")]},
+ ),
+ "E0001": ("%s", "syntax-error", "Used when a syntax error is raised for a module."),
+ "E0011": (
+ "Unrecognized file option %r",
+ "unrecognized-inline-option",
+ "Used when an unknown inline option is encountered.",
+ ),
+ "E0012": (
+ "Bad option value %r",
+ "bad-option-value",
+ "Used when a bad value for an inline option is encountered.",
+ ),
+}
+
+
+def _cpu_count() -> int:
+ """Use sched_affinity if available for virtualized or containerized environments."""
+ sched_getaffinity = getattr(os, "sched_getaffinity", None)
+ # pylint: disable=not-callable,using-constant-test
+ if sched_getaffinity:
+ return len(sched_getaffinity(0))
+ if multiprocessing:
+ return multiprocessing.cpu_count()
+ return 1
+
+
+if multiprocessing is not None:
+
+ class ChildLinter(multiprocessing.Process):
+ def run(self):
+ # pylint: disable=no-member, unbalanced-tuple-unpacking
+ tasks_queue, results_queue, self._config = self._args
+
+ self._config["jobs"] = 1 # Child does not parallelize any further.
+ self._python3_porting_mode = self._config.pop("python3_porting_mode", None)
+ self._plugins = self._config.pop("plugins", None)
+
+ # Run linter for received files/modules.
+ for file_or_module in iter(tasks_queue.get, "STOP"):
+ try:
+ result = self._run_linter(file_or_module[0])
+ results_queue.put(result)
+ except Exception as ex:
+ print(
+ "internal error with sending report for module %s"
+ % file_or_module,
+ file=sys.stderr,
+ )
+ print(ex, file=sys.stderr)
+ results_queue.put({})
+
+ def _run_linter(self, file_or_module):
+ linter = PyLinter()
+
+ # Register standard checkers.
+ linter.load_default_plugins()
+ # Load command line plugins.
+ if self._plugins:
+ linter.load_plugin_modules(self._plugins)
+
+ linter.load_configuration_from_config(self._config)
+
+ # Load plugin specific configuration
+ linter.load_plugin_configuration()
+
+ linter.set_reporter(reporters.CollectingReporter())
+
+ # Enable the Python 3 checker mode. This option is
+ # passed down from the parent linter up to here, since
+ # the Python 3 porting flag belongs to the Run class,
+ # instead of the Linter class.
+ if self._python3_porting_mode:
+ linter.python3_porting_mode()
+
+ # Run the checks.
+ linter.check(file_or_module)
+
+ msgs = [_get_new_args(m) for m in linter.reporter.messages]
+ return (
+ file_or_module,
+ linter.file_state.base_name,
+ linter.current_name,
+ msgs,
+ linter.stats,
+ linter.msg_status,
+ )
+
+
+# pylint: disable=too-many-instance-attributes,too-many-public-methods
+class PyLinter(
+ config.OptionsManagerMixIn,
+ MessagesHandlerMixIn,
+ reporters.ReportsHandlerMixIn,
+ checkers.BaseTokenChecker,
+):
+ """lint Python modules using external checkers.
+
+ This is the main checker controlling the other ones and the reports
+ generation. It is itself both a raw checker and an astroid checker in order
+ to:
+ * handle message activation / deactivation at the module level
+ * handle some basic but necessary stats'data (number of classes, methods...)
+
+ IDE plugin developers: you may have to call
+ `astroid.builder.MANAGER.astroid_cache.clear()` across runs if you want
+ to ensure the latest code version is actually checked.
+ """
+
+ __implements__ = (interfaces.ITokenChecker,)
+
+ name = MAIN_CHECKER_NAME
+ priority = 0
+ level = 0
+ msgs = MSGS
+
+ @staticmethod
+ def make_options():
+ return (
+ (
+ "ignore",
+ {
+ "type": "csv",
+ "metavar": "<file>[,<file>...]",
+ "dest": "black_list",
+ "default": ("CVS",),
+ "help": "Add files or directories to the blacklist. "
+ "They should be base names, not paths.",
+ },
+ ),
+ (
+ "ignore-patterns",
+ {
+ "type": "regexp_csv",
+ "metavar": "<pattern>[,<pattern>...]",
+ "dest": "black_list_re",
+ "default": (),
+ "help": "Add files or directories matching the regex patterns to the"
+ " blacklist. The regex matches against base names, not paths.",
+ },
+ ),
+ (
+ "persistent",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "level": 1,
+ "help": "Pickle collected data for later comparisons.",
+ },
+ ),
+ (
+ "load-plugins",
+ {
+ "type": "csv",
+ "metavar": "<modules>",
+ "default": (),
+ "level": 1,
+ "help": "List of plugins (as comma separated values of "
+ "python module names) to load, usually to register "
+ "additional checkers.",
+ },
+ ),
+ (
+ "output-format",
+ {
+ "default": "text",
+ "type": "string",
+ "metavar": "<format>",
+ "short": "f",
+ "group": "Reports",
+ "help": "Set the output format. Available formats are text,"
+ " parseable, colorized, json and msvs (visual studio)."
+ " You can also give a reporter class, e.g. mypackage.mymodule."
+ "MyReporterClass.",
+ },
+ ),
+ (
+ "reports",
+ {
+ "default": False,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "short": "r",
+ "group": "Reports",
+ "help": "Tells whether to display a full report or only the "
+ "messages.",
+ },
+ ),
+ (
+ "evaluation",
+ {
+ "type": "string",
+ "metavar": "<python_expression>",
+ "group": "Reports",
+ "level": 1,
+ "default": "10.0 - ((float(5 * error + warning + refactor + "
+ "convention) / statement) * 10)",
+ "help": "Python expression which should return a score less "
+ "than or equal to 10. You have access to the variables "
+ "'error', 'warning', 'refactor', and 'convention' which "
+ "contain the number of messages in each category, as well as "
+ "'statement' which is the total number of statements "
+ "analyzed. This score is used by the global "
+ "evaluation report (RP0004).",
+ },
+ ),
+ (
+ "score",
+ {
+ "default": True,
+ "type": "yn",
+ "metavar": "<y_or_n>",
+ "short": "s",
+ "group": "Reports",
+ "help": "Activate the evaluation score.",
+ },
+ ),
+ (
+ "confidence",
+ {
+ "type": "multiple_choice",
+ "metavar": "<levels>",
+ "default": "",
+ "choices": [c.name for c in interfaces.CONFIDENCE_LEVELS],
+ "group": "Messages control",
+ "help": "Only show warnings with the listed confidence levels."
+ " Leave empty to show all. Valid levels: %s."
+ % (", ".join(c.name for c in interfaces.CONFIDENCE_LEVELS),),
+ },
+ ),
+ (
+ "enable",
+ {
+ "type": "csv",
+ "metavar": "<msg ids>",
+ "short": "e",
+ "group": "Messages control",
+ "help": "Enable the message, report, category or checker with the "
+ "given id(s). You can either give multiple identifier "
+ "separated by comma (,) or put this option multiple time "
+ "(only on the command line, not in the configuration file "
+ "where it should appear only once). "
+ 'See also the "--disable" option for examples.',
+ },
+ ),
+ (
+ "disable",
+ {
+ "type": "csv",
+ "metavar": "<msg ids>",
+ "short": "d",
+ "group": "Messages control",
+ "help": "Disable the message, report, category or checker "
+ "with the given id(s). You can either give multiple identifiers "
+ "separated by comma (,) or put this option multiple times "
+ "(only on the command line, not in the configuration file "
+ "where it should appear only once). "
+ 'You can also use "--disable=all" to disable everything first '
+ "and then reenable specific checks. For example, if you want "
+ "to run only the similarities checker, you can use "
+ '"--disable=all --enable=similarities". '
+ "If you want to run only the classes checker, but have no "
+ "Warning level messages displayed, use "
+ '"--disable=all --enable=classes --disable=W".',
+ },
+ ),
+ (
+ "msg-template",
+ {
+ "type": "string",
+ "metavar": "<template>",
+ "group": "Reports",
+ "help": (
+ "Template used to display messages. "
+ "This is a python new-style format string "
+ "used to format the message information. "
+ "See doc for all details."
+ ),
+ },
+ ),
+ (
+ "jobs",
+ {
+ "type": "int",
+ "metavar": "<n-processes>",
+ "short": "j",
+ "default": 1,
+ "help": "Use multiple processes to speed up Pylint. Specifying 0 will "
+ "auto-detect the number of processors available to use.",
+ },
+ ),
+ (
+ "unsafe-load-any-extension",
+ {
+ "type": "yn",
+ "metavar": "<yn>",
+ "default": False,
+ "hide": True,
+ "help": (
+ "Allow loading of arbitrary C extensions. Extensions"
+ " are imported into the active Python interpreter and"
+ " may run arbitrary code."
+ ),
+ },
+ ),
+ (
+ "limit-inference-results",
+ {
+ "type": "int",
+ "metavar": "<number-of-results>",
+ "default": 100,
+ "help": (
+ "Control the amount of potential inferred values when inferring "
+ "a single object. This can help the performance when dealing with "
+ "large functions or complex, nested conditions. "
+ ),
+ },
+ ),
+ (
+ "extension-pkg-whitelist",
+ {
+ "type": "csv",
+ "metavar": "<pkg[,pkg]>",
+ "default": [],
+ "help": (
+ "A comma-separated list of package or module names"
+ " from where C extensions may be loaded. Extensions are"
+ " loading into the active Python interpreter and may run"
+ " arbitrary code."
+ ),
+ },
+ ),
+ (
+ "suggestion-mode",
+ {
+ "type": "yn",
+ "metavar": "<yn>",
+ "default": True,
+ "help": (
+ "When enabled, pylint would attempt to guess common "
+ "misconfiguration and emit user-friendly hints instead "
+ "of false-positive error messages."
+ ),
+ },
+ ),
+ (
+ "exit-zero",
+ {
+ "action": "store_true",
+ "help": (
+ "Always return a 0 (non-error) status code, even if "
+ "lint errors are found. This is primarily useful in "
+ "continuous integration scripts."
+ ),
+ },
+ ),
+ (
+ "from-stdin",
+ {
+ "action": "store_true",
+ "help": (
+ "Interpret the stdin as a python script, whose filename "
+ "needs to be passed as the module_or_package argument."
+ ),
+ },
+ ),
+ )
+
+ option_groups = (
+ ("Messages control", "Options controlling analysis messages"),
+ ("Reports", "Options related to output formatting and reporting"),
+ )
+
+ def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None):
+ # some stuff has to be done before ancestors initialization...
+ #
+ # messages store / checkers / reporter / astroid manager
+ self.msgs_store = MessageDefinitionStore()
+ self.reporter = None
+ self._reporter_name = None
+ self._reporters = {}
+ self._checkers = collections.defaultdict(list)
+ self._pragma_lineno = {}
+ self._ignore_file = False
+ # visit variables
+ self.file_state = FileState()
+ self.current_name = None
+ self.current_file = None
+ self.stats = None
+ # init options
+ self._external_opts = options
+ self.options = options + PyLinter.make_options()
+ self.option_groups = option_groups + PyLinter.option_groups
+ self._options_methods = {"enable": self.enable, "disable": self.disable}
+ self._bw_options_methods = {
+ "disable-msg": self.disable,
+ "enable-msg": self.enable,
+ }
+ full_version = "pylint %s\nastroid %s\nPython %s" % (
+ version,
+ astroid_version,
+ sys.version,
+ )
+ MessagesHandlerMixIn.__init__(self)
+ reporters.ReportsHandlerMixIn.__init__(self)
+ super(PyLinter, self).__init__(
+ usage=__doc__, version=full_version, config_file=pylintrc or config.PYLINTRC
+ )
+ checkers.BaseTokenChecker.__init__(self)
+ # provided reports
+ self.reports = (
+ ("RP0001", "Messages by category", report_total_messages_stats),
+ (
+ "RP0002",
+ "% errors / warnings by module",
+ report_messages_by_module_stats,
+ ),
+ ("RP0003", "Messages", report_messages_stats),
+ )
+ self.register_checker(self)
+ self._dynamic_plugins = set()
+ self._python3_porting_mode = False
+ self._error_mode = False
+ self.load_provider_defaults()
+ if reporter:
+ self.set_reporter(reporter)
+
+ def load_default_plugins(self):
+ checkers.initialize(self)
+ reporters.initialize(self)
+ # Make sure to load the default reporter, because
+ # the option has been set before the plugins had been loaded.
+ if not self.reporter:
+ self._load_reporter()
+
+ def load_plugin_modules(self, modnames):
+ """take a list of module names which are pylint plugins and load
+ and register them
+ """
+ for modname in modnames:
+ if modname in self._dynamic_plugins:
+ continue
+ self._dynamic_plugins.add(modname)
+ module = modutils.load_module_from_name(modname)
+ module.register(self)
+
+ def load_plugin_configuration(self):
+ """Call the configuration hook for plugins
+
+ This walks through the list of plugins, grabs the "load_configuration"
+ hook, if exposed, and calls it to allow plugins to configure specific
+ settings.
+ """
+ for modname in self._dynamic_plugins:
+ module = modutils.load_module_from_name(modname)
+ if hasattr(module, "load_configuration"):
+ module.load_configuration(self)
+
+ def _load_reporter(self):
+ name = self._reporter_name.lower()
+ if name in self._reporters:
+ self.set_reporter(self._reporters[name]())
+ else:
+ try:
+ reporter_class = self._load_reporter_class()
+ except (ImportError, AttributeError):
+ raise exceptions.InvalidReporterError(name)
+ else:
+ self.set_reporter(reporter_class())
+
+ def _load_reporter_class(self):
+ qname = self._reporter_name
+ module = modutils.load_module_from_name(modutils.get_module_part(qname))
+ class_name = qname.split(".")[-1]
+ reporter_class = getattr(module, class_name)
+ return reporter_class
+
+ def set_reporter(self, reporter):
+ """set the reporter used to display messages and reports"""
+ self.reporter = reporter
+ reporter.linter = self
+
+ def set_option(self, optname, value, action=None, optdict=None):
+ """overridden from config.OptionsProviderMixin to handle some
+ special options
+ """
+ if optname in self._options_methods or optname in self._bw_options_methods:
+ if value:
+ try:
+ meth = self._options_methods[optname]
+ except KeyError:
+ meth = self._bw_options_methods[optname]
+ warnings.warn(
+ "%s is deprecated, replace it by %s"
+ % (optname, optname.split("-")[0]),
+ DeprecationWarning,
+ )
+ value = utils._check_csv(value)
+ if isinstance(value, (list, tuple)):
+ for _id in value:
+ meth(_id, ignore_unknown=True)
+ else:
+ meth(value)
+ return # no need to call set_option, disable/enable methods do it
+ elif optname == "output-format":
+ self._reporter_name = value
+ # If the reporters are already available, load
+ # the reporter class.
+ if self._reporters:
+ self._load_reporter()
+
+ try:
+ checkers.BaseTokenChecker.set_option(self, optname, value, action, optdict)
+ except config.UnsupportedAction:
+ print("option %s can't be read from config file" % optname, file=sys.stderr)
+
+ def register_reporter(self, reporter_class):
+ self._reporters[reporter_class.name] = reporter_class
+
+ def report_order(self):
+ reports = sorted(self._reports, key=lambda x: getattr(x, "name", ""))
+ try:
+ # Remove the current reporter and add it
+ # at the end of the list.
+ reports.pop(reports.index(self))
+ except ValueError:
+ pass
+ else:
+ reports.append(self)
+ return reports
+
+ # checkers manipulation methods ############################################
+
+ def register_checker(self, checker):
+ """register a new checker
+
+ checker is an object implementing IRawChecker or / and IAstroidChecker
+ """
+ assert checker.priority <= 0, "checker priority can't be >= 0"
+ self._checkers[checker.name].append(checker)
+ for r_id, r_title, r_cb in checker.reports:
+ self.register_report(r_id, r_title, r_cb, checker)
+ self.register_options_provider(checker)
+ if hasattr(checker, "msgs"):
+ self.msgs_store.register_messages_from_checker(checker)
+ checker.load_defaults()
+
+ # Register the checker, but disable all of its messages.
+ if not getattr(checker, "enabled", True):
+ self.disable(checker.name)
+
+ def disable_noerror_messages(self):
+ for msgcat, msgids in self.msgs_store._msgs_by_category.items():
+ # enable only messages with 'error' severity and above ('fatal')
+ if msgcat in ["E", "F"]:
+ for msgid in msgids:
+ self.enable(msgid)
+ else:
+ for msgid in msgids:
+ self.disable(msgid)
+
+ def disable_reporters(self):
+ """disable all reporters"""
+ for _reporters in self._reports.values():
+ for report_id, _, _ in _reporters:
+ self.disable_report(report_id)
+
+ def error_mode(self):
+ """error mode: enable only errors; no reports, no persistent"""
+ self._error_mode = True
+ self.disable_noerror_messages()
+ self.disable("miscellaneous")
+ if self._python3_porting_mode:
+ self.disable("all")
+ for msg_id in self._checker_messages("python3"):
+ if msg_id.startswith("E"):
+ self.enable(msg_id)
+ config_parser = self.cfgfile_parser
+ if config_parser.has_option("MESSAGES CONTROL", "disable"):
+ value = config_parser.get("MESSAGES CONTROL", "disable")
+ self.global_set_option("disable", value)
+ else:
+ self.disable("python3")
+ self.set_option("reports", False)
+ self.set_option("persistent", False)
+ self.set_option("score", False)
+
+ def python3_porting_mode(self):
+ """Disable all other checkers and enable Python 3 warnings."""
+ self.disable("all")
+ self.enable("python3")
+ if self._error_mode:
+ # The error mode was activated, using the -E flag.
+ # So we'll need to enable only the errors from the
+ # Python 3 porting checker.
+ for msg_id in self._checker_messages("python3"):
+ if msg_id.startswith("E"):
+ self.enable(msg_id)
+ else:
+ self.disable(msg_id)
+ config_parser = self.cfgfile_parser
+ if config_parser.has_option("MESSAGES CONTROL", "disable"):
+ value = config_parser.get("MESSAGES CONTROL", "disable")
+ self.global_set_option("disable", value)
+ self._python3_porting_mode = True
+
+ def list_messages_enabled(self):
+ enabled = [
+ " %s (%s)" % (message.symbol, message.msgid)
+ for message in self.msgs_store.messages
+ if self.is_message_enabled(message.msgid)
+ ]
+ disabled = [
+ " %s (%s)" % (message.symbol, message.msgid)
+ for message in self.msgs_store.messages
+ if not self.is_message_enabled(message.msgid)
+ ]
+ print("Enabled messages:")
+ for msg in sorted(enabled):
+ print(msg)
+ print("\nDisabled messages:")
+ for msg in sorted(disabled):
+ print(msg)
+ print("")
+
+ # block level option handling #############################################
+ #
+ # see func_block_disable_msg.py test case for expected behaviour
+
+ def process_tokens(self, tokens):
+ """process tokens from the current module to search for module/block
+ level options
+ """
+ control_pragmas = {"disable", "enable"}
+ prev_line = None
+ saw_newline = True
+ seen_newline = True
+ for (tok_type, content, start, _, _) in tokens:
+ if prev_line and prev_line != start[0]:
+ saw_newline = seen_newline
+ seen_newline = False
+
+ prev_line = start[0]
+ if tok_type in (tokenize.NL, tokenize.NEWLINE):
+ seen_newline = True
+
+ if tok_type != tokenize.COMMENT:
+ continue
+ match = OPTION_RGX.search(content)
+ if match is None:
+ continue
+
+ first_group = match.group(1)
+ if (
+ first_group.strip() == "disable-all"
+ or first_group.strip() == "skip-file"
+ ):
+ if first_group.strip() == "disable-all":
+ self.add_message(
+ "deprecated-pragma",
+ line=start[0],
+ args=("disable-all", "skip-file"),
+ )
+ self.add_message("file-ignored", line=start[0])
+ self._ignore_file = True
+ return
+ try:
+ opt, value = first_group.split("=", 1)
+ except ValueError:
+ self.add_message(
+ "bad-inline-option", args=first_group.strip(), line=start[0]
+ )
+ continue
+ opt = opt.strip()
+ if opt in self._options_methods or opt in self._bw_options_methods:
+ try:
+ meth = self._options_methods[opt]
+ except KeyError:
+ meth = self._bw_options_methods[opt]
+ # found a "(dis|en)able-msg" pragma deprecated suppression
+ self.add_message(
+ "deprecated-pragma",
+ line=start[0],
+ args=(opt, opt.replace("-msg", "")),
+ )
+ for msgid in utils._splitstrip(value):
+ # Add the line where a control pragma was encountered.
+ if opt in control_pragmas:
+ self._pragma_lineno[msgid] = start[0]
+
+ try:
+ if (opt, msgid) == ("disable", "all"):
+ self.add_message(
+ "deprecated-pragma",
+ line=start[0],
+ args=("disable=all", "skip-file"),
+ )
+ self.add_message("file-ignored", line=start[0])
+ self._ignore_file = True
+ return
+ # If we did not see a newline between the previous line and now,
+ # we saw a backslash so treat the two lines as one.
+ if not saw_newline:
+ meth(msgid, "module", start[0] - 1)
+ meth(msgid, "module", start[0])
+ except exceptions.UnknownMessageError:
+ self.add_message("bad-option-value", args=msgid, line=start[0])
+ else:
+ self.add_message("unrecognized-inline-option", args=opt, line=start[0])
+
+ # code checking methods ###################################################
+
+ def get_checkers(self):
+ """return all available checkers as a list"""
+ return [self] + [
+ c
+ for _checkers in self._checkers.values()
+ for c in _checkers
+ if c is not self
+ ]
+
+ def get_checker_names(self):
+ """Get all the checker names that this linter knows about."""
+ current_checkers = self.get_checkers()
+ return sorted(
+ {
+ checker.name
+ for checker in current_checkers
+ if checker.name != MAIN_CHECKER_NAME
+ }
+ )
+
+ def prepare_checkers(self):
+ """return checkers needed for activated messages and reports"""
+ if not self.config.reports:
+ self.disable_reporters()
+ # get needed checkers
+ needed_checkers = [self]
+ for checker in self.get_checkers()[1:]:
+ messages = {msg for msg in checker.msgs if self.is_message_enabled(msg)}
+ if messages or any(self.report_is_enabled(r[0]) for r in checker.reports):
+ needed_checkers.append(checker)
+ # Sort checkers by priority
+ needed_checkers = sorted(
+ needed_checkers, key=operator.attrgetter("priority"), reverse=True
+ )
+ return needed_checkers
+
+ # pylint: disable=unused-argument
+ @staticmethod
+ def should_analyze_file(modname, path, is_argument=False):
+ """Returns whether or not a module should be checked.
+
+ This implementation returns True for all python source file, indicating
+ that all files should be linted.
+
+ Subclasses may override this method to indicate that modules satisfying
+ certain conditions should not be linted.
+
+ :param str modname: The name of the module to be checked.
+ :param str path: The full path to the source code of the module.
+ :param bool is_argument: Whetter the file is an argument to pylint or not.
+ Files which respect this property are always
+ checked, since the user requested it explicitly.
+ :returns: True if the module should be checked.
+ :rtype: bool
+ """
+ if is_argument:
+ return True
+ return path.endswith(".py")
+
+ # pylint: enable=unused-argument
+
+ def check(self, files_or_modules):
+ """main checking entry: check a list of files or modules from their
+ name.
+ """
+ # initialize msgs_state now that all messages have been registered into
+ # the store
+ for msg in self.msgs_store.messages:
+ if not msg.may_be_emitted():
+ self._msgs_state[msg.msgid] = False
+
+ if not isinstance(files_or_modules, (list, tuple)):
+ files_or_modules = (files_or_modules,)
+
+ if self.config.jobs == 1:
+ self._do_check(files_or_modules)
+ else:
+ self._parallel_check(files_or_modules)
+
+ def _get_jobs_config(self):
+ child_config = collections.OrderedDict()
+ filter_options = {"long-help"}
+ filter_options.update((opt_name for opt_name, _ in self._external_opts))
+ for opt_providers in self._all_options.values():
+ for optname, optdict, val in opt_providers.options_and_values():
+ if optdict.get("deprecated"):
+ continue
+
+ if optname not in filter_options:
+ child_config[optname] = utils._format_option_value(optdict, val)
+ child_config["python3_porting_mode"] = self._python3_porting_mode
+ child_config["plugins"] = self._dynamic_plugins
+ return child_config
+
+ def _parallel_task(self, files_or_modules):
+ # Prepare configuration for child linters.
+ child_config = self._get_jobs_config()
+
+ children = []
+ manager = multiprocessing.Manager()
+ tasks_queue = manager.Queue()
+ results_queue = manager.Queue()
+
+ # Send files to child linters.
+ expanded_files = []
+ for descr in self.expand_files(files_or_modules):
+ modname, filepath, is_arg = descr["name"], descr["path"], descr["isarg"]
+ if self.should_analyze_file(modname, filepath, is_argument=is_arg):
+ expanded_files.append(descr)
+
+ # do not start more jobs than needed
+ for _ in range(min(self.config.jobs, len(expanded_files))):
+ child_linter = ChildLinter(args=(tasks_queue, results_queue, child_config))
+ child_linter.start()
+ children.append(child_linter)
+
+ for files_or_module in expanded_files:
+ path = files_or_module["path"]
+ tasks_queue.put([path])
+
+ # collect results from child linters
+ failed = False
+ for _ in expanded_files:
+ try:
+ result = results_queue.get()
+ except Exception as ex:
+ print(
+ "internal error while receiving results from child linter",
+ file=sys.stderr,
+ )
+ print(ex, file=sys.stderr)
+ failed = True
+ break
+ yield result
+
+ # Stop child linters and wait for their completion.
+ for _ in range(self.config.jobs):
+ tasks_queue.put("STOP")
+ for child in children:
+ child.join()
+
+ if failed:
+ print("Error occurred, stopping the linter.", file=sys.stderr)
+ sys.exit(32)
+
+ def _parallel_check(self, files_or_modules):
+ # Reset stats.
+ self.open()
+
+ all_stats = []
+ module = None
+ for result in self._parallel_task(files_or_modules):
+ if not result:
+ continue
+ (_, self.file_state.base_name, module, messages, stats, msg_status) = result
+
+ for msg in messages:
+ msg = Message(*msg)
+ self.set_current_module(module)
+ self.reporter.handle_message(msg)
+
+ all_stats.append(stats)
+ self.msg_status |= msg_status
+
+ self.stats = _merge_stats(all_stats)
+ self.current_name = module
+
+ # Insert stats data to local checkers.
+ for checker in self.get_checkers():
+ if checker is not self:
+ checker.stats = self.stats
+
+ def _do_check(self, files_or_modules):
+ walker = ASTWalker(self)
+ _checkers = self.prepare_checkers()
+ tokencheckers = [
+ c
+ for c in _checkers
+ if interfaces.implements(c, interfaces.ITokenChecker) and c is not self
+ ]
+ rawcheckers = [
+ c for c in _checkers if interfaces.implements(c, interfaces.IRawChecker)
+ ]
+ # notify global begin
+ for checker in _checkers:
+ checker.open()
+ if interfaces.implements(checker, interfaces.IAstroidChecker):
+ walker.add_checker(checker)
+ # build ast and check modules or packages
+ if self.config.from_stdin:
+ if len(files_or_modules) != 1:
+ raise exceptions.InvalidArgsError(
+ "Missing filename required for --from-stdin"
+ )
+
+ filepath = files_or_modules[0]
+ try:
+ # Note that this function does not really perform an
+ # __import__ but may raise an ImportError exception, which
+ # we want to catch here.
+ modname = ".".join(modutils.modpath_from_file(filepath))
+ except ImportError:
+ modname = os.path.splitext(os.path.basename(filepath))[0]
+
+ self.set_current_module(modname, filepath)
+
+ # get the module representation
+ ast_node = _ast_from_string(_read_stdin(), filepath, modname)
+
+ if ast_node is not None:
+ self.file_state = FileState(filepath)
+ self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers)
+ # warn about spurious inline messages handling
+ spurious_messages = self.file_state.iter_spurious_suppression_messages(
+ self.msgs_store
+ )
+ for msgid, line, args in spurious_messages:
+ self.add_message(msgid, line, None, args)
+ else:
+ for descr in self.expand_files(files_or_modules):
+ modname, filepath, is_arg = descr["name"], descr["path"], descr["isarg"]
+ if not self.should_analyze_file(modname, filepath, is_argument=is_arg):
+ continue
+
+ self.set_current_module(modname, filepath)
+ # get the module representation
+ ast_node = self.get_ast(filepath, modname)
+ if ast_node is None:
+ continue
+
+ self.file_state = FileState(descr["basename"])
+ self._ignore_file = False
+ # fix the current file (if the source file was not available or
+ # if it's actually a c extension)
+ self.current_file = ast_node.file # pylint: disable=maybe-no-member
+ before_check_statements = walker.nbstatements
+ self.check_astroid_module(ast_node, walker, rawcheckers, tokencheckers)
+ self.stats["by_module"][modname]["statement"] = (
+ walker.nbstatements - before_check_statements
+ )
+ # warn about spurious inline messages handling
+ spurious_messages = self.file_state.iter_spurious_suppression_messages(
+ self.msgs_store
+ )
+ for msgid, line, args in spurious_messages:
+ self.add_message(msgid, line, None, args)
+ # notify global end
+ self.stats["statement"] = walker.nbstatements
+ for checker in reversed(_checkers):
+ checker.close()
+
+ def expand_files(self, modules):
+ """get modules and errors from a list of modules and handle errors
+ """
+ result, errors = utils.expand_modules(
+ modules, self.config.black_list, self.config.black_list_re
+ )
+ for error in errors:
+ message = modname = error["mod"]
+ key = error["key"]
+ self.set_current_module(modname)
+ if key == "fatal":
+ message = str(error["ex"]).replace(os.getcwd() + os.sep, "")
+ self.add_message(key, args=message)
+ return result
+
+ def set_current_module(self, modname, filepath=None):
+ """set the name of the currently analyzed module and
+ init statistics for it
+ """
+ if not modname and filepath is None:
+ return
+ self.reporter.on_set_current_module(modname, filepath)
+ self.current_name = modname
+ self.current_file = filepath or modname
+ self.stats["by_module"][modname] = {}
+ self.stats["by_module"][modname]["statement"] = 0
+ for msg_cat in MSG_TYPES.values():
+ self.stats["by_module"][modname][msg_cat] = 0
+
+ def get_ast(self, filepath, modname):
+ """return an ast(roid) representation for a module"""
+ try:
+ return MANAGER.ast_from_file(filepath, modname, source=True)
+ except astroid.AstroidSyntaxError as ex:
+ # pylint: disable=no-member
+ self.add_message(
+ "syntax-error",
+ line=getattr(ex.error, "lineno", 0),
+ col_offset=getattr(ex.error, "offset", None),
+ args=str(ex.error),
+ )
+ except astroid.AstroidBuildingException as ex:
+ self.add_message("parse-error", args=ex)
+ except Exception as ex:
+ traceback.print_exc()
+ self.add_message("astroid-error", args=(ex.__class__, ex))
+
+ def check_astroid_module(self, ast_node, walker, rawcheckers, tokencheckers):
+ """Check a module from its astroid representation."""
+ try:
+ tokens = utils.tokenize_module(ast_node)
+ except tokenize.TokenError as ex:
+ self.add_message("syntax-error", line=ex.args[1][0], args=ex.args[0])
+ return None
+
+ if not ast_node.pure_python:
+ self.add_message("raw-checker-failed", args=ast_node.name)
+ else:
+ # assert astroid.file.endswith('.py')
+ # invoke ITokenChecker interface on self to fetch module/block
+ # level options
+ self.process_tokens(tokens)
+ if self._ignore_file:
+ return False
+ # walk ast to collect line numbers
+ self.file_state.collect_block_lines(self.msgs_store, ast_node)
+ # run raw and tokens checkers
+ for checker in rawcheckers:
+ checker.process_module(ast_node)
+ for checker in tokencheckers:
+ checker.process_tokens(tokens)
+ # generate events to astroid checkers
+ walker.walk(ast_node)
+ return True
+
+ # IAstroidChecker interface #################################################
+
+ def open(self):
+ """initialize counters"""
+ self.stats = {"by_module": {}, "by_msg": {}}
+ MANAGER.always_load_extensions = self.config.unsafe_load_any_extension
+ MANAGER.max_inferable_values = self.config.limit_inference_results
+ MANAGER.extension_package_whitelist.update(self.config.extension_pkg_whitelist)
+ for msg_cat in MSG_TYPES.values():
+ self.stats[msg_cat] = 0
+
+ def generate_reports(self):
+ """close the whole package /module, it's time to make reports !
+
+ if persistent run, pickle results for later comparison
+ """
+ # Display whatever messages are left on the reporter.
+ self.reporter.display_messages(report_nodes.Section())
+
+ if self.file_state.base_name is not None:
+ # load previous results if any
+ previous_stats = config.load_results(self.file_state.base_name)
+ self.reporter.on_close(self.stats, previous_stats)
+ if self.config.reports:
+ sect = self.make_reports(self.stats, previous_stats)
+ else:
+ sect = report_nodes.Section()
+
+ if self.config.reports:
+ self.reporter.display_reports(sect)
+ self._report_evaluation()
+ # save results if persistent run
+ if self.config.persistent:
+ config.save_results(self.stats, self.file_state.base_name)
+ else:
+ self.reporter.on_close(self.stats, {})
+
+ def _report_evaluation(self):
+ """make the global evaluation report"""
+ # check with at least check 1 statements (usually 0 when there is a
+ # syntax error preventing pylint from further processing)
+ previous_stats = config.load_results(self.file_state.base_name)
+ if self.stats["statement"] == 0:
+ return
+
+ # get a global note for the code
+ evaluation = self.config.evaluation
+ try:
+ note = eval(evaluation, {}, self.stats) # pylint: disable=eval-used
+ except Exception as ex:
+ msg = "An exception occurred while rating: %s" % ex
+ else:
+ self.stats["global_note"] = note
+ msg = "Your code has been rated at %.2f/10" % note
+ pnote = previous_stats.get("global_note")
+ if pnote is not None:
+ msg += " (previous run: %.2f/10, %+.2f)" % (pnote, note - pnote)
+
+ if self.config.score:
+ sect = report_nodes.EvaluationSection(msg)
+ self.reporter.display_reports(sect)
+
+
+# some reporting functions ####################################################
+
+
+def report_total_messages_stats(sect, stats, previous_stats):
+ """make total errors / warnings report"""
+ lines = ["type", "number", "previous", "difference"]
+ lines += checkers.table_lines_from_stats(
+ stats, previous_stats, ("convention", "refactor", "warning", "error")
+ )
+ sect.append(report_nodes.Table(children=lines, cols=4, rheaders=1))
+
+
+def report_messages_stats(sect, stats, _):
+ """make messages type report"""
+ if not stats["by_msg"]:
+ # don't print this report when we didn't detected any errors
+ raise exceptions.EmptyReportError()
+ in_order = sorted(
+ [
+ (value, msg_id)
+ for msg_id, value in stats["by_msg"].items()
+ if not msg_id.startswith("I")
+ ]
+ )
+ in_order.reverse()
+ lines = ("message id", "occurrences")
+ for value, msg_id in in_order:
+ lines += (msg_id, str(value))
+ sect.append(report_nodes.Table(children=lines, cols=2, rheaders=1))
+
+
+def report_messages_by_module_stats(sect, stats, _):
+ """make errors / warnings by modules report"""
+ if len(stats["by_module"]) == 1:
+ # don't print this report when we are analysing a single module
+ raise exceptions.EmptyReportError()
+ by_mod = collections.defaultdict(dict)
+ for m_type in ("fatal", "error", "warning", "refactor", "convention"):
+ total = stats[m_type]
+ for module in stats["by_module"].keys():
+ mod_total = stats["by_module"][module][m_type]
+ if total == 0:
+ percent = 0
+ else:
+ percent = float((mod_total) * 100) / total
+ by_mod[module][m_type] = percent
+ sorted_result = []
+ for module, mod_info in by_mod.items():
+ sorted_result.append(
+ (
+ mod_info["error"],
+ mod_info["warning"],
+ mod_info["refactor"],
+ mod_info["convention"],
+ module,
+ )
+ )
+ sorted_result.sort()
+ sorted_result.reverse()
+ lines = ["module", "error", "warning", "refactor", "convention"]
+ for line in sorted_result:
+ # Don't report clean modules.
+ if all(entry == 0 for entry in line[:-1]):
+ continue
+ lines.append(line[-1])
+ for val in line[:-1]:
+ lines.append("%.2f" % val)
+ if len(lines) == 5:
+ raise exceptions.EmptyReportError()
+ sect.append(report_nodes.Table(children=lines, cols=5, rheaders=1))
+
+
+# utilities ###################################################################
+
+
+class ArgumentPreprocessingError(Exception):
+ """Raised if an error occurs during argument preprocessing."""
+
+
+def preprocess_options(args, search_for):
+ """look for some options (keys of <search_for>) which have to be processed
+ before others
+
+ values of <search_for> are callback functions to call when the option is
+ found
+ """
+ i = 0
+ while i < len(args):
+ arg = args[i]
+ if arg.startswith("--"):
+ try:
+ option, val = arg[2:].split("=", 1)
+ except ValueError:
+ option, val = arg[2:], None
+ try:
+ cb, takearg = search_for[option]
+ except KeyError:
+ i += 1
+ else:
+ del args[i]
+ if takearg and val is None:
+ if i >= len(args) or args[i].startswith("-"):
+ msg = "Option %s expects a value" % option
+ raise ArgumentPreprocessingError(msg)
+ val = args[i]
+ del args[i]
+ elif not takearg and val is not None:
+ msg = "Option %s doesn't expects a value" % option
+ raise ArgumentPreprocessingError(msg)
+ cb(option, val)
+ else:
+ i += 1
+
+
+@contextlib.contextmanager
+def fix_import_path(args):
+ """Prepare sys.path for running the linter checks.
+
+ Within this context, each of the given arguments is importable.
+ Paths are added to sys.path in corresponding order to the arguments.
+ We avoid adding duplicate directories to sys.path.
+ `sys.path` is reset to its original value upon exiting this context.
+ """
+ orig = list(sys.path)
+ changes = []
+ for arg in args:
+ path = _get_python_path(arg)
+ if path not in changes:
+ changes.append(path)
+ sys.path[:] = changes + ["."] + sys.path
+ try:
+ yield
+ finally:
+ sys.path[:] = orig
+
+
+class Run:
+ """helper class to use as main for pylint :
+
+ run(*sys.argv[1:])
+ """
+
+ LinterClass = PyLinter
+ option_groups = (
+ (
+ "Commands",
+ "Options which are actually commands. Options in this \
+group are mutually exclusive.",
+ ),
+ )
+
+ def __init__(self, args, reporter=None, do_exit=True):
+ self._rcfile = None
+ self._plugins = []
+ self.verbose = None
+ try:
+ preprocess_options(
+ args,
+ {
+ # option: (callback, takearg)
+ "init-hook": (cb_init_hook, True),
+ "rcfile": (self.cb_set_rcfile, True),
+ "load-plugins": (self.cb_add_plugins, True),
+ "verbose": (self.cb_verbose_mode, False),
+ },
+ )
+ except ArgumentPreprocessingError as ex:
+ print(ex, file=sys.stderr)
+ sys.exit(32)
+
+ self.linter = linter = self.LinterClass(
+ (
+ (
+ "rcfile",
+ {
+ "action": "callback",
+ "callback": lambda *args: 1,
+ "type": "string",
+ "metavar": "<file>",
+ "help": "Specify a configuration file.",
+ },
+ ),
+ (
+ "init-hook",
+ {
+ "action": "callback",
+ "callback": lambda *args: 1,
+ "type": "string",
+ "metavar": "<code>",
+ "level": 1,
+ "help": "Python code to execute, usually for sys.path "
+ "manipulation such as pygtk.require().",
+ },
+ ),
+ (
+ "help-msg",
+ {
+ "action": "callback",
+ "type": "string",
+ "metavar": "<msg-id>",
+ "callback": self.cb_help_message,
+ "group": "Commands",
+ "help": "Display a help message for the given message id and "
+ "exit. The value may be a comma separated list of message ids.",
+ },
+ ),
+ (
+ "list-msgs",
+ {
+ "action": "callback",
+ "metavar": "<msg-id>",
+ "callback": self.cb_list_messages,
+ "group": "Commands",
+ "level": 1,
+ "help": "Generate pylint's messages.",
+ },
+ ),
+ (
+ "list-msgs-enabled",
+ {
+ "action": "callback",
+ "metavar": "<msg-id>",
+ "callback": self.cb_list_messages_enabled,
+ "group": "Commands",
+ "level": 1,
+ "help": "Display a list of what messages are enabled "
+ "and disabled with the given configuration.",
+ },
+ ),
+ (
+ "list-groups",
+ {
+ "action": "callback",
+ "metavar": "<msg-id>",
+ "callback": self.cb_list_groups,
+ "group": "Commands",
+ "level": 1,
+ "help": "List pylint's message groups.",
+ },
+ ),
+ (
+ "list-conf-levels",
+ {
+ "action": "callback",
+ "callback": cb_list_confidence_levels,
+ "group": "Commands",
+ "level": 1,
+ "help": "Generate pylint's confidence levels.",
+ },
+ ),
+ (
+ "full-documentation",
+ {
+ "action": "callback",
+ "metavar": "<msg-id>",
+ "callback": self.cb_full_documentation,
+ "group": "Commands",
+ "level": 1,
+ "help": "Generate pylint's full documentation.",
+ },
+ ),
+ (
+ "generate-rcfile",
+ {
+ "action": "callback",
+ "callback": self.cb_generate_config,
+ "group": "Commands",
+ "help": "Generate a sample configuration file according to "
+ "the current configuration. You can put other options "
+ "before this one to get them in the generated "
+ "configuration.",
+ },
+ ),
+ (
+ "generate-man",
+ {
+ "action": "callback",
+ "callback": self.cb_generate_manpage,
+ "group": "Commands",
+ "help": "Generate pylint's man page.",
+ "hide": True,
+ },
+ ),
+ (
+ "errors-only",
+ {
+ "action": "callback",
+ "callback": self.cb_error_mode,
+ "short": "E",
+ "help": "In error mode, checkers without error messages are "
+ "disabled and for others, only the ERROR messages are "
+ "displayed, and no reports are done by default.",
+ },
+ ),
+ (
+ "py3k",
+ {
+ "action": "callback",
+ "callback": self.cb_python3_porting_mode,
+ "help": "In Python 3 porting mode, all checkers will be "
+ "disabled and only messages emitted by the porting "
+ "checker will be displayed.",
+ },
+ ),
+ (
+ "verbose",
+ {
+ "action": "callback",
+ "callback": self.cb_verbose_mode,
+ "short": "v",
+ "help": "In verbose mode, extra non-checker-related info "
+ "will be displayed.",
+ },
+ ),
+ ),
+ option_groups=self.option_groups,
+ pylintrc=self._rcfile,
+ )
+ # register standard checkers
+ linter.load_default_plugins()
+ # load command line plugins
+ linter.load_plugin_modules(self._plugins)
+ # add some help section
+ linter.add_help_section("Environment variables", config.ENV_HELP, level=1)
+ # pylint: disable=bad-continuation
+ linter.add_help_section(
+ "Output",
+ "Using the default text output, the message format is : \n"
+ " \n"
+ " MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE \n"
+ " \n"
+ "There are 5 kind of message types : \n"
+ " * (C) convention, for programming standard violation \n"
+ " * (R) refactor, for bad code smell \n"
+ " * (W) warning, for python specific problems \n"
+ " * (E) error, for probable bugs in the code \n"
+ " * (F) fatal, if an error occurred which prevented pylint from doing further\n"
+ "processing.\n",
+ level=1,
+ )
+ linter.add_help_section(
+ "Output status code",
+ "Pylint should leave with following status code: \n"
+ " * 0 if everything went fine \n"
+ " * 1 if a fatal message was issued \n"
+ " * 2 if an error message was issued \n"
+ " * 4 if a warning message was issued \n"
+ " * 8 if a refactor message was issued \n"
+ " * 16 if a convention message was issued \n"
+ " * 32 on usage error \n"
+ " \n"
+ "status 1 to 16 will be bit-ORed so you can know which different categories has\n"
+ "been issued by analysing pylint output status code\n",
+ level=1,
+ )
+ # read configuration
+ linter.disable("I")
+ linter.enable("c-extension-no-member")
+ linter.read_config_file(verbose=self.verbose)
+ config_parser = linter.cfgfile_parser
+ # run init hook, if present, before loading plugins
+ if config_parser.has_option("MASTER", "init-hook"):
+ cb_init_hook(
+ "init-hook", utils._unquote(config_parser.get("MASTER", "init-hook"))
+ )
+ # is there some additional plugins in the file configuration, in
+ if config_parser.has_option("MASTER", "load-plugins"):
+ plugins = utils._splitstrip(config_parser.get("MASTER", "load-plugins"))
+ linter.load_plugin_modules(plugins)
+ # now we can load file config and command line, plugins (which can
+ # provide options) have been registered
+ linter.load_config_file()
+
+ if reporter:
+ # if a custom reporter is provided as argument, it may be overridden
+ # by file parameters, so re-set it here, but before command line
+ # parsing so it's still overrideable by command line option
+ linter.set_reporter(reporter)
+ try:
+ args = linter.load_command_line_configuration(args)
+ except SystemExit as exc:
+ if exc.code == 2: # bad options
+ exc.code = 32
+ raise
+ if not args:
+ print(linter.help())
+ sys.exit(32)
+
+ if linter.config.jobs < 0:
+ print(
+ "Jobs number (%d) should be greater than or equal to 0"
+ % linter.config.jobs,
+ file=sys.stderr,
+ )
+ sys.exit(32)
+ if linter.config.jobs > 1 or linter.config.jobs == 0:
+ if multiprocessing is None:
+ print(
+ "Multiprocessing library is missing, " "fallback to single process",
+ file=sys.stderr,
+ )
+ linter.set_option("jobs", 1)
+ else:
+ if linter.config.jobs == 0:
+ linter.config.jobs = _cpu_count()
+
+ # We have loaded configuration from config file and command line. Now, we can
+ # load plugin specific configuration.
+ linter.load_plugin_configuration()
+
+ # insert current working directory to the python path to have a correct
+ # behaviour
+ with fix_import_path(args):
+ linter.check(args)
+ linter.generate_reports()
+ if do_exit:
+ if linter.config.exit_zero:
+ sys.exit(0)
+ else:
+ sys.exit(self.linter.msg_status)
+
+ def cb_set_rcfile(self, name, value):
+ """callback for option preprocessing (i.e. before option parsing)"""
+ self._rcfile = value
+
+ def cb_add_plugins(self, name, value):
+ """callback for option preprocessing (i.e. before option parsing)"""
+ self._plugins.extend(utils._splitstrip(value))
+
+ def cb_error_mode(self, *args, **kwargs):
+ """error mode:
+ * disable all but error messages
+ * disable the 'miscellaneous' checker which can be safely deactivated in
+ debug
+ * disable reports
+ * do not save execution information
+ """
+ self.linter.error_mode()
+
+ def cb_generate_config(self, *args, **kwargs):
+ """optik callback for sample config file generation"""
+ self.linter.generate_config(skipsections=("COMMANDS",))
+ sys.exit(0)
+
+ def cb_generate_manpage(self, *args, **kwargs):
+ """optik callback for sample config file generation"""
+ self.linter.generate_manpage(__pkginfo__)
+ sys.exit(0)
+
+ def cb_help_message(self, option, optname, value, parser):
+ """optik callback for printing some help about a particular message"""
+ self.linter.msgs_store.help_message(utils._splitstrip(value))
+ sys.exit(0)
+
+ def cb_full_documentation(self, option, optname, value, parser):
+ """optik callback for printing full documentation"""
+ self.linter.print_full_documentation()
+ sys.exit(0)
+
+ def cb_list_messages(self, option, optname, value, parser):
+ """optik callback for printing available messages"""
+ self.linter.msgs_store.list_messages()
+ sys.exit(0)
+
+ def cb_list_messages_enabled(self, option, optname, value, parser):
+ """optik callback for printing available messages"""
+ self.linter.list_messages_enabled()
+ sys.exit(0)
+
+ def cb_list_groups(self, *args, **kwargs):
+ """List all the check groups that pylint knows about
+
+ These should be useful to know what check groups someone can disable
+ or enable.
+ """
+ for check in self.linter.get_checker_names():
+ print(check)
+ sys.exit(0)
+
+ def cb_python3_porting_mode(self, *args, **kwargs):
+ """Activate only the python3 porting checker."""
+ self.linter.python3_porting_mode()
+
+ def cb_verbose_mode(self, *args, **kwargs):
+ self.verbose = True
+
+
+def cb_list_confidence_levels(option, optname, value, parser):
+ for level in interfaces.CONFIDENCE_LEVELS:
+ print("%-18s: %s" % level)
+ sys.exit(0)
+
+
+def cb_init_hook(optname, value):
+ """exec arbitrary code to set sys.path for instance"""
+ exec(value) # pylint: disable=exec-used
+
+
+if __name__ == "__main__":
+ Run(sys.argv[1:])
diff --git a/venv/Lib/site-packages/pylint/message/__init__.py b/venv/Lib/site-packages/pylint/message/__init__.py
new file mode 100644
index 0000000..5ac8411
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/__init__.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2009 Vincent
+# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2015 Michal Nowikowski <godfryd@gmail.com>
+# Copyright (c) 2014 LCD 47 <lcd047@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2014 Damien Nozay <damien.nozay@gmail.com>
+# Copyright (c) 2015 Aru Sahni <arusahni@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
+# Copyright (c) 2016 Glenn Matthews <glmatthe@cisco.com>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2016 xmo-odoo <xmo-odoo@users.noreply.github.com>
+# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Pierre Sassoulas <pierre.sassoulas@cea.fr>
+# Copyright (c) 2017 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2017 Chris Lamb <chris@chris-lamb.co.uk>
+# Copyright (c) 2017 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2017 Thomas Hisch <t.hisch@gmail.com>
+# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com>
+# Copyright (c) 2017 Craig Citro <craigcitro@gmail.com>
+# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Pierre Sassoulas <pierre.sassoulas@wisebim.fr>
+# Copyright (c) 2018 Reverb C <reverbc@users.noreply.github.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""All the classes related to Message handling."""
+
+from pylint.message.message import Message
+from pylint.message.message_definition import MessageDefinition
+from pylint.message.message_definition_store import MessageDefinitionStore
+from pylint.message.message_handler_mix_in import MessagesHandlerMixIn
+from pylint.message.message_id_store import MessageIdStore
+
+__all__ = [
+ "Message",
+ "MessageDefinition",
+ "MessageDefinitionStore",
+ "MessagesHandlerMixIn",
+ "MessageIdStore",
+]
diff --git a/venv/Lib/site-packages/pylint/message/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/message/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..f3462f1
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/message/__pycache__/message.cpython-37.pyc b/venv/Lib/site-packages/pylint/message/__pycache__/message.cpython-37.pyc
new file mode 100644
index 0000000..6c89577
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/__pycache__/message.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/message/__pycache__/message_definition.cpython-37.pyc b/venv/Lib/site-packages/pylint/message/__pycache__/message_definition.cpython-37.pyc
new file mode 100644
index 0000000..952803b
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/__pycache__/message_definition.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/message/__pycache__/message_definition_store.cpython-37.pyc b/venv/Lib/site-packages/pylint/message/__pycache__/message_definition_store.cpython-37.pyc
new file mode 100644
index 0000000..ce6f867
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/__pycache__/message_definition_store.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/message/__pycache__/message_handler_mix_in.cpython-37.pyc b/venv/Lib/site-packages/pylint/message/__pycache__/message_handler_mix_in.cpython-37.pyc
new file mode 100644
index 0000000..23cc65a
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/__pycache__/message_handler_mix_in.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/message/__pycache__/message_id_store.cpython-37.pyc b/venv/Lib/site-packages/pylint/message/__pycache__/message_id_store.cpython-37.pyc
new file mode 100644
index 0000000..f132b88
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/__pycache__/message_id_store.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/message/message.py b/venv/Lib/site-packages/pylint/message/message.py
new file mode 100644
index 0000000..e2b0320
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/message.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+
+import collections
+
+from pylint.constants import MSG_TYPES
+
+_MsgBase = collections.namedtuple(
+ "_MsgBase",
+ [
+ "msg_id",
+ "symbol",
+ "msg",
+ "C",
+ "category",
+ "confidence",
+ "abspath",
+ "path",
+ "module",
+ "obj",
+ "line",
+ "column",
+ ],
+)
+
+
+class Message(_MsgBase):
+ """This class represent a message to be issued by the reporters"""
+
+ def __new__(cls, msg_id, symbol, location, msg, confidence):
+ return _MsgBase.__new__(
+ cls,
+ msg_id,
+ symbol,
+ msg,
+ msg_id[0],
+ MSG_TYPES[msg_id[0]],
+ confidence,
+ *location
+ )
+
+ def format(self, template):
+ """Format the message according to the given template.
+
+ The template format is the one of the format method :
+ cf. http://docs.python.org/2/library/string.html#formatstrings
+ """
+ # For some reason, _asdict on derived namedtuples does not work with
+ # Python 3.4. Needs some investigation.
+ return template.format(**dict(zip(self._fields, self)))
diff --git a/venv/Lib/site-packages/pylint/message/message_definition.py b/venv/Lib/site-packages/pylint/message/message_definition.py
new file mode 100644
index 0000000..e54c15a
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/message_definition.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import sys
+
+from pylint.constants import MSG_TYPES
+from pylint.exceptions import InvalidMessageError
+from pylint.utils import normalize_text
+
+
+class MessageDefinition:
+ def __init__(
+ self,
+ checker,
+ msgid,
+ msg,
+ description,
+ symbol,
+ scope,
+ minversion=None,
+ maxversion=None,
+ old_names=None,
+ ):
+ self.checker = checker
+ self.check_msgid(msgid)
+ self.msgid = msgid
+ self.symbol = symbol
+ self.msg = msg
+ self.description = description
+ self.scope = scope
+ self.minversion = minversion
+ self.maxversion = maxversion
+ self.old_names = []
+ if old_names:
+ for old_msgid, old_symbol in old_names:
+ self.check_msgid(old_msgid)
+ self.old_names.append([old_msgid, old_symbol])
+
+ @staticmethod
+ def check_msgid(msgid: str) -> None:
+ if len(msgid) != 5:
+ raise InvalidMessageError("Invalid message id %r" % msgid)
+ if msgid[0] not in MSG_TYPES:
+ raise InvalidMessageError("Bad message type %s in %r" % (msgid[0], msgid))
+
+ def __repr__(self):
+ return "MessageDefinition:%s (%s)" % (self.symbol, self.msgid)
+
+ def __str__(self):
+ return "%s:\n%s %s" % (repr(self), self.msg, self.description)
+
+ def may_be_emitted(self):
+ """return True if message may be emitted using the current interpreter"""
+ if self.minversion is not None and self.minversion > sys.version_info:
+ return False
+ if self.maxversion is not None and self.maxversion <= sys.version_info:
+ return False
+ return True
+
+ def format_help(self, checkerref=False):
+ """return the help string for the given message id"""
+ desc = self.description
+ if checkerref:
+ desc += " This message belongs to the %s checker." % self.checker.name
+ title = self.msg
+ if self.minversion or self.maxversion:
+ restr = []
+ if self.minversion:
+ restr.append("< %s" % ".".join([str(n) for n in self.minversion]))
+ if self.maxversion:
+ restr.append(">= %s" % ".".join([str(n) for n in self.maxversion]))
+ restr = " or ".join(restr)
+ if checkerref:
+ desc += " It can't be emitted when using Python %s." % restr
+ else:
+ desc += " This message can't be emitted when using Python %s." % restr
+ msg_help = normalize_text(" ".join(desc.split()), indent=" ")
+ message_id = "%s (%s)" % (self.symbol, self.msgid)
+ if title != "%s":
+ title = title.splitlines()[0]
+ return ":%s: *%s*\n%s" % (message_id, title.rstrip(" "), msg_help)
+ return ":%s:\n%s" % (message_id, msg_help)
diff --git a/venv/Lib/site-packages/pylint/message/message_definition_store.py b/venv/Lib/site-packages/pylint/message/message_definition_store.py
new file mode 100644
index 0000000..f7d87b6
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/message_definition_store.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import collections
+
+from pylint.exceptions import UnknownMessageError
+from pylint.message.message_id_store import MessageIdStore
+
+
+class MessageDefinitionStore:
+
+ """The messages store knows information about every possible message definition but has
+ no particular state during analysis.
+ """
+
+ def __init__(self):
+ self.message_id_store = MessageIdStore()
+ # Primary registry for all active messages definitions.
+ # It contains the 1:1 mapping from msgid to MessageDefinition.
+ # Keys are msgid, values are MessageDefinition
+ self._messages_definitions = {}
+ # MessageDefinition kept by category
+ self._msgs_by_category = collections.defaultdict(list)
+
+ @property
+ def messages(self) -> list:
+ """The list of all active messages."""
+ return self._messages_definitions.values()
+
+ def register_messages_from_checker(self, checker):
+ """Register all messages definitions from a checker.
+
+ :param BaseChecker checker:
+ """
+ checker.check_consistency()
+ for message in checker.messages:
+ self.register_message(message)
+
+ def register_message(self, message):
+ """Register a MessageDefinition with consistency in mind.
+
+ :param MessageDefinition message: The message definition being added.
+ """
+ self.message_id_store.register_message_definition(message)
+ self._messages_definitions[message.msgid] = message
+ self._msgs_by_category[message.msgid[0]].append(message.msgid)
+
+ def get_message_definitions(self, msgid_or_symbol: str) -> list:
+ """Returns the Message object for this message.
+ :param str msgid_or_symbol: msgid_or_symbol may be either a numeric or symbolic id.
+ :raises UnknownMessageError: if the message id is not defined.
+ :rtype: List of MessageDefinition
+ :return: A message definition corresponding to msgid_or_symbol
+ """
+ return [
+ self._messages_definitions[m]
+ for m in self.message_id_store.get_active_msgids(msgid_or_symbol)
+ ]
+
+ def get_msg_display_string(self, msgid_or_symbol: str):
+ """Generates a user-consumable representation of a message. """
+ message_definitions = self.get_message_definitions(msgid_or_symbol)
+ if len(message_definitions) == 1:
+ return repr(message_definitions[0].symbol)
+ return repr([md.symbol for md in message_definitions])
+
+ def help_message(self, msgids_or_symbols: list):
+ """Display help messages for the given message identifiers"""
+ for msgids_or_symbol in msgids_or_symbols:
+ try:
+ for message_definition in self.get_message_definitions(
+ msgids_or_symbol
+ ):
+ print(message_definition.format_help(checkerref=True))
+ print("")
+ except UnknownMessageError as ex:
+ print(ex)
+ print("")
+ continue
+
+ def list_messages(self):
+ """Output full messages list documentation in ReST format. """
+ messages = sorted(self._messages_definitions.values(), key=lambda m: m.msgid)
+ for message in messages:
+ if not message.may_be_emitted():
+ continue
+ print(message.format_help(checkerref=False))
+ print("")
diff --git a/venv/Lib/site-packages/pylint/message/message_handler_mix_in.py b/venv/Lib/site-packages/pylint/message/message_handler_mix_in.py
new file mode 100644
index 0000000..813cdd7
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/message_handler_mix_in.py
@@ -0,0 +1,393 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import sys
+
+from pylint.constants import (
+ _SCOPE_EXEMPT,
+ MAIN_CHECKER_NAME,
+ MSG_STATE_CONFIDENCE,
+ MSG_STATE_SCOPE_CONFIG,
+ MSG_STATE_SCOPE_MODULE,
+ MSG_TYPES,
+ MSG_TYPES_LONG,
+ MSG_TYPES_STATUS,
+ WarningScope,
+)
+from pylint.exceptions import InvalidMessageError, UnknownMessageError
+from pylint.interfaces import UNDEFINED
+from pylint.message.message import Message
+from pylint.utils import get_module_and_frameid, get_rst_section, get_rst_title
+
+
+class MessagesHandlerMixIn:
+ """a mix-in class containing all the messages related methods for the main
+ lint class
+ """
+
+ __by_id_managed_msgs = [] # type: ignore
+
+ def __init__(self):
+ self._msgs_state = {}
+ self.msg_status = 0
+
+ def _checker_messages(self, checker):
+ for known_checker in self._checkers[checker.lower()]:
+ for msgid in known_checker.msgs:
+ yield msgid
+
+ @classmethod
+ def clear_by_id_managed_msgs(cls):
+ cls.__by_id_managed_msgs.clear()
+
+ @classmethod
+ def get_by_id_managed_msgs(cls):
+ return cls.__by_id_managed_msgs
+
+ def _register_by_id_managed_msg(self, msgid, line, is_disabled=True):
+ """If the msgid is a numeric one, then register it to inform the user
+ it could furnish instead a symbolic msgid."""
+ try:
+ message_definitions = self.msgs_store.get_message_definitions(msgid)
+ for message_definition in message_definitions:
+ if msgid == message_definition.msgid:
+ MessagesHandlerMixIn.__by_id_managed_msgs.append(
+ (
+ self.current_name,
+ message_definition.msgid,
+ message_definition.symbol,
+ line,
+ is_disabled,
+ )
+ )
+ except UnknownMessageError:
+ pass
+
+ def disable(self, msgid, scope="package", line=None, ignore_unknown=False):
+ """don't output message of the given id"""
+ self._set_msg_status(
+ msgid, enable=False, scope=scope, line=line, ignore_unknown=ignore_unknown
+ )
+ self._register_by_id_managed_msg(msgid, line)
+
+ def enable(self, msgid, scope="package", line=None, ignore_unknown=False):
+ """reenable message of the given id"""
+ self._set_msg_status(
+ msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown
+ )
+ self._register_by_id_managed_msg(msgid, line, is_disabled=False)
+
+ def _set_msg_status(
+ self, msgid, enable, scope="package", line=None, ignore_unknown=False
+ ):
+ assert scope in ("package", "module")
+
+ if msgid == "all":
+ for _msgid in MSG_TYPES:
+ self._set_msg_status(_msgid, enable, scope, line, ignore_unknown)
+ if enable and not self._python3_porting_mode:
+ # Don't activate the python 3 porting checker if it wasn't activated explicitly.
+ self.disable("python3")
+ return
+
+ # msgid is a category?
+ category_id = msgid.upper()
+ if category_id not in MSG_TYPES:
+ category_id = MSG_TYPES_LONG.get(category_id)
+ if category_id is not None:
+ for _msgid in self.msgs_store._msgs_by_category.get(category_id):
+ self._set_msg_status(_msgid, enable, scope, line)
+ return
+
+ # msgid is a checker name?
+ if msgid.lower() in self._checkers:
+ for checker in self._checkers[msgid.lower()]:
+ for _msgid in checker.msgs:
+ self._set_msg_status(_msgid, enable, scope, line)
+ return
+
+ # msgid is report id?
+ if msgid.lower().startswith("rp"):
+ if enable:
+ self.enable_report(msgid)
+ else:
+ self.disable_report(msgid)
+ return
+
+ try:
+ # msgid is a symbolic or numeric msgid.
+ message_definitions = self.msgs_store.get_message_definitions(msgid)
+ except UnknownMessageError:
+ if ignore_unknown:
+ return
+ raise
+ for message_definition in message_definitions:
+ self._set_one_msg_status(scope, message_definition, line, enable)
+
+ def _set_one_msg_status(self, scope, msg, line, enable):
+ if scope == "module":
+ self.file_state.set_msg_status(msg, line, enable)
+ if not enable and msg.symbol != "locally-disabled":
+ self.add_message(
+ "locally-disabled", line=line, args=(msg.symbol, msg.msgid)
+ )
+ else:
+ msgs = self._msgs_state
+ msgs[msg.msgid] = enable
+ # sync configuration object
+ self.config.enable = [
+ self._message_symbol(mid) for mid, val in sorted(msgs.items()) if val
+ ]
+ self.config.disable = [
+ self._message_symbol(mid)
+ for mid, val in sorted(msgs.items())
+ if not val
+ ]
+
+ def _message_symbol(self, msgid):
+ """Get the message symbol of the given message id
+
+ Return the original message id if the message does not
+ exist.
+ """
+ try:
+ return [md.symbol for md in self.msgs_store.get_message_definitions(msgid)]
+ except UnknownMessageError:
+ return msgid
+
+ def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED):
+ """Returns the scope at which a message was enabled/disabled."""
+ if self.config.confidence and confidence.name not in self.config.confidence:
+ return MSG_STATE_CONFIDENCE
+ try:
+ if line in self.file_state._module_msgs_state[msgid]:
+ return MSG_STATE_SCOPE_MODULE
+ except (KeyError, TypeError):
+ return MSG_STATE_SCOPE_CONFIG
+ return None
+
+ def is_message_enabled(self, msg_descr, line=None, confidence=None):
+ """return true if the message associated to the given message id is
+ enabled
+
+ msgid may be either a numeric or symbolic message id.
+ """
+ if self.config.confidence and confidence:
+ if confidence.name not in self.config.confidence:
+ return False
+ try:
+ message_definitions = self.msgs_store.get_message_definitions(msg_descr)
+ msgids = [md.msgid for md in message_definitions]
+ except UnknownMessageError:
+ # The linter checks for messages that are not registered
+ # due to version mismatch, just treat them as message IDs
+ # for now.
+ msgids = [msg_descr]
+ for msgid in msgids:
+ if self.is_one_message_enabled(msgid, line):
+ return True
+ return False
+
+ def is_one_message_enabled(self, msgid, line):
+ if line is None:
+ return self._msgs_state.get(msgid, True)
+ try:
+ return self.file_state._module_msgs_state[msgid][line]
+ except KeyError:
+ # Check if the message's line is after the maximum line existing in ast tree.
+ # This line won't appear in the ast tree and won't be referred in
+ #  self.file_state._module_msgs_state
+ # This happens for example with a commented line at the end of a module.
+ max_line_number = self.file_state.get_effective_max_line_number()
+ if max_line_number and line > max_line_number:
+ fallback = True
+ lines = self.file_state._raw_module_msgs_state.get(msgid, {})
+
+ # Doesn't consider scopes, as a disable can be in a different scope
+ # than that of the current line.
+ closest_lines = reversed(
+ [
+ (message_line, enable)
+ for message_line, enable in lines.items()
+ if message_line <= line
+ ]
+ )
+ last_line, is_enabled = next(closest_lines, (None, None))
+ if last_line is not None:
+ fallback = is_enabled
+
+ return self._msgs_state.get(msgid, fallback)
+ return self._msgs_state.get(msgid, True)
+
+ def add_message(
+ self, msgid, line=None, node=None, args=None, confidence=None, col_offset=None
+ ):
+ """Adds a message given by ID or name.
+
+ If provided, the message string is expanded using args.
+
+ AST checkers must provide the node argument (but may optionally
+ provide line if the line number is different), raw and token checkers
+ must provide the line argument.
+ """
+ if confidence is None:
+ confidence = UNDEFINED
+ message_definitions = self.msgs_store.get_message_definitions(msgid)
+ for message_definition in message_definitions:
+ self.add_one_message(
+ message_definition, line, node, args, confidence, col_offset
+ )
+
+ @staticmethod
+ def check_message_definition(message_definition, line, node):
+ if message_definition.msgid[0] not in _SCOPE_EXEMPT:
+ # Fatal messages and reports are special, the node/scope distinction
+ # does not apply to them.
+ if message_definition.scope == WarningScope.LINE:
+ if line is None:
+ raise InvalidMessageError(
+ "Message %s must provide line, got None"
+ % message_definition.msgid
+ )
+ if node is not None:
+ raise InvalidMessageError(
+ "Message %s must only provide line, "
+ "got line=%s, node=%s" % (message_definition.msgid, line, node)
+ )
+ elif message_definition.scope == WarningScope.NODE:
+ # Node-based warnings may provide an override line.
+ if node is None:
+ raise InvalidMessageError(
+ "Message %s must provide Node, got None"
+ % message_definition.msgid
+ )
+
+ def add_one_message(
+ self, message_definition, line, node, args, confidence, col_offset
+ ):
+ self.check_message_definition(message_definition, line, node)
+ if line is None and node is not None:
+ line = node.fromlineno
+ if col_offset is None and hasattr(node, "col_offset"):
+ col_offset = node.col_offset
+
+ # should this message be displayed
+ if not self.is_message_enabled(message_definition.msgid, line, confidence):
+ self.file_state.handle_ignored_message(
+ self.get_message_state_scope(
+ message_definition.msgid, line, confidence
+ ),
+ message_definition.msgid,
+ line,
+ node,
+ args,
+ confidence,
+ )
+ return
+ # update stats
+ msg_cat = MSG_TYPES[message_definition.msgid[0]]
+ self.msg_status |= MSG_TYPES_STATUS[message_definition.msgid[0]]
+ self.stats[msg_cat] += 1
+ self.stats["by_module"][self.current_name][msg_cat] += 1
+ try:
+ self.stats["by_msg"][message_definition.symbol] += 1
+ except KeyError:
+ self.stats["by_msg"][message_definition.symbol] = 1
+ # expand message ?
+ msg = message_definition.msg
+ if args:
+ msg %= args
+ # get module and object
+ if node is None:
+ module, obj = self.current_name, ""
+ abspath = self.current_file
+ else:
+ module, obj = get_module_and_frameid(node)
+ abspath = node.root().file
+ path = abspath.replace(self.reporter.path_strip_prefix, "", 1)
+ # add the message
+ self.reporter.handle_message(
+ Message(
+ message_definition.msgid,
+ message_definition.symbol,
+ (abspath, path, module, obj, line or 1, col_offset or 0),
+ msg,
+ confidence,
+ )
+ )
+
+ def _get_checkers_infos(self):
+ by_checker = {}
+ for checker in self.get_checkers():
+ name = checker.name
+ if name != "master":
+ try:
+ by_checker[name]["checker"] = checker
+ by_checker[name]["options"] += checker.options_and_values()
+ by_checker[name]["msgs"].update(checker.msgs)
+ by_checker[name]["reports"] += checker.reports
+ except KeyError:
+ by_checker[name] = {
+ "checker": checker,
+ "options": list(checker.options_and_values()),
+ "msgs": dict(checker.msgs),
+ "reports": list(checker.reports),
+ }
+ return by_checker
+
+ def get_checkers_documentation(self):
+ result = get_rst_title("Pylint global options and switches", "-")
+ result += """
+Pylint provides global options and switches.
+
+"""
+ for checker in self.get_checkers():
+ name = checker.name
+ if name == MAIN_CHECKER_NAME:
+ if checker.options:
+ for section, options in checker.options_by_section():
+ if section is None:
+ title = "General options"
+ else:
+ title = "%s options" % section.capitalize()
+ result += get_rst_title(title, "~")
+ result += "%s\n" % get_rst_section(None, options)
+ result += get_rst_title("Pylint checkers' options and switches", "-")
+ result += """\
+
+Pylint checkers can provide three set of features:
+
+* options that control their execution,
+* messages that they can raise,
+* reports that they can generate.
+
+Below is a list of all checkers and their features.
+
+"""
+ by_checker = self._get_checkers_infos()
+ for checker in sorted(by_checker):
+ information = by_checker[checker]
+ checker = information["checker"]
+ del information["checker"]
+ result += checker.get_full_documentation(**information)
+ return result
+
+ def print_full_documentation(self, stream=None):
+ """output a full documentation in ReST format"""
+ if not stream:
+ stream = sys.stdout
+ print(self.get_checkers_documentation()[:-1], file=stream)
+
+ @staticmethod
+ def _print_checker_doc(information, stream=None):
+ """Helper method for print_full_documentation.
+
+ Also used by doc/exts/pylint_extensions.py.
+ """
+ if not stream:
+ stream = sys.stdout
+ checker = information["checker"]
+ del information["checker"]
+ print(checker.get_full_documentation(**information)[:-1], file=stream)
diff --git a/venv/Lib/site-packages/pylint/message/message_id_store.py b/venv/Lib/site-packages/pylint/message/message_id_store.py
new file mode 100644
index 0000000..756888a
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/message/message_id_store.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+from typing import List
+
+from pylint.exceptions import InvalidMessageError, UnknownMessageError
+
+
+class MessageIdStore:
+
+ """The MessageIdStore store MessageId and make sure that there is a 1-1 relation between msgid and symbol."""
+
+ def __init__(self):
+ self.__msgid_to_symbol = {}
+ self.__symbol_to_msgid = {}
+ self.__old_names = {}
+
+ def __len__(self):
+ return len(self.__msgid_to_symbol)
+
+ def __repr__(self):
+ result = "MessageIdStore: [\n"
+ for msgid, symbol in self.__msgid_to_symbol.items():
+ result += " - {msgid} ({symbol})\n".format(msgid=msgid, symbol=symbol)
+ result += "]"
+ return result
+
+ def get_symbol(self, msgid: str) -> str:
+ return self.__msgid_to_symbol[msgid]
+
+ def get_msgid(self, symbol: str) -> str:
+ return self.__symbol_to_msgid[symbol]
+
+ def register_message_definition(self, message_definition):
+ self.check_msgid_and_symbol(message_definition.msgid, message_definition.symbol)
+ self.add_msgid_and_symbol(message_definition.msgid, message_definition.symbol)
+ for old_msgid, old_symbol in message_definition.old_names:
+ self.check_msgid_and_symbol(old_msgid, old_symbol)
+ self.add_legacy_msgid_and_symbol(
+ old_msgid, old_symbol, message_definition.msgid
+ )
+
+ def add_msgid_and_symbol(self, msgid: str, symbol: str) -> None:
+ """Add valid message id.
+
+ There is a little duplication with add_legacy_msgid_and_symbol to avoid a function call,
+ this is called a lot at initialization."""
+ self.__msgid_to_symbol[msgid] = symbol
+ self.__symbol_to_msgid[symbol] = msgid
+
+ def add_legacy_msgid_and_symbol(self, msgid: str, symbol: str, new_msgid: str):
+ """Add valid legacy message id.
+
+ There is a little duplication with add_msgid_and_symbol to avoid a function call,
+ this is called a lot at initialization."""
+ self.__msgid_to_symbol[msgid] = symbol
+ self.__symbol_to_msgid[symbol] = msgid
+ existing_old_names = self.__old_names.get(msgid, [])
+ existing_old_names.append(new_msgid)
+ self.__old_names[msgid] = existing_old_names
+
+ def check_msgid_and_symbol(self, msgid: str, symbol: str) -> None:
+ existing_msgid = self.__symbol_to_msgid.get(symbol)
+ existing_symbol = self.__msgid_to_symbol.get(msgid)
+ if existing_symbol is None and existing_msgid is None:
+ return
+ if existing_msgid is not None:
+ if existing_msgid != msgid:
+ self._raise_duplicate_msgid(symbol, msgid, existing_msgid)
+ if existing_symbol != symbol:
+ self._raise_duplicate_symbol(msgid, symbol, existing_symbol)
+
+ @staticmethod
+ def _raise_duplicate_symbol(msgid, symbol, other_symbol):
+ """Raise an error when a symbol is duplicated.
+
+ :param str msgid: The msgid corresponding to the symbols
+ :param str symbol: Offending symbol
+ :param str other_symbol: Other offending symbol
+ :raises InvalidMessageError:"""
+ symbols = [symbol, other_symbol]
+ symbols.sort()
+ error_message = "Message id '{msgid}' cannot have both ".format(msgid=msgid)
+ error_message += "'{other_symbol}' and '{symbol}' as symbolic name.".format(
+ other_symbol=symbols[0], symbol=symbols[1]
+ )
+ raise InvalidMessageError(error_message)
+
+ @staticmethod
+ def _raise_duplicate_msgid(symbol, msgid, other_msgid):
+ """Raise an error when a msgid is duplicated.
+
+ :param str symbol: The symbol corresponding to the msgids
+ :param str msgid: Offending msgid
+ :param str other_msgid: Other offending msgid
+ :raises InvalidMessageError:"""
+ msgids = [msgid, other_msgid]
+ msgids.sort()
+ error_message = (
+ "Message symbol '{symbol}' cannot be used for "
+ "'{other_msgid}' and '{msgid}' at the same time."
+ " If you're creating an 'old_names' use 'old-{symbol}' as the old symbol."
+ ).format(symbol=symbol, other_msgid=msgids[0], msgid=msgids[1])
+ raise InvalidMessageError(error_message)
+
+ def get_active_msgids(self, msgid_or_symbol: str) -> List[str]:
+ """Return msgids but the input can be a symbol."""
+ # Only msgid can have a digit as second letter
+ is_msgid = msgid_or_symbol[1:].isdigit()
+ if is_msgid:
+ msgid = msgid_or_symbol.upper()
+ symbol = self.__msgid_to_symbol.get(msgid)
+ else:
+ msgid = self.__symbol_to_msgid.get(msgid_or_symbol)
+ symbol = msgid_or_symbol
+ if not msgid or not symbol:
+ error_msg = "No such message id or symbol '{msgid_or_symbol}'.".format(
+ msgid_or_symbol=msgid_or_symbol
+ )
+ raise UnknownMessageError(error_msg)
+ # logging.debug(
+ # "Return for {} and msgid {} is {}".format(
+ # msgid_or_symbol, msgid, self.__old_names.get(msgid, [msgid])
+ # )
+ # )
+ return self.__old_names.get(msgid, [msgid])
diff --git a/venv/Lib/site-packages/pylint/pyreverse/__init__.py b/venv/Lib/site-packages/pylint/pyreverse/__init__.py
new file mode 100644
index 0000000..9ca1da5
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/__init__.py
@@ -0,0 +1,8 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""
+pyreverse.extensions
+"""
+
+__revision__ = "$Id $"
diff --git a/venv/Lib/site-packages/pylint/pyreverse/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..6054dd9
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/pyreverse/__pycache__/diadefslib.cpython-37.pyc b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/diadefslib.cpython-37.pyc
new file mode 100644
index 0000000..64bdd6b
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/diadefslib.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/pyreverse/__pycache__/diagrams.cpython-37.pyc b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/diagrams.cpython-37.pyc
new file mode 100644
index 0000000..cd5a663
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/diagrams.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/pyreverse/__pycache__/inspector.cpython-37.pyc b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/inspector.cpython-37.pyc
new file mode 100644
index 0000000..0bcfb4d
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/inspector.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/pyreverse/__pycache__/main.cpython-37.pyc b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/main.cpython-37.pyc
new file mode 100644
index 0000000..c8f9398
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/main.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/pyreverse/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/utils.cpython-37.pyc
new file mode 100644
index 0000000..1711f15
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/pyreverse/__pycache__/vcgutils.cpython-37.pyc b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/vcgutils.cpython-37.pyc
new file mode 100644
index 0000000..f1a93f5
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/vcgutils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/pyreverse/__pycache__/writer.cpython-37.pyc b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/writer.cpython-37.pyc
new file mode 100644
index 0000000..a0ac15c
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/__pycache__/writer.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/pyreverse/diadefslib.py b/venv/Lib/site-packages/pylint/pyreverse/diadefslib.py
new file mode 100644
index 0000000..de4e9fd
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/diadefslib.py
@@ -0,0 +1,238 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006, 2008-2010, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""handle diagram generation options for class diagram or default diagrams
+"""
+
+import astroid
+
+from pylint.pyreverse.diagrams import ClassDiagram, PackageDiagram
+from pylint.pyreverse.utils import LocalsVisitor
+
+BUILTINS_NAME = "builtins"
+
+# diagram generators ##########################################################
+
+
+class DiaDefGenerator:
+ """handle diagram generation options"""
+
+ def __init__(self, linker, handler):
+ """common Diagram Handler initialization"""
+ self.config = handler.config
+ self._set_default_options()
+ self.linker = linker
+ self.classdiagram = None # defined by subclasses
+
+ def get_title(self, node):
+ """get title for objects"""
+ title = node.name
+ if self.module_names:
+ title = "%s.%s" % (node.root().name, title)
+ return title
+
+ def _set_option(self, option):
+ """activate some options if not explicitly deactivated"""
+ # if we have a class diagram, we want more information by default;
+ # so if the option is None, we return True
+ if option is None:
+ return bool(self.config.classes)
+ return option
+
+ def _set_default_options(self):
+ """set different default options with _default dictionary"""
+ self.module_names = self._set_option(self.config.module_names)
+ all_ancestors = self._set_option(self.config.all_ancestors)
+ all_associated = self._set_option(self.config.all_associated)
+ anc_level, association_level = (0, 0)
+ if all_ancestors:
+ anc_level = -1
+ if all_associated:
+ association_level = -1
+ if self.config.show_ancestors is not None:
+ anc_level = self.config.show_ancestors
+ if self.config.show_associated is not None:
+ association_level = self.config.show_associated
+ self.anc_level, self.association_level = anc_level, association_level
+
+ def _get_levels(self):
+ """help function for search levels"""
+ return self.anc_level, self.association_level
+
+ def show_node(self, node):
+ """true if builtins and not show_builtins"""
+ if self.config.show_builtin:
+ return True
+ return node.root().name != BUILTINS_NAME
+
+ def add_class(self, node):
+ """visit one class and add it to diagram"""
+ self.linker.visit(node)
+ self.classdiagram.add_object(self.get_title(node), node)
+
+ def get_ancestors(self, node, level):
+ """return ancestor nodes of a class node"""
+ if level == 0:
+ return
+ for ancestor in node.ancestors(recurs=False):
+ if not self.show_node(ancestor):
+ continue
+ yield ancestor
+
+ def get_associated(self, klass_node, level):
+ """return associated nodes of a class node"""
+ if level == 0:
+ return
+ for association_nodes in list(klass_node.instance_attrs_type.values()) + list(
+ klass_node.locals_type.values()
+ ):
+ for node in association_nodes:
+ if isinstance(node, astroid.Instance):
+ node = node._proxied
+ if not (isinstance(node, astroid.ClassDef) and self.show_node(node)):
+ continue
+ yield node
+
+ def extract_classes(self, klass_node, anc_level, association_level):
+ """extract recursively classes related to klass_node"""
+ if self.classdiagram.has_node(klass_node) or not self.show_node(klass_node):
+ return
+ self.add_class(klass_node)
+
+ for ancestor in self.get_ancestors(klass_node, anc_level):
+ self.extract_classes(ancestor, anc_level - 1, association_level)
+
+ for node in self.get_associated(klass_node, association_level):
+ self.extract_classes(node, anc_level, association_level - 1)
+
+
+class DefaultDiadefGenerator(LocalsVisitor, DiaDefGenerator):
+ """generate minimum diagram definition for the project :
+
+ * a package diagram including project's modules
+ * a class diagram including project's classes
+ """
+
+ def __init__(self, linker, handler):
+ DiaDefGenerator.__init__(self, linker, handler)
+ LocalsVisitor.__init__(self)
+
+ def visit_project(self, node):
+ """visit a pyreverse.utils.Project node
+
+ create a diagram definition for packages
+ """
+ mode = self.config.mode
+ if len(node.modules) > 1:
+ self.pkgdiagram = PackageDiagram("packages %s" % node.name, mode)
+ else:
+ self.pkgdiagram = None
+ self.classdiagram = ClassDiagram("classes %s" % node.name, mode)
+
+ def leave_project(self, node): # pylint: disable=unused-argument
+ """leave the pyreverse.utils.Project node
+
+ return the generated diagram definition
+ """
+ if self.pkgdiagram:
+ return self.pkgdiagram, self.classdiagram
+ return (self.classdiagram,)
+
+ def visit_module(self, node):
+ """visit an astroid.Module node
+
+ add this class to the package diagram definition
+ """
+ if self.pkgdiagram:
+ self.linker.visit(node)
+ self.pkgdiagram.add_object(node.name, node)
+
+ def visit_classdef(self, node):
+ """visit an astroid.Class node
+
+ add this class to the class diagram definition
+ """
+ anc_level, association_level = self._get_levels()
+ self.extract_classes(node, anc_level, association_level)
+
+ def visit_importfrom(self, node):
+ """visit astroid.ImportFrom and catch modules for package diagram
+ """
+ if self.pkgdiagram:
+ self.pkgdiagram.add_from_depend(node, node.modname)
+
+
+class ClassDiadefGenerator(DiaDefGenerator):
+ """generate a class diagram definition including all classes related to a
+ given class
+ """
+
+ def __init__(self, linker, handler):
+ DiaDefGenerator.__init__(self, linker, handler)
+
+ def class_diagram(self, project, klass):
+ """return a class diagram definition for the given klass and its
+ related klasses
+ """
+
+ self.classdiagram = ClassDiagram(klass, self.config.mode)
+ if len(project.modules) > 1:
+ module, klass = klass.rsplit(".", 1)
+ module = project.get_module(module)
+ else:
+ module = project.modules[0]
+ klass = klass.split(".")[-1]
+ klass = next(module.ilookup(klass))
+
+ anc_level, association_level = self._get_levels()
+ self.extract_classes(klass, anc_level, association_level)
+ return self.classdiagram
+
+
+# diagram handler #############################################################
+
+
+class DiadefsHandler:
+ """handle diagram definitions :
+
+ get it from user (i.e. xml files) or generate them
+ """
+
+ def __init__(self, config):
+ self.config = config
+
+ def get_diadefs(self, project, linker):
+ """Get the diagrams configuration data
+
+ :param project:The pyreverse project
+ :type project: pyreverse.utils.Project
+ :param linker: The linker
+ :type linker: pyreverse.inspector.Linker(IdGeneratorMixIn, LocalsVisitor)
+
+ :returns: The list of diagram definitions
+ :rtype: list(:class:`pylint.pyreverse.diagrams.ClassDiagram`)
+ """
+
+ # read and interpret diagram definitions (Diadefs)
+ diagrams = []
+ generator = ClassDiadefGenerator(linker, self)
+ for klass in self.config.classes:
+ diagrams.append(generator.class_diagram(project, klass))
+ if not diagrams:
+ diagrams = DefaultDiadefGenerator(linker, self).visit(project)
+ for diagram in diagrams:
+ diagram.extract_relationships()
+ return diagrams
diff --git a/venv/Lib/site-packages/pylint/pyreverse/diagrams.py b/venv/Lib/site-packages/pylint/pyreverse/diagrams.py
new file mode 100644
index 0000000..b53b845
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/diagrams.py
@@ -0,0 +1,268 @@
+# Copyright (c) 2006, 2008-2010, 2012-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""diagram objects
+"""
+
+import astroid
+
+from pylint.checkers.utils import decorated_with_property
+from pylint.pyreverse.utils import FilterMixIn, is_interface
+
+
+class Figure:
+ """base class for counter handling"""
+
+
+class Relationship(Figure):
+ """a relation ship from an object in the diagram to another
+ """
+
+ def __init__(self, from_object, to_object, relation_type, name=None):
+ Figure.__init__(self)
+ self.from_object = from_object
+ self.to_object = to_object
+ self.type = relation_type
+ self.name = name
+
+
+class DiagramEntity(Figure):
+ """a diagram object, i.e. a label associated to an astroid node
+ """
+
+ def __init__(self, title="No name", node=None):
+ Figure.__init__(self)
+ self.title = title
+ self.node = node
+
+
+class ClassDiagram(Figure, FilterMixIn):
+ """main class diagram handling
+ """
+
+ TYPE = "class"
+
+ def __init__(self, title, mode):
+ FilterMixIn.__init__(self, mode)
+ Figure.__init__(self)
+ self.title = title
+ self.objects = []
+ self.relationships = {}
+ self._nodes = {}
+ self.depends = []
+
+ def get_relationships(self, role):
+ # sorted to get predictable (hence testable) results
+ return sorted(
+ self.relationships.get(role, ()),
+ key=lambda x: (x.from_object.fig_id, x.to_object.fig_id),
+ )
+
+ def add_relationship(self, from_object, to_object, relation_type, name=None):
+ """create a relation ship
+ """
+ rel = Relationship(from_object, to_object, relation_type, name)
+ self.relationships.setdefault(relation_type, []).append(rel)
+
+ def get_relationship(self, from_object, relation_type):
+ """return a relation ship or None
+ """
+ for rel in self.relationships.get(relation_type, ()):
+ if rel.from_object is from_object:
+ return rel
+ raise KeyError(relation_type)
+
+ def get_attrs(self, node):
+ """return visible attributes, possibly with class name"""
+ attrs = []
+ properties = [
+ (n, m)
+ for n, m in node.items()
+ if isinstance(m, astroid.FunctionDef) and decorated_with_property(m)
+ ]
+ for node_name, associated_nodes in (
+ list(node.instance_attrs_type.items())
+ + list(node.locals_type.items())
+ + properties
+ ):
+ if not self.show_attr(node_name):
+ continue
+ names = self.class_names(associated_nodes)
+ if names:
+ node_name = "%s : %s" % (node_name, ", ".join(names))
+ attrs.append(node_name)
+ return sorted(attrs)
+
+ def get_methods(self, node):
+ """return visible methods"""
+ methods = [
+ m
+ for m in node.values()
+ if isinstance(m, astroid.FunctionDef)
+ and not decorated_with_property(m)
+ and self.show_attr(m.name)
+ ]
+ return sorted(methods, key=lambda n: n.name)
+
+ def add_object(self, title, node):
+ """create a diagram object
+ """
+ assert node not in self._nodes
+ ent = DiagramEntity(title, node)
+ self._nodes[node] = ent
+ self.objects.append(ent)
+
+ def class_names(self, nodes):
+ """return class names if needed in diagram"""
+ names = []
+ for node in nodes:
+ if isinstance(node, astroid.Instance):
+ node = node._proxied
+ if (
+ isinstance(node, astroid.ClassDef)
+ and hasattr(node, "name")
+ and not self.has_node(node)
+ ):
+ if node.name not in names:
+ node_name = node.name
+ names.append(node_name)
+ return names
+
+ def nodes(self):
+ """return the list of underlying nodes
+ """
+ return self._nodes.keys()
+
+ def has_node(self, node):
+ """return true if the given node is included in the diagram
+ """
+ return node in self._nodes
+
+ def object_from_node(self, node):
+ """return the diagram object mapped to node
+ """
+ return self._nodes[node]
+
+ def classes(self):
+ """return all class nodes in the diagram"""
+ return [o for o in self.objects if isinstance(o.node, astroid.ClassDef)]
+
+ def classe(self, name):
+ """return a class by its name, raise KeyError if not found
+ """
+ for klass in self.classes():
+ if klass.node.name == name:
+ return klass
+ raise KeyError(name)
+
+ def extract_relationships(self):
+ """extract relation ships between nodes in the diagram
+ """
+ for obj in self.classes():
+ node = obj.node
+ obj.attrs = self.get_attrs(node)
+ obj.methods = self.get_methods(node)
+ # shape
+ if is_interface(node):
+ obj.shape = "interface"
+ else:
+ obj.shape = "class"
+ # inheritance link
+ for par_node in node.ancestors(recurs=False):
+ try:
+ par_obj = self.object_from_node(par_node)
+ self.add_relationship(obj, par_obj, "specialization")
+ except KeyError:
+ continue
+ # implements link
+ for impl_node in node.implements:
+ try:
+ impl_obj = self.object_from_node(impl_node)
+ self.add_relationship(obj, impl_obj, "implements")
+ except KeyError:
+ continue
+ # associations link
+ for name, values in list(node.instance_attrs_type.items()) + list(
+ node.locals_type.items()
+ ):
+ for value in values:
+ if value is astroid.Uninferable:
+ continue
+ if isinstance(value, astroid.Instance):
+ value = value._proxied
+ try:
+ associated_obj = self.object_from_node(value)
+ self.add_relationship(associated_obj, obj, "association", name)
+ except KeyError:
+ continue
+
+
+class PackageDiagram(ClassDiagram):
+ """package diagram handling
+ """
+
+ TYPE = "package"
+
+ def modules(self):
+ """return all module nodes in the diagram"""
+ return [o for o in self.objects if isinstance(o.node, astroid.Module)]
+
+ def module(self, name):
+ """return a module by its name, raise KeyError if not found
+ """
+ for mod in self.modules():
+ if mod.node.name == name:
+ return mod
+ raise KeyError(name)
+
+ def get_module(self, name, node):
+ """return a module by its name, looking also for relative imports;
+ raise KeyError if not found
+ """
+ for mod in self.modules():
+ mod_name = mod.node.name
+ if mod_name == name:
+ return mod
+ # search for fullname of relative import modules
+ package = node.root().name
+ if mod_name == "%s.%s" % (package, name):
+ return mod
+ if mod_name == "%s.%s" % (package.rsplit(".", 1)[0], name):
+ return mod
+ raise KeyError(name)
+
+ def add_from_depend(self, node, from_module):
+ """add dependencies created by from-imports
+ """
+ mod_name = node.root().name
+ obj = self.module(mod_name)
+ if from_module not in obj.node.depends:
+ obj.node.depends.append(from_module)
+
+ def extract_relationships(self):
+ """extract relation ships between nodes in the diagram
+ """
+ ClassDiagram.extract_relationships(self)
+ for obj in self.classes():
+ # ownership
+ try:
+ mod = self.object_from_node(obj.node.root())
+ self.add_relationship(obj, mod, "ownership")
+ except KeyError:
+ continue
+ for obj in self.modules():
+ obj.shape = "package"
+ # dependencies
+ for dep_name in obj.node.depends:
+ try:
+ dep = self.get_module(dep_name, obj.node)
+ except KeyError:
+ continue
+ self.add_relationship(obj, dep, "depends")
diff --git a/venv/Lib/site-packages/pylint/pyreverse/inspector.py b/venv/Lib/site-packages/pylint/pyreverse/inspector.py
new file mode 100644
index 0000000..702b108
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/inspector.py
@@ -0,0 +1,357 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""
+Visitor doing some postprocessing on the astroid tree.
+Try to resolve definitions (namespace) dictionary, relationship...
+"""
+import collections
+import os
+import traceback
+
+import astroid
+from astroid import bases, exceptions, manager, modutils, node_classes
+
+from pylint.pyreverse import utils
+
+
+def _iface_hdlr(_):
+ """Handler used by interfaces to handle suspicious interface nodes."""
+ return True
+
+
+def _astroid_wrapper(func, modname):
+ print("parsing %s..." % modname)
+ try:
+ return func(modname)
+ except exceptions.AstroidBuildingException as exc:
+ print(exc)
+ except Exception as exc: # pylint: disable=broad-except
+ traceback.print_exc()
+
+
+def interfaces(node, herited=True, handler_func=_iface_hdlr):
+ """Return an iterator on interfaces implemented by the given class node."""
+ try:
+ implements = bases.Instance(node).getattr("__implements__")[0]
+ except exceptions.NotFoundError:
+ return
+ if not herited and implements.frame() is not node:
+ return
+ found = set()
+ missing = False
+ for iface in node_classes.unpack_infer(implements):
+ if iface is astroid.Uninferable:
+ missing = True
+ continue
+ if iface not in found and handler_func(iface):
+ found.add(iface)
+ yield iface
+ if missing:
+ raise exceptions.InferenceError()
+
+
+class IdGeneratorMixIn:
+ """Mixin adding the ability to generate integer uid."""
+
+ def __init__(self, start_value=0):
+ self.id_count = start_value
+
+ def init_counter(self, start_value=0):
+ """init the id counter
+ """
+ self.id_count = start_value
+
+ def generate_id(self):
+ """generate a new identifier
+ """
+ self.id_count += 1
+ return self.id_count
+
+
+class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
+ """Walk on the project tree and resolve relationships.
+
+ According to options the following attributes may be
+ added to visited nodes:
+
+ * uid,
+ a unique identifier for the node (on astroid.Project, astroid.Module,
+ astroid.Class and astroid.locals_type). Only if the linker
+ has been instantiated with tag=True parameter (False by default).
+
+ * Function
+ a mapping from locals names to their bounded value, which may be a
+ constant like a string or an integer, or an astroid node
+ (on astroid.Module, astroid.Class and astroid.Function).
+
+ * instance_attrs_type
+ as locals_type but for klass member attributes (only on astroid.Class)
+
+ * implements,
+ list of implemented interface _objects_ (only on astroid.Class nodes)
+ """
+
+ def __init__(self, project, inherited_interfaces=0, tag=False):
+ IdGeneratorMixIn.__init__(self)
+ utils.LocalsVisitor.__init__(self)
+ # take inherited interface in consideration or not
+ self.inherited_interfaces = inherited_interfaces
+ # tag nodes or not
+ self.tag = tag
+ # visited project
+ self.project = project
+
+ def visit_project(self, node):
+ """visit a pyreverse.utils.Project node
+
+ * optionally tag the node with a unique id
+ """
+ if self.tag:
+ node.uid = self.generate_id()
+ for module in node.modules:
+ self.visit(module)
+
+ def visit_package(self, node):
+ """visit an astroid.Package node
+
+ * optionally tag the node with a unique id
+ """
+ if self.tag:
+ node.uid = self.generate_id()
+ for subelmt in node.values():
+ self.visit(subelmt)
+
+ def visit_module(self, node):
+ """visit an astroid.Module node
+
+ * set the locals_type mapping
+ * set the depends mapping
+ * optionally tag the node with a unique id
+ """
+ if hasattr(node, "locals_type"):
+ return
+ node.locals_type = collections.defaultdict(list)
+ node.depends = []
+ if self.tag:
+ node.uid = self.generate_id()
+
+ def visit_classdef(self, node):
+ """visit an astroid.Class node
+
+ * set the locals_type and instance_attrs_type mappings
+ * set the implements list and build it
+ * optionally tag the node with a unique id
+ """
+ if hasattr(node, "locals_type"):
+ return
+ node.locals_type = collections.defaultdict(list)
+ if self.tag:
+ node.uid = self.generate_id()
+ # resolve ancestors
+ for baseobj in node.ancestors(recurs=False):
+ specializations = getattr(baseobj, "specializations", [])
+ specializations.append(node)
+ baseobj.specializations = specializations
+ # resolve instance attributes
+ node.instance_attrs_type = collections.defaultdict(list)
+ for assignattrs in node.instance_attrs.values():
+ for assignattr in assignattrs:
+ self.handle_assignattr_type(assignattr, node)
+ # resolve implemented interface
+ try:
+ node.implements = list(interfaces(node, self.inherited_interfaces))
+ except astroid.InferenceError:
+ node.implements = ()
+
+ def visit_functiondef(self, node):
+ """visit an astroid.Function node
+
+ * set the locals_type mapping
+ * optionally tag the node with a unique id
+ """
+ if hasattr(node, "locals_type"):
+ return
+ node.locals_type = collections.defaultdict(list)
+ if self.tag:
+ node.uid = self.generate_id()
+
+ link_project = visit_project
+ link_module = visit_module
+ link_class = visit_classdef
+ link_function = visit_functiondef
+
+ def visit_assignname(self, node):
+ """visit an astroid.AssignName node
+
+ handle locals_type
+ """
+ # avoid double parsing done by different Linkers.visit
+ # running over the same project:
+ if hasattr(node, "_handled"):
+ return
+ node._handled = True
+ if node.name in node.frame():
+ frame = node.frame()
+ else:
+ # the name has been defined as 'global' in the frame and belongs
+ # there.
+ frame = node.root()
+ try:
+ if not hasattr(frame, "locals_type"):
+ # If the frame doesn't have a locals_type yet,
+ # it means it wasn't yet visited. Visit it now
+ # to add what's missing from it.
+ if isinstance(frame, astroid.ClassDef):
+ self.visit_classdef(frame)
+ elif isinstance(frame, astroid.FunctionDef):
+ self.visit_functiondef(frame)
+ else:
+ self.visit_module(frame)
+
+ current = frame.locals_type[node.name]
+ values = set(node.infer())
+ frame.locals_type[node.name] = list(set(current) | values)
+ except astroid.InferenceError:
+ pass
+
+ @staticmethod
+ def handle_assignattr_type(node, parent):
+ """handle an astroid.assignattr node
+
+ handle instance_attrs_type
+ """
+ try:
+ values = set(node.infer())
+ current = set(parent.instance_attrs_type[node.attrname])
+ parent.instance_attrs_type[node.attrname] = list(current | values)
+ except astroid.InferenceError:
+ pass
+
+ def visit_import(self, node):
+ """visit an astroid.Import node
+
+ resolve module dependencies
+ """
+ context_file = node.root().file
+ for name in node.names:
+ relative = modutils.is_relative(name[0], context_file)
+ self._imported_module(node, name[0], relative)
+
+ def visit_importfrom(self, node):
+ """visit an astroid.ImportFrom node
+
+ resolve module dependencies
+ """
+ basename = node.modname
+ context_file = node.root().file
+ if context_file is not None:
+ relative = modutils.is_relative(basename, context_file)
+ else:
+ relative = False
+ for name in node.names:
+ if name[0] == "*":
+ continue
+ # analyze dependencies
+ fullname = "%s.%s" % (basename, name[0])
+ if fullname.find(".") > -1:
+ try:
+ fullname = modutils.get_module_part(fullname, context_file)
+ except ImportError:
+ continue
+ if fullname != basename:
+ self._imported_module(node, fullname, relative)
+
+ def compute_module(self, context_name, mod_path):
+ """return true if the module should be added to dependencies"""
+ package_dir = os.path.dirname(self.project.path)
+ if context_name == mod_path:
+ return 0
+ if modutils.is_standard_module(mod_path, (package_dir,)):
+ return 1
+ return 0
+
+ def _imported_module(self, node, mod_path, relative):
+ """Notify an imported module, used to analyze dependencies"""
+ module = node.root()
+ context_name = module.name
+ if relative:
+ mod_path = "%s.%s" % (".".join(context_name.split(".")[:-1]), mod_path)
+ if self.compute_module(context_name, mod_path):
+ # handle dependencies
+ if not hasattr(module, "depends"):
+ module.depends = []
+ mod_paths = module.depends
+ if mod_path not in mod_paths:
+ mod_paths.append(mod_path)
+
+
+class Project:
+ """a project handle a set of modules / packages"""
+
+ def __init__(self, name=""):
+ self.name = name
+ self.path = None
+ self.modules = []
+ self.locals = {}
+ self.__getitem__ = self.locals.__getitem__
+ self.__iter__ = self.locals.__iter__
+ self.values = self.locals.values
+ self.keys = self.locals.keys
+ self.items = self.locals.items
+
+ def add_module(self, node):
+ self.locals[node.name] = node
+ self.modules.append(node)
+
+ def get_module(self, name):
+ return self.locals[name]
+
+ def get_children(self):
+ return self.modules
+
+ def __repr__(self):
+ return "<Project %r at %s (%s modules)>" % (
+ self.name,
+ id(self),
+ len(self.modules),
+ )
+
+
+def project_from_files(
+ files, func_wrapper=_astroid_wrapper, project_name="no name", black_list=("CVS",)
+):
+ """return a Project from a list of files or modules"""
+ # build the project representation
+ astroid_manager = manager.AstroidManager()
+ project = Project(project_name)
+ for something in files:
+ if not os.path.exists(something):
+ fpath = modutils.file_from_modpath(something.split("."))
+ elif os.path.isdir(something):
+ fpath = os.path.join(something, "__init__.py")
+ else:
+ fpath = something
+ ast = func_wrapper(astroid_manager.ast_from_file, fpath)
+ if ast is None:
+ continue
+ project.path = project.path or ast.file
+ project.add_module(ast)
+ base_name = ast.name
+ # recurse in package except if __init__ was explicitly given
+ if ast.package and something.find("__init__") == -1:
+ # recurse on others packages / modules if this is a package
+ for fpath in modutils.get_module_files(
+ os.path.dirname(ast.file), black_list
+ ):
+ ast = func_wrapper(astroid_manager.ast_from_file, fpath)
+ if ast is None or ast.name == base_name:
+ continue
+ project.add_module(ast)
+ return project
diff --git a/venv/Lib/site-packages/pylint/pyreverse/main.py b/venv/Lib/site-packages/pylint/pyreverse/main.py
new file mode 100644
index 0000000..652b954
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/main.py
@@ -0,0 +1,214 @@
+# Copyright (c) 2008-2010, 2012-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Alexander Pervakov <frost.nzcr4@jagmort.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""
+ %prog [options] <packages>
+
+ create UML diagrams for classes and modules in <packages>
+"""
+import os
+import subprocess
+import sys
+
+from pylint.config import ConfigurationMixIn
+from pylint.pyreverse import writer
+from pylint.pyreverse.diadefslib import DiadefsHandler
+from pylint.pyreverse.inspector import Linker, project_from_files
+from pylint.pyreverse.utils import insert_default_options
+
+OPTIONS = (
+ (
+ "filter-mode",
+ dict(
+ short="f",
+ default="PUB_ONLY",
+ dest="mode",
+ type="string",
+ action="store",
+ metavar="<mode>",
+ help="""filter attributes and functions according to
+ <mode>. Correct modes are :
+ 'PUB_ONLY' filter all non public attributes
+ [DEFAULT], equivalent to PRIVATE+SPECIAL_A
+ 'ALL' no filter
+ 'SPECIAL' filter Python special functions
+ except constructor
+ 'OTHER' filter protected and private
+ attributes""",
+ ),
+ ),
+ (
+ "class",
+ dict(
+ short="c",
+ action="append",
+ metavar="<class>",
+ dest="classes",
+ default=[],
+ help="create a class diagram with all classes related to <class>;\
+ this uses by default the options -ASmy",
+ ),
+ ),
+ (
+ "show-ancestors",
+ dict(
+ short="a",
+ action="store",
+ metavar="<ancestor>",
+ type="int",
+ help="show <ancestor> generations of ancestor classes not in <projects>",
+ ),
+ ),
+ (
+ "all-ancestors",
+ dict(
+ short="A",
+ default=None,
+ help="show all ancestors off all classes in <projects>",
+ ),
+ ),
+ (
+ "show-associated",
+ dict(
+ short="s",
+ action="store",
+ metavar="<association_level>",
+ type="int",
+ help="show <association_level> levels of associated classes not in <projects>",
+ ),
+ ),
+ (
+ "all-associated",
+ dict(
+ short="S",
+ default=None,
+ help="show recursively all associated off all associated classes",
+ ),
+ ),
+ (
+ "show-builtin",
+ dict(
+ short="b",
+ action="store_true",
+ default=False,
+ help="include builtin objects in representation of classes",
+ ),
+ ),
+ (
+ "module-names",
+ dict(
+ short="m",
+ default=None,
+ type="yn",
+ metavar="[yn]",
+ help="include module name in representation of classes",
+ ),
+ ),
+ (
+ "only-classnames",
+ dict(
+ short="k",
+ action="store_true",
+ default=False,
+ help="don't show attributes and methods in the class boxes; \
+this disables -f values",
+ ),
+ ),
+ (
+ "output",
+ dict(
+ short="o",
+ dest="output_format",
+ action="store",
+ default="dot",
+ metavar="<format>",
+ help="create a *.<format> output file if format available.",
+ ),
+ ),
+ (
+ "ignore",
+ {
+ "type": "csv",
+ "metavar": "<file[,file...]>",
+ "dest": "black_list",
+ "default": ("CVS",),
+ "help": "Add files or directories to the blacklist. They "
+ "should be base names, not paths.",
+ },
+ ),
+ (
+ "project",
+ {
+ "default": "",
+ "type": "string",
+ "short": "p",
+ "metavar": "<project name>",
+ "help": "set the project name.",
+ },
+ ),
+)
+
+
+def _check_graphviz_available(output_format):
+ """check if we need graphviz for different output format"""
+ try:
+ subprocess.call(["dot", "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ except OSError:
+ print(
+ "The output format '%s' is currently not available.\n"
+ "Please install 'Graphviz' to have other output formats "
+ "than 'dot' or 'vcg'." % output_format
+ )
+ sys.exit(32)
+
+
+class Run(ConfigurationMixIn):
+ """base class providing common behaviour for pyreverse commands"""
+
+ options = OPTIONS # type: ignore
+
+ def __init__(self, args):
+ ConfigurationMixIn.__init__(self, usage=__doc__)
+ insert_default_options()
+ args = self.load_command_line_configuration()
+ if self.config.output_format not in ("dot", "vcg"):
+ _check_graphviz_available(self.config.output_format)
+
+ sys.exit(self.run(args))
+
+ def run(self, args):
+ """checking arguments and run project"""
+ if not args:
+ print(self.help())
+ return 1
+ # insert current working directory to the python path to recognize
+ # dependencies to local modules even if cwd is not in the PYTHONPATH
+ sys.path.insert(0, os.getcwd())
+ try:
+ project = project_from_files(
+ args,
+ project_name=self.config.project,
+ black_list=self.config.black_list,
+ )
+ linker = Linker(project, tag=True)
+ handler = DiadefsHandler(self.config)
+ diadefs = handler.get_diadefs(project, linker)
+ finally:
+ sys.path.pop(0)
+
+ if self.config.output_format == "vcg":
+ writer.VCGWriter(self.config).write(diadefs)
+ else:
+ writer.DotWriter(self.config).write(diadefs)
+ return 0
+
+
+if __name__ == "__main__":
+ Run(sys.argv[1:])
diff --git a/venv/Lib/site-packages/pylint/pyreverse/utils.py b/venv/Lib/site-packages/pylint/pyreverse/utils.py
new file mode 100644
index 0000000..5a1e7e2
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/utils.py
@@ -0,0 +1,220 @@
+# Copyright (c) 2006, 2008, 2010, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2017 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""
+generic classes/functions for pyreverse core/extensions
+"""
+import os
+import re
+import sys
+
+########### pyreverse option utils ##############################
+
+
+RCFILE = ".pyreverserc"
+
+
+def get_default_options():
+ """
+ Read config file and return list of options
+ """
+ options = []
+ home = os.environ.get("HOME", "")
+ if home:
+ rcfile = os.path.join(home, RCFILE)
+ try:
+ options = open(rcfile).read().split()
+ except IOError:
+ pass # ignore if no config file found
+ return options
+
+
+def insert_default_options():
+ """insert default options to sys.argv
+ """
+ options = get_default_options()
+ options.reverse()
+ for arg in options:
+ sys.argv.insert(1, arg)
+
+
+# astroid utilities ###########################################################
+
+SPECIAL = re.compile("^__[A-Za-z0-9]+[A-Za-z0-9_]*__$")
+PRIVATE = re.compile("^__[_A-Za-z0-9]*[A-Za-z0-9]+_?$")
+PROTECTED = re.compile("^_[_A-Za-z0-9]*$")
+
+
+def get_visibility(name):
+ """return the visibility from a name: public, protected, private or special
+ """
+ if SPECIAL.match(name):
+ visibility = "special"
+ elif PRIVATE.match(name):
+ visibility = "private"
+ elif PROTECTED.match(name):
+ visibility = "protected"
+
+ else:
+ visibility = "public"
+ return visibility
+
+
+ABSTRACT = re.compile("^.*Abstract.*")
+FINAL = re.compile("^[A-Z_]*$")
+
+
+def is_abstract(node):
+ """return true if the given class node correspond to an abstract class
+ definition
+ """
+ return ABSTRACT.match(node.name)
+
+
+def is_final(node):
+ """return true if the given class/function node correspond to final
+ definition
+ """
+ return FINAL.match(node.name)
+
+
+def is_interface(node):
+ # bw compat
+ return node.type == "interface"
+
+
+def is_exception(node):
+ # bw compat
+ return node.type == "exception"
+
+
+# Helpers #####################################################################
+
+_CONSTRUCTOR = 1
+_SPECIAL = 2
+_PROTECTED = 4
+_PRIVATE = 8
+MODES = {
+ "ALL": 0,
+ "PUB_ONLY": _SPECIAL + _PROTECTED + _PRIVATE,
+ "SPECIAL": _SPECIAL,
+ "OTHER": _PROTECTED + _PRIVATE,
+}
+VIS_MOD = {
+ "special": _SPECIAL,
+ "protected": _PROTECTED,
+ "private": _PRIVATE,
+ "public": 0,
+}
+
+
+class FilterMixIn:
+ """filter nodes according to a mode and nodes' visibility
+ """
+
+ def __init__(self, mode):
+ "init filter modes"
+ __mode = 0
+ for nummod in mode.split("+"):
+ try:
+ __mode += MODES[nummod]
+ except KeyError as ex:
+ print("Unknown filter mode %s" % ex, file=sys.stderr)
+ self.__mode = __mode
+
+ def show_attr(self, node):
+ """return true if the node should be treated
+ """
+ visibility = get_visibility(getattr(node, "name", node))
+ return not self.__mode & VIS_MOD[visibility]
+
+
+class ASTWalker:
+ """a walker visiting a tree in preorder, calling on the handler:
+
+ * visit_<class name> on entering a node, where class name is the class of
+ the node in lower case
+
+ * leave_<class name> on leaving a node, where class name is the class of
+ the node in lower case
+ """
+
+ def __init__(self, handler):
+ self.handler = handler
+ self._cache = {}
+
+ def walk(self, node, _done=None):
+ """walk on the tree from <node>, getting callbacks from handler"""
+ if _done is None:
+ _done = set()
+ if node in _done:
+ raise AssertionError((id(node), node, node.parent))
+ _done.add(node)
+ self.visit(node)
+ for child_node in node.get_children():
+ assert child_node is not node
+ self.walk(child_node, _done)
+ self.leave(node)
+ assert node.parent is not node
+
+ def get_callbacks(self, node):
+ """get callbacks from handler for the visited node"""
+ klass = node.__class__
+ methods = self._cache.get(klass)
+ if methods is None:
+ handler = self.handler
+ kid = klass.__name__.lower()
+ e_method = getattr(
+ handler, "visit_%s" % kid, getattr(handler, "visit_default", None)
+ )
+ l_method = getattr(
+ handler, "leave_%s" % kid, getattr(handler, "leave_default", None)
+ )
+ self._cache[klass] = (e_method, l_method)
+ else:
+ e_method, l_method = methods
+ return e_method, l_method
+
+ def visit(self, node):
+ """walk on the tree from <node>, getting callbacks from handler"""
+ method = self.get_callbacks(node)[0]
+ if method is not None:
+ method(node)
+
+ def leave(self, node):
+ """walk on the tree from <node>, getting callbacks from handler"""
+ method = self.get_callbacks(node)[1]
+ if method is not None:
+ method(node)
+
+
+class LocalsVisitor(ASTWalker):
+ """visit a project by traversing the locals dictionary"""
+
+ def __init__(self):
+ ASTWalker.__init__(self, self)
+ self._visited = set()
+
+ def visit(self, node):
+ """launch the visit starting from the given node"""
+ if node in self._visited:
+ return None
+
+ self._visited.add(node)
+ methods = self.get_callbacks(node)
+ if methods[0] is not None:
+ methods[0](node)
+ if hasattr(node, "locals"): # skip Instance and other proxy
+ for local_node in node.values():
+ self.visit(local_node)
+ if methods[1] is not None:
+ return methods[1](node)
+ return None
diff --git a/venv/Lib/site-packages/pylint/pyreverse/vcgutils.py b/venv/Lib/site-packages/pylint/pyreverse/vcgutils.py
new file mode 100644
index 0000000..89c6911
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/vcgutils.py
@@ -0,0 +1,229 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Functions to generate files readable with Georg Sander's vcg
+(Visualization of Compiler Graphs).
+
+You can download vcg at http://rw4.cs.uni-sb.de/~sander/html/gshome.html
+Note that vcg exists as a debian package.
+
+See vcg's documentation for explanation about the different values that
+maybe used for the functions parameters.
+"""
+
+ATTRS_VAL = {
+ "algos": (
+ "dfs",
+ "tree",
+ "minbackward",
+ "left_to_right",
+ "right_to_left",
+ "top_to_bottom",
+ "bottom_to_top",
+ "maxdepth",
+ "maxdepthslow",
+ "mindepth",
+ "mindepthslow",
+ "mindegree",
+ "minindegree",
+ "minoutdegree",
+ "maxdegree",
+ "maxindegree",
+ "maxoutdegree",
+ ),
+ "booleans": ("yes", "no"),
+ "colors": (
+ "black",
+ "white",
+ "blue",
+ "red",
+ "green",
+ "yellow",
+ "magenta",
+ "lightgrey",
+ "cyan",
+ "darkgrey",
+ "darkblue",
+ "darkred",
+ "darkgreen",
+ "darkyellow",
+ "darkmagenta",
+ "darkcyan",
+ "gold",
+ "lightblue",
+ "lightred",
+ "lightgreen",
+ "lightyellow",
+ "lightmagenta",
+ "lightcyan",
+ "lilac",
+ "turquoise",
+ "aquamarine",
+ "khaki",
+ "purple",
+ "yellowgreen",
+ "pink",
+ "orange",
+ "orchid",
+ ),
+ "shapes": ("box", "ellipse", "rhomb", "triangle"),
+ "textmodes": ("center", "left_justify", "right_justify"),
+ "arrowstyles": ("solid", "line", "none"),
+ "linestyles": ("continuous", "dashed", "dotted", "invisible"),
+}
+
+# meaning of possible values:
+# O -> string
+# 1 -> int
+# list -> value in list
+GRAPH_ATTRS = {
+ "title": 0,
+ "label": 0,
+ "color": ATTRS_VAL["colors"],
+ "textcolor": ATTRS_VAL["colors"],
+ "bordercolor": ATTRS_VAL["colors"],
+ "width": 1,
+ "height": 1,
+ "borderwidth": 1,
+ "textmode": ATTRS_VAL["textmodes"],
+ "shape": ATTRS_VAL["shapes"],
+ "shrink": 1,
+ "stretch": 1,
+ "orientation": ATTRS_VAL["algos"],
+ "vertical_order": 1,
+ "horizontal_order": 1,
+ "xspace": 1,
+ "yspace": 1,
+ "layoutalgorithm": ATTRS_VAL["algos"],
+ "late_edge_labels": ATTRS_VAL["booleans"],
+ "display_edge_labels": ATTRS_VAL["booleans"],
+ "dirty_edge_labels": ATTRS_VAL["booleans"],
+ "finetuning": ATTRS_VAL["booleans"],
+ "manhattan_edges": ATTRS_VAL["booleans"],
+ "smanhattan_edges": ATTRS_VAL["booleans"],
+ "port_sharing": ATTRS_VAL["booleans"],
+ "edges": ATTRS_VAL["booleans"],
+ "nodes": ATTRS_VAL["booleans"],
+ "splines": ATTRS_VAL["booleans"],
+}
+NODE_ATTRS = {
+ "title": 0,
+ "label": 0,
+ "color": ATTRS_VAL["colors"],
+ "textcolor": ATTRS_VAL["colors"],
+ "bordercolor": ATTRS_VAL["colors"],
+ "width": 1,
+ "height": 1,
+ "borderwidth": 1,
+ "textmode": ATTRS_VAL["textmodes"],
+ "shape": ATTRS_VAL["shapes"],
+ "shrink": 1,
+ "stretch": 1,
+ "vertical_order": 1,
+ "horizontal_order": 1,
+}
+EDGE_ATTRS = {
+ "sourcename": 0,
+ "targetname": 0,
+ "label": 0,
+ "linestyle": ATTRS_VAL["linestyles"],
+ "class": 1,
+ "thickness": 0,
+ "color": ATTRS_VAL["colors"],
+ "textcolor": ATTRS_VAL["colors"],
+ "arrowcolor": ATTRS_VAL["colors"],
+ "backarrowcolor": ATTRS_VAL["colors"],
+ "arrowsize": 1,
+ "backarrowsize": 1,
+ "arrowstyle": ATTRS_VAL["arrowstyles"],
+ "backarrowstyle": ATTRS_VAL["arrowstyles"],
+ "textmode": ATTRS_VAL["textmodes"],
+ "priority": 1,
+ "anchor": 1,
+ "horizontal_order": 1,
+}
+
+
+# Misc utilities ###############################################################
+
+
+class VCGPrinter:
+ """A vcg graph writer.
+ """
+
+ def __init__(self, output_stream):
+ self._stream = output_stream
+ self._indent = ""
+
+ def open_graph(self, **args):
+ """open a vcg graph
+ """
+ self._stream.write("%sgraph:{\n" % self._indent)
+ self._inc_indent()
+ self._write_attributes(GRAPH_ATTRS, **args)
+
+ def close_graph(self):
+ """close a vcg graph
+ """
+ self._dec_indent()
+ self._stream.write("%s}\n" % self._indent)
+
+ def node(self, title, **args):
+ """draw a node
+ """
+ self._stream.write('%snode: {title:"%s"' % (self._indent, title))
+ self._write_attributes(NODE_ATTRS, **args)
+ self._stream.write("}\n")
+
+ def edge(self, from_node, to_node, edge_type="", **args):
+ """draw an edge from a node to another.
+ """
+ self._stream.write(
+ '%s%sedge: {sourcename:"%s" targetname:"%s"'
+ % (self._indent, edge_type, from_node, to_node)
+ )
+ self._write_attributes(EDGE_ATTRS, **args)
+ self._stream.write("}\n")
+
+ # private ##################################################################
+
+ def _write_attributes(self, attributes_dict, **args):
+ """write graph, node or edge attributes
+ """
+ for key, value in args.items():
+ try:
+ _type = attributes_dict[key]
+ except KeyError:
+ raise Exception(
+ """no such attribute %s
+possible attributes are %s"""
+ % (key, attributes_dict.keys())
+ )
+
+ if not _type:
+ self._stream.write('%s%s:"%s"\n' % (self._indent, key, value))
+ elif _type == 1:
+ self._stream.write("%s%s:%s\n" % (self._indent, key, int(value)))
+ elif value in _type:
+ self._stream.write("%s%s:%s\n" % (self._indent, key, value))
+ else:
+ raise Exception(
+ """value %s isn\'t correct for attribute %s
+correct values are %s"""
+ % (value, key, _type)
+ )
+
+ def _inc_indent(self):
+ """increment indentation
+ """
+ self._indent = " %s" % self._indent
+
+ def _dec_indent(self):
+ """decrement indentation
+ """
+ self._indent = self._indent[:-2]
diff --git a/venv/Lib/site-packages/pylint/pyreverse/writer.py b/venv/Lib/site-packages/pylint/pyreverse/writer.py
new file mode 100644
index 0000000..609b1ef
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/pyreverse/writer.py
@@ -0,0 +1,213 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2008-2010, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Mike Frysinger <vapier@gentoo.org>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Utilities for creating VCG and Dot diagrams"""
+
+from pylint.graph import DotBackend
+from pylint.pyreverse.utils import is_exception
+from pylint.pyreverse.vcgutils import VCGPrinter
+
+
+class DiagramWriter:
+ """base class for writing project diagrams
+ """
+
+ def __init__(self, config, styles):
+ self.config = config
+ self.pkg_edges, self.inh_edges, self.imp_edges, self.association_edges = styles
+ self.printer = None # defined in set_printer
+
+ def write(self, diadefs):
+ """write files for <project> according to <diadefs>
+ """
+ for diagram in diadefs:
+ basename = diagram.title.strip().replace(" ", "_")
+ file_name = "%s.%s" % (basename, self.config.output_format)
+ self.set_printer(file_name, basename)
+ if diagram.TYPE == "class":
+ self.write_classes(diagram)
+ else:
+ self.write_packages(diagram)
+ self.close_graph()
+
+ def write_packages(self, diagram):
+ """write a package diagram"""
+ # sorted to get predictable (hence testable) results
+ for i, obj in enumerate(sorted(diagram.modules(), key=lambda x: x.title)):
+ self.printer.emit_node(i, label=self.get_title(obj), shape="box")
+ obj.fig_id = i
+ # package dependencies
+ for rel in diagram.get_relationships("depends"):
+ self.printer.emit_edge(
+ rel.from_object.fig_id, rel.to_object.fig_id, **self.pkg_edges
+ )
+
+ def write_classes(self, diagram):
+ """write a class diagram"""
+ # sorted to get predictable (hence testable) results
+ for i, obj in enumerate(sorted(diagram.objects, key=lambda x: x.title)):
+ self.printer.emit_node(i, **self.get_values(obj))
+ obj.fig_id = i
+ # inheritance links
+ for rel in diagram.get_relationships("specialization"):
+ self.printer.emit_edge(
+ rel.from_object.fig_id, rel.to_object.fig_id, **self.inh_edges
+ )
+ # implementation links
+ for rel in diagram.get_relationships("implements"):
+ self.printer.emit_edge(
+ rel.from_object.fig_id, rel.to_object.fig_id, **self.imp_edges
+ )
+ # generate associations
+ for rel in diagram.get_relationships("association"):
+ self.printer.emit_edge(
+ rel.from_object.fig_id,
+ rel.to_object.fig_id,
+ label=rel.name,
+ **self.association_edges
+ )
+
+ def set_printer(self, file_name, basename):
+ """set printer"""
+ raise NotImplementedError
+
+ def get_title(self, obj):
+ """get project title"""
+ raise NotImplementedError
+
+ def get_values(self, obj):
+ """get label and shape for classes."""
+ raise NotImplementedError
+
+ def close_graph(self):
+ """finalize the graph"""
+ raise NotImplementedError
+
+
+class DotWriter(DiagramWriter):
+ """write dot graphs from a diagram definition and a project
+ """
+
+ def __init__(self, config):
+ styles = [
+ dict(arrowtail="none", arrowhead="open"),
+ dict(arrowtail="none", arrowhead="empty"),
+ dict(arrowtail="node", arrowhead="empty", style="dashed"),
+ dict(
+ fontcolor="green", arrowtail="none", arrowhead="diamond", style="solid"
+ ),
+ ]
+ DiagramWriter.__init__(self, config, styles)
+
+ def set_printer(self, file_name, basename):
+ """initialize DotWriter and add options for layout.
+ """
+ layout = dict(rankdir="BT")
+ self.printer = DotBackend(basename, additional_param=layout)
+ self.file_name = file_name
+
+ def get_title(self, obj):
+ """get project title"""
+ return obj.title
+
+ def get_values(self, obj):
+ """get label and shape for classes.
+
+ The label contains all attributes and methods
+ """
+ label = obj.title
+ if obj.shape == "interface":
+ label = "«interface»\\n%s" % label
+ if not self.config.only_classnames:
+ label = r"%s|%s\l|" % (label, r"\l".join(obj.attrs))
+ for func in obj.methods:
+ args = [arg.name for arg in func.args.args if arg.name != "self"]
+ label = r"%s%s(%s)\l" % (label, func.name, ", ".join(args))
+ label = "{%s}" % label
+ if is_exception(obj.node):
+ return dict(fontcolor="red", label=label, shape="record")
+ return dict(label=label, shape="record")
+
+ def close_graph(self):
+ """print the dot graph into <file_name>"""
+ self.printer.generate(self.file_name)
+
+
+class VCGWriter(DiagramWriter):
+ """write vcg graphs from a diagram definition and a project
+ """
+
+ def __init__(self, config):
+ styles = [
+ dict(arrowstyle="solid", backarrowstyle="none", backarrowsize=0),
+ dict(arrowstyle="solid", backarrowstyle="none", backarrowsize=10),
+ dict(
+ arrowstyle="solid",
+ backarrowstyle="none",
+ linestyle="dotted",
+ backarrowsize=10,
+ ),
+ dict(arrowstyle="solid", backarrowstyle="none", textcolor="green"),
+ ]
+ DiagramWriter.__init__(self, config, styles)
+
+ def set_printer(self, file_name, basename):
+ """initialize VCGWriter for a UML graph"""
+ self.graph_file = open(file_name, "w+")
+ self.printer = VCGPrinter(self.graph_file)
+ self.printer.open_graph(
+ title=basename,
+ layoutalgorithm="dfs",
+ late_edge_labels="yes",
+ port_sharing="no",
+ manhattan_edges="yes",
+ )
+ self.printer.emit_node = self.printer.node
+ self.printer.emit_edge = self.printer.edge
+
+ def get_title(self, obj):
+ """get project title in vcg format"""
+ return r"\fb%s\fn" % obj.title
+
+ def get_values(self, obj):
+ """get label and shape for classes.
+
+ The label contains all attributes and methods
+ """
+ if is_exception(obj.node):
+ label = r"\fb\f09%s\fn" % obj.title
+ else:
+ label = r"\fb%s\fn" % obj.title
+ if obj.shape == "interface":
+ shape = "ellipse"
+ else:
+ shape = "box"
+ if not self.config.only_classnames:
+ attrs = obj.attrs
+ methods = [func.name for func in obj.methods]
+ # box width for UML like diagram
+ maxlen = max(len(name) for name in [obj.title] + methods + attrs)
+ line = "_" * (maxlen + 2)
+ label = r"%s\n\f%s" % (label, line)
+ for attr in attrs:
+ label = r"%s\n\f08%s" % (label, attr)
+ if attrs:
+ label = r"%s\n\f%s" % (label, line)
+ for func in methods:
+ label = r"%s\n\f10%s()" % (label, func)
+ return dict(label=label, shape=shape)
+
+ def close_graph(self):
+ """close graph and file"""
+ self.printer.close_graph()
+ self.graph_file.close()
diff --git a/venv/Lib/site-packages/pylint/reporters/__init__.py b/venv/Lib/site-packages/pylint/reporters/__init__.py
new file mode 100644
index 0000000..f01629b
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/__init__.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006, 2010, 2012-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2014-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Ricardo Gemignani <ricardo.gemignani@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2017 Kári Tristan Helgason <kthelgason@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Ville Skyttä <ville.skytta@upcloud.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""utilities methods and classes for reporters"""
+
+
+from pylint import utils
+from pylint.reporters.base_reporter import BaseReporter
+from pylint.reporters.collecting_reporter import CollectingReporter
+from pylint.reporters.json_reporter import JSONReporter
+from pylint.reporters.reports_handler_mix_in import ReportsHandlerMixIn
+
+
+def initialize(linter):
+ """initialize linter with reporters in this package """
+ utils.register_plugins(linter, __path__[0])
+
+
+__all__ = ["BaseReporter", "ReportsHandlerMixIn", "JSONReporter", "CollectingReporter"]
diff --git a/venv/Lib/site-packages/pylint/reporters/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/reporters/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..a1b55a7
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/reporters/__pycache__/base_reporter.cpython-37.pyc b/venv/Lib/site-packages/pylint/reporters/__pycache__/base_reporter.cpython-37.pyc
new file mode 100644
index 0000000..5f35295
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/__pycache__/base_reporter.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/reporters/__pycache__/collecting_reporter.cpython-37.pyc b/venv/Lib/site-packages/pylint/reporters/__pycache__/collecting_reporter.cpython-37.pyc
new file mode 100644
index 0000000..066140c
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/__pycache__/collecting_reporter.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/reporters/__pycache__/json_reporter.cpython-37.pyc b/venv/Lib/site-packages/pylint/reporters/__pycache__/json_reporter.cpython-37.pyc
new file mode 100644
index 0000000..ca871c3
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/__pycache__/json_reporter.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/reporters/__pycache__/reports_handler_mix_in.cpython-37.pyc b/venv/Lib/site-packages/pylint/reporters/__pycache__/reports_handler_mix_in.cpython-37.pyc
new file mode 100644
index 0000000..8269f35
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/__pycache__/reports_handler_mix_in.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/reporters/__pycache__/text.cpython-37.pyc b/venv/Lib/site-packages/pylint/reporters/__pycache__/text.cpython-37.pyc
new file mode 100644
index 0000000..f40cc5e
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/__pycache__/text.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/reporters/base_reporter.py b/venv/Lib/site-packages/pylint/reporters/base_reporter.py
new file mode 100644
index 0000000..1003eeb
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/base_reporter.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import os
+import sys
+
+
+class BaseReporter:
+ """base class for reporters
+
+ symbols: show short symbolic names for messages.
+ """
+
+ extension = ""
+
+ def __init__(self, output=None):
+ self.linter = None
+ self.section = 0
+ self.out = None
+ self.out_encoding = None
+ self.set_output(output)
+ # Build the path prefix to strip to get relative paths
+ self.path_strip_prefix = os.getcwd() + os.sep
+
+ def handle_message(self, msg):
+ """Handle a new message triggered on the current file."""
+
+ def set_output(self, output=None):
+ """set output stream"""
+ self.out = output or sys.stdout
+
+ def writeln(self, string=""):
+ """write a line in the output buffer"""
+ print(string, file=self.out)
+
+ def display_reports(self, layout):
+ """display results encapsulated in the layout tree"""
+ self.section = 0
+ if hasattr(layout, "report_id"):
+ layout.children[0].children[0].data += " (%s)" % layout.report_id
+ self._display(layout)
+
+ def _display(self, layout):
+ """display the layout"""
+ raise NotImplementedError()
+
+ def display_messages(self, layout):
+ """Hook for displaying the messages of the reporter
+
+ This will be called whenever the underlying messages
+ needs to be displayed. For some reporters, it probably
+ doesn't make sense to display messages as soon as they
+ are available, so some mechanism of storing them could be used.
+ This method can be implemented to display them after they've
+ been aggregated.
+ """
+
+ # Event callbacks
+
+ def on_set_current_module(self, module, filepath):
+ """Hook called when a module starts to be analysed."""
+
+ def on_close(self, stats, previous_stats):
+ """Hook called when a module finished analyzing."""
diff --git a/venv/Lib/site-packages/pylint/reporters/collecting_reporter.py b/venv/Lib/site-packages/pylint/reporters/collecting_reporter.py
new file mode 100644
index 0000000..7798d83
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/collecting_reporter.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+from pylint.reporters.base_reporter import BaseReporter
+
+
+class CollectingReporter(BaseReporter):
+ """collects messages"""
+
+ name = "collector"
+
+ def __init__(self):
+ BaseReporter.__init__(self)
+ self.messages = []
+
+ def handle_message(self, msg):
+ self.messages.append(msg)
+
+ _display = None
diff --git a/venv/Lib/site-packages/pylint/reporters/json_reporter.py b/venv/Lib/site-packages/pylint/reporters/json_reporter.py
new file mode 100644
index 0000000..fa6a0f8
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/json_reporter.py
@@ -0,0 +1,58 @@
+# Copyright (c) 2014 Vlad Temian <vladtemian@gmail.com>
+# Copyright (c) 2015-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2017 guillaume2 <guillaume.peillex@gmail.col>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""JSON reporter"""
+import html
+import json
+import sys
+
+from pylint.interfaces import IReporter
+from pylint.reporters.base_reporter import BaseReporter
+
+
+class JSONReporter(BaseReporter):
+ """Report messages and layouts in JSON."""
+
+ __implements__ = IReporter
+ name = "json"
+ extension = "json"
+
+ def __init__(self, output=sys.stdout):
+ BaseReporter.__init__(self, output)
+ self.messages = []
+
+ def handle_message(self, msg):
+ """Manage message of different type and in the context of path."""
+ self.messages.append(
+ {
+ "type": msg.category,
+ "module": msg.module,
+ "obj": msg.obj,
+ "line": msg.line,
+ "column": msg.column,
+ "path": msg.path,
+ "symbol": msg.symbol,
+ "message": html.escape(msg.msg or "", quote=False),
+ "message-id": msg.msg_id,
+ }
+ )
+
+ def display_messages(self, layout):
+ """Launch layouts display"""
+ print(json.dumps(self.messages, indent=4), file=self.out)
+
+ def display_reports(self, layout):
+ """Don't do nothing in this reporter."""
+
+ def _display(self, layout):
+ """Do nothing."""
+
+
+def register(linter):
+ """Register the reporter classes with the linter."""
+ linter.register_reporter(JSONReporter)
diff --git a/venv/Lib/site-packages/pylint/reporters/reports_handler_mix_in.py b/venv/Lib/site-packages/pylint/reporters/reports_handler_mix_in.py
new file mode 100644
index 0000000..6f91a97
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/reports_handler_mix_in.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import collections
+
+from pylint.exceptions import EmptyReportError
+from pylint.reporters.ureports.nodes import Section
+
+
+class ReportsHandlerMixIn:
+ """a mix-in class containing all the reports and stats manipulation
+ related methods for the main lint class
+ """
+
+ def __init__(self):
+ self._reports = collections.defaultdict(list)
+ self._reports_state = {}
+
+ def report_order(self):
+ """ Return a list of reports, sorted in the order
+ in which they must be called.
+ """
+ return list(self._reports)
+
+ def register_report(self, reportid, r_title, r_cb, checker):
+ """register a report
+
+ reportid is the unique identifier for the report
+ r_title the report's title
+ r_cb the method to call to make the report
+ checker is the checker defining the report
+ """
+ reportid = reportid.upper()
+ self._reports[checker].append((reportid, r_title, r_cb))
+
+ def enable_report(self, reportid):
+ """disable the report of the given id"""
+ reportid = reportid.upper()
+ self._reports_state[reportid] = True
+
+ def disable_report(self, reportid):
+ """disable the report of the given id"""
+ reportid = reportid.upper()
+ self._reports_state[reportid] = False
+
+ def report_is_enabled(self, reportid):
+ """return true if the report associated to the given identifier is
+ enabled
+ """
+ return self._reports_state.get(reportid, True)
+
+ def make_reports(self, stats, old_stats):
+ """render registered reports"""
+ sect = Section("Report", "%s statements analysed." % (self.stats["statement"]))
+ for checker in self.report_order():
+ for reportid, r_title, r_cb in self._reports[checker]:
+ if not self.report_is_enabled(reportid):
+ continue
+ report_sect = Section(r_title)
+ try:
+ r_cb(report_sect, stats, old_stats)
+ except EmptyReportError:
+ continue
+ report_sect.report_id = reportid
+ sect.append(report_sect)
+ return sect
+
+ def add_stats(self, **kwargs):
+ """add some stats entries to the statistic dictionary
+ raise an AssertionError if there is a key conflict
+ """
+ for key, value in kwargs.items():
+ if key[-1] == "_":
+ key = key[:-1]
+ assert key not in self.stats
+ self.stats[key] = value
+ return self.stats
diff --git a/venv/Lib/site-packages/pylint/reporters/text.py b/venv/Lib/site-packages/pylint/reporters/text.py
new file mode 100644
index 0000000..ce74174
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/text.py
@@ -0,0 +1,247 @@
+# Copyright (c) 2006-2007, 2010-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 y2kbugger <y2kbugger@users.noreply.github.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Jace Browning <jacebrowning@gmail.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Plain text reporters:
+
+:text: the default one grouping messages by module
+:colorized: an ANSI colorized text reporter
+"""
+import os
+import sys
+import warnings
+
+from pylint import utils
+from pylint.interfaces import IReporter
+from pylint.reporters import BaseReporter
+from pylint.reporters.ureports.text_writer import TextWriter
+
+TITLE_UNDERLINES = ["", "=", "-", "."]
+
+ANSI_PREFIX = "\033["
+ANSI_END = "m"
+ANSI_RESET = "\033[0m"
+ANSI_STYLES = {
+ "reset": "0",
+ "bold": "1",
+ "italic": "3",
+ "underline": "4",
+ "blink": "5",
+ "inverse": "7",
+ "strike": "9",
+}
+ANSI_COLORS = {
+ "reset": "0",
+ "black": "30",
+ "red": "31",
+ "green": "32",
+ "yellow": "33",
+ "blue": "34",
+ "magenta": "35",
+ "cyan": "36",
+ "white": "37",
+}
+
+
+def _get_ansi_code(color=None, style=None):
+ """return ansi escape code corresponding to color and style
+
+ :type color: str or None
+ :param color:
+ the color name (see `ANSI_COLORS` for available values)
+ or the color number when 256 colors are available
+
+ :type style: str or None
+ :param style:
+ style string (see `ANSI_COLORS` for available values). To get
+ several style effects at the same time, use a coma as separator.
+
+ :raise KeyError: if an unexistent color or style identifier is given
+
+ :rtype: str
+ :return: the built escape code
+ """
+ ansi_code = []
+ if style:
+ style_attrs = utils._splitstrip(style)
+ for effect in style_attrs:
+ ansi_code.append(ANSI_STYLES[effect])
+ if color:
+ if color.isdigit():
+ ansi_code.extend(["38", "5"])
+ ansi_code.append(color)
+ else:
+ ansi_code.append(ANSI_COLORS[color])
+ if ansi_code:
+ return ANSI_PREFIX + ";".join(ansi_code) + ANSI_END
+ return ""
+
+
+def colorize_ansi(msg, color=None, style=None):
+ """colorize message by wrapping it with ansi escape codes
+
+ :type msg: str or unicode
+ :param msg: the message string to colorize
+
+ :type color: str or None
+ :param color:
+ the color identifier (see `ANSI_COLORS` for available values)
+
+ :type style: str or None
+ :param style:
+ style string (see `ANSI_COLORS` for available values). To get
+ several style effects at the same time, use a coma as separator.
+
+ :raise KeyError: if an unexistent color or style identifier is given
+
+ :rtype: str or unicode
+ :return: the ansi escaped string
+ """
+ # If both color and style are not defined, then leave the text as is
+ if color is None and style is None:
+ return msg
+ escape_code = _get_ansi_code(color, style)
+ # If invalid (or unknown) color, don't wrap msg with ansi codes
+ if escape_code:
+ return "%s%s%s" % (escape_code, msg, ANSI_RESET)
+ return msg
+
+
+class TextReporter(BaseReporter):
+ """reports messages and layouts in plain text"""
+
+ __implements__ = IReporter
+ name = "text"
+ extension = "txt"
+ line_format = "{path}:{line}:{column}: {msg_id}: {msg} ({symbol})"
+
+ def __init__(self, output=None):
+ BaseReporter.__init__(self, output)
+ self._modules = set()
+ self._template = None
+
+ def on_set_current_module(self, module, filepath):
+ self._template = str(self.linter.config.msg_template or self.line_format)
+
+ def write_message(self, msg):
+ """Convenience method to write a formated message with class default template"""
+ self.writeln(msg.format(self._template))
+
+ def handle_message(self, msg):
+ """manage message of different type and in the context of path"""
+ if msg.module not in self._modules:
+ if msg.module:
+ self.writeln("************* Module %s" % msg.module)
+ self._modules.add(msg.module)
+ else:
+ self.writeln("************* ")
+ self.write_message(msg)
+
+ def _display(self, layout):
+ """launch layouts display"""
+ print(file=self.out)
+ TextWriter().format(layout, self.out)
+
+
+class ParseableTextReporter(TextReporter):
+ """a reporter very similar to TextReporter, but display messages in a form
+ recognized by most text editors :
+
+ <filename>:<linenum>:<msg>
+ """
+
+ name = "parseable"
+ line_format = "{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}"
+
+ def __init__(self, output=None):
+ warnings.warn(
+ "%s output format is deprecated. This is equivalent "
+ "to --msg-template=%s" % (self.name, self.line_format),
+ DeprecationWarning,
+ )
+ TextReporter.__init__(self, output)
+
+
+class VSTextReporter(ParseableTextReporter):
+ """Visual studio text reporter"""
+
+ name = "msvs"
+ line_format = "{path}({line}): [{msg_id}({symbol}){obj}] {msg}"
+
+
+class ColorizedTextReporter(TextReporter):
+ """Simple TextReporter that colorizes text output"""
+
+ name = "colorized"
+ COLOR_MAPPING = {
+ "I": ("green", None),
+ "C": (None, "bold"),
+ "R": ("magenta", "bold, italic"),
+ "W": ("magenta", None),
+ "E": ("red", "bold"),
+ "F": ("red", "bold, underline"),
+ "S": ("yellow", "inverse"), # S stands for module Separator
+ }
+
+ def __init__(self, output=None, color_mapping=None):
+ TextReporter.__init__(self, output)
+ self.color_mapping = color_mapping or dict(ColorizedTextReporter.COLOR_MAPPING)
+ ansi_terms = ["xterm-16color", "xterm-256color"]
+ if os.environ.get("TERM") not in ansi_terms:
+ if sys.platform == "win32":
+ # pylint: disable=import-error,import-outside-toplevel
+ import colorama
+
+ self.out = colorama.AnsiToWin32(self.out)
+
+ def _get_decoration(self, msg_id):
+ """Returns the tuple color, style associated with msg_id as defined
+ in self.color_mapping
+ """
+ try:
+ return self.color_mapping[msg_id[0]]
+ except KeyError:
+ return None, None
+
+ def handle_message(self, msg):
+ """manage message of different types, and colorize output
+ using ansi escape codes
+ """
+ if msg.module not in self._modules:
+ color, style = self._get_decoration("S")
+ if msg.module:
+ modsep = colorize_ansi(
+ "************* Module %s" % msg.module, color, style
+ )
+ else:
+ modsep = colorize_ansi("************* %s" % msg.module, color, style)
+ self.writeln(modsep)
+ self._modules.add(msg.module)
+ color, style = self._get_decoration(msg.C)
+
+ msg = msg._replace(
+ **{
+ attr: colorize_ansi(getattr(msg, attr), color, style)
+ for attr in ("msg", "symbol", "category", "C")
+ }
+ )
+ self.write_message(msg)
+
+
+def register(linter):
+ """Register the reporter classes with the linter."""
+ linter.register_reporter(TextReporter)
+ linter.register_reporter(ParseableTextReporter)
+ linter.register_reporter(VSTextReporter)
+ linter.register_reporter(ColorizedTextReporter)
diff --git a/venv/Lib/site-packages/pylint/reporters/ureports/__init__.py b/venv/Lib/site-packages/pylint/reporters/ureports/__init__.py
new file mode 100644
index 0000000..361552b
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/ureports/__init__.py
@@ -0,0 +1,96 @@
+# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Universal report objects and some formatting drivers.
+
+A way to create simple reports using python objects, primarily designed to be
+formatted as text and html.
+"""
+import os
+import sys
+from io import StringIO
+
+
+class BaseWriter:
+ """base class for ureport writers"""
+
+ def format(self, layout, stream=None, encoding=None):
+ """format and write the given layout into the stream object
+
+ unicode policy: unicode strings may be found in the layout;
+ try to call stream.write with it, but give it back encoded using
+ the given encoding if it fails
+ """
+ if stream is None:
+ stream = sys.stdout
+ if not encoding:
+ encoding = getattr(stream, "encoding", "UTF-8")
+ self.encoding = encoding or "UTF-8"
+ self.out = stream
+ self.begin_format()
+ layout.accept(self)
+ self.end_format()
+
+ def format_children(self, layout):
+ """recurse on the layout children and call their accept method
+ (see the Visitor pattern)
+ """
+ for child in getattr(layout, "children", ()):
+ child.accept(self)
+
+ def writeln(self, string=""):
+ """write a line in the output buffer"""
+ self.write(string + os.linesep)
+
+ def write(self, string):
+ """write a string in the output buffer"""
+ self.out.write(string)
+
+ def begin_format(self):
+ """begin to format a layout"""
+ self.section = 0
+
+ def end_format(self):
+ """finished to format a layout"""
+
+ def get_table_content(self, table):
+ """trick to get table content without actually writing it
+
+ return an aligned list of lists containing table cells values as string
+ """
+ result = [[]]
+ cols = table.cols
+ for cell in self.compute_content(table):
+ if cols == 0:
+ result.append([])
+ cols = table.cols
+ cols -= 1
+ result[-1].append(cell)
+ # fill missing cells
+ while len(result[-1]) < cols:
+ result[-1].append("")
+ return result
+
+ def compute_content(self, layout):
+ """trick to compute the formatting of children layout before actually
+ writing it
+
+ return an iterator on strings (one for each child element)
+ """
+ # Patch the underlying output stream with a fresh-generated stream,
+ # which is used to store a temporary representation of a child
+ # node.
+ out = self.out
+ try:
+ for child in layout.children:
+ stream = StringIO()
+ self.out = stream
+ child.accept(self)
+ yield stream.getvalue()
+ finally:
+ self.out = out
diff --git a/venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..408b51f
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/nodes.cpython-37.pyc b/venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/nodes.cpython-37.pyc
new file mode 100644
index 0000000..2640b32
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/nodes.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/text_writer.cpython-37.pyc b/venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/text_writer.cpython-37.pyc
new file mode 100644
index 0000000..7222ed4
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/ureports/__pycache__/text_writer.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/reporters/ureports/nodes.py b/venv/Lib/site-packages/pylint/reporters/ureports/nodes.py
new file mode 100644
index 0000000..8fafb20
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/ureports/nodes.py
@@ -0,0 +1,188 @@
+# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Micro reports objects.
+
+A micro report is a tree of layout and content objects.
+"""
+
+
+class VNode:
+ def __init__(self, nid=None):
+ self.id = nid
+ # navigation
+ self.parent = None
+ self.children = []
+
+ def __iter__(self):
+ return iter(self.children)
+
+ def append(self, child):
+ """add a node to children"""
+ self.children.append(child)
+ child.parent = self
+
+ def insert(self, index, child):
+ """insert a child node"""
+ self.children.insert(index, child)
+ child.parent = self
+
+ def _get_visit_name(self):
+ """
+ return the visit name for the mixed class. When calling 'accept', the
+ method <'visit_' + name returned by this method> will be called on the
+ visitor
+ """
+ try:
+ # pylint: disable=no-member
+ return self.TYPE.replace("-", "_")
+ # pylint: disable=broad-except
+ except Exception:
+ return self.__class__.__name__.lower()
+
+ def accept(self, visitor, *args, **kwargs):
+ func = getattr(visitor, "visit_%s" % self._get_visit_name())
+ return func(self, *args, **kwargs)
+
+ def leave(self, visitor, *args, **kwargs):
+ func = getattr(visitor, "leave_%s" % self._get_visit_name())
+ return func(self, *args, **kwargs)
+
+
+class BaseLayout(VNode):
+ """base container node
+
+ attributes
+ * children : components in this table (i.e. the table's cells)
+ """
+
+ def __init__(self, children=(), **kwargs):
+ super(BaseLayout, self).__init__(**kwargs)
+ for child in children:
+ if isinstance(child, VNode):
+ self.append(child)
+ else:
+ self.add_text(child)
+
+ def append(self, child):
+ """overridden to detect problems easily"""
+ assert child not in self.parents()
+ VNode.append(self, child)
+
+ def parents(self):
+ """return the ancestor nodes"""
+ assert self.parent is not self
+ if self.parent is None:
+ return []
+ return [self.parent] + self.parent.parents()
+
+ def add_text(self, text):
+ """shortcut to add text data"""
+ self.children.append(Text(text))
+
+
+# non container nodes #########################################################
+
+
+class Text(VNode):
+ """a text portion
+
+ attributes :
+ * data : the text value as an encoded or unicode string
+ """
+
+ def __init__(self, data, escaped=True, **kwargs):
+ super(Text, self).__init__(**kwargs)
+ # if isinstance(data, unicode):
+ # data = data.encode('ascii')
+ assert isinstance(data, str), data.__class__
+ self.escaped = escaped
+ self.data = data
+
+
+class VerbatimText(Text):
+ """a verbatim text, display the raw data
+
+ attributes :
+ * data : the text value as an encoded or unicode string
+ """
+
+
+# container nodes #############################################################
+
+
+class Section(BaseLayout):
+ """a section
+
+ attributes :
+ * BaseLayout attributes
+
+ a title may also be given to the constructor, it'll be added
+ as a first element
+ a description may also be given to the constructor, it'll be added
+ as a first paragraph
+ """
+
+ def __init__(self, title=None, description=None, **kwargs):
+ super(Section, self).__init__(**kwargs)
+ if description:
+ self.insert(0, Paragraph([Text(description)]))
+ if title:
+ self.insert(0, Title(children=(title,)))
+
+
+class EvaluationSection(Section):
+ def __init__(self, message, **kwargs):
+ super(EvaluationSection, self).__init__(**kwargs)
+ title = Paragraph()
+ title.append(Text("-" * len(message)))
+ self.append(title)
+
+ message_body = Paragraph()
+ message_body.append(Text(message))
+ self.append(message_body)
+
+
+class Title(BaseLayout):
+ """a title
+
+ attributes :
+ * BaseLayout attributes
+
+ A title must not contains a section nor a paragraph!
+ """
+
+
+class Paragraph(BaseLayout):
+ """a simple text paragraph
+
+ attributes :
+ * BaseLayout attributes
+
+ A paragraph must not contains a section !
+ """
+
+
+class Table(BaseLayout):
+ """some tabular data
+
+ attributes :
+ * BaseLayout attributes
+ * cols : the number of columns of the table (REQUIRED)
+ * rheaders : the first row's elements are table's header
+ * cheaders : the first col's elements are table's header
+ * title : the table's optional title
+ """
+
+ def __init__(self, cols, title=None, rheaders=0, cheaders=0, **kwargs):
+ super(Table, self).__init__(**kwargs)
+ assert isinstance(cols, int)
+ self.cols = cols
+ self.title = title
+ self.rheaders = rheaders
+ self.cheaders = cheaders
diff --git a/venv/Lib/site-packages/pylint/reporters/ureports/text_writer.py b/venv/Lib/site-packages/pylint/reporters/ureports/text_writer.py
new file mode 100644
index 0000000..8f6aea2
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/reporters/ureports/text_writer.py
@@ -0,0 +1,94 @@
+# Copyright (c) 2015-2016 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""Text formatting drivers for ureports"""
+
+from pylint.reporters.ureports import BaseWriter
+
+TITLE_UNDERLINES = ["", "=", "-", "`", ".", "~", "^"]
+BULLETS = ["*", "-"]
+
+
+class TextWriter(BaseWriter):
+ """format layouts as text
+ (ReStructured inspiration but not totally handled yet)
+ """
+
+ def begin_format(self):
+ super(TextWriter, self).begin_format()
+ self.list_level = 0
+
+ def visit_section(self, layout):
+ """display a section as text
+ """
+ self.section += 1
+ self.writeln()
+ self.format_children(layout)
+ self.section -= 1
+ self.writeln()
+
+ def visit_evaluationsection(self, layout):
+ """Display an evaluation section as a text."""
+ self.section += 1
+ self.format_children(layout)
+ self.section -= 1
+ self.writeln()
+
+ def visit_title(self, layout):
+ title = "".join(list(self.compute_content(layout)))
+ self.writeln(title)
+ try:
+ self.writeln(TITLE_UNDERLINES[self.section] * len(title))
+ except IndexError:
+ print("FIXME TITLE TOO DEEP. TURNING TITLE INTO TEXT")
+
+ def visit_paragraph(self, layout):
+ """enter a paragraph"""
+ self.format_children(layout)
+ self.writeln()
+
+ def visit_table(self, layout):
+ """display a table as text"""
+ table_content = self.get_table_content(layout)
+ # get columns width
+ cols_width = [0] * len(table_content[0])
+ for row in table_content:
+ for index, col in enumerate(row):
+ cols_width[index] = max(cols_width[index], len(col))
+ self.default_table(layout, table_content, cols_width)
+ self.writeln()
+
+ def default_table(self, layout, table_content, cols_width):
+ """format a table"""
+ cols_width = [size + 1 for size in cols_width]
+ format_strings = " ".join(["%%-%ss"] * len(cols_width))
+ format_strings = format_strings % tuple(cols_width)
+ format_strings = format_strings.split(" ")
+ table_linesep = "\n+" + "+".join(["-" * w for w in cols_width]) + "+\n"
+ headsep = "\n+" + "+".join(["=" * w for w in cols_width]) + "+\n"
+
+ self.write(table_linesep)
+ for index, line in enumerate(table_content):
+ self.write("|")
+ for line_index, at_index in enumerate(line):
+ self.write(format_strings[line_index] % at_index)
+ self.write("|")
+ if index == 0 and layout.rheaders:
+ self.write(headsep)
+ else:
+ self.write(table_linesep)
+
+ def visit_verbatimtext(self, layout):
+ """display a verbatim layout as text (so difficult ;)
+ """
+ self.writeln("::\n")
+ for line in layout.data.splitlines():
+ self.writeln(" " + line)
+ self.writeln()
+
+ def visit_text(self, layout):
+ """add some text"""
+ self.write("%s" % layout.data)
diff --git a/venv/Lib/site-packages/pylint/testutils.py b/venv/Lib/site-packages/pylint/testutils.py
new file mode 100644
index 0000000..f214208
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/testutils.py
@@ -0,0 +1,298 @@
+# Copyright (c) 2012-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
+# Copyright (c) 2013-2017 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2013-2014 Google, Inc.
+# Copyright (c) 2013 buck@yelp.com <buck@yelp.com>
+# Copyright (c) 2014 LCD 47 <lcd047@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Ricardo Gemignani <ricardo.gemignani@gmail.com>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2015 Pavel Roskin <proski@gnu.org>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2016 Roy Williams <roy.williams.iii@gmail.com>
+# Copyright (c) 2016 xmo-odoo <xmo-odoo@users.noreply.github.com>
+# Copyright (c) 2017 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""functional/non regression tests for pylint"""
+import collections
+import contextlib
+import functools
+import sys
+import tempfile
+import tokenize
+from glob import glob
+from io import StringIO
+from os import close, getcwd, linesep, remove, sep, write
+from os.path import abspath, basename, dirname, join, splitext
+
+import astroid
+
+from pylint import checkers
+from pylint.interfaces import IReporter
+from pylint.lint import PyLinter
+from pylint.reporters import BaseReporter
+from pylint.utils import ASTWalker
+
+# Utils
+
+SYS_VERS_STR = "%d%d%d" % sys.version_info[:3]
+TITLE_UNDERLINES = ["", "=", "-", "."]
+PREFIX = abspath(dirname(__file__))
+
+
+def _get_tests_info(input_dir, msg_dir, prefix, suffix):
+ """get python input examples and output messages
+
+ We use following conventions for input files and messages:
+ for different inputs:
+ test for python >= x.y -> input = <name>_pyxy.py
+ test for python < x.y -> input = <name>_py_xy.py
+ for one input and different messages:
+ message for python >= x.y -> message = <name>_pyxy.txt
+ lower versions -> message with highest num
+ """
+ result = []
+ for fname in glob(join(input_dir, prefix + "*" + suffix)):
+ infile = basename(fname)
+ fbase = splitext(infile)[0]
+ # filter input files :
+ pyrestr = fbase.rsplit("_py", 1)[-1] # like _26 or 26
+ if pyrestr.isdigit(): # '24', '25'...
+ if SYS_VERS_STR < pyrestr:
+ continue
+ if pyrestr.startswith("_") and pyrestr[1:].isdigit():
+ # skip test for higher python versions
+ if SYS_VERS_STR >= pyrestr[1:]:
+ continue
+ messages = glob(join(msg_dir, fbase + "*.txt"))
+ # the last one will be without ext, i.e. for all or upper versions:
+ if messages:
+ for outfile in sorted(messages, reverse=True):
+ py_rest = outfile.rsplit("_py", 1)[-1][:-4]
+ if py_rest.isdigit() and SYS_VERS_STR >= py_rest:
+ break
+ else:
+ # This will provide an error message indicating the missing filename.
+ outfile = join(msg_dir, fbase + ".txt")
+ result.append((infile, outfile))
+ return result
+
+
+class TestReporter(BaseReporter):
+ """reporter storing plain text messages"""
+
+ __implements__ = IReporter
+
+ def __init__(self): # pylint: disable=super-init-not-called
+
+ self.message_ids = {}
+ self.reset()
+ self.path_strip_prefix = getcwd() + sep
+
+ def reset(self):
+ self.out = StringIO()
+ self.messages = []
+
+ def handle_message(self, msg):
+ """manage message of different type and in the context of path """
+ obj = msg.obj
+ line = msg.line
+ msg_id = msg.msg_id
+ msg = msg.msg
+ self.message_ids[msg_id] = 1
+ if obj:
+ obj = ":%s" % obj
+ sigle = msg_id[0]
+ if linesep != "\n":
+ # 2to3 writes os.linesep instead of using
+ # the previosly used line separators
+ msg = msg.replace("\r\n", "\n")
+ self.messages.append("%s:%3s%s: %s" % (sigle, line, obj, msg))
+
+ def finalize(self):
+ self.messages.sort()
+ for msg in self.messages:
+ print(msg, file=self.out)
+ result = self.out.getvalue()
+ self.reset()
+ return result
+
+ # pylint: disable=unused-argument
+ def on_set_current_module(self, module, filepath):
+ pass
+
+ # pylint: enable=unused-argument
+
+ def display_reports(self, layout):
+ """ignore layouts"""
+
+ _display = None
+
+
+class MinimalTestReporter(BaseReporter):
+ def handle_message(self, msg):
+ self.messages.append(msg)
+
+ def on_set_current_module(self, module, filepath):
+ self.messages = []
+
+ _display = None
+
+
+class Message(
+ collections.namedtuple("Message", ["msg_id", "line", "node", "args", "confidence"])
+):
+ def __new__(cls, msg_id, line=None, node=None, args=None, confidence=None):
+ return tuple.__new__(cls, (msg_id, line, node, args, confidence))
+
+ def __eq__(self, other):
+ if isinstance(other, Message):
+ if self.confidence and other.confidence:
+ return super(Message, self).__eq__(other)
+ return self[:-1] == other[:-1]
+ return NotImplemented # pragma: no cover
+
+ __hash__ = None
+
+
+class UnittestLinter:
+ """A fake linter class to capture checker messages."""
+
+ # pylint: disable=unused-argument, no-self-use
+
+ def __init__(self):
+ self._messages = []
+ self.stats = {}
+
+ def release_messages(self):
+ try:
+ return self._messages
+ finally:
+ self._messages = []
+
+ def add_message(
+ self, msg_id, line=None, node=None, args=None, confidence=None, col_offset=None
+ ):
+ # Do not test col_offset for now since changing Message breaks everything
+ self._messages.append(Message(msg_id, line, node, args, confidence))
+
+ def is_message_enabled(self, *unused_args, **unused_kwargs):
+ return True
+
+ def add_stats(self, **kwargs):
+ for name, value in kwargs.items():
+ self.stats[name] = value
+ return self.stats
+
+ @property
+ def options_providers(self):
+ return linter.options_providers
+
+
+def set_config(**kwargs):
+ """Decorator for setting config values on a checker."""
+
+ def _wrapper(fun):
+ @functools.wraps(fun)
+ def _forward(self):
+ for key, value in kwargs.items():
+ setattr(self.checker.config, key, value)
+ if isinstance(self, CheckerTestCase):
+ # reopen checker in case, it may be interested in configuration change
+ self.checker.open()
+ fun(self)
+
+ return _forward
+
+ return _wrapper
+
+
+class CheckerTestCase:
+ """A base testcase class for unit testing individual checker classes."""
+
+ CHECKER_CLASS = None
+ CONFIG = {}
+
+ def setup_method(self):
+ self.linter = UnittestLinter()
+ self.checker = self.CHECKER_CLASS(self.linter) # pylint: disable=not-callable
+ for key, value in self.CONFIG.items():
+ setattr(self.checker.config, key, value)
+ self.checker.open()
+
+ @contextlib.contextmanager
+ def assertNoMessages(self):
+ """Assert that no messages are added by the given method."""
+ with self.assertAddsMessages():
+ yield
+
+ @contextlib.contextmanager
+ def assertAddsMessages(self, *messages):
+ """Assert that exactly the given method adds the given messages.
+
+ The list of messages must exactly match *all* the messages added by the
+ method. Additionally, we check to see whether the args in each message can
+ actually be substituted into the message string.
+ """
+ yield
+ got = self.linter.release_messages()
+ msg = "Expected messages did not match actual.\n" "Expected:\n%s\nGot:\n%s" % (
+ "\n".join(repr(m) for m in messages),
+ "\n".join(repr(m) for m in got),
+ )
+ assert list(messages) == got, msg
+
+ def walk(self, node):
+ """recursive walk on the given node"""
+ walker = ASTWalker(linter)
+ walker.add_checker(self.checker)
+ walker.walk(node)
+
+
+# Init
+test_reporter = TestReporter()
+linter = PyLinter()
+linter.set_reporter(test_reporter)
+linter.config.persistent = 0
+checkers.initialize(linter)
+
+
+def _tokenize_str(code):
+ return list(tokenize.generate_tokens(StringIO(code).readline))
+
+
+@contextlib.contextmanager
+def _create_tempfile(content=None):
+ """Create a new temporary file.
+
+ If *content* parameter is given, then it will be written
+ in the temporary file, before passing it back.
+ This is a context manager and should be used with a *with* statement.
+ """
+ # Can't use tempfile.NamedTemporaryFile here
+ # because on Windows the file must be closed before writing to it,
+ # see http://bugs.python.org/issue14243
+ file_handle, tmp = tempfile.mkstemp()
+ if content:
+ # erff
+ write(file_handle, bytes(content, "ascii"))
+ try:
+ yield tmp
+ finally:
+ close(file_handle)
+ remove(tmp)
+
+
+@contextlib.contextmanager
+def _create_file_backed_module(code):
+ """Create an astroid module for the given code, backed by a real file."""
+ with _create_tempfile() as temp:
+ module = astroid.parse(code)
+ module.file = temp
+ yield module
diff --git a/venv/Lib/site-packages/pylint/utils/__init__.py b/venv/Lib/site-packages/pylint/utils/__init__.py
new file mode 100644
index 0000000..8ee9e07
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/utils/__init__.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
+# Copyright (c) 2009 Vincent
+# Copyright (c) 2009 Mads Kiilerich <mads@kiilerich.com>
+# Copyright (c) 2012-2014 Google, Inc.
+# Copyright (c) 2014-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2015 Michal Nowikowski <godfryd@gmail.com>
+# Copyright (c) 2014 LCD 47 <lcd047@gmail.com>
+# Copyright (c) 2014 Brett Cannon <brett@python.org>
+# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
+# Copyright (c) 2014 Damien Nozay <damien.nozay@gmail.com>
+# Copyright (c) 2015 Aru Sahni <arusahni@gmail.com>
+# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
+# Copyright (c) 2015 Simu Toni <simutoni@gmail.com>
+# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
+# Copyright (c) 2016 Łukasz Rogalski <rogalski.91@gmail.com>
+# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
+# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
+# Copyright (c) 2016 Glenn Matthews <glmatthe@cisco.com>
+# Copyright (c) 2016 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2016 xmo-odoo <xmo-odoo@users.noreply.github.com>
+# Copyright (c) 2017-2018 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2017 Pierre Sassoulas <pierre.sassoulas@cea.fr>
+# Copyright (c) 2017 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2017 Chris Lamb <chris@chris-lamb.co.uk>
+# Copyright (c) 2017 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2017 Thomas Hisch <t.hisch@gmail.com>
+# Copyright (c) 2017 Mikhail Fesenko <proggga@gmail.com>
+# Copyright (c) 2017 Craig Citro <craigcitro@gmail.com>
+# Copyright (c) 2017 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
+# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
+# Copyright (c) 2018 Pierre Sassoulas <pierre.sassoulas@wisebim.fr>
+# Copyright (c) 2018 Reverb C <reverbc@users.noreply.github.com>
+# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+"""some various utilities and helper classes, most of them used in the
+main pylint class
+"""
+
+from pylint.utils.ast_walker import ASTWalker
+from pylint.utils.file_state import FileState
+from pylint.utils.utils import (
+ _basename_in_blacklist_re,
+ _check_csv,
+ _format_option_value,
+ _splitstrip,
+ _unquote,
+ decoding_stream,
+ deprecated_option,
+ expand_modules,
+ format_section,
+ get_global_option,
+ get_module_and_frameid,
+ get_rst_section,
+ get_rst_title,
+ normalize_text,
+ register_plugins,
+ safe_decode,
+ tokenize_module,
+)
diff --git a/venv/Lib/site-packages/pylint/utils/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/pylint/utils/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..6f3569d
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/utils/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/utils/__pycache__/ast_walker.cpython-37.pyc b/venv/Lib/site-packages/pylint/utils/__pycache__/ast_walker.cpython-37.pyc
new file mode 100644
index 0000000..af27609
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/utils/__pycache__/ast_walker.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/utils/__pycache__/file_state.cpython-37.pyc b/venv/Lib/site-packages/pylint/utils/__pycache__/file_state.cpython-37.pyc
new file mode 100644
index 0000000..4a43508
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/utils/__pycache__/file_state.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/utils/__pycache__/utils.cpython-37.pyc b/venv/Lib/site-packages/pylint/utils/__pycache__/utils.cpython-37.pyc
new file mode 100644
index 0000000..9049995
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/utils/__pycache__/utils.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/pylint/utils/ast_walker.py b/venv/Lib/site-packages/pylint/utils/ast_walker.py
new file mode 100644
index 0000000..2e7a6da
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/utils/ast_walker.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import collections
+
+from astroid import nodes
+
+
+class ASTWalker:
+ def __init__(self, linter):
+ # callbacks per node types
+ self.nbstatements = 0
+ self.visit_events = collections.defaultdict(list)
+ self.leave_events = collections.defaultdict(list)
+ self.linter = linter
+
+ def _is_method_enabled(self, method):
+ if not hasattr(method, "checks_msgs"):
+ return True
+ for msg_desc in method.checks_msgs:
+ if self.linter.is_message_enabled(msg_desc):
+ return True
+ return False
+
+ def add_checker(self, checker):
+ """walk to the checker's dir and collect visit and leave methods"""
+ vcids = set()
+ lcids = set()
+ visits = self.visit_events
+ leaves = self.leave_events
+ for member in dir(checker):
+ cid = member[6:]
+ if cid == "default":
+ continue
+ if member.startswith("visit_"):
+ v_meth = getattr(checker, member)
+ # don't use visit_methods with no activated message:
+ if self._is_method_enabled(v_meth):
+ visits[cid].append(v_meth)
+ vcids.add(cid)
+ elif member.startswith("leave_"):
+ l_meth = getattr(checker, member)
+ # don't use leave_methods with no activated message:
+ if self._is_method_enabled(l_meth):
+ leaves[cid].append(l_meth)
+ lcids.add(cid)
+ visit_default = getattr(checker, "visit_default", None)
+ if visit_default:
+ for cls in nodes.ALL_NODE_CLASSES:
+ cid = cls.__name__.lower()
+ if cid not in vcids:
+ visits[cid].append(visit_default)
+ # for now we have no "leave_default" method in Pylint
+
+ def walk(self, astroid):
+ """call visit events of astroid checkers for the given node, recurse on
+ its children, then leave events.
+ """
+ cid = astroid.__class__.__name__.lower()
+
+ # Detect if the node is a new name for a deprecated alias.
+ # In this case, favour the methods for the deprecated
+ # alias if any, in order to maintain backwards
+ # compatibility.
+ visit_events = self.visit_events.get(cid, ())
+ leave_events = self.leave_events.get(cid, ())
+
+ if astroid.is_statement:
+ self.nbstatements += 1
+ # generate events for this node on each checker
+ for callback in visit_events or ():
+ callback(astroid)
+ # recurse on children
+ for child in astroid.get_children():
+ self.walk(child)
+ for callback in leave_events or ():
+ callback(astroid)
diff --git a/venv/Lib/site-packages/pylint/utils/file_state.py b/venv/Lib/site-packages/pylint/utils/file_state.py
new file mode 100644
index 0000000..1a8dd4d
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/utils/file_state.py
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import collections
+
+from astroid import nodes
+
+from pylint.constants import MSG_STATE_SCOPE_MODULE, WarningScope
+
+
+class FileState:
+ """Hold internal state specific to the currently analyzed file"""
+
+ def __init__(self, modname=None):
+ self.base_name = modname
+ self._module_msgs_state = {}
+ self._raw_module_msgs_state = {}
+ self._ignored_msgs = collections.defaultdict(set)
+ self._suppression_mapping = {}
+ self._effective_max_line_number = None
+
+ def collect_block_lines(self, msgs_store, module_node):
+ """Walk the AST to collect block level options line numbers."""
+ for msg, lines in self._module_msgs_state.items():
+ self._raw_module_msgs_state[msg] = lines.copy()
+ orig_state = self._module_msgs_state.copy()
+ self._module_msgs_state = {}
+ self._suppression_mapping = {}
+ self._effective_max_line_number = module_node.tolineno
+ self._collect_block_lines(msgs_store, module_node, orig_state)
+
+ def _collect_block_lines(self, msgs_store, node, msg_state):
+ """Recursively walk (depth first) AST to collect block level options
+ line numbers.
+ """
+ for child in node.get_children():
+ self._collect_block_lines(msgs_store, child, msg_state)
+ first = node.fromlineno
+ last = node.tolineno
+ # first child line number used to distinguish between disable
+ # which are the first child of scoped node with those defined later.
+ # For instance in the code below:
+ #
+ # 1. def meth8(self):
+ # 2. """test late disabling"""
+ # 3. pylint: disable=not-callable
+ # 4. print(self.blip)
+ # 5. pylint: disable=no-member
+ # 6. print(self.bla)
+ #
+ # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6
+ #
+ # this is necessary to disable locally messages applying to class /
+ # function using their fromlineno
+ if (
+ isinstance(node, (nodes.Module, nodes.ClassDef, nodes.FunctionDef))
+ and node.body
+ ):
+ firstchildlineno = node.body[0].fromlineno
+ else:
+ firstchildlineno = last
+ for msgid, lines in msg_state.items():
+ for lineno, state in list(lines.items()):
+ original_lineno = lineno
+ if first > lineno or last < lineno:
+ continue
+ # Set state for all lines for this block, if the
+ # warning is applied to nodes.
+ message_definitions = msgs_store.get_message_definitions(msgid)
+ for message_definition in message_definitions:
+ if message_definition.scope == WarningScope.NODE:
+ if lineno > firstchildlineno:
+ state = True
+ first_, last_ = node.block_range(lineno)
+ else:
+ first_ = lineno
+ last_ = last
+ for line in range(first_, last_ + 1):
+ # do not override existing entries
+ if line in self._module_msgs_state.get(msgid, ()):
+ continue
+ if line in lines: # state change in the same block
+ state = lines[line]
+ original_lineno = line
+ if not state:
+ self._suppression_mapping[(msgid, line)] = original_lineno
+ try:
+ self._module_msgs_state[msgid][line] = state
+ except KeyError:
+ self._module_msgs_state[msgid] = {line: state}
+ del lines[lineno]
+
+ def set_msg_status(self, msg, line, status):
+ """Set status (enabled/disable) for a given message at a given line"""
+ assert line > 0
+ try:
+ self._module_msgs_state[msg.msgid][line] = status
+ except KeyError:
+ self._module_msgs_state[msg.msgid] = {line: status}
+
+ def handle_ignored_message(
+ self, state_scope, msgid, line, node, args, confidence
+ ): # pylint: disable=unused-argument
+ """Report an ignored message.
+
+ state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG,
+ depending on whether the message was disabled locally in the module,
+ or globally. The other arguments are the same as for add_message.
+ """
+ if state_scope == MSG_STATE_SCOPE_MODULE:
+ try:
+ orig_line = self._suppression_mapping[(msgid, line)]
+ self._ignored_msgs[(msgid, orig_line)].add(line)
+ except KeyError:
+ pass
+
+ def iter_spurious_suppression_messages(self, msgs_store):
+ for warning, lines in self._raw_module_msgs_state.items():
+ for line, enable in lines.items():
+ if not enable and (warning, line) not in self._ignored_msgs:
+ # ignore cyclic-import check which can show false positives
+ # here due to incomplete context
+ if warning != "R0401":
+ yield "useless-suppression", line, (
+ msgs_store.get_msg_display_string(warning),
+ )
+ # don't use iteritems here, _ignored_msgs may be modified by add_message
+ for (warning, from_), lines in list(self._ignored_msgs.items()):
+ for line in lines:
+ yield "suppressed-message", line, (
+ msgs_store.get_msg_display_string(warning),
+ from_,
+ )
+
+ def get_effective_max_line_number(self):
+ return self._effective_max_line_number
diff --git a/venv/Lib/site-packages/pylint/utils/utils.py b/venv/Lib/site-packages/pylint/utils/utils.py
new file mode 100644
index 0000000..5605ecd
--- /dev/null
+++ b/venv/Lib/site-packages/pylint/utils/utils.py
@@ -0,0 +1,371 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
+import codecs
+import re
+import sys
+import textwrap
+import tokenize
+from os import linesep, listdir
+from os.path import basename, dirname, exists, isdir, join, normpath, splitext
+
+from astroid import Module, modutils
+
+from pylint.constants import PY_EXTS
+
+
+def normalize_text(text, line_len=80, indent=""):
+ """Wrap the text on the given line length."""
+ return "\n".join(
+ textwrap.wrap(
+ text, width=line_len, initial_indent=indent, subsequent_indent=indent
+ )
+ )
+
+
+def get_module_and_frameid(node):
+ """return the module name and the frame id in the module"""
+ frame = node.frame()
+ module, obj = "", []
+ while frame:
+ if isinstance(frame, Module):
+ module = frame.name
+ else:
+ obj.append(getattr(frame, "name", "<lambda>"))
+ try:
+ frame = frame.parent.frame()
+ except AttributeError:
+ frame = None
+ obj.reverse()
+ return module, ".".join(obj)
+
+
+def get_rst_title(title, character):
+ """Permit to get a title formatted as ReStructuredText test (underlined with a chosen character)."""
+ return "%s\n%s\n" % (title, character * len(title))
+
+
+def get_rst_section(section, options, doc=None):
+ """format an options section using as a ReStructuredText formatted output"""
+ result = ""
+ if section:
+ result += get_rst_title(section, "'")
+ if doc:
+ formatted_doc = normalize_text(doc, line_len=79, indent="")
+ result += "%s\n\n" % formatted_doc
+ for optname, optdict, value in options:
+ help_opt = optdict.get("help")
+ result += ":%s:\n" % optname
+ if help_opt:
+ formatted_help = normalize_text(help_opt, line_len=79, indent=" ")
+ result += "%s\n" % formatted_help
+ if value:
+ value = str(_format_option_value(optdict, value))
+ result += "\n Default: ``%s``\n" % value.replace("`` ", "```` ``")
+ return result
+
+
+def safe_decode(line, encoding, *args, **kwargs):
+ """return decoded line from encoding or decode with default encoding"""
+ try:
+ return line.decode(encoding or sys.getdefaultencoding(), *args, **kwargs)
+ except LookupError:
+ return line.decode(sys.getdefaultencoding(), *args, **kwargs)
+
+
+def decoding_stream(stream, encoding, errors="strict"):
+ try:
+ reader_cls = codecs.getreader(encoding or sys.getdefaultencoding())
+ except LookupError:
+ reader_cls = codecs.getreader(sys.getdefaultencoding())
+ return reader_cls(stream, errors)
+
+
+def tokenize_module(module):
+ with module.stream() as stream:
+ readline = stream.readline
+ return list(tokenize.tokenize(readline))
+
+
+def _basename_in_blacklist_re(base_name, black_list_re):
+ """Determines if the basename is matched in a regex blacklist
+
+ :param str base_name: The basename of the file
+ :param list black_list_re: A collection of regex patterns to match against.
+ Successful matches are blacklisted.
+
+ :returns: `True` if the basename is blacklisted, `False` otherwise.
+ :rtype: bool
+ """
+ for file_pattern in black_list_re:
+ if file_pattern.match(base_name):
+ return True
+ return False
+
+
+def _modpath_from_file(filename, is_namespace):
+ def _is_package_cb(path, parts):
+ return modutils.check_modpath_has_init(path, parts) or is_namespace
+
+ return modutils.modpath_from_file_with_callback(
+ filename, is_package_cb=_is_package_cb
+ )
+
+
+def expand_modules(files_or_modules, black_list, black_list_re):
+ """take a list of files/modules/packages and return the list of tuple
+ (file, module name) which have to be actually checked
+ """
+ result = []
+ errors = []
+ for something in files_or_modules:
+ if basename(something) in black_list:
+ continue
+ if _basename_in_blacklist_re(basename(something), black_list_re):
+ continue
+ if exists(something):
+ # this is a file or a directory
+ try:
+ modname = ".".join(modutils.modpath_from_file(something))
+ except ImportError:
+ modname = splitext(basename(something))[0]
+ if isdir(something):
+ filepath = join(something, "__init__.py")
+ else:
+ filepath = something
+ else:
+ # suppose it's a module or package
+ modname = something
+ try:
+ filepath = modutils.file_from_modpath(modname.split("."))
+ if filepath is None:
+ continue
+ except (ImportError, SyntaxError) as ex:
+ # The SyntaxError is a Python bug and should be
+ # removed once we move away from imp.find_module: http://bugs.python.org/issue10588
+ errors.append({"key": "fatal", "mod": modname, "ex": ex})
+ continue
+
+ filepath = normpath(filepath)
+ modparts = (modname or something).split(".")
+
+ try:
+ spec = modutils.file_info_from_modpath(modparts, path=sys.path)
+ except ImportError:
+ # Might not be acceptable, don't crash.
+ is_namespace = False
+ is_directory = isdir(something)
+ else:
+ is_namespace = modutils.is_namespace(spec)
+ is_directory = modutils.is_directory(spec)
+
+ if not is_namespace:
+ result.append(
+ {
+ "path": filepath,
+ "name": modname,
+ "isarg": True,
+ "basepath": filepath,
+ "basename": modname,
+ }
+ )
+
+ has_init = (
+ not (modname.endswith(".__init__") or modname == "__init__")
+ and basename(filepath) == "__init__.py"
+ )
+
+ if has_init or is_namespace or is_directory:
+ for subfilepath in modutils.get_module_files(
+ dirname(filepath), black_list, list_all=is_namespace
+ ):
+ if filepath == subfilepath:
+ continue
+ if _basename_in_blacklist_re(basename(subfilepath), black_list_re):
+ continue
+
+ modpath = _modpath_from_file(subfilepath, is_namespace)
+ submodname = ".".join(modpath)
+ result.append(
+ {
+ "path": subfilepath,
+ "name": submodname,
+ "isarg": False,
+ "basepath": filepath,
+ "basename": modname,
+ }
+ )
+ return result, errors
+
+
+def register_plugins(linter, directory):
+ """load all module and package in the given directory, looking for a
+ 'register' function in each one, used to register pylint checkers
+ """
+ imported = {}
+ for filename in listdir(directory):
+ base, extension = splitext(filename)
+ if base in imported or base == "__pycache__":
+ continue
+ if (
+ extension in PY_EXTS
+ and base != "__init__"
+ or (not extension and isdir(join(directory, base)))
+ ):
+ try:
+ module = modutils.load_module_from_file(join(directory, filename))
+ except ValueError:
+ # empty module name (usually emacs auto-save files)
+ continue
+ except ImportError as exc:
+ print(
+ "Problem importing module %s: %s" % (filename, exc), file=sys.stderr
+ )
+ else:
+ if hasattr(module, "register"):
+ module.register(linter)
+ imported[base] = 1
+
+
+def get_global_option(checker, option, default=None):
+ """ Retrieve an option defined by the given *checker* or
+ by all known option providers.
+
+ It will look in the list of all options providers
+ until the given *option* will be found.
+ If the option wasn't found, the *default* value will be returned.
+ """
+ # First, try in the given checker's config.
+ # After that, look in the options providers.
+
+ try:
+ return getattr(checker.config, option.replace("-", "_"))
+ except AttributeError:
+ pass
+ for provider in checker.linter.options_providers:
+ for options in provider.options:
+ if options[0] == option:
+ return getattr(provider.config, option.replace("-", "_"))
+ return default
+
+
+def deprecated_option(
+ shortname=None, opt_type=None, help_msg=None, deprecation_msg=None
+):
+ def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument
+ if deprecation_msg:
+ sys.stderr.write(deprecation_msg % (optname,))
+
+ option = {
+ "help": help_msg,
+ "hide": True,
+ "type": opt_type,
+ "action": "callback",
+ "callback": _warn_deprecated,
+ "deprecated": True,
+ }
+ if shortname:
+ option["shortname"] = shortname
+ return option
+
+
+def _splitstrip(string, sep=","):
+ """return a list of stripped string by splitting the string given as
+ argument on `sep` (',' by default). Empty string are discarded.
+
+ >>> _splitstrip('a, b, c , 4,,')
+ ['a', 'b', 'c', '4']
+ >>> _splitstrip('a')
+ ['a']
+ >>> _splitstrip('a,\nb,\nc,')
+ ['a', 'b', 'c']
+
+ :type string: str or unicode
+ :param string: a csv line
+
+ :type sep: str or unicode
+ :param sep: field separator, default to the comma (',')
+
+ :rtype: str or unicode
+ :return: the unquoted string (or the input string if it wasn't quoted)
+ """
+ return [word.strip() for word in string.split(sep) if word.strip()]
+
+
+def _unquote(string):
+ """remove optional quotes (simple or double) from the string
+
+ :type string: str or unicode
+ :param string: an optionally quoted string
+
+ :rtype: str or unicode
+ :return: the unquoted string (or the input string if it wasn't quoted)
+ """
+ if not string:
+ return string
+ if string[0] in "\"'":
+ string = string[1:]
+ if string[-1] in "\"'":
+ string = string[:-1]
+ return string
+
+
+def _check_csv(value):
+ if isinstance(value, (list, tuple)):
+ return value
+ return _splitstrip(value)
+
+
+def _comment(string):
+ """return string as a comment"""
+ lines = [line.strip() for line in string.splitlines()]
+ return "# " + ("%s# " % linesep).join(lines)
+
+
+def _format_option_value(optdict, value):
+ """return the user input's value from a 'compiled' value"""
+ if isinstance(value, (list, tuple)):
+ value = ",".join(_format_option_value(optdict, item) for item in value)
+ elif isinstance(value, dict):
+ value = ",".join("%s:%s" % (k, v) for k, v in value.items())
+ elif hasattr(value, "match"): # optdict.get('type') == 'regexp'
+ # compiled regexp
+ value = value.pattern
+ elif optdict.get("type") == "yn":
+ value = "yes" if value else "no"
+ elif isinstance(value, str) and value.isspace():
+ value = "'%s'" % value
+ return value
+
+
+def format_section(stream, section, options, doc=None):
+ """format an options section using the INI format"""
+ if doc:
+ print(_comment(doc), file=stream)
+ print("[%s]" % section, file=stream)
+ _ini_format(stream, options)
+
+
+def _ini_format(stream, options):
+ """format options using the INI format"""
+ for optname, optdict, value in options:
+ value = _format_option_value(optdict, value)
+ help_opt = optdict.get("help")
+ if help_opt:
+ help_opt = normalize_text(help_opt, line_len=79, indent="# ")
+ print(file=stream)
+ print(help_opt, file=stream)
+ else:
+ print(file=stream)
+ if value is None:
+ print("#%s=" % optname, file=stream)
+ else:
+ value = str(value).strip()
+ if re.match(r"^([\w-]+,)+[\w-]+$", str(value)):
+ separator = "\n " + " " * len(optname)
+ value = separator.join(x + "," for x in str(value).split(","))
+ # remove trailing ',' from last element of the list
+ value = value[:-1]
+ print("%s=%s" % (optname, value), file=stream)
diff --git a/venv/Lib/site-packages/six-1.14.0.dist-info/INSTALLER b/venv/Lib/site-packages/six-1.14.0.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/Lib/site-packages/six-1.14.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/Lib/site-packages/six-1.14.0.dist-info/LICENSE b/venv/Lib/site-packages/six-1.14.0.dist-info/LICENSE
new file mode 100644
index 0000000..de66331
--- /dev/null
+++ b/venv/Lib/site-packages/six-1.14.0.dist-info/LICENSE
@@ -0,0 +1,18 @@
+Copyright (c) 2010-2020 Benjamin Peterson
+
+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/site-packages/six-1.14.0.dist-info/METADATA b/venv/Lib/site-packages/six-1.14.0.dist-info/METADATA
new file mode 100644
index 0000000..b9c24c7
--- /dev/null
+++ b/venv/Lib/site-packages/six-1.14.0.dist-info/METADATA
@@ -0,0 +1,49 @@
+Metadata-Version: 2.1
+Name: six
+Version: 1.14.0
+Summary: Python 2 and 3 compatibility utilities
+Home-page: https://github.com/benjaminp/six
+Author: Benjamin Peterson
+Author-email: benjamin@python.org
+License: MIT
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 3
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*
+
+.. image:: https://img.shields.io/pypi/v/six.svg
+ :target: https://pypi.org/project/six/
+ :alt: six on PyPI
+
+.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master
+ :target: https://travis-ci.org/benjaminp/six
+ :alt: six on TravisCI
+
+.. image:: https://readthedocs.org/projects/six/badge/?version=latest
+ :target: https://six.readthedocs.io/
+ :alt: six's documentation on Read the Docs
+
+.. image:: https://img.shields.io/badge/license-MIT-green.svg
+ :target: https://github.com/benjaminp/six/blob/master/LICENSE
+ :alt: MIT License badge
+
+Six is a Python 2 and 3 compatibility library. It provides utility functions
+for smoothing over the differences between the Python versions with the goal of
+writing Python code that is compatible on both Python versions. See the
+documentation for more information on what is provided.
+
+Six supports Python 2.7 and 3.3+. It is contained in only one Python
+file, so it can be easily copied into your project. (The copyright and license
+notice must be retained.)
+
+Online documentation is at https://six.readthedocs.io/.
+
+Bugs can be reported to https://github.com/benjaminp/six. The code can also
+be found there.
+
+
diff --git a/venv/Lib/site-packages/six-1.14.0.dist-info/RECORD b/venv/Lib/site-packages/six-1.14.0.dist-info/RECORD
new file mode 100644
index 0000000..3515abb
--- /dev/null
+++ b/venv/Lib/site-packages/six-1.14.0.dist-info/RECORD
@@ -0,0 +1,8 @@
+__pycache__/six.cpython-37.pyc,,
+six-1.14.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+six-1.14.0.dist-info/LICENSE,sha256=i7hQxWWqOJ_cFvOkaWWtI9gq3_YPI5P8J2K2MYXo5sk,1066
+six-1.14.0.dist-info/METADATA,sha256=nEAc9huAtn13Bna3MQ1ZSswoyaV7GMJdKfGluHFX4DU,1795
+six-1.14.0.dist-info/RECORD,,
+six-1.14.0.dist-info/WHEEL,sha256=8zNYZbwQSXoB9IfXOjPfeNwvAsALAjffgk27FqvCWbo,110
+six-1.14.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4
+six.py,sha256=Q6WvEXZ1DGEASAo3CGNCJkKv2tPy8xkSmK-VHE9PYIA,34074
diff --git a/venv/Lib/site-packages/six-1.14.0.dist-info/WHEEL b/venv/Lib/site-packages/six-1.14.0.dist-info/WHEEL
new file mode 100644
index 0000000..8b701e9
--- /dev/null
+++ b/venv/Lib/site-packages/six-1.14.0.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.6)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/venv/Lib/site-packages/six-1.14.0.dist-info/top_level.txt b/venv/Lib/site-packages/six-1.14.0.dist-info/top_level.txt
new file mode 100644
index 0000000..ffe2fce
--- /dev/null
+++ b/venv/Lib/site-packages/six-1.14.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+six
diff --git a/venv/Lib/site-packages/six.py b/venv/Lib/site-packages/six.py
new file mode 100644
index 0000000..5fe9f8e
--- /dev/null
+++ b/venv/Lib/site-packages/six.py
@@ -0,0 +1,980 @@
+# Copyright (c) 2010-2020 Benjamin Peterson
+#
+# 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.
+
+"""Utilities for writing code that runs on Python 2 and 3"""
+
+from __future__ import absolute_import
+
+import functools
+import itertools
+import operator
+import sys
+import types
+
+__author__ = "Benjamin Peterson <benjamin@python.org>"
+__version__ = "1.14.0"
+
+
+# Useful for very coarse version differentiation.
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+PY34 = sys.version_info[0:2] >= (3, 4)
+
+if PY3:
+ string_types = str,
+ integer_types = int,
+ class_types = type,
+ text_type = str
+ binary_type = bytes
+
+ MAXSIZE = sys.maxsize
+else:
+ string_types = basestring,
+ integer_types = (int, long)
+ class_types = (type, types.ClassType)
+ text_type = unicode
+ binary_type = str
+
+ if sys.platform.startswith("java"):
+ # Jython always uses 32 bits.
+ MAXSIZE = int((1 << 31) - 1)
+ else:
+ # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
+ class X(object):
+
+ def __len__(self):
+ return 1 << 31
+ try:
+ len(X())
+ except OverflowError:
+ # 32-bit
+ MAXSIZE = int((1 << 31) - 1)
+ else:
+ # 64-bit
+ MAXSIZE = int((1 << 63) - 1)
+ del X
+
+
+def _add_doc(func, doc):
+ """Add documentation to a function."""
+ func.__doc__ = doc
+
+
+def _import_module(name):
+ """Import module, returning the module after the last dot."""
+ __import__(name)
+ return sys.modules[name]
+
+
+class _LazyDescr(object):
+
+ def __init__(self, name):
+ self.name = name
+
+ def __get__(self, obj, tp):
+ result = self._resolve()
+ setattr(obj, self.name, result) # Invokes __set__.
+ try:
+ # This is a bit ugly, but it avoids running this again by
+ # removing this descriptor.
+ delattr(obj.__class__, self.name)
+ except AttributeError:
+ pass
+ return result
+
+
+class MovedModule(_LazyDescr):
+
+ def __init__(self, name, old, new=None):
+ super(MovedModule, self).__init__(name)
+ if PY3:
+ if new is None:
+ new = name
+ self.mod = new
+ else:
+ self.mod = old
+
+ def _resolve(self):
+ return _import_module(self.mod)
+
+ def __getattr__(self, attr):
+ _module = self._resolve()
+ value = getattr(_module, attr)
+ setattr(self, attr, value)
+ return value
+
+
+class _LazyModule(types.ModuleType):
+
+ def __init__(self, name):
+ super(_LazyModule, self).__init__(name)
+ self.__doc__ = self.__class__.__doc__
+
+ def __dir__(self):
+ attrs = ["__doc__", "__name__"]
+ attrs += [attr.name for attr in self._moved_attributes]
+ return attrs
+
+ # Subclasses should override this
+ _moved_attributes = []
+
+
+class MovedAttribute(_LazyDescr):
+
+ def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
+ super(MovedAttribute, self).__init__(name)
+ if PY3:
+ if new_mod is None:
+ new_mod = name
+ self.mod = new_mod
+ if new_attr is None:
+ if old_attr is None:
+ new_attr = name
+ else:
+ new_attr = old_attr
+ self.attr = new_attr
+ else:
+ self.mod = old_mod
+ if old_attr is None:
+ old_attr = name
+ self.attr = old_attr
+
+ def _resolve(self):
+ module = _import_module(self.mod)
+ return getattr(module, self.attr)
+
+
+class _SixMetaPathImporter(object):
+
+ """
+ A meta path importer to import six.moves and its submodules.
+
+ This class implements a PEP302 finder and loader. It should be compatible
+ with Python 2.5 and all existing versions of Python3
+ """
+
+ def __init__(self, six_module_name):
+ self.name = six_module_name
+ self.known_modules = {}
+
+ def _add_module(self, mod, *fullnames):
+ for fullname in fullnames:
+ self.known_modules[self.name + "." + fullname] = mod
+
+ def _get_module(self, fullname):
+ return self.known_modules[self.name + "." + fullname]
+
+ def find_module(self, fullname, path=None):
+ if fullname in self.known_modules:
+ return self
+ return None
+
+ def __get_module(self, fullname):
+ try:
+ return self.known_modules[fullname]
+ except KeyError:
+ raise ImportError("This loader does not know module " + fullname)
+
+ def load_module(self, fullname):
+ try:
+ # in case of a reload
+ return sys.modules[fullname]
+ except KeyError:
+ pass
+ mod = self.__get_module(fullname)
+ if isinstance(mod, MovedModule):
+ mod = mod._resolve()
+ else:
+ mod.__loader__ = self
+ sys.modules[fullname] = mod
+ return mod
+
+ def is_package(self, fullname):
+ """
+ Return true, if the named module is a package.
+
+ We need this method to get correct spec objects with
+ Python 3.4 (see PEP451)
+ """
+ return hasattr(self.__get_module(fullname), "__path__")
+
+ def get_code(self, fullname):
+ """Return None
+
+ Required, if is_package is implemented"""
+ self.__get_module(fullname) # eventually raises ImportError
+ return None
+ get_source = get_code # same as get_code
+
+_importer = _SixMetaPathImporter(__name__)
+
+
+class _MovedItems(_LazyModule):
+
+ """Lazy loading of moved objects"""
+ __path__ = [] # mark as package
+
+
+_moved_attributes = [
+ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
+ MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+ MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
+ MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
+ MovedAttribute("intern", "__builtin__", "sys"),
+ MovedAttribute("map", "itertools", "builtins", "imap", "map"),
+ MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
+ MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
+ MovedAttribute("getoutput", "commands", "subprocess"),
+ MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
+ MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
+ MovedAttribute("reduce", "__builtin__", "functools"),
+ MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
+ MovedAttribute("StringIO", "StringIO", "io"),
+ MovedAttribute("UserDict", "UserDict", "collections"),
+ MovedAttribute("UserList", "UserList", "collections"),
+ MovedAttribute("UserString", "UserString", "collections"),
+ MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
+ MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
+ MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
+ MovedModule("builtins", "__builtin__"),
+ MovedModule("configparser", "ConfigParser"),
+ MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"),
+ MovedModule("copyreg", "copy_reg"),
+ MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
+ MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"),
+ MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"),
+ MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
+ MovedModule("http_cookies", "Cookie", "http.cookies"),
+ MovedModule("html_entities", "htmlentitydefs", "html.entities"),
+ MovedModule("html_parser", "HTMLParser", "html.parser"),
+ MovedModule("http_client", "httplib", "http.client"),
+ MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
+ MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"),
+ MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
+ MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
+ MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
+ MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
+ MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
+ MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
+ MovedModule("cPickle", "cPickle", "pickle"),
+ MovedModule("queue", "Queue"),
+ MovedModule("reprlib", "repr"),
+ MovedModule("socketserver", "SocketServer"),
+ MovedModule("_thread", "thread", "_thread"),
+ MovedModule("tkinter", "Tkinter"),
+ MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
+ MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
+ MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
+ MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
+ MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
+ MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
+ MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
+ MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
+ MovedModule("tkinter_colorchooser", "tkColorChooser",
+ "tkinter.colorchooser"),
+ MovedModule("tkinter_commondialog", "tkCommonDialog",
+ "tkinter.commondialog"),
+ MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
+ MovedModule("tkinter_font", "tkFont", "tkinter.font"),
+ MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
+ MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
+ "tkinter.simpledialog"),
+ MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
+ MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
+ MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
+ MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
+ MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
+ MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
+]
+# Add windows specific modules.
+if sys.platform == "win32":
+ _moved_attributes += [
+ MovedModule("winreg", "_winreg"),
+ ]
+
+for attr in _moved_attributes:
+ setattr(_MovedItems, attr.name, attr)
+ if isinstance(attr, MovedModule):
+ _importer._add_module(attr, "moves." + attr.name)
+del attr
+
+_MovedItems._moved_attributes = _moved_attributes
+
+moves = _MovedItems(__name__ + ".moves")
+_importer._add_module(moves, "moves")
+
+
+class Module_six_moves_urllib_parse(_LazyModule):
+
+ """Lazy loading of moved objects in six.moves.urllib_parse"""
+
+
+_urllib_parse_moved_attributes = [
+ MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
+ MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
+ MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
+ MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
+ MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
+ MovedAttribute("urljoin", "urlparse", "urllib.parse"),
+ MovedAttribute("urlparse", "urlparse", "urllib.parse"),
+ MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
+ MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
+ MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
+ MovedAttribute("quote", "urllib", "urllib.parse"),
+ MovedAttribute("quote_plus", "urllib", "urllib.parse"),
+ MovedAttribute("unquote", "urllib", "urllib.parse"),
+ MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
+ MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"),
+ MovedAttribute("urlencode", "urllib", "urllib.parse"),
+ MovedAttribute("splitquery", "urllib", "urllib.parse"),
+ MovedAttribute("splittag", "urllib", "urllib.parse"),
+ MovedAttribute("splituser", "urllib", "urllib.parse"),
+ MovedAttribute("splitvalue", "urllib", "urllib.parse"),
+ MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
+ MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
+ MovedAttribute("uses_params", "urlparse", "urllib.parse"),
+ MovedAttribute("uses_query", "urlparse", "urllib.parse"),
+ MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
+]
+for attr in _urllib_parse_moved_attributes:
+ setattr(Module_six_moves_urllib_parse, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
+ "moves.urllib_parse", "moves.urllib.parse")
+
+
+class Module_six_moves_urllib_error(_LazyModule):
+
+ """Lazy loading of moved objects in six.moves.urllib_error"""
+
+
+_urllib_error_moved_attributes = [
+ MovedAttribute("URLError", "urllib2", "urllib.error"),
+ MovedAttribute("HTTPError", "urllib2", "urllib.error"),
+ MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
+]
+for attr in _urllib_error_moved_attributes:
+ setattr(Module_six_moves_urllib_error, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
+ "moves.urllib_error", "moves.urllib.error")
+
+
+class Module_six_moves_urllib_request(_LazyModule):
+
+ """Lazy loading of moved objects in six.moves.urllib_request"""
+
+
+_urllib_request_moved_attributes = [
+ MovedAttribute("urlopen", "urllib2", "urllib.request"),
+ MovedAttribute("install_opener", "urllib2", "urllib.request"),
+ MovedAttribute("build_opener", "urllib2", "urllib.request"),
+ MovedAttribute("pathname2url", "urllib", "urllib.request"),
+ MovedAttribute("url2pathname", "urllib", "urllib.request"),
+ MovedAttribute("getproxies", "urllib", "urllib.request"),
+ MovedAttribute("Request", "urllib2", "urllib.request"),
+ MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
+ MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
+ MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
+ MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
+ MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
+ MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
+ MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
+ MovedAttribute("FileHandler", "urllib2", "urllib.request"),
+ MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
+ MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
+ MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
+ MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
+ MovedAttribute("urlretrieve", "urllib", "urllib.request"),
+ MovedAttribute("urlcleanup", "urllib", "urllib.request"),
+ MovedAttribute("URLopener", "urllib", "urllib.request"),
+ MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
+ MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
+ MovedAttribute("parse_http_list", "urllib2", "urllib.request"),
+ MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"),
+]
+for attr in _urllib_request_moved_attributes:
+ setattr(Module_six_moves_urllib_request, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
+ "moves.urllib_request", "moves.urllib.request")
+
+
+class Module_six_moves_urllib_response(_LazyModule):
+
+ """Lazy loading of moved objects in six.moves.urllib_response"""
+
+
+_urllib_response_moved_attributes = [
+ MovedAttribute("addbase", "urllib", "urllib.response"),
+ MovedAttribute("addclosehook", "urllib", "urllib.response"),
+ MovedAttribute("addinfo", "urllib", "urllib.response"),
+ MovedAttribute("addinfourl", "urllib", "urllib.response"),
+]
+for attr in _urllib_response_moved_attributes:
+ setattr(Module_six_moves_urllib_response, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
+ "moves.urllib_response", "moves.urllib.response")
+
+
+class Module_six_moves_urllib_robotparser(_LazyModule):
+
+ """Lazy loading of moved objects in six.moves.urllib_robotparser"""
+
+
+_urllib_robotparser_moved_attributes = [
+ MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
+]
+for attr in _urllib_robotparser_moved_attributes:
+ setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
+ "moves.urllib_robotparser", "moves.urllib.robotparser")
+
+
+class Module_six_moves_urllib(types.ModuleType):
+
+ """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
+ __path__ = [] # mark as package
+ parse = _importer._get_module("moves.urllib_parse")
+ error = _importer._get_module("moves.urllib_error")
+ request = _importer._get_module("moves.urllib_request")
+ response = _importer._get_module("moves.urllib_response")
+ robotparser = _importer._get_module("moves.urllib_robotparser")
+
+ def __dir__(self):
+ return ['parse', 'error', 'request', 'response', 'robotparser']
+
+_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
+ "moves.urllib")
+
+
+def add_move(move):
+ """Add an item to six.moves."""
+ setattr(_MovedItems, move.name, move)
+
+
+def remove_move(name):
+ """Remove item from six.moves."""
+ try:
+ delattr(_MovedItems, name)
+ except AttributeError:
+ try:
+ del moves.__dict__[name]
+ except KeyError:
+ raise AttributeError("no such move, %r" % (name,))
+
+
+if PY3:
+ _meth_func = "__func__"
+ _meth_self = "__self__"
+
+ _func_closure = "__closure__"
+ _func_code = "__code__"
+ _func_defaults = "__defaults__"
+ _func_globals = "__globals__"
+else:
+ _meth_func = "im_func"
+ _meth_self = "im_self"
+
+ _func_closure = "func_closure"
+ _func_code = "func_code"
+ _func_defaults = "func_defaults"
+ _func_globals = "func_globals"
+
+
+try:
+ advance_iterator = next
+except NameError:
+ def advance_iterator(it):
+ return it.next()
+next = advance_iterator
+
+
+try:
+ callable = callable
+except NameError:
+ def callable(obj):
+ return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+
+
+if PY3:
+ def get_unbound_function(unbound):
+ return unbound
+
+ create_bound_method = types.MethodType
+
+ def create_unbound_method(func, cls):
+ return func
+
+ Iterator = object
+else:
+ def get_unbound_function(unbound):
+ return unbound.im_func
+
+ def create_bound_method(func, obj):
+ return types.MethodType(func, obj, obj.__class__)
+
+ def create_unbound_method(func, cls):
+ return types.MethodType(func, None, cls)
+
+ class Iterator(object):
+
+ def next(self):
+ return type(self).__next__(self)
+
+ callable = callable
+_add_doc(get_unbound_function,
+ """Get the function out of a possibly unbound function""")
+
+
+get_method_function = operator.attrgetter(_meth_func)
+get_method_self = operator.attrgetter(_meth_self)
+get_function_closure = operator.attrgetter(_func_closure)
+get_function_code = operator.attrgetter(_func_code)
+get_function_defaults = operator.attrgetter(_func_defaults)
+get_function_globals = operator.attrgetter(_func_globals)
+
+
+if PY3:
+ def iterkeys(d, **kw):
+ return iter(d.keys(**kw))
+
+ def itervalues(d, **kw):
+ return iter(d.values(**kw))
+
+ def iteritems(d, **kw):
+ return iter(d.items(**kw))
+
+ def iterlists(d, **kw):
+ return iter(d.lists(**kw))
+
+ viewkeys = operator.methodcaller("keys")
+
+ viewvalues = operator.methodcaller("values")
+
+ viewitems = operator.methodcaller("items")
+else:
+ def iterkeys(d, **kw):
+ return d.iterkeys(**kw)
+
+ def itervalues(d, **kw):
+ return d.itervalues(**kw)
+
+ def iteritems(d, **kw):
+ return d.iteritems(**kw)
+
+ def iterlists(d, **kw):
+ return d.iterlists(**kw)
+
+ viewkeys = operator.methodcaller("viewkeys")
+
+ viewvalues = operator.methodcaller("viewvalues")
+
+ viewitems = operator.methodcaller("viewitems")
+
+_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
+_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
+_add_doc(iteritems,
+ "Return an iterator over the (key, value) pairs of a dictionary.")
+_add_doc(iterlists,
+ "Return an iterator over the (key, [values]) pairs of a dictionary.")
+
+
+if PY3:
+ def b(s):
+ return s.encode("latin-1")
+
+ def u(s):
+ return s
+ unichr = chr
+ import struct
+ int2byte = struct.Struct(">B").pack
+ del struct
+ byte2int = operator.itemgetter(0)
+ indexbytes = operator.getitem
+ iterbytes = iter
+ import io
+ StringIO = io.StringIO
+ BytesIO = io.BytesIO
+ del io
+ _assertCountEqual = "assertCountEqual"
+ if sys.version_info[1] <= 1:
+ _assertRaisesRegex = "assertRaisesRegexp"
+ _assertRegex = "assertRegexpMatches"
+ _assertNotRegex = "assertNotRegexpMatches"
+ else:
+ _assertRaisesRegex = "assertRaisesRegex"
+ _assertRegex = "assertRegex"
+ _assertNotRegex = "assertNotRegex"
+else:
+ def b(s):
+ return s
+ # Workaround for standalone backslash
+
+ def u(s):
+ return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
+ unichr = unichr
+ int2byte = chr
+
+ def byte2int(bs):
+ return ord(bs[0])
+
+ def indexbytes(buf, i):
+ return ord(buf[i])
+ iterbytes = functools.partial(itertools.imap, ord)
+ import StringIO
+ StringIO = BytesIO = StringIO.StringIO
+ _assertCountEqual = "assertItemsEqual"
+ _assertRaisesRegex = "assertRaisesRegexp"
+ _assertRegex = "assertRegexpMatches"
+ _assertNotRegex = "assertNotRegexpMatches"
+_add_doc(b, """Byte literal""")
+_add_doc(u, """Text literal""")
+
+
+def assertCountEqual(self, *args, **kwargs):
+ return getattr(self, _assertCountEqual)(*args, **kwargs)
+
+
+def assertRaisesRegex(self, *args, **kwargs):
+ return getattr(self, _assertRaisesRegex)(*args, **kwargs)
+
+
+def assertRegex(self, *args, **kwargs):
+ return getattr(self, _assertRegex)(*args, **kwargs)
+
+
+def assertNotRegex(self, *args, **kwargs):
+ return getattr(self, _assertNotRegex)(*args, **kwargs)
+
+
+if PY3:
+ exec_ = getattr(moves.builtins, "exec")
+
+ def reraise(tp, value, tb=None):
+ try:
+ if value is None:
+ value = tp()
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
+ finally:
+ value = None
+ tb = None
+
+else:
+ def exec_(_code_, _globs_=None, _locs_=None):
+ """Execute code in a namespace."""
+ if _globs_ is None:
+ frame = sys._getframe(1)
+ _globs_ = frame.f_globals
+ if _locs_ is None:
+ _locs_ = frame.f_locals
+ del frame
+ elif _locs_ is None:
+ _locs_ = _globs_
+ exec("""exec _code_ in _globs_, _locs_""")
+
+ exec_("""def reraise(tp, value, tb=None):
+ try:
+ raise tp, value, tb
+ finally:
+ tb = None
+""")
+
+
+if sys.version_info[:2] > (3,):
+ exec_("""def raise_from(value, from_value):
+ try:
+ raise value from from_value
+ finally:
+ value = None
+""")
+else:
+ def raise_from(value, from_value):
+ raise value
+
+
+print_ = getattr(moves.builtins, "print", None)
+if print_ is None:
+ def print_(*args, **kwargs):
+ """The new-style print function for Python 2.4 and 2.5."""
+ fp = kwargs.pop("file", sys.stdout)
+ if fp is None:
+ return
+
+ def write(data):
+ if not isinstance(data, basestring):
+ data = str(data)
+ # If the file has an encoding, encode unicode with it.
+ if (isinstance(fp, file) and
+ isinstance(data, unicode) and
+ fp.encoding is not None):
+ errors = getattr(fp, "errors", None)
+ if errors is None:
+ errors = "strict"
+ data = data.encode(fp.encoding, errors)
+ fp.write(data)
+ want_unicode = False
+ sep = kwargs.pop("sep", None)
+ if sep is not None:
+ if isinstance(sep, unicode):
+ want_unicode = True
+ elif not isinstance(sep, str):
+ raise TypeError("sep must be None or a string")
+ end = kwargs.pop("end", None)
+ if end is not None:
+ if isinstance(end, unicode):
+ want_unicode = True
+ elif not isinstance(end, str):
+ raise TypeError("end must be None or a string")
+ if kwargs:
+ raise TypeError("invalid keyword arguments to print()")
+ if not want_unicode:
+ for arg in args:
+ if isinstance(arg, unicode):
+ want_unicode = True
+ break
+ if want_unicode:
+ newline = unicode("\n")
+ space = unicode(" ")
+ else:
+ newline = "\n"
+ space = " "
+ if sep is None:
+ sep = space
+ if end is None:
+ end = newline
+ for i, arg in enumerate(args):
+ if i:
+ write(sep)
+ write(arg)
+ write(end)
+if sys.version_info[:2] < (3, 3):
+ _print = print_
+
+ def print_(*args, **kwargs):
+ fp = kwargs.get("file", sys.stdout)
+ flush = kwargs.pop("flush", False)
+ _print(*args, **kwargs)
+ if flush and fp is not None:
+ fp.flush()
+
+_add_doc(reraise, """Reraise an exception.""")
+
+if sys.version_info[0:2] < (3, 4):
+ # This does exactly the same what the :func:`py3:functools.update_wrapper`
+ # function does on Python versions after 3.2. It sets the ``__wrapped__``
+ # attribute on ``wrapper`` object and it doesn't raise an error if any of
+ # the attributes mentioned in ``assigned`` and ``updated`` are missing on
+ # ``wrapped`` object.
+ def _update_wrapper(wrapper, wrapped,
+ assigned=functools.WRAPPER_ASSIGNMENTS,
+ updated=functools.WRAPPER_UPDATES):
+ for attr in assigned:
+ try:
+ value = getattr(wrapped, attr)
+ except AttributeError:
+ continue
+ else:
+ setattr(wrapper, attr, value)
+ for attr in updated:
+ getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
+ wrapper.__wrapped__ = wrapped
+ return wrapper
+ _update_wrapper.__doc__ = functools.update_wrapper.__doc__
+
+ def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
+ updated=functools.WRAPPER_UPDATES):
+ return functools.partial(_update_wrapper, wrapped=wrapped,
+ assigned=assigned, updated=updated)
+ wraps.__doc__ = functools.wraps.__doc__
+
+else:
+ wraps = functools.wraps
+
+
+def with_metaclass(meta, *bases):
+ """Create a base class with a metaclass."""
+ # This requires a bit of explanation: the basic idea is to make a dummy
+ # metaclass for one level of class instantiation that replaces itself with
+ # the actual metaclass.
+ class metaclass(type):
+
+ def __new__(cls, name, this_bases, d):
+ if sys.version_info[:2] >= (3, 7):
+ # This version introduced PEP 560 that requires a bit
+ # of extra care (we mimic what is done by __build_class__).
+ resolved_bases = types.resolve_bases(bases)
+ if resolved_bases is not bases:
+ d['__orig_bases__'] = bases
+ else:
+ resolved_bases = bases
+ return meta(name, resolved_bases, d)
+
+ @classmethod
+ def __prepare__(cls, name, this_bases):
+ return meta.__prepare__(name, bases)
+ return type.__new__(metaclass, 'temporary_class', (), {})
+
+
+def add_metaclass(metaclass):
+ """Class decorator for creating a class with a metaclass."""
+ def wrapper(cls):
+ orig_vars = cls.__dict__.copy()
+ slots = orig_vars.get('__slots__')
+ if slots is not None:
+ if isinstance(slots, str):
+ slots = [slots]
+ for slots_var in slots:
+ orig_vars.pop(slots_var)
+ orig_vars.pop('__dict__', None)
+ orig_vars.pop('__weakref__', None)
+ if hasattr(cls, '__qualname__'):
+ orig_vars['__qualname__'] = cls.__qualname__
+ return metaclass(cls.__name__, cls.__bases__, orig_vars)
+ return wrapper
+
+
+def ensure_binary(s, encoding='utf-8', errors='strict'):
+ """Coerce **s** to six.binary_type.
+
+ For Python 2:
+ - `unicode` -> encoded to `str`
+ - `str` -> `str`
+
+ For Python 3:
+ - `str` -> encoded to `bytes`
+ - `bytes` -> `bytes`
+ """
+ if isinstance(s, text_type):
+ return s.encode(encoding, errors)
+ elif isinstance(s, binary_type):
+ return s
+ else:
+ raise TypeError("not expecting type '%s'" % type(s))
+
+
+def ensure_str(s, encoding='utf-8', errors='strict'):
+ """Coerce *s* to `str`.
+
+ For Python 2:
+ - `unicode` -> encoded to `str`
+ - `str` -> `str`
+
+ For Python 3:
+ - `str` -> `str`
+ - `bytes` -> decoded to `str`
+ """
+ if not isinstance(s, (text_type, binary_type)):
+ raise TypeError("not expecting type '%s'" % type(s))
+ if PY2 and isinstance(s, text_type):
+ s = s.encode(encoding, errors)
+ elif PY3 and isinstance(s, binary_type):
+ s = s.decode(encoding, errors)
+ return s
+
+
+def ensure_text(s, encoding='utf-8', errors='strict'):
+ """Coerce *s* to six.text_type.
+
+ For Python 2:
+ - `unicode` -> `unicode`
+ - `str` -> `unicode`
+
+ For Python 3:
+ - `str` -> `str`
+ - `bytes` -> decoded to `str`
+ """
+ if isinstance(s, binary_type):
+ return s.decode(encoding, errors)
+ elif isinstance(s, text_type):
+ return s
+ else:
+ raise TypeError("not expecting type '%s'" % type(s))
+
+
+def python_2_unicode_compatible(klass):
+ """
+ A class decorator that defines __unicode__ and __str__ methods under Python 2.
+ Under Python 3 it does nothing.
+
+ To support Python 2 and 3 with a single code base, define a __str__ method
+ returning text and apply this decorator to the class.
+ """
+ if PY2:
+ if '__str__' not in klass.__dict__:
+ raise ValueError("@python_2_unicode_compatible cannot be applied "
+ "to %s because it doesn't define __str__()." %
+ klass.__name__)
+ klass.__unicode__ = klass.__str__
+ klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
+ return klass
+
+
+# Complete the moves implementation.
+# This code is at the end of this module to speed up module loading.
+# Turn this module into a package.
+__path__ = [] # required for PEP 302 and PEP 451
+__package__ = __name__ # see PEP 366 @ReservedAssignment
+if globals().get("__spec__") is not None:
+ __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
+# Remove other six meta path importers, since they cause problems. This can
+# happen if six is removed from sys.modules and then reloaded. (Setuptools does
+# this for some reason.)
+if sys.meta_path:
+ for i, importer in enumerate(sys.meta_path):
+ # Here's some real nastiness: Another "instance" of the six module might
+ # be floating around. Therefore, we can't use isinstance() to check for
+ # the six meta path importer, since the other six instance will have
+ # inserted an importer with different class.
+ if (type(importer).__name__ == "_SixMetaPathImporter" and
+ importer.name == __name__):
+ del sys.meta_path[i]
+ break
+ del i, importer
+# Finally, add the importer to the meta path import hook.
+sys.meta_path.append(_importer)
diff --git a/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/INSTALLER b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/INSTALLER
new file mode 100644
index 0000000..a1b589e
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/LICENSE b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/LICENSE
new file mode 100644
index 0000000..2565558
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/LICENSE
@@ -0,0 +1,290 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: typed-ast
+Source: https://pypi.python.org/pypi/typed-ast
+
+Files: *
+Copyright: © 2016 David Fisher <ddfisher@dropbox.com>
+License: Apache-2.0
+
+Files: *
+Copyright: © 2016 David Fisher <ddfisher@dropbox.com>
+ © 2008 Armin Ronacher
+Comment: The original CPython source is licensed under the
+ Python Software Foundation License Version 2
+License: Python
+
+Files: ast27/Parser/spark.py
+Copyright: © 1998-2002 John Aycock
+License: Expat
+ 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.
+
+
+License: Apache-2.0
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+ .
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+ .
+ 1. Definitions.
+ .
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+ .
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+ .
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+ .
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+ .
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+ .
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+ .
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+ .
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+ .
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+ .
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+ .
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+ .
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+ .
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+ .
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+ .
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+ .
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+ .
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+ .
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+ .
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+ .
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+ .
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+ .
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+ .
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+ .
+ END OF TERMS AND CONDITIONS
+ .
+ APPENDIX: How to apply the Apache License to your work.
+ .
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+ .
+ Copyright 2016 Dropbox, Inc.
+ .
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ .
+ http://www.apache.org/licenses/LICENSE-2.0
+ .
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+License: Python
+ PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+ --------------------------------------------
+ .
+ 1. This LICENSE AGREEMENT is between the Python Software Foundation
+ ("PSF"), and the Individual or Organization ("Licensee") accessing and
+ otherwise using this software ("Python") in source or binary form and
+ its associated documentation.
+ .
+ 2. Subject to the terms and conditions of this License Agreement, PSF hereby
+ grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+ analyze, test, perform and/or display publicly, prepare derivative works,
+ distribute, and otherwise use Python alone or in any derivative version,
+ provided, however, that PSF's License Agreement and PSF's notice of copyright,
+ i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ 2011, 2012, 2013, 2014, 2015, 2016 Python Software Foundation; All Rights
+ Reserved" are retained in Python alone or in any derivative version prepared by
+ Licensee.
+ .
+ 3. In the event Licensee prepares a derivative work that is based on
+ or incorporates Python or any part thereof, and wants to make
+ the derivative work available to others as provided herein, then
+ Licensee hereby agrees to include in any such work a brief summary of
+ the changes made to Python.
+ .
+ 4. PSF is making Python available to Licensee on an "AS IS"
+ basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+ IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+ DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+ FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+ INFRINGE ANY THIRD PARTY RIGHTS.
+ .
+ 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+ FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+ A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+ OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+ .
+ 6. This License Agreement will automatically terminate upon a material
+ breach of its terms and conditions.
+ .
+ 7. Nothing in this License Agreement shall be deemed to create any
+ relationship of agency, partnership, or joint venture between PSF and
+ Licensee. This License Agreement does not grant permission to use PSF
+ trademarks or trade name in a trademark sense to endorse or promote
+ products or services of Licensee, or any third party.
+ .
+ 8. By copying, installing or otherwise using Python, Licensee
+ agrees to be bound by the terms and conditions of this License
+ Agreement.
diff --git a/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/METADATA b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/METADATA
new file mode 100644
index 0000000..4310a0d
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/METADATA
@@ -0,0 +1,28 @@
+Metadata-Version: 2.1
+Name: typed-ast
+Version: 1.4.1
+Summary: a fork of Python 2 and 3 ast modules with type comment support
+Home-page: https://github.com/python/typed_ast
+Author: David Fisher
+Author-email: UNKNOWN
+License: Apache License 2.0
+Platform: POSIX
+Platform: Windows
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Topic :: Software Development
+
+`typed_ast` is a Python 3 package that provides a Python 2.7 and Python 3
+parser similar to the standard `ast` library. Unlike `ast`, the parsers in
+`typed_ast` include PEP 484 type comments and are independent of the version of
+Python under which they are run. The `typed_ast` parsers produce the standard
+Python AST (plus type comments), and are both fast and correct, as they are
+based on the CPython 2.7 and 3.6 parsers.
+
diff --git a/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/RECORD b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/RECORD
new file mode 100644
index 0000000..aec2f89
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/RECORD
@@ -0,0 +1,18 @@
+typed_ast-1.4.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+typed_ast-1.4.1.dist-info/LICENSE,sha256=PwfDPiHJOi2_T6I50fxiYmilsIHot5rKO48KR7BQ8Uw,15191
+typed_ast-1.4.1.dist-info/METADATA,sha256=L2YMWfFrFfLLglAbPDeooa2VWjr66S9yx5z2k3cIl1g,1163
+typed_ast-1.4.1.dist-info/RECORD,,
+typed_ast-1.4.1.dist-info/WHEEL,sha256=uaZe_9gV-4T_d4AskuIQkCgcY8wMc0UXsVFnf0_mBGs,106
+typed_ast-1.4.1.dist-info/top_level.txt,sha256=LCmBygYWBo6qqIoaZNicoxU-DO9gR2JvhQkVJwSyN1k,23
+typed_ast/__init__.py,sha256=BqOI5y46o1G1RWC9bF1DPL-YM68lGYPmZt1pn6FZFZs,22
+typed_ast/__pycache__/__init__.cpython-37.pyc,,
+typed_ast/__pycache__/ast27.cpython-37.pyc,,
+typed_ast/__pycache__/ast3.cpython-37.pyc,,
+typed_ast/__pycache__/conversions.cpython-37.pyc,,
+typed_ast/_ast27.cp37-win_amd64.pyd,sha256=Br_xsc8o9mz2WB2f4hjB2DE83vdVi7wpY_a0bnPG1ts,165888
+typed_ast/_ast3.cp37-win_amd64.pyd,sha256=SeQgXupZuH-oJEMKYRSSrzqjNDbi-a834i1q44ATaJo,186368
+typed_ast/ast27.py,sha256=MouNmlSUIINEyl3LBa796DXBvZNOjo5-gCPzoYRDb1Q,12630
+typed_ast/ast3.py,sha256=2Fb_0TUknxmDPzBXPYe1XkGhJL1JxeR2zLivWII3TqI,13761
+typed_ast/conversions.py,sha256=J9wBDCg-it3cxfSnIAMWnBDiwLypfRBc7RTftRImwak,8632
+typed_ast/tests/__pycache__/test_basics.cpython-37.pyc,,
+typed_ast/tests/test_basics.py,sha256=2aQmOXfqKyBNaVpB9FeYkMqSWy9Qz1aGwVCs70IrDKo,7516
diff --git a/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/WHEEL b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/WHEEL
new file mode 100644
index 0000000..c4dd0f9
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.33.6)
+Root-Is-Purelib: false
+Tag: cp37-cp37m-win_amd64
+
diff --git a/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/top_level.txt b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/top_level.txt
new file mode 100644
index 0000000..8c96e51
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast-1.4.1.dist-info/top_level.txt
@@ -0,0 +1,3 @@
+_ast27
+_ast3
+typed_ast
diff --git a/venv/Lib/site-packages/typed_ast/__init__.py b/venv/Lib/site-packages/typed_ast/__init__.py
new file mode 100644
index 0000000..bf25615
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/__init__.py
@@ -0,0 +1 @@
+__version__ = "1.4.1"
diff --git a/venv/Lib/site-packages/typed_ast/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/typed_ast/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..8d1e8cf
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/typed_ast/__pycache__/ast27.cpython-37.pyc b/venv/Lib/site-packages/typed_ast/__pycache__/ast27.cpython-37.pyc
new file mode 100644
index 0000000..bc79e0c
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/__pycache__/ast27.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/typed_ast/__pycache__/ast3.cpython-37.pyc b/venv/Lib/site-packages/typed_ast/__pycache__/ast3.cpython-37.pyc
new file mode 100644
index 0000000..f59503a
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/__pycache__/ast3.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/typed_ast/__pycache__/conversions.cpython-37.pyc b/venv/Lib/site-packages/typed_ast/__pycache__/conversions.cpython-37.pyc
new file mode 100644
index 0000000..b5d2c32
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/__pycache__/conversions.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/typed_ast/_ast27.cp37-win_amd64.pyd b/venv/Lib/site-packages/typed_ast/_ast27.cp37-win_amd64.pyd
new file mode 100644
index 0000000..1e1b24a
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/_ast27.cp37-win_amd64.pyd
Binary files differ
diff --git a/venv/Lib/site-packages/typed_ast/_ast3.cp37-win_amd64.pyd b/venv/Lib/site-packages/typed_ast/_ast3.cp37-win_amd64.pyd
new file mode 100644
index 0000000..acc389b
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/_ast3.cp37-win_amd64.pyd
Binary files differ
diff --git a/venv/Lib/site-packages/typed_ast/ast27.py b/venv/Lib/site-packages/typed_ast/ast27.py
new file mode 100644
index 0000000..4ed7a6c
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/ast27.py
@@ -0,0 +1,324 @@
+# -*- coding: utf-8 -*-
+"""
+ ast27
+ ~~~
+
+ The `ast27` module helps Python applications to process trees of the Python
+ abstract syntax grammar. The abstract syntax itself might change with
+ each Python release; this module helps to find out programmatically what
+ the current grammar looks like and allows modifications of it. The `ast27`
+ module is similar to the builtin `ast` module on Python 2.7, except `ast27`
+ runs on Python 3 and provides PEP 484 type comments as part of the AST.
+
+ Specifically, these changes are made to the Python 2.7 AST:
+ - The `FunctionDef`, `Assign`, `For`, and `With` classes all have a
+ `type_comment` field which contains a `str` with the text of the
+ associated type comment, if any.
+ - `arguments` has a `type_comments` list of per-argument type comments.
+ - `parse` has been augmented so it can parse function signature types when
+ called with `mode=func_type`.
+ - `Module` has a `type_ignores` field which contains a list of
+ lines which have been `# type: ignore`d.
+ - `Str` has a `kind` string field which preserves the original string
+ prefix, so that `ast27.parse('br"test"').body[0].value.kind == 'br'`.
+
+ An abstract syntax tree can be generated by using the `parse()`
+ function from this module. The result will be a tree of objects whose
+ classes all inherit from `ast27.AST`.
+
+ A modified abstract syntax tree can be compiled into a Python code object
+ using the built-in `compile()` function.
+
+ Additionally various helper functions are provided that make working with
+ the trees simpler. The main intention of the helper functions and this
+ module in general is to provide an easy to use interface for libraries
+ that work tightly with the python syntax (template engines for example).
+
+
+ :copyright: Copyright 2008 by Armin Ronacher.
+ :license: Python License.
+"""
+from typed_ast import _ast27
+from typed_ast._ast27 import *
+
+
+def parse(source, filename='<unknown>', mode='exec'):
+ """
+ Parse the source into an AST node with type comments.
+ Equivalent to compile(source, filename, mode, PyCF_ONLY_AST).
+ """
+ return _ast27.parse(source, filename, mode)
+
+
+def literal_eval(node_or_string):
+ """
+ Safely evaluate an expression node or a string containing a Python
+ expression. The string or node provided may only consist of the following
+ Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
+ and None.
+ """
+ _safe_names = {'None': None, 'True': True, 'False': False}
+ if isinstance(node_or_string, (str, bytes)):
+ node_or_string = parse(node_or_string, mode='eval')
+ if isinstance(node_or_string, Expression):
+ node_or_string = node_or_string.body
+ def _convert(node):
+ if isinstance(node, Str):
+ return node.s
+ elif isinstance(node, Num):
+ return node.n
+ elif isinstance(node, Tuple):
+ return tuple(map(_convert, node.elts))
+ elif isinstance(node, List):
+ return list(map(_convert, node.elts))
+ elif isinstance(node, Dict):
+ return dict((_convert(k), _convert(v)) for k, v
+ in zip(node.keys, node.values))
+ elif isinstance(node, Name):
+ if node.id in _safe_names:
+ return _safe_names[node.id]
+ elif isinstance(node, BinOp) and \
+ isinstance(node.op, (Add, Sub)) and \
+ isinstance(node.right, Num) and \
+ isinstance(node.right.n, complex) and \
+ isinstance(node.left, Num) and \
+ isinstance(node.left.n, (int, long, float)):
+ left = node.left.n
+ right = node.right.n
+ if isinstance(node.op, Add):
+ return left + right
+ else:
+ return left - right
+ raise ValueError('malformed string')
+ return _convert(node_or_string)
+
+
+def dump(node, annotate_fields=True, include_attributes=False):
+ """
+ Return a formatted dump of the tree in *node*. This is mainly useful for
+ debugging purposes. The returned string will show the names and the values
+ for fields. This makes the code impossible to evaluate, so if evaluation is
+ wanted *annotate_fields* must be set to False. Attributes such as line
+ numbers and column offsets are not dumped by default. If this is wanted,
+ *include_attributes* can be set to True.
+ """
+ def _format(node):
+ if isinstance(node, AST):
+ fields = [(a, _format(b)) for a, b in iter_fields(node)]
+ rv = '%s(%s' % (node.__class__.__name__, ', '.join(
+ ('%s=%s' % field for field in fields)
+ if annotate_fields else
+ (b for a, b in fields)
+ ))
+ if include_attributes and node._attributes:
+ rv += fields and ', ' or ' '
+ rv += ', '.join('%s=%s' % (a, _format(getattr(node, a)))
+ for a in node._attributes)
+ return rv + ')'
+ elif isinstance(node, list):
+ return '[%s]' % ', '.join(_format(x) for x in node)
+ return repr(node)
+ if not isinstance(node, AST):
+ raise TypeError('expected AST, got %r' % node.__class__.__name__)
+ return _format(node)
+
+
+def copy_location(new_node, old_node):
+ """
+ Copy source location (`lineno` and `col_offset` attributes) from
+ *old_node* to *new_node* if possible, and return *new_node*.
+ """
+ for attr in 'lineno', 'col_offset':
+ if attr in old_node._attributes and attr in new_node._attributes \
+ and hasattr(old_node, attr):
+ setattr(new_node, attr, getattr(old_node, attr))
+ return new_node
+
+
+def fix_missing_locations(node):
+ """
+ When you compile a node tree with compile(), the compiler expects lineno and
+ col_offset attributes for every node that supports them. This is rather
+ tedious to fill in for generated nodes, so this helper adds these attributes
+ recursively where not already set, by setting them to the values of the
+ parent node. It works recursively starting at *node*.
+ """
+ def _fix(node, lineno, col_offset):
+ if 'lineno' in node._attributes:
+ if not hasattr(node, 'lineno'):
+ node.lineno = lineno
+ else:
+ lineno = node.lineno
+ if 'col_offset' in node._attributes:
+ if not hasattr(node, 'col_offset'):
+ node.col_offset = col_offset
+ else:
+ col_offset = node.col_offset
+ for child in iter_child_nodes(node):
+ _fix(child, lineno, col_offset)
+ _fix(node, 1, 0)
+ return node
+
+
+def increment_lineno(node, n=1):
+ """
+ Increment the line number of each node in the tree starting at *node* by *n*.
+ This is useful to "move code" to a different location in a file.
+ """
+ for child in walk(node):
+ if 'lineno' in child._attributes:
+ child.lineno = getattr(child, 'lineno', 0) + n
+ return node
+
+
+def iter_fields(node):
+ """
+ Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
+ that is present on *node*.
+ """
+ for field in node._fields:
+ try:
+ yield field, getattr(node, field)
+ except AttributeError:
+ pass
+
+
+def iter_child_nodes(node):
+ """
+ Yield all direct child nodes of *node*, that is, all fields that are nodes
+ and all items of fields that are lists of nodes.
+ """
+ for name, field in iter_fields(node):
+ if isinstance(field, AST):
+ yield field
+ elif isinstance(field, list):
+ for item in field:
+ if isinstance(item, AST):
+ yield item
+
+
+def get_docstring(node, clean=True):
+ """
+ Return the docstring for the given node or None if no docstring can
+ be found. If the node provided does not have docstrings a TypeError
+ will be raised.
+ """
+ if not isinstance(node, (FunctionDef, ClassDef, Module)):
+ raise TypeError("%r can't have docstrings" % node.__class__.__name__)
+ if node.body and isinstance(node.body[0], Expr) and \
+ isinstance(node.body[0].value, Str):
+ if clean:
+ import inspect
+ return inspect.cleandoc(node.body[0].value.s)
+ return node.body[0].value.s
+
+
+def walk(node):
+ """
+ Recursively yield all descendant nodes in the tree starting at *node*
+ (including *node* itself), in no specified order. This is useful if you
+ only want to modify nodes in place and don't care about the context.
+ """
+ from collections import deque
+ todo = deque([node])
+ while todo:
+ node = todo.popleft()
+ todo.extend(iter_child_nodes(node))
+ yield node
+
+
+class NodeVisitor(object):
+ """
+ A node visitor base class that walks the abstract syntax tree and calls a
+ visitor function for every node found. This function may return a value
+ which is forwarded by the `visit` method.
+
+ This class is meant to be subclassed, with the subclass adding visitor
+ methods.
+
+ Per default the visitor functions for the nodes are ``'visit_'`` +
+ class name of the node. So a `TryFinally` node visit function would
+ be `visit_TryFinally`. This behavior can be changed by overriding
+ the `visit` method. If no visitor function exists for a node
+ (return value `None`) the `generic_visit` visitor is used instead.
+
+ Don't use the `NodeVisitor` if you want to apply changes to nodes during
+ traversing. For this a special visitor exists (`NodeTransformer`) that
+ allows modifications.
+ """
+
+ def visit(self, node):
+ """Visit a node."""
+ method = 'visit_' + node.__class__.__name__
+ visitor = getattr(self, method, self.generic_visit)
+ return visitor(node)
+
+ def generic_visit(self, node):
+ """Called if no explicit visitor function exists for a node."""
+ for field, value in iter_fields(node):
+ if isinstance(value, list):
+ for item in value:
+ if isinstance(item, AST):
+ self.visit(item)
+ elif isinstance(value, AST):
+ self.visit(value)
+
+
+class NodeTransformer(NodeVisitor):
+ """
+ A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
+ allows modification of nodes.
+
+ The `NodeTransformer` will walk the AST and use the return value of the
+ visitor methods to replace or remove the old node. If the return value of
+ the visitor method is ``None``, the node will be removed from its location,
+ otherwise it is replaced with the return value. The return value may be the
+ original node in which case no replacement takes place.
+
+ Here is an example transformer that rewrites all occurrences of name lookups
+ (``foo``) to ``data['foo']``::
+
+ class RewriteName(NodeTransformer):
+
+ def visit_Name(self, node):
+ return copy_location(Subscript(
+ value=Name(id='data', ctx=Load()),
+ slice=Index(value=Str(s=node.id, kind='')),
+ ctx=node.ctx
+ ), node)
+
+ Keep in mind that if the node you're operating on has child nodes you must
+ either transform the child nodes yourself or call the :meth:`generic_visit`
+ method for the node first.
+
+ For nodes that were part of a collection of statements (that applies to all
+ statement nodes), the visitor may also return a list of nodes rather than
+ just a single node.
+
+ Usually you use the transformer like this::
+
+ node = YourTransformer().visit(node)
+ """
+
+ def generic_visit(self, node):
+ for field, old_value in iter_fields(node):
+ old_value = getattr(node, field, None)
+ if isinstance(old_value, list):
+ new_values = []
+ for value in old_value:
+ if isinstance(value, AST):
+ value = self.visit(value)
+ if value is None:
+ continue
+ elif not isinstance(value, AST):
+ new_values.extend(value)
+ continue
+ new_values.append(value)
+ old_value[:] = new_values
+ elif isinstance(old_value, AST):
+ new_node = self.visit(old_value)
+ if new_node is None:
+ delattr(node, field)
+ else:
+ setattr(node, field, new_node)
+ return node
diff --git a/venv/Lib/site-packages/typed_ast/ast3.py b/venv/Lib/site-packages/typed_ast/ast3.py
new file mode 100644
index 0000000..0a8a700
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/ast3.py
@@ -0,0 +1,348 @@
+"""
+ typed_ast.ast3
+ ~~~
+
+ The `ast3` module helps Python applications to process trees of the Python
+ abstract syntax grammar. The abstract syntax itself might change with
+ each Python release; this module helps to find out programmatically what
+ the current grammar looks like and allows modifications of it. The
+ difference between the `ast3` module and the builtin `ast` module is
+ that `ast3` is version-independent and provides PEP 484 type comments as
+ part of the AST.
+
+ Specifically, these changes are made to the latest Python 3 AST:
+ - The `FunctionDef`, `AsyncFunctionDef`, `Assign`, `For`, `AsyncFor`,
+ `With`, `AsyncWith`, and `arg` classes all have a `type_comment` field
+ which contains a `str` with the text of the associated type comment, if
+ any.
+ - `parse` has been augmented so it can parse function signature types when
+ called with `mode=func_type`.
+ - `parse` has an additional argument `feature_version`, which disables
+ newer Python syntax features.
+ - `Module` has a `type_ignores` field which contains a list of
+ lines which have been `# type: ignore`d.
+ - `Str` has a `kind` string field which preserves the original string
+ prefix, so that `ast3.parse('u"test"').body[0].value.kind == 'u'`.
+
+ An abstract syntax tree can be generated by using the `parse()`
+ function from this module. The result will be a tree of objects whose
+ classes all inherit from `ast3.AST`.
+
+ Additionally various helper functions are provided that make working with
+ the trees simpler. The main intention of the helper functions and this
+ module in general is to provide an easy to use interface for libraries
+ that work tightly with the python syntax (template engines for example).
+
+
+ :copyright: Copyright 2008 by Armin Ronacher.
+ :license: Python License.
+"""
+from typed_ast import _ast3
+from typed_ast._ast3 import *
+
+LATEST_MINOR_VERSION = 7
+
+def parse(source, filename='<unknown>', mode='exec', feature_version=LATEST_MINOR_VERSION):
+ """
+ Parse the source into an AST node including type comments.
+ Similar to compile(source, filename, mode, PyCF_ONLY_AST).
+
+ Set feature_version to limit the syntax parsed to that minor version of
+ Python 3. For example, feature_version=5 will prevent new syntax features
+ from Python 3.6+ from being used, such as fstrings. Currently only
+ fully supported for Python 3.5+ with partial support for Python 3.4.
+ So, feature_version=3 or less are all equivalent to feature_version=4.
+
+ When feature_version=4, the parser will forbid the use of the async/await
+ keywords and the '@' operator, but will not forbid the use of PEP 448
+ additional unpacking generalizations, which were also added in Python 3.5.
+
+ When feature_version>=7, 'async' and 'await' are always keywords.
+ """
+ return _ast3._parse(source, filename, mode, feature_version)
+
+_NUM_TYPES = (int, float, complex)
+
+def literal_eval(node_or_string):
+ """
+ Safely evaluate an expression node or a string containing a Python
+ expression. The string or node provided may only consist of the following
+ Python literal structures: strings, bytes, numbers, tuples, lists, dicts,
+ sets, booleans, and None.
+ """
+ if isinstance(node_or_string, str):
+ node_or_string = parse(node_or_string, mode='eval')
+ if isinstance(node_or_string, Expression):
+ node_or_string = node_or_string.body
+ def _convert(node):
+ if isinstance(node, Constant):
+ return node.value
+ elif isinstance(node, (Str, Bytes)):
+ return node.s
+ elif isinstance(node, Num):
+ return node.n
+ elif isinstance(node, Tuple):
+ return tuple(map(_convert, node.elts))
+ elif isinstance(node, List):
+ return list(map(_convert, node.elts))
+ elif isinstance(node, Set):
+ return set(map(_convert, node.elts))
+ elif isinstance(node, Dict):
+ return dict((_convert(k), _convert(v)) for k, v
+ in zip(node.keys, node.values))
+ elif isinstance(node, NameConstant):
+ return node.value
+ elif isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
+ operand = _convert(node.operand)
+ if isinstance(operand, _NUM_TYPES):
+ if isinstance(node.op, UAdd):
+ return + operand
+ else:
+ return - operand
+ elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
+ left = _convert(node.left)
+ right = _convert(node.right)
+ if isinstance(left, _NUM_TYPES) and isinstance(right, _NUM_TYPES):
+ if isinstance(node.op, Add):
+ return left + right
+ else:
+ return left - right
+ raise ValueError('malformed node or string: ' + repr(node))
+ return _convert(node_or_string)
+
+
+def dump(node, annotate_fields=True, include_attributes=False):
+ """
+ Return a formatted dump of the tree in *node*. This is mainly useful for
+ debugging purposes. The returned string will show the names and the values
+ for fields. This makes the code impossible to evaluate, so if evaluation is
+ wanted *annotate_fields* must be set to False. Attributes such as line
+ numbers and column offsets are not dumped by default. If this is wanted,
+ *include_attributes* can be set to True.
+ """
+ def _format(node):
+ if isinstance(node, AST):
+ fields = [(a, _format(b)) for a, b in iter_fields(node)]
+ rv = '%s(%s' % (node.__class__.__name__, ', '.join(
+ ('%s=%s' % field for field in fields)
+ if annotate_fields else
+ (b for a, b in fields)
+ ))
+ if include_attributes and node._attributes:
+ rv += fields and ', ' or ' '
+ rv += ', '.join('%s=%s' % (a, _format(getattr(node, a)))
+ for a in node._attributes)
+ return rv + ')'
+ elif isinstance(node, list):
+ return '[%s]' % ', '.join(_format(x) for x in node)
+ return repr(node)
+ if not isinstance(node, AST):
+ raise TypeError('expected AST, got %r' % node.__class__.__name__)
+ return _format(node)
+
+
+def copy_location(new_node, old_node):
+ """
+ Copy source location (`lineno` and `col_offset` attributes) from
+ *old_node* to *new_node* if possible, and return *new_node*.
+ """
+ for attr in 'lineno', 'col_offset':
+ if attr in old_node._attributes and attr in new_node._attributes \
+ and hasattr(old_node, attr):
+ setattr(new_node, attr, getattr(old_node, attr))
+ return new_node
+
+
+def fix_missing_locations(node):
+ """
+ When you compile a node tree with compile(), the compiler expects lineno and
+ col_offset attributes for every node that supports them. This is rather
+ tedious to fill in for generated nodes, so this helper adds these attributes
+ recursively where not already set, by setting them to the values of the
+ parent node. It works recursively starting at *node*.
+ """
+ def _fix(node, lineno, col_offset):
+ if 'lineno' in node._attributes:
+ if not hasattr(node, 'lineno'):
+ node.lineno = lineno
+ else:
+ lineno = node.lineno
+ if 'col_offset' in node._attributes:
+ if not hasattr(node, 'col_offset'):
+ node.col_offset = col_offset
+ else:
+ col_offset = node.col_offset
+ for child in iter_child_nodes(node):
+ _fix(child, lineno, col_offset)
+ _fix(node, 1, 0)
+ return node
+
+
+def increment_lineno(node, n=1):
+ """
+ Increment the line number of each node in the tree starting at *node* by *n*.
+ This is useful to "move code" to a different location in a file.
+ """
+ for child in walk(node):
+ if 'lineno' in child._attributes:
+ child.lineno = getattr(child, 'lineno', 0) + n
+ return node
+
+
+def iter_fields(node):
+ """
+ Yield a tuple of ``(fieldname, value)`` for each field in ``node._fields``
+ that is present on *node*.
+ """
+ for field in node._fields:
+ try:
+ yield field, getattr(node, field)
+ except AttributeError:
+ pass
+
+
+def iter_child_nodes(node):
+ """
+ Yield all direct child nodes of *node*, that is, all fields that are nodes
+ and all items of fields that are lists of nodes.
+ """
+ for name, field in iter_fields(node):
+ if isinstance(field, AST):
+ yield field
+ elif isinstance(field, list):
+ for item in field:
+ if isinstance(item, AST):
+ yield item
+
+
+def get_docstring(node, clean=True):
+ """
+ Return the docstring for the given node or None if no docstring can
+ be found. If the node provided does not have docstrings a TypeError
+ will be raised.
+ """
+ if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
+ raise TypeError("%r can't have docstrings" % node.__class__.__name__)
+ if not(node.body and isinstance(node.body[0], Expr)):
+ return
+ node = node.body[0].value
+ if isinstance(node, Str):
+ text = node.s
+ elif isinstance(node, Constant) and isinstance(node.value, str):
+ text = node.value
+ else:
+ return
+ if clean:
+ import inspect
+ text = inspect.cleandoc(text)
+ return text
+
+
+def walk(node):
+ """
+ Recursively yield all descendant nodes in the tree starting at *node*
+ (including *node* itself), in no specified order. This is useful if you
+ only want to modify nodes in place and don't care about the context.
+ """
+ from collections import deque
+ todo = deque([node])
+ while todo:
+ node = todo.popleft()
+ todo.extend(iter_child_nodes(node))
+ yield node
+
+
+class NodeVisitor(object):
+ """
+ A node visitor base class that walks the abstract syntax tree and calls a
+ visitor function for every node found. This function may return a value
+ which is forwarded by the `visit` method.
+
+ This class is meant to be subclassed, with the subclass adding visitor
+ methods.
+
+ Per default the visitor functions for the nodes are ``'visit_'`` +
+ class name of the node. So a `TryFinally` node visit function would
+ be `visit_TryFinally`. This behavior can be changed by overriding
+ the `visit` method. If no visitor function exists for a node
+ (return value `None`) the `generic_visit` visitor is used instead.
+
+ Don't use the `NodeVisitor` if you want to apply changes to nodes during
+ traversing. For this a special visitor exists (`NodeTransformer`) that
+ allows modifications.
+ """
+
+ def visit(self, node):
+ """Visit a node."""
+ method = 'visit_' + node.__class__.__name__
+ visitor = getattr(self, method, self.generic_visit)
+ return visitor(node)
+
+ def generic_visit(self, node):
+ """Called if no explicit visitor function exists for a node."""
+ for field, value in iter_fields(node):
+ if isinstance(value, list):
+ for item in value:
+ if isinstance(item, AST):
+ self.visit(item)
+ elif isinstance(value, AST):
+ self.visit(value)
+
+
+class NodeTransformer(NodeVisitor):
+ """
+ A :class:`NodeVisitor` subclass that walks the abstract syntax tree and
+ allows modification of nodes.
+
+ The `NodeTransformer` will walk the AST and use the return value of the
+ visitor methods to replace or remove the old node. If the return value of
+ the visitor method is ``None``, the node will be removed from its location,
+ otherwise it is replaced with the return value. The return value may be the
+ original node in which case no replacement takes place.
+
+ Here is an example transformer that rewrites all occurrences of name lookups
+ (``foo``) to ``data['foo']``::
+
+ class RewriteName(NodeTransformer):
+
+ def visit_Name(self, node):
+ return copy_location(Subscript(
+ value=Name(id='data', ctx=Load()),
+ slice=Index(value=Str(s=node.id, kind='')),
+ ctx=node.ctx
+ ), node)
+
+ Keep in mind that if the node you're operating on has child nodes you must
+ either transform the child nodes yourself or call the :meth:`generic_visit`
+ method for the node first.
+
+ For nodes that were part of a collection of statements (that applies to all
+ statement nodes), the visitor may also return a list of nodes rather than
+ just a single node.
+
+ Usually you use the transformer like this::
+
+ node = YourTransformer().visit(node)
+ """
+
+ def generic_visit(self, node):
+ for field, old_value in iter_fields(node):
+ if isinstance(old_value, list):
+ new_values = []
+ for value in old_value:
+ if isinstance(value, AST):
+ value = self.visit(value)
+ if value is None:
+ continue
+ elif not isinstance(value, AST):
+ new_values.extend(value)
+ continue
+ new_values.append(value)
+ old_value[:] = new_values
+ elif isinstance(old_value, AST):
+ new_node = self.visit(old_value)
+ if new_node is None:
+ delattr(node, field)
+ else:
+ setattr(node, field, new_node)
+ return node
diff --git a/venv/Lib/site-packages/typed_ast/conversions.py b/venv/Lib/site-packages/typed_ast/conversions.py
new file mode 100644
index 0000000..b6862dc
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/conversions.py
@@ -0,0 +1,232 @@
+from typed_ast import ast27
+from typed_ast import ast3
+
+def py2to3(ast):
+ """Converts a typed Python 2.7 ast to a typed Python 3.5 ast. The returned
+ ast is a valid Python 3 ast with two exceptions:
+
+ - `arg` objects may contain Tuple objects instead of just identifiers
+ in the case of Python 2 function definitions/lambdas that use the tuple
+ unpacking syntax.
+ - `Raise` objects will have a `traceback` attribute added if the 3
+ argument version of the Python 2 raise is used.
+
+
+ Strange and Rare Uncovered Edge Cases:
+ - Raise: if the second argument to a raise statement is a tuple, its
+ contents are unpacked as arguments to the exception constructor. This
+ case is handled correctly if it's a literal tuple, but not if it's any
+ other sort of tuple expression.
+ """
+ return _AST2To3().visit(ast)
+
+def _copy_attributes(new_value, old_value):
+ attrs = getattr(old_value, '_attributes', None)
+ if attrs is not None:
+ for attr in attrs:
+ setattr(new_value, attr, getattr(old_value, attr))
+ return new_value
+
+class _AST2To3(ast27.NodeTransformer):
+ # note: None, True, and False are *not* translated into NameConstants.
+ def __init__(self):
+ pass
+
+ def visit(self, node):
+ """Visit a node."""
+ method = 'visit_' + node.__class__.__name__
+ visitor = getattr(self, method, self.generic_visit)
+ ret = _copy_attributes(visitor(node), node)
+ return ret
+
+ def maybe_visit(self, node):
+ if node is not None:
+ return self.visit(node)
+ else:
+ return None
+
+ def generic_visit(self, node):
+ class_name = node.__class__.__name__
+ converted_class = getattr(ast3, class_name)
+ new_node = converted_class()
+ for field, old_value in ast27.iter_fields(node):
+ if isinstance(old_value, (ast27.AST, list)):
+ setattr(new_node, field, self.visit(old_value))
+ else:
+ setattr(new_node, field, old_value)
+ return new_node
+
+
+ def visit_list(self, l):
+ return [self.visit(e) if isinstance(e, (ast27.AST, list)) else e for e in l]
+
+ def visit_FunctionDef(self, n):
+ new = self.generic_visit(n)
+ new.returns = None
+ return new
+
+ def visit_ClassDef(self, n):
+ new = self.generic_visit(n)
+ new.keywords = []
+ return new
+
+ def visit_TryExcept(self, n):
+ return ast3.Try(self.visit(n.body),
+ self.visit(n.handlers),
+ self.visit(n.orelse),
+ [])
+
+ def visit_TryFinally(self, n):
+ if len(n.body) == 1 and isinstance(n.body[0], ast27.TryExcept):
+ new = self.visit(n.body[0])
+ new.finalbody = self.visit(n.finalbody)
+ return new
+ else:
+ return ast3.Try(self.visit(n.body),
+ [],
+ [],
+ self.visit(n.finalbody))
+
+
+ def visit_ExceptHandler(self, n):
+ if n.name is None:
+ name = None
+ elif isinstance(n.name, ast27.Name):
+ name = n.name.id
+ else:
+ raise RuntimeError("'{}' has non-Name name.".format(ast27.dump(n)))
+
+ return ast3.ExceptHandler(self.maybe_visit(n.type),
+ name,
+ self.visit(n.body))
+
+ def visit_Print(self, n):
+ keywords = []
+ if n.dest is not None:
+ keywords.append(ast3.keyword("file", self.visit(n.dest)))
+
+ if not n.nl:
+ keywords.append(ast3.keyword("end",
+ ast3.Str(s=" ", kind='', lineno=n.lineno, col_offset=-1)))
+
+ return ast3.Expr(ast3.Call(ast3.Name("print", ast3.Load(), lineno=n.lineno, col_offset=-1),
+ self.visit(n.values),
+ keywords,
+ lineno=n.lineno, col_offset=-1))
+
+ def visit_Raise(self, n):
+ e = None
+ if n.type is not None:
+ e = self.visit(n.type)
+
+ if n.inst is not None and not (isinstance(n.inst, ast27.Name) and n.inst.id == "None"):
+ inst = self.visit(n.inst)
+ if isinstance(inst, ast3.Tuple):
+ args = inst.elts
+ else:
+ args = [inst]
+ e = ast3.Call(e, args, [], lineno=e.lineno, col_offset=-1)
+
+ ret = ast3.Raise(e, None)
+ if n.tback is not None:
+ ret.traceback = self.visit(n.tback)
+ return ret
+
+ def visit_Exec(self, n):
+ new_globals = self.maybe_visit(n.globals)
+ if new_globals is None:
+ new_globals = ast3.Name("None", ast3.Load(), lineno=-1, col_offset=-1)
+ new_locals = self.maybe_visit(n.locals)
+ if new_locals is None:
+ new_locals = ast3.Name("None", ast3.Load(), lineno=-1, col_offset=-1)
+
+ return ast3.Expr(ast3.Call(ast3.Name("exec", ast3.Load(), lineno=n.lineno, col_offset=-1),
+ [self.visit(n.body), new_globals, new_locals],
+ [],
+ lineno=n.lineno, col_offset=-1))
+
+ # TODO(ddfisher): the name repr could be used locally as something else; disambiguate
+ def visit_Repr(self, n):
+ return ast3.Call(ast3.Name("repr", ast3.Load(), lineno=n.lineno, col_offset=-1),
+ [self.visit(n.value)],
+ [])
+
+ # TODO(ddfisher): this will cause strange behavior on multi-item with statements with type comments
+ def visit_With(self, n):
+ return ast3.With([ast3.withitem(self.visit(n.context_expr), self.maybe_visit(n.optional_vars))],
+ self.visit(n.body),
+ n.type_comment)
+
+ def visit_Call(self, n):
+ args = self.visit(n.args)
+ if n.starargs is not None:
+ args.append(ast3.Starred(self.visit(n.starargs), ast3.Load(), lineno=n.starargs.lineno, col_offset=n.starargs.col_offset))
+
+ keywords = self.visit(n.keywords)
+ if n.kwargs is not None:
+ keywords.append(ast3.keyword(None, self.visit(n.kwargs)))
+
+ return ast3.Call(self.visit(n.func),
+ args,
+ keywords)
+
+ # TODO(ddfisher): find better attributes to give Ellipses
+ def visit_Ellipsis(self, n):
+ # ellipses in Python 2 only exist as a slice index
+ return ast3.Index(ast3.Ellipsis(lineno=-1, col_offset=-1))
+
+ def visit_arguments(self, n):
+ def convert_arg(arg, type_comment):
+ if isinstance(arg, ast27.Name):
+ v = arg.id
+ elif isinstance(arg, ast27.Tuple):
+ v = self.visit(arg)
+ else:
+ raise RuntimeError("'{}' is not a valid argument.".format(ast27.dump(arg)))
+ return ast3.arg(v, None, type_comment, lineno=arg.lineno, col_offset=arg.col_offset)
+
+ def get_type_comment(i):
+ if i < len(n.type_comments) and n.type_comments[i] is not None:
+ return n.type_comments[i]
+ return None
+
+ args = [convert_arg(arg, get_type_comment(i)) for i, arg in enumerate(n.args)]
+
+ vararg = None
+ if n.vararg is not None:
+ vararg = ast3.arg(n.vararg,
+ None,
+ get_type_comment(len(args)),
+ lineno=-1, col_offset=-1)
+
+ kwarg = None
+ if n.kwarg is not None:
+ kwarg = ast3.arg(n.kwarg,
+ None,
+ get_type_comment(len(args) + (0 if n.vararg is None else 1)),
+ lineno=-1, col_offset=-1)
+
+ defaults = self.visit(n.defaults)
+
+ return ast3.arguments(args,
+ vararg,
+ [],
+ [],
+ kwarg,
+ defaults)
+
+ def visit_Str(self, s):
+ if isinstance(s.s, bytes):
+ return ast3.Bytes(s.s, s.kind)
+ else:
+ return ast3.Str(s.s, s.kind)
+
+ def visit_Num(self, n):
+ new = self.generic_visit(n)
+ if new.n < 0:
+ # Python 3 uses a unary - operator for negative literals.
+ new.n = -new.n
+ return ast3.UnaryOp(op=ast3.USub(),
+ operand=_copy_attributes(new, n))
+ else:
+ return new
diff --git a/venv/Lib/site-packages/typed_ast/tests/__pycache__/test_basics.cpython-37.pyc b/venv/Lib/site-packages/typed_ast/tests/__pycache__/test_basics.cpython-37.pyc
new file mode 100644
index 0000000..434e554
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/tests/__pycache__/test_basics.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/typed_ast/tests/test_basics.py b/venv/Lib/site-packages/typed_ast/tests/test_basics.py
new file mode 100644
index 0000000..aa240ec
--- /dev/null
+++ b/venv/Lib/site-packages/typed_ast/tests/test_basics.py
@@ -0,0 +1,326 @@
+import os
+
+import pytest
+
+from typed_ast import _ast3
+from typed_ast import _ast27
+import typed_ast.conversions
+
+# Lowest and highest supported Python 3 minor version (inclusive)
+MIN_VER = 4
+MAX_VER = 7
+NEXT_VER = MAX_VER + 1
+
+
+basics = """\
+def foo():
+ # type: () -> int
+ pass
+
+def bar(): # type: () -> None
+ pass
+"""
+def test_basics():
+ for version in range(MIN_VER, NEXT_VER):
+ tree = _ast3._parse(basics, "<basics>", "exec", version)
+ assert tree.body[0].type_comment == "() -> int"
+ assert tree.body[1].type_comment == "() -> None"
+
+
+redundantdef = """\
+def foo(): # type: () -> int
+ # type: () -> str
+ return ''
+"""
+def test_redundantdef():
+ for version in range(MIN_VER, NEXT_VER):
+ with pytest.raises(SyntaxError):
+ t = _ast3._parse(redundantdef, "<redundantdef>", "exec", version)
+
+
+vardecl = """\
+a = 0 # type: int
+a # type: int
+"""
+def test_vardecl():
+ for version in range(MIN_VER, NEXT_VER):
+ tree = _ast3._parse(vardecl, "<vardecl>", "exec", version)
+ assert tree.body[0].type_comment == "int"
+ # Curious fact: an expression can have a type comment
+ # but it is lost in the AST.
+
+
+forstmt = """\
+for a in []: # type: int
+ pass
+"""
+def test_forstmt():
+ for version in range(MIN_VER, NEXT_VER):
+ tree = _ast3._parse(forstmt, "<forstmt>", "exec", version)
+ assert tree.body[0].type_comment == "int"
+
+
+withstmt = """\
+with context(): # type: int
+ pass
+"""
+def test_withstmt():
+ for version in range(MIN_VER, NEXT_VER):
+ tree = _ast3._parse(withstmt, "<withstmt>", "exec", version)
+ assert tree.body[0].type_comment == "int"
+
+
+# A test function named 'fabvk' would have two positional args, a and b,
+# plus a var-arg *v, plus a kw-arg **k. It is verified in test_longargs()
+# that it has exactly these arguments, no more, no fewer.
+longargs = """\
+def fa(
+ a = 1, # type: A
+):
+ pass
+
+def fa(
+ a = 1 # type: A
+):
+ pass
+
+def fab(
+ a, # type: A
+ b, # type: B
+):
+ pass
+
+def fab(
+ a, # type: A
+ b # type: B
+):
+ pass
+
+def fv(
+ *v, # type: V
+):
+ pass
+
+def fv(
+ *v # type: V
+):
+ pass
+
+def fk(
+ **k, # type: K
+):
+ pass
+
+def fk(
+ **k # type: K
+):
+ pass
+
+def fvk(
+ *v, # type: V
+ **k, # type: K
+):
+ pass
+
+def fvk(
+ *v, # type: V
+ **k # type: K
+):
+ pass
+
+def fav(
+ a, # type: A
+ *v, # type: V
+):
+ pass
+
+def fav(
+ a, # type: A
+ *v # type: V
+):
+ pass
+
+def fak(
+ a, # type: A
+ **k, # type: K
+):
+ pass
+
+def fak(
+ a, # type: A
+ **k # type: K
+):
+ pass
+
+def favk(
+ a, # type: A
+ *v, # type: V
+ **k, # type: K
+):
+ pass
+
+def favk(
+ a, # type: A
+ *v, # type: V
+ **k # type: K
+):
+ pass
+
+"""
+def test_longargs():
+ for version in range(MIN_VER, NEXT_VER):
+ tree = _ast3._parse(longargs, "<longargs>", "exec", version)
+ for t in tree.body:
+ # The expected args are encoded in the function name
+ todo = set(t.name[1:])
+ assert len(t.args.args) == len(todo) - bool(t.args.vararg) - bool(t.args.kwarg)
+ assert t.name.startswith('f')
+ for c in t.name[1:]:
+ todo.remove(c)
+ if c == 'v':
+ arg = t.args.vararg
+ elif c == 'k':
+ arg = t.args.kwarg
+ else:
+ assert 0 <= ord(c) - ord('a') < len(t.args.args)
+ arg = t.args.args[ord(c) - ord('a')]
+ assert arg.arg == c # That's the argument name
+ assert arg.type_comment == arg.arg.upper()
+ assert not todo
+
+
+ignores = """\
+def foo():
+ pass # type: ignore
+
+def bar():
+ x = 1 # type: ignore
+
+def baz():
+ pass # type: ignore[excuse]
+ pass # type: ignore=excuse
+ pass # type: ignore [excuse]
+ x = 1 # type: ignore whatever
+"""
+def test_ignores():
+ expected = [
+ (2, ''),
+ (5, ''),
+ (8, '[excuse]'),
+ (9, '=excuse'),
+ (10, ' [excuse]'),
+ (11, ' whatever'),
+ ]
+
+ for version in range(MIN_VER, NEXT_VER):
+ tree = _ast3._parse(ignores, "<ignores>", "exec", version)
+ assert [(ti.lineno, ti.tag) for ti in tree.type_ignores] == expected
+ with pytest.raises(SyntaxError):
+ _ast3._parse("pass # type: ignoreé\n", "<ignores>", "exec", version)
+
+
+ tree = _ast27.parse(ignores, "<ignores>", "exec")
+ assert [(ti.lineno, ti.tag) for ti in tree.type_ignores] == expected
+ with pytest.raises(SyntaxError):
+ _ast27.parse("pass # type: ignoreé\n", "<ignores>", "exec")
+
+
+
+asyncfunc = """\
+async def foo():
+ # type: () -> int
+ return await bar()
+"""
+def test_asyncfunc():
+ for version in range(3, 5):
+ with pytest.raises(SyntaxError):
+ _ast3._parse(asyncfunc, "<asyncfunc>", "exec", version)
+ for version in range(5, NEXT_VER):
+ tree = _ast3._parse(asyncfunc, "<asyncfunc>", "exec", version)
+ assert tree.body[0].type_comment == "() -> int"
+
+
+asyncvar = """\
+async = 12
+await = 13
+"""
+def test_asyncvar():
+ for version in range(3, 7):
+ tree = _ast3._parse(asyncvar, "<asyncvar>", "exec", version)
+ for version in range(7, NEXT_VER):
+ with pytest.raises(SyntaxError):
+ _ast3._parse(asyncvar, "<asyncvar>", "exec", version)
+
+
+asynccomp = """\
+async def foo(xs):
+ [x async for x in xs]
+"""
+def test_asynccomp():
+ for version in range(3, 6):
+ with pytest.raises(SyntaxError):
+ tree = _ast3._parse(asynccomp, "<asynccomp>", "exec", version)
+ for version in range(6, NEXT_VER):
+ _ast3._parse(asynccomp, "<asynccomp>", "exec", version)
+
+
+matmul = """\
+a = b @ c
+"""
+def test_matmul():
+ for version in range(3, 5):
+ with pytest.raises(SyntaxError):
+ tree = _ast3._parse(matmul, "<matmul>", "exec", version)
+ for version in range(5, NEXT_VER):
+ tree = _ast3._parse(matmul, "<matmul>", "exec", version)
+
+
+strkind = """\
+plain = 'abc'
+raw = r'abc'
+plain_bytes = b'abc'
+raw_bytes = br'abc'
+"""
+def test_strkind():
+ # Test that Str() objects have a kind argument/attribute.
+ node = _ast3.Str("foo", "r")
+ assert node.s == "foo"
+ assert node.kind == "r"
+ for version in range(MIN_VER, NEXT_VER):
+ tree = _ast3._parse(strkind, "<strkind>", "exec", version)
+ assert tree.body[0].value.kind == ""
+ assert tree.body[1].value.kind == "r"
+ assert tree.body[2].value.kind == "b"
+ assert tree.body[3].value.kind == "br"
+
+
+basic_py2 = """\
+a = 'hello'
+b = u'hello'
+c = b'hello'
+"""
+def test_convert_strs():
+ ast = _ast27.parse(basic_py2, "<basic_py2>", "exec")
+ tree = typed_ast.conversions.py2to3(ast)
+ assert tree.body[0].value.kind == ""
+ assert tree.body[1].value.kind == "u"
+ assert tree.body[2].value.kind == "b"
+
+simple_fstring = """\
+f'{5}'
+"""
+def test_simple_fstring():
+ for version in range(6, NEXT_VER):
+ tree = _ast3._parse(simple_fstring, "<fstring>", "exec", version)
+ assert isinstance(tree.body[0].value, _ast3.JoinedStr)
+ assert isinstance(tree.body[0].value.values[0].value, _ast3.Num)
+
+# Test the interaction between versions and f strings
+await_fstring = """\
+f'1 + {f"{await}"}'
+"""
+def test_await_fstring():
+ # Should work on 6 but fail on 7
+ _ast3._parse(await_fstring, "<bad-f-string>", "exec", 6)
+ with pytest.raises(SyntaxError):
+ _ast3._parse(await_fstring, "<bad-f-string>", "exec", 7)
diff --git a/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/PKG-INFO b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/PKG-INFO
new file mode 100644
index 0000000..7e42ef0
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/PKG-INFO
@@ -0,0 +1,166 @@
+Metadata-Version: 1.1
+Name: wrapt
+Version: 1.11.2
+Summary: Module for decorators, wrappers and monkey patching.
+Home-page: https://github.com/GrahamDumpleton/wrapt
+Author: Graham Dumpleton
+Author-email: Graham.Dumpleton@gmail.com
+License: BSD
+Description: wrapt
+ =====
+
+ |Travis| |AppVeyor| |Coveralls| |PyPI|
+
+ The aim of the **wrapt** module is to provide a transparent object proxy
+ for Python, which can be used as the basis for the construction of function
+ wrappers and decorator functions.
+
+ The **wrapt** module focuses very much on correctness. It therefore goes
+ way beyond existing mechanisms such as ``functools.wraps()`` to ensure that
+ decorators preserve introspectability, signatures, type checking abilities
+ etc. The decorators that can be constructed using this module will work in
+ far more scenarios than typical decorators and provide more predictable and
+ consistent behaviour.
+
+ To ensure that the overhead is as minimal as possible, a C extension module
+ is used for performance critical components. An automatic fallback to a
+ pure Python implementation is also provided where a target system does not
+ have a compiler to allow the C extension to be compiled.
+
+ Documentation
+ -------------
+
+ For further information on the **wrapt** module see:
+
+ * http://wrapt.readthedocs.org/
+
+ Quick Start
+ -----------
+
+ To implement your decorator you need to first define a wrapper function.
+ This will be called each time a decorated function is called. The wrapper
+ function needs to take four positional arguments:
+
+ * ``wrapped`` - The wrapped function which in turns needs to be called by your wrapper function.
+ * ``instance`` - The object to which the wrapped function was bound when it was called.
+ * ``args`` - The list of positional arguments supplied when the decorated function was called.
+ * ``kwargs`` - The dictionary of keyword arguments supplied when the decorated function was called.
+
+ The wrapper function would do whatever it needs to, but would usually in
+ turn call the wrapped function that is passed in via the ``wrapped``
+ argument.
+
+ The decorator ``@wrapt.decorator`` then needs to be applied to the wrapper
+ function to convert it into a decorator which can in turn be applied to
+ other functions.
+
+ ::
+
+ import wrapt
+
+ @wrapt.decorator
+ def pass_through(wrapped, instance, args, kwargs):
+ return wrapped(*args, **kwargs)
+
+ @pass_through
+ def function():
+ pass
+
+ If you wish to implement a decorator which accepts arguments, then wrap the
+ definition of the decorator in a function closure. Any arguments supplied
+ to the outer function when the decorator is applied, will be available to
+ the inner wrapper when the wrapped function is called.
+
+ ::
+
+ import wrapt
+
+ def with_arguments(myarg1, myarg2):
+ @wrapt.decorator
+ def wrapper(wrapped, instance, args, kwargs):
+ return wrapped(*args, **kwargs)
+ return wrapper
+
+ @with_arguments(1, 2)
+ def function():
+ pass
+
+ When applied to a normal function or static method, the wrapper function
+ when called will be passed ``None`` as the ``instance`` argument.
+
+ When applied to an instance method, the wrapper function when called will
+ be passed the instance of the class the method is being called on as the
+ ``instance`` argument. This will be the case even when the instance method
+ was called explicitly via the class and the instance passed as the first
+ argument. That is, the instance will never be passed as part of ``args``.
+
+ When applied to a class method, the wrapper function when called will be
+ passed the class type as the ``instance`` argument.
+
+ When applied to a class, the wrapper function when called will be passed
+ ``None`` as the ``instance`` argument. The ``wrapped`` argument in this
+ case will be the class.
+
+ The above rules can be summarised with the following example.
+
+ ::
+
+ import inspect
+
+ @wrapt.decorator
+ def universal(wrapped, instance, args, kwargs):
+ if instance is None:
+ if inspect.isclass(wrapped):
+ # Decorator was applied to a class.
+ return wrapped(*args, **kwargs)
+ else:
+ # Decorator was applied to a function or staticmethod.
+ return wrapped(*args, **kwargs)
+ else:
+ if inspect.isclass(instance):
+ # Decorator was applied to a classmethod.
+ return wrapped(*args, **kwargs)
+ else:
+ # Decorator was applied to an instancemethod.
+ return wrapped(*args, **kwargs)
+
+ Using these checks it is therefore possible to create a universal decorator
+ that can be applied in all situations. It is no longer necessary to create
+ different variants of decorators for normal functions and instance methods,
+ or use additional wrappers to convert a function decorator into one that
+ will work for instance methods.
+
+ In all cases, the wrapped function passed to the wrapper function is called
+ in the same way, with ``args`` and ``kwargs`` being passed. The
+ ``instance`` argument doesn't need to be used in calling the wrapped
+ function.
+
+ Repository
+ ----------
+
+ Full source code for the **wrapt** module, including documentation files
+ and unit tests, can be obtained from github.
+
+ * https://github.com/GrahamDumpleton/wrapt
+
+ .. |Travis| image:: https://travis-ci.org/GrahamDumpleton/wrapt.svg?branch=develop
+ :target: https://travis-ci.org/GrahamDumpleton/wrapt
+ .. |Appveyor| image:: https://ci.appveyor.com/api/projects/status/32r7s2skrgm9ubva?svg=true
+ :target: https://ci.appveyor.com/project/GrahamDumpleton/wrapt/branch/develop
+ .. |Coveralls| image:: https://img.shields.io/coveralls/GrahamDumpleton/wrapt/develop.svg
+ :target: https://coveralls.io/github/GrahamDumpleton/wrapt?branch=develop
+ .. |PyPI| image:: https://img.shields.io/pypi/v/wrapt.svg
+ :target: https://pypi.python.org/pypi/wrapt
+
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
diff --git a/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/SOURCES.txt b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/SOURCES.txt
new file mode 100644
index 0000000..3bc59f3
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/SOURCES.txt
@@ -0,0 +1,10 @@
+README.rst
+setup.py
+src/wrapt/__init__.py
+src/wrapt/decorators.py
+src/wrapt/importer.py
+src/wrapt/wrappers.py
+src/wrapt.egg-info/PKG-INFO
+src/wrapt.egg-info/SOURCES.txt
+src/wrapt.egg-info/dependency_links.txt
+src/wrapt.egg-info/top_level.txt \ No newline at end of file
diff --git a/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/dependency_links.txt b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/installed-files.txt b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/installed-files.txt
new file mode 100644
index 0000000..1bc0573
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/installed-files.txt
@@ -0,0 +1,12 @@
+..\wrapt\__init__.py
+..\wrapt\__pycache__\__init__.cpython-37.pyc
+..\wrapt\__pycache__\decorators.cpython-37.pyc
+..\wrapt\__pycache__\importer.cpython-37.pyc
+..\wrapt\__pycache__\wrappers.cpython-37.pyc
+..\wrapt\decorators.py
+..\wrapt\importer.py
+..\wrapt\wrappers.py
+PKG-INFO
+SOURCES.txt
+dependency_links.txt
+top_level.txt
diff --git a/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/top_level.txt b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/top_level.txt
new file mode 100644
index 0000000..ba11553
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt-1.11.2-py3.7.egg-info/top_level.txt
@@ -0,0 +1 @@
+wrapt
diff --git a/venv/Lib/site-packages/wrapt/__init__.py b/venv/Lib/site-packages/wrapt/__init__.py
new file mode 100644
index 0000000..8e858a0
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt/__init__.py
@@ -0,0 +1,16 @@
+__version_info__ = ('1', '11', '2')
+__version__ = '.'.join(__version_info__)
+
+from .wrappers import (ObjectProxy, CallableObjectProxy, FunctionWrapper,
+ BoundFunctionWrapper, WeakFunctionProxy, PartialCallableObjectProxy,
+ resolve_path, apply_patch, wrap_object, wrap_object_attribute,
+ function_wrapper, wrap_function_wrapper, patch_function_wrapper,
+ transient_function_wrapper)
+
+from .decorators import (adapter_factory, AdapterFactory, decorator,
+ synchronized)
+
+from .importer import (register_post_import_hook, when_imported,
+ notify_module_loaded, discover_post_import_hooks)
+
+from inspect import getcallargs
diff --git a/venv/Lib/site-packages/wrapt/__pycache__/__init__.cpython-37.pyc b/venv/Lib/site-packages/wrapt/__pycache__/__init__.cpython-37.pyc
new file mode 100644
index 0000000..2e30dcf
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt/__pycache__/__init__.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/wrapt/__pycache__/decorators.cpython-37.pyc b/venv/Lib/site-packages/wrapt/__pycache__/decorators.cpython-37.pyc
new file mode 100644
index 0000000..aee61e9
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt/__pycache__/decorators.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/wrapt/__pycache__/importer.cpython-37.pyc b/venv/Lib/site-packages/wrapt/__pycache__/importer.cpython-37.pyc
new file mode 100644
index 0000000..d468b2d
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt/__pycache__/importer.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/wrapt/__pycache__/wrappers.cpython-37.pyc b/venv/Lib/site-packages/wrapt/__pycache__/wrappers.cpython-37.pyc
new file mode 100644
index 0000000..c21924d
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt/__pycache__/wrappers.cpython-37.pyc
Binary files differ
diff --git a/venv/Lib/site-packages/wrapt/decorators.py b/venv/Lib/site-packages/wrapt/decorators.py
new file mode 100644
index 0000000..11e11de
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt/decorators.py
@@ -0,0 +1,514 @@
+"""This module implements decorators for implementing other decorators
+as well as some commonly used decorators.
+
+"""
+
+import sys
+
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+ string_types = str,
+
+ import builtins
+ exec_ = getattr(builtins, "exec")
+ del builtins
+
+else:
+ string_types = basestring,
+
+ def exec_(_code_, _globs_=None, _locs_=None):
+ """Execute code in a namespace."""
+ if _globs_ is None:
+ frame = sys._getframe(1)
+ _globs_ = frame.f_globals
+ if _locs_ is None:
+ _locs_ = frame.f_locals
+ del frame
+ elif _locs_ is None:
+ _locs_ = _globs_
+ exec("""exec _code_ in _globs_, _locs_""")
+
+from functools import partial
+from inspect import ismethod, isclass, formatargspec
+from collections import namedtuple
+from threading import Lock, RLock
+
+try:
+ from inspect import signature
+except ImportError:
+ pass
+
+from .wrappers import (FunctionWrapper, BoundFunctionWrapper, ObjectProxy,
+ CallableObjectProxy)
+
+# Adapter wrapper for the wrapped function which will overlay certain
+# properties from the adapter function onto the wrapped function so that
+# functions such as inspect.getargspec(), inspect.getfullargspec(),
+# inspect.signature() and inspect.getsource() return the correct results
+# one would expect.
+
+class _AdapterFunctionCode(CallableObjectProxy):
+
+ def __init__(self, wrapped_code, adapter_code):
+ super(_AdapterFunctionCode, self).__init__(wrapped_code)
+ self._self_adapter_code = adapter_code
+
+ @property
+ def co_argcount(self):
+ return self._self_adapter_code.co_argcount
+
+ @property
+ def co_code(self):
+ return self._self_adapter_code.co_code
+
+ @property
+ def co_flags(self):
+ return self._self_adapter_code.co_flags
+
+ @property
+ def co_kwonlyargcount(self):
+ return self._self_adapter_code.co_kwonlyargcount
+
+ @property
+ def co_varnames(self):
+ return self._self_adapter_code.co_varnames
+
+class _AdapterFunctionSurrogate(CallableObjectProxy):
+
+ def __init__(self, wrapped, adapter):
+ super(_AdapterFunctionSurrogate, self).__init__(wrapped)
+ self._self_adapter = adapter
+
+ @property
+ def __code__(self):
+ return _AdapterFunctionCode(self.__wrapped__.__code__,
+ self._self_adapter.__code__)
+
+ @property
+ def __defaults__(self):
+ return self._self_adapter.__defaults__
+
+ @property
+ def __kwdefaults__(self):
+ return self._self_adapter.__kwdefaults__
+
+ @property
+ def __signature__(self):
+ if 'signature' not in globals():
+ return self._self_adapter.__signature__
+ else:
+ # Can't allow this to fail on Python 3 else it falls
+ # through to using __wrapped__, but that will be the
+ # wrong function we want to derive the signature
+ # from. Thus generate the signature ourselves.
+
+ return signature(self._self_adapter)
+
+ if PY2:
+ func_code = __code__
+ func_defaults = __defaults__
+
+class _BoundAdapterWrapper(BoundFunctionWrapper):
+
+ @property
+ def __func__(self):
+ return _AdapterFunctionSurrogate(self.__wrapped__.__func__,
+ self._self_parent._self_adapter)
+
+ if PY2:
+ im_func = __func__
+
+class AdapterWrapper(FunctionWrapper):
+
+ __bound_function_wrapper__ = _BoundAdapterWrapper
+
+ def __init__(self, *args, **kwargs):
+ adapter = kwargs.pop('adapter')
+ super(AdapterWrapper, self).__init__(*args, **kwargs)
+ self._self_surrogate = _AdapterFunctionSurrogate(
+ self.__wrapped__, adapter)
+ self._self_adapter = adapter
+
+ @property
+ def __code__(self):
+ return self._self_surrogate.__code__
+
+ @property
+ def __defaults__(self):
+ return self._self_surrogate.__defaults__
+
+ @property
+ def __kwdefaults__(self):
+ return self._self_surrogate.__kwdefaults__
+
+ if PY2:
+ func_code = __code__
+ func_defaults = __defaults__
+
+ @property
+ def __signature__(self):
+ return self._self_surrogate.__signature__
+
+class AdapterFactory(object):
+ def __call__(self, wrapped):
+ raise NotImplementedError()
+
+class DelegatedAdapterFactory(AdapterFactory):
+ def __init__(self, factory):
+ super(DelegatedAdapterFactory, self).__init__()
+ self.factory = factory
+ def __call__(self, wrapped):
+ return self.factory(wrapped)
+
+adapter_factory = DelegatedAdapterFactory
+
+# Decorator for creating other decorators. This decorator and the
+# wrappers which they use are designed to properly preserve any name
+# attributes, function signatures etc, in addition to the wrappers
+# themselves acting like a transparent proxy for the original wrapped
+# function so the wrapper is effectively indistinguishable from the
+# original wrapped function.
+
+def decorator(wrapper=None, enabled=None, adapter=None):
+ # The decorator should be supplied with a single positional argument
+ # which is the wrapper function to be used to implement the
+ # decorator. This may be preceded by a step whereby the keyword
+ # arguments are supplied to customise the behaviour of the
+ # decorator. The 'adapter' argument is used to optionally denote a
+ # separate function which is notionally used by an adapter
+ # decorator. In that case parts of the function '__code__' and
+ # '__defaults__' attributes are used from the adapter function
+ # rather than those of the wrapped function. This allows for the
+ # argument specification from inspect.getargspec() and similar
+ # functions to be overridden with a prototype for a different
+ # function than what was wrapped. The 'enabled' argument provides a
+ # way to enable/disable the use of the decorator. If the type of
+ # 'enabled' is a boolean, then it is evaluated immediately and the
+ # wrapper not even applied if it is False. If not a boolean, it will
+ # be evaluated when the wrapper is called for an unbound wrapper,
+ # and when binding occurs for a bound wrapper. When being evaluated,
+ # if 'enabled' is callable it will be called to obtain the value to
+ # be checked. If False, the wrapper will not be called and instead
+ # the original wrapped function will be called directly instead.
+
+ if wrapper is not None:
+ # Helper function for creating wrapper of the appropriate
+ # time when we need it down below.
+
+ def _build(wrapped, wrapper, enabled=None, adapter=None):
+ if adapter:
+ if isinstance(adapter, AdapterFactory):
+ adapter = adapter(wrapped)
+
+ if not callable(adapter):
+ ns = {}
+ if not isinstance(adapter, string_types):
+ adapter = formatargspec(*adapter)
+ exec_('def adapter{}: pass'.format(adapter), ns, ns)
+ adapter = ns['adapter']
+
+ return AdapterWrapper(wrapped=wrapped, wrapper=wrapper,
+ enabled=enabled, adapter=adapter)
+
+ return FunctionWrapper(wrapped=wrapped, wrapper=wrapper,
+ enabled=enabled)
+
+ # The wrapper has been provided so return the final decorator.
+ # The decorator is itself one of our function wrappers so we
+ # can determine when it is applied to functions, instance methods
+ # or class methods. This allows us to bind the instance or class
+ # method so the appropriate self or cls attribute is supplied
+ # when it is finally called.
+
+ def _wrapper(wrapped, instance, args, kwargs):
+ # We first check for the case where the decorator was applied
+ # to a class type.
+ #
+ # @decorator
+ # class mydecoratorclass(object):
+ # def __init__(self, arg=None):
+ # self.arg = arg
+ # def __call__(self, wrapped, instance, args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # @mydecoratorclass(arg=1)
+ # def function():
+ # pass
+ #
+ # In this case an instance of the class is to be used as the
+ # decorator wrapper function. If args was empty at this point,
+ # then it means that there were optional keyword arguments
+ # supplied to be used when creating an instance of the class
+ # to be used as the wrapper function.
+
+ if instance is None and isclass(wrapped) and not args:
+ # We still need to be passed the target function to be
+ # wrapped as yet, so we need to return a further function
+ # to be able to capture it.
+
+ def _capture(target_wrapped):
+ # Now have the target function to be wrapped and need
+ # to create an instance of the class which is to act
+ # as the decorator wrapper function. Before we do that,
+ # we need to first check that use of the decorator
+ # hadn't been disabled by a simple boolean. If it was,
+ # the target function to be wrapped is returned instead.
+
+ _enabled = enabled
+ if type(_enabled) is bool:
+ if not _enabled:
+ return target_wrapped
+ _enabled = None
+
+ # Now create an instance of the class which is to act
+ # as the decorator wrapper function. Any arguments had
+ # to be supplied as keyword only arguments so that is
+ # all we pass when creating it.
+
+ target_wrapper = wrapped(**kwargs)
+
+ # Finally build the wrapper itself and return it.
+
+ return _build(target_wrapped, target_wrapper,
+ _enabled, adapter)
+
+ return _capture
+
+ # We should always have the target function to be wrapped at
+ # this point as the first (and only) value in args.
+
+ target_wrapped = args[0]
+
+ # Need to now check that use of the decorator hadn't been
+ # disabled by a simple boolean. If it was, then target
+ # function to be wrapped is returned instead.
+
+ _enabled = enabled
+ if type(_enabled) is bool:
+ if not _enabled:
+ return target_wrapped
+ _enabled = None
+
+ # We now need to build the wrapper, but there are a couple of
+ # different cases we need to consider.
+
+ if instance is None:
+ if isclass(wrapped):
+ # In this case the decorator was applied to a class
+ # type but optional keyword arguments were not supplied
+ # for initialising an instance of the class to be used
+ # as the decorator wrapper function.
+ #
+ # @decorator
+ # class mydecoratorclass(object):
+ # def __init__(self, arg=None):
+ # self.arg = arg
+ # def __call__(self, wrapped, instance,
+ # args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # @mydecoratorclass
+ # def function():
+ # pass
+ #
+ # We still need to create an instance of the class to
+ # be used as the decorator wrapper function, but no
+ # arguments are pass.
+
+ target_wrapper = wrapped()
+
+ else:
+ # In this case the decorator was applied to a normal
+ # function, or possibly a static method of a class.
+ #
+ # @decorator
+ # def mydecoratorfuntion(wrapped, instance,
+ # args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # @mydecoratorfunction
+ # def function():
+ # pass
+ #
+ # That normal function becomes the decorator wrapper
+ # function.
+
+ target_wrapper = wrapper
+
+ else:
+ if isclass(instance):
+ # In this case the decorator was applied to a class
+ # method.
+ #
+ # class myclass(object):
+ # @decorator
+ # @classmethod
+ # def decoratorclassmethod(cls, wrapped,
+ # instance, args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # instance = myclass()
+ #
+ # @instance.decoratorclassmethod
+ # def function():
+ # pass
+ #
+ # This one is a bit strange because binding was actually
+ # performed on the wrapper created by our decorator
+ # factory. We need to apply that binding to the decorator
+ # wrapper function which which the decorator factory
+ # was applied to.
+
+ target_wrapper = wrapper.__get__(None, instance)
+
+ else:
+ # In this case the decorator was applied to an instance
+ # method.
+ #
+ # class myclass(object):
+ # @decorator
+ # def decoratorclassmethod(self, wrapped,
+ # instance, args, kwargs):
+ # return wrapped(*args, **kwargs)
+ #
+ # instance = myclass()
+ #
+ # @instance.decoratorclassmethod
+ # def function():
+ # pass
+ #
+ # This one is a bit strange because binding was actually
+ # performed on the wrapper created by our decorator
+ # factory. We need to apply that binding to the decorator
+ # wrapper function which which the decorator factory
+ # was applied to.
+
+ target_wrapper = wrapper.__get__(instance, type(instance))
+
+ # Finally build the wrapper itself and return it.
+
+ return _build(target_wrapped, target_wrapper, _enabled, adapter)
+
+ # We first return our magic function wrapper here so we can
+ # determine in what context the decorator factory was used. In
+ # other words, it is itself a universal decorator. The decorator
+ # function is used as the adapter so that linters see a signature
+ # corresponding to the decorator and not the wrapper it is being
+ # applied to.
+
+ return _build(wrapper, _wrapper, adapter=decorator)
+
+ else:
+ # The wrapper still has not been provided, so we are just
+ # collecting the optional keyword arguments. Return the
+ # decorator again wrapped in a partial using the collected
+ # arguments.
+
+ return partial(decorator, enabled=enabled, adapter=adapter)
+
+# Decorator for implementing thread synchronization. It can be used as a
+# decorator, in which case the synchronization context is determined by
+# what type of function is wrapped, or it can also be used as a context
+# manager, where the user needs to supply the correct synchronization
+# context. It is also possible to supply an object which appears to be a
+# synchronization primitive of some sort, by virtue of having release()
+# and acquire() methods. In that case that will be used directly as the
+# synchronization primitive without creating a separate lock against the
+# derived or supplied context.
+
+def synchronized(wrapped):
+ # Determine if being passed an object which is a synchronization
+ # primitive. We can't check by type for Lock, RLock, Semaphore etc,
+ # as the means of creating them isn't the type. Therefore use the
+ # existence of acquire() and release() methods. This is more
+ # extensible anyway as it allows custom synchronization mechanisms.
+
+ if hasattr(wrapped, 'acquire') and hasattr(wrapped, 'release'):
+ # We remember what the original lock is and then return a new
+ # decorator which accesses and locks it. When returning the new
+ # decorator we wrap it with an object proxy so we can override
+ # the context manager methods in case it is being used to wrap
+ # synchronized statements with a 'with' statement.
+
+ lock = wrapped
+
+ @decorator
+ def _synchronized(wrapped, instance, args, kwargs):
+ # Execute the wrapped function while the original supplied
+ # lock is held.
+
+ with lock:
+ return wrapped(*args, **kwargs)
+
+ class _PartialDecorator(CallableObjectProxy):
+
+ def __enter__(self):
+ lock.acquire()
+ return lock
+
+ def __exit__(self, *args):
+ lock.release()
+
+ return _PartialDecorator(wrapped=_synchronized)
+
+ # Following only apply when the lock is being created automatically
+ # based on the context of what was supplied. In this case we supply
+ # a final decorator, but need to use FunctionWrapper directly as we
+ # want to derive from it to add context manager methods in case it is
+ # being used to wrap synchronized statements with a 'with' statement.
+
+ def _synchronized_lock(context):
+ # Attempt to retrieve the lock for the specific context.
+
+ lock = vars(context).get('_synchronized_lock', None)
+
+ if lock is None:
+ # There is no existing lock defined for the context we
+ # are dealing with so we need to create one. This needs
+ # to be done in a way to guarantee there is only one
+ # created, even if multiple threads try and create it at
+ # the same time. We can't always use the setdefault()
+ # method on the __dict__ for the context. This is the
+ # case where the context is a class, as __dict__ is
+ # actually a dictproxy. What we therefore do is use a
+ # meta lock on this wrapper itself, to control the
+ # creation and assignment of the lock attribute against
+ # the context.
+
+ with synchronized._synchronized_meta_lock:
+ # We need to check again for whether the lock we want
+ # exists in case two threads were trying to create it
+ # at the same time and were competing to create the
+ # meta lock.
+
+ lock = vars(context).get('_synchronized_lock', None)
+
+ if lock is None:
+ lock = RLock()
+ setattr(context, '_synchronized_lock', lock)
+
+ return lock
+
+ def _synchronized_wrapper(wrapped, instance, args, kwargs):
+ # Execute the wrapped function while the lock for the
+ # desired context is held. If instance is None then the
+ # wrapped function is used as the context.
+
+ with _synchronized_lock(instance or wrapped):
+ return wrapped(*args, **kwargs)
+
+ class _FinalDecorator(FunctionWrapper):
+
+ def __enter__(self):
+ self._self_lock = _synchronized_lock(self.__wrapped__)
+ self._self_lock.acquire()
+ return self._self_lock
+
+ def __exit__(self, *args):
+ self._self_lock.release()
+
+ return _FinalDecorator(wrapped=wrapped, wrapper=_synchronized_wrapper)
+
+synchronized._synchronized_meta_lock = Lock()
diff --git a/venv/Lib/site-packages/wrapt/importer.py b/venv/Lib/site-packages/wrapt/importer.py
new file mode 100644
index 0000000..9e617cd
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt/importer.py
@@ -0,0 +1,230 @@
+"""This module implements a post import hook mechanism styled after what is
+described in PEP-369. Note that it doesn't cope with modules being reloaded.
+
+"""
+
+import sys
+import threading
+
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+ import importlib
+ string_types = str,
+else:
+ string_types = basestring,
+
+from .decorators import synchronized
+
+# The dictionary registering any post import hooks to be triggered once
+# the target module has been imported. Once a module has been imported
+# and the hooks fired, the list of hooks recorded against the target
+# module will be truncacted but the list left in the dictionary. This
+# acts as a flag to indicate that the module had already been imported.
+
+_post_import_hooks = {}
+_post_import_hooks_init = False
+_post_import_hooks_lock = threading.RLock()
+
+# Register a new post import hook for the target module name. This
+# differs from the PEP-369 implementation in that it also allows the
+# hook function to be specified as a string consisting of the name of
+# the callback in the form 'module:function'. This will result in a
+# proxy callback being registered which will defer loading of the
+# specified module containing the callback function until required.
+
+def _create_import_hook_from_string(name):
+ def import_hook(module):
+ module_name, function = name.split(':')
+ attrs = function.split('.')
+ __import__(module_name)
+ callback = sys.modules[module_name]
+ for attr in attrs:
+ callback = getattr(callback, attr)
+ return callback(module)
+ return import_hook
+
+@synchronized(_post_import_hooks_lock)
+def register_post_import_hook(hook, name):
+ # Create a deferred import hook if hook is a string name rather than
+ # a callable function.
+
+ if isinstance(hook, string_types):
+ hook = _create_import_hook_from_string(hook)
+
+ # Automatically install the import hook finder if it has not already
+ # been installed.
+
+ global _post_import_hooks_init
+
+ if not _post_import_hooks_init:
+ _post_import_hooks_init = True
+ sys.meta_path.insert(0, ImportHookFinder())
+
+ # Determine if any prior registration of a post import hook for
+ # the target modules has occurred and act appropriately.
+
+ hooks = _post_import_hooks.get(name, None)
+
+ if hooks is None:
+ # No prior registration of post import hooks for the target
+ # module. We need to check whether the module has already been
+ # imported. If it has we fire the hook immediately and add an
+ # empty list to the registry to indicate that the module has
+ # already been imported and hooks have fired. Otherwise add
+ # the post import hook to the registry.
+
+ module = sys.modules.get(name, None)
+
+ if module is not None:
+ _post_import_hooks[name] = []
+ hook(module)
+
+ else:
+ _post_import_hooks[name] = [hook]
+
+ elif hooks == []:
+ # A prior registration of port import hooks for the target
+ # module was done and the hooks already fired. Fire the hook
+ # immediately.
+
+ module = sys.modules[name]
+ hook(module)
+
+ else:
+ # A prior registration of port import hooks for the target
+ # module was done but the module has not yet been imported.
+
+ _post_import_hooks[name].append(hook)
+
+# Register post import hooks defined as package entry points.
+
+def _create_import_hook_from_entrypoint(entrypoint):
+ def import_hook(module):
+ __import__(entrypoint.module_name)
+ callback = sys.modules[entrypoint.module_name]
+ for attr in entrypoint.attrs:
+ callback = getattr(callback, attr)
+ return callback(module)
+ return import_hook
+
+def discover_post_import_hooks(group):
+ try:
+ import pkg_resources
+ except ImportError:
+ return
+
+ for entrypoint in pkg_resources.iter_entry_points(group=group):
+ callback = _create_import_hook_from_entrypoint(entrypoint)
+ register_post_import_hook(callback, entrypoint.name)
+
+# Indicate that a module has been loaded. Any post import hooks which
+# were registered against the target module will be invoked. If an
+# exception is raised in any of the post import hooks, that will cause
+# the import of the target module to fail.
+
+@synchronized(_post_import_hooks_lock)
+def notify_module_loaded(module):
+ name = getattr(module, '__name__', None)
+ hooks = _post_import_hooks.get(name, None)
+
+ if hooks:
+ _post_import_hooks[name] = []
+
+ for hook in hooks:
+ hook(module)
+
+# A custom module import finder. This intercepts attempts to import
+# modules and watches out for attempts to import target modules of
+# interest. When a module of interest is imported, then any post import
+# hooks which are registered will be invoked.
+
+class _ImportHookLoader:
+
+ def load_module(self, fullname):
+ module = sys.modules[fullname]
+ notify_module_loaded(module)
+
+ return module
+
+class _ImportHookChainedLoader:
+
+ def __init__(self, loader):
+ self.loader = loader
+
+ def load_module(self, fullname):
+ module = self.loader.load_module(fullname)
+ notify_module_loaded(module)
+
+ return module
+
+class ImportHookFinder:
+
+ def __init__(self):
+ self.in_progress = {}
+
+ @synchronized(_post_import_hooks_lock)
+ def find_module(self, fullname, path=None):
+ # If the module being imported is not one we have registered
+ # post import hooks for, we can return immediately. We will
+ # take no further part in the importing of this module.
+
+ if not fullname in _post_import_hooks:
+ return None
+
+ # When we are interested in a specific module, we will call back
+ # into the import system a second time to defer to the import
+ # finder that is supposed to handle the importing of the module.
+ # We set an in progress flag for the target module so that on
+ # the second time through we don't trigger another call back
+ # into the import system and cause a infinite loop.
+
+ if fullname in self.in_progress:
+ return None
+
+ self.in_progress[fullname] = True
+
+ # Now call back into the import system again.
+
+ try:
+ if PY3:
+ # For Python 3 we need to use find_spec().loader
+ # from the importlib.util module. It doesn't actually
+ # import the target module and only finds the
+ # loader. If a loader is found, we need to return
+ # our own loader which will then in turn call the
+ # real loader to import the module and invoke the
+ # post import hooks.
+ try:
+ import importlib.util
+ loader = importlib.util.find_spec(fullname).loader
+ except (ImportError, AttributeError):
+ loader = importlib.find_loader(fullname, path)
+ if loader:
+ return _ImportHookChainedLoader(loader)
+
+ else:
+ # For Python 2 we don't have much choice but to
+ # call back in to __import__(). This will
+ # actually cause the module to be imported. If no
+ # module could be found then ImportError will be
+ # raised. Otherwise we return a loader which
+ # returns the already loaded module and invokes
+ # the post import hooks.
+
+ __import__(fullname)
+
+ return _ImportHookLoader()
+
+ finally:
+ del self.in_progress[fullname]
+
+# Decorator for marking that a function should be called as a post
+# import hook when the target module is imported.
+
+def when_imported(name):
+ def register(hook):
+ register_post_import_hook(hook, name)
+ return hook
+ return register
diff --git a/venv/Lib/site-packages/wrapt/wrappers.py b/venv/Lib/site-packages/wrapt/wrappers.py
new file mode 100644
index 0000000..1d6131d
--- /dev/null
+++ b/venv/Lib/site-packages/wrapt/wrappers.py
@@ -0,0 +1,943 @@
+import os
+import sys
+import functools
+import operator
+import weakref
+import inspect
+
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+ string_types = str,
+else:
+ string_types = basestring,
+
+def with_metaclass(meta, *bases):
+ """Create a base class with a metaclass."""
+ return meta("NewBase", bases, {})
+
+class _ObjectProxyMethods(object):
+
+ # We use properties to override the values of __module__ and
+ # __doc__. If we add these in ObjectProxy, the derived class
+ # __dict__ will still be setup to have string variants of these
+ # attributes and the rules of descriptors means that they appear to
+ # take precedence over the properties in the base class. To avoid
+ # that, we copy the properties into the derived class type itself
+ # via a meta class. In that way the properties will always take
+ # precedence.
+
+ @property
+ def __module__(self):
+ return self.__wrapped__.__module__
+
+ @__module__.setter
+ def __module__(self, value):
+ self.__wrapped__.__module__ = value
+
+ @property
+ def __doc__(self):
+ return self.__wrapped__.__doc__
+
+ @__doc__.setter
+ def __doc__(self, value):
+ self.__wrapped__.__doc__ = value
+
+ # We similar use a property for __dict__. We need __dict__ to be
+ # explicit to ensure that vars() works as expected.
+
+ @property
+ def __dict__(self):
+ return self.__wrapped__.__dict__
+
+ # Need to also propagate the special __weakref__ attribute for case
+ # where decorating classes which will define this. If do not define
+ # it and use a function like inspect.getmembers() on a decorator
+ # class it will fail. This can't be in the derived classes.
+
+ @property
+ def __weakref__(self):
+ return self.__wrapped__.__weakref__
+
+class _ObjectProxyMetaType(type):
+ def __new__(cls, name, bases, dictionary):
+ # Copy our special properties into the class so that they
+ # always take precedence over attributes of the same name added
+ # during construction of a derived class. This is to save
+ # duplicating the implementation for them in all derived classes.
+
+ dictionary.update(vars(_ObjectProxyMethods))
+
+ return type.__new__(cls, name, bases, dictionary)
+
+class ObjectProxy(with_metaclass(_ObjectProxyMetaType)):
+
+ __slots__ = '__wrapped__'
+
+ def __init__(self, wrapped):
+ object.__setattr__(self, '__wrapped__', wrapped)
+
+ # Python 3.2+ has the __qualname__ attribute, but it does not
+ # allow it to be overridden using a property and it must instead
+ # be an actual string object instead.
+
+ try:
+ object.__setattr__(self, '__qualname__', wrapped.__qualname__)
+ except AttributeError:
+ pass
+
+ @property
+ def __name__(self):
+ return self.__wrapped__.__name__
+
+ @__name__.setter
+ def __name__(self, value):
+ self.__wrapped__.__name__ = value
+
+ @property
+ def __class__(self):
+ return self.__wrapped__.__class__
+
+ @__class__.setter
+ def __class__(self, value):
+ self.__wrapped__.__class__ = value
+
+ @property
+ def __annotations__(self):
+ return self.__wrapped__.__annotations__
+
+ @__annotations__.setter
+ def __annotations__(self, value):
+ self.__wrapped__.__annotations__ = value
+
+ def __dir__(self):
+ return dir(self.__wrapped__)
+
+ def __str__(self):
+ return str(self.__wrapped__)
+
+ if PY3:
+ def __bytes__(self):
+ return bytes(self.__wrapped__)
+
+ def __repr__(self):
+ return '<{} at 0x{:x} for {} at 0x{:x}>'.format(
+ type(self).__name__, id(self),
+ type(self.__wrapped__).__name__,
+ id(self.__wrapped__))
+
+ def __reversed__(self):
+ return reversed(self.__wrapped__)
+
+ if PY3:
+ def __round__(self):
+ return round(self.__wrapped__)
+
+ def __lt__(self, other):
+ return self.__wrapped__ < other
+
+ def __le__(self, other):
+ return self.__wrapped__ <= other
+
+ def __eq__(self, other):
+ return self.__wrapped__ == other
+
+ def __ne__(self, other):
+ return self.__wrapped__ != other
+
+ def __gt__(self, other):
+ return self.__wrapped__ > other
+
+ def __ge__(self, other):
+ return self.__wrapped__ >= other
+
+ def __hash__(self):
+ return hash(self.__wrapped__)
+
+ def __nonzero__(self):
+ return bool(self.__wrapped__)
+
+ def __bool__(self):
+ return bool(self.__wrapped__)
+
+ def __setattr__(self, name, value):
+ if name.startswith('_self_'):
+ object.__setattr__(self, name, value)
+
+ elif name == '__wrapped__':
+ object.__setattr__(self, name, value)
+ try:
+ object.__delattr__(self, '__qualname__')
+ except AttributeError:
+ pass
+ try:
+ object.__setattr__(self, '__qualname__', value.__qualname__)
+ except AttributeError:
+ pass
+
+ elif name == '__qualname__':
+ setattr(self.__wrapped__, name, value)
+ object.__setattr__(self, name, value)
+
+ elif hasattr(type(self), name):
+ object.__setattr__(self, name, value)
+
+ else:
+ setattr(self.__wrapped__, name, value)
+
+ def __getattr__(self, name):
+ # If we are being to lookup '__wrapped__' then the
+ # '__init__()' method cannot have been called.
+
+ if name == '__wrapped__':
+ raise ValueError('wrapper has not been initialised')
+
+ return getattr(self.__wrapped__, name)
+
+ def __delattr__(self, name):
+ if name.startswith('_self_'):
+ object.__delattr__(self, name)
+
+ elif name == '__wrapped__':
+ raise TypeError('__wrapped__ must be an object')
+
+ elif name == '__qualname__':
+ object.__delattr__(self, name)
+ delattr(self.__wrapped__, name)
+
+ elif hasattr(type(self), name):
+ object.__delattr__(self, name)
+
+ else:
+ delattr(self.__wrapped__, name)
+
+ def __add__(self, other):
+ return self.__wrapped__ + other
+
+ def __sub__(self, other):
+ return self.__wrapped__ - other
+
+ def __mul__(self, other):
+ return self.__wrapped__ * other
+
+ def __div__(self, other):
+ return operator.div(self.__wrapped__, other)
+
+ def __truediv__(self, other):
+ return operator.truediv(self.__wrapped__, other)
+
+ def __floordiv__(self, other):
+ return self.__wrapped__ // other
+
+ def __mod__(self, other):
+ return self.__wrapped__ % other
+
+ def __divmod__(self, other):
+ return divmod(self.__wrapped__, other)
+
+ def __pow__(self, other, *args):
+ return pow(self.__wrapped__, other, *args)
+
+ def __lshift__(self, other):
+ return self.__wrapped__ << other
+
+ def __rshift__(self, other):
+ return self.__wrapped__ >> other
+
+ def __and__(self, other):
+ return self.__wrapped__ & other
+
+ def __xor__(self, other):
+ return self.__wrapped__ ^ other
+
+ def __or__(self, other):
+ return self.__wrapped__ | other
+
+ def __radd__(self, other):
+ return other + self.__wrapped__
+
+ def __rsub__(self, other):
+ return other - self.__wrapped__
+
+ def __rmul__(self, other):
+ return other * self.__wrapped__
+
+ def __rdiv__(self, other):
+ return operator.div(other, self.__wrapped__)
+
+ def __rtruediv__(self, other):
+ return operator.truediv(other, self.__wrapped__)
+
+ def __rfloordiv__(self, other):
+ return other // self.__wrapped__
+
+ def __rmod__(self, other):
+ return other % self.__wrapped__
+
+ def __rdivmod__(self, other):
+ return divmod(other, self.__wrapped__)
+
+ def __rpow__(self, other, *args):
+ return pow(other, self.__wrapped__, *args)
+
+ def __rlshift__(self, other):
+ return other << self.__wrapped__
+
+ def __rrshift__(self, other):
+ return other >> self.__wrapped__
+
+ def __rand__(self, other):
+ return other & self.__wrapped__
+
+ def __rxor__(self, other):
+ return other ^ self.__wrapped__
+
+ def __ror__(self, other):
+ return other | self.__wrapped__
+
+ def __iadd__(self, other):
+ self.__wrapped__ += other
+ return self
+
+ def __isub__(self, other):
+ self.__wrapped__ -= other
+ return self
+
+ def __imul__(self, other):
+ self.__wrapped__ *= other
+ return self
+
+ def __idiv__(self, other):
+ self.__wrapped__ = operator.idiv(self.__wrapped__, other)
+ return self
+
+ def __itruediv__(self, other):
+ self.__wrapped__ = operator.itruediv(self.__wrapped__, other)
+ return self
+
+ def __ifloordiv__(self, other):
+ self.__wrapped__ //= other
+ return self
+
+ def __imod__(self, other):
+ self.__wrapped__ %= other
+ return self
+
+ def __ipow__(self, other):
+ self.__wrapped__ **= other
+ return self
+
+ def __ilshift__(self, other):
+ self.__wrapped__ <<= other
+ return self
+
+ def __irshift__(self, other):
+ self.__wrapped__ >>= other
+ return self
+
+ def __iand__(self, other):
+ self.__wrapped__ &= other
+ return self
+
+ def __ixor__(self, other):
+ self.__wrapped__ ^= other
+ return self
+
+ def __ior__(self, other):
+ self.__wrapped__ |= other
+ return self
+
+ def __neg__(self):
+ return -self.__wrapped__
+
+ def __pos__(self):
+ return +self.__wrapped__
+
+ def __abs__(self):
+ return abs(self.__wrapped__)
+
+ def __invert__(self):
+ return ~self.__wrapped__
+
+ def __int__(self):
+ return int(self.__wrapped__)
+
+ def __long__(self):
+ return long(self.__wrapped__)
+
+ def __float__(self):
+ return float(self.__wrapped__)
+
+ def __complex__(self):
+ return complex(self.__wrapped__)
+
+ def __oct__(self):
+ return oct(self.__wrapped__)
+
+ def __hex__(self):
+ return hex(self.__wrapped__)
+
+ def __index__(self):
+ return operator.index(self.__wrapped__)
+
+ def __len__(self):
+ return len(self.__wrapped__)
+
+ def __contains__(self, value):
+ return value in self.__wrapped__
+
+ def __getitem__(self, key):
+ return self.__wrapped__[key]
+
+ def __setitem__(self, key, value):
+ self.__wrapped__[key] = value
+
+ def __delitem__(self, key):
+ del self.__wrapped__[key]
+
+ def __getslice__(self, i, j):
+ return self.__wrapped__[i:j]
+
+ def __setslice__(self, i, j, value):
+ self.__wrapped__[i:j] = value
+
+ def __delslice__(self, i, j):
+ del self.__wrapped__[i:j]
+
+ def __enter__(self):
+ return self.__wrapped__.__enter__()
+
+ def __exit__(self, *args, **kwargs):
+ return self.__wrapped__.__exit__(*args, **kwargs)
+
+ def __iter__(self):
+ return iter(self.__wrapped__)
+
+ def __copy__(self):
+ raise NotImplementedError('object proxy must define __copy__()')
+
+ def __deepcopy__(self, memo):
+ raise NotImplementedError('object proxy must define __deepcopy__()')
+
+ def __reduce__(self):
+ raise NotImplementedError(
+ 'object proxy must define __reduce_ex__()')
+
+ def __reduce_ex__(self, protocol):
+ raise NotImplementedError(
+ 'object proxy must define __reduce_ex__()')
+
+class CallableObjectProxy(ObjectProxy):
+
+ def __call__(self, *args, **kwargs):
+ return self.__wrapped__(*args, **kwargs)
+
+class PartialCallableObjectProxy(ObjectProxy):
+
+ def __init__(self, *args, **kwargs):
+ if len(args) < 1:
+ raise TypeError('partial type takes at least one argument')
+
+ wrapped, args = args[0], args[1:]
+
+ if not callable(wrapped):
+ raise TypeError('the first argument must be callable')
+
+ super(PartialCallableObjectProxy, self).__init__(wrapped)
+
+ self._self_args = args
+ self._self_kwargs = kwargs
+
+ def __call__(self, *args, **kwargs):
+ _args = self._self_args + args
+
+ _kwargs = dict(self._self_kwargs)
+ _kwargs.update(kwargs)
+
+ return self.__wrapped__(*_args, **_kwargs)
+
+class _FunctionWrapperBase(ObjectProxy):
+
+ __slots__ = ('_self_instance', '_self_wrapper', '_self_enabled',
+ '_self_binding', '_self_parent')
+
+ def __init__(self, wrapped, instance, wrapper, enabled=None,
+ binding='function', parent=None):
+
+ super(_FunctionWrapperBase, self).__init__(wrapped)
+
+ object.__setattr__(self, '_self_instance', instance)
+ object.__setattr__(self, '_self_wrapper', wrapper)
+ object.__setattr__(self, '_self_enabled', enabled)
+ object.__setattr__(self, '_self_binding', binding)
+ object.__setattr__(self, '_self_parent', parent)
+
+ def __get__(self, instance, owner):
+ # This method is actually doing double duty for both unbound and
+ # bound derived wrapper classes. It should possibly be broken up
+ # and the distinct functionality moved into the derived classes.
+ # Can't do that straight away due to some legacy code which is
+ # relying on it being here in this base class.
+ #
+ # The distinguishing attribute which determines whether we are
+ # being called in an unbound or bound wrapper is the parent
+ # attribute. If binding has never occurred, then the parent will
+ # be None.
+ #
+ # First therefore, is if we are called in an unbound wrapper. In
+ # this case we perform the binding.
+ #
+ # We have one special case to worry about here. This is where we
+ # are decorating a nested class. In this case the wrapped class
+ # would not have a __get__() method to call. In that case we
+ # simply return self.
+ #
+ # Note that we otherwise still do binding even if instance is
+ # None and accessing an unbound instance method from a class.
+ # This is because we need to be able to later detect that
+ # specific case as we will need to extract the instance from the
+ # first argument of those passed in.
+
+ if self._self_parent is None:
+ if not inspect.isclass(self.__wrapped__):
+ descriptor = self.__wrapped__.__get__(instance, owner)
+
+ return self.__bound_function_wrapper__(descriptor, instance,
+ self._self_wrapper, self._self_enabled,
+ self._self_binding, self)
+
+ return self
+
+ # Now we have the case of binding occurring a second time on what
+ # was already a bound function. In this case we would usually
+ # return ourselves again. This mirrors what Python does.
+ #
+ # The special case this time is where we were originally bound
+ # with an instance of None and we were likely an instance
+ # method. In that case we rebind against the original wrapped
+ # function from the parent again.
+
+ if self._self_instance is None and self._self_binding == 'function':
+ descriptor = self._self_parent.__wrapped__.__get__(
+ instance, owner)
+
+ return self._self_parent.__bound_function_wrapper__(
+ descriptor, instance, self._self_wrapper,
+ self._self_enabled, self._self_binding,
+ self._self_parent)
+
+ return self
+
+ def __call__(self, *args, **kwargs):
+ # If enabled has been specified, then evaluate it at this point
+ # and if the wrapper is not to be executed, then simply return
+ # the bound function rather than a bound wrapper for the bound
+ # function. When evaluating enabled, if it is callable we call
+ # it, otherwise we evaluate it as a boolean.
+
+ if self._self_enabled is not None:
+ if callable(self._self_enabled):
+ if not self._self_enabled():
+ return self.__wrapped__(*args, **kwargs)
+ elif not self._self_enabled:
+ return self.__wrapped__(*args, **kwargs)
+
+ # This can occur where initial function wrapper was applied to
+ # a function that was already bound to an instance. In that case
+ # we want to extract the instance from the function and use it.
+
+ if self._self_binding == 'function':
+ if self._self_instance is None:
+ instance = getattr(self.__wrapped__, '__self__', None)
+ if instance is not None:
+ return self._self_wrapper(self.__wrapped__, instance,
+ args, kwargs)
+
+ # This is generally invoked when the wrapped function is being
+ # called as a normal function and is not bound to a class as an
+ # instance method. This is also invoked in the case where the
+ # wrapped function was a method, but this wrapper was in turn
+ # wrapped using the staticmethod decorator.
+
+ return self._self_wrapper(self.__wrapped__, self._self_instance,
+ args, kwargs)
+
+class BoundFunctionWrapper(_FunctionWrapperBase):
+
+ def __call__(self, *args, **kwargs):
+ # If enabled has been specified, then evaluate it at this point
+ # and if the wrapper is not to be executed, then simply return
+ # the bound function rather than a bound wrapper for the bound
+ # function. When evaluating enabled, if it is callable we call
+ # it, otherwise we evaluate it as a boolean.
+
+ if self._self_enabled is not None:
+ if callable(self._self_enabled):
+ if not self._self_enabled():
+ return self.__wrapped__(*args, **kwargs)
+ elif not self._self_enabled:
+ return self.__wrapped__(*args, **kwargs)
+
+ # We need to do things different depending on whether we are
+ # likely wrapping an instance method vs a static method or class
+ # method.
+
+ if self._self_binding == 'function':
+ if self._self_instance is None:
+ # This situation can occur where someone is calling the
+ # instancemethod via the class type and passing the instance
+ # as the first argument. We need to shift the args before
+ # making the call to the wrapper and effectively bind the
+ # instance to the wrapped function using a partial so the
+ # wrapper doesn't see anything as being different.
+
+ if not args:
+ raise TypeError('missing 1 required positional argument')
+
+ instance, args = args[0], args[1:]
+ wrapped = PartialCallableObjectProxy(self.__wrapped__, instance)
+ return self._self_wrapper(wrapped, instance, args, kwargs)
+
+ return self._self_wrapper(self.__wrapped__, self._self_instance,
+ args, kwargs)
+
+ else:
+ # As in this case we would be dealing with a classmethod or
+ # staticmethod, then _self_instance will only tell us whether
+ # when calling the classmethod or staticmethod they did it via an
+ # instance of the class it is bound to and not the case where
+ # done by the class type itself. We thus ignore _self_instance
+ # and use the __self__ attribute of the bound function instead.
+ # For a classmethod, this means instance will be the class type
+ # and for a staticmethod it will be None. This is probably the
+ # more useful thing we can pass through even though we loose
+ # knowledge of whether they were called on the instance vs the
+ # class type, as it reflects what they have available in the
+ # decoratored function.
+
+ instance = getattr(self.__wrapped__, '__self__', None)
+
+ return self._self_wrapper(self.__wrapped__, instance, args,
+ kwargs)
+
+class FunctionWrapper(_FunctionWrapperBase):
+
+ __bound_function_wrapper__ = BoundFunctionWrapper
+
+ def __init__(self, wrapped, wrapper, enabled=None):
+ # What it is we are wrapping here could be anything. We need to
+ # try and detect specific cases though. In particular, we need
+ # to detect when we are given something that is a method of a
+ # class. Further, we need to know when it is likely an instance
+ # method, as opposed to a class or static method. This can
+ # become problematic though as there isn't strictly a fool proof
+ # method of knowing.
+ #
+ # The situations we could encounter when wrapping a method are:
+ #
+ # 1. The wrapper is being applied as part of a decorator which
+ # is a part of the class definition. In this case what we are
+ # given is the raw unbound function, classmethod or staticmethod
+ # wrapper objects.
+ #
+ # The problem here is that we will not know we are being applied
+ # in the context of the class being set up. This becomes
+ # important later for the case of an instance method, because in
+ # that case we just see it as a raw function and can't
+ # distinguish it from wrapping a normal function outside of
+ # a class context.
+ #
+ # 2. The wrapper is being applied when performing monkey
+ # patching of the class type afterwards and the method to be
+ # wrapped was retrieved direct from the __dict__ of the class
+ # type. This is effectively the same as (1) above.
+ #
+ # 3. The wrapper is being applied when performing monkey
+ # patching of the class type afterwards and the method to be
+ # wrapped was retrieved from the class type. In this case
+ # binding will have been performed where the instance against
+ # which the method is bound will be None at that point.
+ #
+ # This case is a problem because we can no longer tell if the
+ # method was a static method, plus if using Python3, we cannot
+ # tell if it was an instance method as the concept of an
+ # unnbound method no longer exists.
+ #
+ # 4. The wrapper is being applied when performing monkey
+ # patching of an instance of a class. In this case binding will
+ # have been perfomed where the instance was not None.
+ #
+ # This case is a problem because we can no longer tell if the
+ # method was a static method.
+ #
+ # Overall, the best we can do is look at the original type of the
+ # object which was wrapped prior to any binding being done and
+ # see if it is an instance of classmethod or staticmethod. In
+ # the case where other decorators are between us and them, if
+ # they do not propagate the __class__ attribute so that the
+ # isinstance() checks works, then likely this will do the wrong
+ # thing where classmethod and staticmethod are used.
+ #
+ # Since it is likely to be very rare that anyone even puts
+ # decorators around classmethod and staticmethod, likelihood of
+ # that being an issue is very small, so we accept it and suggest
+ # that those other decorators be fixed. It is also only an issue
+ # if a decorator wants to actually do things with the arguments.
+ #
+ # As to not being able to identify static methods properly, we
+ # just hope that that isn't something people are going to want
+ # to wrap, or if they do suggest they do it the correct way by
+ # ensuring that it is decorated in the class definition itself,
+ # or patch it in the __dict__ of the class type.
+ #
+ # So to get the best outcome we can, whenever we aren't sure what
+ # it is, we label it as a 'function'. If it was already bound and
+ # that is rebound later, we assume that it will be an instance
+ # method and try an cope with the possibility that the 'self'
+ # argument it being passed as an explicit argument and shuffle
+ # the arguments around to extract 'self' for use as the instance.
+
+ if isinstance(wrapped, classmethod):
+ binding = 'classmethod'
+
+ elif isinstance(wrapped, staticmethod):
+ binding = 'staticmethod'
+
+ elif hasattr(wrapped, '__self__'):
+ if inspect.isclass(wrapped.__self__):
+ binding = 'classmethod'
+ else:
+ binding = 'function'
+
+ else:
+ binding = 'function'
+
+ super(FunctionWrapper, self).__init__(wrapped, None, wrapper,
+ enabled, binding)
+
+try:
+ if not os.environ.get('WRAPT_DISABLE_EXTENSIONS'):
+ from ._wrappers import (ObjectProxy, CallableObjectProxy,
+ PartialCallableObjectProxy, FunctionWrapper,
+ BoundFunctionWrapper, _FunctionWrapperBase)
+except ImportError:
+ pass
+
+# Helper functions for applying wrappers to existing functions.
+
+def resolve_path(module, name):
+ if isinstance(module, string_types):
+ __import__(module)
+ module = sys.modules[module]
+
+ parent = module
+
+ path = name.split('.')
+ attribute = path[0]
+
+ original = getattr(parent, attribute)
+ for attribute in path[1:]:
+ parent = original
+
+ # We can't just always use getattr() because in doing
+ # that on a class it will cause binding to occur which
+ # will complicate things later and cause some things not
+ # to work. For the case of a class we therefore access
+ # the __dict__ directly. To cope though with the wrong
+ # class being given to us, or a method being moved into
+ # a base class, we need to walk the class hierarchy to
+ # work out exactly which __dict__ the method was defined
+ # in, as accessing it from __dict__ will fail if it was
+ # not actually on the class given. Fallback to using
+ # getattr() if we can't find it. If it truly doesn't
+ # exist, then that will fail.
+
+ if inspect.isclass(original):
+ for cls in inspect.getmro(original):
+ if attribute in vars(cls):
+ original = vars(cls)[attribute]
+ break
+ else:
+ original = getattr(original, attribute)
+
+ else:
+ original = getattr(original, attribute)
+
+ return (parent, attribute, original)
+
+def apply_patch(parent, attribute, replacement):
+ setattr(parent, attribute, replacement)
+
+def wrap_object(module, name, factory, args=(), kwargs={}):
+ (parent, attribute, original) = resolve_path(module, name)
+ wrapper = factory(original, *args, **kwargs)
+ apply_patch(parent, attribute, wrapper)
+ return wrapper
+
+# Function for applying a proxy object to an attribute of a class
+# instance. The wrapper works by defining an attribute of the same name
+# on the class which is a descriptor and which intercepts access to the
+# instance attribute. Note that this cannot be used on attributes which
+# are themselves defined by a property object.
+
+class AttributeWrapper(object):
+
+ def __init__(self, attribute, factory, args, kwargs):
+ self.attribute = attribute
+ self.factory = factory
+ self.args = args
+ self.kwargs = kwargs
+
+ def __get__(self, instance, owner):
+ value = instance.__dict__[self.attribute]
+ return self.factory(value, *self.args, **self.kwargs)
+
+ def __set__(self, instance, value):
+ instance.__dict__[self.attribute] = value
+
+ def __delete__(self, instance):
+ del instance.__dict__[self.attribute]
+
+def wrap_object_attribute(module, name, factory, args=(), kwargs={}):
+ path, attribute = name.rsplit('.', 1)
+ parent = resolve_path(module, path)[2]
+ wrapper = AttributeWrapper(attribute, factory, args, kwargs)
+ apply_patch(parent, attribute, wrapper)
+ return wrapper
+
+# Functions for creating a simple decorator using a FunctionWrapper,
+# plus short cut functions for applying wrappers to functions. These are
+# for use when doing monkey patching. For a more featured way of
+# creating decorators see the decorator decorator instead.
+
+def function_wrapper(wrapper):
+ def _wrapper(wrapped, instance, args, kwargs):
+ target_wrapped = args[0]
+ if instance is None:
+ target_wrapper = wrapper
+ elif inspect.isclass(instance):
+ target_wrapper = wrapper.__get__(None, instance)
+ else:
+ target_wrapper = wrapper.__get__(instance, type(instance))
+ return FunctionWrapper(target_wrapped, target_wrapper)
+ return FunctionWrapper(wrapper, _wrapper)
+
+def wrap_function_wrapper(module, name, wrapper):
+ return wrap_object(module, name, FunctionWrapper, (wrapper,))
+
+def patch_function_wrapper(module, name):
+ def _wrapper(wrapper):
+ return wrap_object(module, name, FunctionWrapper, (wrapper,))
+ return _wrapper
+
+def transient_function_wrapper(module, name):
+ def _decorator(wrapper):
+ def _wrapper(wrapped, instance, args, kwargs):
+ target_wrapped = args[0]
+ if instance is None:
+ target_wrapper = wrapper
+ elif inspect.isclass(instance):
+ target_wrapper = wrapper.__get__(None, instance)
+ else:
+ target_wrapper = wrapper.__get__(instance, type(instance))
+ def _execute(wrapped, instance, args, kwargs):
+ (parent, attribute, original) = resolve_path(module, name)
+ replacement = FunctionWrapper(original, target_wrapper)
+ setattr(parent, attribute, replacement)
+ try:
+ return wrapped(*args, **kwargs)
+ finally:
+ setattr(parent, attribute, original)
+ return FunctionWrapper(target_wrapped, _execute)
+ return FunctionWrapper(wrapper, _wrapper)
+ return _decorator
+
+# A weak function proxy. This will work on instance methods, class
+# methods, static methods and regular functions. Special treatment is
+# needed for the method types because the bound method is effectively a
+# transient object and applying a weak reference to one will immediately
+# result in it being destroyed and the weakref callback called. The weak
+# reference is therefore applied to the instance the method is bound to
+# and the original function. The function is then rebound at the point
+# of a call via the weak function proxy.
+
+def _weak_function_proxy_callback(ref, proxy, callback):
+ if proxy._self_expired:
+ return
+
+ proxy._self_expired = True
+
+ # This could raise an exception. We let it propagate back and let
+ # the weakref.proxy() deal with it, at which point it generally
+ # prints out a short error message direct to stderr and keeps going.
+
+ if callback is not None:
+ callback(proxy)
+
+class WeakFunctionProxy(ObjectProxy):
+
+ __slots__ = ('_self_expired', '_self_instance')
+
+ def __init__(self, wrapped, callback=None):
+ # We need to determine if the wrapped function is actually a
+ # bound method. In the case of a bound method, we need to keep a
+ # reference to the original unbound function and the instance.
+ # This is necessary because if we hold a reference to the bound
+ # function, it will be the only reference and given it is a
+ # temporary object, it will almost immediately expire and
+ # the weakref callback triggered. So what is done is that we
+ # hold a reference to the instance and unbound function and
+ # when called bind the function to the instance once again and
+ # then call it. Note that we avoid using a nested function for
+ # the callback here so as not to cause any odd reference cycles.
+
+ _callback = callback and functools.partial(
+ _weak_function_proxy_callback, proxy=self,
+ callback=callback)
+
+ self._self_expired = False
+
+ if isinstance(wrapped, _FunctionWrapperBase):
+ self._self_instance = weakref.ref(wrapped._self_instance,
+ _callback)
+
+ if wrapped._self_parent is not None:
+ super(WeakFunctionProxy, self).__init__(
+ weakref.proxy(wrapped._self_parent, _callback))
+
+ else:
+ super(WeakFunctionProxy, self).__init__(
+ weakref.proxy(wrapped, _callback))
+
+ return
+
+ try:
+ self._self_instance = weakref.ref(wrapped.__self__, _callback)
+
+ super(WeakFunctionProxy, self).__init__(
+ weakref.proxy(wrapped.__func__, _callback))
+
+ except AttributeError:
+ self._self_instance = None
+
+ super(WeakFunctionProxy, self).__init__(
+ weakref.proxy(wrapped, _callback))
+
+ def __call__(self, *args, **kwargs):
+ # We perform a boolean check here on the instance and wrapped
+ # function as that will trigger the reference error prior to
+ # calling if the reference had expired.
+
+ instance = self._self_instance and self._self_instance()
+ function = self.__wrapped__ and self.__wrapped__
+
+ # If the wrapped function was originally a bound function, for
+ # which we retained a reference to the instance and the unbound
+ # function we need to rebind the function and then call it. If
+ # not just called the wrapped function.
+
+ if instance is None:
+ return self.__wrapped__(*args, **kwargs)
+
+ return function.__get__(instance, type(instance))(*args, **kwargs)