diff --git a/main.py b/main.py index 4fb5d46d..ffb4cd2f 100644 --- a/main.py +++ b/main.py @@ -21,30 +21,33 @@ Does not work if no Start (Should print out NO START FOUND) Does not work if no End (Should print out NO END FOUND) Does not work if no path (Should print out NO PATH FOUND) """ + + + + import heapq - - class MazeSolver: - # self corresponds to "this" in js, it refers to object of MazeSolver class - def __init__(self, maze): - # assign readed maze 2D array to parameter from class MazeSolver - self.maze = maze - self.start, self.end = self.find_start_and_end() + # self corresponds to "this" in js, it refers to object of MazeSolver class + def __init__(self, maze): + # assign readed maze 2D array to parameter from class MazeSolver + self.maze = maze + self.start, self.end = self.find_start_and_end() -# go through each character in 2D array and find one that corresponds to Start/End character +# go through each character in 2D array and find one that corresponds to +# Start/End character - def find_start_and_end(self): - start = end = None - for row in range(len(self.maze)): - for col in range(len(self.maze[row])): - if self.maze[row][col] == 'S': - start = (row, col) - elif self.maze[row][col] == 'E': - end = (row, col) - if start != None and end != None: - return start, end - print(f"DID NOT FOUND START OR END, Start: {start}, End: {end}") + def find_start_and_end(self): + start = end = None + for row in range(len(self.maze)): + for col in range(len(self.maze[row])): + if self.maze[row][col] == 'S': + start = (row, col) + elif self.maze[row][col] == 'E': + end = (row, col) + if start is not None and end is not None: + return start, end + print(f"DID NOT FOUND START OR END, Start: {start}, End: {end}") # Go through each neighboor # N @@ -52,94 +55,102 @@ class MazeSolver: # N # If it is not a "wall" (#) add its position to list of neighbors - def get_neighbors(self, position): - row, col = position - neighbors = [] - if row > 0 and self.maze[row - 1][col] != '#': - neighbors.append((row - 1, col)) - if col > 0 and self.maze[row][col - 1] != '#': - neighbors.append((row, col - 1)) - if row < len(self.maze) - 1 and self.maze[row + 1][col] != '#': - neighbors.append((row + 1, col)) - if col < len(self.maze[row]) - 1 and self.maze[row][col + 1] != '#': - neighbors.append((row, col + 1)) - return neighbors + def get_neighbors(self, position): + row, col = position + neighbors = [] + if row > 0 and self.maze[row - 1][col] != '#': + neighbors.append((row - 1, col)) + if col > 0 and self.maze[row][col - 1] != '#': + neighbors.append((row, col - 1)) + if row < len(self.maze) - 1 and self.maze[row + 1][col] != '#': + neighbors.append((row + 1, col)) + if col < len(self.maze[row]) - 1 and self.maze[row][col + 1] != '#': + neighbors.append((row, col + 1)) + return neighbors # find path through maze - def solve(self): - queue = [] - # set means that values inside can not repeat - visited = set() - # https://docs.python.org/3/library/heapq.html - # push onto the queue (which becomes heapq), element containinig values - # we use heapq so the element with lowest heurisitc value will always be at the top of heap - heapq.heappush( - queue, (self.heuristicEuclidean(self.start), self.start, [self.start])) + def solve(self): + queue = [] + # set means that values inside can not repeat + visited = set() + # https://docs.python.org/3/library/heapq.html + # push onto the queue (which becomes heapq), element containinig values + # we use heapq so the element with lowest heurisitc value will always + # be at the top of heap + heapq.heappush( + queue, (self.heuristicEuclidean( + self.start), self.start, [ + self.start])) - # go through queue until it's empty - # find neighbour (which is not wall) closests to END point (based on heuristic) - # go there and repeat - # if cannot find path it starts over but skips the path that lead it to dead end - while queue: - # pop first element of heap - # first value is skipped and we only save current position and path on heap - _, current, path = heapq.heappop(queue) - # if we already visited current skip code and go to next iteration - if current in visited: - continue + # go through queue until it's empty + # find neighbour (which is not wall) closests to END point (based on heuristic) + # go there and repeat + # if cannot find path it starts over but skips the path that lead it to + # dead end + while queue: + # pop first element of heap + # first value is skipped and we only save current position and path + # on heap + _, current, path = heapq.heappop(queue) + # if we already visited current skip code and go to next iteration + if current in visited: + continue # if we found the end return path - if current == self.end: - return path - visited.add(current) - for neighbor in self.get_neighbors(current): - if neighbor not in visited: - new_path = path + [neighbor] - heapq.heappush(queue, - (self.heuristicEuclidean(neighbor), neighbor, new_path)) - print_maze(self.maze, new_path) - print() + if current == self.end: + return path + visited.add(current) + for neighbor in self.get_neighbors(current): + if neighbor not in visited: + new_path = path + [neighbor] + heapq.heappush( + queue, (self.heuristicEuclidean(neighbor), neighbor, new_path)) + print_maze(self.maze, new_path) + print() - # This heuristic returns the Manhatan distance between the given position and the maze's end - def heuristicManhatan(self, position): - return abs(position[0] - self.end[0]) + abs(position[1] - self.end[1]) + # This heuristic returns the Manhatan distance between the given position + # and the maze's end + def heuristicManhatan(self, position): + return abs(position[0] - self.end[0]) + abs(position[1] - self.end[1]) - # This heuristic returns the Euclidean distance between the given position and the maze's end - def heuristicEuclidean(self, position): - return (abs(position[0] - self.end[0])**2 + - abs(position[1] - self.end[1])**2)**0.5 + # This heuristic returns the Euclidean distance between the given position + # and the maze's end + def heuristicEuclidean(self, position): + return (abs(position[0] - self.end[0])**2 + + abs(position[1] - self.end[1])**2)**0.5 # Open and load text file to array def load_maze(filename): - # Open for reading only and save to fileContents - fileContents = open(filename, 'r') - # strip() removes extra white spaces from the beginning and the end of a string - # list() changes string to array of chars - # Inside of square brackets we will have an array of characters for each line of file - # after going through every line in a file we will have 2D array of arrays of characters of every line - maze = [list(line.strip()) for line in fileContents] - return maze + # Open for reading only and save to fileContents + fileContents = open(filename, 'r') + # strip() removes extra white spaces from the beginning and the end of a string + # list() changes string to array of chars + # Inside of square brackets we will have an array of characters for each line of file + # after going through every line in a file we will have 2D array of arrays + # of characters of every line + maze = [list(line.strip()) for line in fileContents] + return maze def print_maze(maze, path=None): - if path is None: - path = [] - for row in range(len(maze)): - for col in range(len(maze[row])): - if (row, col) in path: - print('*', end='') - else: - print(maze[row][col], end='') - print() + if path is None: + path = [] + for row in range(len(maze)): + for col in range(len(maze[row])): + if (row, col) in path: + print('*', end='') + else: + print(maze[row][col], end='') + print() # Ran first in the code if __name__ == '__main__': - # Open and load text file to array - maze = load_maze('mazes/mazeDeadEnd.txt') - # Initialize MazeSolver object with maze as paramater - solver = MazeSolver(maze) - # Find path using MazeSolver solve method - path = solver.solve() - print_maze(maze, path) + # Open and load text file to array + maze = load_maze('mazes/mazeDeadEnd.txt') + # Initialize MazeSolver object with maze as paramater + solver = MazeSolver(maze) + # Find path using MazeSolver solve method + path = solver.solve() + print_maze(maze, path) diff --git a/venv/bin/autopep8 b/venv/bin/autopep8 new file mode 100755 index 00000000..53c1bd46 --- /dev/null +++ b/venv/bin/autopep8 @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import re +import sys +from autopep8 import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/epylint b/venv/bin/epylint new file mode 100755 index 00000000..d37d6f8c --- /dev/null +++ b/venv/bin/epylint @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_epylint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_epylint()) diff --git a/venv/bin/get_objgraph b/venv/bin/get_objgraph new file mode 120000 index 00000000..058fc9d8 --- /dev/null +++ b/venv/bin/get_objgraph @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/6a/50/3f/1f2a45ac215d6ff2ad0866b0c04c341528fa5265298b72142c813ce4da \ No newline at end of file diff --git a/venv/bin/isort b/venv/bin/isort new file mode 100755 index 00000000..b5d54d4a --- /dev/null +++ b/venv/bin/isort @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import re +import sys +from isort.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/venv/bin/isort-identify-imports b/venv/bin/isort-identify-imports new file mode 100755 index 00000000..d7304b15 --- /dev/null +++ b/venv/bin/isort-identify-imports @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import re +import sys +from isort.main import identify_imports_main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(identify_imports_main()) diff --git a/venv/bin/pycodestyle b/venv/bin/pycodestyle new file mode 100755 index 00000000..d6925748 --- /dev/null +++ b/venv/bin/pycodestyle @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import re +import sys +from pycodestyle import _main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(_main()) diff --git a/venv/bin/pylint b/venv/bin/pylint new file mode 100755 index 00000000..7708e71f --- /dev/null +++ b/venv/bin/pylint @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_pylint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_pylint()) diff --git a/venv/bin/pylint-config b/venv/bin/pylint-config new file mode 100755 index 00000000..60946af1 --- /dev/null +++ b/venv/bin/pylint-config @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import re +import sys +from pylint import _run_pylint_config +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(_run_pylint_config()) diff --git a/venv/bin/pyreverse b/venv/bin/pyreverse new file mode 100755 index 00000000..610df84c --- /dev/null +++ b/venv/bin/pyreverse @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_pyreverse +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_pyreverse()) diff --git a/venv/bin/symilar b/venv/bin/symilar new file mode 100755 index 00000000..2f93feb6 --- /dev/null +++ b/venv/bin/symilar @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_symilar +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_symilar()) diff --git a/venv/bin/undill b/venv/bin/undill new file mode 120000 index 00000000..3375e250 --- /dev/null +++ b/venv/bin/undill @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e2/49/4a/6f73df3fa16a5661da7e3c9b169500e2697bc26be21656ce72fcca59af \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/__pycache__/autopep8.cpython-310.pyc b/venv/lib/python3.10/site-packages/__pycache__/autopep8.cpython-310.pyc new file mode 100644 index 00000000..1bd8aea1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/__pycache__/autopep8.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/__pycache__/mccabe.cpython-310.pyc b/venv/lib/python3.10/site-packages/__pycache__/mccabe.cpython-310.pyc new file mode 100644 index 00000000..a34b8cb8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/__pycache__/mccabe.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/__pycache__/pycodestyle.cpython-310.pyc b/venv/lib/python3.10/site-packages/__pycache__/pycodestyle.cpython-310.pyc new file mode 100644 index 00000000..422ce9af Binary files /dev/null and b/venv/lib/python3.10/site-packages/__pycache__/pycodestyle.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/__pycache__/typing_extensions.cpython-310.pyc b/venv/lib/python3.10/site-packages/__pycache__/typing_extensions.cpython-310.pyc index d41cdc43..817292d2 100644 Binary files a/venv/lib/python3.10/site-packages/__pycache__/typing_extensions.cpython-310.pyc and b/venv/lib/python3.10/site-packages/__pycache__/typing_extensions.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/CONTRIBUTORS.txt b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/CONTRIBUTORS.txt new file mode 100644 index 00000000..685f8a0b --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/CONTRIBUTORS.txt @@ -0,0 +1,199 @@ +# This file is autocompleted by 'contributors-txt', +# using the configuration in 'script/.contributors_aliases.json'. +# Do not add new persons manually and only add information without +# using '-' as the line first character. +# Please verify that your change are stable if you modify manually. + +Ex-maintainers +-------------- +- Claudiu Popa +- Sylvain Thénault +- Torsten Marek + + +Maintainers +----------- +- Pierre Sassoulas +- Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> +- Hippo91 +- Marc Mueller <30130371+cdce8p@users.noreply.github.com> +- Jacob Walls +- Bryce Guinta +- Ceridwen +- Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> +- Łukasz Rogalski +- Florian Bruhin +- Ashley Whetter +- Dimitri Prybysh +- Areveny + + +Contributors +------------ +- Emile Anclin +- Nick Drozd +- Andrew Haigh +- Julien Cristau +- David Liu +- Alexandre Fayolle +- Eevee (Alex Munroe) +- David Gilman +- Julien Jehannet +- Calen Pennington +- Tushar Sadhwani <86737547+tushar-deepsource@users.noreply.github.com> +- Hugo van Kemenade +- Tim Martin +- Phil Schaf +- Alex Hall +- Raphael Gaschignard +- Radosław Ganczarek +- Paligot Gérard +- Ioana Tagirta +- Derek Gustafson +- David Shea +- Daniel Harding +- Christian Clauss +- Ville Skyttä +- Rene Zhang +- Philip Lorenz +- Nicolas Chauvat +- Michael K +- Mario Corchero +- Marien Zwart +- Laura Médioni +- James Addison <55152140+jayaddison@users.noreply.github.com> +- FELD Boris +- Enji Cooper +- Dani Alcala <112832187+clavedeluna@users.noreply.github.com> +- Adrien Di Mascio +- tristanlatr <19967168+tristanlatr@users.noreply.github.com> +- emile@crater.logilab.fr +- doranid +- brendanator +- Tomas Gavenciak +- Tim Paine +- Thomas Hisch +- Stefan Scherfke +- Sergei Lebedev <185856+superbobry@users.noreply.github.com> +- Saugat Pachhai (सौगात) +- Ram Rachum +- Pierre-Yves David +- Peter Pentchev +- Peter Kolbus +- Omer Katz +- Moises Lopez +- Michal Vasilek +- Keichi Takahashi +- Kavins Singh +- Karthikeyan Singaravelan +- Joshua Cannon +- John Vandenberg +- Jacob Bogdanov +- Google, Inc. +- David Euresti +- David Douard +- David Cain +- Anthony Truchet +- Anthony Sottile +- Alexander Shadchin +- wgehalo +- rr- +- raylu +- plucury +- ostr00000 +- noah-weingarden <33741795+noah-weingarden@users.noreply.github.com> +- nathannaveen <42319948+nathannaveen@users.noreply.github.com> +- mathieui +- markmcclain +- ioanatia +- grayjk +- adam-grant-hendry <59346180+adam-grant-hendry@users.noreply.github.com> +- Zbigniew Jędrzejewski-Szmek +- Zac Hatfield-Dodds +- Vilnis Termanis +- Valentin Valls +- Uilian Ries +- Tomas Novak +- Thirumal Venkat +- SupImDos <62866982+SupImDos@users.noreply.github.com> +- Stanislav Levin +- Simon Hewitt +- Serhiy Storchaka +- Roy Wright +- Robin Jarry +- René Fritze <47802+renefritze@users.noreply.github.com> +- Redoubts +- Philipp Hörist +- Peter de Blanc +- Peter Talley +- Ovidiu Sabou +- Nicolas Noirbent +- Neil Girdhar +- Michał Masłowski +- Mateusz Bysiek +- Leandro T. C. Melo +- Konrad Weihmann +- Kian Meng, Ang +- Kai Mueller <15907922+kasium@users.noreply.github.com> +- Jörg Thalheim +- Jonathan Striebel +- John Belmonte +- Jeff Widman +- Jeff Quast +- Jarrad Hope +- Jared Garst +- Jakub Wilk +- Iva Miholic +- Ionel Maries Cristian +- HoverHell +- HQupgradeHQ <18361586+HQupgradeHQ@users.noreply.github.com> +- Grygorii Iermolenko +- Gregory P. Smith +- Giuseppe Scrivano +- Frédéric Chapoton +- Francis Charette Migneault +- Felix Mölder +- Federico Bond +- DudeNr33 <3929834+DudeNr33@users.noreply.github.com> +- Dmitry Shachnev +- Denis Laxalde +- Deepyaman Datta +- David Poirier +- Dave Hirschfeld +- Dave Baum +- Daniel Martin +- Daniel Colascione +- Damien Baty +- Craig Franklin +- Colin Kennedy +- Cole Robinson +- Christoph Reiter +- Chris Philip +- BioGeek +- Bianca Power <30207144+biancapower@users.noreply.github.com> +- Benjamin Elven <25181435+S3ntinelX@users.noreply.github.com> +- Ben Elliston +- Becker Awqatty +- Batuhan Taskaya +- BasPH +- Azeem Bande-Ali +- Avram Lubkin +- Aru Sahni +- Artsiom Kaval +- Anubhav <35621759+anubh-v@users.noreply.github.com> +- Antoine Boellinger +- Alphadelta14 +- Alexander Scheel +- Alexander Presnyakov +- Ahmed Azzaoui + +Co-Author +--------- +The following persons were credited manually but did not commit themselves +under this name, or we did not manage to find their commits in the history. + +- François Mockers +- platings +- carl +- alain lefroy +- Mark Gius diff --git a/venv/lib/python3.10/site-packages/typing_extensions-3.10.0.2.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/INSTALLER similarity index 100% rename from venv/lib/python3.10/site-packages/typing_extensions-3.10.0.2.dist-info/INSTALLER rename to venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/INSTALLER diff --git a/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/LICENSE b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/LICENSE new file mode 120000 index 00000000..767cd6d2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/LICENSE @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/fe/a1/6b/da9e734dea0d9c8d9f5b909878ef4171626354329609fffcf3db42cb24 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/METADATA new file mode 100644 index 00000000..2cbe2760 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/METADATA @@ -0,0 +1,128 @@ +Metadata-Version: 2.1 +Name: astroid +Version: 2.15.0 +Summary: An abstract syntax tree for Python with inference support. +Author-email: Python Code Quality Authority +License: LGPL-2.1-or-later +Project-URL: Docs, https://pylint.readthedocs.io/projects/astroid/en/latest/ +Project-URL: Source Code, https://github.com/PyCQA/astroid +Project-URL: Bug tracker, https://github.com/PyCQA/astroid/issues +Project-URL: Discord server, https://discord.gg/Egy6P8AMB5 +Keywords: static code analysis,python,abstract syntax tree +Classifier: Development Status :: 6 - Mature +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2) +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Topic :: Software Development :: Testing +Requires-Python: >=3.7.2 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: CONTRIBUTORS.txt +Requires-Dist: lazy-object-proxy (>=1.4.0) +Requires-Dist: typed-ast (<2.0,>=1.4.0) ; implementation_name == "cpython" and python_version < "3.8" +Requires-Dist: wrapt (<2,>=1.11) ; python_version < "3.11" +Requires-Dist: typing-extensions (>=4.0.0) ; python_version < "3.11" +Requires-Dist: wrapt (<2,>=1.14) ; python_version >= "3.11" + +Astroid +======= + +.. image:: https://codecov.io/gh/PyCQA/astroid/branch/main/graph/badge.svg?token=Buxy4WptLb + :target: https://codecov.io/gh/PyCQA/astroid + :alt: Coverage badge from codecov + +.. image:: https://readthedocs.org/projects/astroid/badge/?version=latest + :target: http://astroid.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + +.. image:: https://results.pre-commit.ci/badge/github/PyCQA/astroid/main.svg + :target: https://results.pre-commit.ci/latest/github/PyCQA/astroid/main + :alt: pre-commit.ci status + +.. |tidelift_logo| image:: https://raw.githubusercontent.com/PyCQA/astroid/main/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png + :width: 200 + :alt: Tidelift + +.. list-table:: + :widths: 10 100 + + * - |tidelift_logo| + - Professional support for astroid is available as part of the + `Tidelift Subscription`_. Tidelift gives software development teams a single source for + purchasing and maintaining their software, with professional grade assurances + from the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-astroid?utm_source=pypi-astroid&utm_medium=referral&utm_campaign=readme + + + +What's this? +------------ + +The aim of this module is to provide a common base representation of +python source code. It is currently the library powering pylint's capabilities. + +It provides a compatible representation which comes from the `_ast` +module. It rebuilds the tree generated by the builtin _ast module by +recursively walking down the AST and building an extended ast. The new +node classes have additional methods and attributes for different +usages. They include some support for static inference and local name +scopes. Furthermore, astroid can also build partial trees by inspecting living +objects. + + +Installation +------------ + +Extract the tarball, jump into the created directory and run:: + + pip install . + + +If you want to do an editable installation, you can run:: + + pip install -e . + + +If you have any questions, please mail the code-quality@python.org +mailing list for support. See +http://mail.python.org/mailman/listinfo/code-quality for subscription +information and archives. + +Documentation +------------- +http://astroid.readthedocs.io/en/latest/ + + +Python Versions +--------------- + +astroid 2.0 is currently available for Python 3 only. If you want Python 2 +support, use an older version of astroid (though note that these versions +are no longer supported). + +Test +---- + +Tests are in the 'test' subdirectory. To launch the whole tests suite, you can use +either `tox` or `pytest`:: + + tox + pytest diff --git a/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/RECORD new file mode 100644 index 00000000..619a9b45 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/RECORD @@ -0,0 +1,209 @@ +astroid-2.15.0.dist-info/CONTRIBUTORS.txt,sha256=_R56rA3okHgrKxY42SDN2zOaFZV49Oks0iFtQ_tIIBs,8038 +astroid-2.15.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +astroid-2.15.0.dist-info/LICENSE,sha256=_qFr2p5zTeoNnI2fW5CYeO9BcWJjVDKWCf_889tCyyQ,26516 +astroid-2.15.0.dist-info/METADATA,sha256=rju1DTgF4pzdcf_H6Kb8EQeonNs7FtpDgLyaecs6tmg,4786 +astroid-2.15.0.dist-info/RECORD,, +astroid-2.15.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +astroid-2.15.0.dist-info/top_level.txt,sha256=HsdW4O2x7ZXRj6k-agi3RaQybGLobI3VSE-jt4vQUXM,8 +astroid/__init__.py,sha256=HKHncgrj70qSs9Gg4aDpFyiAQFHhrSoR1O20OQ3K0Bo,5091 +astroid/__pkginfo__.py,sha256=yDKgWZv5-RlTCQCeK9XRzNWKAN03w8wVDGG2JiXtjZM,274 +astroid/__pycache__/__init__.cpython-310.pyc,, +astroid/__pycache__/__pkginfo__.cpython-310.pyc,, +astroid/__pycache__/_ast.cpython-310.pyc,, +astroid/__pycache__/_backport_stdlib_names.cpython-310.pyc,, +astroid/__pycache__/_cache.cpython-310.pyc,, +astroid/__pycache__/arguments.cpython-310.pyc,, +astroid/__pycache__/astroid_manager.cpython-310.pyc,, +astroid/__pycache__/bases.cpython-310.pyc,, +astroid/__pycache__/builder.cpython-310.pyc,, +astroid/__pycache__/const.cpython-310.pyc,, +astroid/__pycache__/constraint.cpython-310.pyc,, +astroid/__pycache__/context.cpython-310.pyc,, +astroid/__pycache__/decorators.cpython-310.pyc,, +astroid/__pycache__/exceptions.cpython-310.pyc,, +astroid/__pycache__/filter_statements.cpython-310.pyc,, +astroid/__pycache__/helpers.cpython-310.pyc,, +astroid/__pycache__/inference.cpython-310.pyc,, +astroid/__pycache__/inference_tip.cpython-310.pyc,, +astroid/__pycache__/manager.cpython-310.pyc,, +astroid/__pycache__/mixins.cpython-310.pyc,, +astroid/__pycache__/modutils.cpython-310.pyc,, +astroid/__pycache__/node_classes.cpython-310.pyc,, +astroid/__pycache__/objects.cpython-310.pyc,, +astroid/__pycache__/protocols.cpython-310.pyc,, +astroid/__pycache__/raw_building.cpython-310.pyc,, +astroid/__pycache__/rebuilder.cpython-310.pyc,, +astroid/__pycache__/scoped_nodes.cpython-310.pyc,, +astroid/__pycache__/test_utils.cpython-310.pyc,, +astroid/__pycache__/transforms.cpython-310.pyc,, +astroid/__pycache__/typing.cpython-310.pyc,, +astroid/__pycache__/util.cpython-310.pyc,, +astroid/_ast.py,sha256=fx6vCj7YJJRQSf3GQYRllJ9hRKPh9-QczU3crjt3IrA,4146 +astroid/_backport_stdlib_names.py,sha256=pmMEGwionQpfJdJaJomDF6Qc6peNMxChcOsNa8U2IDo,7016 +astroid/_cache.py,sha256=ZuO1Kl9HXaxoJ_hJXbzWqkMGmQR7CGf-R4cPen9odzM,786 +astroid/arguments.py,sha256=9GeVion6IpISnxQGyXU6uHxfJ_kuDXOCYm3Jv6SJNUg,12958 +astroid/astroid_manager.py,sha256=02l_ltGWEglvkoL_qMGmYxuHpDA5-ySPf0r1cGwIQqU,572 +astroid/bases.py,sha256=9GT3_gPrLdlS3zmvW3Nmv2X2pQcY5_vywz1MVky4ero,25594 +astroid/brain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/brain/__pycache__/__init__.cpython-310.pyc,, +astroid/brain/__pycache__/brain_argparse.cpython-310.pyc,, +astroid/brain/__pycache__/brain_attrs.cpython-310.pyc,, +astroid/brain/__pycache__/brain_boto3.cpython-310.pyc,, +astroid/brain/__pycache__/brain_builtin_inference.cpython-310.pyc,, +astroid/brain/__pycache__/brain_collections.cpython-310.pyc,, +astroid/brain/__pycache__/brain_crypt.cpython-310.pyc,, +astroid/brain/__pycache__/brain_ctypes.cpython-310.pyc,, +astroid/brain/__pycache__/brain_curses.cpython-310.pyc,, +astroid/brain/__pycache__/brain_dataclasses.cpython-310.pyc,, +astroid/brain/__pycache__/brain_dateutil.cpython-310.pyc,, +astroid/brain/__pycache__/brain_fstrings.cpython-310.pyc,, +astroid/brain/__pycache__/brain_functools.cpython-310.pyc,, +astroid/brain/__pycache__/brain_gi.cpython-310.pyc,, +astroid/brain/__pycache__/brain_hashlib.cpython-310.pyc,, +astroid/brain/__pycache__/brain_http.cpython-310.pyc,, +astroid/brain/__pycache__/brain_hypothesis.cpython-310.pyc,, +astroid/brain/__pycache__/brain_io.cpython-310.pyc,, +astroid/brain/__pycache__/brain_mechanize.cpython-310.pyc,, +astroid/brain/__pycache__/brain_multiprocessing.cpython-310.pyc,, +astroid/brain/__pycache__/brain_namedtuple_enum.cpython-310.pyc,, +astroid/brain/__pycache__/brain_nose.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_einsumfunc.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_core_umath.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_ma.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_ndarray.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-310.pyc,, +astroid/brain/__pycache__/brain_numpy_utils.cpython-310.pyc,, +astroid/brain/__pycache__/brain_pathlib.cpython-310.pyc,, +astroid/brain/__pycache__/brain_pkg_resources.cpython-310.pyc,, +astroid/brain/__pycache__/brain_pytest.cpython-310.pyc,, +astroid/brain/__pycache__/brain_qt.cpython-310.pyc,, +astroid/brain/__pycache__/brain_random.cpython-310.pyc,, +astroid/brain/__pycache__/brain_re.cpython-310.pyc,, +astroid/brain/__pycache__/brain_regex.cpython-310.pyc,, +astroid/brain/__pycache__/brain_responses.cpython-310.pyc,, +astroid/brain/__pycache__/brain_scipy_signal.cpython-310.pyc,, +astroid/brain/__pycache__/brain_signal.cpython-310.pyc,, +astroid/brain/__pycache__/brain_six.cpython-310.pyc,, +astroid/brain/__pycache__/brain_sqlalchemy.cpython-310.pyc,, +astroid/brain/__pycache__/brain_ssl.cpython-310.pyc,, +astroid/brain/__pycache__/brain_subprocess.cpython-310.pyc,, +astroid/brain/__pycache__/brain_threading.cpython-310.pyc,, +astroid/brain/__pycache__/brain_type.cpython-310.pyc,, +astroid/brain/__pycache__/brain_typing.cpython-310.pyc,, +astroid/brain/__pycache__/brain_unittest.cpython-310.pyc,, +astroid/brain/__pycache__/brain_uuid.cpython-310.pyc,, +astroid/brain/__pycache__/helpers.cpython-310.pyc,, +astroid/brain/brain_argparse.py,sha256=ozU4z8-HTSX6tl6g2ysoqFGn6b8jK4Bt_NlAKl_jYyQ,1557 +astroid/brain/brain_attrs.py,sha256=PGFhUbL428Mc_cuyFgZIB2alt2ICfeaamWB-0C_kxAk,2857 +astroid/brain/brain_boto3.py,sha256=gZOkDoY1YZk24EpOMVQVj0MBuNfBWn_XfDmmlH4jyHU,1012 +astroid/brain/brain_builtin_inference.py,sha256=N-B5FxLAID8wztOTdiZEoc42qrQh5M8MrtSH4BaQwZs,34254 +astroid/brain/brain_collections.py,sha256=__vh_SUp3ymY68i_UYW1OMlAjDN-Hiv_NHdDQs98HV4,4416 +astroid/brain/brain_crypt.py,sha256=Zo66z94cQOL-HNOcAQSPNs_Ib_xNLLY9qEtxZ1VkbAM,863 +astroid/brain/brain_ctypes.py,sha256=55flnAIj_Th_eqqG1HRMDR5ctt74ww1vDsSF42F26Es,2660 +astroid/brain/brain_curses.py,sha256=XXyCeLASFFksO2zSbrZgB_AImGBlIGZ7Pgjdm04x-jg,3477 +astroid/brain/brain_dataclasses.py,sha256=81fglaiPlsUkbImjLpSIpdVCdmmZa8vYUcCKQFo9YAI,22089 +astroid/brain/brain_dateutil.py,sha256=1INt87VBa9Qt0DzEuI1r7cEtdyg5CvNDUIdVCv-j_mc,767 +astroid/brain/brain_fstrings.py,sha256=VU14-7tVsJWlZt0r3UMB8lPS-Bb72dyYcDvd1PmTPJM,2471 +astroid/brain/brain_functools.py,sha256=91kJd49eYZGh9fG45uBS4I6j635Q7VctZND-iqB2dRg,5986 +astroid/brain/brain_gi.py,sha256=LYtvbe48qmyugTP31CcrqfM6X0ra_h6ntTBuKGCIrcM,7543 +astroid/brain/brain_hashlib.py,sha256=WE7fQJCA3Rp1ZFDIseQjhrLEcsYhOfrka2PcvvuZ6Tg,2821 +astroid/brain/brain_http.py,sha256=mYLcMdh_ka5d11IpdWg6WrG3R7R2wN4bKOLVG0W3vPk,10640 +astroid/brain/brain_hypothesis.py,sha256=K4LfRb2KT6H5B7oGxAXQLcJFl9bkeKfFW8IEceGAbfw,1732 +astroid/brain/brain_io.py,sha256=l499fbVi5HUbPWoyQn1xTHaPgRcf0hsKjjI90BdHzyQ,1526 +astroid/brain/brain_mechanize.py,sha256=45syVjTGklYjS469vPsuHmyic09OTZtcJuM7FEXcMYI,2646 +astroid/brain/brain_multiprocessing.py,sha256=htHZ3zSyv4eqh6c-AFRPt2GlyyPb4Jg8OQfAolojR8Q,3211 +astroid/brain/brain_namedtuple_enum.py,sha256=j9GnadFsrVT6A-tz_w-HQcOBUHU4IiG09qKpJNSdA0s,22725 +astroid/brain/brain_nose.py,sha256=DCUwKzR14Se5tF-s54AMwFoRvhsHH_nf6agrh966VpQ,2320 +astroid/brain/brain_numpy_core_einsumfunc.py,sha256=CuZtZYZGDhPzV46l1xyNl_7RfMCaQ4bYyZgJ9f8KxcQ,825 +astroid/brain/brain_numpy_core_fromnumeric.py,sha256=3UFRtK5U51AVQNxawf9ElhFv2tizhj5RfyCSnPT3u54,732 +astroid/brain/brain_numpy_core_function_base.py,sha256=_k-WRQ_x0QEHhvr9IwuTLnpGoCYOVO0GAH7e3DyoHNQ,1298 +astroid/brain/brain_numpy_core_multiarray.py,sha256=NIxnvno-mhp-P6IKfyRaKOFtiHx8H9cEchjgx69oht0,4238 +astroid/brain/brain_numpy_core_numeric.py,sha256=VAc51Ud8QDujsh29e-RQK5SBcKW_jzjZIFmntCslCKU,1629 +astroid/brain/brain_numpy_core_numerictypes.py,sha256=HVJ_paQwjh2V8qz0Juei6b2ZRm_dmDJodiDKfAcE-mE,8546 +astroid/brain/brain_numpy_core_umath.py,sha256=2EhXmtiw8wQcdUtbnlLLTjHAOHpQ1c9BXHdc6kHJGbA,4893 +astroid/brain/brain_numpy_ma.py,sha256=1XHvZH1C3CEXTiRH3BJxpFiztgqBYEGd9JDFquGsUMk,896 +astroid/brain/brain_numpy_ndarray.py,sha256=HohawmatzpNMOJAhYbEPJGZdCTNmYO1Oz-IPxYDd5mM,8998 +astroid/brain/brain_numpy_random_mtrand.py,sha256=Wrpm9SX8kH0Xsk9b3qpR0rhs-MEb0I5aN4E-66wkLkM,3436 +astroid/brain/brain_numpy_utils.py,sha256=wgSRUgGdn8Io0AoTe6avll35oceRq2S6ZXqNviljvGI,2637 +astroid/brain/brain_pathlib.py,sha256=Ec8Xuo-4IIG2wH4875c8c2Xe_I0fqPbPqoKCJOttVlE,1542 +astroid/brain/brain_pkg_resources.py,sha256=W3Q4G6mYTYtJY9iWQv3vEO8_lpmLFAin9aQLtBRLxAc,2200 +astroid/brain/brain_pytest.py,sha256=suTDBHCJ_p4F7TGXjwVJAFtiAP-VlkFE2gncXjLRQKM,2223 +astroid/brain/brain_qt.py,sha256=qvexV4IFa4YKL0Nyo6qxmz_DfDDNwFVwBTJGADtVkiI,2808 +astroid/brain/brain_random.py,sha256=grXJrYL3rZpzm8SmuEnWkLwWNqWKtd5YmwbBoV6mjN4,2890 +astroid/brain/brain_re.py,sha256=aSU-HyEWsCrc1seiFohZw54ivcNtC-_AyiChJOSU-7Q,2870 +astroid/brain/brain_regex.py,sha256=UzW2lF2ctbdD8BX4fkC7PFtXDdGNUcdBKZx7sJiO4Z0,3362 +astroid/brain/brain_responses.py,sha256=Jn7jS8L26T5hnUwOcHX469FTkVs3_Yz8OiVRPAx3Bjo,1868 +astroid/brain/brain_scipy_signal.py,sha256=Mi93D6-j94tqhqDHCbabVs3_eDAhti88CL1_rOvnG_I,2276 +astroid/brain/brain_signal.py,sha256=18rOCgLVmM1rtc6BFYPx4qziSrfPt6CrP-A2fJyMWBY,3880 +astroid/brain/brain_six.py,sha256=MWQm81s9Zb_i1NIqY3MnXuawlsXqgK1BDj1vBDZ-MdE,7625 +astroid/brain/brain_sqlalchemy.py,sha256=onKAxgKY-GYLe2v6PVK3APBn_z8txokEZfDcXt33GuE,1009 +astroid/brain/brain_ssl.py,sha256=8MdXhl9ZvYVe6U_gtSTtgnm_Dned2Ic9MbgfSj56RHU,6554 +astroid/brain/brain_subprocess.py,sha256=Kayri4dMqwtXbZTOhB0E05p2ckKc7hIGPt1jA3zv2xA,2996 +astroid/brain/brain_threading.py,sha256=rN-dQ_4Zo0AZOOiPQigxF3NEgmDC7DY-r0ujLN6W52c,870 +astroid/brain/brain_type.py,sha256=VUwQsehFIShvKrm98ywa5-gGz7eLwJnBHCpQdreGOGM,2483 +astroid/brain/brain_typing.py,sha256=uficlx7AzFIkcBQKeOvmzg_75kxAZENTDilXTYv-rfE,14315 +astroid/brain/brain_unittest.py,sha256=X_xqI70br7JgIrUiUwP4Hm9mQwCx9jBfvNW5h0wmp-0,1147 +astroid/brain/brain_uuid.py,sha256=t0fj_QeNzbPga03GS0YP92sjjEmxN04rlgZ4dt82JHE,667 +astroid/brain/helpers.py,sha256=qtThXG2zllaFm8Ril8Mjbb6yuRPUqy-elXz4Qu8UKX0,908 +astroid/builder.py,sha256=bkCDciVprfLz3vyg6BfoTaUMe34tRB1n0z3hCXvoyLE,18788 +astroid/const.py,sha256=-C9gZEVAgp83YpjnG4k6BKBlTkn22J3UeR59IzPzC-s,1095 +astroid/constraint.py,sha256=RgYVUe0CiEXeXB0iGDd896CEDgCHpwALdzZEFogagwo,5043 +astroid/context.py,sha256=BwdZndGFMeP9ldxt-c4LP21N61ZGCFjySNCASiGHWhw,5994 +astroid/decorators.py,sha256=4_SEOBUSxU60_7SaOkACXvBiiwGxYVd7E6YJdhB-5AY,10090 +astroid/exceptions.py,sha256=wcJFoxkuWUslQMsjjBsvQadlTl-CCLin4I5Mg9EA4XQ,13089 +astroid/filter_statements.py,sha256=zd55vGal1W6k1DZRsZoTQodtTZCtKrmebUVkECeoUS4,9643 +astroid/helpers.py,sha256=2ppef_tEbjaFyvFb2QrNq36I8pNk-wxLDkpfwXN0dc8,11312 +astroid/inference.py,sha256=IJZGg3YLTIXfHxDuOnEdd5IAfdk7sTPWSGt8OXqEHpM,45121 +astroid/inference_tip.py,sha256=4ims3HAlCZwVybV1CyLxreDtPVNEnvta_OKb_ELAbyA,2888 +astroid/interpreter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/interpreter/__pycache__/__init__.cpython-310.pyc,, +astroid/interpreter/__pycache__/dunder_lookup.cpython-310.pyc,, +astroid/interpreter/__pycache__/objectmodel.cpython-310.pyc,, +astroid/interpreter/_import/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +astroid/interpreter/_import/__pycache__/__init__.cpython-310.pyc,, +astroid/interpreter/_import/__pycache__/spec.cpython-310.pyc,, +astroid/interpreter/_import/__pycache__/util.cpython-310.pyc,, +astroid/interpreter/_import/spec.py,sha256=1s6GkO0yVgdtXHJAhD8v8W27ZmtkfT5p-mAKf8v4xmY,16381 +astroid/interpreter/_import/util.py,sha256=b5ggIRSth_I5Uwv3s44jMwf3OlnFAWIV-57wX3XK9yM,4592 +astroid/interpreter/dunder_lookup.py,sha256=77YNmgp6l2sPA7UIaUpjaP6-4x26NP93IihPR5EEv-c,2172 +astroid/interpreter/objectmodel.py,sha256=SVtDFz0kAlHgkRTcIp4r3t2UEjeJLE2jXSxZdo8QhXw,31177 +astroid/manager.py,sha256=g17lbC6NM9ieO3hZVJLtwE6Gb_JlCsEJJ42YtK7B27Q,17598 +astroid/mixins.py,sha256=4fuPIi4N5ucNbocjVrS5UpEc_6Jkn3gsYD--JG85TII,1182 +astroid/modutils.py,sha256=nmwwzrvSGIl4DRHC6WoyGPDkDtKgyKpcHSidjw_B8UU,23481 +astroid/node_classes.py,sha256=ws0UW2SFejWF75iHfxx-u80XQt4beDAiDyabWrOwlPE,1827 +astroid/nodes/__init__.py,sha256=Uuli4TfBDzdUTPvVvjp-aEB0SpMv-6gdoWajJt1fguo,5043 +astroid/nodes/__pycache__/__init__.cpython-310.pyc,, +astroid/nodes/__pycache__/_base_nodes.cpython-310.pyc,, +astroid/nodes/__pycache__/as_string.cpython-310.pyc,, +astroid/nodes/__pycache__/const.cpython-310.pyc,, +astroid/nodes/__pycache__/node_classes.cpython-310.pyc,, +astroid/nodes/__pycache__/node_ng.cpython-310.pyc,, +astroid/nodes/__pycache__/utils.cpython-310.pyc,, +astroid/nodes/_base_nodes.py,sha256=oQ4ueaxb5-JAnG1w17lK8TRo4xs3KatHrY985M0qgss,7398 +astroid/nodes/as_string.py,sha256=i4s5M3Ghaef0d-Jgqi5NkPz8AYcXG3ZL7N3HPYZsp_0,24451 +astroid/nodes/const.py,sha256=iyQn_v-wEzK6uXc-dEMoliPVSRy0NZ8T3XmvvPXLuP4,797 +astroid/nodes/node_classes.py,sha256=vmiMXFjaRS7o8ag4yShavJxfyd3CIh9xOfJiORxxQ2M,172738 +astroid/nodes/node_ng.py,sha256=pqrceCAgS64qDmFGBghjWnneodH6P9KYsUFhsi_9Aq8,28223 +astroid/nodes/scoped_nodes/__init__.py,sha256=CRdThe_LiW7SgcJz3pHSL8oFqHKNIJWg1WkQOkws994,1219 +astroid/nodes/scoped_nodes/__pycache__/__init__.cpython-310.pyc,, +astroid/nodes/scoped_nodes/__pycache__/mixin.cpython-310.pyc,, +astroid/nodes/scoped_nodes/__pycache__/scoped_nodes.cpython-310.pyc,, +astroid/nodes/scoped_nodes/__pycache__/utils.cpython-310.pyc,, +astroid/nodes/scoped_nodes/mixin.py,sha256=mN3oqKpS2AWOtrBjHDDfReVO7OxAMhl_es-jzsn89Js,6917 +astroid/nodes/scoped_nodes/scoped_nodes.py,sha256=DdqlbBVNPoQbP3AE8nJGsnHjMH0uU92Tp32B8F87v1w,106494 +astroid/nodes/scoped_nodes/utils.py,sha256=Nnjdkcbm9zs72HcoV9b_hE6P-SLZ7MCB9urU-WUDNVg,1216 +astroid/nodes/utils.py,sha256=Uy6xoownLyWwDtuZ5qUpjbq19P2pUIcj0J04khbH5HY,423 +astroid/objects.py,sha256=8roM53sWwFzusAIvz3nOj0LaQEXdzJw0ANfb6mDMy5w,12757 +astroid/protocols.py,sha256=dkQszq5bOlJ7Jlyu4hdF8l7sv7hVfH7glKWVblf3bkU,32684 +astroid/raw_building.py,sha256=TI55TaZL35Zi3bk2DThToKEQ8rMOhZCgAMayfktxbAM,22875 +astroid/rebuilder.py,sha256=ura2IJRixG6FQjgHFXP3-XrGfhPKoW6t1nGXuHl8lQs,79566 +astroid/scoped_nodes.py,sha256=j51jEOiN1rkMqmrhvVtYje8d-EWaGbzrgCwvg_s4TE0,958 +astroid/test_utils.py,sha256=7wyVL8A8WYQXbix1mT05lh8yO4kXk1gRjCCuqwfV4f8,2434 +astroid/transforms.py,sha256=eg7b0lehQu_GsFHCsLdUsNNvbxYBGcTC0p7rF2EKIXc,3271 +astroid/typing.py,sha256=OTOZbQ7tHSMq1tlASsVgRMC90GTjFoVSCgevZarAwhI,1983 +astroid/util.py,sha256=T5ux2LDjSNv2et5blQuUU7xCBr1id-CeU8lorr-yAVo,4729 diff --git a/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/WHEEL new file mode 120000 index 00000000..0df5e3de --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/WHEEL @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/db/07/a9/3359e4e034b8785a58ad6d534ea3dca0635f1e184efe2e66e1c3a299ba \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/top_level.txt new file mode 120000 index 00000000..62af17c1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid-2.15.0.dist-info/top_level.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/1e/c7/56/e0edb1ed95d18fa93e6a08b745a4326c62e86c8dd5484fa3b78bd05173 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/__init__.py b/venv/lib/python3.10/site-packages/astroid/__init__.py new file mode 120000 index 00000000..83a7cc97 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/1c/a1/e7/720ae3ef4a92b3d1a0e1a0e91728804051e1ad2a11d4edb4390dcad01a \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/__pkginfo__.py b/venv/lib/python3.10/site-packages/astroid/__pkginfo__.py new file mode 100644 index 00000000..3f1d1a29 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/__pkginfo__.py @@ -0,0 +1,6 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +__version__ = "2.15.0" +version = __version__ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..3cd9aca3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/__pkginfo__.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/__pkginfo__.cpython-310.pyc new file mode 100644 index 00000000..55fdea39 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/__pkginfo__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/_ast.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/_ast.cpython-310.pyc new file mode 100644 index 00000000..b39ab8f7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/_ast.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/_backport_stdlib_names.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/_backport_stdlib_names.cpython-310.pyc new file mode 100644 index 00000000..d3deae4a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/_backport_stdlib_names.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/_cache.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/_cache.cpython-310.pyc new file mode 100644 index 00000000..dd32893d Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/_cache.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/arguments.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/arguments.cpython-310.pyc new file mode 100644 index 00000000..2af7c204 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/arguments.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/astroid_manager.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/astroid_manager.cpython-310.pyc new file mode 100644 index 00000000..ff2c57da Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/astroid_manager.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/bases.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/bases.cpython-310.pyc new file mode 100644 index 00000000..b38ee54e Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/bases.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/builder.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/builder.cpython-310.pyc new file mode 100644 index 00000000..62ea1b4d Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/builder.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/const.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/const.cpython-310.pyc new file mode 100644 index 00000000..bf57a5fe Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/const.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/constraint.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/constraint.cpython-310.pyc new file mode 100644 index 00000000..c88d4c1e Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/constraint.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/context.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/context.cpython-310.pyc new file mode 100644 index 00000000..1000a3e5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/context.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/decorators.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/decorators.cpython-310.pyc new file mode 100644 index 00000000..5c54e9b2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/decorators.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/exceptions.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 00000000..b90221f0 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/exceptions.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/filter_statements.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/filter_statements.cpython-310.pyc new file mode 100644 index 00000000..d94fb3a9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/filter_statements.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/helpers.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/helpers.cpython-310.pyc new file mode 100644 index 00000000..b64f5a98 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/helpers.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/inference.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/inference.cpython-310.pyc new file mode 100644 index 00000000..9c0764bf Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/inference.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/inference_tip.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/inference_tip.cpython-310.pyc new file mode 100644 index 00000000..cb968879 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/inference_tip.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/manager.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/manager.cpython-310.pyc new file mode 100644 index 00000000..77e7f427 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/manager.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/mixins.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/mixins.cpython-310.pyc new file mode 100644 index 00000000..1e80b14a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/mixins.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/modutils.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/modutils.cpython-310.pyc new file mode 100644 index 00000000..be3cac2a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/modutils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/node_classes.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/node_classes.cpython-310.pyc new file mode 100644 index 00000000..59e30931 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/node_classes.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/objects.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/objects.cpython-310.pyc new file mode 100644 index 00000000..3779fa59 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/objects.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/protocols.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/protocols.cpython-310.pyc new file mode 100644 index 00000000..3845cdcf Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/protocols.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/raw_building.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/raw_building.cpython-310.pyc new file mode 100644 index 00000000..b7a795a4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/raw_building.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/rebuilder.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/rebuilder.cpython-310.pyc new file mode 100644 index 00000000..64e6e098 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/rebuilder.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/scoped_nodes.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/scoped_nodes.cpython-310.pyc new file mode 100644 index 00000000..b6c26851 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/scoped_nodes.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/test_utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/test_utils.cpython-310.pyc new file mode 100644 index 00000000..135b9e2f Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/test_utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/transforms.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/transforms.cpython-310.pyc new file mode 100644 index 00000000..3e2ab4e9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/transforms.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/typing.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/typing.cpython-310.pyc new file mode 100644 index 00000000..108cc634 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/typing.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/__pycache__/util.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/__pycache__/util.cpython-310.pyc new file mode 100644 index 00000000..dafa2c0f Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/__pycache__/util.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/_ast.py b/venv/lib/python3.10/site-packages/astroid/_ast.py new file mode 120000 index 00000000..5e7b15be --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/_ast.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/7f/1e/af/0a3ed824945049fdc6418465949f6144a3e1f7e41ccd4ddcae3b7722b0 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/_backport_stdlib_names.py b/venv/lib/python3.10/site-packages/astroid/_backport_stdlib_names.py new file mode 100644 index 00000000..51d6957d --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/_backport_stdlib_names.py @@ -0,0 +1,356 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +""" +Shim to support Python versions < 3.10 that don't have sys.stdlib_module_names + +These values were created by cherry-picking the commits from +https://bugs.python.org/issue42955 into each version, but may be updated +manually if changes are needed. +""" + +import sys + +# TODO: Remove this file when Python 3.9 is no longer supported + +PY_3_7 = frozenset( + { + "__future__", + "_abc", + "_ast", + "_asyncio", + "_bisect", + "_blake2", + "_bootlocale", + "_bz2", + "_codecs", + "_codecs_cn", + "_codecs_hk", + "_codecs_iso2022", + "_codecs_jp", + "_codecs_kr", + "_codecs_tw", + "_collections", + "_collections_abc", + "_compat_pickle", + "_compression", + "_contextvars", + "_crypt", + "_csv", + "_ctypes", + "_curses", + "_curses_panel", + "_datetime", + "_dbm", + "_decimal", + "_dummy_thread", + "_elementtree", + "_functools", + "_gdbm", + "_hashlib", + "_heapq", + "_imp", + "_io", + "_json", + "_locale", + "_lsprof", + "_lzma", + "_markupbase", + "_md5", + "_msi", + "_multibytecodec", + "_multiprocessing", + "_opcode", + "_operator", + "_osx_support", + "_pickle", + "_posixsubprocess", + "_py_abc", + "_pydecimal", + "_pyio", + "_queue", + "_random", + "_sha1", + "_sha256", + "_sha3", + "_sha512", + "_signal", + "_sitebuiltins", + "_socket", + "_sqlite3", + "_sre", + "_ssl", + "_stat", + "_string", + "_strptime", + "_struct", + "_symtable", + "_thread", + "_threading_local", + "_tkinter", + "_tracemalloc", + "_uuid", + "_warnings", + "_weakref", + "_weakrefset", + "_winapi", + "abc", + "aifc", + "antigravity", + "argparse", + "array", + "ast", + "asynchat", + "asyncio", + "asyncore", + "atexit", + "audioop", + "base64", + "bdb", + "binascii", + "binhex", + "bisect", + "builtins", + "bz2", + "cProfile", + "calendar", + "cgi", + "cgitb", + "chunk", + "cmath", + "cmd", + "code", + "codecs", + "codeop", + "collections", + "colorsys", + "compileall", + "concurrent", + "configparser", + "contextlib", + "contextvars", + "copy", + "copyreg", + "crypt", + "csv", + "ctypes", + "curses", + "dataclasses", + "datetime", + "dbm", + "decimal", + "difflib", + "dis", + "distutils", + "doctest", + "dummy_threading", + "email", + "encodings", + "ensurepip", + "enum", + "errno", + "faulthandler", + "fcntl", + "filecmp", + "fileinput", + "fnmatch", + "formatter", + "fractions", + "ftplib", + "functools", + "gc", + "genericpath", + "getopt", + "getpass", + "gettext", + "glob", + "grp", + "gzip", + "hashlib", + "heapq", + "hmac", + "html", + "http", + "idlelib", + "imaplib", + "imghdr", + "imp", + "importlib", + "inspect", + "io", + "ipaddress", + "itertools", + "json", + "keyword", + "lib2to3", + "linecache", + "locale", + "logging", + "lzma", + "macpath", + "mailbox", + "mailcap", + "marshal", + "math", + "mimetypes", + "mmap", + "modulefinder", + "msilib", + "msvcrt", + "multiprocessing", + "netrc", + "nis", + "nntplib", + "nt", + "ntpath", + "nturl2path", + "numbers", + "opcode", + "operator", + "optparse", + "os", + "ossaudiodev", + "parser", + "pathlib", + "pdb", + "pickle", + "pickletools", + "pipes", + "pkgutil", + "platform", + "plistlib", + "poplib", + "posix", + "posixpath", + "pprint", + "profile", + "pstats", + "pty", + "pwd", + "py_compile", + "pyclbr", + "pydoc", + "pydoc_data", + "pyexpat", + "queue", + "quopri", + "random", + "re", + "readline", + "reprlib", + "resource", + "rlcompleter", + "runpy", + "sched", + "secrets", + "select", + "selectors", + "shelve", + "shlex", + "shutil", + "signal", + "site", + "smtpd", + "smtplib", + "sndhdr", + "socket", + "socketserver", + "spwd", + "sqlite3", + "sre_compile", + "sre_constants", + "sre_parse", + "ssl", + "stat", + "statistics", + "string", + "stringprep", + "struct", + "subprocess", + "sunau", + "symbol", + "symtable", + "sys", + "sysconfig", + "syslog", + "tabnanny", + "tarfile", + "telnetlib", + "tempfile", + "termios", + "textwrap", + "this", + "threading", + "time", + "timeit", + "tkinter", + "token", + "tokenize", + "trace", + "traceback", + "tracemalloc", + "tty", + "turtle", + "turtledemo", + "types", + "typing", + "unicodedata", + "unittest", + "urllib", + "uu", + "uuid", + "venv", + "warnings", + "wave", + "weakref", + "webbrowser", + "winreg", + "winsound", + "wsgiref", + "xdrlib", + "xml", + "xmlrpc", + "zipapp", + "zipfile", + "zipimport", + "zlib", + } +) + +PY_3_8 = frozenset( + PY_3_7 + - { + "macpath", + } + | { + "_posixshmem", + "_statistics", + "_xxsubinterpreters", + } +) + +PY_3_9 = frozenset( + PY_3_8 + - { + "_dummy_thread", + "dummy_threading", + } + | { + "_aix_support", + "_bootsubprocess", + "_peg_parser", + "_zoneinfo", + "graphlib", + "zoneinfo", + } +) + +if sys.version_info[:2] == (3, 7): + stdlib_module_names = PY_3_7 +elif sys.version_info[:2] == (3, 8): + stdlib_module_names = PY_3_8 +elif sys.version_info[:2] == (3, 9): + stdlib_module_names = PY_3_9 +else: + raise AssertionError("This module is only intended as a backport for Python <= 3.9") diff --git a/venv/lib/python3.10/site-packages/astroid/_cache.py b/venv/lib/python3.10/site-packages/astroid/_cache.py new file mode 120000 index 00000000..38254cd9 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/_cache.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/66/e3/b5/2a5f475dac6827f8495dbcd6aa430699047b0867fe47870f7a7f687733 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/arguments.py b/venv/lib/python3.10/site-packages/astroid/arguments.py new file mode 100644 index 00000000..59369957 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/arguments.py @@ -0,0 +1,309 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +from __future__ import annotations + +from astroid import nodes +from astroid.bases import Instance +from astroid.context import CallContext, InferenceContext +from astroid.exceptions import InferenceError, NoDefault +from astroid.util import Uninferable, UninferableBase + + +class CallSite: + """Class for understanding arguments passed into a call site. + + It needs a call context, which contains the arguments and the + keyword arguments that were passed into a given call site. + In order to infer what an argument represents, call :meth:`infer_argument` + with the corresponding function node and the argument name. + + :param callcontext: + An instance of :class:`astroid.context.CallContext`, that holds + the arguments for the call site. + :param argument_context_map: + Additional contexts per node, passed in from :attr:`astroid.context.Context.extra_context` + :param context: + An instance of :class:`astroid.context.Context`. + """ + + def __init__( + self, + callcontext: CallContext, + argument_context_map=None, + context: InferenceContext | None = None, + ): + if argument_context_map is None: + argument_context_map = {} + self.argument_context_map = argument_context_map + args = callcontext.args + keywords = callcontext.keywords + self.duplicated_keywords: set[str] = set() + self._unpacked_args = self._unpack_args(args, context=context) + self._unpacked_kwargs = self._unpack_keywords(keywords, context=context) + + self.positional_arguments = [ + arg for arg in self._unpacked_args if not isinstance(arg, UninferableBase) + ] + self.keyword_arguments = { + key: value + for key, value in self._unpacked_kwargs.items() + if not isinstance(value, UninferableBase) + } + + @classmethod + def from_call(cls, call_node, context: InferenceContext | None = None): + """Get a CallSite object from the given Call node. + + context will be used to force a single inference path. + """ + + # Determine the callcontext from the given `context` object if any. + context = context or InferenceContext() + callcontext = CallContext(call_node.args, call_node.keywords) + return cls(callcontext, context=context) + + def has_invalid_arguments(self): + """Check if in the current CallSite were passed *invalid* arguments. + + This can mean multiple things. For instance, if an unpacking + of an invalid object was passed, then this method will return True. + Other cases can be when the arguments can't be inferred by astroid, + for example, by passing objects which aren't known statically. + """ + return len(self.positional_arguments) != len(self._unpacked_args) + + def has_invalid_keywords(self) -> bool: + """Check if in the current CallSite were passed *invalid* keyword arguments. + + For instance, unpacking a dictionary with integer keys is invalid + (**{1:2}), because the keys must be strings, which will make this + method to return True. Other cases where this might return True if + objects which can't be inferred were passed. + """ + return len(self.keyword_arguments) != len(self._unpacked_kwargs) + + def _unpack_keywords(self, keywords, context: InferenceContext | None = None): + values = {} + context = context or InferenceContext() + context.extra_context = self.argument_context_map + for name, value in keywords: + if name is None: + # Then it's an unpacking operation (**) + try: + inferred = next(value.infer(context=context)) + except InferenceError: + values[name] = Uninferable + continue + except StopIteration: + continue + + if not isinstance(inferred, nodes.Dict): + # Not something we can work with. + values[name] = Uninferable + continue + + for dict_key, dict_value in inferred.items: + try: + dict_key = next(dict_key.infer(context=context)) + except InferenceError: + values[name] = Uninferable + continue + except StopIteration: + continue + if not isinstance(dict_key, nodes.Const): + values[name] = Uninferable + continue + if not isinstance(dict_key.value, str): + values[name] = Uninferable + continue + if dict_key.value in values: + # The name is already in the dictionary + values[dict_key.value] = Uninferable + self.duplicated_keywords.add(dict_key.value) + continue + values[dict_key.value] = dict_value + else: + values[name] = value + return values + + def _unpack_args(self, args, context: InferenceContext | None = None): + values = [] + context = context or InferenceContext() + context.extra_context = self.argument_context_map + for arg in args: + if isinstance(arg, nodes.Starred): + try: + inferred = next(arg.value.infer(context=context)) + except InferenceError: + values.append(Uninferable) + continue + except StopIteration: + continue + + if isinstance(inferred, UninferableBase): + values.append(Uninferable) + continue + if not hasattr(inferred, "elts"): + values.append(Uninferable) + continue + values.extend(inferred.elts) + else: + values.append(arg) + return values + + def infer_argument(self, funcnode, name, context): # noqa: C901 + """Infer a function argument value according to the call context. + + Arguments: + funcnode: The function being called. + name: The name of the argument whose value is being inferred. + context: Inference context object + """ + if name in self.duplicated_keywords: + raise InferenceError( + "The arguments passed to {func!r} have duplicate keywords.", + call_site=self, + func=funcnode, + arg=name, + context=context, + ) + + # Look into the keywords first, maybe it's already there. + try: + return self.keyword_arguments[name].infer(context) + except KeyError: + pass + + # Too many arguments given and no variable arguments. + if len(self.positional_arguments) > len(funcnode.args.args): + if not funcnode.args.vararg and not funcnode.args.posonlyargs: + raise InferenceError( + "Too many positional arguments " + "passed to {func!r} that does " + "not have *args.", + call_site=self, + func=funcnode, + arg=name, + context=context, + ) + + positional = self.positional_arguments[: len(funcnode.args.args)] + vararg = self.positional_arguments[len(funcnode.args.args) :] + argindex = funcnode.args.find_argname(name)[0] + kwonlyargs = {arg.name for arg in funcnode.args.kwonlyargs} + kwargs = { + key: value + for key, value in self.keyword_arguments.items() + if key not in kwonlyargs + } + # If there are too few positionals compared to + # what the function expects to receive, check to see + # if the missing positional arguments were passed + # as keyword arguments and if so, place them into the + # positional args list. + if len(positional) < len(funcnode.args.args): + for func_arg in funcnode.args.args: + if func_arg.name in kwargs: + arg = kwargs.pop(func_arg.name) + positional.append(arg) + + if argindex is not None: + boundnode = getattr(context, "boundnode", None) + # 2. first argument of instance/class method + if argindex == 0 and funcnode.type in {"method", "classmethod"}: + # context.boundnode is None when an instance method is called with + # the class, e.g. MyClass.method(obj, ...). In this case, self + # is the first argument. + if boundnode is None and funcnode.type == "method" and positional: + return positional[0].infer(context=context) + if boundnode is None: + # XXX can do better ? + boundnode = funcnode.parent.frame(future=True) + + if isinstance(boundnode, nodes.ClassDef): + # Verify that we're accessing a method + # of the metaclass through a class, as in + # `cls.metaclass_method`. In this case, the + # first argument is always the class. + method_scope = funcnode.parent.scope() + if method_scope is boundnode.metaclass(): + return iter((boundnode,)) + + if funcnode.type == "method": + if not isinstance(boundnode, Instance): + boundnode = boundnode.instantiate_class() + return iter((boundnode,)) + if funcnode.type == "classmethod": + return iter((boundnode,)) + # if we have a method, extract one position + # from the index, so we'll take in account + # the extra parameter represented by `self` or `cls` + if funcnode.type in {"method", "classmethod"} and boundnode: + argindex -= 1 + # 2. search arg index + try: + return self.positional_arguments[argindex].infer(context) + except IndexError: + pass + + if funcnode.args.kwarg == name: + # It wants all the keywords that were passed into + # the call site. + if self.has_invalid_keywords(): + raise InferenceError( + "Inference failed to find values for all keyword arguments " + "to {func!r}: {unpacked_kwargs!r} doesn't correspond to " + "{keyword_arguments!r}.", + keyword_arguments=self.keyword_arguments, + unpacked_kwargs=self._unpacked_kwargs, + call_site=self, + func=funcnode, + arg=name, + context=context, + ) + kwarg = nodes.Dict( + lineno=funcnode.args.lineno, + col_offset=funcnode.args.col_offset, + parent=funcnode.args, + ) + kwarg.postinit( + [(nodes.const_factory(key), value) for key, value in kwargs.items()] + ) + return iter((kwarg,)) + if funcnode.args.vararg == name: + # It wants all the args that were passed into + # the call site. + if self.has_invalid_arguments(): + raise InferenceError( + "Inference failed to find values for all positional " + "arguments to {func!r}: {unpacked_args!r} doesn't " + "correspond to {positional_arguments!r}.", + positional_arguments=self.positional_arguments, + unpacked_args=self._unpacked_args, + call_site=self, + func=funcnode, + arg=name, + context=context, + ) + args = nodes.Tuple( + lineno=funcnode.args.lineno, + col_offset=funcnode.args.col_offset, + parent=funcnode.args, + ) + args.postinit(vararg) + return iter((args,)) + + # Check if it's a default parameter. + try: + return funcnode.args.default_value(name).infer(context) + except NoDefault: + pass + raise InferenceError( + "No value found for argument {arg} to {func!r}", + call_site=self, + func=funcnode, + arg=name, + context=context, + ) diff --git a/venv/lib/python3.10/site-packages/astroid/astroid_manager.py b/venv/lib/python3.10/site-packages/astroid/astroid_manager.py new file mode 120000 index 00000000..7ef1b440 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/astroid_manager.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/d3/69/7f/96d19612096f9282ffa8c1a6631b87a43039fb248f7f4af5706c0842a5 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/bases.py b/venv/lib/python3.10/site-packages/astroid/bases.py new file mode 100644 index 00000000..d1972c17 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/bases.py @@ -0,0 +1,711 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""This module contains base classes and functions for the nodes and some +inference utils. +""" +from __future__ import annotations + +import collections +import collections.abc +import sys +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any, ClassVar + +from astroid import decorators, nodes +from astroid.const import PY310_PLUS +from astroid.context import ( + CallContext, + InferenceContext, + bind_context_to_node, + copy_context, +) +from astroid.exceptions import ( + AstroidTypeError, + AttributeInferenceError, + InferenceError, + NameInferenceError, +) +from astroid.typing import InferBinaryOp, InferenceErrorInfo, InferenceResult +from astroid.util import Uninferable, UninferableBase, lazy_descriptor, lazy_import + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + +if TYPE_CHECKING: + from astroid.constraint import Constraint + +objectmodel = lazy_import("interpreter.objectmodel") +helpers = lazy_import("helpers") +manager = lazy_import("manager") + + +# TODO: check if needs special treatment +BOOL_SPECIAL_METHOD = "__bool__" +BUILTINS = "builtins" # TODO Remove in 2.8 + +PROPERTIES = {"builtins.property", "abc.abstractproperty"} +if PY310_PLUS: + PROPERTIES.add("enum.property") + +# List of possible property names. We use this list in order +# to see if a method is a property or not. This should be +# pretty reliable and fast, the alternative being to check each +# decorator to see if its a real property-like descriptor, which +# can be too complicated. +# Also, these aren't qualified, because each project can +# define them, we shouldn't expect to know every possible +# property-like decorator! +POSSIBLE_PROPERTIES = { + "cached_property", + "cachedproperty", + "lazyproperty", + "lazy_property", + "reify", + "lazyattribute", + "lazy_attribute", + "LazyProperty", + "lazy", + "cache_readonly", + "DynamicClassAttribute", +} + + +def _is_property(meth, context: InferenceContext | None = None) -> bool: + decoratornames = meth.decoratornames(context=context) + if PROPERTIES.intersection(decoratornames): + return True + stripped = { + name.split(".")[-1] + for name in decoratornames + if not isinstance(name, UninferableBase) + } + if any(name in stripped for name in POSSIBLE_PROPERTIES): + return True + + # Lookup for subclasses of *property* + if not meth.decorators: + return False + for decorator in meth.decorators.nodes or (): + inferred = helpers.safe_infer(decorator, context=context) + if inferred is None or isinstance(inferred, UninferableBase): + continue + if inferred.__class__.__name__ == "ClassDef": + for base_class in inferred.bases: + if base_class.__class__.__name__ != "Name": + continue + module, _ = base_class.lookup(base_class.name) + if module.name == "builtins" and base_class.name == "property": + return True + + return False + + +class Proxy: + """A simple proxy object. + + Note: + + Subclasses of this object will need a custom __getattr__ + if new instance attributes are created. See the Const class + """ + + _proxied: nodes.ClassDef | nodes.Lambda | Proxy | None = ( + None # proxied object may be set by class or by instance + ) + + def __init__( + self, proxied: nodes.ClassDef | nodes.Lambda | Proxy | None = None + ) -> None: + if proxied is None: + # This is a hack to allow calling this __init__ during bootstrapping of + # builtin classes and their docstrings. + # For Const, Generator, and UnionType nodes the _proxied attribute + # is set during bootstrapping + # as we first need to build the ClassDef that they can proxy. + # Thus, if proxied is None self should be a Const or Generator + # as that is the only way _proxied will be correctly set as a ClassDef. + assert isinstance(self, (nodes.Const, Generator, UnionType)) + else: + self._proxied = proxied + + def __getattr__(self, name): + if name == "_proxied": + return self.__class__._proxied + if name in self.__dict__: + return self.__dict__[name] + return getattr(self._proxied, name) + + def infer( # type: ignore[return] + self, context: InferenceContext | None = None, **kwargs: Any + ) -> collections.abc.Generator[InferenceResult, None, InferenceErrorInfo | None]: + yield self + + +def _infer_stmts( + stmts: Sequence[nodes.NodeNG | UninferableBase | Instance], + context: InferenceContext | None, + frame: nodes.NodeNG | Instance | None = None, +) -> collections.abc.Generator[InferenceResult, None, None]: + """Return an iterator on statements inferred by each statement in *stmts*.""" + inferred = False + constraint_failed = False + if context is not None: + name = context.lookupname + context = context.clone() + constraints = context.constraints.get(name, {}) + else: + name = None + constraints = {} + context = InferenceContext() + + for stmt in stmts: + if isinstance(stmt, UninferableBase): + yield stmt + inferred = True + continue + # 'context' is always InferenceContext and Instances get '_infer_name' from ClassDef + context.lookupname = stmt._infer_name(frame, name) # type: ignore[union-attr] + try: + stmt_constraints: set[Constraint] = set() + for constraint_stmt, potential_constraints in constraints.items(): + if not constraint_stmt.parent_of(stmt): + stmt_constraints.update(potential_constraints) + for inf in stmt.infer(context=context): + if all(constraint.satisfied_by(inf) for constraint in stmt_constraints): + yield inf + inferred = True + else: + constraint_failed = True + except NameInferenceError: + continue + except InferenceError: + yield Uninferable + inferred = True + + if not inferred and constraint_failed: + yield Uninferable + elif not inferred: + raise InferenceError( + "Inference failed for all members of {stmts!r}.", + stmts=stmts, + frame=frame, + context=context, + ) + + +def _infer_method_result_truth(instance, method_name, context): + # Get the method from the instance and try to infer + # its return's truth value. + meth = next(instance.igetattr(method_name, context=context), None) + if meth and hasattr(meth, "infer_call_result"): + if not meth.callable(): + return Uninferable + try: + context.callcontext = CallContext(args=[], callee=meth) + for value in meth.infer_call_result(instance, context=context): + if isinstance(value, UninferableBase): + return value + try: + inferred = next(value.infer(context=context)) + except StopIteration as e: + raise InferenceError(context=context) from e + return inferred.bool_value() + except InferenceError: + pass + return Uninferable + + +class BaseInstance(Proxy): + """An instance base class, which provides lookup methods for potential + instances. + """ + + special_attributes = None + + def display_type(self) -> str: + return "Instance of" + + def getattr(self, name, context: InferenceContext | None = None, lookupclass=True): + try: + values = self._proxied.instance_attr(name, context) + except AttributeInferenceError as exc: + if self.special_attributes and name in self.special_attributes: + return [self.special_attributes.lookup(name)] + + if lookupclass: + # Class attributes not available through the instance + # unless they are explicitly defined. + return self._proxied.getattr(name, context, class_context=False) + + raise AttributeInferenceError( + target=self, attribute=name, context=context + ) from exc + # since we've no context information, return matching class members as + # well + if lookupclass: + try: + return values + self._proxied.getattr( + name, context, class_context=False + ) + except AttributeInferenceError: + pass + return values + + def igetattr(self, name, context: InferenceContext | None = None): + """Inferred getattr.""" + if not context: + context = InferenceContext() + try: + context.lookupname = name + # avoid recursively inferring the same attr on the same class + if context.push(self._proxied): + raise InferenceError( + message="Cannot infer the same attribute again", + node=self, + context=context, + ) + + # XXX frame should be self._proxied, or not ? + get_attr = self.getattr(name, context, lookupclass=False) + yield from _infer_stmts( + self._wrap_attr(get_attr, context), context, frame=self + ) + except AttributeInferenceError: + try: + # fallback to class.igetattr since it has some logic to handle + # descriptors + # But only if the _proxied is the Class. + if self._proxied.__class__.__name__ != "ClassDef": + raise + attrs = self._proxied.igetattr(name, context, class_context=False) + yield from self._wrap_attr(attrs, context) + except AttributeInferenceError as error: + raise InferenceError(**vars(error)) from error + + def _wrap_attr(self, attrs, context: InferenceContext | None = None): + """Wrap bound methods of attrs in a InstanceMethod proxies.""" + for attr in attrs: + if isinstance(attr, UnboundMethod): + if _is_property(attr): + yield from attr.infer_call_result(self, context) + else: + yield BoundMethod(attr, self) + elif hasattr(attr, "name") and attr.name == "": + if attr.args.arguments and attr.args.arguments[0].name == "self": + yield BoundMethod(attr, self) + continue + yield attr + else: + yield attr + + def infer_call_result( + self, caller: nodes.Call | Proxy, context: InferenceContext | None = None + ): + """Infer what a class instance is returning when called.""" + context = bind_context_to_node(context, self) + inferred = False + + # If the call is an attribute on the instance, we infer the attribute itself + if isinstance(caller, nodes.Call) and isinstance(caller.func, nodes.Attribute): + for res in self.igetattr(caller.func.attrname, context): + inferred = True + yield res + + # Otherwise we infer the call to the __call__ dunder normally + for node in self._proxied.igetattr("__call__", context): + if isinstance(node, UninferableBase) or not node.callable(): + continue + for res in node.infer_call_result(caller, context): + inferred = True + yield res + if not inferred: + raise InferenceError(node=self, caller=caller, context=context) + + +class Instance(BaseInstance): + """A special node representing a class instance.""" + + _proxied: nodes.ClassDef + + # pylint: disable=unnecessary-lambda + special_attributes = lazy_descriptor(lambda: objectmodel.InstanceModel()) + + def __init__(self, proxied: nodes.ClassDef | None) -> None: + super().__init__(proxied) + + infer_binary_op: ClassVar[InferBinaryOp[Instance]] + + def __repr__(self) -> str: + return "".format( + self._proxied.root().name, self._proxied.name, id(self) + ) + + def __str__(self) -> str: + return f"Instance of {self._proxied.root().name}.{self._proxied.name}" + + def callable(self) -> bool: + try: + self._proxied.getattr("__call__", class_context=False) + return True + except AttributeInferenceError: + return False + + def pytype(self) -> str: + return self._proxied.qname() + + def display_type(self) -> str: + return "Instance of" + + def bool_value(self, context: InferenceContext | None = None): + """Infer the truth value for an Instance. + + The truth value of an instance is determined by these conditions: + + * if it implements __bool__ on Python 3 or __nonzero__ + on Python 2, then its bool value will be determined by + calling this special method and checking its result. + * when this method is not defined, __len__() is called, if it + is defined, and the object is considered true if its result is + nonzero. If a class defines neither __len__() nor __bool__(), + all its instances are considered true. + """ + context = context or InferenceContext() + context.boundnode = self + + try: + result = _infer_method_result_truth(self, BOOL_SPECIAL_METHOD, context) + except (InferenceError, AttributeInferenceError): + # Fallback to __len__. + try: + result = _infer_method_result_truth(self, "__len__", context) + except (AttributeInferenceError, InferenceError): + return True + return result + + def getitem(self, index, context: InferenceContext | None = None): + new_context = bind_context_to_node(context, self) + if not context: + context = new_context + method = next(self.igetattr("__getitem__", context=context), None) + # Create a new CallContext for providing index as an argument. + new_context.callcontext = CallContext(args=[index], callee=method) + if not isinstance(method, BoundMethod): + raise InferenceError( + "Could not find __getitem__ for {node!r}.", node=self, context=context + ) + if len(method.args.arguments) != 2: # (self, index) + raise AstroidTypeError( + "__getitem__ for {node!r} does not have correct signature", + node=self, + context=context, + ) + return next(method.infer_call_result(self, new_context), None) + + +class UnboundMethod(Proxy): + """A special node representing a method not bound to an instance.""" + + # pylint: disable=unnecessary-lambda + special_attributes = lazy_descriptor(lambda: objectmodel.UnboundMethodModel()) + + def __repr__(self) -> str: + frame = self._proxied.parent.frame(future=True) + return "<{} {} of {} at 0x{}".format( + self.__class__.__name__, self._proxied.name, frame.qname(), id(self) + ) + + def implicit_parameters(self) -> Literal[0]: + return 0 + + def is_bound(self) -> Literal[False]: + return False + + def getattr(self, name, context: InferenceContext | None = None): + if name in self.special_attributes: + return [self.special_attributes.lookup(name)] + return self._proxied.getattr(name, context) + + def igetattr(self, name, context: InferenceContext | None = None): + if name in self.special_attributes: + return iter((self.special_attributes.lookup(name),)) + return self._proxied.igetattr(name, context) + + def infer_call_result(self, caller, context): + """ + The boundnode of the regular context with a function called + on ``object.__new__`` will be of type ``object``, + which is incorrect for the argument in general. + If no context is given the ``object.__new__`` call argument will + be correctly inferred except when inside a call that requires + the additional context (such as a classmethod) of the boundnode + to determine which class the method was called from + """ + + # If we're unbound method __new__ of a builtin, the result is an + # instance of the class given as first argument. + if self._proxied.name == "__new__": + qname = self._proxied.parent.frame(future=True).qname() + # Avoid checking builtins.type: _infer_type_new_call() does more validation + if qname.startswith("builtins.") and qname != "builtins.type": + return self._infer_builtin_new(caller, context) + return self._proxied.infer_call_result(caller, context) + + def _infer_builtin_new( + self, + caller: nodes.Call, + context: InferenceContext, + ) -> collections.abc.Generator[ + nodes.Const | Instance | UninferableBase, None, None + ]: + if not caller.args: + return + # Attempt to create a constant + if len(caller.args) > 1: + value = None + if isinstance(caller.args[1], nodes.Const): + value = caller.args[1].value + else: + inferred_arg = next(caller.args[1].infer(), None) + if isinstance(inferred_arg, nodes.Const): + value = inferred_arg.value + if value is not None: + yield nodes.const_factory(value) + return + + node_context = context.extra_context.get(caller.args[0]) + for inferred in caller.args[0].infer(context=node_context): + if isinstance(inferred, UninferableBase): + yield inferred + if isinstance(inferred, nodes.ClassDef): + yield Instance(inferred) + raise InferenceError + + def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: + return True + + +class BoundMethod(UnboundMethod): + """A special node representing a method bound to an instance.""" + + # pylint: disable=unnecessary-lambda + special_attributes = lazy_descriptor(lambda: objectmodel.BoundMethodModel()) + + def __init__(self, proxy, bound): + super().__init__(proxy) + self.bound = bound + + def implicit_parameters(self) -> Literal[0, 1]: + if self.name == "__new__": + # __new__ acts as a classmethod but the class argument is not implicit. + return 0 + return 1 + + def is_bound(self) -> Literal[True]: + return True + + def _infer_type_new_call(self, caller, context): # noqa: C901 + """Try to infer what type.__new__(mcs, name, bases, attrs) returns. + + In order for such call to be valid, the metaclass needs to be + a subtype of ``type``, the name needs to be a string, the bases + needs to be a tuple of classes + """ + # pylint: disable=import-outside-toplevel; circular import + from astroid.nodes import Pass + + # Verify the metaclass + try: + mcs = next(caller.args[0].infer(context=context)) + except StopIteration as e: + raise InferenceError(context=context) from e + if mcs.__class__.__name__ != "ClassDef": + # Not a valid first argument. + return None + if not mcs.is_subtype_of("builtins.type"): + # Not a valid metaclass. + return None + + # Verify the name + try: + name = next(caller.args[1].infer(context=context)) + except StopIteration as e: + raise InferenceError(context=context) from e + if name.__class__.__name__ != "Const": + # Not a valid name, needs to be a const. + return None + if not isinstance(name.value, str): + # Needs to be a string. + return None + + # Verify the bases + try: + bases = next(caller.args[2].infer(context=context)) + except StopIteration as e: + raise InferenceError(context=context) from e + if bases.__class__.__name__ != "Tuple": + # Needs to be a tuple. + return None + try: + inferred_bases = [next(elt.infer(context=context)) for elt in bases.elts] + except StopIteration as e: + raise InferenceError(context=context) from e + if any(base.__class__.__name__ != "ClassDef" for base in inferred_bases): + # All the bases needs to be Classes + return None + + # Verify the attributes. + try: + attrs = next(caller.args[3].infer(context=context)) + except StopIteration as e: + raise InferenceError(context=context) from e + if attrs.__class__.__name__ != "Dict": + # Needs to be a dictionary. + return None + cls_locals = collections.defaultdict(list) + for key, value in attrs.items: + try: + key = next(key.infer(context=context)) + except StopIteration as e: + raise InferenceError(context=context) from e + try: + value = next(value.infer(context=context)) + except StopIteration as e: + raise InferenceError(context=context) from e + # Ignore non string keys + if key.__class__.__name__ == "Const" and isinstance(key.value, str): + cls_locals[key.value].append(value) + + # Build the class from now. + cls = mcs.__class__( + name=name.value, + lineno=caller.lineno, + col_offset=caller.col_offset, + parent=caller, + ) + empty = Pass() + cls.postinit( + bases=bases.elts, + body=[empty], + decorators=[], + newstyle=True, + metaclass=mcs, + keywords=[], + ) + cls.locals = cls_locals + return cls + + def infer_call_result(self, caller, context: InferenceContext | None = None): + context = bind_context_to_node(context, self.bound) + if ( + self.bound.__class__.__name__ == "ClassDef" + and self.bound.name == "type" + and self.name == "__new__" + and len(caller.args) == 4 + ): + # Check if we have a ``type.__new__(mcs, name, bases, attrs)`` call. + new_cls = self._infer_type_new_call(caller, context) + if new_cls: + return iter((new_cls,)) + + return super().infer_call_result(caller, context) + + def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: + return True + + +class Generator(BaseInstance): + """A special node representing a generator. + + Proxied class is set once for all in raw_building. + """ + + _proxied: nodes.ClassDef + + special_attributes = lazy_descriptor(objectmodel.GeneratorModel) + + def __init__( + self, parent=None, generator_initial_context: InferenceContext | None = None + ): + super().__init__() + self.parent = parent + self._call_context = copy_context(generator_initial_context) + + @decorators.cached + def infer_yield_types(self): + yield from self.parent.infer_yield_result(self._call_context) + + def callable(self) -> Literal[False]: + return False + + def pytype(self) -> Literal["builtins.generator"]: + return "builtins.generator" + + def display_type(self) -> str: + return "Generator" + + def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: + return True + + def __repr__(self) -> str: + return f"" + + def __str__(self) -> str: + return f"Generator({self._proxied.name})" + + +class AsyncGenerator(Generator): + """Special node representing an async generator.""" + + def pytype(self) -> Literal["builtins.async_generator"]: + return "builtins.async_generator" + + def display_type(self) -> str: + return "AsyncGenerator" + + def __repr__(self) -> str: + return f"" + + def __str__(self) -> str: + return f"AsyncGenerator({self._proxied.name})" + + +class UnionType(BaseInstance): + """Special node representing new style typing unions. + + Proxied class is set once for all in raw_building. + """ + + _proxied: nodes.ClassDef + + def __init__( + self, + left: UnionType | nodes.ClassDef | nodes.Const, + right: UnionType | nodes.ClassDef | nodes.Const, + parent: nodes.NodeNG | None = None, + ) -> None: + super().__init__() + self.parent = parent + self.left = left + self.right = right + + def callable(self) -> Literal[False]: + return False + + def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: + return True + + def pytype(self) -> Literal["types.UnionType"]: + return "types.UnionType" + + def display_type(self) -> str: + return "UnionType" + + def __repr__(self) -> str: + return f"" + + def __str__(self) -> str: + return f"UnionType({self._proxied.name})" diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__init__.py b/venv/lib/python3.10/site-packages/astroid/brain/__init__.py new file mode 120000 index 00000000..05b4099d --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e3/b0/c4/4298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..ba47c784 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_argparse.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_argparse.cpython-310.pyc new file mode 100644 index 00000000..b4cc6fb9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_argparse.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_attrs.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_attrs.cpython-310.pyc new file mode 100644 index 00000000..283dee1d Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_attrs.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_boto3.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_boto3.cpython-310.pyc new file mode 100644 index 00000000..4ed60d35 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_boto3.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_builtin_inference.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_builtin_inference.cpython-310.pyc new file mode 100644 index 00000000..5be2c555 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_builtin_inference.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_collections.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_collections.cpython-310.pyc new file mode 100644 index 00000000..057e53f5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_collections.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_crypt.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_crypt.cpython-310.pyc new file mode 100644 index 00000000..699cb912 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_crypt.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_ctypes.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_ctypes.cpython-310.pyc new file mode 100644 index 00000000..ecaf3ab7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_ctypes.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_curses.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_curses.cpython-310.pyc new file mode 100644 index 00000000..2ead2ee9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_curses.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_dataclasses.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_dataclasses.cpython-310.pyc new file mode 100644 index 00000000..0e411edb Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_dataclasses.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_dateutil.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_dateutil.cpython-310.pyc new file mode 100644 index 00000000..c6e99997 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_dateutil.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_fstrings.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_fstrings.cpython-310.pyc new file mode 100644 index 00000000..329c49ab Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_fstrings.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_functools.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_functools.cpython-310.pyc new file mode 100644 index 00000000..27834ac5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_functools.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_gi.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_gi.cpython-310.pyc new file mode 100644 index 00000000..322aebc7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_gi.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_hashlib.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_hashlib.cpython-310.pyc new file mode 100644 index 00000000..03a662c8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_hashlib.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_http.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_http.cpython-310.pyc new file mode 100644 index 00000000..c2724e86 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_http.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_hypothesis.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_hypothesis.cpython-310.pyc new file mode 100644 index 00000000..a5e195e2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_hypothesis.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_io.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_io.cpython-310.pyc new file mode 100644 index 00000000..5f770028 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_io.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_mechanize.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_mechanize.cpython-310.pyc new file mode 100644 index 00000000..ed855923 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_mechanize.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_multiprocessing.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_multiprocessing.cpython-310.pyc new file mode 100644 index 00000000..2978423b Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_multiprocessing.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_namedtuple_enum.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_namedtuple_enum.cpython-310.pyc new file mode 100644 index 00000000..a6cc743f Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_namedtuple_enum.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_nose.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_nose.cpython-310.pyc new file mode 100644 index 00000000..586c876a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_nose.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_einsumfunc.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_einsumfunc.cpython-310.pyc new file mode 100644 index 00000000..98fe9f0f Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_einsumfunc.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-310.pyc new file mode 100644 index 00000000..859b3214 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_fromnumeric.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-310.pyc new file mode 100644 index 00000000..7b2202a8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_function_base.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-310.pyc new file mode 100644 index 00000000..6ab13a7f Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_multiarray.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-310.pyc new file mode 100644 index 00000000..f8722bbf Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_numeric.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-310.pyc new file mode 100644 index 00000000..be384b8a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_numerictypes.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_umath.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_umath.cpython-310.pyc new file mode 100644 index 00000000..a9c8901d Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_core_umath.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_ma.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_ma.cpython-310.pyc new file mode 100644 index 00000000..c15ad878 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_ma.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_ndarray.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_ndarray.cpython-310.pyc new file mode 100644 index 00000000..bd746721 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_ndarray.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-310.pyc new file mode 100644 index 00000000..3750a15f Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_random_mtrand.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_utils.cpython-310.pyc new file mode 100644 index 00000000..e90ee85a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_numpy_utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_pathlib.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_pathlib.cpython-310.pyc new file mode 100644 index 00000000..2962617e Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_pathlib.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_pkg_resources.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_pkg_resources.cpython-310.pyc new file mode 100644 index 00000000..9768f7ab Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_pkg_resources.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_pytest.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_pytest.cpython-310.pyc new file mode 100644 index 00000000..098e0cb7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_pytest.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_qt.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_qt.cpython-310.pyc new file mode 100644 index 00000000..8fd83929 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_qt.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_random.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_random.cpython-310.pyc new file mode 100644 index 00000000..8b2cfe6a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_random.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_re.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_re.cpython-310.pyc new file mode 100644 index 00000000..01ddb5eb Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_re.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_regex.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_regex.cpython-310.pyc new file mode 100644 index 00000000..3f6e5ff8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_regex.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_responses.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_responses.cpython-310.pyc new file mode 100644 index 00000000..637fa415 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_responses.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_scipy_signal.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_scipy_signal.cpython-310.pyc new file mode 100644 index 00000000..0b627e43 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_scipy_signal.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_signal.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_signal.cpython-310.pyc new file mode 100644 index 00000000..7187b433 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_signal.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_six.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_six.cpython-310.pyc new file mode 100644 index 00000000..68579223 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_six.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_sqlalchemy.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_sqlalchemy.cpython-310.pyc new file mode 100644 index 00000000..37a9b792 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_sqlalchemy.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_ssl.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_ssl.cpython-310.pyc new file mode 100644 index 00000000..410b8197 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_ssl.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_subprocess.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_subprocess.cpython-310.pyc new file mode 100644 index 00000000..6f0c84ff Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_subprocess.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_threading.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_threading.cpython-310.pyc new file mode 100644 index 00000000..87c3c9f8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_threading.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_type.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_type.cpython-310.pyc new file mode 100644 index 00000000..c6b83b72 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_type.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_typing.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_typing.cpython-310.pyc new file mode 100644 index 00000000..63736b7a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_typing.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_unittest.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_unittest.cpython-310.pyc new file mode 100644 index 00000000..11e102b8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_unittest.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_uuid.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_uuid.cpython-310.pyc new file mode 100644 index 00000000..91b1a07e Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/brain_uuid.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/helpers.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/helpers.cpython-310.pyc new file mode 100644 index 00000000..5a35af06 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/brain/__pycache__/helpers.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py new file mode 120000 index 00000000..9d7f9295 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_argparse.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a3/35/38/cfcf874d25fab65ea0db2b28a851a7e9bf232b806dfcd9402a5fe36324 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py new file mode 120000 index 00000000..ed327773 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_attrs.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/3c/61/61/51b2f8dbc31cfdcbb21606480766a5b762027de69a99607ed02fe4c409 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py new file mode 120000 index 00000000..e458af83 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_boto3.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/81/93/a4/0e8635619936e04a4e3154158f4301b8d7c15a7fd77c39a6947e23c875 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py new file mode 100644 index 00000000..383621d4 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_builtin_inference.py @@ -0,0 +1,1007 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Astroid hooks for various builtins.""" + +from __future__ import annotations + +import itertools +from collections.abc import Iterator +from functools import partial + +from astroid import arguments, helpers, inference_tip, nodes, objects, util +from astroid.builder import AstroidBuilder +from astroid.context import InferenceContext +from astroid.exceptions import ( + AstroidTypeError, + AttributeInferenceError, + InferenceError, + MroError, + UseInferenceDefault, +) +from astroid.manager import AstroidManager +from astroid.nodes import scoped_nodes + +OBJECT_DUNDER_NEW = "object.__new__" + +STR_CLASS = """ +class whatever(object): + def join(self, iterable): + return {rvalue} + def replace(self, old, new, count=None): + return {rvalue} + def format(self, *args, **kwargs): + return {rvalue} + def encode(self, encoding='ascii', errors=None): + return b'' + def decode(self, encoding='ascii', errors=None): + return u'' + def capitalize(self): + return {rvalue} + def title(self): + return {rvalue} + def lower(self): + return {rvalue} + def upper(self): + return {rvalue} + def swapcase(self): + return {rvalue} + def index(self, sub, start=None, end=None): + return 0 + def find(self, sub, start=None, end=None): + return 0 + def count(self, sub, start=None, end=None): + return 0 + def strip(self, chars=None): + return {rvalue} + def lstrip(self, chars=None): + return {rvalue} + def rstrip(self, chars=None): + return {rvalue} + def rjust(self, width, fillchar=None): + return {rvalue} + def center(self, width, fillchar=None): + return {rvalue} + def ljust(self, width, fillchar=None): + return {rvalue} +""" + + +BYTES_CLASS = """ +class whatever(object): + def join(self, iterable): + return {rvalue} + def replace(self, old, new, count=None): + return {rvalue} + def decode(self, encoding='ascii', errors=None): + return u'' + def capitalize(self): + return {rvalue} + def title(self): + return {rvalue} + def lower(self): + return {rvalue} + def upper(self): + return {rvalue} + def swapcase(self): + return {rvalue} + def index(self, sub, start=None, end=None): + return 0 + def find(self, sub, start=None, end=None): + return 0 + def count(self, sub, start=None, end=None): + return 0 + def strip(self, chars=None): + return {rvalue} + def lstrip(self, chars=None): + return {rvalue} + def rstrip(self, chars=None): + return {rvalue} + def rjust(self, width, fillchar=None): + return {rvalue} + def center(self, width, fillchar=None): + return {rvalue} + def ljust(self, width, fillchar=None): + return {rvalue} +""" + + +def _extend_string_class(class_node, code, rvalue): + """Function to extend builtin str/unicode class.""" + code = code.format(rvalue=rvalue) + fake = AstroidBuilder(AstroidManager()).string_build(code)["whatever"] + for method in fake.mymethods(): + method.parent = class_node + method.lineno = None + method.col_offset = None + if "__class__" in method.locals: + method.locals["__class__"] = [class_node] + class_node.locals[method.name] = [method] + method.parent = class_node + + +def _extend_builtins(class_transforms): + builtin_ast = AstroidManager().builtins_module + for class_name, transform in class_transforms.items(): + transform(builtin_ast[class_name]) + + +_extend_builtins( + { + "bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"), + "str": partial(_extend_string_class, code=STR_CLASS, rvalue="''"), + } +) + + +def _builtin_filter_predicate(node, builtin_name) -> bool: + if ( + builtin_name == "type" + and node.root().name == "re" + and isinstance(node.func, nodes.Name) + and node.func.name == "type" + and isinstance(node.parent, nodes.Assign) + and len(node.parent.targets) == 1 + and isinstance(node.parent.targets[0], nodes.AssignName) + and node.parent.targets[0].name in {"Pattern", "Match"} + ): + # Handle re.Pattern and re.Match in brain_re + # Match these patterns from stdlib/re.py + # ```py + # Pattern = type(...) + # Match = type(...) + # ``` + return False + if isinstance(node.func, nodes.Name) and node.func.name == builtin_name: + return True + if isinstance(node.func, nodes.Attribute): + return ( + node.func.attrname == "fromkeys" + and isinstance(node.func.expr, nodes.Name) + and node.func.expr.name == "dict" + ) + return False + + +def register_builtin_transform(transform, builtin_name) -> None: + """Register a new transform function for the given *builtin_name*. + + The transform function must accept two parameters, a node and + an optional context. + """ + + def _transform_wrapper(node, context: InferenceContext | None = None): + result = transform(node, context=context) + if result: + if not result.parent: + # Let the transformation function determine + # the parent for its result. Otherwise, + # we set it to be the node we transformed from. + result.parent = node + + if result.lineno is None: + result.lineno = node.lineno + # Can be a 'Module' see https://github.com/PyCQA/pylint/issues/4671 + # We don't have a regression test on this one: tread carefully + if hasattr(result, "col_offset") and result.col_offset is None: + result.col_offset = node.col_offset + return iter([result]) + + AstroidManager().register_transform( + nodes.Call, + inference_tip(_transform_wrapper), + partial(_builtin_filter_predicate, builtin_name=builtin_name), + ) + + +def _container_generic_inference(node, context, node_type, transform): + args = node.args + if not args: + return node_type() + if len(node.args) > 1: + raise UseInferenceDefault() + + (arg,) = args + transformed = transform(arg) + if not transformed: + try: + inferred = next(arg.infer(context=context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + if isinstance(inferred, util.UninferableBase): + raise UseInferenceDefault + transformed = transform(inferred) + if not transformed or isinstance(transformed, util.UninferableBase): + raise UseInferenceDefault + return transformed + + +def _container_generic_transform( # pylint: disable=inconsistent-return-statements + arg, context, klass, iterables, build_elts +): + if isinstance(arg, klass): + return arg + if isinstance(arg, iterables): + if all(isinstance(elt, nodes.Const) for elt in arg.elts): + elts = [elt.value for elt in arg.elts] + else: + # TODO: Does not handle deduplication for sets. + elts = [] + for element in arg.elts: + if not element: + continue + inferred = helpers.safe_infer(element, context=context) + if inferred: + evaluated_object = nodes.EvaluatedObject( + original=element, value=inferred + ) + elts.append(evaluated_object) + elif isinstance(arg, nodes.Dict): + # Dicts need to have consts as strings already. + if not all(isinstance(elt[0], nodes.Const) for elt in arg.items): + raise UseInferenceDefault() + elts = [item[0].value for item in arg.items] + elif isinstance(arg, nodes.Const) and isinstance(arg.value, (str, bytes)): + elts = arg.value + else: + return + return klass.from_elements(elts=build_elts(elts)) + + +def _infer_builtin_container( + node, context, klass=None, iterables=None, build_elts=None +): + transform_func = partial( + _container_generic_transform, + context=context, + klass=klass, + iterables=iterables, + build_elts=build_elts, + ) + + return _container_generic_inference(node, context, klass, transform_func) + + +# pylint: disable=invalid-name +infer_tuple = partial( + _infer_builtin_container, + klass=nodes.Tuple, + iterables=( + nodes.List, + nodes.Set, + objects.FrozenSet, + objects.DictItems, + objects.DictKeys, + objects.DictValues, + ), + build_elts=tuple, +) + +infer_list = partial( + _infer_builtin_container, + klass=nodes.List, + iterables=( + nodes.Tuple, + nodes.Set, + objects.FrozenSet, + objects.DictItems, + objects.DictKeys, + objects.DictValues, + ), + build_elts=list, +) + +infer_set = partial( + _infer_builtin_container, + klass=nodes.Set, + iterables=(nodes.List, nodes.Tuple, objects.FrozenSet, objects.DictKeys), + build_elts=set, +) + +infer_frozenset = partial( + _infer_builtin_container, + klass=objects.FrozenSet, + iterables=(nodes.List, nodes.Tuple, nodes.Set, objects.FrozenSet, objects.DictKeys), + build_elts=frozenset, +) + + +def _get_elts(arg, context): + def is_iterable(n): + return isinstance(n, (nodes.List, nodes.Tuple, nodes.Set)) + + try: + inferred = next(arg.infer(context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + if isinstance(inferred, nodes.Dict): + items = inferred.items + elif is_iterable(inferred): + items = [] + for elt in inferred.elts: + # If an item is not a pair of two items, + # then fallback to the default inference. + # Also, take in consideration only hashable items, + # tuples and consts. We are choosing Names as well. + if not is_iterable(elt): + raise UseInferenceDefault() + if len(elt.elts) != 2: + raise UseInferenceDefault() + if not isinstance(elt.elts[0], (nodes.Tuple, nodes.Const, nodes.Name)): + raise UseInferenceDefault() + items.append(tuple(elt.elts)) + else: + raise UseInferenceDefault() + return items + + +def infer_dict(node, context: InferenceContext | None = None): + """Try to infer a dict call to a Dict node. + + The function treats the following cases: + + * dict() + * dict(mapping) + * dict(iterable) + * dict(iterable, **kwargs) + * dict(mapping, **kwargs) + * dict(**kwargs) + + If a case can't be inferred, we'll fallback to default inference. + """ + call = arguments.CallSite.from_call(node, context=context) + if call.has_invalid_arguments() or call.has_invalid_keywords(): + raise UseInferenceDefault + + args = call.positional_arguments + kwargs = list(call.keyword_arguments.items()) + + if not args and not kwargs: + # dict() + return nodes.Dict() + if kwargs and not args: + # dict(a=1, b=2, c=4) + items = [(nodes.Const(key), value) for key, value in kwargs] + elif len(args) == 1 and kwargs: + # dict(some_iterable, b=2, c=4) + elts = _get_elts(args[0], context) + keys = [(nodes.Const(key), value) for key, value in kwargs] + items = elts + keys + elif len(args) == 1: + items = _get_elts(args[0], context) + else: + raise UseInferenceDefault() + value = nodes.Dict( + col_offset=node.col_offset, lineno=node.lineno, parent=node.parent + ) + value.postinit(items) + return value + + +def infer_super(node, context: InferenceContext | None = None): + """Understand super calls. + + There are some restrictions for what can be understood: + + * unbounded super (one argument form) is not understood. + + * if the super call is not inside a function (classmethod or method), + then the default inference will be used. + + * if the super arguments can't be inferred, the default inference + will be used. + """ + if len(node.args) == 1: + # Ignore unbounded super. + raise UseInferenceDefault + + scope = node.scope() + if not isinstance(scope, nodes.FunctionDef): + # Ignore non-method uses of super. + raise UseInferenceDefault + if scope.type not in ("classmethod", "method"): + # Not interested in staticmethods. + raise UseInferenceDefault + + cls = scoped_nodes.get_wrapping_class(scope) + if not node.args: + mro_pointer = cls + # In we are in a classmethod, the interpreter will fill + # automatically the class as the second argument, not an instance. + if scope.type == "classmethod": + mro_type = cls + else: + mro_type = cls.instantiate_class() + else: + try: + mro_pointer = next(node.args[0].infer(context=context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + try: + mro_type = next(node.args[1].infer(context=context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + + if isinstance(mro_pointer, util.UninferableBase) or isinstance( + mro_type, util.UninferableBase + ): + # No way we could understand this. + raise UseInferenceDefault + + super_obj = objects.Super( + mro_pointer=mro_pointer, mro_type=mro_type, self_class=cls, scope=scope + ) + super_obj.parent = node + return super_obj + + +def _infer_getattr_args(node, context): + if len(node.args) not in (2, 3): + # Not a valid getattr call. + raise UseInferenceDefault + + try: + obj = next(node.args[0].infer(context=context)) + attr = next(node.args[1].infer(context=context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + + if isinstance(obj, util.UninferableBase) or isinstance(attr, util.UninferableBase): + # If one of the arguments is something we can't infer, + # then also make the result of the getattr call something + # which is unknown. + return util.Uninferable, util.Uninferable + + is_string = isinstance(attr, nodes.Const) and isinstance(attr.value, str) + if not is_string: + raise UseInferenceDefault + + return obj, attr.value + + +def infer_getattr(node, context: InferenceContext | None = None): + """Understand getattr calls. + + If one of the arguments is an Uninferable object, then the + result will be an Uninferable object. Otherwise, the normal attribute + lookup will be done. + """ + obj, attr = _infer_getattr_args(node, context) + if ( + isinstance(obj, util.UninferableBase) + or isinstance(attr, util.UninferableBase) + or not hasattr(obj, "igetattr") + ): + return util.Uninferable + + try: + return next(obj.igetattr(attr, context=context)) + except (StopIteration, InferenceError, AttributeInferenceError): + if len(node.args) == 3: + # Try to infer the default and return it instead. + try: + return next(node.args[2].infer(context=context)) + except (StopIteration, InferenceError) as exc: + raise UseInferenceDefault from exc + + raise UseInferenceDefault + + +def infer_hasattr(node, context: InferenceContext | None = None): + """Understand hasattr calls. + + This always guarantees three possible outcomes for calling + hasattr: Const(False) when we are sure that the object + doesn't have the intended attribute, Const(True) when + we know that the object has the attribute and Uninferable + when we are unsure of the outcome of the function call. + """ + try: + obj, attr = _infer_getattr_args(node, context) + if ( + isinstance(obj, util.UninferableBase) + or isinstance(attr, util.UninferableBase) + or not hasattr(obj, "getattr") + ): + return util.Uninferable + obj.getattr(attr, context=context) + except UseInferenceDefault: + # Can't infer something from this function call. + return util.Uninferable + except AttributeInferenceError: + # Doesn't have it. + return nodes.Const(False) + return nodes.Const(True) + + +def infer_callable(node, context: InferenceContext | None = None): + """Understand callable calls. + + This follows Python's semantics, where an object + is callable if it provides an attribute __call__, + even though that attribute is something which can't be + called. + """ + if len(node.args) != 1: + # Invalid callable call. + raise UseInferenceDefault + + argument = node.args[0] + try: + inferred = next(argument.infer(context=context)) + except (InferenceError, StopIteration): + return util.Uninferable + if isinstance(inferred, util.UninferableBase): + return util.Uninferable + return nodes.Const(inferred.callable()) + + +def infer_property( + node: nodes.Call, context: InferenceContext | None = None +) -> objects.Property: + """Understand `property` class. + + This only infers the output of `property` + call, not the arguments themselves. + """ + if len(node.args) < 1: + # Invalid property call. + raise UseInferenceDefault + + getter = node.args[0] + try: + inferred = next(getter.infer(context=context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + + if not isinstance(inferred, (nodes.FunctionDef, nodes.Lambda)): + raise UseInferenceDefault + + prop_func = objects.Property( + function=inferred, + name=inferred.name, + lineno=node.lineno, + parent=node, + col_offset=node.col_offset, + ) + prop_func.postinit( + body=[], + args=inferred.args, + doc_node=getattr(inferred, "doc_node", None), + ) + return prop_func + + +def infer_bool(node, context: InferenceContext | None = None): + """Understand bool calls.""" + if len(node.args) > 1: + # Invalid bool call. + raise UseInferenceDefault + + if not node.args: + return nodes.Const(False) + + argument = node.args[0] + try: + inferred = next(argument.infer(context=context)) + except (InferenceError, StopIteration): + return util.Uninferable + if isinstance(inferred, util.UninferableBase): + return util.Uninferable + + bool_value = inferred.bool_value(context=context) + if isinstance(bool_value, util.UninferableBase): + return util.Uninferable + return nodes.Const(bool_value) + + +def infer_type(node, context: InferenceContext | None = None): + """Understand the one-argument form of *type*.""" + if len(node.args) != 1: + raise UseInferenceDefault + + return helpers.object_type(node.args[0], context) + + +def infer_slice(node, context: InferenceContext | None = None): + """Understand `slice` calls.""" + args = node.args + if not 0 < len(args) <= 3: + raise UseInferenceDefault + + infer_func = partial(helpers.safe_infer, context=context) + args = [infer_func(arg) for arg in args] + for arg in args: + if not arg or isinstance(arg, util.UninferableBase): + raise UseInferenceDefault + if not isinstance(arg, nodes.Const): + raise UseInferenceDefault + if not isinstance(arg.value, (type(None), int)): + raise UseInferenceDefault + + if len(args) < 3: + # Make sure we have 3 arguments. + args.extend([None] * (3 - len(args))) + + slice_node = nodes.Slice( + lineno=node.lineno, col_offset=node.col_offset, parent=node.parent + ) + slice_node.postinit(*args) + return slice_node + + +def _infer_object__new__decorator(node, context: InferenceContext | None = None): + # Instantiate class immediately + # since that's what @object.__new__ does + return iter((node.instantiate_class(),)) + + +def _infer_object__new__decorator_check(node) -> bool: + """Predicate before inference_tip. + + Check if the given ClassDef has an @object.__new__ decorator + """ + if not node.decorators: + return False + + for decorator in node.decorators.nodes: + if isinstance(decorator, nodes.Attribute): + if decorator.as_string() == OBJECT_DUNDER_NEW: + return True + return False + + +def infer_issubclass(callnode, context: InferenceContext | None = None): + """Infer issubclass() calls. + + :param nodes.Call callnode: an `issubclass` call + :param InferenceContext context: the context for the inference + :rtype nodes.Const: Boolean Const value of the `issubclass` call + :raises UseInferenceDefault: If the node cannot be inferred + """ + call = arguments.CallSite.from_call(callnode, context=context) + if call.keyword_arguments: + # issubclass doesn't support keyword arguments + raise UseInferenceDefault("TypeError: issubclass() takes no keyword arguments") + if len(call.positional_arguments) != 2: + raise UseInferenceDefault( + f"Expected two arguments, got {len(call.positional_arguments)}" + ) + # The left hand argument is the obj to be checked + obj_node, class_or_tuple_node = call.positional_arguments + + try: + obj_type = next(obj_node.infer(context=context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + if not isinstance(obj_type, nodes.ClassDef): + raise UseInferenceDefault("TypeError: arg 1 must be class") + + # The right hand argument is the class(es) that the given + # object is to be checked against. + try: + class_container = _class_or_tuple_to_container( + class_or_tuple_node, context=context + ) + except InferenceError as exc: + raise UseInferenceDefault from exc + try: + issubclass_bool = helpers.object_issubclass(obj_type, class_container, context) + except AstroidTypeError as exc: + raise UseInferenceDefault("TypeError: " + str(exc)) from exc + except MroError as exc: + raise UseInferenceDefault from exc + return nodes.Const(issubclass_bool) + + +def infer_isinstance(callnode, context: InferenceContext | None = None): + """Infer isinstance calls. + + :param nodes.Call callnode: an isinstance call + :rtype nodes.Const: Boolean Const value of isinstance call + + :raises UseInferenceDefault: If the node cannot be inferred + """ + call = arguments.CallSite.from_call(callnode, context=context) + if call.keyword_arguments: + # isinstance doesn't support keyword arguments + raise UseInferenceDefault("TypeError: isinstance() takes no keyword arguments") + if len(call.positional_arguments) != 2: + raise UseInferenceDefault( + f"Expected two arguments, got {len(call.positional_arguments)}" + ) + # The left hand argument is the obj to be checked + obj_node, class_or_tuple_node = call.positional_arguments + # The right hand argument is the class(es) that the given + # obj is to be check is an instance of + try: + class_container = _class_or_tuple_to_container( + class_or_tuple_node, context=context + ) + except InferenceError as exc: + raise UseInferenceDefault from exc + try: + isinstance_bool = helpers.object_isinstance(obj_node, class_container, context) + except AstroidTypeError as exc: + raise UseInferenceDefault("TypeError: " + str(exc)) from exc + except MroError as exc: + raise UseInferenceDefault from exc + if isinstance(isinstance_bool, util.UninferableBase): + raise UseInferenceDefault + return nodes.Const(isinstance_bool) + + +def _class_or_tuple_to_container(node, context: InferenceContext | None = None): + # Move inferences results into container + # to simplify later logic + # raises InferenceError if any of the inferences fall through + try: + node_infer = next(node.infer(context=context)) + except StopIteration as e: + raise InferenceError(node=node, context=context) from e + # arg2 MUST be a type or a TUPLE of types + # for isinstance + if isinstance(node_infer, nodes.Tuple): + try: + class_container = [ + next(node.infer(context=context)) for node in node_infer.elts + ] + except StopIteration as e: + raise InferenceError(node=node, context=context) from e + class_container = [ + klass_node for klass_node in class_container if klass_node is not None + ] + else: + class_container = [node_infer] + return class_container + + +def infer_len(node, context: InferenceContext | None = None): + """Infer length calls. + + :param nodes.Call node: len call to infer + :param context.InferenceContext: node context + :rtype nodes.Const: a Const node with the inferred length, if possible + """ + call = arguments.CallSite.from_call(node, context=context) + if call.keyword_arguments: + raise UseInferenceDefault("TypeError: len() must take no keyword arguments") + if len(call.positional_arguments) != 1: + raise UseInferenceDefault( + "TypeError: len() must take exactly one argument " + "({len}) given".format(len=len(call.positional_arguments)) + ) + [argument_node] = call.positional_arguments + + try: + return nodes.Const(helpers.object_len(argument_node, context=context)) + except (AstroidTypeError, InferenceError) as exc: + raise UseInferenceDefault(str(exc)) from exc + + +def infer_str(node, context: InferenceContext | None = None): + """Infer str() calls. + + :param nodes.Call node: str() call to infer + :param context.InferenceContext: node context + :rtype nodes.Const: a Const containing an empty string + """ + call = arguments.CallSite.from_call(node, context=context) + if call.keyword_arguments: + raise UseInferenceDefault("TypeError: str() must take no keyword arguments") + try: + return nodes.Const("") + except (AstroidTypeError, InferenceError) as exc: + raise UseInferenceDefault(str(exc)) from exc + + +def infer_int(node, context: InferenceContext | None = None): + """Infer int() calls. + + :param nodes.Call node: int() call to infer + :param context.InferenceContext: node context + :rtype nodes.Const: a Const containing the integer value of the int() call + """ + call = arguments.CallSite.from_call(node, context=context) + if call.keyword_arguments: + raise UseInferenceDefault("TypeError: int() must take no keyword arguments") + + if call.positional_arguments: + try: + first_value = next(call.positional_arguments[0].infer(context=context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault(str(exc)) from exc + + if isinstance(first_value, util.UninferableBase): + raise UseInferenceDefault + + if isinstance(first_value, nodes.Const) and isinstance( + first_value.value, (int, str) + ): + try: + actual_value = int(first_value.value) + except ValueError: + return nodes.Const(0) + return nodes.Const(actual_value) + + return nodes.Const(0) + + +def infer_dict_fromkeys(node, context: InferenceContext | None = None): + """Infer dict.fromkeys. + + :param nodes.Call node: dict.fromkeys() call to infer + :param context.InferenceContext context: node context + :rtype nodes.Dict: + a Dictionary containing the values that astroid was able to infer. + In case the inference failed for any reason, an empty dictionary + will be inferred instead. + """ + + def _build_dict_with_elements(elements): + new_node = nodes.Dict( + col_offset=node.col_offset, lineno=node.lineno, parent=node.parent + ) + new_node.postinit(elements) + return new_node + + call = arguments.CallSite.from_call(node, context=context) + if call.keyword_arguments: + raise UseInferenceDefault("TypeError: int() must take no keyword arguments") + if len(call.positional_arguments) not in {1, 2}: + raise UseInferenceDefault( + "TypeError: Needs between 1 and 2 positional arguments" + ) + + default = nodes.Const(None) + values = call.positional_arguments[0] + try: + inferred_values = next(values.infer(context=context)) + except (InferenceError, StopIteration): + return _build_dict_with_elements([]) + if inferred_values is util.Uninferable: + return _build_dict_with_elements([]) + + # Limit to a couple of potential values, as this can become pretty complicated + accepted_iterable_elements = (nodes.Const,) + if isinstance(inferred_values, (nodes.List, nodes.Set, nodes.Tuple)): + elements = inferred_values.elts + for element in elements: + if not isinstance(element, accepted_iterable_elements): + # Fallback to an empty dict + return _build_dict_with_elements([]) + + elements_with_value = [(element, default) for element in elements] + return _build_dict_with_elements(elements_with_value) + if isinstance(inferred_values, nodes.Const) and isinstance( + inferred_values.value, (str, bytes) + ): + elements = [ + (nodes.Const(element), default) for element in inferred_values.value + ] + return _build_dict_with_elements(elements) + if isinstance(inferred_values, nodes.Dict): + keys = inferred_values.itered() + for key in keys: + if not isinstance(key, accepted_iterable_elements): + # Fallback to an empty dict + return _build_dict_with_elements([]) + + elements_with_value = [(element, default) for element in keys] + return _build_dict_with_elements(elements_with_value) + + # Fallback to an empty dictionary + return _build_dict_with_elements([]) + + +def _infer_copy_method( + node: nodes.Call, context: InferenceContext | None = None +) -> Iterator[nodes.NodeNG]: + assert isinstance(node.func, nodes.Attribute) + inferred_orig, inferred_copy = itertools.tee(node.func.expr.infer(context=context)) + if all( + isinstance( + inferred_node, (nodes.Dict, nodes.List, nodes.Set, objects.FrozenSet) + ) + for inferred_node in inferred_orig + ): + return inferred_copy + + raise UseInferenceDefault() + + +def _is_str_format_call(node: nodes.Call) -> bool: + """Catch calls to str.format().""" + if not isinstance(node.func, nodes.Attribute) or not node.func.attrname == "format": + return False + + if isinstance(node.func.expr, nodes.Name): + value = helpers.safe_infer(node.func.expr) + else: + value = node.func.expr + + return isinstance(value, nodes.Const) and isinstance(value.value, str) + + +def _infer_str_format_call( + node: nodes.Call, context: InferenceContext | None = None +) -> Iterator[nodes.Const | util.UninferableBase]: + """Return a Const node based on the template and passed arguments.""" + call = arguments.CallSite.from_call(node, context=context) + if isinstance(node.func.expr, nodes.Name): + value: nodes.Const | None = helpers.safe_infer(node.func.expr) + if value is None: + return iter([util.Uninferable]) + else: + value = node.func.expr + + format_template = value.value + + # Get the positional arguments passed + inferred_positional = [ + helpers.safe_infer(i, context) for i in call.positional_arguments + ] + if not all(isinstance(i, nodes.Const) for i in inferred_positional): + return iter([util.Uninferable]) + pos_values: list[str] = [i.value for i in inferred_positional] + + # Get the keyword arguments passed + inferred_keyword = { + k: helpers.safe_infer(v, context) for k, v in call.keyword_arguments.items() + } + if not all(isinstance(i, nodes.Const) for i in inferred_keyword.values()): + return iter([util.Uninferable]) + keyword_values: dict[str, str] = {k: v.value for k, v in inferred_keyword.items()} + + try: + formatted_string = format_template.format(*pos_values, **keyword_values) + except (AttributeError, IndexError, KeyError, TypeError, ValueError): + # AttributeError: named field in format string was not found in the arguments + # IndexError: there are too few arguments to interpolate + # TypeError: Unsupported format string + # ValueError: Unknown format code + return iter([util.Uninferable]) + + return iter([nodes.const_factory(formatted_string)]) + + +# Builtins inference +register_builtin_transform(infer_bool, "bool") +register_builtin_transform(infer_super, "super") +register_builtin_transform(infer_callable, "callable") +register_builtin_transform(infer_property, "property") +register_builtin_transform(infer_getattr, "getattr") +register_builtin_transform(infer_hasattr, "hasattr") +register_builtin_transform(infer_tuple, "tuple") +register_builtin_transform(infer_set, "set") +register_builtin_transform(infer_list, "list") +register_builtin_transform(infer_dict, "dict") +register_builtin_transform(infer_frozenset, "frozenset") +register_builtin_transform(infer_type, "type") +register_builtin_transform(infer_slice, "slice") +register_builtin_transform(infer_isinstance, "isinstance") +register_builtin_transform(infer_issubclass, "issubclass") +register_builtin_transform(infer_len, "len") +register_builtin_transform(infer_str, "str") +register_builtin_transform(infer_int, "int") +register_builtin_transform(infer_dict_fromkeys, "dict.fromkeys") + + +# Infer object.__new__ calls +AstroidManager().register_transform( + nodes.ClassDef, + inference_tip(_infer_object__new__decorator), + _infer_object__new__decorator_check, +) + +AstroidManager().register_transform( + nodes.Call, + inference_tip(_infer_copy_method), + lambda node: isinstance(node.func, nodes.Attribute) + and node.func.attrname == "copy", +) + +AstroidManager().register_transform( + nodes.Call, inference_tip(_infer_str_format_call), _is_str_format_call +) diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py new file mode 120000 index 00000000..da3b19b3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_collections.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ff/fb/e1/fd2529df2998ebc8bf5185b538c9408c337e1e2bff34774342cf7c1d5e \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py new file mode 120000 index 00000000..433ec781 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_crypt.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/66/8e/ba/cfde1c40e2fe1cd39c01048f36cfc86ffc4d2cb63da84b716755646c03 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py new file mode 120000 index 00000000..f069d0ca --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_ctypes.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e7/97/e5/9c0223fd387f7aaa86d4744c0d1e5cb6def8c30d6f0ec485e36176e84b \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py new file mode 120000 index 00000000..bb30bbdf --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_curses.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/5d/7c/82/78b01214592c3b6cd26eb66007f00898606520667b3e08dd9b4e31fa38 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py new file mode 100644 index 00000000..1397ed14 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_dataclasses.py @@ -0,0 +1,636 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +""" +Astroid hook for the dataclasses library. + +Support built-in dataclasses, pydantic.dataclasses, and marshmallow_dataclass-annotated +dataclasses. References: +- https://docs.python.org/3/library/dataclasses.html +- https://pydantic-docs.helpmanual.io/usage/dataclasses/ +- https://lovasoa.github.io/marshmallow_dataclass/ +""" + +from __future__ import annotations + +import sys +from collections.abc import Iterator +from typing import Tuple, Union + +from astroid import bases, context, helpers, nodes +from astroid.builder import parse +from astroid.const import PY39_PLUS, PY310_PLUS +from astroid.exceptions import AstroidSyntaxError, InferenceError, UseInferenceDefault +from astroid.inference_tip import inference_tip +from astroid.manager import AstroidManager +from astroid.typing import InferenceResult +from astroid.util import Uninferable, UninferableBase + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + +_FieldDefaultReturn = Union[ + None, + Tuple[Literal["default"], nodes.NodeNG], + Tuple[Literal["default_factory"], nodes.Call], +] + +DATACLASSES_DECORATORS = frozenset(("dataclass",)) +FIELD_NAME = "field" +DATACLASS_MODULES = frozenset( + ("dataclasses", "marshmallow_dataclass", "pydantic.dataclasses") +) +DEFAULT_FACTORY = "_HAS_DEFAULT_FACTORY" # based on typing.py + + +def is_decorated_with_dataclass( + node: nodes.ClassDef, decorator_names: frozenset[str] = DATACLASSES_DECORATORS +) -> bool: + """Return True if a decorated node has a `dataclass` decorator applied.""" + if not isinstance(node, nodes.ClassDef) or not node.decorators: + return False + + return any( + _looks_like_dataclass_decorator(decorator_attribute, decorator_names) + for decorator_attribute in node.decorators.nodes + ) + + +def dataclass_transform(node: nodes.ClassDef) -> None: + """Rewrite a dataclass to be easily understood by pylint.""" + node.is_dataclass = True + + for assign_node in _get_dataclass_attributes(node): + name = assign_node.target.name + + rhs_node = nodes.Unknown( + lineno=assign_node.lineno, + col_offset=assign_node.col_offset, + parent=assign_node, + ) + rhs_node = AstroidManager().visit_transforms(rhs_node) + node.instance_attrs[name] = [rhs_node] + + if not _check_generate_dataclass_init(node): + return + + kw_only_decorated = False + if PY310_PLUS and node.decorators.nodes: + for decorator in node.decorators.nodes: + if not isinstance(decorator, nodes.Call): + kw_only_decorated = False + break + for keyword in decorator.keywords: + if keyword.arg == "kw_only": + kw_only_decorated = keyword.value.bool_value() + + init_str = _generate_dataclass_init( + node, + list(_get_dataclass_attributes(node, init=True)), + kw_only_decorated, + ) + + try: + init_node = parse(init_str)["__init__"] + except AstroidSyntaxError: + pass + else: + init_node.parent = node + init_node.lineno, init_node.col_offset = None, None + node.locals["__init__"] = [init_node] + + root = node.root() + if DEFAULT_FACTORY not in root.locals: + new_assign = parse(f"{DEFAULT_FACTORY} = object()").body[0] + new_assign.parent = root + root.locals[DEFAULT_FACTORY] = [new_assign.targets[0]] + + +def _get_dataclass_attributes( + node: nodes.ClassDef, init: bool = False +) -> Iterator[nodes.AnnAssign]: + """Yield the AnnAssign nodes of dataclass attributes for the node. + + If init is True, also include InitVars. + """ + for assign_node in node.body: + if not isinstance(assign_node, nodes.AnnAssign) or not isinstance( + assign_node.target, nodes.AssignName + ): + continue + + # Annotation is never None + if _is_class_var(assign_node.annotation): # type: ignore[arg-type] + continue + + if _is_keyword_only_sentinel(assign_node.annotation): + continue + + # Annotation is never None + if not init and _is_init_var(assign_node.annotation): # type: ignore[arg-type] + continue + + yield assign_node + + +def _check_generate_dataclass_init(node: nodes.ClassDef) -> bool: + """Return True if we should generate an __init__ method for node. + + This is True when: + - node doesn't define its own __init__ method + - the dataclass decorator was called *without* the keyword argument init=False + """ + if "__init__" in node.locals: + return False + + found = None + + for decorator_attribute in node.decorators.nodes: + if not isinstance(decorator_attribute, nodes.Call): + continue + + if _looks_like_dataclass_decorator(decorator_attribute): + found = decorator_attribute + + if found is None: + return True + + # Check for keyword arguments of the form init=False + return not any( + keyword.arg == "init" + and not keyword.value.bool_value() # type: ignore[union-attr] # value is never None + for keyword in found.keywords + ) + + +def _find_arguments_from_base_classes( + node: nodes.ClassDef, +) -> tuple[ + dict[str, tuple[str | None, str | None]], dict[str, tuple[str | None, str | None]] +]: + """Iterate through all bases and get their typing and defaults.""" + pos_only_store: dict[str, tuple[str | None, str | None]] = {} + kw_only_store: dict[str, tuple[str | None, str | None]] = {} + # See TODO down below + # all_have_defaults = True + + for base in reversed(node.mro()): + if not base.is_dataclass: + continue + try: + base_init: nodes.FunctionDef = base.locals["__init__"][0] + except KeyError: + continue + + pos_only, kw_only = base_init.args._get_arguments_data() + for posarg, data in pos_only.items(): + # if data[1] is None: + # if all_have_defaults and pos_only_store: + # # TODO: This should return an Uninferable as this would raise + # # a TypeError at runtime. However, transforms can't return + # # Uninferables currently. + # pass + # all_have_defaults = False + pos_only_store[posarg] = data + + for kwarg, data in kw_only.items(): + kw_only_store[kwarg] = data + return pos_only_store, kw_only_store + + +def _parse_arguments_into_strings( + pos_only_store: dict[str, tuple[str | None, str | None]], + kw_only_store: dict[str, tuple[str | None, str | None]], +) -> tuple[str, str]: + """Parse positional and keyword arguments into strings for an __init__ method.""" + pos_only, kw_only = "", "" + for pos_arg, data in pos_only_store.items(): + pos_only += pos_arg + if data[0]: + pos_only += ": " + data[0] + if data[1]: + pos_only += " = " + data[1] + pos_only += ", " + for kw_arg, data in kw_only_store.items(): + kw_only += kw_arg + if data[0]: + kw_only += ": " + data[0] + if data[1]: + kw_only += " = " + data[1] + kw_only += ", " + + return pos_only, kw_only + + +def _get_previous_field_default(node: nodes.ClassDef, name: str) -> nodes.NodeNG | None: + """Get the default value of a previously defined field.""" + for base in reversed(node.mro()): + if not base.is_dataclass: + continue + if name in base.locals: + for assign in base.locals[name]: + if ( + isinstance(assign.parent, nodes.AnnAssign) + and assign.parent.value + and isinstance(assign.parent.value, nodes.Call) + and _looks_like_dataclass_field_call(assign.parent.value) + ): + default = _get_field_default(assign.parent.value) + if default: + return default[1] + return None + + +def _generate_dataclass_init( # pylint: disable=too-many-locals + node: nodes.ClassDef, assigns: list[nodes.AnnAssign], kw_only_decorated: bool +) -> str: + """Return an init method for a dataclass given the targets.""" + params: list[str] = [] + kw_only_params: list[str] = [] + assignments: list[str] = [] + + prev_pos_only_store, prev_kw_only_store = _find_arguments_from_base_classes(node) + + for assign in assigns: + name, annotation, value = assign.target.name, assign.annotation, assign.value + + # Check whether this assign is overriden by a property assignment + property_node: nodes.FunctionDef | None = None + for additional_assign in node.locals[name]: + if not isinstance(additional_assign, nodes.FunctionDef): + continue + if not additional_assign.decorators: + continue + if "builtins.property" in additional_assign.decoratornames(): + property_node = additional_assign + break + + is_field = isinstance(value, nodes.Call) and _looks_like_dataclass_field_call( + value, check_scope=False + ) + + if is_field: + # Skip any fields that have `init=False` + if any( + keyword.arg == "init" and not keyword.value.bool_value() + for keyword in value.keywords # type: ignore[union-attr] # value is never None + ): + # Also remove the name from the previous arguments to be inserted later + prev_pos_only_store.pop(name, None) + prev_kw_only_store.pop(name, None) + continue + + if _is_init_var(annotation): # type: ignore[arg-type] # annotation is never None + init_var = True + if isinstance(annotation, nodes.Subscript): + annotation = annotation.slice + else: + # Cannot determine type annotation for parameter from InitVar + annotation = None + assignment_str = "" + else: + init_var = False + assignment_str = f"self.{name} = {name}" + + ann_str, default_str = None, None + if annotation is not None: + ann_str = annotation.as_string() + + if value: + if is_field: + result = _get_field_default(value) # type: ignore[arg-type] + if result: + default_type, default_node = result + if default_type == "default": + default_str = default_node.as_string() + elif default_type == "default_factory": + default_str = DEFAULT_FACTORY + assignment_str = ( + f"self.{name} = {default_node.as_string()} " + f"if {name} is {DEFAULT_FACTORY} else {name}" + ) + else: + default_str = value.as_string() + elif property_node: + # We set the result of the property call as default + # This hides the fact that this would normally be a 'property object' + # But we can't represent those as string + try: + # Call str to make sure also Uninferable gets stringified + default_str = str(next(property_node.infer_call_result()).as_string()) + except (InferenceError, StopIteration): + pass + else: + # Even with `init=False` the default value still can be propogated to + # later assignments. Creating weird signatures like: + # (self, a: str = 1) -> None + previous_default = _get_previous_field_default(node, name) + if previous_default: + default_str = previous_default.as_string() + + # Construct the param string to add to the init if necessary + param_str = name + if ann_str is not None: + param_str += f": {ann_str}" + if default_str is not None: + param_str += f" = {default_str}" + + # If the field is a kw_only field, we need to add it to the kw_only_params + # This overwrites whether or not the class is kw_only decorated + if is_field: + kw_only = [k for k in value.keywords if k.arg == "kw_only"] # type: ignore[union-attr] + if kw_only: + if kw_only[0].value.bool_value(): + kw_only_params.append(param_str) + else: + params.append(param_str) + continue + # If kw_only decorated, we need to add all parameters to the kw_only_params + if kw_only_decorated: + if name in prev_kw_only_store: + prev_kw_only_store[name] = (ann_str, default_str) + else: + kw_only_params.append(param_str) + else: + # If the name was previously seen, overwrite that data + # pylint: disable-next=else-if-used + if name in prev_pos_only_store: + prev_pos_only_store[name] = (ann_str, default_str) + elif name in prev_kw_only_store: + params = [name] + params + prev_kw_only_store.pop(name) + else: + params.append(param_str) + + if not init_var: + assignments.append(assignment_str) + + prev_pos_only, prev_kw_only = _parse_arguments_into_strings( + prev_pos_only_store, prev_kw_only_store + ) + + # Construct the new init method paramter string + # First we do the positional only parameters, making sure to add the + # the self parameter and the comma to allow adding keyword only parameters + params_string = "" if "self" in prev_pos_only else "self, " + params_string += prev_pos_only + ", ".join(params) + if not params_string.endswith(", "): + params_string += ", " + + # Then we add the keyword only parameters + if prev_kw_only or kw_only_params: + params_string += "*, " + params_string += f"{prev_kw_only}{', '.join(kw_only_params)}" + + assignments_string = "\n ".join(assignments) if assignments else "pass" + return f"def __init__({params_string}) -> None:\n {assignments_string}" + + +def infer_dataclass_attribute( + node: nodes.Unknown, ctx: context.InferenceContext | None = None +) -> Iterator[InferenceResult]: + """Inference tip for an Unknown node that was dynamically generated to + represent a dataclass attribute. + + In the case that a default value is provided, that is inferred first. + Then, an Instance of the annotated class is yielded. + """ + assign = node.parent + if not isinstance(assign, nodes.AnnAssign): + yield Uninferable + return + + annotation, value = assign.annotation, assign.value + if value is not None: + yield from value.infer(context=ctx) + if annotation is not None: + yield from _infer_instance_from_annotation(annotation, ctx=ctx) + else: + yield Uninferable + + +def infer_dataclass_field_call( + node: nodes.Call, ctx: context.InferenceContext | None = None +) -> Iterator[InferenceResult]: + """Inference tip for dataclass field calls.""" + if not isinstance(node.parent, (nodes.AnnAssign, nodes.Assign)): + raise UseInferenceDefault + result = _get_field_default(node) + if not result: + yield Uninferable + else: + default_type, default = result + if default_type == "default": + yield from default.infer(context=ctx) + else: + new_call = parse(default.as_string()).body[0].value + new_call.parent = node.parent + yield from new_call.infer(context=ctx) + + +def _looks_like_dataclass_decorator( + node: nodes.NodeNG, decorator_names: frozenset[str] = DATACLASSES_DECORATORS +) -> bool: + """Return True if node looks like a dataclass decorator. + + Uses inference to lookup the value of the node, and if that fails, + matches against specific names. + """ + if isinstance(node, nodes.Call): # decorator with arguments + node = node.func + try: + inferred = next(node.infer()) + except (InferenceError, StopIteration): + inferred = Uninferable + + if isinstance(inferred, UninferableBase): + if isinstance(node, nodes.Name): + return node.name in decorator_names + if isinstance(node, nodes.Attribute): + return node.attrname in decorator_names + + return False + + return ( + isinstance(inferred, nodes.FunctionDef) + and inferred.name in decorator_names + and inferred.root().name in DATACLASS_MODULES + ) + + +def _looks_like_dataclass_attribute(node: nodes.Unknown) -> bool: + """Return True if node was dynamically generated as the child of an AnnAssign + statement. + """ + parent = node.parent + if not parent: + return False + + scope = parent.scope() + return ( + isinstance(parent, nodes.AnnAssign) + and isinstance(scope, nodes.ClassDef) + and is_decorated_with_dataclass(scope) + ) + + +def _looks_like_dataclass_field_call( + node: nodes.Call, check_scope: bool = True +) -> bool: + """Return True if node is calling dataclasses field or Field + from an AnnAssign statement directly in the body of a ClassDef. + + If check_scope is False, skips checking the statement and body. + """ + if check_scope: + stmt = node.statement(future=True) + scope = stmt.scope() + if not ( + isinstance(stmt, nodes.AnnAssign) + and stmt.value is not None + and isinstance(scope, nodes.ClassDef) + and is_decorated_with_dataclass(scope) + ): + return False + + try: + inferred = next(node.func.infer()) + except (InferenceError, StopIteration): + return False + + if not isinstance(inferred, nodes.FunctionDef): + return False + + return inferred.name == FIELD_NAME and inferred.root().name in DATACLASS_MODULES + + +def _get_field_default(field_call: nodes.Call) -> _FieldDefaultReturn: + """Return a the default value of a field call, and the corresponding keyword + argument name. + + field(default=...) results in the ... node + field(default_factory=...) results in a Call node with func ... and no arguments + + If neither or both arguments are present, return ("", None) instead, + indicating that there is not a valid default value. + """ + default, default_factory = None, None + for keyword in field_call.keywords: + if keyword.arg == "default": + default = keyword.value + elif keyword.arg == "default_factory": + default_factory = keyword.value + + if default is not None and default_factory is None: + return "default", default + + if default is None and default_factory is not None: + new_call = nodes.Call( + lineno=field_call.lineno, + col_offset=field_call.col_offset, + parent=field_call.parent, + ) + new_call.postinit(func=default_factory) + return "default_factory", new_call + + return None + + +def _is_class_var(node: nodes.NodeNG) -> bool: + """Return True if node is a ClassVar, with or without subscripting.""" + if PY39_PLUS: + try: + inferred = next(node.infer()) + except (InferenceError, StopIteration): + return False + + return getattr(inferred, "name", "") == "ClassVar" + + # Before Python 3.9, inference returns typing._SpecialForm instead of ClassVar. + # Our backup is to inspect the node's structure. + return isinstance(node, nodes.Subscript) and ( + isinstance(node.value, nodes.Name) + and node.value.name == "ClassVar" + or isinstance(node.value, nodes.Attribute) + and node.value.attrname == "ClassVar" + ) + + +def _is_keyword_only_sentinel(node: nodes.NodeNG) -> bool: + """Return True if node is the KW_ONLY sentinel.""" + if not PY310_PLUS: + return False + inferred = helpers.safe_infer(node) + return ( + isinstance(inferred, bases.Instance) + and inferred.qname() == "dataclasses._KW_ONLY_TYPE" + ) + + +def _is_init_var(node: nodes.NodeNG) -> bool: + """Return True if node is an InitVar, with or without subscripting.""" + try: + inferred = next(node.infer()) + except (InferenceError, StopIteration): + return False + + return getattr(inferred, "name", "") == "InitVar" + + +# Allowed typing classes for which we support inferring instances +_INFERABLE_TYPING_TYPES = frozenset( + ( + "Dict", + "FrozenSet", + "List", + "Set", + "Tuple", + ) +) + + +def _infer_instance_from_annotation( + node: nodes.NodeNG, ctx: context.InferenceContext | None = None +) -> Iterator[UninferableBase | bases.Instance]: + """Infer an instance corresponding to the type annotation represented by node. + + Currently has limited support for the typing module. + """ + klass = None + try: + klass = next(node.infer(context=ctx)) + except (InferenceError, StopIteration): + yield Uninferable + if not isinstance(klass, nodes.ClassDef): + yield Uninferable + elif klass.root().name in { + "typing", + "_collections_abc", + "", + }: # "" because of synthetic nodes in brain_typing.py + if klass.name in _INFERABLE_TYPING_TYPES: + yield klass.instantiate_class() + else: + yield Uninferable + else: + yield klass.instantiate_class() + + +AstroidManager().register_transform( + nodes.ClassDef, dataclass_transform, is_decorated_with_dataclass +) + +AstroidManager().register_transform( + nodes.Call, + inference_tip(infer_dataclass_field_call, raise_on_overwrite=True), + _looks_like_dataclass_field_call, +) + +AstroidManager().register_transform( + nodes.Unknown, + inference_tip(infer_dataclass_attribute, raise_on_overwrite=True), + _looks_like_dataclass_attribute, +) diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py new file mode 120000 index 00000000..ac23e5f9 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_dateutil.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/d4/83/6d/f3b5416bd42dd03cc4b88d6bedc12d7728390af3435087550affa3fe67 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_fstrings.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_fstrings.py new file mode 100644 index 00000000..c0df22e8 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_fstrings.py @@ -0,0 +1,61 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +from __future__ import annotations + +import collections.abc +from typing import TypeVar + +from astroid import nodes +from astroid.manager import AstroidManager + +_NodeT = TypeVar("_NodeT", bound=nodes.NodeNG) + + +def _clone_node_with_lineno( + node: _NodeT, parent: nodes.NodeNG, lineno: int | None +) -> _NodeT: + cls = node.__class__ + other_fields = node._other_fields + _astroid_fields = node._astroid_fields + init_params = {"lineno": lineno, "col_offset": node.col_offset, "parent": parent} + postinit_params = {param: getattr(node, param) for param in _astroid_fields} + if other_fields: + init_params.update({param: getattr(node, param) for param in other_fields}) + new_node = cls(**init_params) + if hasattr(node, "postinit") and _astroid_fields: + for param, child in postinit_params.items(): + if child and not isinstance(child, collections.abc.Sequence): + cloned_child = _clone_node_with_lineno( + node=child, lineno=new_node.lineno, parent=new_node + ) + postinit_params[param] = cloned_child + new_node.postinit(**postinit_params) + return new_node + + +def _transform_formatted_value( # pylint: disable=inconsistent-return-statements + node: nodes.FormattedValue, +) -> nodes.FormattedValue | None: + if node.value and node.value.lineno == 1: + if node.lineno != node.value.lineno: + new_node = nodes.FormattedValue( + lineno=node.lineno, col_offset=node.col_offset, parent=node.parent + ) + new_value = _clone_node_with_lineno( + node=node.value, lineno=node.lineno, parent=new_node + ) + new_node.postinit( + value=new_value, + conversion=node.conversion, + format_spec=node.format_spec, + ) + return new_node + + +# TODO: this fix tries to *patch* http://bugs.python.org/issue29051 +# The problem is that FormattedValue.value, which is a Name node, +# has wrong line numbers, usually 1. This creates problems for pylint, +# which expects correct line numbers for things such as message control. +AstroidManager().register_transform(nodes.FormattedValue, _transform_formatted_value) diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py new file mode 100644 index 00000000..f6a9830d --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_functools.py @@ -0,0 +1,164 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Astroid hooks for understanding functools library module.""" + +from __future__ import annotations + +from collections.abc import Iterator +from functools import partial +from itertools import chain + +from astroid import BoundMethod, arguments, extract_node, helpers, nodes, objects +from astroid.context import InferenceContext +from astroid.exceptions import InferenceError, UseInferenceDefault +from astroid.inference_tip import inference_tip +from astroid.interpreter import objectmodel +from astroid.manager import AstroidManager +from astroid.nodes.node_classes import AssignName, Attribute, Call, Name +from astroid.nodes.scoped_nodes import FunctionDef +from astroid.util import UninferableBase + +LRU_CACHE = "functools.lru_cache" + + +class LruWrappedModel(objectmodel.FunctionModel): + """Special attribute model for functions decorated with functools.lru_cache. + + The said decorators patches at decoration time some functions onto + the decorated function. + """ + + @property + def attr___wrapped__(self): + return self._instance + + @property + def attr_cache_info(self): + cache_info = extract_node( + """ + from functools import _CacheInfo + _CacheInfo(0, 0, 0, 0) + """ + ) + + class CacheInfoBoundMethod(BoundMethod): + def infer_call_result( + self, caller, context: InferenceContext | None = None + ): + yield helpers.safe_infer(cache_info) + + return CacheInfoBoundMethod(proxy=self._instance, bound=self._instance) + + @property + def attr_cache_clear(self): + node = extract_node("""def cache_clear(self): pass""") + return BoundMethod(proxy=node, bound=self._instance.parent.scope()) + + +def _transform_lru_cache(node, context: InferenceContext | None = None) -> None: + # TODO: this is not ideal, since the node should be immutable, + # but due to https://github.com/PyCQA/astroid/issues/354, + # there's not much we can do now. + # Replacing the node would work partially, because, + # in pylint, the old node would still be available, leading + # to spurious false positives. + node.special_attributes = LruWrappedModel()(node) + + +def _functools_partial_inference( + node: nodes.Call, context: InferenceContext | None = None +) -> Iterator[objects.PartialFunction]: + call = arguments.CallSite.from_call(node, context=context) + number_of_positional = len(call.positional_arguments) + if number_of_positional < 1: + raise UseInferenceDefault("functools.partial takes at least one argument") + if number_of_positional == 1 and not call.keyword_arguments: + raise UseInferenceDefault( + "functools.partial needs at least to have some filled arguments" + ) + + partial_function = call.positional_arguments[0] + try: + inferred_wrapped_function = next(partial_function.infer(context=context)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + if isinstance(inferred_wrapped_function, UninferableBase): + raise UseInferenceDefault("Cannot infer the wrapped function") + if not isinstance(inferred_wrapped_function, FunctionDef): + raise UseInferenceDefault("The wrapped function is not a function") + + # Determine if the passed keywords into the callsite are supported + # by the wrapped function. + if not inferred_wrapped_function.args: + function_parameters = [] + else: + function_parameters = chain( + inferred_wrapped_function.args.args or (), + inferred_wrapped_function.args.posonlyargs or (), + inferred_wrapped_function.args.kwonlyargs or (), + ) + parameter_names = { + param.name for param in function_parameters if isinstance(param, AssignName) + } + if set(call.keyword_arguments) - parameter_names: + raise UseInferenceDefault("wrapped function received unknown parameters") + + partial_function = objects.PartialFunction( + call, + name=inferred_wrapped_function.name, + lineno=inferred_wrapped_function.lineno, + col_offset=inferred_wrapped_function.col_offset, + parent=node.parent, + ) + partial_function.postinit( + args=inferred_wrapped_function.args, + body=inferred_wrapped_function.body, + decorators=inferred_wrapped_function.decorators, + returns=inferred_wrapped_function.returns, + type_comment_returns=inferred_wrapped_function.type_comment_returns, + type_comment_args=inferred_wrapped_function.type_comment_args, + doc_node=inferred_wrapped_function.doc_node, + ) + return iter((partial_function,)) + + +def _looks_like_lru_cache(node) -> bool: + """Check if the given function node is decorated with lru_cache.""" + if not node.decorators: + return False + for decorator in node.decorators.nodes: + if not isinstance(decorator, Call): + continue + if _looks_like_functools_member(decorator, "lru_cache"): + return True + return False + + +def _looks_like_functools_member(node, member) -> bool: + """Check if the given Call node is a functools.partial call.""" + if isinstance(node.func, Name): + return node.func.name == member + if isinstance(node.func, Attribute): + return ( + node.func.attrname == member + and isinstance(node.func.expr, Name) + and node.func.expr.name == "functools" + ) + return False + + +_looks_like_partial = partial(_looks_like_functools_member, member="partial") + + +AstroidManager().register_transform( + FunctionDef, _transform_lru_cache, _looks_like_lru_cache +) + + +AstroidManager().register_transform( + Call, + inference_tip(_functools_partial_inference), + _looks_like_partial, +) diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py new file mode 120000 index 00000000..407fd209 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_gi.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/2d/8b/6f/6dee3caa6cae8133f7d4272ba9f33a5f4adafe1ea7b5306e286088adc3 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py new file mode 120000 index 00000000..65abac63 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_hashlib.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/58/4e/df/409080dd1a756450c8b1e42386b2c472c62139fae46b63dcbefb99e938 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_http.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_http.py new file mode 120000 index 00000000..eab14dbd --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_http.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/99/82/dc/31d87f91ae5dd7522975683a5ab1b747b476c0de1b28e2d51b45b7bcf9 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py new file mode 120000 index 00000000..6f2fe7ec --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_hypothesis.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/2b/82/df/45bd8a4fa1f907ba06c405d02dc24597d6e478a7c55bc20471e1806dfc \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_io.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_io.py new file mode 120000 index 00000000..98140154 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_io.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/97/8f/7d/7db562e4751b3d6a32427d714c768f81171fd21b0a8e323dd01747cf24 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py new file mode 120000 index 00000000..41a361a7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_mechanize.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e3/9b/32/5634c69256234b8ebdbcfb2e1e6ca2734f4e4d9b5c26e33b1445dc3182 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py new file mode 120000 index 00000000..826c5ae9 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_multiprocessing.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/86/d1/d9/df34b2bf87aa87a73e00544fb761a5cb23dbe0983c3907c0a25a2347c4 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py new file mode 100644 index 00000000..36b70361 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_namedtuple_enum.py @@ -0,0 +1,639 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Astroid hooks for the Python standard library.""" + +from __future__ import annotations + +import functools +import keyword +import sys +from collections.abc import Iterator +from textwrap import dedent + +import astroid +from astroid import arguments, bases, inference_tip, nodes, util +from astroid.builder import AstroidBuilder, _extract_single_node, extract_node +from astroid.context import InferenceContext +from astroid.exceptions import ( + AstroidTypeError, + AstroidValueError, + InferenceError, + MroError, + UseInferenceDefault, +) +from astroid.manager import AstroidManager + +if sys.version_info >= (3, 8): + from typing import Final +else: + from typing_extensions import Final + + +ENUM_BASE_NAMES = { + "Enum", + "IntEnum", + "enum.Enum", + "enum.IntEnum", + "IntFlag", + "enum.IntFlag", +} +ENUM_QNAME: Final[str] = "enum.Enum" +TYPING_NAMEDTUPLE_QUALIFIED: Final = { + "typing.NamedTuple", + "typing_extensions.NamedTuple", +} +TYPING_NAMEDTUPLE_BASENAMES: Final = { + "NamedTuple", + "typing.NamedTuple", + "typing_extensions.NamedTuple", +} + + +def _infer_first(node, context): + if isinstance(node, util.UninferableBase): + raise UseInferenceDefault + try: + value = next(node.infer(context=context)) + except StopIteration as exc: + raise InferenceError from exc + if isinstance(value, util.UninferableBase): + raise UseInferenceDefault() + return value + + +def _find_func_form_arguments(node, context): + def _extract_namedtuple_arg_or_keyword( # pylint: disable=inconsistent-return-statements + position, key_name=None + ): + if len(args) > position: + return _infer_first(args[position], context) + if key_name and key_name in found_keywords: + return _infer_first(found_keywords[key_name], context) + + args = node.args + keywords = node.keywords + found_keywords = ( + {keyword.arg: keyword.value for keyword in keywords} if keywords else {} + ) + + name = _extract_namedtuple_arg_or_keyword(position=0, key_name="typename") + names = _extract_namedtuple_arg_or_keyword(position=1, key_name="field_names") + if name and names: + return name.value, names + + raise UseInferenceDefault() + + +def infer_func_form( + node: nodes.Call, + base_type: list[nodes.NodeNG], + context: InferenceContext | None = None, + enum: bool = False, +) -> tuple[nodes.ClassDef, str, list[str]]: + """Specific inference function for namedtuple or Python 3 enum.""" + # node is a Call node, class name as first argument and generated class + # attributes as second argument + + # namedtuple or enums list of attributes can be a list of strings or a + # whitespace-separate string + try: + name, names = _find_func_form_arguments(node, context) + try: + attributes: list[str] = names.value.replace(",", " ").split() + except AttributeError as exc: + # Handle attributes of NamedTuples + if not enum: + attributes = [] + fields = _get_namedtuple_fields(node) + if fields: + fields_node = extract_node(fields) + attributes = [ + _infer_first(const, context).value for const in fields_node.elts + ] + + # Handle attributes of Enums + else: + # Enums supports either iterator of (name, value) pairs + # or mappings. + if hasattr(names, "items") and isinstance(names.items, list): + attributes = [ + _infer_first(const[0], context).value + for const in names.items + if isinstance(const[0], nodes.Const) + ] + elif hasattr(names, "elts"): + # Enums can support either ["a", "b", "c"] + # or [("a", 1), ("b", 2), ...], but they can't + # be mixed. + if all(isinstance(const, nodes.Tuple) for const in names.elts): + attributes = [ + _infer_first(const.elts[0], context).value + for const in names.elts + if isinstance(const, nodes.Tuple) + ] + else: + attributes = [ + _infer_first(const, context).value for const in names.elts + ] + else: + raise AttributeError from exc + if not attributes: + raise AttributeError from exc + except (AttributeError, InferenceError) as exc: + raise UseInferenceDefault from exc + + if not enum: + # namedtuple maps sys.intern(str()) over over field_names + attributes = [str(attr) for attr in attributes] + # XXX this should succeed *unless* __str__/__repr__ is incorrect or throws + # in which case we should not have inferred these values and raised earlier + attributes = [attr for attr in attributes if " " not in attr] + + # If we can't infer the name of the class, don't crash, up to this point + # we know it is a namedtuple anyway. + name = name or "Uninferable" + # we want to return a Class node instance with proper attributes set + class_node = nodes.ClassDef(name) + # A typical ClassDef automatically adds its name to the parent scope, + # but doing so causes problems, so defer setting parent until after init + # see: https://github.com/PyCQA/pylint/issues/5982 + class_node.parent = node.parent + class_node.postinit( + # set base class=tuple + bases=base_type, + body=[], + decorators=None, + ) + # XXX add __init__(*attributes) method + for attr in attributes: + fake_node = nodes.EmptyNode() + fake_node.parent = class_node + fake_node.attrname = attr + class_node.instance_attrs[attr] = [fake_node] + return class_node, name, attributes + + +def _has_namedtuple_base(node): + """Predicate for class inference tip. + + :type node: ClassDef + :rtype: bool + """ + return set(node.basenames) & TYPING_NAMEDTUPLE_BASENAMES + + +def _looks_like(node, name) -> bool: + func = node.func + if isinstance(func, nodes.Attribute): + return func.attrname == name + if isinstance(func, nodes.Name): + return func.name == name + return False + + +_looks_like_namedtuple = functools.partial(_looks_like, name="namedtuple") +_looks_like_enum = functools.partial(_looks_like, name="Enum") +_looks_like_typing_namedtuple = functools.partial(_looks_like, name="NamedTuple") + + +def infer_named_tuple( + node: nodes.Call, context: InferenceContext | None = None +) -> Iterator[nodes.ClassDef]: + """Specific inference function for namedtuple Call node.""" + tuple_base_name: list[nodes.NodeNG] = [nodes.Name(name="tuple", parent=node.root())] + class_node, name, attributes = infer_func_form( + node, tuple_base_name, context=context + ) + call_site = arguments.CallSite.from_call(node, context=context) + node = extract_node("import collections; collections.namedtuple") + try: + func = next(node.infer()) + except StopIteration as e: + raise InferenceError(node=node) from e + try: + rename = next(call_site.infer_argument(func, "rename", context)).bool_value() + except (InferenceError, StopIteration): + rename = False + + try: + attributes = _check_namedtuple_attributes(name, attributes, rename) + except AstroidTypeError as exc: + raise UseInferenceDefault("TypeError: " + str(exc)) from exc + except AstroidValueError as exc: + raise UseInferenceDefault("ValueError: " + str(exc)) from exc + + replace_args = ", ".join(f"{arg}=None" for arg in attributes) + field_def = ( + " {name} = property(lambda self: self[{index:d}], " + "doc='Alias for field number {index:d}')" + ) + field_defs = "\n".join( + field_def.format(name=name, index=index) + for index, name in enumerate(attributes) + ) + fake = AstroidBuilder(AstroidManager()).string_build( + f""" +class {name}(tuple): + __slots__ = () + _fields = {attributes!r} + def _asdict(self): + return self.__dict__ + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + return new(cls, iterable) + def _replace(self, {replace_args}): + return self + def __getnewargs__(self): + return tuple(self) +{field_defs} + """ + ) + class_node.locals["_asdict"] = fake.body[0].locals["_asdict"] + class_node.locals["_make"] = fake.body[0].locals["_make"] + class_node.locals["_replace"] = fake.body[0].locals["_replace"] + class_node.locals["_fields"] = fake.body[0].locals["_fields"] + for attr in attributes: + class_node.locals[attr] = fake.body[0].locals[attr] + # we use UseInferenceDefault, we can't be a generator so return an iterator + return iter([class_node]) + + +def _get_renamed_namedtuple_attributes(field_names): + names = list(field_names) + seen = set() + for i, name in enumerate(field_names): + if ( + not all(c.isalnum() or c == "_" for c in name) + or keyword.iskeyword(name) + or not name + or name[0].isdigit() + or name.startswith("_") + or name in seen + ): + names[i] = "_%d" % i + seen.add(name) + return tuple(names) + + +def _check_namedtuple_attributes(typename, attributes, rename=False): + attributes = tuple(attributes) + if rename: + attributes = _get_renamed_namedtuple_attributes(attributes) + + # The following snippet is derived from the CPython Lib/collections/__init__.py sources + # + for name in (typename,) + attributes: + if not isinstance(name, str): + raise AstroidTypeError("Type names and field names must be strings") + if not name.isidentifier(): + raise AstroidValueError( + "Type names and field names must be valid" + f"identifiers: {name!r}" + ) + if keyword.iskeyword(name): + raise AstroidValueError( + f"Type names and field names cannot be a keyword: {name!r}" + ) + + seen = set() + for name in attributes: + if name.startswith("_") and not rename: + raise AstroidValueError( + f"Field names cannot start with an underscore: {name!r}" + ) + if name in seen: + raise AstroidValueError(f"Encountered duplicate field name: {name!r}") + seen.add(name) + # + + return attributes + + +def infer_enum( + node: nodes.Call, context: InferenceContext | None = None +) -> Iterator[bases.Instance]: + """Specific inference function for enum Call node.""" + # Raise `UseInferenceDefault` if `node` is a call to a a user-defined Enum. + try: + inferred = node.func.infer(context) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + + if not any( + isinstance(item, nodes.ClassDef) and item.qname() == ENUM_QNAME + for item in inferred + ): + raise UseInferenceDefault + + enum_meta = _extract_single_node( + """ + class EnumMeta(object): + 'docstring' + def __call__(self, node): + class EnumAttribute(object): + name = '' + value = 0 + return EnumAttribute() + def __iter__(self): + class EnumAttribute(object): + name = '' + value = 0 + return [EnumAttribute()] + def __reversed__(self): + class EnumAttribute(object): + name = '' + value = 0 + return (EnumAttribute, ) + def __next__(self): + return next(iter(self)) + def __getitem__(self, attr): + class Value(object): + @property + def name(self): + return '' + @property + def value(self): + return attr + + return Value() + __members__ = [''] + """ + ) + class_node = infer_func_form(node, [enum_meta], context=context, enum=True)[0] + return iter([class_node.instantiate_class()]) + + +INT_FLAG_ADDITION_METHODS = """ + def __or__(self, other): + return {name}(self.value | other.value) + def __and__(self, other): + return {name}(self.value & other.value) + def __xor__(self, other): + return {name}(self.value ^ other.value) + def __add__(self, other): + return {name}(self.value + other.value) + def __div__(self, other): + return {name}(self.value / other.value) + def __invert__(self): + return {name}(~self.value) + def __mul__(self, other): + return {name}(self.value * other.value) +""" + + +def infer_enum_class(node: nodes.ClassDef) -> nodes.ClassDef: + """Specific inference for enums.""" + for basename in (b for cls in node.mro() for b in cls.basenames): + if node.root().name == "enum": + # Skip if the class is directly from enum module. + break + dunder_members = {} + target_names = set() + for local, values in node.locals.items(): + if any(not isinstance(value, nodes.AssignName) for value in values): + continue + + stmt = values[0].statement(future=True) + if isinstance(stmt, nodes.Assign): + if isinstance(stmt.targets[0], nodes.Tuple): + targets = stmt.targets[0].itered() + else: + targets = stmt.targets + elif isinstance(stmt, nodes.AnnAssign): + targets = [stmt.target] + else: + continue + + inferred_return_value = None + if stmt.value is not None: + if isinstance(stmt.value, nodes.Const): + if isinstance(stmt.value.value, str): + inferred_return_value = repr(stmt.value.value) + else: + inferred_return_value = stmt.value.value + else: + inferred_return_value = stmt.value.as_string() + + new_targets = [] + for target in targets: + if isinstance(target, nodes.Starred): + continue + target_names.add(target.name) + # Replace all the assignments with our mocked class. + classdef = dedent( + """ + class {name}({types}): + @property + def value(self): + return {return_value} + @property + def name(self): + return "{name}" + """.format( + name=target.name, + types=", ".join(node.basenames), + return_value=inferred_return_value, + ) + ) + if "IntFlag" in basename: + # Alright, we need to add some additional methods. + # Unfortunately we still can't infer the resulting objects as + # Enum members, but once we'll be able to do that, the following + # should result in some nice symbolic execution + classdef += INT_FLAG_ADDITION_METHODS.format(name=target.name) + + fake = AstroidBuilder( + AstroidManager(), apply_transforms=False + ).string_build(classdef)[target.name] + fake.parent = target.parent + for method in node.mymethods(): + fake.locals[method.name] = [method] + new_targets.append(fake.instantiate_class()) + dunder_members[local] = fake + node.locals[local] = new_targets + + # The undocumented `_value2member_map_` member: + node.locals["_value2member_map_"] = [nodes.Dict(parent=node)] + + members = nodes.Dict(parent=node) + members.postinit( + [ + (nodes.Const(k, parent=members), nodes.Name(v.name, parent=members)) + for k, v in dunder_members.items() + ] + ) + node.locals["__members__"] = [members] + # The enum.Enum class itself defines two @DynamicClassAttribute data-descriptors + # "name" and "value" (which we override in the mocked class for each enum member + # above). When dealing with inference of an arbitrary instance of the enum + # class, e.g. in a method defined in the class body like: + # class SomeEnum(enum.Enum): + # def method(self): + # self.name # <- here + # In the absence of an enum member called "name" or "value", these attributes + # should resolve to the descriptor on that particular instance, i.e. enum member. + # For "value", we have no idea what that should be, but for "name", we at least + # know that it should be a string, so infer that as a guess. + if "name" not in target_names: + code = dedent( + """ + @property + def name(self): + return '' + """ + ) + name_dynamicclassattr = AstroidBuilder(AstroidManager()).string_build(code)[ + "name" + ] + node.locals["name"] = [name_dynamicclassattr] + break + return node + + +def infer_typing_namedtuple_class(class_node, context: InferenceContext | None = None): + """Infer a subclass of typing.NamedTuple.""" + # Check if it has the corresponding bases + annassigns_fields = [ + annassign.target.name + for annassign in class_node.body + if isinstance(annassign, nodes.AnnAssign) + ] + code = dedent( + """ + from collections import namedtuple + namedtuple({typename!r}, {fields!r}) + """ + ).format(typename=class_node.name, fields=",".join(annassigns_fields)) + node = extract_node(code) + try: + generated_class_node = next(infer_named_tuple(node, context)) + except StopIteration as e: + raise InferenceError(node=node, context=context) from e + for method in class_node.mymethods(): + generated_class_node.locals[method.name] = [method] + + for body_node in class_node.body: + if isinstance(body_node, nodes.Assign): + for target in body_node.targets: + attr = target.name + generated_class_node.locals[attr] = class_node.locals[attr] + elif isinstance(body_node, nodes.ClassDef): + generated_class_node.locals[body_node.name] = [body_node] + + return iter((generated_class_node,)) + + +def infer_typing_namedtuple_function(node, context: InferenceContext | None = None): + """ + Starting with python3.9, NamedTuple is a function of the typing module. + The class NamedTuple is build dynamically through a call to `type` during + initialization of the `_NamedTuple` variable. + """ + klass = extract_node( + """ + from typing import _NamedTuple + _NamedTuple + """ + ) + return klass.infer(context) + + +def infer_typing_namedtuple( + node: nodes.Call, context: InferenceContext | None = None +) -> Iterator[nodes.ClassDef]: + """Infer a typing.NamedTuple(...) call.""" + # This is essentially a namedtuple with different arguments + # so we extract the args and infer a named tuple. + try: + func = next(node.func.infer()) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + + if func.qname() not in TYPING_NAMEDTUPLE_QUALIFIED: + raise UseInferenceDefault + + if len(node.args) != 2: + raise UseInferenceDefault + + if not isinstance(node.args[1], (nodes.List, nodes.Tuple)): + raise UseInferenceDefault + + return infer_named_tuple(node, context) + + +def _get_namedtuple_fields(node: nodes.Call) -> str: + """Get and return fields of a NamedTuple in code-as-a-string. + + Because the fields are represented in their code form we can + extract a node from them later on. + """ + names = [] + container = None + try: + container = next(node.args[1].infer()) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + # We pass on IndexError as we'll try to infer 'field_names' from the keywords + except IndexError: + pass + if not container: + for keyword_node in node.keywords: + if keyword_node.arg == "field_names": + try: + container = next(keyword_node.value.infer()) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + break + if not isinstance(container, nodes.BaseContainer): + raise UseInferenceDefault + for elt in container.elts: + if isinstance(elt, nodes.Const): + names.append(elt.as_string()) + continue + if not isinstance(elt, (nodes.List, nodes.Tuple)): + raise UseInferenceDefault + if len(elt.elts) != 2: + raise UseInferenceDefault + names.append(elt.elts[0].as_string()) + + if names: + field_names = f"({','.join(names)},)" + else: + field_names = "" + return field_names + + +def _is_enum_subclass(cls: astroid.ClassDef) -> bool: + """Return whether cls is a subclass of an Enum.""" + try: + return any( + klass.name in ENUM_BASE_NAMES + and getattr(klass.root(), "name", None) == "enum" + for klass in cls.mro() + ) + except MroError: + return False + + +AstroidManager().register_transform( + nodes.Call, inference_tip(infer_named_tuple), _looks_like_namedtuple +) +AstroidManager().register_transform( + nodes.Call, inference_tip(infer_enum), _looks_like_enum +) +AstroidManager().register_transform( + nodes.ClassDef, infer_enum_class, predicate=_is_enum_subclass +) +AstroidManager().register_transform( + nodes.ClassDef, inference_tip(infer_typing_namedtuple_class), _has_namedtuple_base +) +AstroidManager().register_transform( + nodes.FunctionDef, + inference_tip(infer_typing_namedtuple_function), + lambda node: node.name == "NamedTuple" + and getattr(node.root(), "name", None) == "typing", +) +AstroidManager().register_transform( + nodes.Call, inference_tip(infer_typing_namedtuple), _looks_like_typing_namedtuple +) diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_nose.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_nose.py new file mode 120000 index 00000000..6a28d34a --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_nose.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/0c/25/30/2b3475e127b9b45face7800cc05a11be1b071ff9dfe9a82b87deba5694 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py new file mode 120000 index 00000000..20cbc20d --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_einsumfunc.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/0a/e6/6d/6586460e13f3578ea5d71c8d97fed17cc09a4386d8c99809f5ff0ac5c4 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py new file mode 120000 index 00000000..950dd190 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_fromnumeric.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/dd/41/51/b4ae54e7501540dc5ac1ff4496116fdad8b3863e517f20929cf4f7bb9e \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py new file mode 120000 index 00000000..3d546a11 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_function_base.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/fe/4f/96/450ff1d1010786fafd230b932e7a46a0260e54ed06007ededc3ca81cd4 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py new file mode 120000 index 00000000..f157b8a8 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_multiarray.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/34/8c/67/be7a3e9a1a7e3fa20a7f245a28e16d887c7c1fd7047218e0c7af6886dd \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py new file mode 120000 index 00000000..6cccf0f6 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numeric.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/54/07/39/d5477c403ba3b21dbd7be4502b948170a5bf8f38d92059a7b42b2508a5 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py new file mode 120000 index 00000000..c88bca1f --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_numerictypes.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/1d/52/7f/a5a4308e1d95f2acf426e7a2e9bd99466fdd9832687620ca7c0704fa61 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py new file mode 120000 index 00000000..b7bd98f5 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_core_umath.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/d8/48/57/9ad8b0f3041c754b5b9e52cb4e31c0387a50d5cf415c775cea41c919b0 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py new file mode 120000 index 00000000..06f8b337 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ma.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/d5/71/ef/647d42dc21174e2447dc1271a458b3b60a8160419df490c5aae1ac50c9 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py new file mode 120000 index 00000000..a178f9d9 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_ndarray.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/1e/88/5a/c266adce934c38902161b10f24665d09336660ed4ecfe20fc580dde663 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py new file mode 120000 index 00000000..e6b8c872 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_random_mtrand.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/5a/ba/66/f525fc907d17b24f5bdeaa51d2b86cf8c11bd08e5a37813eebac242e43 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py new file mode 120000 index 00000000..3004c436 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_numpy_utils.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/c2/04/91/52019d9fc228d00a137ba6af965df9a1c791ab64ba657a8dbe2963bc62 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py new file mode 120000 index 00000000..55ddc609 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_pathlib.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/11/cf/17/ba8fb82081b6c07e3cef973c7365defc8d1fa8f6cfaa828224eb6d5651 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py new file mode 120000 index 00000000..d509e629 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_pkg_resources.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/5b/74/38/1ba9984d8b4963d89642fdef10ef3f96998b1408a7f5a40bb4144bc407 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py new file mode 120000 index 00000000..730108a5 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_pytest.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/b2/e4/c3/047089fe9e05ed31978f0549005b6200ff95964144da09dc5e32d140a3 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py new file mode 120000 index 00000000..b23a0d96 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_qt.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/aa/f7/b1/5782056b860a2f4372a3aab19b3fc37c30cdc05570053246003b559222 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_random.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_random.py new file mode 120000 index 00000000..7b00c741 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_random.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/82/b5/c9/ad82f7ad9a739bc4a6b849d690bc1636a58ab5de589b06c1a15ea68cde \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_re.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_re.py new file mode 120000 index 00000000..09b3aab2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_re.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/69/25/3e/1f2116b02adcd6c7a2168859c39e22bdc36d0befc0ca20a124e494fbb4 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py new file mode 120000 index 00000000..6b7f5130 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_regex.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/53/35/b6/945d9cb5b743f015f87e40bb3c5b570dd18d51c741299c7bb0988ee19d \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py new file mode 120000 index 00000000..c20c3bf7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_responses.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/26/7e/e3/4bc2f6e93e619d4c0e7075f8ebd153915b37fd8cfc3a25513c0c77063a \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py new file mode 120000 index 00000000..8c2f8c62 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_scipy_signal.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/32/2f/77/0fafa3f78b6a86a0c709b69b56cdff783021b62f3c08bd7facebe71bf2 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py new file mode 120000 index 00000000..207536e5 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_signal.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/d7/ca/ce/0a02d598cd6bb5ce811583f1e2ace24ab7cfb7a0ab3fe0367c9c8c5816 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_six.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_six.py new file mode 120000 index 00000000..318ea6d1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_six.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/31/64/26/f35b3d65bfe2d4d22a6373275ee6b096c5ea80ad410e3d6f04367e31d1 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py new file mode 120000 index 00000000..b8c81fdd --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_sqlalchemy.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a2/72/80/c60298f8660b7b6bfa3d52b700f067ff3f2dc6890465f0dc5eddf71ae1 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py new file mode 120000 index 00000000..d9fff5ac --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_ssl.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/f0/c7/57/865f59bd855ee94fe0b524ed8279bf0e779dd8873d31b81f4a3e7a4475 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py new file mode 120000 index 00000000..56908feb --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_subprocess.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/29/ac/ab/8b874cab0b576d94ce841d04d39a7672429cee12063edd63037cefdb10 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py new file mode 120000 index 00000000..c758daa7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_threading.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ac/df/9d/43fe19a3401938e88f4228311773448260c2ec363eaf4ba32cde96e767 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_type.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_type.py new file mode 120000 index 00000000..d0261860 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_type.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/55/4c/10/b1e84521286f2ab9bdf32c1ae7e806cfb78bc099c11c2a5076b7863863 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py new file mode 100644 index 00000000..e0a9dfd1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_typing.py @@ -0,0 +1,438 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Astroid hooks for typing.py support.""" + +from __future__ import annotations + +import sys +import typing +from collections.abc import Iterator +from functools import partial + +from astroid import context, extract_node, inference_tip +from astroid.builder import _extract_single_node +from astroid.const import PY38_PLUS, PY39_PLUS +from astroid.exceptions import ( + AttributeInferenceError, + InferenceError, + UseInferenceDefault, +) +from astroid.manager import AstroidManager +from astroid.nodes.node_classes import ( + Assign, + AssignName, + Attribute, + Call, + Const, + JoinedStr, + Name, + NodeNG, + Subscript, + Tuple, +) +from astroid.nodes.scoped_nodes import ClassDef, FunctionDef + +if sys.version_info >= (3, 8): + from typing import Final +else: + from typing_extensions import Final + +TYPING_TYPEVARS = {"TypeVar", "NewType"} +TYPING_TYPEVARS_QUALIFIED: Final = { + "typing.TypeVar", + "typing.NewType", + "typing_extensions.TypeVar", +} +TYPING_TYPEDDICT_QUALIFIED: Final = {"typing.TypedDict", "typing_extensions.TypedDict"} +TYPING_TYPE_TEMPLATE = """ +class Meta(type): + def __getitem__(self, item): + return self + + @property + def __args__(self): + return () + +class {0}(metaclass=Meta): + pass +""" +TYPING_MEMBERS = set(getattr(typing, "__all__", [])) + +TYPING_ALIAS = frozenset( + ( + "typing.Hashable", + "typing.Awaitable", + "typing.Coroutine", + "typing.AsyncIterable", + "typing.AsyncIterator", + "typing.Iterable", + "typing.Iterator", + "typing.Reversible", + "typing.Sized", + "typing.Container", + "typing.Collection", + "typing.Callable", + "typing.AbstractSet", + "typing.MutableSet", + "typing.Mapping", + "typing.MutableMapping", + "typing.Sequence", + "typing.MutableSequence", + "typing.ByteString", + "typing.Tuple", + "typing.List", + "typing.Deque", + "typing.Set", + "typing.FrozenSet", + "typing.MappingView", + "typing.KeysView", + "typing.ItemsView", + "typing.ValuesView", + "typing.ContextManager", + "typing.AsyncContextManager", + "typing.Dict", + "typing.DefaultDict", + "typing.OrderedDict", + "typing.Counter", + "typing.ChainMap", + "typing.Generator", + "typing.AsyncGenerator", + "typing.Type", + "typing.Pattern", + "typing.Match", + ) +) + +CLASS_GETITEM_TEMPLATE = """ +@classmethod +def __class_getitem__(cls, item): + return cls +""" + + +def looks_like_typing_typevar_or_newtype(node) -> bool: + func = node.func + if isinstance(func, Attribute): + return func.attrname in TYPING_TYPEVARS + if isinstance(func, Name): + return func.name in TYPING_TYPEVARS + return False + + +def infer_typing_typevar_or_newtype(node, context_itton=None): + """Infer a typing.TypeVar(...) or typing.NewType(...) call.""" + try: + func = next(node.func.infer(context=context_itton)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + + if func.qname() not in TYPING_TYPEVARS_QUALIFIED: + raise UseInferenceDefault + if not node.args: + raise UseInferenceDefault + # Cannot infer from a dynamic class name (f-string) + if isinstance(node.args[0], JoinedStr): + raise UseInferenceDefault + + typename = node.args[0].as_string().strip("'") + node = extract_node(TYPING_TYPE_TEMPLATE.format(typename)) + return node.infer(context=context_itton) + + +def _looks_like_typing_subscript(node) -> bool: + """Try to figure out if a Subscript node *might* be a typing-related subscript.""" + if isinstance(node, Name): + return node.name in TYPING_MEMBERS + if isinstance(node, Attribute): + return node.attrname in TYPING_MEMBERS + if isinstance(node, Subscript): + return _looks_like_typing_subscript(node.value) + return False + + +def infer_typing_attr( + node: Subscript, ctx: context.InferenceContext | None = None +) -> Iterator[ClassDef]: + """Infer a typing.X[...] subscript.""" + try: + value = next(node.value.infer()) # type: ignore[union-attr] # value shouldn't be None for Subscript. + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + + if not value.qname().startswith("typing.") or value.qname() in TYPING_ALIAS: + # If typing subscript belongs to an alias handle it separately. + raise UseInferenceDefault + + if isinstance(value, ClassDef) and value.qname() in { + "typing.Generic", + "typing.Annotated", + "typing_extensions.Annotated", + }: + # typing.Generic and typing.Annotated (PY39) are subscriptable + # through __class_getitem__. Since astroid can't easily + # infer the native methods, replace them for an easy inference tip + func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE) + value.locals["__class_getitem__"] = [func_to_add] + if ( + isinstance(node.parent, ClassDef) + and node in node.parent.bases + and getattr(node.parent, "__cache", None) + ): + # node.parent.slots is evaluated and cached before the inference tip + # is first applied. Remove the last result to allow a recalculation of slots + cache = node.parent.__cache # type: ignore[attr-defined] # Unrecognized getattr + if cache.get(node.parent.slots) is not None: + del cache[node.parent.slots] + return iter([value]) + + node = extract_node(TYPING_TYPE_TEMPLATE.format(value.qname().split(".")[-1])) + return node.infer(context=ctx) + + +def _looks_like_typedDict( # pylint: disable=invalid-name + node: FunctionDef | ClassDef, +) -> bool: + """Check if node is TypedDict FunctionDef.""" + return node.qname() in TYPING_TYPEDDICT_QUALIFIED + + +def infer_old_typedDict( # pylint: disable=invalid-name + node: ClassDef, ctx: context.InferenceContext | None = None +) -> Iterator[ClassDef]: + func_to_add = _extract_single_node("dict") + node.locals["__call__"] = [func_to_add] + return iter([node]) + + +def infer_typedDict( # pylint: disable=invalid-name + node: FunctionDef, ctx: context.InferenceContext | None = None +) -> Iterator[ClassDef]: + """Replace TypedDict FunctionDef with ClassDef.""" + class_def = ClassDef( + name="TypedDict", + lineno=node.lineno, + col_offset=node.col_offset, + parent=node.parent, + ) + class_def.postinit(bases=[extract_node("dict")], body=[], decorators=None) + func_to_add = _extract_single_node("dict") + class_def.locals["__call__"] = [func_to_add] + return iter([class_def]) + + +def _looks_like_typing_alias(node: Call) -> bool: + """ + Returns True if the node corresponds to a call to _alias function. + + For example : + + MutableSet = _alias(collections.abc.MutableSet, T) + + :param node: call node + """ + return ( + isinstance(node.func, Name) + and node.func.name == "_alias" + and ( + # _alias function works also for builtins object such as list and dict + isinstance(node.args[0], (Attribute, Name)) + ) + ) + + +def _forbid_class_getitem_access(node: ClassDef) -> None: + """Disable the access to __class_getitem__ method for the node in parameters.""" + + def full_raiser(origin_func, attr, *args, **kwargs): + """ + Raises an AttributeInferenceError in case of access to __class_getitem__ method. + Otherwise, just call origin_func. + """ + if attr == "__class_getitem__": + raise AttributeInferenceError("__class_getitem__ access is not allowed") + return origin_func(attr, *args, **kwargs) + + try: + node.getattr("__class_getitem__") + # If we are here, then we are sure to modify an object that does have + # __class_getitem__ method (which origin is the protocol defined in + # collections module) whereas the typing module considers it should not. + # We do not want __class_getitem__ to be found in the classdef + partial_raiser = partial(full_raiser, node.getattr) + node.getattr = partial_raiser + except AttributeInferenceError: + pass + + +def infer_typing_alias( + node: Call, ctx: context.InferenceContext | None = None +) -> Iterator[ClassDef]: + """ + Infers the call to _alias function + Insert ClassDef, with same name as aliased class, + in mro to simulate _GenericAlias. + + :param node: call node + :param context: inference context + """ + if ( + not isinstance(node.parent, Assign) + or not len(node.parent.targets) == 1 + or not isinstance(node.parent.targets[0], AssignName) + ): + raise UseInferenceDefault + try: + res = next(node.args[0].infer(context=ctx)) + except StopIteration as e: + raise InferenceError(node=node.args[0], context=ctx) from e + + assign_name = node.parent.targets[0] + + class_def = ClassDef( + name=assign_name.name, + lineno=assign_name.lineno, + col_offset=assign_name.col_offset, + parent=node.parent, + ) + if isinstance(res, ClassDef): + # Only add `res` as base if it's a `ClassDef` + # This isn't the case for `typing.Pattern` and `typing.Match` + class_def.postinit(bases=[res], body=[], decorators=None) + + maybe_type_var = node.args[1] + if ( + not PY39_PLUS + and not (isinstance(maybe_type_var, Tuple) and not maybe_type_var.elts) + or PY39_PLUS + and isinstance(maybe_type_var, Const) + and maybe_type_var.value > 0 + ): + # If typing alias is subscriptable, add `__class_getitem__` to ClassDef + func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE) + class_def.locals["__class_getitem__"] = [func_to_add] + else: + # If not, make sure that `__class_getitem__` access is forbidden. + # This is an issue in cases where the aliased class implements it, + # but the typing alias isn't subscriptable. E.g., `typing.ByteString` for PY39+ + _forbid_class_getitem_access(class_def) + return iter([class_def]) + + +def _looks_like_special_alias(node: Call) -> bool: + """Return True if call is for Tuple or Callable alias. + + In PY37 and PY38 the call is to '_VariadicGenericAlias' with 'tuple' as + first argument. In PY39+ it is replaced by a call to '_TupleType'. + + PY37: Tuple = _VariadicGenericAlias(tuple, (), inst=False, special=True) + PY39: Tuple = _TupleType(tuple, -1, inst=False, name='Tuple') + + PY37: Callable = _VariadicGenericAlias(collections.abc.Callable, (), special=True) + PY39: Callable = _CallableType(collections.abc.Callable, 2) + """ + return isinstance(node.func, Name) and ( + not PY39_PLUS + and node.func.name == "_VariadicGenericAlias" + and ( + isinstance(node.args[0], Name) + and node.args[0].name == "tuple" + or isinstance(node.args[0], Attribute) + and node.args[0].as_string() == "collections.abc.Callable" + ) + or PY39_PLUS + and ( + node.func.name == "_TupleType" + and isinstance(node.args[0], Name) + and node.args[0].name == "tuple" + or node.func.name == "_CallableType" + and isinstance(node.args[0], Attribute) + and node.args[0].as_string() == "collections.abc.Callable" + ) + ) + + +def infer_special_alias( + node: Call, ctx: context.InferenceContext | None = None +) -> Iterator[ClassDef]: + """Infer call to tuple alias as new subscriptable class typing.Tuple.""" + if not ( + isinstance(node.parent, Assign) + and len(node.parent.targets) == 1 + and isinstance(node.parent.targets[0], AssignName) + ): + raise UseInferenceDefault + try: + res = next(node.args[0].infer(context=ctx)) + except StopIteration as e: + raise InferenceError(node=node.args[0], context=ctx) from e + + assign_name = node.parent.targets[0] + class_def = ClassDef( + name=assign_name.name, + parent=node.parent, + ) + class_def.postinit(bases=[res], body=[], decorators=None) + func_to_add = _extract_single_node(CLASS_GETITEM_TEMPLATE) + class_def.locals["__class_getitem__"] = [func_to_add] + return iter([class_def]) + + +def _looks_like_typing_cast(node: Call) -> bool: + return isinstance(node, Call) and ( + isinstance(node.func, Name) + and node.func.name == "cast" + or isinstance(node.func, Attribute) + and node.func.attrname == "cast" + ) + + +def infer_typing_cast( + node: Call, ctx: context.InferenceContext | None = None +) -> Iterator[NodeNG]: + """Infer call to cast() returning same type as casted-from var.""" + if not isinstance(node.func, (Name, Attribute)): + raise UseInferenceDefault + + try: + func = next(node.func.infer(context=ctx)) + except (InferenceError, StopIteration) as exc: + raise UseInferenceDefault from exc + if ( + not isinstance(func, FunctionDef) + or func.qname() != "typing.cast" + or len(node.args) != 2 + ): + raise UseInferenceDefault + + return node.args[1].infer(context=ctx) + + +AstroidManager().register_transform( + Call, + inference_tip(infer_typing_typevar_or_newtype), + looks_like_typing_typevar_or_newtype, +) +AstroidManager().register_transform( + Subscript, inference_tip(infer_typing_attr), _looks_like_typing_subscript +) +AstroidManager().register_transform( + Call, inference_tip(infer_typing_cast), _looks_like_typing_cast +) + +if PY39_PLUS: + AstroidManager().register_transform( + FunctionDef, inference_tip(infer_typedDict), _looks_like_typedDict + ) +elif PY38_PLUS: + AstroidManager().register_transform( + ClassDef, inference_tip(infer_old_typedDict), _looks_like_typedDict + ) + +AstroidManager().register_transform( + Call, inference_tip(infer_typing_alias), _looks_like_typing_alias +) +AstroidManager().register_transform( + Call, inference_tip(infer_special_alias), _looks_like_special_alias +) diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py new file mode 120000 index 00000000..bddfa19b --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_unittest.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/5f/fc/6a/23bd1bafb26022b5225303f81e6f664300b1f6305fbcd5b9874c26a7ed \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py b/venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py new file mode 120000 index 00000000..05b44b33 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/brain_uuid.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/b7/47/e3/fd078dcdb3e06b4dc64b460ff76b238c49b1374e2b96067876df362471 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/brain/helpers.py b/venv/lib/python3.10/site-packages/astroid/brain/helpers.py new file mode 120000 index 00000000..6a5a7472 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/brain/helpers.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/aa/d4/e1/5c6db39656859bc46297c3236dbeb2b913d4ab2f9e957cf842ef14297d \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/builder.py b/venv/lib/python3.10/site-packages/astroid/builder.py new file mode 100644 index 00000000..d115feb4 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/builder.py @@ -0,0 +1,494 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""The AstroidBuilder makes astroid from living object and / or from _ast. + +The builder is not thread safe and can't be used to parse different sources +at the same time. +""" + +from __future__ import annotations + +import ast +import os +import textwrap +import types +from collections.abc import Iterator, Sequence +from io import TextIOWrapper +from tokenize import detect_encoding +from typing import TYPE_CHECKING + +from astroid import bases, modutils, nodes, raw_building, rebuilder, util +from astroid._ast import ParserModule, get_parser_module +from astroid.exceptions import AstroidBuildingError, AstroidSyntaxError, InferenceError +from astroid.manager import AstroidManager + +if TYPE_CHECKING: + from astroid import objects +else: + objects = util.lazy_import("objects") + + +# The name of the transient function that is used to +# wrap expressions to be extracted when calling +# extract_node. +_TRANSIENT_FUNCTION = "__" + +# The comment used to select a statement to be extracted +# when calling extract_node. +_STATEMENT_SELECTOR = "#@" +MISPLACED_TYPE_ANNOTATION_ERROR = "misplaced type annotation" + + +def open_source_file(filename: str) -> tuple[TextIOWrapper, str, str]: + # pylint: disable=consider-using-with + with open(filename, "rb") as byte_stream: + encoding = detect_encoding(byte_stream.readline)[0] + stream = open(filename, newline=None, encoding=encoding) + data = stream.read() + return stream, encoding, data + + +def _can_assign_attr(node: nodes.ClassDef, attrname: str | None) -> bool: + try: + slots = node.slots() + except NotImplementedError: + pass + else: + if slots and attrname not in {slot.value for slot in slots}: + return False + return node.qname() != "builtins.object" + + +class AstroidBuilder(raw_building.InspectBuilder): + """Class for building an astroid tree from source code or from a live module. + + The param *manager* specifies the manager class which should be used. + If no manager is given, then the default one will be used. The + param *apply_transforms* determines if the transforms should be + applied after the tree was built from source or from a live object, + by default being True. + """ + + def __init__( + self, manager: AstroidManager | None = None, apply_transforms: bool = True + ) -> None: + super().__init__(manager) + self._apply_transforms = apply_transforms + + def module_build( + self, module: types.ModuleType, modname: str | None = None + ) -> nodes.Module: + """Build an astroid from a living module instance.""" + node = None + path = getattr(module, "__file__", None) + loader = getattr(module, "__loader__", None) + # Prefer the loader to get the source rather than assuming we have a + # filesystem to read the source file from ourselves. + if loader: + modname = modname or module.__name__ + source = loader.get_source(modname) + if source: + node = self.string_build(source, modname, path=path) + if node is None and path is not None: + path_, ext = os.path.splitext(modutils._path_from_filename(path)) + if ext in {".py", ".pyc", ".pyo"} and os.path.exists(path_ + ".py"): + node = self.file_build(path_ + ".py", modname) + if node is None: + # this is a built-in module + # get a partial representation by introspection + node = self.inspect_build(module, modname=modname, path=path) + if self._apply_transforms: + # We have to handle transformation by ourselves since the + # rebuilder isn't called for builtin nodes + node = self._manager.visit_transforms(node) + assert isinstance(node, nodes.Module) + return node + + def file_build(self, path: str, modname: str | None = None) -> nodes.Module: + """Build astroid from a source code file (i.e. from an ast). + + *path* is expected to be a python source file + """ + try: + stream, encoding, data = open_source_file(path) + except OSError as exc: + raise AstroidBuildingError( + "Unable to load file {path}:\n{error}", + modname=modname, + path=path, + error=exc, + ) from exc + except (SyntaxError, LookupError) as exc: + raise AstroidSyntaxError( + "Python 3 encoding specification error or unknown encoding:\n" + "{error}", + modname=modname, + path=path, + error=exc, + ) from exc + except UnicodeError as exc: # wrong encoding + # detect_encoding returns utf-8 if no encoding specified + raise AstroidBuildingError( + "Wrong or no encoding specified for {filename}.", filename=path + ) from exc + with stream: + # get module name if necessary + if modname is None: + try: + modname = ".".join(modutils.modpath_from_file(path)) + except ImportError: + modname = os.path.splitext(os.path.basename(path))[0] + # build astroid representation + module, builder = self._data_build(data, modname, path) + return self._post_build(module, builder, encoding) + + def string_build( + self, data: str, modname: str = "", path: str | None = None + ) -> nodes.Module: + """Build astroid from source code string.""" + module, builder = self._data_build(data, modname, path) + module.file_bytes = data.encode("utf-8") + return self._post_build(module, builder, "utf-8") + + def _post_build( + self, module: nodes.Module, builder: rebuilder.TreeRebuilder, encoding: str + ) -> nodes.Module: + """Handles encoding and delayed nodes after a module has been built.""" + module.file_encoding = encoding + self._manager.cache_module(module) + # post tree building steps after we stored the module in the cache: + for from_node in builder._import_from_nodes: + if from_node.modname == "__future__": + for symbol, _ in from_node.names: + module.future_imports.add(symbol) + self.add_from_names_to_locals(from_node) + # handle delayed assattr nodes + for delayed in builder._delayed_assattr: + self.delayed_assattr(delayed) + + # Visit the transforms + if self._apply_transforms: + module = self._manager.visit_transforms(module) + return module + + def _data_build( + self, data: str, modname: str, path: str | None + ) -> tuple[nodes.Module, rebuilder.TreeRebuilder]: + """Build tree node from data and add some informations.""" + try: + node, parser_module = _parse_string(data, type_comments=True) + except (TypeError, ValueError, SyntaxError) as exc: + raise AstroidSyntaxError( + "Parsing Python code failed:\n{error}", + source=data, + modname=modname, + path=path, + error=exc, + ) from exc + + if path is not None: + node_file = os.path.abspath(path) + else: + node_file = "" + if modname.endswith(".__init__"): + modname = modname[:-9] + package = True + else: + package = ( + path is not None + and os.path.splitext(os.path.basename(path))[0] == "__init__" + ) + builder = rebuilder.TreeRebuilder(self._manager, parser_module, data) + module = builder.visit_module(node, modname, node_file, package) + return module, builder + + def add_from_names_to_locals(self, node: nodes.ImportFrom) -> None: + """Store imported names to the locals. + + Resort the locals if coming from a delayed node + """ + + def _key_func(node: nodes.NodeNG) -> int: + return node.fromlineno or 0 + + def sort_locals(my_list: list[nodes.NodeNG]) -> None: + my_list.sort(key=_key_func) + + assert node.parent # It should always default to the module + for name, asname in node.names: + if name == "*": + try: + imported = node.do_import_module() + except AstroidBuildingError: + continue + for name in imported.public_names(): + node.parent.set_local(name, node) + sort_locals(node.parent.scope().locals[name]) # type: ignore[arg-type] + else: + node.parent.set_local(asname or name, node) + sort_locals(node.parent.scope().locals[asname or name]) # type: ignore[arg-type] + + def delayed_assattr(self, node: nodes.AssignAttr) -> None: + """Visit a AssAttr node. + + This adds name to locals and handle members definition. + """ + try: + frame = node.frame(future=True) + for inferred in node.expr.infer(): + if isinstance(inferred, util.UninferableBase): + continue + try: + # pylint: disable=unidiomatic-typecheck # We want a narrow check on the + # parent type, not all of its subclasses + if ( + type(inferred) == bases.Instance + or type(inferred) == objects.ExceptionInstance + ): + inferred = inferred._proxied + iattrs = inferred.instance_attrs + if not _can_assign_attr(inferred, node.attrname): + continue + elif isinstance(inferred, bases.Instance): + # Const, Tuple or other containers that inherit from + # `Instance` + continue + elif isinstance(inferred, (bases.Proxy, util.UninferableBase)): + continue + elif inferred.is_function: + iattrs = inferred.instance_attrs + else: + iattrs = inferred.locals + except AttributeError: + # XXX log error + continue + values = iattrs.setdefault(node.attrname, []) + if node in values: + continue + # get assign in __init__ first XXX useful ? + if ( + frame.name == "__init__" + and values + and values[0].frame(future=True).name != "__init__" + ): + values.insert(0, node) + else: + values.append(node) + except InferenceError: + pass + + +def build_namespace_package_module(name: str, path: Sequence[str]) -> nodes.Module: + # TODO: Typing: Remove the cast to list and just update typing to accept Sequence + return nodes.Module(name, path=list(path), package=True) + + +def parse( + code: str, + module_name: str = "", + path: str | None = None, + apply_transforms: bool = True, +) -> nodes.Module: + """Parses a source string in order to obtain an astroid AST from it. + + :param str code: The code for the module. + :param str module_name: The name for the module, if any + :param str path: The path for the module + :param bool apply_transforms: + Apply the transforms for the give code. Use it if you + don't want the default transforms to be applied. + """ + code = textwrap.dedent(code) + builder = AstroidBuilder( + manager=AstroidManager(), apply_transforms=apply_transforms + ) + return builder.string_build(code, modname=module_name, path=path) + + +def _extract_expressions(node: nodes.NodeNG) -> Iterator[nodes.NodeNG]: + """Find expressions in a call to _TRANSIENT_FUNCTION and extract them. + + The function walks the AST recursively to search for expressions that + are wrapped into a call to _TRANSIENT_FUNCTION. If it finds such an + expression, it completely removes the function call node from the tree, + replacing it by the wrapped expression inside the parent. + + :param node: An astroid node. + :type node: astroid.bases.NodeNG + :yields: The sequence of wrapped expressions on the modified tree + expression can be found. + """ + if ( + isinstance(node, nodes.Call) + and isinstance(node.func, nodes.Name) + and node.func.name == _TRANSIENT_FUNCTION + ): + real_expr = node.args[0] + assert node.parent + real_expr.parent = node.parent + # Search for node in all _astng_fields (the fields checked when + # get_children is called) of its parent. Some of those fields may + # be lists or tuples, in which case the elements need to be checked. + # When we find it, replace it by real_expr, so that the AST looks + # like no call to _TRANSIENT_FUNCTION ever took place. + for name in node.parent._astroid_fields: + child = getattr(node.parent, name) + if isinstance(child, list): + for idx, compound_child in enumerate(child): + if compound_child is node: + child[idx] = real_expr + elif child is node: + setattr(node.parent, name, real_expr) + yield real_expr + else: + for child in node.get_children(): + yield from _extract_expressions(child) + + +def _find_statement_by_line(node: nodes.NodeNG, line: int) -> nodes.NodeNG | None: + """Extracts the statement on a specific line from an AST. + + If the line number of node matches line, it will be returned; + otherwise its children are iterated and the function is called + recursively. + + :param node: An astroid node. + :type node: astroid.bases.NodeNG + :param line: The line number of the statement to extract. + :type line: int + :returns: The statement on the line, or None if no statement for the line + can be found. + :rtype: astroid.bases.NodeNG or None + """ + if isinstance(node, (nodes.ClassDef, nodes.FunctionDef, nodes.MatchCase)): + # This is an inaccuracy in the AST: the nodes that can be + # decorated do not carry explicit information on which line + # the actual definition (class/def), but .fromline seems to + # be close enough. + node_line = node.fromlineno + else: + node_line = node.lineno + + if node_line == line: + return node + + for child in node.get_children(): + result = _find_statement_by_line(child, line) + if result: + return result + + return None + + +def extract_node(code: str, module_name: str = "") -> nodes.NodeNG | list[nodes.NodeNG]: + """Parses some Python code as a module and extracts a designated AST node. + + Statements: + To extract one or more statement nodes, append #@ to the end of the line + + Examples: + >>> def x(): + >>> def y(): + >>> return 1 #@ + + The return statement will be extracted. + + >>> class X(object): + >>> def meth(self): #@ + >>> pass + + The function object 'meth' will be extracted. + + Expressions: + To extract arbitrary expressions, surround them with the fake + function call __(...). After parsing, the surrounded expression + will be returned and the whole AST (accessible via the returned + node's parent attribute) will look like the function call was + never there in the first place. + + Examples: + >>> a = __(1) + + The const node will be extracted. + + >>> def x(d=__(foo.bar)): pass + + The node containing the default argument will be extracted. + + >>> def foo(a, b): + >>> return 0 < __(len(a)) < b + + The node containing the function call 'len' will be extracted. + + If no statements or expressions are selected, the last toplevel + statement will be returned. + + If the selected statement is a discard statement, (i.e. an expression + turned into a statement), the wrapped expression is returned instead. + + For convenience, singleton lists are unpacked. + + :param str code: A piece of Python code that is parsed as + a module. Will be passed through textwrap.dedent first. + :param str module_name: The name of the module. + :returns: The designated node from the parse tree, or a list of nodes. + """ + + def _extract(node: nodes.NodeNG | None) -> nodes.NodeNG | None: + if isinstance(node, nodes.Expr): + return node.value + + return node + + requested_lines: list[int] = [] + for idx, line in enumerate(code.splitlines()): + if line.strip().endswith(_STATEMENT_SELECTOR): + requested_lines.append(idx + 1) + + tree = parse(code, module_name=module_name) + if not tree.body: + raise ValueError("Empty tree, cannot extract from it") + + extracted: list[nodes.NodeNG | None] = [] + if requested_lines: + extracted = [_find_statement_by_line(tree, line) for line in requested_lines] + + # Modifies the tree. + extracted.extend(_extract_expressions(tree)) + + if not extracted: + extracted.append(tree.body[-1]) + + extracted = [_extract(node) for node in extracted] + extracted_without_none = [node for node in extracted if node is not None] + if len(extracted_without_none) == 1: + return extracted_without_none[0] + return extracted_without_none + + +def _extract_single_node(code: str, module_name: str = "") -> nodes.NodeNG: + """Call extract_node while making sure that only one value is returned.""" + ret = extract_node(code, module_name) + if isinstance(ret, list): + return ret[0] + return ret + + +def _parse_string( + data: str, type_comments: bool = True +) -> tuple[ast.Module, ParserModule]: + parser_module = get_parser_module(type_comments=type_comments) + try: + parsed = parser_module.parse(data + "\n", type_comments=type_comments) + except SyntaxError as exc: + # If the type annotations are misplaced for some reason, we do not want + # to fail the entire parsing of the file, so we need to retry the parsing without + # type comment support. + if exc.args[0] != MISPLACED_TYPE_ANNOTATION_ERROR or not type_comments: + raise + + parser_module = get_parser_module(type_comments=False) + parsed = parser_module.parse(data + "\n", type_comments=False) + return parsed, parser_module diff --git a/venv/lib/python3.10/site-packages/astroid/const.py b/venv/lib/python3.10/site-packages/astroid/const.py new file mode 120000 index 00000000..a7fd290f --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/const.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/f8/2f/60/644540829f376298e71b893a04a0654e49f6d89dd4791e7d2333f30beb \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/constraint.py b/venv/lib/python3.10/site-packages/astroid/constraint.py new file mode 100644 index 00000000..b6dc35cb --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/constraint.py @@ -0,0 +1,137 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Classes representing different types of constraints on inference values.""" +from __future__ import annotations + +import sys +from abc import ABC, abstractmethod +from collections.abc import Iterator +from typing import Union + +from astroid import bases, nodes, util +from astroid.typing import InferenceResult + +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + +_NameNodes = Union[nodes.AssignAttr, nodes.Attribute, nodes.AssignName, nodes.Name] + + +class Constraint(ABC): + """Represents a single constraint on a variable.""" + + def __init__(self, node: nodes.NodeNG, negate: bool) -> None: + self.node = node + """The node that this constraint applies to.""" + self.negate = negate + """True if this constraint is negated. E.g., "is not" instead of "is".""" + + @classmethod + @abstractmethod + def match( + cls: type[Self], node: _NameNodes, expr: nodes.NodeNG, negate: bool = False + ) -> Self | None: + """Return a new constraint for node matched from expr, if expr matches + the constraint pattern. + + If negate is True, negate the constraint. + """ + + @abstractmethod + def satisfied_by(self, inferred: InferenceResult) -> bool: + """Return True if this constraint is satisfied by the given inferred value.""" + + +class NoneConstraint(Constraint): + """Represents an "is None" or "is not None" constraint.""" + + CONST_NONE: nodes.Const = nodes.Const(None) + + @classmethod + def match( + cls: type[Self], node: _NameNodes, expr: nodes.NodeNG, negate: bool = False + ) -> Self | None: + """Return a new constraint for node matched from expr, if expr matches + the constraint pattern. + + Negate the constraint based on the value of negate. + """ + if isinstance(expr, nodes.Compare) and len(expr.ops) == 1: + left = expr.left + op, right = expr.ops[0] + if op in {"is", "is not"} and ( + _matches(left, node) and _matches(right, cls.CONST_NONE) + ): + negate = (op == "is" and negate) or (op == "is not" and not negate) + return cls(node=node, negate=negate) + + return None + + def satisfied_by(self, inferred: InferenceResult) -> bool: + """Return True if this constraint is satisfied by the given inferred value.""" + # Assume true if uninferable + if isinstance(inferred, util.UninferableBase): + return True + + # Return the XOR of self.negate and matches(inferred, self.CONST_NONE) + return self.negate ^ _matches(inferred, self.CONST_NONE) + + +def get_constraints( + expr: _NameNodes, frame: nodes.LocalsDictNodeNG +) -> dict[nodes.If, set[Constraint]]: + """Returns the constraints for the given expression. + + The returned dictionary maps the node where the constraint was generated to the + corresponding constraint(s). + + Constraints are computed statically by analysing the code surrounding expr. + Currently this only supports constraints generated from if conditions. + """ + current_node: nodes.NodeNG | None = expr + constraints_mapping: dict[nodes.If, set[Constraint]] = {} + while current_node is not None and current_node is not frame: + parent = current_node.parent + if isinstance(parent, nodes.If): + branch, _ = parent.locate_child(current_node) + constraints: set[Constraint] | None = None + if branch == "body": + constraints = set(_match_constraint(expr, parent.test)) + elif branch == "orelse": + constraints = set(_match_constraint(expr, parent.test, invert=True)) + + if constraints: + constraints_mapping[parent] = constraints + current_node = parent + + return constraints_mapping + + +ALL_CONSTRAINT_CLASSES = frozenset((NoneConstraint,)) +"""All supported constraint types.""" + + +def _matches(node1: nodes.NodeNG | bases.Proxy, node2: nodes.NodeNG) -> bool: + """Returns True if the two nodes match.""" + if isinstance(node1, nodes.Name) and isinstance(node2, nodes.Name): + return node1.name == node2.name + if isinstance(node1, nodes.Attribute) and isinstance(node2, nodes.Attribute): + return node1.attrname == node2.attrname and _matches(node1.expr, node2.expr) + if isinstance(node1, nodes.Const) and isinstance(node2, nodes.Const): + return node1.value == node2.value + + return False + + +def _match_constraint( + node: _NameNodes, expr: nodes.NodeNG, invert: bool = False +) -> Iterator[Constraint]: + """Yields all constraint patterns for node that match.""" + for constraint_cls in ALL_CONSTRAINT_CLASSES: + constraint = constraint_cls.match(node, expr, invert) + if constraint: + yield constraint diff --git a/venv/lib/python3.10/site-packages/astroid/context.py b/venv/lib/python3.10/site-packages/astroid/context.py new file mode 120000 index 00000000..61777258 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/context.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/07/07/59/9dd18531e3fd95dc6df9ce0b3f6d4deb56460858f248d0804a21875a1c \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/decorators.py b/venv/lib/python3.10/site-packages/astroid/decorators.py new file mode 100644 index 00000000..6bba37b6 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/decorators.py @@ -0,0 +1,290 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""A few useful function/method decorators.""" + +from __future__ import annotations + +import functools +import inspect +import sys +import warnings +from collections.abc import Callable, Generator +from typing import TypeVar + +import wrapt + +from astroid import _cache, util +from astroid.context import InferenceContext +from astroid.exceptions import InferenceError + +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + +_R = TypeVar("_R") +_P = ParamSpec("_P") + + +@wrapt.decorator +def cached(func, instance, args, kwargs): + """Simple decorator to cache result of method calls without args.""" + cache = getattr(instance, "__cache", None) + if cache is None: + instance.__cache = cache = {} + _cache.CACHE_MANAGER.add_dict_cache(cache) + try: + return cache[func] + except KeyError: + cache[func] = result = func(*args, **kwargs) + return result + + +# TODO: Remove when support for 3.7 is dropped +# TODO: astroid 3.0 -> move class behind sys.version_info < (3, 8) guard +class cachedproperty: + """Provides a cached property equivalent to the stacking of + @cached and @property, but more efficient. + + After first usage, the becomes part of the object's + __dict__. Doing: + + del obj. empties the cache. + + Idea taken from the pyramid_ framework and the mercurial_ project. + + .. _pyramid: http://pypi.python.org/pypi/pyramid + .. _mercurial: http://pypi.python.org/pypi/Mercurial + """ + + __slots__ = ("wrapped",) + + def __init__(self, wrapped): + if sys.version_info >= (3, 8): + warnings.warn( + "cachedproperty has been deprecated and will be removed in astroid 3.0 for Python 3.8+. " + "Use functools.cached_property instead.", + DeprecationWarning, + stacklevel=2, + ) + try: + wrapped.__name__ + except AttributeError as exc: + raise TypeError(f"{wrapped} must have a __name__ attribute") from exc + self.wrapped = wrapped + + @property + def __doc__(self): + doc = getattr(self.wrapped, "__doc__", None) + return "%s" % ( + "\n%s" % doc if doc else "" + ) + + def __get__(self, inst, objtype=None): + if inst is None: + return self + val = self.wrapped(inst) + setattr(inst, self.wrapped.__name__, val) + return val + + +def path_wrapper(func): + """Return the given infer function wrapped to handle the path. + + Used to stop inference if the node has already been looked + at for a given `InferenceContext` to prevent infinite recursion + """ + + @functools.wraps(func) + def wrapped( + node, context: InferenceContext | None = None, _func=func, **kwargs + ) -> Generator: + """Wrapper function handling context.""" + if context is None: + context = InferenceContext() + if context.push(node): + return + + yielded = set() + + for res in _func(node, context, **kwargs): + # unproxy only true instance, not const, tuple, dict... + if res.__class__.__name__ == "Instance": + ares = res._proxied + else: + ares = res + if ares not in yielded: + yield res + yielded.add(ares) + + return wrapped + + +@wrapt.decorator +def yes_if_nothing_inferred(func, instance, args, kwargs): + generator = func(*args, **kwargs) + + try: + yield next(generator) + except StopIteration: + # generator is empty + yield util.Uninferable + return + + yield from generator + + +@wrapt.decorator +def raise_if_nothing_inferred(func, instance, args, kwargs): + generator = func(*args, **kwargs) + try: + yield next(generator) + except StopIteration as error: + # generator is empty + if error.args: + # pylint: disable=not-a-mapping + raise InferenceError(**error.args[0]) from error + raise InferenceError( + "StopIteration raised without any error information." + ) from error + except RecursionError as error: + raise InferenceError( + f"RecursionError raised with limit {sys.getrecursionlimit()}." + ) from error + + yield from generator + + +# Expensive decorators only used to emit Deprecation warnings. +# If no other than the default DeprecationWarning are enabled, +# fall back to passthrough implementations. +if util.check_warnings_filter(): # noqa: C901 + + def deprecate_default_argument_values( + astroid_version: str = "3.0", **arguments: str + ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: + """Decorator which emits a DeprecationWarning if any arguments specified + are None or not passed at all. + + Arguments should be a key-value mapping, with the key being the argument to check + and the value being a type annotation as string for the value of the argument. + + To improve performance, only used when DeprecationWarnings other than + the default one are enabled. + """ + # Helpful links + # Decorator for DeprecationWarning: https://stackoverflow.com/a/49802489 + # Typing of stacked decorators: https://stackoverflow.com/a/68290080 + + def deco(func: Callable[_P, _R]) -> Callable[_P, _R]: + """Decorator function.""" + + @functools.wraps(func) + def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R: + """Emit DeprecationWarnings if conditions are met.""" + + keys = list(inspect.signature(func).parameters.keys()) + for arg, type_annotation in arguments.items(): + try: + index = keys.index(arg) + except ValueError: + raise ValueError( + f"Can't find argument '{arg}' for '{args[0].__class__.__qualname__}'" + ) from None + if ( + # Check kwargs + # - if found, check it's not None + (arg in kwargs and kwargs[arg] is None) + # Check args + # - make sure not in kwargs + # - len(args) needs to be long enough, if too short + # arg can't be in args either + # - args[index] should not be None + or arg not in kwargs + and ( + index == -1 + or len(args) <= index + or (len(args) > index and args[index] is None) + ) + ): + warnings.warn( + f"'{arg}' will be a required argument for " + f"'{args[0].__class__.__qualname__}.{func.__name__}'" + f" in astroid {astroid_version} " + f"('{arg}' should be of type: '{type_annotation}')", + DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return wrapper + + return deco + + def deprecate_arguments( + astroid_version: str = "3.0", **arguments: str + ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: + """Decorator which emits a DeprecationWarning if any arguments specified + are passed. + + Arguments should be a key-value mapping, with the key being the argument to check + and the value being a string that explains what to do instead of passing the argument. + + To improve performance, only used when DeprecationWarnings other than + the default one are enabled. + """ + + def deco(func: Callable[_P, _R]) -> Callable[_P, _R]: + @functools.wraps(func) + def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R: + keys = list(inspect.signature(func).parameters.keys()) + for arg, note in arguments.items(): + try: + index = keys.index(arg) + except ValueError: + raise ValueError( + f"Can't find argument '{arg}' for '{args[0].__class__.__qualname__}'" + ) from None + if arg in kwargs or len(args) > index: + warnings.warn( + f"The argument '{arg}' for " + f"'{args[0].__class__.__qualname__}.{func.__name__}' is deprecated " + f"and will be removed in astroid {astroid_version} ({note})", + DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return wrapper + + return deco + +else: + + def deprecate_default_argument_values( + astroid_version: str = "3.0", **arguments: str + ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: + """Passthrough decorator to improve performance if DeprecationWarnings are + disabled. + """ + + def deco(func: Callable[_P, _R]) -> Callable[_P, _R]: + """Decorator function.""" + return func + + return deco + + def deprecate_arguments( + astroid_version: str = "3.0", **arguments: str + ) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: + """Passthrough decorator to improve performance if DeprecationWarnings are + disabled. + """ + + def deco(func: Callable[_P, _R]) -> Callable[_P, _R]: + """Decorator function.""" + return func + + return deco diff --git a/venv/lib/python3.10/site-packages/astroid/exceptions.py b/venv/lib/python3.10/site-packages/astroid/exceptions.py new file mode 120000 index 00000000..94a69445 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/exceptions.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/c1/c2/45/a3192e594b2540cb238c1b2f41a7654e5f8208b8a7e08e4c83d100e174 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/filter_statements.py b/venv/lib/python3.10/site-packages/astroid/filter_statements.py new file mode 120000 index 00000000..483fe48c --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/filter_statements.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/cd/de/79/bc66a5d56ea4d43651b19a1342876d4d90ad2ab99e6d45641027a8512e \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/helpers.py b/venv/lib/python3.10/site-packages/astroid/helpers.py new file mode 100644 index 00000000..24dba6d7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/helpers.py @@ -0,0 +1,320 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Various helper utilities.""" + +from __future__ import annotations + +from collections.abc import Generator + +from astroid import bases, manager, nodes, raw_building, util +from astroid.context import CallContext, InferenceContext +from astroid.exceptions import ( + AstroidTypeError, + AttributeInferenceError, + InferenceError, + MroError, + _NonDeducibleTypeHierarchy, +) +from astroid.nodes import scoped_nodes +from astroid.typing import InferenceResult, SuccessfulInferenceResult + + +def _build_proxy_class(cls_name: str, builtins: nodes.Module) -> nodes.ClassDef: + proxy = raw_building.build_class(cls_name) + proxy.parent = builtins + return proxy + + +def _function_type( + function: nodes.Lambda | bases.UnboundMethod, builtins: nodes.Module +) -> nodes.ClassDef: + if isinstance(function, scoped_nodes.Lambda): + if function.root().name == "builtins": + cls_name = "builtin_function_or_method" + else: + cls_name = "function" + elif isinstance(function, bases.BoundMethod): + cls_name = "method" + else: + cls_name = "function" + return _build_proxy_class(cls_name, builtins) + + +def _object_type( + node: SuccessfulInferenceResult, context: InferenceContext | None = None +) -> Generator[InferenceResult | None, None, None]: + astroid_manager = manager.AstroidManager() + builtins = astroid_manager.builtins_module + context = context or InferenceContext() + + for inferred in node.infer(context=context): + if isinstance(inferred, scoped_nodes.ClassDef): + if inferred.newstyle: + metaclass = inferred.metaclass(context=context) + if metaclass: + yield metaclass + continue + yield builtins.getattr("type")[0] + elif isinstance(inferred, (scoped_nodes.Lambda, bases.UnboundMethod)): + yield _function_type(inferred, builtins) + elif isinstance(inferred, scoped_nodes.Module): + yield _build_proxy_class("module", builtins) + elif isinstance(inferred, nodes.Unknown): + raise InferenceError + elif isinstance(inferred, util.UninferableBase): + yield inferred + elif isinstance(inferred, (bases.Proxy, nodes.Slice)): + yield inferred._proxied + else: # pragma: no cover + raise AssertionError(f"We don't handle {type(inferred)} currently") + + +def object_type( + node: SuccessfulInferenceResult, context: InferenceContext | None = None +) -> InferenceResult | None: + """Obtain the type of the given node. + + This is used to implement the ``type`` builtin, which means that it's + used for inferring type calls, as well as used in a couple of other places + in the inference. + The node will be inferred first, so this function can support all + sorts of objects, as long as they support inference. + """ + + try: + types = set(_object_type(node, context)) + except InferenceError: + return util.Uninferable + if len(types) > 1 or not types: + return util.Uninferable + return list(types)[0] + + +def _object_type_is_subclass( + obj_type, class_or_seq, context: InferenceContext | None = None +): + if not isinstance(class_or_seq, (tuple, list)): + class_seq = (class_or_seq,) + else: + class_seq = class_or_seq + + if isinstance(obj_type, util.UninferableBase): + return util.Uninferable + + # Instances are not types + class_seq = [ + item if not isinstance(item, bases.Instance) else util.Uninferable + for item in class_seq + ] + # strict compatibility with issubclass + # issubclass(type, (object, 1)) evaluates to true + # issubclass(object, (1, type)) raises TypeError + for klass in class_seq: + if isinstance(klass, util.UninferableBase): + raise AstroidTypeError("arg 2 must be a type or tuple of types") + + for obj_subclass in obj_type.mro(): + if obj_subclass == klass: + return True + return False + + +def object_isinstance(node, class_or_seq, context: InferenceContext | None = None): + """Check if a node 'isinstance' any node in class_or_seq. + + :param node: A given node + :param class_or_seq: Union[nodes.NodeNG, Sequence[nodes.NodeNG]] + :rtype: bool + + :raises AstroidTypeError: if the given ``classes_or_seq`` are not types + """ + obj_type = object_type(node, context) + if isinstance(obj_type, util.UninferableBase): + return util.Uninferable + return _object_type_is_subclass(obj_type, class_or_seq, context=context) + + +def object_issubclass(node, class_or_seq, context: InferenceContext | None = None): + """Check if a type is a subclass of any node in class_or_seq. + + :param node: A given node + :param class_or_seq: Union[Nodes.NodeNG, Sequence[nodes.NodeNG]] + :rtype: bool + + :raises AstroidTypeError: if the given ``classes_or_seq`` are not types + :raises AstroidError: if the type of the given node cannot be inferred + or its type's mro doesn't work + """ + if not isinstance(node, nodes.ClassDef): + raise TypeError(f"{node} needs to be a ClassDef node") + return _object_type_is_subclass(node, class_or_seq, context=context) + + +def safe_infer( + node: nodes.NodeNG | bases.Proxy, context: InferenceContext | None = None +) -> InferenceResult | None: + """Return the inferred value for the given node. + + Return None if inference failed or if there is some ambiguity (more than + one node has been inferred). + """ + try: + inferit = node.infer(context=context) + value = next(inferit) + except (InferenceError, StopIteration): + return None + try: + next(inferit) + return None # None if there is ambiguity on the inferred node + except InferenceError: + return None # there is some kind of ambiguity + except StopIteration: + return value + + +def has_known_bases(klass, context: InferenceContext | None = None) -> bool: + """Return whether all base classes of a class could be inferred.""" + try: + return klass._all_bases_known + except AttributeError: + pass + for base in klass.bases: + result = safe_infer(base, context=context) + # TODO: check for A->B->A->B pattern in class structure too? + if ( + not isinstance(result, scoped_nodes.ClassDef) + or result is klass + or not has_known_bases(result, context=context) + ): + klass._all_bases_known = False + return False + klass._all_bases_known = True + return True + + +def _type_check(type1, type2) -> bool: + if not all(map(has_known_bases, (type1, type2))): + raise _NonDeducibleTypeHierarchy + + if not all([type1.newstyle, type2.newstyle]): + return False + try: + return type1 in type2.mro()[:-1] + except MroError as e: + # The MRO is invalid. + raise _NonDeducibleTypeHierarchy from e + + +def is_subtype(type1, type2) -> bool: + """Check if *type1* is a subtype of *type2*.""" + return _type_check(type1=type2, type2=type1) + + +def is_supertype(type1, type2) -> bool: + """Check if *type2* is a supertype of *type1*.""" + return _type_check(type1, type2) + + +def class_instance_as_index(node: SuccessfulInferenceResult) -> nodes.Const | None: + """Get the value as an index for the given instance. + + If an instance provides an __index__ method, then it can + be used in some scenarios where an integer is expected, + for instance when multiplying or subscripting a list. + """ + context = InferenceContext() + try: + for inferred in node.igetattr("__index__", context=context): + if not isinstance(inferred, bases.BoundMethod): + continue + + context.boundnode = node + context.callcontext = CallContext(args=[], callee=inferred) + for result in inferred.infer_call_result(node, context=context): + if isinstance(result, nodes.Const) and isinstance(result.value, int): + return result + except InferenceError: + pass + return None + + +def object_len(node, context: InferenceContext | None = None): + """Infer length of given node object. + + :param Union[nodes.ClassDef, nodes.Instance] node: + :param node: Node to infer length of + + :raises AstroidTypeError: If an invalid node is returned + from __len__ method or no __len__ method exists + :raises InferenceError: If the given node cannot be inferred + or if multiple nodes are inferred or if the code executed in python + would result in a infinite recursive check for length + :rtype int: Integer length of node + """ + # pylint: disable=import-outside-toplevel; circular import + from astroid.objects import FrozenSet + + inferred_node = safe_infer(node, context=context) + + # prevent self referential length calls from causing a recursion error + # see https://github.com/PyCQA/astroid/issues/777 + node_frame = node.frame(future=True) + if ( + isinstance(node_frame, scoped_nodes.FunctionDef) + and node_frame.name == "__len__" + and hasattr(inferred_node, "_proxied") + and inferred_node._proxied == node_frame.parent + ): + message = ( + "Self referential __len__ function will " + "cause a RecursionError on line {} of {}".format( + node.lineno, node.root().file + ) + ) + raise InferenceError(message) + + if inferred_node is None or isinstance(inferred_node, util.UninferableBase): + raise InferenceError(node=node) + if isinstance(inferred_node, nodes.Const) and isinstance( + inferred_node.value, (bytes, str) + ): + return len(inferred_node.value) + if isinstance(inferred_node, (nodes.List, nodes.Set, nodes.Tuple, FrozenSet)): + return len(inferred_node.elts) + if isinstance(inferred_node, nodes.Dict): + return len(inferred_node.items) + + node_type = object_type(inferred_node, context=context) + if not node_type: + raise InferenceError(node=node) + + try: + len_call = next(node_type.igetattr("__len__", context=context)) + except StopIteration as e: + raise AstroidTypeError(str(e)) from e + except AttributeInferenceError as e: + raise AstroidTypeError( + f"object of type '{node_type.pytype()}' has no len()" + ) from e + + inferred = len_call.infer_call_result(node, context) + if isinstance(inferred, util.UninferableBase): + raise InferenceError(node=node, context=context) + result_of_len = next(inferred, None) + if ( + isinstance(result_of_len, nodes.Const) + and result_of_len.pytype() == "builtins.int" + ): + return result_of_len.value + if ( + result_of_len is None + or isinstance(result_of_len, bases.Instance) + and result_of_len.is_subtype_of("builtins.int") + ): + # Fake a result as we don't know the arguments of the instance call. + return 0 + raise AstroidTypeError( + f"'{result_of_len}' object cannot be interpreted as an integer" + ) diff --git a/venv/lib/python3.10/site-packages/astroid/inference.py b/venv/lib/python3.10/site-packages/astroid/inference.py new file mode 100644 index 00000000..65d03d30 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/inference.py @@ -0,0 +1,1273 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""This module contains a set of functions to handle inference on astroid trees.""" + +from __future__ import annotations + +import ast +import functools +import itertools +import operator +import typing +from collections.abc import Callable, Generator, Iterable, Iterator +from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union + +from astroid import bases, constraint, decorators, helpers, nodes, protocols, util +from astroid.const import PY310_PLUS +from astroid.context import ( + CallContext, + InferenceContext, + bind_context_to_node, + copy_context, +) +from astroid.exceptions import ( + AstroidBuildingError, + AstroidError, + AstroidIndexError, + AstroidTypeError, + AstroidValueError, + AttributeInferenceError, + InferenceError, + NameInferenceError, + _NonDeducibleTypeHierarchy, +) +from astroid.interpreter import dunder_lookup +from astroid.manager import AstroidManager +from astroid.typing import ( + InferenceErrorInfo, + InferenceResult, + SuccessfulInferenceResult, +) + +if TYPE_CHECKING: + from astroid.objects import Property + +# Prevents circular imports +objects = util.lazy_import("objects") + +_T = TypeVar("_T") +_BaseContainerT = TypeVar("_BaseContainerT", bound=nodes.BaseContainer) +_FunctionDefT = TypeVar("_FunctionDefT", bound=nodes.FunctionDef) + +GetFlowFactory = typing.Callable[ + [ + InferenceResult, + Optional[InferenceResult], + Union[nodes.AugAssign, nodes.BinOp], + InferenceResult, + Optional[InferenceResult], + InferenceContext, + InferenceContext, + ], + "list[functools.partial[Generator[InferenceResult, None, None]]]", +] + +# .infer method ############################################################### + + +def infer_end( + self: _T, context: InferenceContext | None = None, **kwargs: Any +) -> Iterator[_T]: + """Inference's end for nodes that yield themselves on inference. + + These are objects for which inference does not have any semantic, + such as Module or Consts. + """ + yield self + + +# We add ignores to all assignments to methods +# See https://github.com/python/mypy/issues/2427 +nodes.Module._infer = infer_end +nodes.ClassDef._infer = infer_end +nodes.Lambda._infer = infer_end # type: ignore[assignment] +nodes.Const._infer = infer_end # type: ignore[assignment] +nodes.Slice._infer = infer_end # type: ignore[assignment] + + +def _infer_sequence_helper( + node: _BaseContainerT, context: InferenceContext | None = None +) -> list[SuccessfulInferenceResult]: + """Infer all values based on _BaseContainer.elts.""" + values = [] + + for elt in node.elts: + if isinstance(elt, nodes.Starred): + starred = helpers.safe_infer(elt.value, context) + if not starred: + raise InferenceError(node=node, context=context) + if not hasattr(starred, "elts"): + raise InferenceError(node=node, context=context) + values.extend(_infer_sequence_helper(starred)) + elif isinstance(elt, nodes.NamedExpr): + value = helpers.safe_infer(elt.value, context) + if not value: + raise InferenceError(node=node, context=context) + values.append(value) + else: + values.append(elt) + return values + + +@decorators.raise_if_nothing_inferred +def infer_sequence( + self: _BaseContainerT, + context: InferenceContext | None = None, + **kwargs: Any, +) -> Iterator[_BaseContainerT]: + has_starred_named_expr = any( + isinstance(e, (nodes.Starred, nodes.NamedExpr)) for e in self.elts + ) + if has_starred_named_expr: + values = _infer_sequence_helper(self, context) + new_seq = type(self)( + lineno=self.lineno, col_offset=self.col_offset, parent=self.parent + ) + new_seq.postinit(values) + + yield new_seq + else: + yield self + + +nodes.List._infer = infer_sequence # type: ignore[assignment] +nodes.Tuple._infer = infer_sequence # type: ignore[assignment] +nodes.Set._infer = infer_sequence # type: ignore[assignment] + + +def infer_map( + self: nodes.Dict, context: InferenceContext | None = None +) -> Iterator[nodes.Dict]: + if not any(isinstance(k, nodes.DictUnpack) for k, _ in self.items): + yield self + else: + items = _infer_map(self, context) + new_seq = type(self)(self.lineno, self.col_offset, self.parent) + new_seq.postinit(list(items.items())) + yield new_seq + + +def _update_with_replacement( + lhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult], + rhs_dict: dict[SuccessfulInferenceResult, SuccessfulInferenceResult], +) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]: + """Delete nodes that equate to duplicate keys. + + Since an astroid node doesn't 'equal' another node with the same value, + this function uses the as_string method to make sure duplicate keys + don't get through + + Note that both the key and the value are astroid nodes + + Fixes issue with DictUnpack causing duplicate keys + in inferred Dict items + + :param lhs_dict: Dictionary to 'merge' nodes into + :param rhs_dict: Dictionary with nodes to pull from + :return : merged dictionary of nodes + """ + combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items()) + # Overwrite keys which have the same string values + string_map = {key.as_string(): (key, value) for key, value in combined_dict} + # Return to dictionary + return dict(string_map.values()) + + +def _infer_map( + node: nodes.Dict, context: InferenceContext | None +) -> dict[SuccessfulInferenceResult, SuccessfulInferenceResult]: + """Infer all values based on Dict.items.""" + values: dict[SuccessfulInferenceResult, SuccessfulInferenceResult] = {} + for name, value in node.items: + if isinstance(name, nodes.DictUnpack): + double_starred = helpers.safe_infer(value, context) + if not double_starred: + raise InferenceError + if not isinstance(double_starred, nodes.Dict): + raise InferenceError(node=node, context=context) + unpack_items = _infer_map(double_starred, context) + values = _update_with_replacement(values, unpack_items) + else: + key = helpers.safe_infer(name, context=context) + safe_value = helpers.safe_infer(value, context=context) + if any(not elem for elem in (key, safe_value)): + raise InferenceError(node=node, context=context) + # safe_value is SuccessfulInferenceResult as bool(Uninferable) == False + values = _update_with_replacement(values, {key: safe_value}) + return values + + +nodes.Dict._infer = infer_map # type: ignore[assignment] + + +def _higher_function_scope(node: nodes.NodeNG) -> nodes.FunctionDef | None: + """Search for the first function which encloses the given + scope. This can be used for looking up in that function's + scope, in case looking up in a lower scope for a particular + name fails. + + :param node: A scope node. + :returns: + ``None``, if no parent function scope was found, + otherwise an instance of :class:`astroid.nodes.scoped_nodes.Function`, + which encloses the given node. + """ + current = node + while current.parent and not isinstance(current.parent, nodes.FunctionDef): + current = current.parent + if current and current.parent: + return current.parent # type: ignore[no-any-return] + return None + + +def infer_name( + self: nodes.Name | nodes.AssignName, + context: InferenceContext | None = None, + **kwargs: Any, +) -> Generator[InferenceResult, None, None]: + """Infer a Name: use name lookup rules.""" + frame, stmts = self.lookup(self.name) + if not stmts: + # Try to see if the name is enclosed in a nested function + # and use the higher (first function) scope for searching. + parent_function = _higher_function_scope(self.scope()) + if parent_function: + _, stmts = parent_function.lookup(self.name) + + if not stmts: + raise NameInferenceError( + name=self.name, scope=self.scope(), context=context + ) + context = copy_context(context) + context.lookupname = self.name + context.constraints[self.name] = constraint.get_constraints(self, frame) + + return bases._infer_stmts(stmts, context, frame) + + +# pylint: disable=no-value-for-parameter +# The order of the decorators here is important +# See https://github.com/PyCQA/astroid/commit/0a8a75db30da060a24922e05048bc270230f5 +nodes.Name._infer = decorators.raise_if_nothing_inferred( + decorators.path_wrapper(infer_name) +) +nodes.AssignName.infer_lhs = infer_name # won't work with a path wrapper + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_call( + self: nodes.Call, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, InferenceErrorInfo]: + """Infer a Call node by trying to guess what the function returns.""" + callcontext = copy_context(context) + callcontext.boundnode = None + if context is not None: + callcontext.extra_context = _populate_context_lookup(self, context.clone()) + + for callee in self.func.infer(context): + if isinstance(callee, util.UninferableBase): + yield callee + continue + try: + if hasattr(callee, "infer_call_result"): + callcontext.callcontext = CallContext( + args=self.args, keywords=self.keywords, callee=callee + ) + yield from callee.infer_call_result(caller=self, context=callcontext) + except InferenceError: + continue + return InferenceErrorInfo(node=self, context=context) + + +nodes.Call._infer = infer_call # type: ignore[assignment] + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_import( + self: nodes.Import, + context: InferenceContext | None = None, + asname: bool = True, + **kwargs: Any, +) -> Generator[nodes.Module, None, None]: + """Infer an Import node: return the imported module/object.""" + context = context or InferenceContext() + name = context.lookupname + if name is None: + raise InferenceError(node=self, context=context) + + try: + if asname: + yield self.do_import_module(self.real_name(name)) + else: + yield self.do_import_module(name) + except AstroidBuildingError as exc: + raise InferenceError(node=self, context=context) from exc + + +nodes.Import._infer = infer_import + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_import_from( + self: nodes.ImportFrom, + context: InferenceContext | None = None, + asname: bool = True, + **kwargs: Any, +) -> Generator[InferenceResult, None, None]: + """Infer a ImportFrom node: return the imported module/object.""" + context = context or InferenceContext() + name = context.lookupname + if name is None: + raise InferenceError(node=self, context=context) + if asname: + try: + name = self.real_name(name) + except AttributeInferenceError as exc: + # See https://github.com/PyCQA/pylint/issues/4692 + raise InferenceError(node=self, context=context) from exc + try: + module = self.do_import_module() + except AstroidBuildingError as exc: + raise InferenceError(node=self, context=context) from exc + + try: + context = copy_context(context) + context.lookupname = name + stmts = module.getattr(name, ignore_locals=module is self.root()) + return bases._infer_stmts(stmts, context) + except AttributeInferenceError as error: + raise InferenceError( + str(error), target=self, attribute=name, context=context + ) from error + + +nodes.ImportFrom._infer = infer_import_from # type: ignore[assignment] + + +def infer_attribute( + self: nodes.Attribute | nodes.AssignAttr, + context: InferenceContext | None = None, + **kwargs: Any, +) -> Generator[InferenceResult, None, InferenceErrorInfo]: + """Infer an Attribute node by using getattr on the associated object.""" + for owner in self.expr.infer(context): + if isinstance(owner, util.UninferableBase): + yield owner + continue + + context = copy_context(context) + old_boundnode = context.boundnode + try: + context.boundnode = owner + if isinstance(owner, (nodes.ClassDef, bases.Instance)): + frame = owner if isinstance(owner, nodes.ClassDef) else owner._proxied + context.constraints[self.attrname] = constraint.get_constraints( + self, frame=frame + ) + yield from owner.igetattr(self.attrname, context) + except ( + AttributeInferenceError, + InferenceError, + AttributeError, + ): + pass + finally: + context.boundnode = old_boundnode + return InferenceErrorInfo(node=self, context=context) + + +# The order of the decorators here is important +# See https://github.com/PyCQA/astroid/commit/0a8a75db30da060a24922e05048bc270230f5 +nodes.Attribute._infer = decorators.raise_if_nothing_inferred( + decorators.path_wrapper(infer_attribute) +) +# won't work with a path wrapper +nodes.AssignAttr.infer_lhs = decorators.raise_if_nothing_inferred(infer_attribute) + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_global( + self: nodes.Global, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, None]: + if context is None or context.lookupname is None: + raise InferenceError(node=self, context=context) + try: + return bases._infer_stmts(self.root().getattr(context.lookupname), context) + except AttributeInferenceError as error: + raise InferenceError( + str(error), target=self, attribute=context.lookupname, context=context + ) from error + + +nodes.Global._infer = infer_global # type: ignore[assignment] + + +_SUBSCRIPT_SENTINEL = object() + + +def infer_subscript( + self: nodes.Subscript, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: + """Inference for subscripts. + + We're understanding if the index is a Const + or a slice, passing the result of inference + to the value's `getitem` method, which should + handle each supported index type accordingly. + """ + + found_one = False + for value in self.value.infer(context): + if isinstance(value, util.UninferableBase): + yield util.Uninferable + return None + for index in self.slice.infer(context): + if isinstance(index, util.UninferableBase): + yield util.Uninferable + return None + + # Try to deduce the index value. + index_value = _SUBSCRIPT_SENTINEL + if value.__class__ == bases.Instance: + index_value = index + elif index.__class__ == bases.Instance: + instance_as_index = helpers.class_instance_as_index(index) + if instance_as_index: + index_value = instance_as_index + else: + index_value = index + + if index_value is _SUBSCRIPT_SENTINEL: + raise InferenceError(node=self, context=context) + + try: + assigned = value.getitem(index_value, context) + except ( + AstroidTypeError, + AstroidIndexError, + AstroidValueError, + AttributeInferenceError, + AttributeError, + ) as exc: + raise InferenceError(node=self, context=context) from exc + + # Prevent inferring if the inferred subscript + # is the same as the original subscripted object. + if self is assigned or isinstance(assigned, util.UninferableBase): + yield util.Uninferable + return None + yield from assigned.infer(context) + found_one = True + + if found_one: + return InferenceErrorInfo(node=self, context=context) + return None + + +# The order of the decorators here is important +# See https://github.com/PyCQA/astroid/commit/0a8a75db30da060a24922e05048bc270230f5 +nodes.Subscript._infer = decorators.raise_if_nothing_inferred( # type: ignore[assignment] + decorators.path_wrapper(infer_subscript) +) +nodes.Subscript.infer_lhs = decorators.raise_if_nothing_inferred(infer_subscript) + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def _infer_boolop( + self: nodes.BoolOp, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: + """Infer a boolean operation (and / or / not). + + The function will calculate the boolean operation + for all pairs generated through inference for each component + node. + """ + values = self.values + if self.op == "or": + predicate = operator.truth + else: + predicate = operator.not_ + + try: + inferred_values = [value.infer(context=context) for value in values] + except InferenceError: + yield util.Uninferable + return None + + for pair in itertools.product(*inferred_values): + if any(isinstance(item, util.UninferableBase) for item in pair): + # Can't infer the final result, just yield Uninferable. + yield util.Uninferable + continue + + bool_values = [item.bool_value() for item in pair] + if any(isinstance(item, util.UninferableBase) for item in bool_values): + # Can't infer the final result, just yield Uninferable. + yield util.Uninferable + continue + + # Since the boolean operations are short circuited operations, + # this code yields the first value for which the predicate is True + # and if no value respected the predicate, then the last value will + # be returned (or Uninferable if there was no last value). + # This is conforming to the semantics of `and` and `or`: + # 1 and 0 -> 1 + # 0 and 1 -> 0 + # 1 or 0 -> 1 + # 0 or 1 -> 1 + value = util.Uninferable + for value, bool_value in zip(pair, bool_values): + if predicate(bool_value): + yield value + break + else: + yield value + + return InferenceErrorInfo(node=self, context=context) + + +nodes.BoolOp._infer = _infer_boolop + + +# UnaryOp, BinOp and AugAssign inferences + + +def _filter_operation_errors( + self: _T, + infer_callable: Callable[ + [_T, InferenceContext | None], + Generator[InferenceResult | util.BadOperationMessage, None, None], + ], + context: InferenceContext | None, + error: type[util.BadOperationMessage], +) -> Generator[InferenceResult, None, None]: + for result in infer_callable(self, context): + if isinstance(result, error): + # For the sake of .infer(), we don't care about operation + # errors, which is the job of pylint. So return something + # which shows that we can't infer the result. + yield util.Uninferable + else: + yield result + + +def _infer_unaryop( + self: nodes.UnaryOp, context: InferenceContext | None = None +) -> Generator[InferenceResult | util.BadUnaryOperationMessage, None, None]: + """Infer what an UnaryOp should return when evaluated.""" + for operand in self.operand.infer(context): + try: + yield operand.infer_unary_op(self.op) + except TypeError as exc: + # The operand doesn't support this operation. + yield util.BadUnaryOperationMessage(operand, self.op, exc) + except AttributeError as exc: + meth = protocols.UNARY_OP_METHOD[self.op] + if meth is None: + # `not node`. Determine node's boolean + # value and negate its result, unless it is + # Uninferable, which will be returned as is. + bool_value = operand.bool_value() + if not isinstance(bool_value, util.UninferableBase): + yield nodes.const_factory(not bool_value) + else: + yield util.Uninferable + else: + if not isinstance(operand, (bases.Instance, nodes.ClassDef)): + # The operation was used on something which + # doesn't support it. + yield util.BadUnaryOperationMessage(operand, self.op, exc) + continue + + try: + try: + methods = dunder_lookup.lookup(operand, meth) + except AttributeInferenceError: + yield util.BadUnaryOperationMessage(operand, self.op, exc) + continue + + meth = methods[0] + inferred = next(meth.infer(context=context), None) + if ( + isinstance(inferred, util.UninferableBase) + or not inferred.callable() + ): + continue + + context = copy_context(context) + context.boundnode = operand + context.callcontext = CallContext(args=[], callee=inferred) + + call_results = inferred.infer_call_result(self, context=context) + result = next(call_results, None) + if result is None: + # Failed to infer, return the same type. + yield operand + else: + yield result + except AttributeInferenceError as inner_exc: + # The unary operation special method was not found. + yield util.BadUnaryOperationMessage(operand, self.op, inner_exc) + except InferenceError: + yield util.Uninferable + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_unaryop( + self: nodes.UnaryOp, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, InferenceErrorInfo]: + """Infer what an UnaryOp should return when evaluated.""" + yield from _filter_operation_errors( + self, _infer_unaryop, context, util.BadUnaryOperationMessage + ) + return InferenceErrorInfo(node=self, context=context) + + +nodes.UnaryOp._infer_unaryop = _infer_unaryop +nodes.UnaryOp._infer = infer_unaryop + + +def _is_not_implemented(const) -> bool: + """Check if the given const node is NotImplemented.""" + return isinstance(const, nodes.Const) and const.value is NotImplemented + + +def _infer_old_style_string_formatting( + instance: nodes.Const, other: nodes.NodeNG, context: InferenceContext +) -> tuple[util.UninferableBase | nodes.Const]: + """Infer the result of '"string" % ...'. + + TODO: Instead of returning Uninferable we should rely + on the call to '%' to see if the result is actually uninferable. + """ + if isinstance(other, nodes.Tuple): + if util.Uninferable in other.elts: + return (util.Uninferable,) + inferred_positional = [helpers.safe_infer(i, context) for i in other.elts] + if all(isinstance(i, nodes.Const) for i in inferred_positional): + values = tuple(i.value for i in inferred_positional) + else: + values = None + elif isinstance(other, nodes.Dict): + values: dict[Any, Any] = {} + for pair in other.items: + key = helpers.safe_infer(pair[0], context) + if not isinstance(key, nodes.Const): + return (util.Uninferable,) + value = helpers.safe_infer(pair[1], context) + if not isinstance(value, nodes.Const): + return (util.Uninferable,) + values[key.value] = value.value + elif isinstance(other, nodes.Const): + values = other.value + else: + return (util.Uninferable,) + + try: + return (nodes.const_factory(instance.value % values),) + except (TypeError, KeyError, ValueError): + return (util.Uninferable,) + + +def _invoke_binop_inference( + instance: InferenceResult, + opnode: nodes.AugAssign | nodes.BinOp, + op: str, + other: InferenceResult, + context: InferenceContext, + method_name: str, +) -> Generator[InferenceResult, None, None]: + """Invoke binary operation inference on the given instance.""" + methods = dunder_lookup.lookup(instance, method_name) + context = bind_context_to_node(context, instance) + method = methods[0] + context.callcontext.callee = method + + if ( + isinstance(instance, nodes.Const) + and isinstance(instance.value, str) + and op == "%" + ): + return iter(_infer_old_style_string_formatting(instance, other, context)) + + try: + inferred = next(method.infer(context=context)) + except StopIteration as e: + raise InferenceError(node=method, context=context) from e + if isinstance(inferred, util.UninferableBase): + raise InferenceError + if not isinstance( + instance, (nodes.Const, nodes.Tuple, nodes.List, nodes.ClassDef, bases.Instance) + ): + raise InferenceError # pragma: no cover # Used as a failsafe + return instance.infer_binary_op(opnode, op, other, context, inferred) + + +def _aug_op( + instance: InferenceResult, + opnode: nodes.AugAssign, + op: str, + other: InferenceResult, + context: InferenceContext, + reverse: bool = False, +) -> functools.partial[Generator[InferenceResult, None, None]]: + """Get an inference callable for an augmented binary operation.""" + method_name = protocols.AUGMENTED_OP_METHOD[op] + return functools.partial( + _invoke_binop_inference, + instance=instance, + op=op, + opnode=opnode, + other=other, + context=context, + method_name=method_name, + ) + + +def _bin_op( + instance: InferenceResult, + opnode: nodes.AugAssign | nodes.BinOp, + op: str, + other: InferenceResult, + context: InferenceContext, + reverse: bool = False, +) -> functools.partial[Generator[InferenceResult, None, None]]: + """Get an inference callable for a normal binary operation. + + If *reverse* is True, then the reflected method will be used instead. + """ + if reverse: + method_name = protocols.REFLECTED_BIN_OP_METHOD[op] + else: + method_name = protocols.BIN_OP_METHOD[op] + return functools.partial( + _invoke_binop_inference, + instance=instance, + op=op, + opnode=opnode, + other=other, + context=context, + method_name=method_name, + ) + + +def _bin_op_or_union_type( + left: bases.UnionType | nodes.ClassDef | nodes.Const, + right: bases.UnionType | nodes.ClassDef | nodes.Const, +) -> Generator[InferenceResult, None, None]: + """Create a new UnionType instance for binary or, e.g. int | str.""" + yield bases.UnionType(left, right) + + +def _get_binop_contexts(context, left, right): + """Get contexts for binary operations. + + This will return two inference contexts, the first one + for x.__op__(y), the other one for y.__rop__(x), where + only the arguments are inversed. + """ + # The order is important, since the first one should be + # left.__op__(right). + for arg in (right, left): + new_context = context.clone() + new_context.callcontext = CallContext(args=[arg]) + new_context.boundnode = None + yield new_context + + +def _same_type(type1, type2) -> bool: + """Check if type1 is the same as type2.""" + return type1.qname() == type2.qname() + + +def _get_binop_flow( + left: InferenceResult, + left_type: InferenceResult | None, + binary_opnode: nodes.AugAssign | nodes.BinOp, + right: InferenceResult, + right_type: InferenceResult | None, + context: InferenceContext, + reverse_context: InferenceContext, +) -> list[functools.partial[Generator[InferenceResult, None, None]]]: + """Get the flow for binary operations. + + The rules are a bit messy: + + * if left and right have the same type, then only one + method will be called, left.__op__(right) + * if left and right are unrelated typewise, then first + left.__op__(right) is tried and if this does not exist + or returns NotImplemented, then right.__rop__(left) is tried. + * if left is a subtype of right, then only left.__op__(right) + is tried. + * if left is a supertype of right, then right.__rop__(left) + is first tried and then left.__op__(right) + """ + op = binary_opnode.op + if _same_type(left_type, right_type): + methods = [_bin_op(left, binary_opnode, op, right, context)] + elif helpers.is_subtype(left_type, right_type): + methods = [_bin_op(left, binary_opnode, op, right, context)] + elif helpers.is_supertype(left_type, right_type): + methods = [ + _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True), + _bin_op(left, binary_opnode, op, right, context), + ] + else: + methods = [ + _bin_op(left, binary_opnode, op, right, context), + _bin_op(right, binary_opnode, op, left, reverse_context, reverse=True), + ] + + if ( + PY310_PLUS + and op == "|" + and ( + isinstance(left, (bases.UnionType, nodes.ClassDef)) + or isinstance(left, nodes.Const) + and left.value is None + ) + and ( + isinstance(right, (bases.UnionType, nodes.ClassDef)) + or isinstance(right, nodes.Const) + and right.value is None + ) + ): + methods.extend([functools.partial(_bin_op_or_union_type, left, right)]) + return methods + + +def _get_aug_flow( + left: InferenceResult, + left_type: InferenceResult | None, + aug_opnode: nodes.AugAssign, + right: InferenceResult, + right_type: InferenceResult | None, + context: InferenceContext, + reverse_context: InferenceContext, +) -> list[functools.partial[Generator[InferenceResult, None, None]]]: + """Get the flow for augmented binary operations. + + The rules are a bit messy: + + * if left and right have the same type, then left.__augop__(right) + is first tried and then left.__op__(right). + * if left and right are unrelated typewise, then + left.__augop__(right) is tried, then left.__op__(right) + is tried and then right.__rop__(left) is tried. + * if left is a subtype of right, then left.__augop__(right) + is tried and then left.__op__(right). + * if left is a supertype of right, then left.__augop__(right) + is tried, then right.__rop__(left) and then + left.__op__(right) + """ + bin_op = aug_opnode.op.strip("=") + aug_op = aug_opnode.op + if _same_type(left_type, right_type): + methods = [ + _aug_op(left, aug_opnode, aug_op, right, context), + _bin_op(left, aug_opnode, bin_op, right, context), + ] + elif helpers.is_subtype(left_type, right_type): + methods = [ + _aug_op(left, aug_opnode, aug_op, right, context), + _bin_op(left, aug_opnode, bin_op, right, context), + ] + elif helpers.is_supertype(left_type, right_type): + methods = [ + _aug_op(left, aug_opnode, aug_op, right, context), + _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True), + _bin_op(left, aug_opnode, bin_op, right, context), + ] + else: + methods = [ + _aug_op(left, aug_opnode, aug_op, right, context), + _bin_op(left, aug_opnode, bin_op, right, context), + _bin_op(right, aug_opnode, bin_op, left, reverse_context, reverse=True), + ] + return methods + + +def _infer_binary_operation( + left: InferenceResult, + right: InferenceResult, + binary_opnode: nodes.AugAssign | nodes.BinOp, + context: InferenceContext, + flow_factory: GetFlowFactory, +) -> Generator[InferenceResult | util.BadBinaryOperationMessage, None, None]: + """Infer a binary operation between a left operand and a right operand. + + This is used by both normal binary operations and augmented binary + operations, the only difference is the flow factory used. + """ + + context, reverse_context = _get_binop_contexts(context, left, right) + left_type = helpers.object_type(left) + right_type = helpers.object_type(right) + methods = flow_factory( + left, left_type, binary_opnode, right, right_type, context, reverse_context + ) + for method in methods: + try: + results = list(method()) + except AttributeError: + continue + except AttributeInferenceError: + continue + except InferenceError: + yield util.Uninferable + return + else: + if any(isinstance(result, util.UninferableBase) for result in results): + yield util.Uninferable + return + + if all(map(_is_not_implemented, results)): + continue + not_implemented = sum( + 1 for result in results if _is_not_implemented(result) + ) + if not_implemented and not_implemented != len(results): + # Can't infer yet what this is. + yield util.Uninferable + return + + yield from results + return + # The operation doesn't seem to be supported so let the caller know about it + yield util.BadBinaryOperationMessage(left_type, binary_opnode.op, right_type) + + +def _infer_binop( + self: nodes.BinOp, context: InferenceContext | None = None +) -> Generator[InferenceResult | util.BadBinaryOperationMessage, None, None]: + """Binary operation inference logic.""" + left = self.left + right = self.right + + # we use two separate contexts for evaluating lhs and rhs because + # 1. evaluating lhs may leave some undesired entries in context.path + # which may not let us infer right value of rhs + context = context or InferenceContext() + lhs_context = copy_context(context) + rhs_context = copy_context(context) + lhs_iter = left.infer(context=lhs_context) + rhs_iter = right.infer(context=rhs_context) + for lhs, rhs in itertools.product(lhs_iter, rhs_iter): + if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)): + # Don't know how to process this. + yield util.Uninferable + return + + try: + yield from _infer_binary_operation(lhs, rhs, self, context, _get_binop_flow) + except _NonDeducibleTypeHierarchy: + yield util.Uninferable + + +@decorators.yes_if_nothing_inferred +@decorators.path_wrapper +def infer_binop( + self: nodes.BinOp, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, None]: + return _filter_operation_errors( + self, _infer_binop, context, util.BadBinaryOperationMessage + ) + + +nodes.BinOp._infer_binop = _infer_binop +nodes.BinOp._infer = infer_binop + +COMPARE_OPS: dict[str, Callable[[Any, Any], bool]] = { + "==": operator.eq, + "!=": operator.ne, + "<": operator.lt, + "<=": operator.le, + ">": operator.gt, + ">=": operator.ge, + "in": lambda a, b: a in b, + "not in": lambda a, b: a not in b, +} +UNINFERABLE_OPS = { + "is", + "is not", +} + + +def _to_literal(node: nodes.NodeNG) -> Any: + # Can raise SyntaxError or ValueError from ast.literal_eval + # Can raise AttributeError from node.as_string() as not all nodes have a visitor + # Is this the stupidest idea or the simplest idea? + return ast.literal_eval(node.as_string()) + + +def _do_compare( + left_iter: Iterable[nodes.NodeNG], op: str, right_iter: Iterable[nodes.NodeNG] +) -> bool | util.UninferableBase: + """ + If all possible combinations are either True or False, return that: + >>> _do_compare([1, 2], '<=', [3, 4]) + True + >>> _do_compare([1, 2], '==', [3, 4]) + False + + If any item is uninferable, or if some combinations are True and some + are False, return Uninferable: + >>> _do_compare([1, 3], '<=', [2, 4]) + util.Uninferable + """ + retval: bool | None = None + if op in UNINFERABLE_OPS: + return util.Uninferable + op_func = COMPARE_OPS[op] + + for left, right in itertools.product(left_iter, right_iter): + if isinstance(left, util.UninferableBase) or isinstance( + right, util.UninferableBase + ): + return util.Uninferable + + try: + left, right = _to_literal(left), _to_literal(right) + except (SyntaxError, ValueError, AttributeError): + return util.Uninferable + + try: + expr = op_func(left, right) + except TypeError as exc: + raise AstroidTypeError from exc + + if retval is None: + retval = expr + elif retval != expr: + return util.Uninferable + # (or both, but "True | False" is basically the same) + + assert retval is not None + return retval # it was all the same value + + +def _infer_compare( + self: nodes.Compare, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[nodes.Const | util.UninferableBase, None, None]: + """Chained comparison inference logic.""" + retval: bool | util.UninferableBase = True + + ops = self.ops + left_node = self.left + lhs = list(left_node.infer(context=context)) + # should we break early if first element is uninferable? + for op, right_node in ops: + # eagerly evaluate rhs so that values can be re-used as lhs + rhs = list(right_node.infer(context=context)) + try: + retval = _do_compare(lhs, op, rhs) + except AstroidTypeError: + retval = util.Uninferable + break + if retval is not True: + break # short-circuit + lhs = rhs # continue + if retval is util.Uninferable: + yield retval # type: ignore[misc] + else: + yield nodes.Const(retval) + + +nodes.Compare._infer = _infer_compare # type: ignore[assignment] + + +def _infer_augassign( + self: nodes.AugAssign, context: InferenceContext | None = None +) -> Generator[InferenceResult | util.BadBinaryOperationMessage, None, None]: + """Inference logic for augmented binary operations.""" + context = context or InferenceContext() + + rhs_context = context.clone() + + lhs_iter = self.target.infer_lhs(context=context) + rhs_iter = self.value.infer(context=rhs_context) + for lhs, rhs in itertools.product(lhs_iter, rhs_iter): + if any(isinstance(value, util.UninferableBase) for value in (rhs, lhs)): + # Don't know how to process this. + yield util.Uninferable + return + + try: + yield from _infer_binary_operation( + left=lhs, + right=rhs, + binary_opnode=self, + context=context, + flow_factory=_get_aug_flow, + ) + except _NonDeducibleTypeHierarchy: + yield util.Uninferable + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_augassign( + self: nodes.AugAssign, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, None]: + return _filter_operation_errors( + self, _infer_augassign, context, util.BadBinaryOperationMessage + ) + + +nodes.AugAssign._infer_augassign = _infer_augassign +nodes.AugAssign._infer = infer_augassign + +# End of binary operation inference. + + +@decorators.raise_if_nothing_inferred +def infer_arguments( + self: nodes.Arguments, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, None]: + if context is None or context.lookupname is None: + raise InferenceError(node=self, context=context) + return protocols._arguments_infer_argname(self, context.lookupname, context) + + +nodes.Arguments._infer = infer_arguments # type: ignore[assignment] + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_assign( + self: nodes.AssignName | nodes.AssignAttr, + context: InferenceContext | None = None, + **kwargs: Any, +) -> Generator[InferenceResult, None, None]: + """Infer a AssignName/AssignAttr: need to inspect the RHS part of the + assign node. + """ + if isinstance(self.parent, nodes.AugAssign): + return self.parent.infer(context) + + stmts = list(self.assigned_stmts(context=context)) + return bases._infer_stmts(stmts, context) + + +nodes.AssignName._infer = infer_assign +nodes.AssignAttr._infer = infer_assign + + +@decorators.raise_if_nothing_inferred +@decorators.path_wrapper +def infer_empty_node( + self: nodes.EmptyNode, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, None]: + if not self.has_underlying_object(): + yield util.Uninferable + else: + try: + yield from AstroidManager().infer_ast_from_something( + self.object, context=context + ) + except AstroidError: + yield util.Uninferable + + +nodes.EmptyNode._infer = infer_empty_node # type: ignore[assignment] + + +def _populate_context_lookup(call: nodes.Call, context: InferenceContext | None): + # Allows context to be saved for later + # for inference inside a function + context_lookup: dict[InferenceResult, InferenceContext] = {} + if context is None: + return context_lookup + for arg in call.args: + if isinstance(arg, nodes.Starred): + context_lookup[arg.value] = context + else: + context_lookup[arg] = context + keywords = call.keywords if call.keywords is not None else [] + for keyword in keywords: + context_lookup[keyword.value] = context + return context_lookup + + +@decorators.raise_if_nothing_inferred +def infer_ifexp( + self: nodes.IfExp, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[InferenceResult, None, None]: + """Support IfExp inference. + + If we can't infer the truthiness of the condition, we default + to inferring both branches. Otherwise, we infer either branch + depending on the condition. + """ + both_branches = False + # We use two separate contexts for evaluating lhs and rhs because + # evaluating lhs may leave some undesired entries in context.path + # which may not let us infer right value of rhs. + + context = context or InferenceContext() + lhs_context = copy_context(context) + rhs_context = copy_context(context) + try: + test = next(self.test.infer(context=context.clone())) + except (InferenceError, StopIteration): + both_branches = True + else: + if not isinstance(test, util.UninferableBase): + if test.bool_value(): + yield from self.body.infer(context=lhs_context) + else: + yield from self.orelse.infer(context=rhs_context) + else: + both_branches = True + if both_branches: + yield from self.body.infer(context=lhs_context) + yield from self.orelse.infer(context=rhs_context) + + +nodes.IfExp._infer = infer_ifexp # type: ignore[assignment] + + +def infer_functiondef( + self: _FunctionDefT, context: InferenceContext | None = None, **kwargs: Any +) -> Generator[Property | _FunctionDefT, None, InferenceErrorInfo]: + if not self.decorators or not bases._is_property(self): + yield self + return InferenceErrorInfo(node=self, context=context) + + # When inferring a property, we instantiate a new `objects.Property` object, + # which in turn, because it inherits from `FunctionDef`, sets itself in the locals + # of the wrapping frame. This means that every time we infer a property, the locals + # are mutated with a new instance of the property. To avoid this, we detect this + # scenario and avoid passing the `parent` argument to the constructor. + parent_frame = self.parent.frame(future=True) + property_already_in_parent_locals = self.name in parent_frame.locals and any( + isinstance(val, objects.Property) for val in parent_frame.locals[self.name] + ) + # We also don't want to pass parent if the definition is within a Try node + if isinstance(self.parent, (nodes.TryExcept, nodes.TryFinally, nodes.If)): + property_already_in_parent_locals = True + + prop_func = objects.Property( + function=self, + name=self.name, + lineno=self.lineno, + parent=self.parent if not property_already_in_parent_locals else None, + col_offset=self.col_offset, + ) + if property_already_in_parent_locals: + prop_func.parent = self.parent + prop_func.postinit(body=[], args=self.args, doc_node=self.doc_node) + yield prop_func + return InferenceErrorInfo(node=self, context=context) + + +nodes.FunctionDef._infer = infer_functiondef diff --git a/venv/lib/python3.10/site-packages/astroid/inference_tip.py b/venv/lib/python3.10/site-packages/astroid/inference_tip.py new file mode 100644 index 00000000..9f315a53 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/inference_tip.py @@ -0,0 +1,85 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Transform utilities (filters and decorator).""" + +from __future__ import annotations + +import typing +from collections.abc import Iterator + +import wrapt + +from astroid.exceptions import InferenceOverwriteError, UseInferenceDefault +from astroid.nodes import NodeNG +from astroid.typing import InferenceResult, InferFn + +_cache: dict[tuple[InferFn, NodeNG], list[InferenceResult] | None] = {} + + +def clear_inference_tip_cache() -> None: + """Clear the inference tips cache.""" + _cache.clear() + + +@wrapt.decorator +def _inference_tip_cached( + func: InferFn, instance: None, args: typing.Any, kwargs: typing.Any +) -> Iterator[InferenceResult]: + """Cache decorator used for inference tips.""" + node = args[0] + try: + result = _cache[func, node] + # If through recursion we end up trying to infer the same + # func + node we raise here. + if result is None: + raise UseInferenceDefault() + except KeyError: + _cache[func, node] = None + result = _cache[func, node] = list(func(*args, **kwargs)) + assert result + return iter(result) + + +def inference_tip(infer_function: InferFn, raise_on_overwrite: bool = False) -> InferFn: + """Given an instance specific inference function, return a function to be + given to AstroidManager().register_transform to set this inference function. + + :param bool raise_on_overwrite: Raise an `InferenceOverwriteError` + if the inference tip will overwrite another. Used for debugging + + Typical usage + + .. sourcecode:: python + + AstroidManager().register_transform(Call, inference_tip(infer_named_tuple), + predicate) + + .. Note:: + + Using an inference tip will override + any previously set inference tip for the given + node. Use a predicate in the transform to prevent + excess overwrites. + """ + + def transform(node: NodeNG, infer_function: InferFn = infer_function) -> NodeNG: + if ( + raise_on_overwrite + and node._explicit_inference is not None + and node._explicit_inference is not infer_function + ): + raise InferenceOverwriteError( + "Inference already set to {existing_inference}. " + "Trying to overwrite with {new_inference} for {node}".format( + existing_inference=infer_function, + new_inference=node._explicit_inference, + node=node, + ) + ) + # pylint: disable=no-value-for-parameter + node._explicit_inference = _inference_tip_cached(infer_function) + return node + + return transform diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py b/venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py new file mode 120000 index 00000000..05b4099d --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/interpreter/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e3/b0/c4/4298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/interpreter/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..1231bd9a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/interpreter/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/__pycache__/dunder_lookup.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/interpreter/__pycache__/dunder_lookup.cpython-310.pyc new file mode 100644 index 00000000..fce5475d Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/interpreter/__pycache__/dunder_lookup.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/__pycache__/objectmodel.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/interpreter/__pycache__/objectmodel.cpython-310.pyc new file mode 100644 index 00000000..245d3430 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/interpreter/__pycache__/objectmodel.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py new file mode 120000 index 00000000..05b4099d --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e3/b0/c4/4298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..94e9b49f Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__pycache__/spec.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__pycache__/spec.cpython-310.pyc new file mode 100644 index 00000000..fca96e12 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__pycache__/spec.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__pycache__/util.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__pycache__/util.cpython-310.pyc new file mode 100644 index 00000000..15895411 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/__pycache__/util.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py new file mode 100644 index 00000000..f000468e --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/spec.py @@ -0,0 +1,472 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +from __future__ import annotations + +import abc +import enum +import importlib +import importlib.machinery +import importlib.util +import os +import pathlib +import sys +import types +import zipimport +from collections.abc import Iterator, Sequence +from pathlib import Path +from typing import Any, NamedTuple + +from astroid.const import PY310_PLUS +from astroid.modutils import EXT_LIB_DIRS + +from . import util + +if sys.version_info >= (3, 8): + from typing import Literal, Protocol +else: + from typing_extensions import Literal, Protocol + + +# The MetaPathFinder protocol comes from typeshed, which says: +# Intentionally omits one deprecated and one optional method of `importlib.abc.MetaPathFinder` +class _MetaPathFinder(Protocol): + def find_spec( + self, + fullname: str, + path: Sequence[str] | None, + target: types.ModuleType | None = ..., + ) -> importlib.machinery.ModuleSpec | None: + ... # pragma: no cover + + +class ModuleType(enum.Enum): + """Python module types used for ModuleSpec.""" + + C_BUILTIN = enum.auto() + C_EXTENSION = enum.auto() + PKG_DIRECTORY = enum.auto() + PY_CODERESOURCE = enum.auto() + PY_COMPILED = enum.auto() + PY_FROZEN = enum.auto() + PY_RESOURCE = enum.auto() + PY_SOURCE = enum.auto() + PY_ZIPMODULE = enum.auto() + PY_NAMESPACE = enum.auto() + + +_MetaPathFinderModuleTypes: dict[str, ModuleType] = { + # Finders created by setuptools editable installs + "_EditableFinder": ModuleType.PY_SOURCE, + "_EditableNamespaceFinder": ModuleType.PY_NAMESPACE, + # Finders create by six + "_SixMetaPathImporter": ModuleType.PY_SOURCE, +} + +_EditableFinderClasses: set[str] = { + "_EditableFinder", + "_EditableNamespaceFinder", +} + + +class ModuleSpec(NamedTuple): + """Defines a class similar to PEP 420's ModuleSpec. + + A module spec defines a name of a module, its type, location + and where submodules can be found, if the module is a package. + """ + + name: str + type: ModuleType | None + location: str | None = None + origin: str | None = None + submodule_search_locations: Sequence[str] | None = None + + +class Finder: + """A finder is a class which knows how to find a particular module.""" + + def __init__(self, path: Sequence[str] | None = None) -> None: + self._path = path or sys.path + + @abc.abstractmethod + def find_module( + self, + modname: str, + module_parts: Sequence[str], + processed: list[str], + submodule_path: Sequence[str] | None, + ) -> ModuleSpec | None: + """Find the given module. + + Each finder is responsible for each protocol of finding, as long as + they all return a ModuleSpec. + + :param modname: The module which needs to be searched. + :param module_parts: It should be a list of strings, + where each part contributes to the module's + namespace. + :param processed: What parts from the module parts were processed + so far. + :param submodule_path: A list of paths where the module + can be looked into. + :returns: A ModuleSpec, describing how and where the module was found, + None, otherwise. + """ + + def contribute_to_path( + self, spec: ModuleSpec, processed: list[str] + ) -> Sequence[str] | None: + """Get a list of extra paths where this finder can search.""" + + +class ImportlibFinder(Finder): + """A finder based on the importlib module.""" + + _SUFFIXES: Sequence[tuple[str, ModuleType]] = ( + [(s, ModuleType.C_EXTENSION) for s in importlib.machinery.EXTENSION_SUFFIXES] + + [(s, ModuleType.PY_SOURCE) for s in importlib.machinery.SOURCE_SUFFIXES] + + [(s, ModuleType.PY_COMPILED) for s in importlib.machinery.BYTECODE_SUFFIXES] + ) + + def find_module( + self, + modname: str, + module_parts: Sequence[str], + processed: list[str], + submodule_path: Sequence[str] | None, + ) -> ModuleSpec | None: + if submodule_path is not None: + submodule_path = list(submodule_path) + elif modname in sys.builtin_module_names: + return ModuleSpec( + name=modname, + location=None, + type=ModuleType.C_BUILTIN, + ) + else: + try: + spec = importlib.util.find_spec(modname) + if ( + spec + and spec.loader # type: ignore[comparison-overlap] # noqa: E501 + is importlib.machinery.FrozenImporter + ): + # No need for BuiltinImporter; builtins handled above + return ModuleSpec( + name=modname, + location=getattr(spec.loader_state, "filename", None), + type=ModuleType.PY_FROZEN, + ) + except ValueError: + pass + submodule_path = sys.path + + for entry in submodule_path: + package_directory = os.path.join(entry, modname) + for suffix in (".py", importlib.machinery.BYTECODE_SUFFIXES[0]): + package_file_name = "__init__" + suffix + file_path = os.path.join(package_directory, package_file_name) + if os.path.isfile(file_path): + return ModuleSpec( + name=modname, + location=package_directory, + type=ModuleType.PKG_DIRECTORY, + ) + for suffix, type_ in ImportlibFinder._SUFFIXES: + file_name = modname + suffix + file_path = os.path.join(entry, file_name) + if os.path.isfile(file_path): + return ModuleSpec(name=modname, location=file_path, type=type_) + return None + + def contribute_to_path( + self, spec: ModuleSpec, processed: list[str] + ) -> Sequence[str] | None: + if spec.location is None: + # Builtin. + return None + + if _is_setuptools_namespace(Path(spec.location)): + # extend_path is called, search sys.path for module/packages + # of this name see pkgutil.extend_path documentation + path = [ + os.path.join(p, *processed) + for p in sys.path + if os.path.isdir(os.path.join(p, *processed)) + ] + elif spec.name == "distutils" and not any( + spec.location.lower().startswith(ext_lib_dir.lower()) + for ext_lib_dir in EXT_LIB_DIRS + ): + # virtualenv below 20.0 patches distutils in an unexpected way + # so we just find the location of distutils that will be + # imported to avoid spurious import-error messages + # https://github.com/PyCQA/pylint/issues/5645 + # A regression test to create this scenario exists in release-tests.yml + # and can be triggered manually from GitHub Actions + distutils_spec = importlib.util.find_spec("distutils") + if distutils_spec and distutils_spec.origin: + origin_path = Path( + distutils_spec.origin + ) # e.g. .../distutils/__init__.py + path = [str(origin_path.parent)] # e.g. .../distutils + else: + path = [spec.location] + else: + path = [spec.location] + return path + + +class ExplicitNamespacePackageFinder(ImportlibFinder): + """A finder for the explicit namespace packages.""" + + def find_module( + self, + modname: str, + module_parts: Sequence[str], + processed: list[str], + submodule_path: Sequence[str] | None, + ) -> ModuleSpec | None: + if processed: + modname = ".".join(processed + [modname]) + if util.is_namespace(modname) and modname in sys.modules: + submodule_path = sys.modules[modname].__path__ + return ModuleSpec( + name=modname, + location="", + origin="namespace", + type=ModuleType.PY_NAMESPACE, + submodule_search_locations=submodule_path, + ) + return None + + def contribute_to_path( + self, spec: ModuleSpec, processed: list[str] + ) -> Sequence[str] | None: + return spec.submodule_search_locations + + +class ZipFinder(Finder): + """Finder that knows how to find a module inside zip files.""" + + def __init__(self, path: Sequence[str]) -> None: + super().__init__(path) + for entry_path in path: + if entry_path not in sys.path_importer_cache: + try: + sys.path_importer_cache[entry_path] = zipimport.zipimporter( # type: ignore[assignment] + entry_path + ) + except zipimport.ZipImportError: + continue + + def find_module( + self, + modname: str, + module_parts: Sequence[str], + processed: list[str], + submodule_path: Sequence[str] | None, + ) -> ModuleSpec | None: + try: + file_type, filename, path = _search_zip(module_parts) + except ImportError: + return None + + return ModuleSpec( + name=modname, + location=filename, + origin="egg", + type=file_type, + submodule_search_locations=path, + ) + + +class PathSpecFinder(Finder): + """Finder based on importlib.machinery.PathFinder.""" + + def find_module( + self, + modname: str, + module_parts: Sequence[str], + processed: list[str], + submodule_path: Sequence[str] | None, + ) -> ModuleSpec | None: + spec = importlib.machinery.PathFinder.find_spec(modname, path=submodule_path) + if spec is not None: + is_namespace_pkg = spec.origin is None + location = spec.origin if not is_namespace_pkg else None + module_type = ModuleType.PY_NAMESPACE if is_namespace_pkg else None + return ModuleSpec( + name=spec.name, + location=location, + origin=spec.origin, + type=module_type, + submodule_search_locations=list(spec.submodule_search_locations or []), + ) + return spec + + def contribute_to_path( + self, spec: ModuleSpec, processed: list[str] + ) -> Sequence[str] | None: + if spec.type == ModuleType.PY_NAMESPACE: + return spec.submodule_search_locations + return None + + +_SPEC_FINDERS = ( + ImportlibFinder, + ZipFinder, + PathSpecFinder, + ExplicitNamespacePackageFinder, +) + + +def _is_setuptools_namespace(location: pathlib.Path) -> bool: + try: + with open(location / "__init__.py", "rb") as stream: + data = stream.read(4096) + except OSError: + return False + extend_path = b"pkgutil" in data and b"extend_path" in data + declare_namespace = ( + b"pkg_resources" in data and b"declare_namespace(__name__)" in data + ) + return extend_path or declare_namespace + + +def _get_zipimporters() -> Iterator[tuple[str, zipimport.zipimporter]]: + for filepath, importer in sys.path_importer_cache.items(): + if isinstance(importer, zipimport.zipimporter): + yield filepath, importer + + +def _search_zip( + modpath: Sequence[str], +) -> tuple[Literal[ModuleType.PY_ZIPMODULE], str, str]: + for filepath, importer in _get_zipimporters(): + if PY310_PLUS: + found: Any = importer.find_spec(modpath[0]) + else: + found = importer.find_module(modpath[0]) + if found: + if PY310_PLUS: + if not importer.find_spec(os.path.sep.join(modpath)): + raise ImportError( + "No module named %s in %s/%s" + % (".".join(modpath[1:]), filepath, modpath) + ) + elif not importer.find_module(os.path.sep.join(modpath)): + raise ImportError( + "No module named %s in %s/%s" + % (".".join(modpath[1:]), filepath, modpath) + ) + return ( + ModuleType.PY_ZIPMODULE, + os.path.abspath(filepath) + os.path.sep + os.path.sep.join(modpath), + filepath, + ) + raise ImportError(f"No module named {'.'.join(modpath)}") + + +def _find_spec_with_path( + search_path: Sequence[str], + modname: str, + module_parts: list[str], + processed: list[str], + submodule_path: Sequence[str] | None, +) -> tuple[Finder | _MetaPathFinder, ModuleSpec]: + for finder in _SPEC_FINDERS: + finder_instance = finder(search_path) + spec = finder_instance.find_module( + modname, module_parts, processed, submodule_path + ) + if spec is None: + continue + return finder_instance, spec + + # Support for custom finders + for meta_finder in sys.meta_path: + # See if we support the customer import hook of the meta_finder + meta_finder_name = meta_finder.__class__.__name__ + if meta_finder_name not in _MetaPathFinderModuleTypes: + # Setuptools>62 creates its EditableFinders dynamically and have + # "type" as their __class__.__name__. We check __name__ as well + # to see if we can support the finder. + try: + meta_finder_name = meta_finder.__name__ + except AttributeError: + continue + if meta_finder_name not in _MetaPathFinderModuleTypes: + continue + + module_type = _MetaPathFinderModuleTypes[meta_finder_name] + + # Meta path finders are supposed to have a find_spec method since + # Python 3.4. However, some third-party finders do not implement it. + # PEP302 does not refer to find_spec as well. + # See: https://github.com/PyCQA/astroid/pull/1752/ + if not hasattr(meta_finder, "find_spec"): + continue + + spec = meta_finder.find_spec(modname, submodule_path) + if spec: + return ( + meta_finder, + ModuleSpec( + spec.name, + module_type, + spec.origin, + spec.origin, + spec.submodule_search_locations, + ), + ) + + raise ImportError(f"No module named {'.'.join(module_parts)}") + + +def find_spec(modpath: list[str], path: Sequence[str] | None = None) -> ModuleSpec: + """Find a spec for the given module. + + :type modpath: list or tuple + :param modpath: + split module's name (i.e name of a module or package split + on '.'), with leading empty strings for explicit relative import + + :type path: list or None + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :rtype: ModuleSpec + :return: A module spec, which describes how the module was + found and where. + """ + _path = path or sys.path + + # Need a copy for not mutating the argument. + modpath = modpath[:] + + submodule_path = None + module_parts = modpath[:] + processed: list[str] = [] + + while modpath: + modname = modpath.pop(0) + finder, spec = _find_spec_with_path( + _path, modname, module_parts, processed, submodule_path or path + ) + processed.append(modname) + if modpath: + if isinstance(finder, Finder): + submodule_path = finder.contribute_to_path(spec, processed) + # If modname is a package from an editable install, update submodule_path + # so that the next module in the path will be found inside of it using importlib. + elif finder.__name__ in _EditableFinderClasses: + submodule_path = spec.submodule_search_locations + + if spec.type == ModuleType.PKG_DIRECTORY: + spec = spec._replace(submodule_search_locations=submodule_path) + + return spec diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py new file mode 120000 index 00000000..30185708 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/interpreter/_import/util.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/6f/98/20/2114ad87f239530bf7b38e233307f73a59c5016215fb9ef05f75caf723 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py b/venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py new file mode 120000 index 00000000..b11abdc8 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/interpreter/dunder_lookup.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ef/b6/0d/9a0a7a976b0f03b508694a6368febee31dba34ff7722284f479104bfe7 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py b/venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py new file mode 100644 index 00000000..b7dac7f7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/interpreter/objectmodel.py @@ -0,0 +1,940 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +""" +Data object model, as per https://docs.python.org/3/reference/datamodel.html. + +This module describes, at least partially, a data object model for some +of astroid's nodes. The model contains special attributes that nodes such +as functions, classes, modules etc have, such as __doc__, __class__, +__module__ etc, being used when doing attribute lookups over nodes. + +For instance, inferring `obj.__class__` will first trigger an inference +of the `obj` variable. If it was successfully inferred, then an attribute +`__class__ will be looked for in the inferred object. This is the part +where the data model occurs. The model is attached to those nodes +and the lookup mechanism will try to see if attributes such as +`__class__` are defined by the model or not. If they are defined, +the model will be requested to return the corresponding value of that +attribute. Thus the model can be viewed as a special part of the lookup +mechanism. +""" + +from __future__ import annotations + +import itertools +import os +import pprint +import sys +import types +from functools import lru_cache +from typing import TYPE_CHECKING, Any + +import astroid +from astroid import bases, nodes, util +from astroid.context import InferenceContext, copy_context +from astroid.exceptions import AttributeInferenceError, InferenceError, NoDefault +from astroid.manager import AstroidManager +from astroid.nodes import node_classes + +objects = util.lazy_import("objects") +builder = util.lazy_import("builder") + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + +if TYPE_CHECKING: + from astroid import builder + from astroid.objects import Property + +IMPL_PREFIX = "attr_" +LEN_OF_IMPL_PREFIX = len(IMPL_PREFIX) + + +def _dunder_dict(instance, attributes): + obj = node_classes.Dict(parent=instance) + + # Convert the keys to node strings + keys = [ + node_classes.Const(value=value, parent=obj) for value in list(attributes.keys()) + ] + + # The original attribute has a list of elements for each key, + # but that is not useful for retrieving the special attribute's value. + # In this case, we're picking the last value from each list. + values = [elem[-1] for elem in attributes.values()] + + obj.postinit(list(zip(keys, values))) + return obj + + +def _get_bound_node(model: ObjectModel) -> Any: + # TODO: Use isinstance instead of try ... except after _instance has typing + try: + return model._instance._proxied + except AttributeError: + return model._instance + + +class ObjectModel: + def __init__(self): + self._instance = None + + def __repr__(self): + result = [] + cname = type(self).__name__ + string = "%(cname)s(%(fields)s)" + alignment = len(cname) + 1 + for field in sorted(self.attributes()): + width = 80 - len(field) - alignment + lines = pprint.pformat(field, indent=2, width=width).splitlines(True) + + inner = [lines[0]] + for line in lines[1:]: + inner.append(" " * alignment + line) + result.append(field) + + return string % { + "cname": cname, + "fields": (",\n" + " " * alignment).join(result), + } + + def __call__(self, instance): + self._instance = instance + return self + + def __get__(self, instance, cls=None): + # ObjectModel needs to be a descriptor so that just doing + # `special_attributes = SomeObjectModel` should be enough in the body of a node. + # But at the same time, node.special_attributes should return an object + # which can be used for manipulating the special attributes. That's the reason + # we pass the instance through which it got accessed to ObjectModel.__call__, + # returning itself afterwards, so we can still have access to the + # underlying data model and to the instance for which it got accessed. + return self(instance) + + def __contains__(self, name) -> bool: + return name in self.attributes() + + @lru_cache() # noqa + def attributes(self) -> list[str]: + """Get the attributes which are exported by this object model.""" + return [o[LEN_OF_IMPL_PREFIX:] for o in dir(self) if o.startswith(IMPL_PREFIX)] + + def lookup(self, name): + """Look up the given *name* in the current model. + + It should return an AST or an interpreter object, + but if the name is not found, then an AttributeInferenceError will be raised. + """ + if name in self.attributes(): + return getattr(self, IMPL_PREFIX + name) + raise AttributeInferenceError(target=self._instance, attribute=name) + + @property + def attr___new__(self) -> bases.BoundMethod: + """Calling cls.__new__(type) on an object returns an instance of 'type'.""" + node: nodes.FunctionDef = builder.extract_node( + """def __new__(self, cls): return cls()""" + ) + # We set the parent as being the ClassDef of 'object' as that + # triggers correct inference as a call to __new__ in bases.py + node.parent = AstroidManager().builtins_module["object"] + + return bases.BoundMethod(proxy=node, bound=_get_bound_node(self)) + + @property + def attr___init__(self) -> bases.BoundMethod: + """Calling cls.__init__() normally returns None.""" + # The *args and **kwargs are necessary not to trigger warnings about missing + # or extra parameters for '__init__' methods we don't infer correctly. + # This BoundMethod is the fallback value for those. + node: nodes.FunctionDef = builder.extract_node( + """def __init__(self, *args, **kwargs): return None""" + ) + # We set the parent as being the ClassDef of 'object' as that + # is where this method originally comes from + node.parent = AstroidManager().builtins_module["object"] + + return bases.BoundMethod(proxy=node, bound=_get_bound_node(self)) + + +class ModuleModel(ObjectModel): + def _builtins(self): + builtins_ast_module = AstroidManager().builtins_module + return builtins_ast_module.special_attributes.lookup("__dict__") + + @property + def attr_builtins(self): + return self._builtins() + + @property + def attr___path__(self): + if not self._instance.package: + raise AttributeInferenceError(target=self._instance, attribute="__path__") + + path_objs = [ + node_classes.Const( + value=path + if not path.endswith("__init__.py") + else os.path.dirname(path), + parent=self._instance, + ) + for path in self._instance.path + ] + + container = node_classes.List(parent=self._instance) + container.postinit(path_objs) + + return container + + @property + def attr___name__(self): + return node_classes.Const(value=self._instance.name, parent=self._instance) + + @property + def attr___doc__(self): + return node_classes.Const( + value=getattr(self._instance.doc_node, "value", None), + parent=self._instance, + ) + + @property + def attr___file__(self): + return node_classes.Const(value=self._instance.file, parent=self._instance) + + @property + def attr___dict__(self): + return _dunder_dict(self._instance, self._instance.globals) + + @property + def attr___package__(self): + if not self._instance.package: + value = "" + else: + value = self._instance.name + + return node_classes.Const(value=value, parent=self._instance) + + # These are related to the Python 3 implementation of the + # import system, + # https://docs.python.org/3/reference/import.html#import-related-module-attributes + + @property + def attr___spec__(self): + # No handling for now. + return node_classes.Unknown() + + @property + def attr___loader__(self): + # No handling for now. + return node_classes.Unknown() + + @property + def attr___cached__(self): + # No handling for now. + return node_classes.Unknown() + + +class FunctionModel(ObjectModel): + @property + def attr___name__(self): + return node_classes.Const(value=self._instance.name, parent=self._instance) + + @property + def attr___doc__(self): + return node_classes.Const( + value=getattr(self._instance.doc_node, "value", None), + parent=self._instance, + ) + + @property + def attr___qualname__(self): + return node_classes.Const(value=self._instance.qname(), parent=self._instance) + + @property + def attr___defaults__(self): + func = self._instance + if not func.args.defaults: + return node_classes.Const(value=None, parent=func) + + defaults_obj = node_classes.Tuple(parent=func) + defaults_obj.postinit(func.args.defaults) + return defaults_obj + + @property + def attr___annotations__(self): + obj = node_classes.Dict(parent=self._instance) + + 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 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): + func = self._instance + + class DescriptorBoundMethod(bases.BoundMethod): + """Bound method which knows how to understand calling descriptor + binding. + """ + + def implicit_parameters(self) -> Literal[0]: + # Different than BoundMethod since the signature + # is different. + return 0 + + def infer_call_result( + self, caller, context: InferenceContext | None = None + ): + if len(caller.args) > 2 or len(caller.args) < 1: + raise InferenceError( + "Invalid arguments for descriptor binding", + target=self, + context=context, + ) + + context = copy_context(context) + try: + cls = next(caller.args[0].infer(context=context)) + except StopIteration as e: + raise InferenceError(context=context, node=caller.args[0]) from e + + if isinstance(cls, util.UninferableBase): + raise InferenceError( + "Invalid class inferred", target=self, context=context + ) + + # For some reason func is a Node that the below + # code is not expecting + if isinstance(func, bases.BoundMethod): + yield func + return + + # Rebuild the original value, but with the parent set as the + # class where it will be bound. + new_func = func.__class__( + name=func.name, + lineno=func.lineno, + col_offset=func.col_offset, + parent=func.parent, + ) + # pylint: disable=no-member + new_func.postinit( + func.args, + func.body, + func.decorators, + func.returns, + doc_node=func.doc_node, + ) + + # Build a proper bound method that points to our newly built function. + proxy = bases.UnboundMethod(new_func) + yield bases.BoundMethod(proxy=proxy, bound=cls) + + @property + def args(self): + """Overwrite the underlying args to match those of the underlying func. + + Usually the underlying *func* is a function/method, as in: + + def test(self): + pass + + This has only the *self* parameter but when we access test.__get__ + we get a new object which has two parameters, *self* and *type*. + """ + nonlocal func + 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___lt__ = attr___ne__ + attr___eq__ = attr___ne__ + attr___gt__ = attr___ne__ + attr___format__ = attr___ne__ + attr___delattr___ = attr___ne__ + attr___getattribute__ = attr___ne__ + attr___hash__ = attr___ne__ + attr___dir__ = attr___ne__ + attr___call__ = attr___ne__ + attr___class__ = attr___ne__ + attr___closure__ = attr___ne__ + attr___code__ = attr___ne__ + + +class ClassModel(ObjectModel): + def __init__(self): + # Add a context so that inferences called from an instance don't recurse endlessly + self.context = InferenceContext() + + super().__init__() + + @property + def attr___module__(self): + return node_classes.Const(self._instance.root().qname()) + + @property + def attr___name__(self): + return node_classes.Const(self._instance.name) + + @property + def attr___qualname__(self): + return node_classes.Const(self._instance.qname()) + + @property + def attr___doc__(self): + return node_classes.Const(getattr(self._instance.doc_node, "value", None)) + + @property + def attr___mro__(self): + if not self._instance.newstyle: + raise AttributeInferenceError(target=self._instance, attribute="__mro__") + + mro = self._instance.mro() + obj = node_classes.Tuple(parent=self._instance) + obj.postinit(mro) + return obj + + @property + def attr_mro(self): + if not self._instance.newstyle: + raise AttributeInferenceError(target=self._instance, attribute="mro") + + other_self = self + + # Cls.mro is a method and we need to return one in order to have a proper inference. + # The method we're returning is capable of inferring the underlying MRO though. + class MroBoundMethod(bases.BoundMethod): + def infer_call_result( + self, caller, context: InferenceContext | None = 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 = InferenceContext() + elts = list(self._instance._inferred_bases(context)) + obj.postinit(elts=elts) + return obj + + @property + def attr___class__(self): + # pylint: disable=import-outside-toplevel; circular import + from astroid import helpers + + return helpers.object_type(self._instance) + + @property + def attr___subclasses__(self): + """Get the subclasses of the underlying class. + + This looks only in the current module for retrieving the subclasses, + thus it might miss a couple of them. + """ + if not self._instance.newstyle: + raise AttributeInferenceError( + target=self._instance, attribute="__subclasses__" + ) + + qname = self._instance.qname() + root = self._instance.root() + classes = [ + cls + for cls in root.nodes_of_class(nodes.ClassDef) + if cls != self._instance and cls.is_subtype_of(qname, context=self.context) + ] + + obj = node_classes.List(parent=self._instance) + obj.postinit(classes) + + class SubclassesBoundMethod(bases.BoundMethod): + def infer_call_result( + self, caller, context: InferenceContext | None = 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) + + @property + def attr___call__(self): + """Calling a class A() returns an instance of A.""" + return self._instance.instantiate_class() + + +class SuperModel(ObjectModel): + @property + def attr___thisclass__(self): + return self._instance.mro_pointer + + @property + def attr___self_class__(self): + return self._instance._self_class + + @property + def attr___self__(self): + return self._instance.type + + @property + def attr___class__(self): + return self._instance._proxied + + +class UnboundMethodModel(ObjectModel): + @property + def attr___class__(self): + # pylint: disable=import-outside-toplevel; circular import + from astroid import helpers + + return helpers.object_type(self._instance) + + @property + def attr___func__(self): + return self._instance._proxied + + @property + def attr___self__(self): + return node_classes.Const(value=None, parent=self._instance) + + attr_im_func = attr___func__ + attr_im_class = attr___class__ + attr_im_self = attr___self__ + + +class ContextManagerModel(ObjectModel): + """Model for context managers. + + Based on 3.3.9 of the Data Model documentation: + https://docs.python.org/3/reference/datamodel.html#with-statement-context-managers + """ + + @property + def attr___enter__(self) -> bases.BoundMethod: + """Representation of the base implementation of __enter__. + + As per Python documentation: + Enter the runtime context related to this object. The with statement + will bind this method's return value to the target(s) specified in the + as clause of the statement, if any. + """ + node: nodes.FunctionDef = builder.extract_node("""def __enter__(self): ...""") + # We set the parent as being the ClassDef of 'object' as that + # is where this method originally comes from + node.parent = AstroidManager().builtins_module["object"] + + return bases.BoundMethod(proxy=node, bound=_get_bound_node(self)) + + @property + def attr___exit__(self) -> bases.BoundMethod: + """Representation of the base implementation of __exit__. + + As per Python documentation: + Exit the runtime context related to this object. The parameters describe the + exception that caused the context to be exited. If the context was exited + without an exception, all three arguments will be None. + """ + node: nodes.FunctionDef = builder.extract_node( + """def __exit__(self, exc_type, exc_value, traceback): ...""" + ) + # We set the parent as being the ClassDef of 'object' as that + # is where this method originally comes from + node.parent = AstroidManager().builtins_module["object"] + + return bases.BoundMethod(proxy=node, bound=_get_bound_node(self)) + + +class BoundMethodModel(FunctionModel): + @property + def attr___func__(self): + return self._instance._proxied._proxied + + @property + def attr___self__(self): + return self._instance.bound + + +class GeneratorModel(FunctionModel, ContextManagerModel): + def __new__(cls, *args, **kwargs): + # Append the values from the GeneratorType unto this object. + ret = super().__new__(cls, *args, **kwargs) + generator = AstroidManager().builtins_module["generator"] + for name, values in generator.locals.items(): + method = values[0] + + def patched(cls, meth=method): + return meth + + setattr(type(ret), IMPL_PREFIX + name, property(patched)) + + return ret + + @property + def attr___name__(self): + return node_classes.Const( + value=self._instance.parent.name, parent=self._instance + ) + + @property + def attr___doc__(self): + return node_classes.Const( + value=getattr(self._instance.parent.doc_node, "value", None), + parent=self._instance, + ) + + +class AsyncGeneratorModel(GeneratorModel): + def __new__(cls, *args, **kwargs): + # Append the values from the AGeneratorType unto this object. + ret = super().__new__(cls, *args, **kwargs) + astroid_builtins = AstroidManager().builtins_module + generator = astroid_builtins.get("async_generator") + if generator is None: + # Make it backward compatible. + generator = astroid_builtins.get("generator") + + for name, values in generator.locals.items(): + method = values[0] + + def patched(cls, meth=method): + return meth + + setattr(type(ret), IMPL_PREFIX + name, property(patched)) + + return ret + + +class InstanceModel(ObjectModel): + @property + def attr___class__(self): + return self._instance._proxied + + @property + def attr___module__(self): + return node_classes.Const(self._instance.root().qname()) + + @property + def attr___doc__(self): + return node_classes.Const(getattr(self._instance.doc_node, "value", None)) + + @property + def attr___dict__(self): + return _dunder_dict(self._instance, self._instance.instance_attrs) + + +# Exception instances + + +class ExceptionInstanceModel(InstanceModel): + @property + def attr_args(self) -> nodes.Tuple: + return nodes.Tuple(parent=self._instance) + + @property + def attr___traceback__(self): + builtins_ast_module = AstroidManager().builtins_module + traceback_type = builtins_ast_module[types.TracebackType.__name__] + return traceback_type.instantiate_class() + + +class SyntaxErrorInstanceModel(ExceptionInstanceModel): + @property + def attr_text(self): + return node_classes.Const("") + + +class OSErrorInstanceModel(ExceptionInstanceModel): + @property + def attr_filename(self): + return node_classes.Const("") + + @property + def attr_errno(self): + return node_classes.Const(0) + + @property + def attr_strerror(self): + return node_classes.Const("") + + attr_filename2 = attr_filename + + +class ImportErrorInstanceModel(ExceptionInstanceModel): + @property + def attr_name(self): + return node_classes.Const("") + + @property + def attr_path(self): + return node_classes.Const("") + + +class UnicodeDecodeErrorInstanceModel(ExceptionInstanceModel): + @property + def attr_object(self): + return node_classes.Const("") + + +BUILTIN_EXCEPTIONS = { + "builtins.SyntaxError": SyntaxErrorInstanceModel, + "builtins.ImportError": ImportErrorInstanceModel, + "builtins.UnicodeDecodeError": UnicodeDecodeErrorInstanceModel, + # These are all similar to OSError in terms of attributes + "builtins.OSError": OSErrorInstanceModel, + "builtins.BlockingIOError": OSErrorInstanceModel, + "builtins.BrokenPipeError": OSErrorInstanceModel, + "builtins.ChildProcessError": OSErrorInstanceModel, + "builtins.ConnectionAbortedError": OSErrorInstanceModel, + "builtins.ConnectionError": OSErrorInstanceModel, + "builtins.ConnectionRefusedError": OSErrorInstanceModel, + "builtins.ConnectionResetError": OSErrorInstanceModel, + "builtins.FileExistsError": OSErrorInstanceModel, + "builtins.FileNotFoundError": OSErrorInstanceModel, + "builtins.InterruptedError": OSErrorInstanceModel, + "builtins.IsADirectoryError": OSErrorInstanceModel, + "builtins.NotADirectoryError": OSErrorInstanceModel, + "builtins.PermissionError": OSErrorInstanceModel, + "builtins.ProcessLookupError": OSErrorInstanceModel, + "builtins.TimeoutError": OSErrorInstanceModel, +} + + +class DictModel(ObjectModel): + @property + def attr___class__(self): + return self._instance._proxied + + def _generic_dict_attribute(self, obj, name): + """Generate a bound method that can infer the given *obj*.""" + + class DictMethodBoundMethod(astroid.BoundMethod): + def infer_call_result( + self, caller, context: InferenceContext | None = None + ): + yield obj + + meth = next(self._instance._proxied.igetattr(name), None) + 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) + + 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) + + 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) + + obj = objects.DictValues(obj) + return self._generic_dict_attribute(obj, "values") + + +class PropertyModel(ObjectModel): + """Model for a builtin property.""" + + def _init_function(self, name): + function = nodes.FunctionDef(name=name, parent=self._instance) + + args = nodes.Arguments(parent=function) + args.postinit( + args=[], + defaults=[], + kwonlyargs=[], + kw_defaults=[], + annotations=[], + posonlyargs=[], + posonlyargs_annotations=[], + kwonlyargs_annotations=[], + ) + + function.postinit(args=args, body=[]) + return function + + @property + def attr_fget(self): + func = self._instance + + class PropertyFuncAccessor(nodes.FunctionDef): + def infer_call_result( + self, caller=None, context: InferenceContext | None = None + ): + nonlocal func + if caller and len(caller.args) != 1: + raise InferenceError( + "fget() needs a single argument", target=self, context=context + ) + + yield from func.function.infer_call_result( + caller=caller, context=context + ) + + property_accessor = PropertyFuncAccessor(name="fget", parent=self._instance) + property_accessor.postinit(args=func.args, body=func.body) + return property_accessor + + @property + def attr_fset(self): + func = self._instance + + def find_setter(func: Property) -> astroid.FunctionDef | None: + """ + Given a property, find the corresponding setter function and returns it. + + :param func: property for which the setter has to be found + :return: the setter function or None + """ + for target in [ + t for t in func.parent.get_children() if t.name == func.function.name + ]: + for dec_name in target.decoratornames(): + if dec_name.endswith(func.function.name + ".setter"): + return target + return None + + func_setter = find_setter(func) + if not func_setter: + raise InferenceError( + f"Unable to find the setter of property {func.function.name}" + ) + + class PropertyFuncAccessor(nodes.FunctionDef): + def infer_call_result( + self, caller=None, context: InferenceContext | None = None + ): + nonlocal func_setter + if caller and len(caller.args) != 2: + raise InferenceError( + "fset() needs two arguments", target=self, context=context + ) + yield from func_setter.infer_call_result(caller=caller, context=context) + + property_accessor = PropertyFuncAccessor(name="fset", parent=self._instance) + property_accessor.postinit(args=func_setter.args, body=func_setter.body) + return property_accessor + + @property + def attr_setter(self): + return self._init_function("setter") + + @property + def attr_deleter(self): + return self._init_function("deleter") + + @property + def attr_getter(self): + return self._init_function("getter") + + # pylint: enable=import-outside-toplevel diff --git a/venv/lib/python3.10/site-packages/astroid/manager.py b/venv/lib/python3.10/site-packages/astroid/manager.py new file mode 100644 index 00000000..965dd5a5 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/manager.py @@ -0,0 +1,447 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""astroid manager: avoid multiple astroid build of a same module when +possible by providing a class responsible to get astroid representation +from various source and using a cache of built modules) +""" + +from __future__ import annotations + +import collections +import os +import types +import zipimport +from collections.abc import Callable, Iterator, Sequence +from importlib.util import find_spec, module_from_spec +from typing import Any, ClassVar + +from astroid import nodes +from astroid._cache import CACHE_MANAGER +from astroid.const import BRAIN_MODULES_DIRECTORY +from astroid.context import InferenceContext, _invalidate_cache +from astroid.exceptions import AstroidBuildingError, AstroidImportError +from astroid.interpreter._import import spec, util +from astroid.modutils import ( + NoSourceFile, + _cache_normalize_path_, + file_info_from_modpath, + get_source_file, + is_module_name_part_of_extension_package_whitelist, + is_python_source, + is_stdlib_module, + load_module_from_name, + modpath_from_file, +) +from astroid.transforms import TransformVisitor +from astroid.typing import AstroidManagerBrain, InferenceResult + +ZIP_IMPORT_EXTS = (".zip", ".egg", ".whl", ".pyz", ".pyzw") + + +def safe_repr(obj: Any) -> str: + try: + return repr(obj) + except Exception: # pylint: disable=broad-except + return "???" + + +class AstroidManager: + """Responsible to build astroid from files or modules. + + Use the Borg (singleton) pattern. + """ + + name = "astroid loader" + brain: AstroidManagerBrain = { + "astroid_cache": {}, + "_mod_file_cache": {}, + "_failed_import_hooks": [], + "always_load_extensions": False, + "optimize_ast": False, + "extension_package_whitelist": set(), + "_transform": TransformVisitor(), + } + max_inferable_values: ClassVar[int] = 100 + + def __init__(self) -> None: + # NOTE: cache entries are added by the [re]builder + self.astroid_cache = AstroidManager.brain["astroid_cache"] + self._mod_file_cache = AstroidManager.brain["_mod_file_cache"] + self._failed_import_hooks = AstroidManager.brain["_failed_import_hooks"] + self.always_load_extensions = AstroidManager.brain["always_load_extensions"] + self.optimize_ast = AstroidManager.brain["optimize_ast"] + self.extension_package_whitelist = AstroidManager.brain[ + "extension_package_whitelist" + ] + self._transform = AstroidManager.brain["_transform"] + + @property + def register_transform(self): + # This and unregister_transform below are exported for convenience + return self._transform.register_transform + + @property + def unregister_transform(self): + return self._transform.unregister_transform + + @property + def builtins_module(self) -> nodes.Module: + return self.astroid_cache["builtins"] + + def visit_transforms(self, node: nodes.NodeNG) -> InferenceResult: + """Visit the transforms and apply them to the given *node*.""" + return self._transform.visit(node) + + def ast_from_file( + self, + filepath: str, + modname: str | None = None, + fallback: bool = True, + source: bool = False, + ) -> nodes.Module: + """Given a module name, return the astroid object.""" + try: + filepath = get_source_file(filepath, include_no_ext=True) + source = True + except NoSourceFile: + pass + if modname is None: + try: + modname = ".".join(modpath_from_file(filepath)) + except ImportError: + modname = filepath + if ( + modname in self.astroid_cache + and self.astroid_cache[modname].file == filepath + ): + return self.astroid_cache[modname] + if source: + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + return AstroidBuilder(self).file_build(filepath, modname) + if fallback and modname: + return self.ast_from_module_name(modname) + raise AstroidBuildingError("Unable to build an AST for {path}.", path=filepath) + + def ast_from_string( + self, data: str, modname: str = "", filepath: str | None = None + ) -> nodes.Module: + """Given some source code as a string, return its corresponding astroid + object. + """ + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + return AstroidBuilder(self).string_build(data, modname, filepath) + + def _build_stub_module(self, modname: str) -> nodes.Module: + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + return AstroidBuilder(self).string_build("", modname) + + def _build_namespace_module( + self, modname: str, path: Sequence[str] + ) -> nodes.Module: + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import build_namespace_package_module + + return build_namespace_package_module(modname, path) + + def _can_load_extension(self, modname: str) -> bool: + if self.always_load_extensions: + return True + if is_stdlib_module(modname): + return True + return is_module_name_part_of_extension_package_whitelist( + modname, self.extension_package_whitelist + ) + + def ast_from_module_name( # noqa: C901 + self, + modname: str | None, + context_file: str | None = None, + use_cache: bool = True, + ) -> nodes.Module: + """Given a module name, return the astroid object.""" + if modname is None: + raise AstroidBuildingError("No module name given.") + # Sometimes we don't want to use the cache. For example, when we're + # importing a module with the same name as the file that is importing + # we want to fallback on the import system to make sure we get the correct + # module. + if modname in self.astroid_cache and use_cache: + return self.astroid_cache[modname] + if modname == "__main__": + return self._build_stub_module(modname) + if context_file: + old_cwd = os.getcwd() + os.chdir(os.path.dirname(context_file)) + try: + found_spec = self.file_from_module_name(modname, context_file) + if found_spec.type == spec.ModuleType.PY_ZIPMODULE: + module = self.zip_import_data(found_spec.location) + if module is not None: + return module + + elif found_spec.type in ( + spec.ModuleType.C_BUILTIN, + spec.ModuleType.C_EXTENSION, + ): + if ( + found_spec.type == spec.ModuleType.C_EXTENSION + and not self._can_load_extension(modname) + ): + return self._build_stub_module(modname) + try: + named_module = load_module_from_name(modname) + except Exception as e: + raise AstroidImportError( + "Loading {modname} failed with:\n{error}", + modname=modname, + path=found_spec.location, + ) from e + return self.ast_from_module(named_module, modname) + + elif found_spec.type == spec.ModuleType.PY_COMPILED: + raise AstroidImportError( + "Unable to load compiled module {modname}.", + modname=modname, + path=found_spec.location, + ) + + elif found_spec.type == spec.ModuleType.PY_NAMESPACE: + return self._build_namespace_module( + modname, found_spec.submodule_search_locations or [] + ) + elif found_spec.type == spec.ModuleType.PY_FROZEN: + if found_spec.location is None: + return self._build_stub_module(modname) + # For stdlib frozen modules we can determine the location and + # can therefore create a module from the source file + return self.ast_from_file(found_spec.location, modname, fallback=False) + + if found_spec.location is None: + raise AstroidImportError( + "Can't find a file for module {modname}.", modname=modname + ) + + return self.ast_from_file(found_spec.location, modname, fallback=False) + except AstroidBuildingError as e: + for hook in self._failed_import_hooks: + try: + return hook(modname) + except AstroidBuildingError: + pass + raise e + finally: + if context_file: + os.chdir(old_cwd) + + def zip_import_data(self, filepath: str) -> nodes.Module | None: + if zipimport is None: + return None + + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + builder = AstroidBuilder(self) + for ext in ZIP_IMPORT_EXTS: + try: + eggpath, resource = filepath.rsplit(ext + os.path.sep, 1) + except ValueError: + continue + try: + # pylint: disable-next=no-member + importer = zipimport.zipimporter(eggpath + ext) + zmodname = resource.replace(os.path.sep, ".") + if importer.is_package(resource): + zmodname = zmodname + ".__init__" + module = builder.string_build( + importer.get_source(resource), zmodname, filepath + ) + return module + except Exception: # pylint: disable=broad-except + continue + return None + + def file_from_module_name( + self, modname: str, contextfile: str | None + ) -> spec.ModuleSpec: + try: + value = self._mod_file_cache[(modname, contextfile)] + except KeyError: + try: + value = file_info_from_modpath( + modname.split("."), context_file=contextfile + ) + except ImportError as e: + # pylint: disable-next=redefined-variable-type + value = AstroidImportError( + "Failed to import module {modname} with error:\n{error}.", + modname=modname, + # we remove the traceback here to save on memory usage (since these exceptions are cached) + error=e.with_traceback(None), + ) + self._mod_file_cache[(modname, contextfile)] = value + if isinstance(value, AstroidBuildingError): + # we remove the traceback here to save on memory usage (since these exceptions are cached) + raise value.with_traceback(None) # pylint: disable=no-member + return value + + def ast_from_module( + self, module: types.ModuleType, modname: str | None = None + ) -> nodes.Module: + """Given an imported module, return the astroid object.""" + modname = modname or module.__name__ + if modname in self.astroid_cache: + return self.astroid_cache[modname] + try: + # some builtin modules don't have __file__ attribute + filepath = module.__file__ + if is_python_source(filepath): + # Type is checked in is_python_source + return self.ast_from_file(filepath, modname) # type: ignore[arg-type] + except AttributeError: + pass + + # pylint: disable=import-outside-toplevel; circular import + from astroid.builder import AstroidBuilder + + return AstroidBuilder(self).module_build(module, modname) + + def ast_from_class(self, klass: type, modname: str | None = None) -> nodes.ClassDef: + """Get astroid for the given class.""" + if modname is None: + try: + modname = klass.__module__ + except AttributeError as exc: + raise AstroidBuildingError( + "Unable to get module for class {class_name}.", + cls=klass, + class_repr=safe_repr(klass), + modname=modname, + ) from exc + modastroid = self.ast_from_module_name(modname) + ret = modastroid.getattr(klass.__name__)[0] + assert isinstance(ret, nodes.ClassDef) + return ret + + def infer_ast_from_something( + self, obj: object, context: InferenceContext | None = None + ) -> Iterator[InferenceResult]: + """Infer astroid for the given class.""" + if hasattr(obj, "__class__") and not isinstance(obj, type): + klass = obj.__class__ + elif isinstance(obj, type): + klass = obj + else: + raise AstroidBuildingError( # pragma: no cover + "Unable to get type for {class_repr}.", + cls=None, + class_repr=safe_repr(obj), + ) + try: + modname = klass.__module__ + except AttributeError as exc: + raise AstroidBuildingError( + "Unable to get module for {class_repr}.", + cls=klass, + class_repr=safe_repr(klass), + ) from exc + except Exception as exc: + raise AstroidImportError( + "Unexpected error while retrieving module for {class_repr}:\n" + "{error}", + cls=klass, + class_repr=safe_repr(klass), + ) from exc + try: + name = klass.__name__ + except AttributeError as exc: + raise AstroidBuildingError( + "Unable to get name for {class_repr}:\n", + cls=klass, + class_repr=safe_repr(klass), + ) from exc + except Exception as exc: + raise AstroidImportError( + "Unexpected error while retrieving name for {class_repr}:\n{error}", + cls=klass, + class_repr=safe_repr(klass), + ) from exc + # take care, on living object __module__ is regularly wrong :( + modastroid = self.ast_from_module_name(modname) + if klass is obj: + for inferred in modastroid.igetattr(name, context): + yield inferred + else: + for inferred in modastroid.igetattr(name, context): + yield inferred.instantiate_class() + + def register_failed_import_hook(self, hook: Callable[[str], nodes.Module]) -> None: + """Registers a hook to resolve imports that cannot be found otherwise. + + `hook` must be a function that accepts a single argument `modname` which + contains the name of the module or package that could not be imported. + If `hook` can resolve the import, must return a node of type `astroid.Module`, + otherwise, it must raise `AstroidBuildingError`. + """ + self._failed_import_hooks.append(hook) + + def cache_module(self, module: nodes.Module) -> None: + """Cache a module if no module with the same name is known yet.""" + self.astroid_cache.setdefault(module.name, module) + + def bootstrap(self) -> None: + """Bootstrap the required AST modules needed for the manager to work. + + The bootstrap usually involves building the AST for the builtins + module, which is required by the rest of astroid to work correctly. + """ + from astroid import raw_building # pylint: disable=import-outside-toplevel + + raw_building._astroid_bootstrapping() + + def clear_cache(self) -> None: + """Clear the underlying caches, bootstrap the builtins module and + re-register transforms. + """ + # import here because of cyclic imports + # pylint: disable=import-outside-toplevel + from astroid.inference_tip import clear_inference_tip_cache + from astroid.interpreter.objectmodel import ObjectModel + from astroid.nodes.node_classes import LookupMixIn + from astroid.nodes.scoped_nodes import ClassDef + + clear_inference_tip_cache() + _invalidate_cache() # inference context cache + + self.astroid_cache.clear() + # NB: not a new TransformVisitor() + AstroidManager.brain["_transform"].transforms = collections.defaultdict(list) + + CACHE_MANAGER.clear_all_caches() + + for lru_cache in ( + LookupMixIn.lookup, + _cache_normalize_path_, + util.is_namespace, + ObjectModel.attributes, + ClassDef._metaclass_lookup_attribute, + ): + lru_cache.cache_clear() # type: ignore[attr-defined] + + self.bootstrap() + + # Reload brain plugins. During initialisation this is done in astroid.__init__.py + for module in BRAIN_MODULES_DIRECTORY.iterdir(): + if module.suffix == ".py": + module_spec = find_spec(f"astroid.brain.{module.stem}") + assert module_spec + module_object = module_from_spec(module_spec) + assert module_spec.loader + module_spec.loader.exec_module(module_object) diff --git a/venv/lib/python3.10/site-packages/astroid/mixins.py b/venv/lib/python3.10/site-packages/astroid/mixins.py new file mode 100644 index 00000000..942e824a --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/mixins.py @@ -0,0 +1,31 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""This module contains some mixins for the different nodes.""" + +import warnings + +from astroid.nodes._base_nodes import AssignTypeNode as AssignTypeMixin +from astroid.nodes._base_nodes import FilterStmtsBaseNode as FilterStmtsMixin +from astroid.nodes._base_nodes import ImportNode as ImportFromMixin +from astroid.nodes._base_nodes import MultiLineBlockNode as MultiLineBlockMixin +from astroid.nodes._base_nodes import MultiLineWithElseBlockNode as BlockRangeMixIn +from astroid.nodes._base_nodes import NoChildrenNode as NoChildrenMixin +from astroid.nodes._base_nodes import ParentAssignNode as ParentAssignTypeMixin + +__all__ = ( + "AssignTypeMixin", + "BlockRangeMixIn", + "FilterStmtsMixin", + "ImportFromMixin", + "MultiLineBlockMixin", + "NoChildrenMixin", + "ParentAssignTypeMixin", +) + +warnings.warn( + "The 'astroid.mixins' module is deprecated and will become private in astroid 3.0.0", + DeprecationWarning, + stacklevel=2, +) diff --git a/venv/lib/python3.10/site-packages/astroid/modutils.py b/venv/lib/python3.10/site-packages/astroid/modutils.py new file mode 100644 index 00000000..f05b5f89 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/modutils.py @@ -0,0 +1,701 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Python modules manipulation utility functions. + +:type PY_SOURCE_EXTS: tuple(str) +:var PY_SOURCE_EXTS: list of possible python source file extension + +:type STD_LIB_DIRS: set of str +:var STD_LIB_DIRS: directories where standard modules are located + +:type BUILTIN_MODULES: dict +:var BUILTIN_MODULES: dictionary with builtin module names has key +""" + +from __future__ import annotations + +import importlib +import importlib.machinery +import importlib.util +import io +import itertools +import logging +import os +import sys +import sysconfig +import types +import warnings +from collections.abc import Callable, Iterable, Sequence +from contextlib import redirect_stderr, redirect_stdout +from functools import lru_cache +from pathlib import Path + +from astroid.const import IS_JYTHON, IS_PYPY, PY310_PLUS +from astroid.interpreter._import import spec, util + +if PY310_PLUS: + from sys import stdlib_module_names +else: + from astroid._backport_stdlib_names import stdlib_module_names + +logger = logging.getLogger(__name__) + + +if sys.platform.startswith("win"): + PY_SOURCE_EXTS = ("py", "pyw") + PY_COMPILED_EXTS = ("dll", "pyd") +else: + PY_SOURCE_EXTS = ("py",) + PY_COMPILED_EXTS = ("so",) + + +# TODO: Adding `platstdlib` is a fix for a workaround in virtualenv. At some point we should +# revisit whether this is still necessary. See https://github.com/PyCQA/astroid/pull/1323. +STD_LIB_DIRS = {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")} + +if os.name == "nt": + STD_LIB_DIRS.add(os.path.join(sys.prefix, "dlls")) + try: + # real_prefix is defined when running inside virtual environments, + # created with the **virtualenv** library. + # Deprecated in virtualenv==16.7.9 + # See: https://github.com/pypa/virtualenv/issues/1622 + STD_LIB_DIRS.add(os.path.join(sys.real_prefix, "dlls")) # type: ignore[attr-defined] + except AttributeError: + # sys.base_exec_prefix is always defined, but in a virtual environment + # created with the stdlib **venv** module, it points to the original + # installation, if the virtual env is activated. + try: + STD_LIB_DIRS.add(os.path.join(sys.base_exec_prefix, "dlls")) + except AttributeError: + pass + +if IS_PYPY and sys.version_info < (3, 8): + # PyPy stores the stdlib in two places: sys.prefix/lib_pypy and sys.prefix/lib-python/3 + # sysconfig.get_path on PyPy returns the first, but without an underscore so we patch this manually. + # Beginning with 3.8 the stdlib is only stored in: sys.prefix/pypy{py_version_short} + STD_LIB_DIRS.add(str(Path(sysconfig.get_path("stdlib")).parent / "lib_pypy")) + STD_LIB_DIRS.add(str(Path(sysconfig.get_path("stdlib")).parent / "lib-python/3")) + + # TODO: This is a fix for a workaround in virtualenv. At some point we should revisit + # whether this is still necessary. See https://github.com/PyCQA/astroid/pull/1324. + STD_LIB_DIRS.add(str(Path(sysconfig.get_path("platstdlib")).parent / "lib_pypy")) + STD_LIB_DIRS.add( + str(Path(sysconfig.get_path("platstdlib")).parent / "lib-python/3") + ) + +if os.name == "posix": + # Need the real prefix if we're in a virtualenv, otherwise + # the usual one will do. + # Deprecated in virtualenv==16.7.9 + # See: https://github.com/pypa/virtualenv/issues/1622 + try: + prefix: str = sys.real_prefix # type: ignore[attr-defined] + except AttributeError: + prefix = sys.prefix + + def _posix_path(path: str) -> str: + base_python = "python%d.%d" % sys.version_info[:2] + return os.path.join(prefix, path, base_python) + + STD_LIB_DIRS.add(_posix_path("lib")) + if sys.maxsize > 2**32: + # This tries to fix a problem with /usr/lib64 builds, + # where systems are running both 32-bit and 64-bit code + # on the same machine, which reflects into the places where + # standard library could be found. More details can be found + # here http://bugs.python.org/issue1294959. + # An easy reproducing case would be + # https://github.com/PyCQA/pylint/issues/712#issuecomment-163178753 + STD_LIB_DIRS.add(_posix_path("lib64")) + +EXT_LIB_DIRS = {sysconfig.get_path("purelib"), sysconfig.get_path("platlib")} +BUILTIN_MODULES = dict.fromkeys(sys.builtin_module_names, True) + + +class NoSourceFile(Exception): + """Exception raised when we are not able to get a python + source file for a precompiled file. + """ + + +def _normalize_path(path: str) -> str: + """Resolve symlinks in path and convert to absolute path. + + Note that environment variables and ~ in the path need to be expanded in + advance. + + This can be cached by using _cache_normalize_path. + """ + return os.path.normcase(os.path.realpath(path)) + + +def _path_from_filename(filename: str, is_jython: bool = IS_JYTHON) -> str: + if not is_jython: + return filename + head, has_pyclass, _ = filename.partition("$py.class") + if has_pyclass: + return head + ".py" + return filename + + +def _handle_blacklist( + blacklist: Sequence[str], dirnames: list[str], filenames: list[str] +) -> None: + """Remove files/directories in the black list. + + dirnames/filenames are usually from os.walk + """ + for norecurs in blacklist: + if norecurs in dirnames: + dirnames.remove(norecurs) + elif norecurs in filenames: + filenames.remove(norecurs) + + +@lru_cache() +def _cache_normalize_path_(path: str) -> str: + return _normalize_path(path) + + +def _cache_normalize_path(path: str) -> str: + """Normalize path with caching.""" + # _module_file calls abspath on every path in sys.path every time it's + # called; on a larger codebase this easily adds up to half a second just + # assembling path components. This cache alleviates that. + if not path: # don't cache result for '' + return _normalize_path(path) + return _cache_normalize_path_(path) + + +def load_module_from_name(dotted_name: str) -> types.ModuleType: + """Load a Python module from its name. + + :type dotted_name: str + :param dotted_name: python name of a module or package + + :raise ImportError: if the module or package is not found + + :rtype: module + :return: the loaded module + """ + try: + return sys.modules[dotted_name] + except KeyError: + pass + + # Capture and log anything emitted during import to avoid + # contaminating JSON reports in pylint + with redirect_stderr(io.StringIO()) as stderr, redirect_stdout( + io.StringIO() + ) as stdout: + module = importlib.import_module(dotted_name) + + stderr_value = stderr.getvalue() + if stderr_value: + logger.error( + "Captured stderr while importing %s:\n%s", dotted_name, stderr_value + ) + stdout_value = stdout.getvalue() + if stdout_value: + logger.info( + "Captured stdout while importing %s:\n%s", dotted_name, stdout_value + ) + + return module + + +def load_module_from_modpath(parts: Sequence[str]) -> types.ModuleType: + """Load a python module from its split name. + + :param parts: + python name of a module or package split on '.' + + :raise ImportError: if the module or package is not found + + :return: the loaded module + """ + return load_module_from_name(".".join(parts)) + + +def load_module_from_file(filepath: str) -> types.ModuleType: + """Load a Python module from it's path. + + :type filepath: str + :param filepath: path to the python module or package + + :raise ImportError: if the module or package is not found + + :rtype: module + :return: the loaded module + """ + modpath = modpath_from_file(filepath) + return load_module_from_modpath(modpath) + + +def check_modpath_has_init(path: str, mod_path: list[str]) -> bool: + """Check there are some __init__.py all along the way.""" + modpath: list[str] = [] + for part in mod_path: + modpath.append(part) + path = os.path.join(path, part) + if not _has_init(path): + old_namespace = util.is_namespace(".".join(modpath)) + if not old_namespace: + return False + return True + + +def _get_relative_base_path(filename: str, path_to_check: str) -> list[str] | None: + """Extracts the relative mod path of the file to import from. + + Check if a file is within the passed in path and if so, returns the + relative mod path from the one passed in. + + If the filename is no in path_to_check, returns None + + Note this function will look for both abs and realpath of the file, + this allows to find the relative base path even if the file is a + symlink of a file in the passed in path + + Examples: + _get_relative_base_path("/a/b/c/d.py", "/a/b") -> ["c","d"] + _get_relative_base_path("/a/b/c/d.py", "/dev") -> None + """ + importable_path = None + path_to_check = os.path.normcase(path_to_check) + abs_filename = os.path.abspath(filename) + if os.path.normcase(abs_filename).startswith(path_to_check): + importable_path = abs_filename + + real_filename = os.path.realpath(filename) + if os.path.normcase(real_filename).startswith(path_to_check): + importable_path = real_filename + + # if "var" in path_to_check: + # breakpoint() + + if importable_path: + base_path = os.path.splitext(importable_path)[0] + relative_base_path = base_path[len(path_to_check) :] + return [pkg for pkg in relative_base_path.split(os.sep) if pkg] + + return None + + +def modpath_from_file_with_callback( + filename: str, + path: Sequence[str] | None = None, + is_package_cb: Callable[[str, list[str]], bool] | None = None, +) -> list[str]: + filename = os.path.expanduser(_path_from_filename(filename)) + paths_to_check = sys.path.copy() + if path: + paths_to_check += path + for pathname in itertools.chain( + paths_to_check, map(_cache_normalize_path, paths_to_check) + ): + if not pathname: + continue + modpath = _get_relative_base_path(filename, pathname) + if not modpath: + continue + assert is_package_cb is not None + if is_package_cb(pathname, modpath[:-1]): + return modpath + + raise ImportError( + "Unable to find module for {} in {}".format(filename, ", \n".join(sys.path)) + ) + + +def modpath_from_file(filename: str, path: Sequence[str] | None = None) -> list[str]: + """Get the corresponding split module's name from a filename. + + This function will return the name of a module or package split on `.`. + + :type filename: str + :param filename: file's path for which we want the module's name + + :type Optional[List[str]] path: + Optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :raise ImportError: + if the corresponding module's name has not been found + + :rtype: list(str) + :return: the corresponding split module's name + """ + return modpath_from_file_with_callback(filename, path, check_modpath_has_init) + + +def file_from_modpath( + modpath: list[str], + path: Sequence[str] | None = None, + context_file: str | None = None, +) -> str | None: + return file_info_from_modpath(modpath, path, context_file).location + + +def file_info_from_modpath( + modpath: list[str], + path: Sequence[str] | None = None, + context_file: str | None = None, +) -> spec.ModuleSpec: + """Given a mod path (i.e. split module / package name), return the + corresponding file. + + Giving priority to source file over precompiled file if it exists. + + :param modpath: + split module's name (i.e name of a module or package split + on '.') + (this means explicit relative imports that start with dots have + empty strings in this list!) + + :param path: + optional list of path where the module or package should be + searched (use sys.path if nothing or None is given) + + :param context_file: + context file to consider, necessary if the identifier has been + introduced using a relative import unresolvable in the actual + context (i.e. modutils) + + :raise ImportError: if there is no such module in the directory + + :return: + the path to the module's file or None if it's an integrated + builtin module such as 'sys' + """ + if context_file is not None: + context: str | None = os.path.dirname(context_file) + else: + context = context_file + if modpath[0] == "xml": + # handle _xmlplus + try: + return _spec_from_modpath(["_xmlplus"] + modpath[1:], path, context) + except ImportError: + return _spec_from_modpath(modpath, path, context) + elif modpath == ["os", "path"]: + # FIXME: currently ignoring search_path... + return spec.ModuleSpec( + name="os.path", + location=os.path.__file__, + type=spec.ModuleType.PY_SOURCE, + ) + return _spec_from_modpath(modpath, path, context) + + +def get_module_part(dotted_name: str, context_file: str | None = None) -> str: + """Given a dotted name return the module part of the name : + + >>> get_module_part('astroid.as_string.dump') + 'astroid.as_string' + + :param dotted_name: full name of the identifier we are interested in + + :param context_file: + context file to consider, necessary if the identifier has been + introduced using a relative import unresolvable in the actual + context (i.e. modutils) + + :raise ImportError: if there is no such module in the directory + + :return: + the module part of the name or None if we have not been able at + all to import the given name + + XXX: deprecated, since it doesn't handle package precedence over module + (see #10066) + """ + # os.path trick + if dotted_name.startswith("os.path"): + return "os.path" + parts = dotted_name.split(".") + if context_file is not None: + # first check for builtin module which won't be considered latter + # in that case (path != None) + if parts[0] in BUILTIN_MODULES: + if len(parts) > 2: + raise ImportError(dotted_name) + return parts[0] + # don't use += or insert, we want a new list to be created ! + path: list[str] | None = None + starti = 0 + if parts[0] == "": + assert ( + context_file is not None + ), "explicit relative import, but no context_file?" + path = [] # prevent resolving the import non-relatively + starti = 1 + while parts[starti] == "": # for all further dots: change context + starti += 1 + assert ( + context_file is not None + ), "explicit relative import, but no context_file?" + context_file = os.path.dirname(context_file) + for i in range(starti, len(parts)): + try: + file_from_modpath( + parts[starti : i + 1], path=path, context_file=context_file + ) + except ImportError: + if i < max(1, len(parts) - 2): + raise + return ".".join(parts[:i]) + return dotted_name + + +def get_module_files( + src_directory: str, blacklist: Sequence[str], list_all: bool = False +) -> list[str]: + """Given a package directory return a list of all available python + module's files in the package and its subpackages. + + :param src_directory: + path of the directory corresponding to the package + + :param blacklist: iterable + list of files or directories to ignore. + + :param list_all: + get files from all paths, including ones without __init__.py + + :return: + the list of all available python module's files in the package and + its subpackages + """ + files: list[str] = [] + for directory, dirnames, filenames in os.walk(src_directory): + if directory in blacklist: + continue + _handle_blacklist(blacklist, dirnames, filenames) + # check for __init__.py + if not list_all and "__init__.py" 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: str, include_no_ext: bool = False) -> str: + """Given a python module's file name return the matching source file + name (the filename will be returned identically if it's already an. + + absolute path to a python source file...) + + :param filename: python module's file name + + :raise NoSourceFile: if no source file exists on the file system + + :return: the absolute path of the source file if it exists + """ + filename = os.path.abspath(_path_from_filename(filename)) + base, orig_ext = os.path.splitext(filename) + for ext in PY_SOURCE_EXTS: + source_path = f"{base}.{ext}" + if os.path.exists(source_path): + return source_path + if include_no_ext and not orig_ext and os.path.exists(base): + return base + raise NoSourceFile(filename) + + +def is_python_source(filename: str | None) -> bool: + """Return: True if the filename is a python source file.""" + if not filename: + return False + return os.path.splitext(filename)[1][1:] in PY_SOURCE_EXTS + + +def is_stdlib_module(modname: str) -> bool: + """Return: True if the modname is in the standard library""" + return modname.split(".")[0] in stdlib_module_names + + +def module_in_path(modname: str, path: str | Iterable[str]) -> bool: + """Try to determine if a module is imported from one of the specified paths + + :param modname: name of the module + + :param path: paths to consider + + :return: + true if the module: + - is located on the path listed in one of the directory in `paths` + """ + + modname = modname.split(".")[0] + try: + filename = file_from_modpath([modname]) + except ImportError: + # Import failed, we can't check path if we don't know it + return False + + if filename is None: + # No filename likely means it's compiled in, or potentially a namespace + return False + filename = _normalize_path(filename) + + if isinstance(path, str): + return filename.startswith(_cache_normalize_path(path)) + + return any(filename.startswith(_cache_normalize_path(entry)) for entry in path) + + +def is_standard_module(modname: str, std_path: Iterable[str] | None = None) -> bool: + """Try to guess if a module is a standard python module (by default, + see `std_path` parameter's description). + + :param modname: name of the module we are interested in + + :param std_path: list of path considered has standard + + :return: + true if the module: + - is located on the path listed in one of the directory in `std_path` + - is a built-in module + """ + warnings.warn( + "is_standard_module() is deprecated. Use, is_stdlib_module() or module_in_path() instead", + DeprecationWarning, + stacklevel=2, + ) + + modname = modname.split(".")[0] + try: + filename = file_from_modpath([modname]) + except ImportError: + # import failed, i'm probably not so wrong by supposing it's + # not standard... + return False + # modules which are not living in a file are considered standard + # (sys and __builtin__ for instance) + if filename is None: + # we assume there are no namespaces in stdlib + return not util.is_namespace(modname) + filename = _normalize_path(filename) + for path in EXT_LIB_DIRS: + if filename.startswith(_cache_normalize_path(path)): + return False + if std_path is None: + std_path = STD_LIB_DIRS + + return any(filename.startswith(_cache_normalize_path(path)) for path in std_path) + + +def is_relative(modname: str, from_file: str) -> bool: + """Return true if the given module name is relative to the given + file name. + + :param modname: name of the module we are interested in + + :param from_file: + path of the module from which modname has been imported + + :return: + true if the module has been imported relatively to `from_file` + """ + if not os.path.isdir(from_file): + from_file = os.path.dirname(from_file) + if from_file in sys.path: + return False + return bool( + importlib.machinery.PathFinder.find_spec( + modname.split(".", maxsplit=1)[0], [from_file] + ) + ) + + +# internal only functions ##################################################### + + +def _spec_from_modpath( + modpath: list[str], + path: Sequence[str] | None = None, + context: str | None = None, +) -> spec.ModuleSpec: + """Given a mod path (i.e. split module / package name), return the + corresponding spec. + + this function is used internally, see `file_from_modpath`'s + documentation for more information + """ + assert modpath + location = None + if context is not None: + try: + found_spec = spec.find_spec(modpath, [context]) + location = found_spec.location + except ImportError: + found_spec = spec.find_spec(modpath, path) + location = found_spec.location + else: + found_spec = spec.find_spec(modpath, path) + if found_spec.type == spec.ModuleType.PY_COMPILED: + try: + assert found_spec.location is not None + location = get_source_file(found_spec.location) + return found_spec._replace( + location=location, type=spec.ModuleType.PY_SOURCE + ) + except NoSourceFile: + return found_spec._replace(location=location) + elif found_spec.type == spec.ModuleType.C_BUILTIN: + # integrated builtin module + return found_spec._replace(location=None) + elif found_spec.type == spec.ModuleType.PKG_DIRECTORY: + assert found_spec.location is not None + location = _has_init(found_spec.location) + return found_spec._replace(location=location, type=spec.ModuleType.PY_SOURCE) + return found_spec + + +def _is_python_file(filename: str) -> bool: + """Return true if the given filename should be considered as a python file. + + .pyc and .pyo are ignored + """ + return filename.endswith((".py", ".so", ".pyd", ".pyw")) + + +def _has_init(directory: str) -> str | None: + """If the given directory has a valid __init__ file, return its path, + else return None. + """ + mod_or_pack = os.path.join(directory, "__init__") + for ext in PY_SOURCE_EXTS + ("pyc", "pyo"): + if os.path.exists(mod_or_pack + "." + ext): + return mod_or_pack + "." + ext + return None + + +def is_namespace(specobj: spec.ModuleSpec) -> bool: + return specobj.type == spec.ModuleType.PY_NAMESPACE + + +def is_directory(specobj: spec.ModuleSpec) -> bool: + return specobj.type == spec.ModuleType.PKG_DIRECTORY + + +def is_module_name_part_of_extension_package_whitelist( + module_name: str, package_whitelist: set[str] +) -> bool: + """ + Returns True if one part of the module name is in the package whitelist. + + >>> is_module_name_part_of_extension_package_whitelist('numpy.core.umath', {'numpy'}) + True + """ + parts = module_name.split(".") + return any( + ".".join(parts[:x]) in package_whitelist for x in range(1, len(parts) + 1) + ) diff --git a/venv/lib/python3.10/site-packages/astroid/node_classes.py b/venv/lib/python3.10/site-packages/astroid/node_classes.py new file mode 100644 index 00000000..9ea1e8d2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/node_classes.py @@ -0,0 +1,99 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +# pylint: disable=unused-import + +import warnings + +from astroid.nodes.node_classes import ( # pylint: disable=redefined-builtin (Ellipsis) + CONST_CLS, + AnnAssign, + Arguments, + Assert, + Assign, + AssignAttr, + AssignName, + AsyncFor, + AsyncWith, + Attribute, + AugAssign, + Await, + BaseContainer, + BinOp, + BoolOp, + Break, + Call, + Compare, + Comprehension, + Const, + Continue, + Decorators, + DelAttr, + Delete, + DelName, + Dict, + DictUnpack, + Ellipsis, + EmptyNode, + EvaluatedObject, + ExceptHandler, + Expr, + ExtSlice, + For, + FormattedValue, + Global, + If, + IfExp, + Import, + ImportFrom, + Index, + JoinedStr, + Keyword, + List, + LookupMixIn, + Match, + MatchAs, + MatchCase, + MatchClass, + MatchMapping, + MatchOr, + MatchSequence, + MatchSingleton, + MatchStar, + MatchValue, + Name, + NamedExpr, + NodeNG, + Nonlocal, + Pass, + Pattern, + Raise, + Return, + Set, + Slice, + Starred, + Subscript, + TryExcept, + TryFinally, + Tuple, + UnaryOp, + Unknown, + While, + With, + Yield, + YieldFrom, + are_exclusive, + const_factory, + unpack_infer, +) + +# We cannot create a __all__ here because it would create a circular import +# Please remove astroid/scoped_nodes.py|astroid/node_classes.py in autoflake +# exclude when removing this file. +warnings.warn( + "The 'astroid.node_classes' module is deprecated and will be replaced by " + "'astroid.nodes' in astroid 3.0.0", + DeprecationWarning, + stacklevel=2, +) diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/__init__.py b/venv/lib/python3.10/site-packages/astroid/nodes/__init__.py new file mode 100644 index 00000000..b527ff7c --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/__init__.py @@ -0,0 +1,302 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Every available node class. + +.. seealso:: + :doc:`ast documentation ` + +All nodes inherit from :class:`~astroid.nodes.node_classes.NodeNG`. +""" + +# Nodes not present in the builtin ast module: DictUnpack, Unknown, and EvaluatedObject. + +# This is the only node we re-export from the private _base_nodes module. This +# is because it was originally part of the public API and hasn't been deprecated. +from astroid.nodes._base_nodes import Statement +from astroid.nodes.node_classes import ( # pylint: disable=redefined-builtin (Ellipsis) + CONST_CLS, + AnnAssign, + Arguments, + Assert, + Assign, + AssignAttr, + AssignName, + AsyncFor, + AsyncWith, + Attribute, + AugAssign, + Await, + BaseContainer, + BinOp, + BoolOp, + Break, + Call, + Compare, + Comprehension, + Const, + Continue, + Decorators, + DelAttr, + Delete, + DelName, + Dict, + DictUnpack, + Ellipsis, + EmptyNode, + EvaluatedObject, + ExceptHandler, + Expr, + ExtSlice, + For, + FormattedValue, + Global, + If, + IfExp, + Import, + ImportFrom, + Index, + JoinedStr, + Keyword, + List, + Match, + MatchAs, + MatchCase, + MatchClass, + MatchMapping, + MatchOr, + MatchSequence, + MatchSingleton, + MatchStar, + MatchValue, + Name, + NamedExpr, + NodeNG, + Nonlocal, + Pass, + Pattern, + Raise, + Return, + Set, + Slice, + Starred, + Subscript, + TryExcept, + TryFinally, + TryStar, + Tuple, + UnaryOp, + Unknown, + While, + With, + Yield, + YieldFrom, + are_exclusive, + const_factory, + unpack_infer, +) +from astroid.nodes.scoped_nodes import ( + AsyncFunctionDef, + ClassDef, + ComprehensionScope, + DictComp, + FunctionDef, + GeneratorExp, + Lambda, + ListComp, + LocalsDictNodeNG, + Module, + SetComp, + builtin_lookup, + function_to_method, + get_wrapping_class, +) +from astroid.nodes.utils import Position + +_BaseContainer = BaseContainer # TODO Remove for astroid 3.0 + +ALL_NODE_CLASSES = ( + _BaseContainer, + BaseContainer, + AnnAssign, + Arguments, + Assert, + Assign, + AssignAttr, + AssignName, + AsyncFor, + AsyncFunctionDef, + AsyncWith, + Attribute, + AugAssign, + Await, + BinOp, + BoolOp, + Break, + Call, + ClassDef, + Compare, + Comprehension, + ComprehensionScope, + Const, + const_factory, + Continue, + Decorators, + DelAttr, + Delete, + DelName, + Dict, + DictComp, + DictUnpack, + Ellipsis, + EmptyNode, + EvaluatedObject, + ExceptHandler, + Expr, + ExtSlice, + For, + FormattedValue, + FunctionDef, + GeneratorExp, + Global, + If, + IfExp, + Import, + ImportFrom, + Index, + JoinedStr, + Keyword, + Lambda, + List, + ListComp, + LocalsDictNodeNG, + Match, + MatchAs, + MatchCase, + MatchClass, + MatchMapping, + MatchOr, + MatchSequence, + MatchSingleton, + MatchStar, + MatchValue, + Module, + Name, + NamedExpr, + NodeNG, + Nonlocal, + Pass, + Pattern, + Raise, + Return, + Set, + SetComp, + Slice, + Starred, + Subscript, + TryExcept, + TryFinally, + Tuple, + UnaryOp, + Unknown, + While, + With, + Yield, + YieldFrom, +) + +__all__ = ( + "AnnAssign", + "are_exclusive", + "Arguments", + "Assert", + "Assign", + "AssignAttr", + "AssignName", + "AsyncFor", + "AsyncFunctionDef", + "AsyncWith", + "Attribute", + "AugAssign", + "Await", + "BinOp", + "BoolOp", + "Break", + "builtin_lookup", + "Call", + "ClassDef", + "CONST_CLS", + "Compare", + "Comprehension", + "ComprehensionScope", + "Const", + "const_factory", + "Continue", + "Decorators", + "DelAttr", + "Delete", + "DelName", + "Dict", + "DictComp", + "DictUnpack", + "Ellipsis", + "EmptyNode", + "EvaluatedObject", + "ExceptHandler", + "Expr", + "ExtSlice", + "For", + "FormattedValue", + "FunctionDef", + "function_to_method", + "GeneratorExp", + "get_wrapping_class", + "Global", + "If", + "IfExp", + "Import", + "ImportFrom", + "Index", + "JoinedStr", + "Keyword", + "Lambda", + "List", + "ListComp", + "LocalsDictNodeNG", + "Match", + "MatchAs", + "MatchCase", + "MatchClass", + "MatchMapping", + "MatchOr", + "MatchSequence", + "MatchSingleton", + "MatchStar", + "MatchValue", + "Module", + "Name", + "NamedExpr", + "NodeNG", + "Nonlocal", + "Pass", + "Position", + "Raise", + "Return", + "Set", + "SetComp", + "Slice", + "Starred", + "Statement", + "Subscript", + "TryExcept", + "TryFinally", + "Tuple", + "UnaryOp", + "Unknown", + "unpack_infer", + "While", + "With", + "Yield", + "YieldFrom", +) diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..1c306edb Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/_base_nodes.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/_base_nodes.cpython-310.pyc new file mode 100644 index 00000000..0338052f Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/_base_nodes.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/as_string.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/as_string.cpython-310.pyc new file mode 100644 index 00000000..68846d59 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/as_string.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/const.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/const.cpython-310.pyc new file mode 100644 index 00000000..67122851 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/const.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/node_classes.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/node_classes.cpython-310.pyc new file mode 100644 index 00000000..528d28c6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/node_classes.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/node_ng.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/node_ng.cpython-310.pyc new file mode 100644 index 00000000..e8efa696 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/node_ng.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/utils.cpython-310.pyc new file mode 100644 index 00000000..0b16a0da Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/__pycache__/utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py b/venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py new file mode 120000 index 00000000..c8cb813f --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/_base_nodes.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a1/0e/2e/79ac5be7e2409c6d70d7b94af13468e31b3729ab47ad8f7ce4cd2a82cb \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/as_string.py b/venv/lib/python3.10/site-packages/astroid/nodes/as_string.py new file mode 120000 index 00000000..0d6c5483 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/as_string.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/8b/8b/39/3371a169e7f477e260aa2e4d90fcfc0187171b764becddc73d866ca7fd \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/const.py b/venv/lib/python3.10/site-packages/astroid/nodes/const.py new file mode 120000 index 00000000..b367eaf1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/const.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/8b/24/27/feffb01332bab9773e7443289623d5491cb4359f13dd79afbcf5cbb8fe \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py b/venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py new file mode 100644 index 00000000..b7772c3c --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/node_classes.py @@ -0,0 +1,5643 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""Module for some node classes. More nodes in scoped_nodes.py""" + +from __future__ import annotations + +import abc +import itertools +import sys +import typing +import warnings +from collections.abc import Generator, Iterable, Mapping +from functools import lru_cache +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, TypeVar, Union + +from astroid import decorators, util +from astroid.bases import Instance, _infer_stmts +from astroid.const import _EMPTY_OBJECT_MARKER, Context +from astroid.context import InferenceContext +from astroid.exceptions import ( + AstroidIndexError, + AstroidTypeError, + AstroidValueError, + InferenceError, + NoDefault, + ParentMissingError, +) +from astroid.manager import AstroidManager +from astroid.nodes import _base_nodes +from astroid.nodes.const import OP_PRECEDENCE +from astroid.nodes.node_ng import NodeNG +from astroid.typing import ( + ConstFactoryResult, + InferBinaryOp, + InferenceErrorInfo, + InferenceResult, + SuccessfulInferenceResult, +) + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + +if TYPE_CHECKING: + from astroid import nodes + from astroid.nodes import LocalsDictNodeNG + +if sys.version_info >= (3, 8): + from functools import cached_property +else: + from astroid.decorators import cachedproperty as cached_property + + +def _is_const(value) -> bool: + return isinstance(value, tuple(CONST_CLS)) + + +_NodesT = TypeVar("_NodesT", bound=NodeNG) +_BadOpMessageT = TypeVar("_BadOpMessageT", bound=util.BadOperationMessage) + +AssignedStmtsPossibleNode = Union["List", "Tuple", "AssignName", "AssignAttr", None] +AssignedStmtsCall = Callable[ + [ + _NodesT, + AssignedStmtsPossibleNode, + Optional[InferenceContext], + Optional[typing.List[int]], + ], + Any, +] +InferBinaryOperation = Callable[ + [_NodesT, Optional[InferenceContext]], + typing.Generator[Union[InferenceResult, _BadOpMessageT], None, None], +] +InferLHS = Callable[ + [_NodesT, Optional[InferenceContext]], + typing.Generator[InferenceResult, None, Optional[InferenceErrorInfo]], +] +InferUnaryOp = Callable[[_NodesT, str], ConstFactoryResult] + + +@decorators.raise_if_nothing_inferred +def unpack_infer(stmt, context: InferenceContext | None = None): + """recursively generate nodes inferred by the given statement. + If the inferred value is a list or a tuple, recurse on the elements + """ + if isinstance(stmt, (List, Tuple)): + for elt in stmt.elts: + if elt is util.Uninferable: + yield elt + continue + yield from unpack_infer(elt, context) + return {"node": stmt, "context": context} + # if inferred is a final node, return it and stop + inferred = next(stmt.infer(context), util.Uninferable) + if inferred is stmt: + yield inferred + return {"node": stmt, "context": context} + # else, infer recursively, except Uninferable object that should be returned as is + for inferred in stmt.infer(context): + if isinstance(inferred, util.UninferableBase): + yield inferred + else: + yield from unpack_infer(inferred, context) + + return {"node": stmt, "context": context} + + +def are_exclusive(stmt1, stmt2, exceptions: list[str] | None = None) -> bool: + """return true if the two given statements are mutually exclusive + + `exceptions` may be a list of exception names. If specified, discard If + branches and check one of the statement is in an exception handler catching + one of the given exceptions. + + algorithm : + 1) index stmt1's parents + 2) climb among stmt2's parents until we find a common parent + 3) if the common parent is a If or TryExcept statement, look if nodes are + in exclusive branches + """ + # index stmt1's parents + stmt1_parents = {} + children = {} + previous = stmt1 + for node in stmt1.node_ancestors(): + stmt1_parents[node] = 1 + children[node] = previous + previous = node + # climb among stmt2's parents until we find a common parent + previous = stmt2 + for node in stmt2.node_ancestors(): + if node in stmt1_parents: + # if the common parent is a If or TryExcept statement, look if + # nodes are in exclusive branches + if isinstance(node, If) and exceptions is None: + c2attr, c2node = node.locate_child(previous) + c1attr, c1node = node.locate_child(children[node]) + if "test" in (c1attr, c2attr): + # If any node is `If.test`, then it must be inclusive with + # the other node (`If.body` and `If.orelse`) + return False + if c1attr != c2attr: + # different `If` branches (`If.body` and `If.orelse`) + return True + elif isinstance(node, 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 + return False + + +# getitem() helpers. + +_SLICE_SENTINEL = object() + + +def _slice_value(index, context: InferenceContext | None = None): + """Get the value of the given slice index.""" + + if isinstance(index, Const): + if isinstance(index.value, (int, type(None))): + return index.value + elif index is None: + return None + else: + # Try to infer what the index actually is. + # Since we can't return all the possible values, + # we'll stop at the first possible value. + try: + inferred = next(index.infer(context=context)) + except (InferenceError, StopIteration): + pass + else: + if isinstance(inferred, Const): + if isinstance(inferred.value, (int, type(None))): + return inferred.value + + # Use a sentinel, because None can be a valid + # value that this function can return, + # as it is the case for unspecified bounds. + return _SLICE_SENTINEL + + +def _infer_slice(node, context: InferenceContext | None = None): + lower = _slice_value(node.lower, context) + upper = _slice_value(node.upper, context) + step = _slice_value(node.step, context) + if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)): + return slice(lower, upper, step) + + raise AstroidTypeError( + message="Could not infer slice used in subscript", + node=node, + index=node.parent, + context=context, + ) + + +def _container_getitem(instance, elts, index, context: InferenceContext | None = None): + """Get a slice or an item, using the given *index*, for the given sequence.""" + try: + if isinstance(index, Slice): + index_slice = _infer_slice(index, context=context) + new_cls = instance.__class__() + new_cls.elts = elts[index_slice] + new_cls.parent = instance.parent + return new_cls + if isinstance(index, Const): + return elts[index.value] + except ValueError as exc: + raise AstroidValueError( + message="Slice {index!r} cannot index container", + node=instance, + index=index, + context=context, + ) from exc + except IndexError as exc: + raise AstroidIndexError( + message="Index {index!s} out of range", + node=instance, + index=index, + context=context, + ) from exc + except TypeError as exc: + raise AstroidTypeError( + message="Type error {error!r}", node=instance, index=index, context=context + ) from exc + + raise AstroidTypeError(f"Could not use {index} as subscript index") + + +class BaseContainer(_base_nodes.ParentAssignNode, Instance, metaclass=abc.ABCMeta): + """Base class for Set, FrozenSet, Tuple and List.""" + + _astroid_fields = ("elts",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.elts: list[NodeNG] = [] + """The elements in the node.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, elts: list[NodeNG]) -> None: + """Do some setup after initialisation. + + :param elts: The list of elements the that node contains. + """ + 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, context: InferenceContext | None = None) -> bool: + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + """ + return bool(self.elts) + + @abc.abstractmethod + def pytype(self) -> str: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + + def get_children(self): + yield from self.elts + + +# TODO: Move into _base_nodes. Blocked by import of _infer_stmts from bases. +class LookupMixIn(NodeNG): + """Mixin to look up a name in the right scope.""" + + @lru_cache() # noqa + def lookup(self, name: str) -> tuple[LocalsDictNodeNG, list[NodeNG]]: + """Lookup where the given variable is assigned. + + The lookup starts from self's scope. If self is not a frame itself + and the name is found in the inner frame locals, statements will be + filtered to remove ignorable statements according to self's location. + + :param name: The name of the variable to find assignments for. + + :returns: The scope node and the list of assignments associated to the + given name according to the scope where it has been found (locals, + globals or builtin). + """ + return self.scope().scope_lookup(self, name) + + def ilookup(self, name): + """Lookup the inferred values of the given variable. + + :param name: The variable name to find values for. + :type name: str + + :returns: The inferred values of the statements returned from + :meth:`lookup`. + :rtype: iterable + """ + frame, stmts = self.lookup(name) + context = InferenceContext() + return _infer_stmts(stmts, context, frame) + + +# Name classes + + +class AssignName(_base_nodes.NoChildrenNode, LookupMixIn, _base_nodes.ParentAssignNode): + """Variation of :class:`ast.Assign` representing assignment to a name. + + An :class:`AssignName` is the name of something that is assigned to. + This includes variables defined in a function signature or in a loop. + + >>> import astroid + >>> node = astroid.extract_node('variable = range(10)') + >>> node + + >>> list(node.get_children()) + [, ] + >>> list(node.get_children())[0].as_string() + 'variable' + """ + + _other_fields = ("name",) + + infer_lhs: ClassVar[InferLHS[AssignName]] + + @decorators.deprecate_default_argument_values(name="str") + def __init__( + self, + name: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param name: The name that is assigned to. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.name: str | None = name + """The name that is assigned to.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + assigned_stmts: ClassVar[AssignedStmtsCall[AssignName]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + +class DelName(_base_nodes.NoChildrenNode, LookupMixIn, _base_nodes.ParentAssignNode): + """Variation of :class:`ast.Delete` representing deletion of a name. + + A :class:`DelName` is the name of something that is deleted. + + >>> import astroid + >>> node = astroid.extract_node("del variable #@") + >>> list(node.get_children()) + [] + >>> list(node.get_children())[0].as_string() + 'variable' + """ + + _other_fields = ("name",) + + @decorators.deprecate_default_argument_values(name="str") + def __init__( + self, + name: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param name: The name that is being deleted. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.name: str | None = name + """The name that is being deleted.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + +class Name(_base_nodes.NoChildrenNode, LookupMixIn): + """Class representing an :class:`ast.Name` node. + + A :class:`Name` node is something that is named, but not covered by + :class:`AssignName` or :class:`DelName`. + + >>> import astroid + >>> node = astroid.extract_node('range(10)') + >>> node + + >>> list(node.get_children()) + [, ] + >>> list(node.get_children())[0].as_string() + 'range' + """ + + _other_fields = ("name",) + + @decorators.deprecate_default_argument_values(name="str") + def __init__( + self, + name: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param name: The name that this node refers to. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.name: str | None = name + """The name that this node refers to.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def _get_name_nodes(self): + yield self + + for child_node in self.get_children(): + yield from child_node._get_name_nodes() + + +class Arguments(_base_nodes.AssignTypeNode): + """Class representing an :class:`ast.arguments` node. + + An :class:`Arguments` node represents that arguments in a + function definition. + + >>> import astroid + >>> node = astroid.extract_node('def foo(bar): pass') + >>> node + + >>> node.args + + """ + + # Python 3.4+ uses a different approach regarding annotations, + # each argument is a new class, _ast.arg, which exposes an + # 'annotation' attribute. In astroid though, arguments are exposed + # as is in the Arguments node and the only way to expose annotations + # is by using something similar with Python 3.3: + # - we expose 'varargannotation' and 'kwargannotation' of annotations + # of varargs and kwargs. + # - we expose 'annotation', a list with annotations for + # for each normal argument. If an argument doesn't have an + # annotation, its value will be None. + _astroid_fields = ( + "args", + "defaults", + "kwonlyargs", + "posonlyargs", + "posonlyargs_annotations", + "kw_defaults", + "annotations", + "varargannotation", + "kwargannotation", + "kwonlyargs_annotations", + "type_comment_args", + "type_comment_kwonlyargs", + "type_comment_posonlyargs", + ) + + _other_fields = ("vararg", "kwarg") + + lineno: None + col_offset: None + end_lineno: None + end_col_offset: None + + def __init__( + self, + vararg: str | None = None, + kwarg: str | None = None, + parent: NodeNG | None = None, + ) -> None: + """ + :param vararg: The name of the variable length arguments. + + :param kwarg: The name of the variable length keyword arguments. + + :param parent: The parent node in the syntax tree. + """ + super().__init__(parent=parent) + + self.vararg: str | None = vararg # can be None + """The name of the variable length arguments.""" + + self.kwarg: str | None = kwarg # can be None + """The name of the variable length keyword arguments.""" + + self.args: list[AssignName] | None + """The names of the required arguments. + + Can be None if the associated function does not have a retrievable + signature and the arguments are therefore unknown. + This can happen with (builtin) functions implemented in C that have + incomplete signature information. + """ + # TODO: Check if other attributes should also be None when + # .args is None. + + self.defaults: list[NodeNG] | None + """The default values for arguments that can be passed positionally.""" + + self.kwonlyargs: list[AssignName] + """The keyword arguments that cannot be passed positionally.""" + + self.posonlyargs: list[AssignName] = [] + """The arguments that can only be passed positionally.""" + + self.kw_defaults: list[NodeNG | None] | None + """ + The default values for keyword arguments that cannot be passed positionally. + + See .args for why this can be None. + """ + + self.annotations: list[NodeNG | None] + """The type annotations of arguments that can be passed positionally.""" + + self.posonlyargs_annotations: list[NodeNG | None] = [] + """The type annotations of arguments that can only be passed positionally.""" + + self.kwonlyargs_annotations: list[NodeNG | None] = [] + """The type annotations of arguments that cannot be passed positionally.""" + + self.type_comment_args: list[NodeNG | None] = [] + """The type annotation, passed by a type comment, of each argument. + + If an argument does not have a type comment, + the value for that argument will be None. + """ + + self.type_comment_kwonlyargs: list[NodeNG | None] = [] + """The type annotation, passed by a type comment, of each keyword only argument. + + If an argument does not have a type comment, + the value for that argument will be None. + """ + + self.type_comment_posonlyargs: list[NodeNG | None] = [] + """The type annotation, passed by a type comment, of each positional argument. + + If an argument does not have a type comment, + the value for that argument will be None. + """ + + self.varargannotation: NodeNG | None = None # can be None + """The type annotation for the variable length arguments.""" + + self.kwargannotation: NodeNG | None = None # can be None + """The type annotation for the variable length keyword arguments.""" + + # pylint: disable=too-many-arguments + def postinit( + self, + args: list[AssignName] | None, + defaults: list[NodeNG] | None, + kwonlyargs: list[AssignName], + kw_defaults: list[NodeNG | None] | None, + annotations: list[NodeNG | None], + posonlyargs: list[AssignName] | None = None, + kwonlyargs_annotations: list[NodeNG | None] | None = None, + posonlyargs_annotations: list[NodeNG | None] | None = None, + varargannotation: NodeNG | None = None, + kwargannotation: NodeNG | None = None, + type_comment_args: list[NodeNG | None] | None = None, + type_comment_kwonlyargs: list[NodeNG | None] | None = None, + type_comment_posonlyargs: list[NodeNG | None] | None = None, + ) -> None: + """Do some setup after initialisation. + + :param args: The names of the required arguments. + + :param defaults: The default values for arguments that can be passed + positionally. + + :param kwonlyargs: The keyword arguments that cannot be passed + positionally. + + :param posonlyargs: The arguments that can only be passed + positionally. + + :param kw_defaults: The default values for keyword arguments that + cannot be passed positionally. + + :param annotations: The type annotations of arguments that can be + passed positionally. + + :param kwonlyargs_annotations: The type annotations of arguments that + cannot be passed positionally. This should always be passed in + Python 3. + + :param posonlyargs_annotations: The type annotations of arguments that + can only be passed positionally. This should always be passed in + Python 3. + + :param varargannotation: The type annotation for the variable length + arguments. + + :param kwargannotation: The type annotation for the variable length + keyword arguments. + + :param type_comment_args: The type annotation, + passed by a type comment, of each argument. + + :param type_comment_args: The type annotation, + passed by a type comment, of each keyword only argument. + + :param type_comment_args: The type annotation, + passed by a type comment, of each positional argument. + """ + self.args = args + self.defaults = defaults + self.kwonlyargs = kwonlyargs + if posonlyargs is not None: + self.posonlyargs = posonlyargs + self.kw_defaults = kw_defaults + self.annotations = annotations + if kwonlyargs_annotations is not None: + self.kwonlyargs_annotations = kwonlyargs_annotations + if posonlyargs_annotations is not None: + self.posonlyargs_annotations = posonlyargs_annotations + self.varargannotation = varargannotation + self.kwargannotation = kwargannotation + if type_comment_args is not None: + self.type_comment_args = type_comment_args + if type_comment_kwonlyargs is not None: + self.type_comment_kwonlyargs = type_comment_kwonlyargs + if type_comment_posonlyargs is not None: + self.type_comment_posonlyargs = type_comment_posonlyargs + + assigned_stmts: ClassVar[AssignedStmtsCall[Arguments]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + def _infer_name(self, frame, name): + if self.parent is frame: + return name + return None + + @cached_property + def fromlineno(self): + """The first line that this node appears on in the source code. + + :type: int or None + """ + lineno = super().fromlineno + return max(lineno, self.parent.fromlineno or 0) + + @cached_property + def arguments(self): + """Get all the arguments for this node, including positional only and positional and keyword""" + return list(itertools.chain((self.posonlyargs or ()), self.args or ())) + + def format_args(self, *, skippable_names: set[str] | None = None) -> str: + """Get the arguments formatted as string. + + :returns: The formatted arguments. + :rtype: str + """ + result = [] + positional_only_defaults = [] + positional_or_keyword_defaults = self.defaults + if self.defaults: + args = self.args or [] + positional_or_keyword_defaults = self.defaults[-len(args) :] + positional_only_defaults = self.defaults[: len(self.defaults) - len(args)] + + if self.posonlyargs: + result.append( + _format_args( + self.posonlyargs, + positional_only_defaults, + self.posonlyargs_annotations, + skippable_names=skippable_names, + ) + ) + result.append("/") + if self.args: + result.append( + _format_args( + self.args, + positional_or_keyword_defaults, + getattr(self, "annotations", None), + skippable_names=skippable_names, + ) + ) + if self.vararg: + result.append(f"*{self.vararg}") + if self.kwonlyargs: + if not self.vararg: + result.append("*") + result.append( + _format_args( + self.kwonlyargs, + self.kw_defaults, + self.kwonlyargs_annotations, + skippable_names=skippable_names, + ) + ) + if self.kwarg: + result.append(f"**{self.kwarg}") + return ", ".join(result) + + def _get_arguments_data( + self, + ) -> tuple[ + dict[str, tuple[str | None, str | None]], + dict[str, tuple[str | None, str | None]], + ]: + """Get the arguments as dictionary with information about typing and defaults. + + The return tuple contains a dictionary for positional and keyword arguments with their typing + and their default value, if any. + The method follows a similar order as format_args but instead of formatting into a string it + returns the data that is used to do so. + """ + pos_only: dict[str, tuple[str | None, str | None]] = {} + kw_only: dict[str, tuple[str | None, str | None]] = {} + + # Setup and match defaults with arguments + positional_only_defaults = [] + positional_or_keyword_defaults = self.defaults + if self.defaults: + args = self.args or [] + positional_or_keyword_defaults = self.defaults[-len(args) :] + positional_only_defaults = self.defaults[: len(self.defaults) - len(args)] + + for index, posonly in enumerate(self.posonlyargs): + annotation, default = self.posonlyargs_annotations[index], None + if annotation is not None: + annotation = annotation.as_string() + if positional_only_defaults: + default = positional_only_defaults[index].as_string() + pos_only[posonly.name] = (annotation, default) + + for index, arg in enumerate(self.args): + annotation, default = self.annotations[index], None + if annotation is not None: + annotation = annotation.as_string() + if positional_or_keyword_defaults: + defaults_offset = len(self.args) - len(positional_or_keyword_defaults) + default_index = index - defaults_offset + if ( + default_index > -1 + and positional_or_keyword_defaults[default_index] is not None + ): + default = positional_or_keyword_defaults[default_index].as_string() + pos_only[arg.name] = (annotation, default) + + if self.vararg: + annotation = self.varargannotation + if annotation is not None: + annotation = annotation.as_string() + pos_only[self.vararg] = (annotation, None) + + for index, kwarg in enumerate(self.kwonlyargs): + annotation = self.kwonlyargs_annotations[index] + if annotation is not None: + annotation = annotation.as_string() + default = self.kw_defaults[index] + if default is not None: + default = default.as_string() + kw_only[kwarg.name] = (annotation, default) + + if self.kwarg: + annotation = self.kwargannotation + if annotation is not None: + annotation = annotation.as_string() + kw_only[self.kwarg] = (annotation, None) + + return pos_only, kw_only + + def default_value(self, argname): + """Get the default value for an argument. + + :param argname: The name of the argument to get the default value for. + :type argname: str + + :raises NoDefault: If there is no default value defined for the + given argument. + """ + args = self.arguments + 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 NoDefault(func=self.parent, name=argname) + + def is_argument(self, name) -> bool: + """Check if the given name is defined in the arguments. + + :param name: The name to check for. + :type name: str + + :returns: Whether the given name is defined in the arguments, + """ + if name == self.vararg: + return True + if name == self.kwarg: + return True + return ( + self.find_argname(name, 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.arguments: + return _find_arg(argname, self.arguments, rec) + return None, None + + def get_children(self): + yield from self.posonlyargs or () + + for elt in self.posonlyargs_annotations: + if elt is not None: + yield elt + + yield from self.args or () + + if self.defaults is not None: + yield from self.defaults + yield from self.kwonlyargs + + for elt in self.kw_defaults or (): + if elt is not None: + yield elt + + for elt in self.annotations: + if elt is not None: + yield elt + + if self.varargannotation is not None: + yield self.varargannotation + + if self.kwargannotation is not None: + yield self.kwargannotation + + for elt in self.kwonlyargs_annotations: + if elt is not None: + yield elt + + +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, skippable_names: set[str] | None = None +) -> str: + if skippable_names is None: + skippable_names = set() + values = [] + if args is None: + return "" + if annotations is None: + annotations = [] + if defaults is not None: + default_offset = len(args) - len(defaults) + packed = itertools.zip_longest(args, annotations) + for i, (arg, annotation) in enumerate(packed): + if arg.name in skippable_names: + continue + if isinstance(arg, Tuple): + values.append(f"({_format_args(arg.elts)})") + else: + argname = arg.name + default_sep = "=" + if annotation is not None: + argname += ": " + annotation.as_string() + default_sep = " = " + values.append(argname) + + if defaults is not None and i >= default_offset: + if defaults[i - default_offset] is not None: + values[-1] += default_sep + defaults[i - default_offset].as_string() + return ", ".join(values) + + +class AssignAttr(_base_nodes.ParentAssignNode): + """Variation of :class:`ast.Assign` representing assignment to an attribute. + + >>> import astroid + >>> node = astroid.extract_node('self.attribute = range(10)') + >>> node + + >>> list(node.get_children()) + [, ] + >>> list(node.get_children())[0].as_string() + 'self.attribute' + """ + + _astroid_fields = ("expr",) + _other_fields = ("attrname",) + + infer_lhs: ClassVar[InferLHS[AssignAttr]] + + @decorators.deprecate_default_argument_values(attrname="str") + def __init__( + self, + attrname: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param attrname: The name of the attribute being assigned to. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.expr: NodeNG | None = None + """What has the attribute that is being assigned to.""" + + self.attrname: str | None = attrname + """The name of the attribute being assigned to.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, expr: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param expr: What has the attribute that is being assigned to. + """ + self.expr = expr + + assigned_stmts: ClassVar[AssignedStmtsCall[AssignAttr]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + def get_children(self): + yield self.expr + + +class Assert(_base_nodes.Statement): + """Class representing an :class:`ast.Assert` node. + + An :class:`Assert` node represents an assert statement. + + >>> import astroid + >>> node = astroid.extract_node('assert len(things) == 10, "Not enough things"') + >>> node + + """ + + _astroid_fields = ("test", "fail") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.test: NodeNG | None = None + """The test that passes or fails the assertion.""" + + self.fail: NodeNG | None = None # can be None + """The message shown when the assertion fails.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, test: NodeNG | None = None, fail: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param test: The test that passes or fails the assertion. + + :param fail: The message shown when the assertion fails. + """ + self.fail = fail + self.test = test + + def get_children(self): + yield self.test + + if self.fail is not None: + yield self.fail + + +class Assign(_base_nodes.AssignTypeNode, _base_nodes.Statement): + """Class representing an :class:`ast.Assign` node. + + An :class:`Assign` is a statement where something is explicitly + asssigned to. + + >>> import astroid + >>> node = astroid.extract_node('variable = range(10)') + >>> node + + """ + + _astroid_fields = ("targets", "value") + _other_other_fields = ("type_annotation",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.targets: list[NodeNG] = [] + """What is being assigned to.""" + + self.value: NodeNG | None = None + """The value being assigned to the variables.""" + + self.type_annotation: NodeNG | None = None # can be None + """If present, this will contain the type annotation passed by a type comment""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + targets: list[NodeNG] | None = None, + value: NodeNG | None = None, + type_annotation: NodeNG | None = None, + ) -> None: + """Do some setup after initialisation. + + :param targets: What is being assigned to. + :param value: The value being assigned to the variables. + :param type_annotation: + """ + if targets is not None: + self.targets = targets + self.value = value + self.type_annotation = type_annotation + + assigned_stmts: ClassVar[AssignedStmtsCall[Assign]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + def get_children(self): + yield from self.targets + + yield self.value + + @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(_base_nodes.AssignTypeNode, _base_nodes.Statement): + """Class representing an :class:`ast.AnnAssign` node. + + An :class:`AnnAssign` is an assignment with a type annotation. + + >>> import astroid + >>> node = astroid.extract_node('variable: List[int] = range(10)') + >>> node + + """ + + _astroid_fields = ("target", "annotation", "value") + _other_fields = ("simple",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.target: NodeNG | None = None + """What is being assigned to.""" + + self.annotation: NodeNG | None = None + """The type annotation of what is being assigned to.""" + + self.value: NodeNG | None = None # can be None + """The value being assigned to the variables.""" + + self.simple: int | None = None + """Whether :attr:`target` is a pure name or a complex statement.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + target: NodeNG, + annotation: NodeNG, + simple: int, + value: NodeNG | None = None, + ) -> None: + """Do some setup after initialisation. + + :param target: What is being assigned to. + + :param annotation: The type annotation of what is being assigned to. + + :param simple: Whether :attr:`target` is a pure name + or a complex statement. + + :param value: The value being assigned to the variables. + """ + self.target = target + self.annotation = annotation + self.value = value + self.simple = simple + + assigned_stmts: ClassVar[AssignedStmtsCall[AnnAssign]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + def get_children(self): + yield self.target + yield self.annotation + + if self.value is not None: + yield self.value + + +class AugAssign(_base_nodes.AssignTypeNode, _base_nodes.Statement): + """Class representing an :class:`ast.AugAssign` node. + + An :class:`AugAssign` is an assignment paired with an operator. + + >>> import astroid + >>> node = astroid.extract_node('variable += 1') + >>> node + + """ + + _astroid_fields = ("target", "value") + _other_fields = ("op",) + + @decorators.deprecate_default_argument_values(op="str") + def __init__( + self, + op: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param op: The operator that is being combined with the assignment. + This includes the equals sign. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.target: NodeNG | None = None + """What is being assigned to.""" + + self.op: str | None = op + """The operator that is being combined with the assignment. + + This includes the equals sign. + """ + + self.value: NodeNG | None = None + """The value being assigned to the variable.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, target: NodeNG | None = None, value: NodeNG | None = None + ) -> None: + """Do some setup after initialisation. + + :param target: What is being assigned to. + + :param value: The value being assigned to the variable. + """ + self.target = target + self.value = value + + assigned_stmts: ClassVar[AssignedStmtsCall[AugAssign]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + # This is set by inference.py + _infer_augassign: ClassVar[ + InferBinaryOperation[AugAssign, util.BadBinaryOperationMessage] + ] + + def type_errors(self, context: InferenceContext | None = None): + """Get a list of type errors which can occur during inference. + + Each TypeError is represented by a :class:`BadBinaryOperationMessage` , + which holds the original exception. + + :returns: The list of possible type errors. + :rtype: list(BadBinaryOperationMessage) + """ + try: + results = self._infer_augassign(context=context) + return [ + result + for result in results + if isinstance(result, util.BadBinaryOperationMessage) + ] + except InferenceError: + return [] + + def get_children(self): + yield self.target + yield self.value + + def _get_yield_nodes_skip_lambdas(self): + """An AugAssign node can contain a Yield node in the value""" + yield from self.value._get_yield_nodes_skip_lambdas() + yield from super()._get_yield_nodes_skip_lambdas() + + +class BinOp(NodeNG): + """Class representing an :class:`ast.BinOp` node. + + A :class:`BinOp` node is an application of a binary operator. + + >>> import astroid + >>> node = astroid.extract_node('a + b') + >>> node + + """ + + _astroid_fields = ("left", "right") + _other_fields = ("op",) + + @decorators.deprecate_default_argument_values(op="str") + def __init__( + self, + op: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param op: The operator. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.left: NodeNG | None = None + """What is being applied to the operator on the left side.""" + + self.op: str | None = op + """The operator.""" + + self.right: NodeNG | None = None + """What is being applied to the operator on the right side.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, left: NodeNG | None = None, right: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param left: What is being applied to the operator on the left side. + + :param right: What is being applied to the operator on the right side. + """ + self.left = left + self.right = right + + # This is set by inference.py + _infer_binop: ClassVar[InferBinaryOperation[BinOp, util.BadBinaryOperationMessage]] + + def type_errors(self, context: InferenceContext | None = None): + """Get a list of type errors which can occur during inference. + + Each TypeError is represented by a :class:`BadBinaryOperationMessage`, + which holds the original exception. + + :returns: The list of possible type errors. + :rtype: list(BadBinaryOperationMessage) + """ + try: + results = self._infer_binop(context=context) + return [ + result + for result in results + if isinstance(result, util.BadBinaryOperationMessage) + ] + except InferenceError: + return [] + + def get_children(self): + yield self.left + yield self.right + + def op_precedence(self): + return OP_PRECEDENCE[self.op] + + def op_left_associative(self) -> bool: + # 2**3**4 == 2**(3**4) + return self.op != "**" + + +class BoolOp(NodeNG): + """Class representing an :class:`ast.BoolOp` node. + + A :class:`BoolOp` is an application of a boolean operator. + + >>> import astroid + >>> node = astroid.extract_node('a and b') + >>> node + + """ + + _astroid_fields = ("values",) + _other_fields = ("op",) + + @decorators.deprecate_default_argument_values(op="str") + def __init__( + self, + op: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param op: The operator. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.op: str | None = op + """The operator.""" + + self.values: list[NodeNG] = [] + """The values being applied to the operator.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, values: list[NodeNG] | None = None) -> None: + """Do some setup after initialisation. + + :param values: The values being applied to the operator. + """ + if values is not None: + self.values = values + + def get_children(self): + yield from self.values + + def op_precedence(self): + return OP_PRECEDENCE[self.op] + + +class Break(_base_nodes.NoChildrenNode, _base_nodes.Statement): + """Class representing an :class:`ast.Break` node. + + >>> import astroid + >>> node = astroid.extract_node('break') + >>> node + + """ + + +class Call(NodeNG): + """Class representing an :class:`ast.Call` node. + + A :class:`Call` node is a call to a function, method, etc. + + >>> import astroid + >>> node = astroid.extract_node('function()') + >>> node + + """ + + _astroid_fields = ("func", "args", "keywords") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.func: NodeNG | None = None + """What is being called.""" + + self.args: list[NodeNG] = [] + """The positional arguments being given to the call.""" + + self.keywords: list[Keyword] = [] + """The keyword arguments being given to the call.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + func: NodeNG | None = None, + args: list[NodeNG] | None = None, + keywords: list[Keyword] | None = None, + ) -> None: + """Do some setup after initialisation. + + :param func: What is being called. + + :param args: The positional arguments being given to the call. + + :param keywords: The keyword arguments being given to the call. + """ + self.func = func + if args is not None: + self.args = args + if keywords is not None: + self.keywords = keywords + + @property + def starargs(self) -> list[Starred]: + """The positional arguments that unpack something.""" + return [arg for arg in self.args if isinstance(arg, Starred)] + + @property + def kwargs(self) -> list[Keyword]: + """The keyword arguments that unpack something.""" + return [keyword for keyword in self.keywords if keyword.arg is None] + + def get_children(self): + yield self.func + + yield from self.args + + yield from self.keywords + + +class Compare(NodeNG): + """Class representing an :class:`ast.Compare` node. + + A :class:`Compare` node indicates a comparison. + + >>> import astroid + >>> node = astroid.extract_node('a <= b <= c') + >>> node + + >>> node.ops + [('<=', ), ('<=', )] + """ + + _astroid_fields = ("left", "ops") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.left: NodeNG | None = None + """The value at the left being applied to a comparison operator.""" + + self.ops: list[tuple[str, NodeNG]] = [] + """The remainder of the operators and their relevant right hand value.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + left: NodeNG | None = None, + ops: list[tuple[str, NodeNG]] | None = None, + ) -> None: + """Do some setup after initialisation. + + :param left: The value at the left being applied to a comparison + operator. + + :param ops: The remainder of the operators + and their relevant right hand value. + """ + self.left = left + if ops is not None: + 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. + + >>> import astroid + >>> node = astroid.extract_node('[x for x in some_values]') + >>> list(node.get_children()) + [, ] + >>> list(node.get_children())[1].as_string() + 'for x in some_values' + """ + + _astroid_fields = ("target", "iter", "ifs") + _other_fields = ("is_async",) + + optional_assign = True + """Whether this node optionally assigns a variable.""" + + lineno: None + col_offset: None + end_lineno: None + end_col_offset: None + + def __init__(self, parent: NodeNG | None = None) -> None: + """ + :param parent: The parent node in the syntax tree. + """ + self.target: NodeNG | None = None + """What is assigned to by the comprehension.""" + + self.iter: NodeNG | None = None + """What is iterated over by the comprehension.""" + + self.ifs: list[NodeNG] = [] + """The contents of any if statements that filter the comprehension.""" + + self.is_async: bool | None = None + """Whether this is an asynchronous comprehension or not.""" + + super().__init__(parent=parent) + + # pylint: disable=redefined-builtin; same name as builtin ast module. + def postinit( + self, + target: NodeNG | None = None, + iter: NodeNG | None = None, + ifs: list[NodeNG] | None = None, + is_async: bool | None = None, + ) -> None: + """Do some setup after initialisation. + + :param target: What is assigned to by the comprehension. + + :param iter: What is iterated over by the comprehension. + + :param ifs: The contents of any if statements that filter + the comprehension. + + :param is_async: Whether this is an asynchronous comprehension or not. + """ + self.target = target + self.iter = iter + if ifs is not None: + self.ifs = ifs + self.is_async = is_async + + assigned_stmts: ClassVar[AssignedStmtsCall[Comprehension]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + def assign_type(self): + """The type of assignment that this node performs. + + :returns: The assignment type. + :rtype: NodeNG + """ + return self + + def _get_filtered_stmts( + self, lookup_node, node, stmts, mystmt: _base_nodes.Statement | None + ): + """method used in filter_stmts""" + if self is mystmt: + if isinstance(lookup_node, (Const, Name)): + return [lookup_node], True + + elif self.statement(future=True) is mystmt: + # original node's statement is the assignment, only keeps + # current node (gen exp, list comp) + + return [node], True + + return stmts, False + + def get_children(self): + yield self.target + yield self.iter + + yield from self.ifs + + +class Const(_base_nodes.NoChildrenNode, Instance): + """Class representing any constant including num, str, bool, None, bytes. + + >>> import astroid + >>> node = astroid.extract_node('(5, "This is a string.", True, None, b"bytes")') + >>> node + + >>> list(node.get_children()) + [, + , + , + , + ] + """ + + _other_fields = ("value", "kind") + + def __init__( + self, + value: Any, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + kind: str | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param value: The value that the constant represents. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param kind: The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.value: Any = value + """The value that the constant represents.""" + + self.kind: str | None = kind # can be None + """"The string prefix. "u" for u-prefixed strings and ``None`` otherwise. Python 3.8+ only.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + Instance.__init__(self, None) + + infer_unary_op: ClassVar[InferUnaryOp[Const]] + infer_binary_op: ClassVar[InferBinaryOp[Const]] + + def __getattr__(self, name): + # This is needed because of Proxy's __getattr__ method. + # Calling object.__new__ on this class without calling + # __init__ would result in an infinite loop otherwise + # since __getattr__ is called when an attribute doesn't + # exist and self._proxied indirectly calls self.value + # and Proxy __getattr__ calls self.value + if name == "value": + raise AttributeError + return super().__getattr__(name) + + def getitem(self, index, context: InferenceContext | None = None): + """Get an item from this node if subscriptable. + + :param index: The node to use as a subscript index. + :type index: Const or Slice + + :raises AstroidTypeError: When the given index cannot be used as a + subscript index, or if this node is not subscriptable. + """ + if isinstance(index, Const): + index_value = index.value + elif isinstance(index, Slice): + index_value = _infer_slice(index, context=context) + + else: + raise AstroidTypeError( + f"Could not use type {type(index)} as subscript index" + ) + + try: + if isinstance(self.value, (str, bytes)): + return Const(self.value[index_value]) + except ValueError as exc: + raise AstroidValueError( + f"Could not index {self.value!r} with {index_value!r}" + ) from exc + except IndexError as exc: + raise AstroidIndexError( + message="Index {index!r} out of range", + node=self, + index=index, + context=context, + ) from exc + except TypeError as exc: + raise AstroidTypeError( + message="Type error {error!r}", node=self, index=index, context=context + ) from exc + + raise AstroidTypeError(f"{self!r} (value={self.value})") + + def has_dynamic_getattr(self) -> bool: + """Check if the node has a custom __getattr__ or __getattribute__. + + :returns: Whether the class has a custom __getattr__ or __getattribute__. + For a :class:`Const` this is always ``False``. + """ + return False + + def itered(self): + """An iterator over the elements this node contains. + + :returns: The contents of this node. + :rtype: iterable(Const) + + :raises TypeError: If this node does not represent something that is iterable. + """ + if isinstance(self.value, str): + return [const_factory(elem) for elem in self.value] + raise TypeError(f"Cannot iterate over type {type(self.value)!r}") + + def pytype(self) -> str: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + return self._proxied.qname() + + def bool_value(self, context: InferenceContext | None = None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + :rtype: bool + """ + return bool(self.value) + + +class Continue(_base_nodes.NoChildrenNode, _base_nodes.Statement): + """Class representing an :class:`ast.Continue` node. + + >>> import astroid + >>> node = astroid.extract_node('continue') + >>> node + + """ + + +class Decorators(NodeNG): + """A node representing a list of decorators. + + A :class:`Decorators` is the decorators that are applied to + a method or function. + + >>> import astroid + >>> node = astroid.extract_node(''' + @property + def my_property(self): + return 3 + ''') + >>> node + + >>> list(node.get_children())[0] + + """ + + _astroid_fields = ("nodes",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.nodes: list[NodeNG] + """The decorators that this node contains. + + :type: list(Name or Call) or None + """ + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, nodes: list[NodeNG]) -> None: + """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) -> LocalsDictNodeNG: + """The first parent node defining a new scope. + These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes. + + :returns: The first parent scope node. + """ + # skip the function node to go directly to the upper level scope + if not self.parent: + raise ParentMissingError(target=self) + if not self.parent.parent: + raise ParentMissingError(target=self.parent) + return self.parent.parent.scope() + + def get_children(self): + yield from self.nodes + + +class DelAttr(_base_nodes.ParentAssignNode): + """Variation of :class:`ast.Delete` representing deletion of an attribute. + + >>> import astroid + >>> node = astroid.extract_node('del self.attr') + >>> node + + >>> list(node.get_children())[0] + + """ + + _astroid_fields = ("expr",) + _other_fields = ("attrname",) + + @decorators.deprecate_default_argument_values(attrname="str") + def __init__( + self, + attrname: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param attrname: The name of the attribute that is being deleted. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.expr: NodeNG | None = None + """The name that this node represents. + + :type: Name or None + """ + + self.attrname: str | None = attrname + """The name of the attribute that is being deleted.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, expr: NodeNG | None = None) -> 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(_base_nodes.AssignTypeNode, _base_nodes.Statement): + """Class representing an :class:`ast.Delete` node. + + A :class:`Delete` is a ``del`` statement this is deleting something. + + >>> import astroid + >>> node = astroid.extract_node('del self.attr') + >>> node + + """ + + _astroid_fields = ("targets",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.targets: list[NodeNG] = [] + """What is being deleted.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, targets: list[NodeNG] | None = None) -> None: + """Do some setup after initialisation. + + :param targets: What is being deleted. + """ + if targets is not None: + self.targets = targets + + def get_children(self): + yield from self.targets + + +class Dict(NodeNG, Instance): + """Class representing an :class:`ast.Dict` node. + + A :class:`Dict` is a dictionary that is created with ``{}`` syntax. + + >>> import astroid + >>> node = astroid.extract_node('{1: "1"}') + >>> node + + """ + + _astroid_fields = ("items",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.items: list[ + tuple[SuccessfulInferenceResult, SuccessfulInferenceResult] + ] = [] + """The key-value pairs contained in the dictionary.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, items: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] + ) -> None: + """Do some setup after initialisation. + + :param items: The key-value pairs contained in the dictionary. + """ + self.items = items + + infer_unary_op: ClassVar[InferUnaryOp[Dict]] + + @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) -> Literal["builtins.dict"]: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + return "builtins.dict" + + def get_children(self): + """Get the key and value nodes below this node. + + Children are returned in the order that they are defined in the source + code, key first then the value. + + :returns: The children. + :rtype: iterable(NodeNG) + """ + for key, value in self.items: + yield key + yield value + + def last_child(self): + """An optimized version of list(get_children())[-1] + + :returns: The last child, or None if no children exist. + :rtype: NodeNG or None + """ + if self.items: + return self.items[-1][1] + return None + + def itered(self): + """An iterator over the keys this node contains. + + :returns: The keys of this node. + :rtype: iterable(NodeNG) + """ + return [key for (key, _) in self.items] + + def getitem( + self, index: Const | Slice, context: InferenceContext | None = None + ) -> NodeNG: + """Get an item from this node. + + :param index: The node to use as a subscript index. + + :raises AstroidTypeError: When the given index cannot be used as a + subscript index, or if this node is not subscriptable. + :raises AstroidIndexError: If the given index does not exist in the + dictionary. + """ + # pylint: disable-next=import-outside-toplevel; circular import + from astroid.helpers import safe_infer + + for key, value in self.items: + # TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}. + if isinstance(key, DictUnpack): + inferred_value = safe_infer(value, context) + if not isinstance(inferred_value, Dict): + continue + + try: + return inferred_value.getitem(index, context) + except (AstroidTypeError, AstroidIndexError): + continue + + for inferredkey in key.infer(context): + if isinstance(inferredkey, util.UninferableBase): + continue + if isinstance(inferredkey, Const) and isinstance(index, Const): + if inferredkey.value == index.value: + return value + + raise AstroidIndexError(index) + + def bool_value(self, context: InferenceContext | None = None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + :rtype: bool + """ + return bool(self.items) + + +class Expr(_base_nodes.Statement): + """Class representing an :class:`ast.Expr` node. + + An :class:`Expr` is any expression that does not have its value used or + stored. + + >>> import astroid + >>> node = astroid.extract_node('method()') + >>> node + + >>> node.parent + + """ + + _astroid_fields = ("value",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.value: NodeNG | None = None + """What the expression does.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, value: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param value: What the expression does. + """ + 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(_base_nodes.NoChildrenNode): # pylint: disable=redefined-builtin + """Class representing an :class:`ast.Ellipsis` node. + + An :class:`Ellipsis` is the ``...`` syntax. + + Deprecated since v2.6.0 - Use :class:`Const` instead. + Will be removed with the release v2.7.0 + """ + + +class EmptyNode(_base_nodes.NoChildrenNode): + """Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`.""" + + object = None + + def has_underlying_object(self) -> bool: + return self.object is not None and self.object is not _EMPTY_OBJECT_MARKER + + +class ExceptHandler( + _base_nodes.MultiLineBlockNode, _base_nodes.AssignTypeNode, _base_nodes.Statement +): + """Class representing an :class:`ast.ExceptHandler`. node. + + An :class:`ExceptHandler` is an ``except`` block on a try-except. + + >>> import astroid + >>> node = astroid.extract_node(''' + try: + do_something() + except Exception as error: + print("Error!") + ''') + >>> node + + >>> node.handlers + [] + """ + + _astroid_fields = ("type", "name", "body") + _multi_line_block_fields = ("body",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.type: NodeNG | None = None # can be None + """The types that the block handles. + + :type: Tuple or NodeNG or None + """ + + self.name: AssignName | None = None # can be None + """The name that the caught exception is assigned to.""" + + self.body: list[NodeNG] = [] + """The contents of the block.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + assigned_stmts: ClassVar[AssignedStmtsCall[ExceptHandler]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + 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: NodeNG | None = None, + name: AssignName | None = None, + body: list[NodeNG] | None = None, + ) -> 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. + + :param body:The contents of the block. + """ + self.type = type + self.name = name + if body is not None: + self.body = body + + @cached_property + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + if self.name: + return self.name.tolineno + if self.type: + return self.type.tolineno + return self.lineno + + def catch(self, exceptions: list[str] | None) -> bool: + """Check if this node handles any of the given + + :param exceptions: The names of the exceptions to check for. + """ + if self.type is None or exceptions is None: + return True + return any(node.name in exceptions for node in self.type._get_name_nodes()) + + +class ExtSlice(NodeNG): + """Class representing an :class:`ast.ExtSlice` node. + + An :class:`ExtSlice` is a complex slice expression. + + Deprecated since v2.6.0 - Now part of the :class:`Subscript` node. + Will be removed with the release of v2.7.0 + """ + + +class For( + _base_nodes.MultiLineWithElseBlockNode, + _base_nodes.AssignTypeNode, + _base_nodes.Statement, +): + """Class representing an :class:`ast.For` node. + + >>> import astroid + >>> node = astroid.extract_node('for thing in things: print(thing)') + >>> node + + """ + + _astroid_fields = ("target", "iter", "body", "orelse") + _other_other_fields = ("type_annotation",) + _multi_line_block_fields = ("body", "orelse") + + optional_assign = True + """Whether this node optionally assigns a variable. + + This is always ``True`` for :class:`For` nodes. + """ + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.target: NodeNG | None = None + """What the loop assigns to.""" + + self.iter: NodeNG | None = None + """What the loop iterates over.""" + + self.body: list[NodeNG] = [] + """The contents of the body of the loop.""" + + self.orelse: list[NodeNG] = [] + """The contents of the ``else`` block of the loop.""" + + self.type_annotation: NodeNG | None = None # can be None + """If present, this will contain the type annotation passed by a type comment""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. + def postinit( + self, + target: NodeNG | None = None, + iter: NodeNG | None = None, + body: list[NodeNG] | None = None, + orelse: list[NodeNG] | None = None, + type_annotation: NodeNG | None = None, + ) -> None: + """Do some setup after initialisation. + + :param target: What the loop assigns to. + + :param iter: What the loop iterates over. + + :param body: The contents of the body of the loop. + + :param orelse: The contents of the ``else`` block of the loop. + """ + self.target = target + self.iter = iter + if body is not None: + self.body = body + if orelse is not None: + self.orelse = orelse + self.type_annotation = type_annotation + + assigned_stmts: ClassVar[AssignedStmtsCall[For]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + @cached_property + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.iter.tolineno + + def get_children(self): + yield self.target + yield self.iter + + yield from self.body + yield from self.orelse + + +class AsyncFor(For): + """Class representing an :class:`ast.AsyncFor` node. + + An :class:`AsyncFor` is an asynchronous :class:`For` built with + the ``async`` keyword. + + >>> import astroid + >>> node = astroid.extract_node(''' + async def func(things): + async for thing in things: + print(thing) + ''') + >>> node + + >>> node.body[0] + + """ + + +class Await(NodeNG): + """Class representing an :class:`ast.Await` node. + + An :class:`Await` is the ``await`` keyword. + + >>> import astroid + >>> node = astroid.extract_node(''' + async def func(things): + await other_func() + ''') + >>> node + + >>> node.body[0] + + >>> list(node.body[0].get_children())[0] + + """ + + _astroid_fields = ("value",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.value: NodeNG | None = None + """What to wait for.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, value: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param value: What to wait for. + """ + self.value = value + + def get_children(self): + yield self.value + + +class ImportFrom(_base_nodes.ImportNode): + """Class representing an :class:`ast.ImportFrom` node. + + >>> import astroid + >>> node = astroid.extract_node('from my_package import my_module') + >>> node + + """ + + _other_fields = ("modname", "names", "level") + + def __init__( + self, + fromname: str | None, + names: list[tuple[str, str | None]], + level: int | None = 0, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param fromname: The module that is being imported from. + + :param names: What is being imported from the module. + + :param level: The level of relative import. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.modname: str | None = fromname # can be None + """The module that is being imported from. + + This is ``None`` for relative imports. + """ + + self.names: list[tuple[str, str | None]] = names + """What is being imported from the module. + + Each entry is a :class:`tuple` of the name being imported, + and the alias that the name is assigned to (if any). + """ + + # TODO When is 'level' None? + self.level: int | None = level # can be None + """The level of relative import. + + Essentially this is the number of dots in the import. + This is always 0 for absolute imports. + """ + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + +class Attribute(NodeNG): + """Class representing an :class:`ast.Attribute` node.""" + + _astroid_fields = ("expr",) + _other_fields = ("attrname",) + + @decorators.deprecate_default_argument_values(attrname="str") + def __init__( + self, + attrname: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param attrname: The name of the attribute. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.expr: NodeNG | None = None + """The name that this node represents. + + :type: Name or None + """ + + self.attrname: str | None = attrname + """The name of the attribute.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, expr: NodeNG | None = None) -> 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(_base_nodes.NoChildrenNode, _base_nodes.Statement): + """Class representing an :class:`ast.Global` node. + + >>> import astroid + >>> node = astroid.extract_node('global a_global') + >>> node + + """ + + _other_fields = ("names",) + + def __init__( + self, + names: list[str], + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param names: The names being declared as global. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.names: list[str] = names + """The names being declared as global.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def _infer_name(self, frame, name): + return name + + +class If(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement): + """Class representing an :class:`ast.If` node. + + >>> import astroid + >>> node = astroid.extract_node('if condition: print(True)') + >>> node + + """ + + _astroid_fields = ("test", "body", "orelse") + _multi_line_block_fields = ("body", "orelse") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.test: NodeNG | None = None + """The condition that the statement tests.""" + + self.body: list[NodeNG] = [] + """The contents of the block.""" + + self.orelse: list[NodeNG] = [] + """The contents of the ``else`` block.""" + + self.is_orelse: bool = False + """Whether the if-statement is the orelse-block of another if statement.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + test: NodeNG | None = None, + body: list[NodeNG] | None = None, + orelse: list[NodeNG] | None = None, + ) -> None: + """Do some setup after initialisation. + + :param test: The condition that the statement tests. + + :param body: The contents of the block. + + :param orelse: The contents of the ``else`` block. + """ + self.test = test + if body is not None: + self.body = body + if orelse is not None: + self.orelse = orelse + if isinstance(self.parent, If) and self in self.parent.orelse: + self.is_orelse = True + + @cached_property + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.test.tolineno + + def block_range(self, lineno): + """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) + + def _get_yield_nodes_skip_lambdas(self): + """An If node can contain a Yield node in the test""" + yield from self.test._get_yield_nodes_skip_lambdas() + yield from super()._get_yield_nodes_skip_lambdas() + + def is_sys_guard(self) -> bool: + """Return True if IF stmt is a sys.version_info guard. + + >>> import astroid + >>> node = astroid.extract_node(''' + import sys + if sys.version_info > (3, 8): + from typing import Literal + else: + from typing_extensions import Literal + ''') + >>> node.is_sys_guard() + True + """ + warnings.warn( + "The 'is_sys_guard' function is deprecated and will be removed in astroid 3.0.0 " + "It has been moved to pylint and can be imported from 'pylint.checkers.utils' " + "starting with pylint 2.12", + DeprecationWarning, + stacklevel=2, + ) + if isinstance(self.test, Compare): + value = self.test.left + if isinstance(value, Subscript): + value = value.value + if isinstance(value, Attribute) and value.as_string() == "sys.version_info": + return True + + return False + + def is_typing_guard(self) -> bool: + """Return True if IF stmt is a typing guard. + + >>> import astroid + >>> node = astroid.extract_node(''' + from typing import TYPE_CHECKING + if TYPE_CHECKING: + from xyz import a + ''') + >>> node.is_typing_guard() + True + """ + warnings.warn( + "The 'is_typing_guard' function is deprecated and will be removed in astroid 3.0.0 " + "It has been moved to pylint and can be imported from 'pylint.checkers.utils' " + "starting with pylint 2.12", + DeprecationWarning, + stacklevel=2, + ) + return isinstance( + self.test, (Name, Attribute) + ) and self.test.as_string().endswith("TYPE_CHECKING") + + +class IfExp(NodeNG): + """Class representing an :class:`ast.IfExp` node. + >>> import astroid + >>> node = astroid.extract_node('value if condition else other') + >>> node + + """ + + _astroid_fields = ("test", "body", "orelse") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.test: NodeNG | None = None + """The condition that the statement tests.""" + + self.body: NodeNG | None = None + """The contents of the block.""" + + self.orelse: NodeNG | None = None + """The contents of the ``else`` block.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + test: NodeNG | None = None, + body: NodeNG | None = None, + orelse: NodeNG | None = None, + ) -> None: + """Do some setup after initialisation. + + :param test: The condition that the statement tests. + + :param body: The contents of the block. + + :param orelse: The contents of the ``else`` block. + """ + self.test = test + self.body = body + self.orelse = orelse + + def get_children(self): + yield self.test + yield self.body + yield self.orelse + + def op_left_associative(self) -> Literal[False]: + # `1 if True else 2 if False else 3` is parsed as + # `1 if True else (2 if False else 3)` + return False + + +class Import(_base_nodes.ImportNode): + """Class representing an :class:`ast.Import` node. + >>> import astroid + >>> node = astroid.extract_node('import astroid') + >>> node + + """ + + _other_fields = ("names",) + + @decorators.deprecate_default_argument_values(names="list[tuple[str, str | None]]") + def __init__( + self, + names: list[tuple[str, str | None]] | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param names: The names being imported. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.names: list[tuple[str, str | None]] = names or [] + """The names being imported. + + Each entry is a :class:`tuple` of the name being imported, + and the alias that the name is assigned to (if any). + """ + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + +class Index(NodeNG): + """Class representing an :class:`ast.Index` node. + + An :class:`Index` is a simple subscript. + + Deprecated since v2.6.0 - Now part of the :class:`Subscript` node. + Will be removed with the release of v2.7.0 + """ + + +class Keyword(NodeNG): + """Class representing an :class:`ast.keyword` node. + + >>> import astroid + >>> node = astroid.extract_node('function(a_kwarg=True)') + >>> node + + >>> node.keywords + [] + """ + + _astroid_fields = ("value",) + _other_fields = ("arg",) + + def __init__( + self, + arg: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param arg: The argument being assigned to. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.arg: str | None = arg # can be None + """The argument being assigned to.""" + + self.value: NodeNG | None = None + """The value being assigned to the keyword argument.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, value: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param value: The value being assigned to the keyword argument. + """ + self.value = value + + def get_children(self): + yield self.value + + +class List(BaseContainer): + """Class representing an :class:`ast.List` node. + + >>> import astroid + >>> node = astroid.extract_node('[1, 2, 3]') + >>> node + + """ + + _other_fields = ("ctx",) + + def __init__( + self, + ctx: Context | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param ctx: Whether the list is assigned to or loaded from. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.ctx: Context | None = ctx + """Whether the list is assigned to or loaded from.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + assigned_stmts: ClassVar[AssignedStmtsCall[List]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + infer_unary_op: ClassVar[InferUnaryOp[List]] + infer_binary_op: ClassVar[InferBinaryOp[List]] + + def pytype(self) -> Literal["builtins.list"]: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + return "builtins.list" + + def getitem(self, index, context: InferenceContext | None = None): + """Get an item from this node. + + :param index: The node to use as a subscript index. + :type index: Const or Slice + """ + return _container_getitem(self, self.elts, index, context=context) + + +class Nonlocal(_base_nodes.NoChildrenNode, _base_nodes.Statement): + """Class representing an :class:`ast.Nonlocal` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + def function(): + nonlocal var + ''') + >>> node + + >>> node.body[0] + + """ + + _other_fields = ("names",) + + def __init__( + self, + names: list[str], + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param names: The names being declared as not local. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.names: list[str] = names + """The names being declared as not local.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def _infer_name(self, frame, name): + return name + + +class Pass(_base_nodes.NoChildrenNode, _base_nodes.Statement): + """Class representing an :class:`ast.Pass` node. + + >>> import astroid + >>> node = astroid.extract_node('pass') + >>> node + + """ + + +class Raise(_base_nodes.Statement): + """Class representing an :class:`ast.Raise` node. + + >>> import astroid + >>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")') + >>> node + + """ + + _astroid_fields = ("exc", "cause") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.exc: NodeNG | None = None # can be None + """What is being raised.""" + + self.cause: NodeNG | None = None # can be None + """The exception being used to raise this one.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + exc: NodeNG | None = None, + cause: NodeNG | None = None, + ) -> None: + """Do some setup after initialisation. + + :param exc: What is being raised. + + :param cause: The exception being used to raise this one. + """ + self.exc = exc + self.cause = cause + + def raises_not_implemented(self) -> bool: + """Check if this node raises a :class:`NotImplementedError`. + + :returns: Whether this node raises a :class:`NotImplementedError`. + """ + if not self.exc: + return False + return any( + name.name == "NotImplementedError" for name in self.exc._get_name_nodes() + ) + + def get_children(self): + if self.exc is not None: + yield self.exc + + if self.cause is not None: + yield self.cause + + +class Return(_base_nodes.Statement): + """Class representing an :class:`ast.Return` node. + + >>> import astroid + >>> node = astroid.extract_node('return True') + >>> node + + """ + + _astroid_fields = ("value",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.value: NodeNG | None = None # can be None + """The value being returned.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, value: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param value: The value being returned. + """ + self.value = value + + def get_children(self): + if self.value is not None: + yield self.value + + def is_tuple_return(self): + return isinstance(self.value, Tuple) + + def _get_return_nodes_skip_functions(self): + yield self + + +class Set(BaseContainer): + """Class representing an :class:`ast.Set` node. + + >>> import astroid + >>> node = astroid.extract_node('{1, 2, 3}') + >>> node + + """ + + infer_unary_op: ClassVar[InferUnaryOp[Set]] + + def pytype(self) -> Literal["builtins.set"]: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + return "builtins.set" + + +class Slice(NodeNG): + """Class representing an :class:`ast.Slice` node. + + >>> import astroid + >>> node = astroid.extract_node('things[1:3]') + >>> node + + >>> node.slice + + """ + + _astroid_fields = ("lower", "upper", "step") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.lower: NodeNG | None = None # can be None + """The lower index in the slice.""" + + self.upper: NodeNG | None = None # can be None + """The upper index in the slice.""" + + self.step: NodeNG | None = None # can be None + """The step to take between indexes.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + lower: NodeNG | None = None, + upper: NodeNG | None = None, + step: NodeNG | None = None, + ) -> None: + """Do some setup after initialisation. + + :param lower: The lower index in the slice. + + :param upper: The upper index in the slice. + + :param step: The step to take between index. + """ + self.lower = lower + self.upper = upper + self.step = step + + def _wrap_attribute(self, attr): + """Wrap the empty attributes of the Slice in a Const node.""" + if not attr: + const = const_factory(attr) + const.parent = self + return const + return attr + + @cached_property + def _proxied(self) -> nodes.ClassDef: + builtins = AstroidManager().builtins_module + return builtins.getattr("slice")[0] + + def pytype(self) -> Literal["builtins.slice"]: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + return "builtins.slice" + + def igetattr(self, attrname, context: InferenceContext | None = 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: InferenceContext | None = None): + return self._proxied.getattr(attrname, context) + + def get_children(self): + if self.lower is not None: + yield self.lower + + if self.upper is not None: + yield self.upper + + if self.step is not None: + yield self.step + + +class Starred(_base_nodes.ParentAssignNode): + """Class representing an :class:`ast.Starred` node. + + >>> import astroid + >>> node = astroid.extract_node('*args') + >>> node + + """ + + _astroid_fields = ("value",) + _other_fields = ("ctx",) + + def __init__( + self, + ctx: Context | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param ctx: Whether the list is assigned to or loaded from. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.value: NodeNG | None = None + """What is being unpacked.""" + + self.ctx: Context | None = ctx + """Whether the starred item is assigned to or loaded from.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, value: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param value: What is being unpacked. + """ + self.value = value + + assigned_stmts: ClassVar[AssignedStmtsCall[Starred]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + def get_children(self): + yield self.value + + +class Subscript(NodeNG): + """Class representing an :class:`ast.Subscript` node. + + >>> import astroid + >>> node = astroid.extract_node('things[1:3]') + >>> node + + """ + + _astroid_fields = ("value", "slice") + _other_fields = ("ctx",) + + infer_lhs: ClassVar[InferLHS[Subscript]] + + def __init__( + self, + ctx: Context | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param ctx: Whether the subscripted item is assigned to or loaded from. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.value: NodeNG | None = None + """What is being indexed.""" + + self.slice: NodeNG | None = None + """The slice being used to lookup.""" + + self.ctx: Context | None = ctx + """Whether the subscripted item is assigned to or loaded from.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + # pylint: disable=redefined-builtin; had to use the same name as builtin ast module. + def postinit( + self, value: NodeNG | None = None, slice: NodeNG | None = None + ) -> None: + """Do some setup after initialisation. + + :param value: What is being indexed. + + :param slice: The slice being used to lookup. + """ + self.value = value + self.slice = slice + + def get_children(self): + yield self.value + yield self.slice + + +class TryExcept(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement): + """Class representing an :class:`ast.TryExcept` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + try: + do_something() + except Exception as error: + print("Error!") + ''') + >>> node + + """ + + _astroid_fields = ("body", "handlers", "orelse") + _multi_line_block_fields = ("body", "handlers", "orelse") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.body: list[NodeNG] = [] + """The contents of the block to catch exceptions from.""" + + self.handlers: list[ExceptHandler] = [] + """The exception handlers.""" + + self.orelse: list[NodeNG] = [] + """The contents of the ``else`` block.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + body: list[NodeNG] | None = None, + handlers: list[ExceptHandler] | None = None, + orelse: list[NodeNG] | None = None, + ) -> None: + """Do some setup after initialisation. + + :param body: The contents of the block to catch exceptions from. + + :param handlers: The exception handlers. + + :param orelse: The contents of the ``else`` block. + """ + if body is not None: + self.body = body + if handlers is not None: + self.handlers = handlers + if orelse is not None: + 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(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement): + """Class representing an :class:`ast.TryFinally` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + try: + do_something() + except Exception as error: + print("Error!") + finally: + print("Cleanup!") + ''') + >>> node + + """ + + _astroid_fields = ("body", "finalbody") + _multi_line_block_fields = ("body", "finalbody") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.body: list[NodeNG | TryExcept] = [] + """The try-except that the finally is attached to.""" + + self.finalbody: list[NodeNG] = [] + """The contents of the ``finally`` block.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + body: list[NodeNG | TryExcept] | None = None, + finalbody: list[NodeNG] | None = None, + ) -> None: + """Do some setup after initialisation. + + :param body: The try-except that the finally is attached to. + + :param finalbody: The contents of the ``finally`` block. + """ + if body is not None: + self.body = body + if finalbody is not None: + 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 TryStar(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement): + """Class representing an :class:`ast.TryStar` node.""" + + _astroid_fields = ("body", "handlers", "orelse", "finalbody") + _multi_line_block_fields = ("body", "handlers", "orelse", "finalbody") + + def __init__( + self, + *, + lineno: int | None = None, + col_offset: int | None = None, + end_lineno: int | None = None, + end_col_offset: int | None = None, + parent: NodeNG | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + :param col_offset: The column that this node appears on in the + source code. + :param parent: The parent node in the syntax tree. + :param end_lineno: The last line this node appears on in the source code. + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.body: list[NodeNG] = [] + """The contents of the block to catch exceptions from.""" + + self.handlers: list[ExceptHandler] = [] + """The exception handlers.""" + + self.orelse: list[NodeNG] = [] + """The contents of the ``else`` block.""" + + self.finalbody: list[NodeNG] = [] + """The contents of the ``finally`` block.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + *, + body: list[NodeNG] | None = None, + handlers: list[ExceptHandler] | None = None, + orelse: list[NodeNG] | None = None, + finalbody: list[NodeNG] | None = None, + ) -> None: + """Do some setup after initialisation. + :param body: The contents of the block to catch exceptions from. + :param handlers: The exception handlers. + :param orelse: The contents of the ``else`` block. + :param finalbody: The contents of the ``finally`` block. + """ + if body: + self.body = body + if handlers: + self.handlers = handlers + if orelse: + self.orelse = orelse + if finalbody: + self.finalbody = finalbody + + def _infer_name(self, frame, name): + return name + + def block_range(self, lineno: int) -> tuple[int, int]: + """Get a range from a given line number to where this node ends.""" + if lineno == self.fromlineno: + return lineno, lineno + if self.body and self.body[0].fromlineno <= lineno <= self.body[-1].tolineno: + # Inside try body - return from lineno till end of try body + return lineno, self.body[-1].tolineno + for exhandler in self.handlers: + if exhandler.type and lineno == exhandler.type.fromlineno: + return lineno, lineno + if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno: + return lineno, exhandler.body[-1].tolineno + if self.orelse: + if self.orelse[0].fromlineno - 1 == lineno: + return lineno, lineno + if self.orelse[0].fromlineno <= lineno <= self.orelse[-1].tolineno: + return lineno, self.orelse[-1].tolineno + if self.finalbody: + if self.finalbody[0].fromlineno - 1 == lineno: + return lineno, lineno + if self.finalbody[0].fromlineno <= lineno <= self.finalbody[-1].tolineno: + return lineno, self.finalbody[-1].tolineno + return lineno, self.tolineno + + def get_children(self): + yield from self.body + yield from self.handlers + yield from self.orelse + yield from self.finalbody + + +class Tuple(BaseContainer): + """Class representing an :class:`ast.Tuple` node. + + >>> import astroid + >>> node = astroid.extract_node('(1, 2, 3)') + >>> node + + """ + + _other_fields = ("ctx",) + + def __init__( + self, + ctx: Context | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param ctx: Whether the tuple is assigned to or loaded from. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.ctx: Context | None = ctx + """Whether the tuple is assigned to or loaded from.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + assigned_stmts: ClassVar[AssignedStmtsCall[Tuple]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + infer_unary_op: ClassVar[InferUnaryOp[Tuple]] + infer_binary_op: ClassVar[InferBinaryOp[Tuple]] + + def pytype(self) -> Literal["builtins.tuple"]: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + return "builtins.tuple" + + def getitem(self, index, context: InferenceContext | None = None): + """Get an item from this node. + + :param index: The node to use as a subscript index. + :type index: Const or Slice + """ + return _container_getitem(self, self.elts, index, context=context) + + +class UnaryOp(NodeNG): + """Class representing an :class:`ast.UnaryOp` node. + + >>> import astroid + >>> node = astroid.extract_node('-5') + >>> node + + """ + + _astroid_fields = ("operand",) + _other_fields = ("op",) + + @decorators.deprecate_default_argument_values(op="str") + def __init__( + self, + op: str | None = None, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param op: The operator. + + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.op: str | None = op + """The operator.""" + + self.operand: NodeNG | None = None + """What the unary operator is applied to.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, operand: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param operand: What the unary operator is applied to. + """ + self.operand = operand + + # This is set by inference.py + _infer_unaryop: ClassVar[ + InferBinaryOperation[UnaryOp, util.BadUnaryOperationMessage] + ] + + def type_errors(self, context: InferenceContext | None = None): + """Get a list of type errors which can occur during inference. + + Each TypeError is represented by a :class:`BadBinaryOperationMessage`, + which holds the original exception. + + :returns: The list of possible type errors. + :rtype: list(BadBinaryOperationMessage) + """ + try: + results = self._infer_unaryop(context=context) + return [ + result + for result in results + if isinstance(result, util.BadUnaryOperationMessage) + ] + except InferenceError: + return [] + + def get_children(self): + yield self.operand + + def op_precedence(self): + if self.op == "not": + return OP_PRECEDENCE[self.op] + + return super().op_precedence() + + +class While(_base_nodes.MultiLineWithElseBlockNode, _base_nodes.Statement): + """Class representing an :class:`ast.While` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + while condition(): + print("True") + ''') + >>> node + + """ + + _astroid_fields = ("test", "body", "orelse") + _multi_line_block_fields = ("body", "orelse") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.test: NodeNG | None = None + """The condition that the loop tests.""" + + self.body: list[NodeNG] = [] + """The contents of the loop.""" + + self.orelse: list[NodeNG] = [] + """The contents of the ``else`` block.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + test: NodeNG | None = None, + body: list[NodeNG] | None = None, + orelse: list[NodeNG] | None = None, + ) -> None: + """Do some setup after initialisation. + + :param test: The condition that the loop tests. + + :param body: The contents of the loop. + + :param orelse: The contents of the ``else`` block. + """ + self.test = test + if body is not None: + self.body = body + if orelse is not None: + self.orelse = orelse + + @cached_property + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.test.tolineno + + def block_range(self, lineno): + """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 + + def _get_yield_nodes_skip_lambdas(self): + """A While node can contain a Yield node in the test""" + yield from self.test._get_yield_nodes_skip_lambdas() + yield from super()._get_yield_nodes_skip_lambdas() + + +class With( + _base_nodes.MultiLineWithElseBlockNode, + _base_nodes.AssignTypeNode, + _base_nodes.Statement, +): + """Class representing an :class:`ast.With` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + with open(file_path) as file_: + print(file_.read()) + ''') + >>> node + + """ + + _astroid_fields = ("items", "body") + _other_other_fields = ("type_annotation",) + _multi_line_block_fields = ("body",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.items: list[tuple[NodeNG, NodeNG | None]] = [] + """The pairs of context managers and the names they are assigned to.""" + + self.body: list[NodeNG] = [] + """The contents of the ``with`` block.""" + + self.type_annotation: NodeNG | None = None # can be None + """If present, this will contain the type annotation passed by a type comment""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + items: list[tuple[NodeNG, NodeNG | None]] | None = None, + body: list[NodeNG] | None = None, + type_annotation: NodeNG | None = None, + ) -> None: + """Do some setup after initialisation. + + :param items: The pairs of context managers and the names + they are assigned to. + + :param body: The contents of the ``with`` block. + """ + if items is not None: + self.items = items + if body is not None: + self.body = body + self.type_annotation = type_annotation + + assigned_stmts: ClassVar[AssignedStmtsCall[With]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + @cached_property + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.items[-1][0].tolineno + + def get_children(self): + """Get the child nodes below this node. + + :returns: The children. + :rtype: iterable(NodeNG) + """ + for expr, var in self.items: + yield expr + if var: + yield var + yield from self.body + + +class AsyncWith(With): + """Asynchronous ``with`` built with the ``async`` keyword.""" + + +class Yield(NodeNG): + """Class representing an :class:`ast.Yield` node. + + >>> import astroid + >>> node = astroid.extract_node('yield True') + >>> node + + """ + + _astroid_fields = ("value",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.value: NodeNG | None = None # can be None + """The value to yield.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, value: NodeNG | None = None) -> None: + """Do some setup after initialisation. + + :param value: The value to yield. + """ + 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): # TODO value is required, not optional + """Class representing an :class:`ast.YieldFrom` node.""" + + +class DictUnpack(_base_nodes.NoChildrenNode): + """Represents the unpacking of dicts into dicts using :pep:`448`.""" + + +class FormattedValue(NodeNG): + """Class representing an :class:`ast.FormattedValue` node. + + Represents a :pep:`498` format string. + + >>> import astroid + >>> node = astroid.extract_node('f"Format {type_}"') + >>> node + + >>> node.values + [, ] + """ + + _astroid_fields = ("value", "format_spec") + _other_fields = ("conversion",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.value: NodeNG + """The value to be formatted into the string.""" + + self.conversion: int + """The type of formatting to be applied to the value. + + .. seealso:: + :class:`ast.FormattedValue` + """ + + self.format_spec: JoinedStr | None = None + """The formatting to be applied to the value. + + .. seealso:: + :class:`ast.FormattedValue` + """ + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + *, + value: NodeNG, + conversion: int, + format_spec: JoinedStr | None = None, + ) -> None: + """Do some setup after initialisation. + + :param value: The value to be formatted into the string. + + :param conversion: The type of formatting to be applied to the value. + + :param format_spec: The formatting to be applied to the value. + :type format_spec: JoinedStr or None + """ + self.value = value + self.conversion = conversion + self.format_spec = format_spec + + def get_children(self): + yield self.value + + if self.format_spec is not None: + yield self.format_spec + + +class JoinedStr(NodeNG): + """Represents a list of string expressions to be joined. + + >>> import astroid + >>> node = astroid.extract_node('f"Format {type_}"') + >>> node + + """ + + _astroid_fields = ("values",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.values: list[NodeNG] = [] + """The string expressions to be joined. + + :type: list(FormattedValue or Const) + """ + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, values: list[NodeNG] | None = None) -> None: + """Do some setup after initialisation. + + :param value: The string expressions to be joined. + + :type: list(FormattedValue or Const) + """ + if values is not None: + self.values = values + + def get_children(self): + yield from self.values + + +class NamedExpr(_base_nodes.AssignTypeNode): + """Represents the assignment from the assignment expression + + >>> import astroid + >>> module = astroid.parse('if a := 1: pass') + >>> module.body[0].test + + """ + + _astroid_fields = ("target", "value") + + optional_assign = True + """Whether this node optionally assigns a variable. + + Since NamedExpr are not always called they do not always assign.""" + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.target: NodeNG + """The assignment target + + :type: Name + """ + + self.value: NodeNG + """The value that gets assigned in the expression""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, target: NodeNG, value: NodeNG) -> None: + self.target = target + self.value = value + + assigned_stmts: ClassVar[AssignedStmtsCall[NamedExpr]] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + def frame( + self, *, future: Literal[None, True] = None + ) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda: + """The first parent frame node. + + A frame node is a :class:`Module`, :class:`FunctionDef`, + or :class:`ClassDef`. + + :returns: The first parent frame node. + """ + if not self.parent: + raise ParentMissingError(target=self) + + # For certain parents NamedExpr evaluate to the scope of the parent + if isinstance(self.parent, (Arguments, Keyword, Comprehension)): + if not self.parent.parent: + raise ParentMissingError(target=self.parent) + if not self.parent.parent.parent: + raise ParentMissingError(target=self.parent.parent) + return self.parent.parent.parent.frame(future=True) + + return self.parent.frame(future=True) + + def scope(self) -> LocalsDictNodeNG: + """The first parent node defining a new scope. + These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes. + + :returns: The first parent scope node. + """ + if not self.parent: + raise ParentMissingError(target=self) + + # For certain parents NamedExpr evaluate to the scope of the parent + if isinstance(self.parent, (Arguments, Keyword, Comprehension)): + if not self.parent.parent: + raise ParentMissingError(target=self.parent) + if not self.parent.parent.parent: + raise ParentMissingError(target=self.parent.parent) + return self.parent.parent.parent.scope() + + return self.parent.scope() + + def set_local(self, name: str, stmt: NodeNG) -> None: + """Define that the given name is declared in the given statement node. + NamedExpr's in Arguments, Keyword or Comprehension are evaluated in their + parent's parent scope. So we add to their frame's locals. + + .. seealso:: :meth:`scope` + + :param name: The name that is being defined. + + :param stmt: The statement that defines the given name. + """ + self.frame(future=True).set_local(name, stmt) + + +class Unknown(_base_nodes.AssignTypeNode): + """This node represents a node in a constructed AST where + introspection is not possible. At the moment, it's only used in + the args attribute of FunctionDef nodes where function signature + introspection failed. + """ + + name = "Unknown" + + def qname(self) -> Literal["Unknown"]: + return "Unknown" + + def _infer(self, context: InferenceContext | None = None, **kwargs): + """Inference on an Unknown node immediately terminates.""" + yield util.Uninferable + + +class EvaluatedObject(NodeNG): + """Contains an object that has already been inferred + + This class is useful to pre-evaluate a particular node, + with the resulting class acting as the non-evaluated node. + """ + + name = "EvaluatedObject" + _astroid_fields = ("original",) + _other_fields = ("value",) + + def __init__(self, original: NodeNG, value: NodeNG | util.UninferableBase) -> None: + self.original: NodeNG = original + """The original node that has already been evaluated""" + + self.value: NodeNG | util.UninferableBase = value + """The inferred value""" + + super().__init__( + lineno=self.original.lineno, + col_offset=self.original.col_offset, + parent=self.original.parent, + ) + + def _infer( + self, context: InferenceContext | None = None, **kwargs: Any + ) -> Generator[NodeNG | util.UninferableBase, None, None]: + yield self.value + + +# Pattern matching ####################################################### + + +class Match(_base_nodes.Statement, _base_nodes.MultiLineBlockNode): + """Class representing a :class:`ast.Match` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case 200: + ... + case _: + ... + ''') + >>> node + + """ + + _astroid_fields = ("subject", "cases") + _multi_line_block_fields = ("cases",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + self.subject: NodeNG + self.cases: list[MatchCase] + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + *, + subject: NodeNG, + cases: list[MatchCase], + ) -> None: + self.subject = subject + self.cases = cases + + +class Pattern(NodeNG): + """Base class for all Pattern nodes.""" + + +class MatchCase(_base_nodes.MultiLineBlockNode): + """Class representing a :class:`ast.match_case` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case 200: + ... + ''') + >>> node.cases[0] + + """ + + _astroid_fields = ("pattern", "guard", "body") + _multi_line_block_fields = ("body",) + + lineno: None + col_offset: None + end_lineno: None + end_col_offset: None + + def __init__(self, *, parent: NodeNG | None = None) -> None: + self.pattern: Pattern + self.guard: NodeNG | None + self.body: list[NodeNG] + super().__init__(parent=parent) + + def postinit( + self, + *, + pattern: Pattern, + guard: NodeNG | None, + body: list[NodeNG], + ) -> None: + self.pattern = pattern + self.guard = guard + self.body = body + + +class MatchValue(Pattern): + """Class representing a :class:`ast.MatchValue` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case 200: + ... + ''') + >>> node.cases[0].pattern + + """ + + _astroid_fields = ("value",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + self.value: NodeNG + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, *, value: NodeNG) -> None: + self.value = value + + +class MatchSingleton(Pattern): + """Class representing a :class:`ast.MatchSingleton` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case True: + ... + case False: + ... + case None: + ... + ''') + >>> node.cases[0].pattern + + >>> node.cases[1].pattern + + >>> node.cases[2].pattern + + """ + + _other_fields = ("value",) + + def __init__( + self, + *, + value: Literal[True, False, None], + lineno: int | None = None, + col_offset: int | None = None, + end_lineno: int | None = None, + end_col_offset: int | None = None, + parent: NodeNG | None = None, + ) -> None: + self.value = value + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + +class MatchSequence(Pattern): + """Class representing a :class:`ast.MatchSequence` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case [1, 2]: + ... + case (1, 2, *_): + ... + ''') + >>> node.cases[0].pattern + + >>> node.cases[1].pattern + + """ + + _astroid_fields = ("patterns",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + self.patterns: list[Pattern] + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, *, patterns: list[Pattern]) -> None: + self.patterns = patterns + + +class MatchMapping(_base_nodes.AssignTypeNode, Pattern): + """Class representing a :class:`ast.MatchMapping` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case {1: "Hello", 2: "World", 3: _, **rest}: + ... + ''') + >>> node.cases[0].pattern + + """ + + _astroid_fields = ("keys", "patterns", "rest") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + self.keys: list[NodeNG] + self.patterns: list[Pattern] + self.rest: AssignName | None + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + *, + keys: list[NodeNG], + patterns: list[Pattern], + rest: AssignName | None, + ) -> None: + self.keys = keys + self.patterns = patterns + self.rest = rest + + assigned_stmts: ClassVar[ + Callable[ + [ + MatchMapping, + AssignName, + InferenceContext | None, + None, + ], + Generator[NodeNG, None, None], + ] + ] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + +class MatchClass(Pattern): + """Class representing a :class:`ast.MatchClass` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case Point2D(0, 0): + ... + case Point3D(x=0, y=0, z=0): + ... + ''') + >>> node.cases[0].pattern + + >>> node.cases[1].pattern + + """ + + _astroid_fields = ("cls", "patterns", "kwd_patterns") + _other_fields = ("kwd_attrs",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + self.cls: NodeNG + self.patterns: list[Pattern] + self.kwd_attrs: list[str] + self.kwd_patterns: list[Pattern] + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + *, + cls: NodeNG, + patterns: list[Pattern], + kwd_attrs: list[str], + kwd_patterns: list[Pattern], + ) -> None: + self.cls = cls + self.patterns = patterns + self.kwd_attrs = kwd_attrs + self.kwd_patterns = kwd_patterns + + +class MatchStar(_base_nodes.AssignTypeNode, Pattern): + """Class representing a :class:`ast.MatchStar` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case [1, *_]: + ... + ''') + >>> node.cases[0].pattern.patterns[1] + + """ + + _astroid_fields = ("name",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + self.name: AssignName | None + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, *, name: AssignName | None) -> None: + self.name = name + + assigned_stmts: ClassVar[ + Callable[ + [ + MatchStar, + AssignName, + InferenceContext | None, + None, + ], + Generator[NodeNG, None, None], + ] + ] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + +class MatchAs(_base_nodes.AssignTypeNode, Pattern): + """Class representing a :class:`ast.MatchAs` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case [1, a]: + ... + case {'key': b}: + ... + case Point2D(0, 0) as c: + ... + case d: + ... + ''') + >>> node.cases[0].pattern.patterns[1] + + >>> node.cases[1].pattern.patterns[0] + + >>> node.cases[2].pattern + + >>> node.cases[3].pattern + + """ + + _astroid_fields = ("pattern", "name") + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + self.pattern: Pattern | None + self.name: AssignName | None + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + *, + pattern: Pattern | None, + name: AssignName | None, + ) -> None: + self.pattern = pattern + self.name = name + + assigned_stmts: ClassVar[ + Callable[ + [ + MatchAs, + AssignName, + InferenceContext | None, + None, + ], + Generator[NodeNG, None, None], + ] + ] + """Returns the assigned statement (non inferred) according to the assignment type. + See astroid/protocols.py for actual implementation. + """ + + +class MatchOr(Pattern): + """Class representing a :class:`ast.MatchOr` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + match x: + case 400 | 401 | 402: + ... + ''') + >>> node.cases[0].pattern + + """ + + _astroid_fields = ("patterns",) + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + self.patterns: list[Pattern] + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, *, patterns: list[Pattern]) -> None: + self.patterns = patterns + + +# constants ############################################################## + +# The _proxied attribute of all container types (List, Tuple, etc.) +# are set during bootstrapping by _astroid_bootstrapping(). +CONST_CLS: dict[type, type[NodeNG]] = { + list: List, + tuple: Tuple, + dict: Dict, + set: Set, + type(None): Const, + type(NotImplemented): Const, + type(...): Const, + bool: Const, + int: Const, + float: Const, + complex: Const, + str: Const, + bytes: Const, +} + + +def _create_basic_elements( + value: Iterable[Any], node: List | Set | Tuple +) -> list[NodeNG]: + """Create a list of nodes to function as the elements of a new node.""" + elements: list[NodeNG] = [] + for element in value: + element_node = const_factory(element) + element_node.parent = node + elements.append(element_node) + return elements + + +def _create_dict_items( + values: Mapping[Any, Any], node: Dict +) -> list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]]: + """Create a list of node pairs to function as the items of a new dict node.""" + elements: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] = [] + for key, value in values.items(): + key_node = const_factory(key) + key_node.parent = node + value_node = const_factory(value) + value_node.parent = node + elements.append((key_node, value_node)) + return elements + + +def const_factory(value: Any) -> ConstFactoryResult: + """Return an astroid node for a python value.""" + assert not isinstance(value, NodeNG) + + # This only handles instances of the CONST types. Any + # subclasses get inferred as EmptyNode. + # TODO: See if we should revisit these with the normal builder. + if value.__class__ not in CONST_CLS: + node = EmptyNode() + node.object = value + return node + + instance: List | Set | Tuple | Dict + initializer_cls = CONST_CLS[value.__class__] + if issubclass(initializer_cls, (List, Set, Tuple)): + instance = initializer_cls() + instance.postinit(_create_basic_elements(value, instance)) + return instance + if issubclass(initializer_cls, Dict): + instance = initializer_cls() + instance.postinit(_create_dict_items(value, instance)) + return instance + return Const(value) diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py b/venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py new file mode 100644 index 00000000..617f8ba4 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/node_ng.py @@ -0,0 +1,814 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +from __future__ import annotations + +import pprint +import sys +import warnings +from collections.abc import Generator, Iterator +from functools import singledispatch as _singledispatch +from typing import ( + TYPE_CHECKING, + Any, + ClassVar, + Tuple, + Type, + TypeVar, + Union, + cast, + overload, +) + +from astroid import decorators, util +from astroid.context import InferenceContext +from astroid.exceptions import ( + AstroidError, + InferenceError, + ParentMissingError, + StatementMissing, + UseInferenceDefault, +) +from astroid.manager import AstroidManager +from astroid.nodes.as_string import AsStringVisitor +from astroid.nodes.const import OP_PRECEDENCE +from astroid.nodes.utils import Position +from astroid.typing import InferenceErrorInfo, InferenceResult, InferFn + +if TYPE_CHECKING: + from astroid import nodes + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + +if sys.version_info >= (3, 8): + from functools import cached_property +else: + from astroid.decorators import cachedproperty as cached_property + +# Types for 'NodeNG.nodes_of_class()' +_NodesT = TypeVar("_NodesT", bound="NodeNG") +_NodesT2 = TypeVar("_NodesT2", bound="NodeNG") +_NodesT3 = TypeVar("_NodesT3", bound="NodeNG") +SkipKlassT = Union[None, Type["NodeNG"], Tuple[Type["NodeNG"], ...]] + + +class NodeNG: + """A node of the new Abstract Syntax Tree (AST). + + This is the base class for all Astroid node classes. + """ + + is_statement: ClassVar[bool] = False + """Whether this node indicates a statement.""" + optional_assign: ClassVar[ + bool + ] = False # True for For (and for Comprehension if py <3.0) + """Whether this node optionally assigns a variable. + + This is for loop assignments because loop won't necessarily perform an + assignment if the loop has no iterations. + This is also the case from comprehensions in Python 2. + """ + is_function: ClassVar[bool] = False # True for FunctionDef nodes + """Whether this node indicates a function.""" + is_lambda: ClassVar[bool] = False + + # Attributes below are set by the builder module or by raw factories + _astroid_fields: ClassVar[tuple[str, ...]] = () + """Node attributes that contain child nodes. + + This is redefined in most concrete classes. + """ + _other_fields: ClassVar[tuple[str, ...]] = () + """Node attributes that do not contain child nodes.""" + _other_other_fields: ClassVar[tuple[str, ...]] = () + """Attributes that contain AST-dependent fields.""" + # instance specific inference function infer(node, context) + _explicit_inference: InferFn | None = None + + def __init__( + self, + lineno: int | None = None, + col_offset: int | None = None, + parent: NodeNG | None = None, + *, + end_lineno: int | None = None, + end_col_offset: int | None = None, + ) -> None: + """ + :param lineno: The line that this node appears on in the source code. + + :param col_offset: The column that this node appears on in the + source code. + + :param parent: The parent node in the syntax tree. + + :param end_lineno: The last line this node appears on in the source code. + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + """ + self.lineno: int | None = lineno + """The line that this node appears on in the source code.""" + + self.col_offset: int | None = col_offset + """The column that this node appears on in the source code.""" + + self.parent: NodeNG | None = parent + """The parent node in the syntax tree.""" + + self.end_lineno: int | None = end_lineno + """The last line this node appears on in the source code.""" + + self.end_col_offset: int | None = end_col_offset + """The end column this node appears on in the source code. + + Note: This is after the last symbol. + """ + + self.position: Position | None = None + """Position of keyword(s) and name. + + Used as fallback for block nodes which might not provide good + enough positional information. E.g. ClassDef, FunctionDef. + """ + + def infer( + self, context: InferenceContext | None = None, **kwargs: Any + ) -> Generator[InferenceResult, None, None]: + """Get a generator of the inferred values. + + This is the main entry point to the inference system. + + .. seealso:: :ref:`inference` + + If the instance has some explicit inference function set, it will be + called instead of the default interface. + + :returns: The inferred values. + :rtype: iterable + """ + if context is 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 + results = list(self._explicit_inference(self, context, **kwargs)) + if context is not None: + context.nodes_inferred += len(results) + yield from results + return + except UseInferenceDefault: + pass + + if not context: + # nodes_inferred? + yield from self._infer(context=context, **kwargs) + return + + key = (self, context.lookupname, context.callcontext, context.boundnode) + if key in context.inferred: + yield from context.inferred[key] + return + + results = [] + + # Limit inference amount to help with performance issues with + # exponentially exploding possible results. + limit = AstroidManager.max_inferable_values + for i, result in enumerate(self._infer(context=context, **kwargs)): + if i >= limit or (context.nodes_inferred > context.max_inferred): + results.append(util.Uninferable) + yield util.Uninferable + break + results.append(result) + yield result + context.nodes_inferred += 1 + + # Cache generated results for subsequent inferences of the + # same node using the same context + context.inferred[key] = tuple(results) + return + + def _repr_name(self) -> str: + """Get a name for nice representation. + + This is either :attr:`name`, :attr:`attrname`, or the empty string. + + :returns: The nice name. + :rtype: str + """ + if all(name not in self._astroid_fields for name in ("name", "attrname")): + return getattr(self, "name", "") or getattr(self, "attrname", "") + return "" + + def __str__(self) -> str: + rname = self._repr_name() + cname = type(self).__name__ + if rname: + string = "%(cname)s.%(rname)s(%(fields)s)" + alignment = len(cname) + len(rname) + 2 + else: + string = "%(cname)s(%(fields)s)" + alignment = len(cname) + 1 + result = [] + for field in self._other_fields + self._astroid_fields: + value = getattr(self, field) + width = 80 - len(field) - alignment + lines = pprint.pformat(value, indent=2, width=width).splitlines(True) + + inner = [lines[0]] + for line in lines[1:]: + inner.append(" " * alignment + line) + result.append(f"{field}={''.join(inner)}") + + return string % { + "cname": cname, + "rname": rname, + "fields": (",\n" + " " * alignment).join(result), + } + + def __repr__(self) -> str: + rname = self._repr_name() + 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) -> Iterator[NodeNG]: + """Get the child nodes below this node.""" + for field in self._astroid_fields: + attr = getattr(self, field) + if attr is None: + continue + if isinstance(attr, (list, tuple)): + yield from attr + else: + yield attr + yield from () + + def last_child(self) -> NodeNG | None: + """An optimized version of list(get_children())[-1].""" + for field in self._astroid_fields[::-1]: + attr = getattr(self, field) + if not attr: # None or empty list / tuple + continue + if isinstance(attr, (list, tuple)): + return attr[-1] + return attr + return None + + def node_ancestors(self) -> Iterator[NodeNG]: + """Yield parent, grandparent, etc until there are no more.""" + parent = self.parent + while parent is not None: + yield parent + parent = parent.parent + + def parent_of(self, node) -> bool: + """Check if this node is the parent of the given node. + + :param node: The node to check if it is the child. + :type node: NodeNG + + :returns: Whether this node is the parent of the given node. + """ + return any(self is parent for parent in node.node_ancestors()) + + @overload + def statement(self, *, future: None = ...) -> nodes.Statement | nodes.Module: + ... + + @overload + def statement(self, *, future: Literal[True]) -> nodes.Statement: + ... + + def statement( + self, *, future: Literal[None, True] = None + ) -> nodes.Statement | nodes.Module: + """The first parent node, including self, marked as statement node. + + TODO: Deprecate the future parameter and only raise StatementMissing and return + nodes.Statement + + :raises AttributeError: If self has no parent attribute + :raises StatementMissing: If self has no parent attribute and future is True + """ + if self.is_statement: + return cast("nodes.Statement", self) + if not self.parent: + if future: + raise StatementMissing(target=self) + warnings.warn( + "In astroid 3.0.0 NodeNG.statement() will return either a nodes.Statement " + "or raise a StatementMissing exception. AttributeError will no longer be raised. " + "This behaviour can already be triggered " + "by passing 'future=True' to a statement() call.", + DeprecationWarning, + stacklevel=2, + ) + raise AttributeError(f"{self} object has no attribute 'parent'") + return self.parent.statement(future=future) + + def frame( + self, *, future: Literal[None, True] = None + ) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda: + """The first parent frame node. + + A frame node is a :class:`Module`, :class:`FunctionDef`, + :class:`ClassDef` or :class:`Lambda`. + + :returns: The first parent frame node. + """ + if self.parent is None: + if future: + raise ParentMissingError(target=self) + warnings.warn( + "In astroid 3.0.0 NodeNG.frame() will return either a Frame node, " + "or raise ParentMissingError. AttributeError will no longer be raised. " + "This behaviour can already be triggered " + "by passing 'future=True' to a frame() call.", + DeprecationWarning, + stacklevel=2, + ) + raise AttributeError(f"{self} object has no attribute 'parent'") + + return self.parent.frame(future=future) + + def scope(self) -> nodes.LocalsDictNodeNG: + """The first parent node defining a new scope. + + These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes. + + :returns: The first parent scope node. + """ + if not self.parent: + raise ParentMissingError(target=self) + return self.parent.scope() + + def root(self) -> nodes.Module: + """Return the root node of the syntax tree. + + :returns: The root node. + """ + if self.parent: + return self.parent.root() + return self # type: ignore[return-value] # Only 'Module' does not have a parent node. + + def child_sequence(self, child): + """Search for the sequence that contains this child. + + :param child: The child node to search sequences for. + :type child: NodeNG + + :returns: The sequence containing the given child node. + :rtype: iterable(NodeNG) + + :raises AstroidError: If no sequence could be found that contains + the given child. + """ + for field in self._astroid_fields: + node_or_sequence = getattr(self, field) + if node_or_sequence is child: + return [node_or_sequence] + # /!\ compiler.ast Nodes have an __iter__ walking over child nodes + if ( + isinstance(node_or_sequence, (tuple, list)) + and child in node_or_sequence + ): + return node_or_sequence + + msg = "Could not find %s in %s's children" + raise AstroidError(msg % (repr(child), repr(self))) + + def locate_child(self, child): + """Find the field of this node that contains the given child. + + :param child: The child node to search fields for. + :type child: NodeNG + + :returns: A tuple of the name of the field that contains the child, + and the sequence or node that contains the child node. + :rtype: tuple(str, iterable(NodeNG) or NodeNG) + + :raises AstroidError: If no field could be found that contains + the given child. + """ + for field in self._astroid_fields: + node_or_sequence = getattr(self, field) + # /!\ compiler.ast Nodes have an __iter__ walking over child nodes + if child is node_or_sequence: + return field, child + if ( + isinstance(node_or_sequence, (tuple, list)) + and child in node_or_sequence + ): + return field, node_or_sequence + msg = "Could not find %s in %s's children" + raise AstroidError(msg % (repr(child), repr(self))) + + # FIXME : should we merge child_sequence and locate_child ? locate_child + # is only used in are_exclusive, child_sequence one time in pylint. + + def next_sibling(self): + """The next sibling statement node. + + :returns: The next sibling statement node. + :rtype: NodeNG or None + """ + return self.parent.next_sibling() + + def previous_sibling(self): + """The previous sibling statement. + + :returns: The previous sibling statement node. + :rtype: NodeNG or None + """ + return self.parent.previous_sibling() + + # these are lazy because they're relatively expensive to compute for every + # single node, and they rarely get looked at + + @cached_property + def fromlineno(self) -> int | None: + """The first line that this node appears on in the source code.""" + if self.lineno is None: + return self._fixed_source_line() + return self.lineno + + @cached_property + def tolineno(self) -> int | None: + """The last line that this node appears on in the source code.""" + if self.end_lineno is not None: + return self.end_lineno + if not self._astroid_fields: + # can't have children + last_child = None + else: + last_child = self.last_child() + if last_child is None: + return self.fromlineno + return last_child.tolineno + + def _fixed_source_line(self) -> int | None: + """Attempt to find the line that this node appears on. + + We need this method since not all nodes have :attr:`lineno` set. + """ + line = self.lineno + _node = self + try: + while line is None: + _node = next(_node.get_children()) + line = _node.lineno + except StopIteration: + parent = self.parent + while parent and line is None: + line = parent.lineno + parent = parent.parent + return line + + 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: str, stmt: NodeNG) -> None: + """Define that the given name is declared in the given statement node. + + This definition is stored on the parent scope node. + + .. seealso:: :meth:`scope` + + :param name: The name that is being defined. + + :param stmt: The statement that defines the given name. + """ + assert self.parent + self.parent.set_local(name, stmt) + + @overload + def nodes_of_class( + self, + klass: type[_NodesT], + skip_klass: SkipKlassT = ..., + ) -> Iterator[_NodesT]: + ... + + @overload + def nodes_of_class( + self, + klass: tuple[type[_NodesT], type[_NodesT2]], + skip_klass: SkipKlassT = ..., + ) -> Iterator[_NodesT] | Iterator[_NodesT2]: + ... + + @overload + def nodes_of_class( + self, + klass: tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]], + skip_klass: SkipKlassT = ..., + ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]: + ... + + @overload + def nodes_of_class( + self, + klass: tuple[type[_NodesT], ...], + skip_klass: SkipKlassT = ..., + ) -> Iterator[_NodesT]: + ... + + def nodes_of_class( # type: ignore[misc] # mypy doesn't correctly recognize the overloads + self, + klass: ( + type[_NodesT] + | tuple[type[_NodesT], type[_NodesT2]] + | tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]] + | tuple[type[_NodesT], ...] + ), + skip_klass: SkipKlassT = None, + ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]: + """Get the nodes (including this one or below) of the given types. + + :param klass: The types of node to search for. + + :param skip_klass: The types of node to ignore. This is useful to ignore + subclasses of :attr:`klass`. + + :returns: The node of the given types. + """ + if isinstance(self, klass): + yield self + + if skip_klass is None: + for child_node in self.get_children(): + yield from child_node.nodes_of_class(klass, skip_klass) + + return + + for child_node in self.get_children(): + if isinstance(child_node, skip_klass): + continue + yield from child_node.nodes_of_class(klass, skip_klass) + + @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: InferenceContext | None = None, **kwargs: Any + ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]: + """We don't know how to resolve a statement by default.""" + # this method is overridden by most concrete classes + raise InferenceError( + "No inference function for {node!r}.", node=self, context=context + ) + + def inferred(self): + """Get a list of the inferred values. + + .. seealso:: :ref:`inference` + + :returns: The inferred values. + :rtype: list + """ + return list(self.infer()) + + def instantiate_class(self): + """Instantiate an instance of the defined class. + + .. note:: + + On anything other than a :class:`ClassDef` this will return self. + + :returns: An instance of the defined class. + :rtype: object + """ + return self + + def has_base(self, node) -> bool: + """Check if this node inherits from the given type. + + :param node: The node defining the base to look for. + Usually this is a :class:`Name` node. + :type node: NodeNG + """ + return False + + def callable(self) -> bool: + """Whether this node defines something that is callable. + + :returns: Whether this defines something that is callable. + """ + return False + + def eq(self, value) -> bool: + return False + + def as_string(self) -> str: + """Get the source code that this node represents.""" + return AsStringVisitor()(self) + + def repr_tree( + self, + ids=False, + include_linenos=False, + ast_state=False, + indent=" ", + max_depth=0, + max_width=80, + ) -> str: + """Get a string representation of the AST from this node. + + :param ids: If true, includes the ids with the node type names. + :type ids: bool + + :param include_linenos: If true, includes the line numbers and + column offsets. + :type include_linenos: bool + + :param ast_state: If true, includes information derived from + the whole AST like local and global variables. + :type ast_state: bool + + :param indent: A string to use to indent the output string. + :type indent: str + + :param max_depth: If set to a positive integer, won't return + nodes deeper than max_depth in the string. + :type max_depth: int + + :param max_width: Attempt to format the output string to stay + within this number of characters, but can exceed it under some + circumstances. Only positive integer values are valid, the default is 80. + :type max_width: int + + :returns: The string representation of the AST. + :rtype: str + """ + + @_singledispatch + def _repr_tree(node, result, done, cur_indent="", depth=1): + """Outputs a representation of a non-tuple/list, non-node that's + contained within an AST, including strings. + """ + lines = pprint.pformat( + node, width=max(max_width - len(cur_indent), 1) + ).splitlines(True) + result.append(lines[0]) + result.extend([cur_indent + line for line in lines[1:]]) + return len(lines) != 1 + + # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch + @_repr_tree.register(tuple) + @_repr_tree.register(list) + def _repr_seq(node, result, done, cur_indent="", depth=1): + """Outputs a representation of a sequence that's contained within an + AST. + """ + cur_indent += indent + result.append("[") + if not node: + broken = False + elif len(node) == 1: + broken = _repr_tree(node[0], result, done, cur_indent, depth) + elif len(node) == 2: + broken = _repr_tree(node[0], result, done, cur_indent, depth) + if not broken: + result.append(", ") + else: + result.append(",\n") + result.append(cur_indent) + broken = _repr_tree(node[1], result, done, cur_indent, depth) or broken + else: + result.append("\n") + result.append(cur_indent) + for child in node[:-1]: + _repr_tree(child, result, done, cur_indent, depth) + result.append(",\n") + result.append(cur_indent) + _repr_tree(node[-1], result, done, cur_indent, depth) + broken = True + result.append("]") + return broken + + # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch + @_repr_tree.register(NodeNG) + def _repr_node(node, result, done, cur_indent="", depth=1): + """Outputs a strings representation of an astroid node.""" + if node in done: + result.append( + indent + f" max_depth: + result.append("...") + return False + depth += 1 + cur_indent += indent + if ids: + result.append(f"{type(node).__name__}<0x{id(node):x}>(\n") + else: + result.append(f"{type(node).__name__}(") + fields = [] + if include_linenos: + fields.extend(("lineno", "col_offset")) + fields.extend(node._other_fields) + fields.extend(node._astroid_fields) + if ast_state: + fields.extend(node._other_other_fields) + if not fields: + broken = False + elif len(fields) == 1: + result.append(f"{fields[0]}=") + broken = _repr_tree( + getattr(node, fields[0]), result, done, cur_indent, depth + ) + else: + result.append("\n") + result.append(cur_indent) + for field in fields[:-1]: + # TODO: Remove this after removal of the 'doc' attribute + if field == "doc": + continue + result.append(f"{field}=") + _repr_tree(getattr(node, field), result, done, cur_indent, depth) + result.append(",\n") + result.append(cur_indent) + result.append(f"{fields[-1]}=") + _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, depth) + broken = True + result.append(")") + return broken + + result: list[str] = [] + _repr_tree(self, result, set()) + return "".join(result) + + def bool_value(self, context: InferenceContext | None = None): + """Determine the boolean value of this node. + + The boolean value of a node can have three + possible values: + + * False: For instance, empty data structures, + False, empty strings, instances which return + explicitly False from the __nonzero__ / __bool__ + method. + * True: Most of constructs are True by default: + classes, functions, modules etc + * Uninferable: The inference engine is uncertain of the + node's value. + + :returns: The boolean value of this node. + :rtype: bool or Uninferable + """ + return util.Uninferable + + def op_precedence(self): + # Look up by class name or default to highest precedence + return OP_PRECEDENCE.get(self.__class__.__name__, len(OP_PRECEDENCE)) + + def op_left_associative(self) -> Literal[True]: + # Everything is left associative except `**` and IfExp + return True diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py new file mode 120000 index 00000000..ee4132b9 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/09/17/53/85efcb896ed281c273de91d22fca05a8728d2095a0d569103a4c2cf7de \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..ac8db071 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/mixin.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/mixin.cpython-310.pyc new file mode 100644 index 00000000..3566160a Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/mixin.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/scoped_nodes.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/scoped_nodes.cpython-310.pyc new file mode 100644 index 00000000..462bed47 Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/scoped_nodes.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/utils.cpython-310.pyc new file mode 100644 index 00000000..8af4c20d Binary files /dev/null and b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/__pycache__/utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py new file mode 120000 index 00000000..6a7b15b3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/mixin.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/98/dd/e8/a8aa52d8058eb6b0631c30df45e54eecec4032197f7acfa3cec9fcf49b \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py new file mode 100644 index 00000000..bec817d7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/scoped_nodes.py @@ -0,0 +1,3084 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +""" +This module contains the classes for "scoped" node, i.e. which are opening a +new local scope in the language definition : Module, ClassDef, FunctionDef (and +Lambda, GeneratorExp, DictComp and SetComp to some extent). +""" + +from __future__ import annotations + +import io +import itertools +import os +import sys +import warnings +from collections.abc import Generator, Iterator +from functools import lru_cache +from typing import TYPE_CHECKING, ClassVar, NoReturn, TypeVar, overload + +from astroid import bases +from astroid import decorators as decorators_mod +from astroid import util +from astroid.const import IS_PYPY, PY38, PY38_PLUS, PY39_PLUS, PYPY_7_3_11_PLUS +from astroid.context import ( + CallContext, + InferenceContext, + bind_context_to_node, + copy_context, +) +from astroid.exceptions import ( + AstroidBuildingError, + AstroidTypeError, + AttributeInferenceError, + DuplicateBasesError, + InconsistentMroError, + InferenceError, + MroError, + StatementMissing, + TooManyLevelsError, +) +from astroid.interpreter.dunder_lookup import lookup +from astroid.interpreter.objectmodel import ClassModel, FunctionModel, ModuleModel +from astroid.manager import AstroidManager +from astroid.nodes import Arguments, Const, NodeNG, _base_nodes, node_classes +from astroid.nodes.scoped_nodes.mixin import ComprehensionScope, LocalsDictNodeNG +from astroid.nodes.scoped_nodes.utils import builtin_lookup +from astroid.nodes.utils import Position +from astroid.typing import InferBinaryOp, InferenceResult, SuccessfulInferenceResult + +if sys.version_info >= (3, 8): + from functools import cached_property + from typing import Literal +else: + from typing_extensions import Literal + + from astroid.decorators import cachedproperty as cached_property + +if TYPE_CHECKING: + from astroid import nodes + + +ITER_METHODS = ("__iter__", "__getitem__") +EXCEPTION_BASE_CLASSES = frozenset({"Exception", "BaseException"}) +objects = util.lazy_import("objects") +BUILTIN_DESCRIPTORS = frozenset( + {"classmethod", "staticmethod", "builtins.classmethod", "builtins.staticmethod"} +) + +_T = TypeVar("_T") + + +def _c3_merge(sequences, cls, context): + """Merges MROs in *sequences* to a single MRO using the C3 algorithm. + + Adapted from http://www.python.org/download/releases/2.3/mro/. + + """ + result = [] + while True: + sequences = [s for s in sequences if s] # purge empty sequences + if not sequences: + return result + for s1 in sequences: # find merge candidates among seq heads + candidate = s1[0] + for s2 in sequences: + if candidate in s2[1:]: + candidate = None + break # reject the current head, it appears later + else: + break + if not candidate: + # Show all the remaining bases, which were considered as + # candidates for the next mro sequence. + raise InconsistentMroError( + message="Cannot create a consistent method resolution order " + "for MROs {mros} of class {cls!r}.", + mros=sequences, + cls=cls, + context=context, + ) + + result.append(candidate) + # remove the chosen candidate + for seq in sequences: + if seq[0] == candidate: + del seq[0] + return None + + +def clean_typing_generic_mro(sequences: list[list[ClassDef]]) -> None: + """A class can inherit from typing.Generic directly, as base, + and as base of bases. The merged MRO must however only contain the last entry. + To prepare for _c3_merge, remove some typing.Generic entries from + sequences if multiple are present. + + This method will check if Generic is in inferred_bases and also + part of bases_mro. If true, remove it from inferred_bases + as well as its entry the bases_mro. + + Format sequences: [[self]] + bases_mro + [inferred_bases] + """ + bases_mro = sequences[1:-1] + inferred_bases = sequences[-1] + # Check if Generic is part of inferred_bases + for i, base in enumerate(inferred_bases): + if base.qname() == "typing.Generic": + position_in_inferred_bases = i + break + else: + return + # Check if also part of bases_mro + # Ignore entry for typing.Generic + for i, seq in enumerate(bases_mro): + if i == position_in_inferred_bases: + continue + if any(base.qname() == "typing.Generic" for base in seq): + break + else: + return + # Found multiple Generics in mro, remove entry from inferred_bases + # and the corresponding one from bases_mro + inferred_bases.pop(position_in_inferred_bases) + bases_mro.pop(position_in_inferred_bases) + + +def clean_duplicates_mro(sequences, 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 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 == "property": + return n + if n.type != "staticmethod": + return bases.UnboundMethod(n) + return n + + +class Module(LocalsDictNodeNG): + """Class representing an :class:`ast.Module` node. + + >>> import astroid + >>> node = astroid.extract_node('import astroid') + >>> node + + >>> node.parent + + """ + + _astroid_fields = ("doc_node", "body") + + fromlineno: Literal[0] = 0 + """The first line that this node appears on in the source code.""" + + lineno: Literal[0] = 0 + """The line that this node appears on in the source code.""" + + # attributes below are set by the builder module or by raw factories + + file_bytes: str | bytes | None = None + """The string/bytes that this ast was built from.""" + + file_encoding: str | None = None + """The encoding of the source file. + + This is used to get unicode out of a source file. + Python 2 only. + """ + + special_attributes = ModuleModel() + """The names of special attributes that this module has.""" + + # names of module attributes available through the global scope + scope_attrs = {"__name__", "__doc__", "__file__", "__path__", "__package__"} + """The names of module attributes available through the global scope.""" + + _other_fields = ( + "name", + "doc", + "file", + "path", + "package", + "pure_python", + "future_imports", + ) + _other_other_fields = ("locals", "globals") + + col_offset: None + end_lineno: None + end_col_offset: None + parent: None + + @decorators_mod.deprecate_arguments(doc="Use the postinit arg 'doc_node' instead") + def __init__( + self, + name: str, + doc: str | None = None, + file: str | None = None, + path: list[str] | None = None, + package: bool | None = None, + parent: None = None, + pure_python: bool | None = True, + ) -> None: + """ + :param name: The name of the module. + + :param doc: The module docstring. + + :param file: The path to the file that this ast has been extracted from. + + :param path: + + :param package: Whether the node represents a package or a module. + + :param parent: The parent node in the syntax tree. + + :param pure_python: Whether the ast was built from source. + """ + self.name = name + """The name of the module.""" + + self._doc = doc + """The module docstring.""" + + self.file = file + """The path to the file that this ast has been extracted from. + + This will be ``None`` when the representation has been built from a + built-in module. + """ + + self.path = path + + self.package = package + """Whether the node represents a package or a module.""" + + self.pure_python = pure_python + """Whether the ast was built from source.""" + + self.globals: dict[str, list[node_classes.NodeNG]] + """A map of the name of a global variable to the node defining the global.""" + + self.locals = self.globals = {} + """A map of the name of a local variable to the node defining the local.""" + + self.body: list[node_classes.NodeNG] | None = [] + """The contents of the module.""" + + self.doc_node: Const | None = None + """The doc node associated with this node.""" + + self.future_imports: set[str] = set() + """The imports from ``__future__``.""" + + super().__init__(lineno=0, parent=parent) + + # pylint: enable=redefined-builtin + + def postinit(self, body=None, *, doc_node: Const | None = None): + """Do some setup after initialisation. + + :param body: The contents of the module. + :type body: list(NodeNG) or None + :param doc_node: The doc node associated with this node. + """ + self.body = body + self.doc_node = doc_node + if doc_node: + self._doc = doc_node.value + + @property + def doc(self) -> str | None: + """The module docstring.""" + warnings.warn( + "The 'Module.doc' attribute is deprecated, " + "use 'Module.doc_node' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self._doc + + @doc.setter + def doc(self, value: str | None) -> None: + warnings.warn( + "Setting the 'Module.doc' attribute is deprecated, " + "use 'Module.doc_node' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._doc = value + + def _get_stream(self): + if self.file_bytes is not None: + return io.BytesIO(self.file_bytes) + if self.file is not None: + # pylint: disable=consider-using-with + stream = open(self.file, "rb") + return stream + return None + + def stream(self): + """Get a stream to the underlying file or bytes. + + :type: file or io.BytesIO or None + """ + return self._get_stream() + + def block_range(self, lineno): + """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 AttributeInferenceError: + return self, () + return self._scope_lookup(node, name, offset) + + def pytype(self) -> Literal["builtins.module"]: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + return "builtins.module" + + def display_type(self) -> str: + """A human readable type of this node. + + :returns: The type of this node. + :rtype: str + """ + return "Module" + + def getattr( + self, name, context: InferenceContext | None = None, ignore_locals=False + ): + if not name: + raise AttributeInferenceError(target=self, attribute=name, context=context) + + result = [] + name_in_locals = name in self.locals + + if name in self.special_attributes and not ignore_locals and not name_in_locals: + result = [self.special_attributes.lookup(name)] + elif not ignore_locals and name_in_locals: + result = self.locals[name] + elif self.package: + try: + result = [self.import_module(name, relative_only=True)] + except (AstroidBuildingError, SyntaxError) as exc: + raise AttributeInferenceError( + target=self, attribute=name, context=context + ) from exc + result = [n for n in result if not isinstance(n, node_classes.DelName)] + if result: + return result + raise AttributeInferenceError(target=self, attribute=name, context=context) + + def igetattr(self, name, context: InferenceContext | None = 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 = copy_context(context) + context.lookupname = name + try: + return bases._infer_stmts(self.getattr(name, context), context, frame=self) + except AttributeInferenceError as error: + raise InferenceError( + str(error), target=self, attribute=name, context=context + ) from error + + def fully_defined(self) -> bool: + """Check if this module has been build from a .py file. + + If so, the module contains a complete representation, + including the code. + + :returns: Whether the module has been built from a .py file. + """ + return self.file is not None and self.file.endswith(".py") + + @overload + def statement(self, *, future: None = ...) -> Module: + ... + + @overload + def statement(self, *, future: Literal[True]) -> NoReturn: + ... + + def statement(self, *, future: Literal[None, True] = None) -> Module | NoReturn: + """The first parent node, including self, marked as statement node. + + When called on a :class:`Module` with the future parameter this raises an error. + + TODO: Deprecate the future parameter and only raise StatementMissing + + :raises StatementMissing: If no self has no parent attribute and future is True + """ + if future: + raise StatementMissing(target=self) + warnings.warn( + "In astroid 3.0.0 NodeNG.statement() will return either a nodes.Statement " + "or raise a StatementMissing exception. nodes.Module will no longer be " + "considered a statement. This behaviour can already be triggered " + "by passing 'future=True' to a statement() call.", + DeprecationWarning, + stacklevel=2, + ) + 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) -> bool: + """Whether :pep:`328` absolute import behaviour has been enabled. + + :returns: Whether :pep:`328` has been enabled. + """ + return self._absolute_import_activated + + def import_module( + self, + modname: str | None, + relative_only: bool = False, + level: int | None = None, + use_cache: bool = True, + ) -> Module: + """Get the ast for a given module as if imported from this module. + + :param modname: The name of the module to "import". + + :param relative_only: Whether to only consider relative imports. + + :param level: The level of relative import. + + :param use_cache: Whether to use the astroid_cache of modules. + + :returns: The imported module ast. + """ + if relative_only and level is None: + level = 0 + absmodname = self.relative_to_absolute_name(modname, level) + + try: + return AstroidManager().ast_from_module_name( + absmodname, use_cache=use_cache + ) + except AstroidBuildingError: + # we only want to import a sub module or package of this module, + # skip here + if relative_only: + raise + return AstroidManager().ast_from_module_name(modname) + + def relative_to_absolute_name( + self, modname: str | None, level: int | None + ) -> str | None: + """Get the absolute module name for a relative import. + + The relative import can be implicit or explicit. + + :param modname: The module name to convert. + + :param level: The level of relative import. + + :returns: The absolute module name. + + :raises TooManyLevelsError: When the relative import refers to a + module too far above this one. + """ + # XXX this returns non sens when called on an absolute import + # like 'pylint.checkers.astroid.utils' + # XXX doesn't return absolute name if self.name isn't absolute name + if self.absolute_import_activated() and level is None: + return modname + if level: + if self.package: + level = level - 1 + package_name = self.name.rsplit(".", level)[0] + elif ( + self.path + and not os.path.exists(os.path.dirname(self.path[0]) + "/__init__.py") + and os.path.exists( + os.path.dirname(self.path[0]) + "/" + modname.split(".")[0] + ) + ): + level = level - 1 + package_name = "" + else: + package_name = self.name.rsplit(".", level)[0] + if level and self.name.count(".") < level: + raise TooManyLevelsError(level=level, name=self.name) + + elif self.package: + package_name = self.name + else: + package_name = self.name.rsplit(".", 1)[0] + + if package_name: + if not modname: + return package_name + return f"{package_name}.{modname}" + return modname + + def wildcard_import_names(self): + """The list of imported names when this module is 'wildcard imported'. + + It doesn't include the '__builtins__' name which is added by the + current CPython implementation of wildcard imports. + + :returns: The list of imported names. + :rtype: list(str) + """ + # We separate the different steps of lookup in try/excepts + # to avoid catching too many Exceptions + default = [name for name in self.keys() if not name.startswith("_")] + try: + all_values = self["__all__"] + except KeyError: + return default + + try: + explicit = next(all_values.assigned_stmts()) + except (InferenceError, StopIteration): + return default + except AttributeError: + # not an assignment node + # XXX infer? + return default + + # Try our best to detect the exported name. + inferred = [] + try: + explicit = next(explicit.infer()) + except (InferenceError, StopIteration): + return default + if not isinstance(explicit, (node_classes.Tuple, node_classes.List)): + return default + + def str_const(node) -> bool: + return isinstance(node, node_classes.Const) and isinstance(node.value, str) + + for node in explicit.elts: + if str_const(node): + inferred.append(node.value) + else: + try: + inferred_node = next(node.infer()) + except (InferenceError, StopIteration): + continue + if str_const(inferred_node): + inferred.append(inferred_node.value) + return inferred + + def public_names(self): + """The list of the names that are publicly available in this module. + + :returns: The list of public names. + :rtype: list(str) + """ + return [name for name in self.keys() if not name.startswith("_")] + + def bool_value(self, context: InferenceContext | None = None) -> bool: + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`Module` this is always ``True``. + """ + return True + + def get_children(self): + yield from self.body + + def frame(self: _T, *, future: Literal[None, True] = None) -> _T: + """The node's frame node. + + A frame node is a :class:`Module`, :class:`FunctionDef`, + :class:`ClassDef` or :class:`Lambda`. + + :returns: The node itself. + """ + return self + + +class GeneratorExp(ComprehensionScope): + """Class representing an :class:`ast.GeneratorExp` node. + + >>> import astroid + >>> node = astroid.extract_node('(thing for thing in things if thing)') + >>> node + + """ + + _astroid_fields = ("elt", "generators") + _other_other_fields = ("locals",) + elt = None + """The element that forms the output of the expression. + + :type: NodeNG or None + """ + + def __init__( + self, + lineno=None, + col_offset=None, + parent=None, + *, + end_lineno=None, + end_col_offset=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 + + :param end_lineno: The last line this node appears on in the source code. + :type end_lineno: Optional[int] + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + :type end_col_offset: Optional[int] + """ + self.locals = {} + """A map of the name of a local variable to the node defining the local.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, elt=None, generators: list[nodes.Comprehension] | None = 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. + """ + self.elt = elt + if generators is None: + self.generators = [] + else: + self.generators = generators + + def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`GeneratorExp` this is always ``True``. + """ + return True + + def get_children(self): + yield self.elt + + yield from self.generators + + +class DictComp(ComprehensionScope): + """Class representing an :class:`ast.DictComp` node. + + >>> import astroid + >>> node = astroid.extract_node('{k:v for k, v in things if k > v}') + >>> node + + """ + + _astroid_fields = ("key", "value", "generators") + _other_other_fields = ("locals",) + key = None + """What produces the keys. + + :type: NodeNG or None + """ + value = None + """What produces the values. + + :type: NodeNG or None + """ + + def __init__( + self, + lineno=None, + col_offset=None, + parent=None, + *, + end_lineno=None, + end_col_offset=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 + + :param end_lineno: The last line this node appears on in the source code. + :type end_lineno: Optional[int] + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + :type end_col_offset: Optional[int] + """ + self.locals = {} + """A map of the name of a local variable to the node defining the local.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit( + self, + key=None, + value=None, + generators: list[nodes.Comprehension] | None = 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. + """ + self.key = key + self.value = value + if generators is None: + self.generators = [] + else: + self.generators = generators + + def bool_value(self, context: InferenceContext | None = None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`DictComp` this is always :class:`Uninferable`. + :rtype: Uninferable + """ + return util.Uninferable + + def get_children(self): + yield self.key + yield self.value + + yield from self.generators + + +class SetComp(ComprehensionScope): + """Class representing an :class:`ast.SetComp` node. + + >>> import astroid + >>> node = astroid.extract_node('{thing for thing in things if thing}') + >>> node + + """ + + _astroid_fields = ("elt", "generators") + _other_other_fields = ("locals",) + elt = None + """The element that forms the output of the expression. + + :type: NodeNG or None + """ + + def __init__( + self, + lineno=None, + col_offset=None, + parent=None, + *, + end_lineno=None, + end_col_offset=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 + + :param end_lineno: The last line this node appears on in the source code. + :type end_lineno: Optional[int] + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + :type end_col_offset: Optional[int] + """ + self.locals = {} + """A map of the name of a local variable to the node defining the local.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, elt=None, generators: list[nodes.Comprehension] | None = 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. + """ + self.elt = elt + if generators is None: + self.generators = [] + else: + self.generators = generators + + def bool_value(self, context: InferenceContext | None = None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`SetComp` this is always :class:`Uninferable`. + :rtype: Uninferable + """ + return util.Uninferable + + def get_children(self): + yield self.elt + + yield from self.generators + + +class ListComp(ComprehensionScope): + """Class representing an :class:`ast.ListComp` node. + + >>> import astroid + >>> node = astroid.extract_node('[thing for thing in things if thing]') + >>> node + + """ + + _astroid_fields = ("elt", "generators") + _other_other_fields = ("locals",) + + elt = None + """The element that forms the output of the expression. + + :type: NodeNG or None + """ + + def __init__( + self, + lineno=None, + col_offset=None, + parent=None, + *, + end_lineno=None, + end_col_offset=None, + ): + self.locals = {} + """A map of the name of a local variable to the node defining it.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, elt=None, generators: list[nodes.Comprehension] | None = 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, context: InferenceContext | None = None): + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`ListComp` this is always :class:`Uninferable`. + :rtype: Uninferable + """ + return util.Uninferable + + def get_children(self): + yield self.elt + + yield from self.generators + + +def _infer_decorator_callchain(node): + """Detect decorator call chaining and see if the end result is a + static or a classmethod. + """ + if not isinstance(node, FunctionDef): + return None + if not node.parent: + return None + try: + result = next(node.infer_call_result(node.parent), None) + except InferenceError: + return None + if isinstance(result, bases.Instance): + result = result._proxied + if isinstance(result, ClassDef): + if result.is_subtype_of("builtins.classmethod"): + return "classmethod" + if result.is_subtype_of("builtins.staticmethod"): + return "staticmethod" + if isinstance(result, FunctionDef): + if not result.decorators: + return None + # Determine if this function is decorated with one of the builtin descriptors we want. + for decorator in result.decorators.nodes: + if isinstance(decorator, node_classes.Name): + if decorator.name in BUILTIN_DESCRIPTORS: + return decorator.name + if ( + isinstance(decorator, node_classes.Attribute) + and isinstance(decorator.expr, node_classes.Name) + and decorator.expr.name == "builtins" + and decorator.attrname in BUILTIN_DESCRIPTORS + ): + return decorator.attrname + return None + + +class Lambda(_base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG): + """Class representing an :class:`ast.Lambda` node. + + >>> import astroid + >>> node = astroid.extract_node('lambda arg: arg + 1') + >>> node + l.1 at 0x7f23b2e41518> + """ + + _astroid_fields = ("args", "body") + _other_other_fields = ("locals",) + name = "" + is_lambda = True + special_attributes = FunctionModel() + """The names of special attributes that this function has.""" + + def implicit_parameters(self) -> Literal[0]: + return 0 + + @property + def type(self) -> Literal["method", "function"]: + """Whether this is a method or function. + + :returns: 'method' if this is a method, 'function' otherwise. + """ + if self.args.arguments and self.args.arguments[0].name == "self": + if isinstance(self.parent.scope(), ClassDef): + return "method" + return "function" + + def __init__( + self, + lineno=None, + col_offset=None, + parent=None, + *, + end_lineno=None, + end_col_offset=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 + + :param end_lineno: The last line this node appears on in the source code. + :type end_lineno: Optional[int] + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + :type end_col_offset: Optional[int] + """ + self.locals = {} + """A map of the name of a local variable to the node defining it.""" + + self.args: Arguments + """The arguments that the function takes.""" + + self.body = [] + """The contents of the function body. + + :type: list(NodeNG) + """ + + self.instance_attrs: dict[str, list[NodeNG]] = {} + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + + def postinit(self, args: Arguments, body): + """Do some setup after initialisation. + + :param args: The arguments that the function takes. + + :param body: The contents of the function body. + :type body: list(NodeNG) + """ + self.args = args + self.body = body + + def pytype(self) -> Literal["bultins.instancemethod", "builtins.function"]: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + if "method" in self.type: + return "builtins.instancemethod" + return "builtins.function" + + def display_type(self) -> str: + """A human readable type of this node. + + :returns: The type of this node. + :rtype: str + """ + if "method" in self.type: + return "Method" + return "Function" + + def callable(self) -> Literal[True]: + """Whether this node defines something that is callable. + + :returns: Whether this defines something that is callable + For a :class:`Lambda` this is always ``True``. + """ + return True + + def argnames(self) -> list[str]: + """Get the names of each of the arguments, including that + of the collections of variable-length arguments ("args", "kwargs", + etc.), as well as positional-only and keyword-only arguments. + + :returns: The names of the arguments. + :rtype: list(str) + """ + if self.args.arguments: # maybe None with builtin functions + names = _rec_get_names(self.args.arguments) + else: + names = [] + if self.args.vararg: + names.append(self.args.vararg) + names += [elt.name for elt in self.args.kwonlyargs] + if self.args.kwarg: + names.append(self.args.kwarg) + return names + + def infer_call_result(self, caller, context: InferenceContext | None = 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)) + """ + if node in self.args.defaults or node in self.args.kw_defaults: + frame = self.parent.frame(future=True) + # line offset to avoid that def func(f=func) resolve the default + # value to the defined function + offset = -1 + else: + # check this is not used in function decorators + frame = self + return frame._scope_lookup(node, name, offset) + + def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`Lambda` this is always ``True``. + """ + return True + + def get_children(self): + yield self.args + yield self.body + + def frame(self: _T, *, future: Literal[None, True] = None) -> _T: + """The node's frame node. + + A frame node is a :class:`Module`, :class:`FunctionDef`, + :class:`ClassDef` or :class:`Lambda`. + + :returns: The node itself. + """ + return self + + def getattr( + self, name: str, context: InferenceContext | None = None + ) -> list[NodeNG]: + if not name: + raise AttributeInferenceError(target=self, attribute=name, context=context) + + found_attrs = [] + if name in self.instance_attrs: + found_attrs = self.instance_attrs[name] + if name in self.special_attributes: + found_attrs.append(self.special_attributes.lookup(name)) + if found_attrs: + return found_attrs + raise AttributeInferenceError(target=self, attribute=name) + + +class FunctionDef(_base_nodes.MultiLineBlockNode, _base_nodes.Statement, Lambda): + """Class representing an :class:`ast.FunctionDef`. + + >>> import astroid + >>> node = astroid.extract_node(''' + ... def my_func(arg): + ... return arg + 1 + ... ''') + >>> node + + """ + + _astroid_fields = ("decorators", "args", "returns", "doc_node", "body") + _multi_line_block_fields = ("body",) + returns = None + decorators: node_classes.Decorators | None = None + """The decorators that are applied to this method or function.""" + + 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", "position") + _other_other_fields = ( + "locals", + "_type", + "type_comment_returns", + "type_comment_args", + ) + _type = None + + @decorators_mod.deprecate_arguments(doc="Use the postinit arg 'doc_node' instead") + def __init__( + self, + name=None, + doc: str | None = None, + lineno=None, + col_offset=None, + parent=None, + *, + end_lineno=None, + end_col_offset=None, + ): + """ + :param name: The name of the function. + :type name: str or None + + :param doc: The function docstring. + + :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 + + :param end_lineno: The last line this node appears on in the source code. + :type end_lineno: Optional[int] + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + :type end_col_offset: Optional[int] + """ + self.name = name + """The name of the function. + + :type name: str or None + """ + + self._doc = doc + """The function docstring.""" + + self.doc_node: Const | None = None + """The doc node associated with this node.""" + + self.instance_attrs = {} + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + if parent: + frame = parent.frame(future=True) + frame.set_local(name, self) + + def postinit( + self, + args: Arguments, + body, + decorators: node_classes.Decorators | None = None, + returns=None, + type_comment_returns=None, + type_comment_args=None, + *, + position: Position | None = None, + doc_node: Const | None = None, + ): + """Do some setup after initialisation. + + :param args: The arguments that the function takes. + + :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. + :params position: + Position of function keyword(s) and name. + :param doc_node: + The doc node associated with this node. + """ + self.args = args + self.body = body + self.decorators = decorators + self.returns = returns + self.type_comment_returns = type_comment_returns + self.type_comment_args = type_comment_args + self.position = position + self.doc_node = doc_node + if doc_node: + self._doc = doc_node.value + + @property + def doc(self) -> str | None: + """The function docstring.""" + warnings.warn( + "The 'FunctionDef.doc' attribute is deprecated, " + "use 'FunctionDef.doc_node' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self._doc + + @doc.setter + def doc(self, value: str | None) -> None: + warnings.warn( + "Setting the 'FunctionDef.doc' attribute is deprecated, " + "use 'FunctionDef.doc_node' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._doc = value + + @cached_property + def extra_decorators(self) -> list[node_classes.Call]: + """The extra decorators that this function can have. + + Additional decorators are considered when they are used as + assignments, as in ``method = staticmethod(method)``. + The property will return all the callables that are used for + decoration. + """ + frame = self.parent.frame(future=True) + if not isinstance(frame, ClassDef): + return [] + + decorators: list[node_classes.Call] = [] + 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(future=True) == frame + ): + decorators.append(assign.value) + return decorators + + @cached_property + def type(self) -> str: # pylint: disable=too-many-return-statements # noqa: C901 + """The function type for this node. + + Possible values are: method, function, staticmethod, classmethod. + """ + for decorator in self.extra_decorators: + if decorator.func.name in BUILTIN_DESCRIPTORS: + return decorator.func.name + + frame = self.parent.frame(future=True) + type_name = "function" + if isinstance(frame, ClassDef): + if self.name == "__new__": + return "classmethod" + if self.name == "__init_subclass__": + return "classmethod" + if self.name == "__class_getitem__": + return "classmethod" + + type_name = "method" + + if not self.decorators: + return type_name + + for node in self.decorators.nodes: + if isinstance(node, node_classes.Name): + if node.name in BUILTIN_DESCRIPTORS: + return node.name + if ( + isinstance(node, node_classes.Attribute) + and isinstance(node.expr, node_classes.Name) + and node.expr.name == "builtins" + and node.attrname in BUILTIN_DESCRIPTORS + ): + return node.attrname + + if isinstance(node, node_classes.Call): + # Handle the following case: + # @some_decorator(arg1, arg2) + # def func(...) + # + try: + current = next(node.func.infer()) + except (InferenceError, StopIteration): + continue + _type = _infer_decorator_callchain(current) + if _type is not None: + return _type + + try: + for inferred in node.infer(): + # Check to see if this returns a static or a class method. + _type = _infer_decorator_callchain(inferred) + if _type is not None: + return _type + + if not isinstance(inferred, ClassDef): + continue + for ancestor in inferred.ancestors(): + if not isinstance(ancestor, ClassDef): + continue + if ancestor.is_subtype_of("builtins.classmethod"): + return "classmethod" + if ancestor.is_subtype_of("builtins.staticmethod"): + return "staticmethod" + except InferenceError: + pass + return type_name + + @cached_property + def fromlineno(self) -> int | None: + """The first line that this node appears on in the source code.""" + # lineno is the line number of the first decorator, we want the def + # statement lineno. Similar to 'ClassDef.fromlineno' + lineno = self.lineno + if self.decorators is not None: + lineno += sum( + node.tolineno - node.lineno + 1 for node in self.decorators.nodes + ) + + return lineno + + @cached_property + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + return self.args.tolineno + + def implicit_parameters(self) -> Literal[0, 1]: + return 1 if self.is_bound() else 0 + + def block_range(self, lineno): + """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 igetattr(self, name, context: InferenceContext | None = None): + """Inferred getattr, which returns an iterator of inferred statements.""" + try: + return bases._infer_stmts(self.getattr(name, context), context, frame=self) + except AttributeInferenceError as error: + raise InferenceError( + str(error), target=self, attribute=name, context=context + ) from error + + def is_method(self) -> bool: + """Check if this function node represents a method. + + :returns: Whether this is a method. + """ + # check we are defined in a ClassDef, because this is usually expected + # (e.g. pylint...) when is_method() return True + return self.type != "function" and isinstance( + self.parent.frame(future=True), ClassDef + ) + + @decorators_mod.cached + def decoratornames(self, context: InferenceContext | None = None): + """Get the qualified names of each of the decorators on this function. + + :param context: + An inference context that can be passed to inference functions + :returns: The names of the decorators. + :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(context=context): + result.add(infnode.qname()) + except InferenceError: + continue + return result + + def is_bound(self) -> bool: + """Check if the function is bound to an instance or class. + + :returns: Whether the function is bound to an instance or class. + """ + return self.type in {"method", "classmethod"} + + def is_abstract(self, pass_is_abstract=True, any_raise_is_abstract=False) -> bool: + """Check if the method is abstract. + + A method is considered abstract if any of the following is true: + * The only statement is 'raise NotImplementedError' + * The only statement is 'raise ' and any_raise_is_abstract is True + * The only statement is 'pass' and pass_is_abstract is True + * The method is annotated with abc.astractproperty/abc.abstractmethod + + :returns: Whether the method is abstract. + """ + if self.decorators: + for node in self.decorators.nodes: + try: + inferred = next(node.infer()) + except (InferenceError, StopIteration): + continue + if inferred and inferred.qname() in { + "abc.abstractproperty", + "abc.abstractmethod", + }: + return True + + for child_node in self.body: + if isinstance(child_node, node_classes.Raise): + if any_raise_is_abstract: + return True + if child_node.raises_not_implemented(): + return True + return pass_is_abstract and isinstance(child_node, node_classes.Pass) + # empty function is the same as function with a single "pass" statement + if pass_is_abstract: + return True + + return False + + def is_generator(self) -> bool: + """Check if this is a generator function. + + :returns: Whether this is a generator function. + """ + return bool(next(self._get_yield_nodes_skip_lambdas(), False)) + + def infer_yield_result(self, context: InferenceContext | None = None): + """Infer what the function yields when called + + :returns: What the function yields + :rtype: iterable(NodeNG or Uninferable) or None + """ + # pylint: disable=not-an-iterable + # https://github.com/PyCQA/astroid/issues/1015 + for yield_ in self.nodes_of_class(node_classes.Yield): + if yield_.value is None: + const = node_classes.Const(None) + const.parent = yield_ + const.lineno = yield_.lineno + yield const + elif yield_.scope() == self: + yield from yield_.value.infer(context=context) + + def infer_call_result(self, caller=None, context: InferenceContext | None = 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, generator_initial_context=context) + yield result + return + # This is really a gigantic hack to work around metaclass generators + # that return transient class-generating functions. Pylint's AST structure + # cannot handle a base class object that is only used for calling __new__, + # but does not contribute to the inheritance structure itself. We inject + # a fake class into the hierarchy here for several well-known metaclass + # generators, and filter it out later. + if ( + self.name == "with_metaclass" + and len(self.args.args) == 1 + and self.args.vararg is not None + ): + metaclass = next(caller.args[0].infer(context), None) + if isinstance(metaclass, ClassDef): + try: + class_bases = [next(arg.infer(context)) for arg in caller.args[1:]] + except StopIteration as e: + raise InferenceError(node=caller.args[1:], context=context) from e + new_class = ClassDef(name="temporary_class") + new_class.hide = True + new_class.parent = self + new_class.postinit( + bases=[ + base + for base in class_bases + if not isinstance(base, util.UninferableBase) + ], + 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: + if self.is_abstract(pass_is_abstract=True, any_raise_is_abstract=True): + yield util.Uninferable + else: + yield node_classes.Const(None) + return + + raise InferenceError("The function does not have any return statements") + + for returnnode in itertools.chain((first_return,), returns): + if returnnode.value is None: + yield node_classes.Const(None) + else: + try: + yield from returnnode.value.infer(context) + except InferenceError: + yield util.Uninferable + + def bool_value(self, context: InferenceContext | None = None) -> bool: + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`FunctionDef` this is always ``True``. + """ + return True + + def get_children(self): + if self.decorators is not None: + yield self.decorators + + yield self.args + + if self.returns is not None: + yield self.returns + + yield from self.body + + def scope_lookup(self, node, 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(future=True) + if isinstance(frame, ClassDef): + return self, [frame] + return super().scope_lookup(node, name, offset) + + def frame(self: _T, *, future: Literal[None, True] = None) -> _T: + """The node's frame node. + + A frame node is a :class:`Module`, :class:`FunctionDef`, + :class:`ClassDef` or :class:`Lambda`. + + :returns: The node itself. + """ + return self + + +class AsyncFunctionDef(FunctionDef): + """Class representing an :class:`ast.FunctionDef` node. + + A :class:`AsyncFunctionDef` is an asynchronous function + created with the `async` keyword. + + >>> import astroid + >>> node = astroid.extract_node(''' + async def func(things): + async for thing in things: + print(thing) + ''') + >>> node + + >>> node.body[0] + + """ + + +def _rec_get_names(args, names: list[str] | None = None) -> list[str]: + """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) -> bool: + """Return if the given class can be + used as a metaclass. + """ + if klass.name == "type": + return True + if seen is None: + seen = set() + for base in klass.bases: + try: + for baseobj in base.infer(): + baseobj_name = baseobj.qname() + if baseobj_name in seen: + continue + + seen.add(baseobj_name) + if isinstance(baseobj, bases.Instance): + # not abstract + return False + if baseobj is klass: + continue + if not isinstance(baseobj, ClassDef): + continue + if baseobj._type == "metaclass": + return True + if _is_metaclass(baseobj, seen): + return True + except 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(future=True) + while klass is not None and not isinstance(klass, ClassDef): + if klass.parent is None: + klass = None + else: + klass = klass.parent.frame(future=True) + return klass + + +# pylint: disable=too-many-instance-attributes +class ClassDef( + _base_nodes.FilterStmtsBaseNode, LocalsDictNodeNG, _base_nodes.Statement +): + """Class representing an :class:`ast.ClassDef` node. + + >>> import astroid + >>> node = astroid.extract_node(''' + class Thing: + def my_meth(self, arg): + return arg + self.offset + ''') + >>> node + + """ + + # some of the attributes below are set by the builder module or + # by a raw factories + + # a dictionary of class instances attributes + _astroid_fields = ("decorators", "bases", "keywords", "doc_node", "body") # name + + decorators = None + """The decorators that are applied to this class. + + :type: Decorators or None + """ + special_attributes = ClassModel() + """The names of special attributes that this class has. + + :type: objectmodel.ClassModel + """ + + _type = None + _metaclass: NodeNG | None = None + _metaclass_hack = False + hide = False + type = property( + _class_type, + doc=( + "The class type for this node.\n\n" + "Possible values are: class, metaclass, exception.\n\n" + ":type: str" + ), + ) + _other_fields = ("name", "doc", "is_dataclass", "position") + _other_other_fields = ("locals", "_newstyle") + _newstyle = None + + @decorators_mod.deprecate_arguments(doc="Use the postinit arg 'doc_node' instead") + def __init__( + self, + name=None, + doc: str | None = None, + lineno=None, + col_offset=None, + parent=None, + *, + end_lineno=None, + end_col_offset=None, + ): + """ + :param name: The name of the class. + :type name: str or None + + :param doc: The class docstring. + + :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 + + :param end_lineno: The last line this node appears on in the source code. + :type end_lineno: Optional[int] + + :param end_col_offset: The end column this node appears on in the + source code. Note: This is after the last symbol. + :type end_col_offset: Optional[int] + """ + self.instance_attrs = {} + self.locals = {} + """A map of the name of a local variable to the node defining it.""" + + 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: list[NodeNG] = [] + """What the class inherits from.""" + + 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.""" + + self.doc_node: Const | None = None + """The doc node associated with this node.""" + + self.is_dataclass: bool = False + """Whether this class is a dataclass.""" + + super().__init__( + lineno=lineno, + col_offset=col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + if parent is not None: + parent.frame(future=True).set_local(name, self) + + for local_name, node in self.implicit_locals(): + self.add_local_node(node, local_name) + + infer_binary_op: ClassVar[InferBinaryOp[ClassDef]] + + @property + def doc(self) -> str | None: + """The class docstring.""" + warnings.warn( + "The 'ClassDef.doc' attribute is deprecated, " + "use 'ClassDef.doc_node' instead.", + DeprecationWarning, + stacklevel=2, + ) + return self._doc + + @doc.setter + def doc(self, value: str | None) -> None: + warnings.warn( + "Setting the 'ClassDef.doc' attribute is deprecated, " + "use 'ClassDef.doc_node.value' instead.", + DeprecationWarning, + stacklevel=2, + ) + self._doc = value + + def implicit_parameters(self) -> Literal[1]: + return 1 + + def implicit_locals(self): + """Get implicitly defined class definition locals. + + :returns: the the name and Const pair for each local + :rtype: tuple(tuple(str, node_classes.Const), ...) + """ + locals_ = (("__module__", self.special_attributes.attr___module__),) + # __qualname__ is defined in PEP3155 + locals_ += (("__qualname__", self.special_attributes.attr___qualname__),) + return locals_ + + # pylint: disable=redefined-outer-name + def postinit( + self, + bases, + body, + decorators, + newstyle=None, + metaclass: NodeNG | None = None, + keywords=None, + *, + position: Position | None = None, + doc_node: Const | None = 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. + + :param keywords: The keywords given to the class definition. + :type keywords: list(Keyword) or None + + :param position: Position of class keyword and name. + + :param doc_node: The doc node associated with this node. + """ + if keywords is not 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 + self.position = position + self.doc_node = doc_node + if doc_node: + self._doc = doc_node.value + + def _newstyle_impl(self, context: InferenceContext | None = None): + if context is None: + context = InferenceContext() + if self._newstyle is not None: + return self._newstyle + for base in self.ancestors(recurs=False, context=context): + if base._newstyle_impl(context): + self._newstyle = True + break + klass = self.declared_metaclass() + # could be any callable, we'd need to infer the result of klass(name, + # bases, dict). punt if it's not a class node. + if klass is not None and isinstance(klass, ClassDef): + self._newstyle = klass._newstyle_impl(context) + if self._newstyle is None: + self._newstyle = False + return self._newstyle + + _newstyle = None + newstyle = property( + _newstyle_impl, + doc=("Whether this is a new style class or not\n\n" ":type: bool or None"), + ) + + @cached_property + def fromlineno(self) -> int | None: + """The first line that this node appears on in the source code.""" + if not PY38_PLUS or IS_PYPY and PY38 and not PYPY_7_3_11_PLUS: + # For Python < 3.8 the lineno is the line number of the first decorator. + # We want the class statement lineno. Similar to 'FunctionDef.fromlineno' + # PyPy (3.8): Fixed with version v7.3.11 + lineno = self.lineno + if self.decorators is not None: + lineno += sum( + node.tolineno - node.lineno + 1 for node in self.decorators.nodes + ) + + return lineno + return super().fromlineno + + @cached_property + def blockstart_tolineno(self): + """The line on which the beginning of this block ends. + + :type: int + """ + if self.bases: + return self.bases[-1].tolineno + + return self.fromlineno + + def block_range(self, lineno): + """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) -> Literal["builtins.type", "builtins.classobj"]: + """Get the name of the type that this node represents. + + :returns: The name of the type. + """ + if self.newstyle: + return "builtins.type" + return "builtins.classobj" + + def display_type(self) -> str: + """A human readable type of this node. + + :returns: The type of this node. + :rtype: str + """ + return "Class" + + def callable(self) -> bool: + """Whether this node defines something that is callable. + + :returns: Whether this defines something that is callable. + For a :class:`ClassDef` this is always ``True``. + """ + return True + + def is_subtype_of(self, type_name, context: InferenceContext | None = None) -> bool: + """Whether this class is a subtype of the given type. + + :param type_name: The name of the type of check against. + :type type_name: str + + :returns: Whether this class is a subtype of the given type. + """ + if self.qname() == type_name: + return True + + return any(anc.qname() == type_name for anc in self.ancestors(context=context)) + + def _infer_type_call(self, caller, context): + try: + name_node = next(caller.args[0].infer(context)) + except StopIteration as e: + raise InferenceError(node=caller.args[0], context=context) from e + if isinstance(name_node, node_classes.Const) and isinstance( + name_node.value, str + ): + name = name_node.value + else: + return util.Uninferable + + result = ClassDef(name) + + # Get the bases of the class. + try: + class_bases = next(caller.args[1].infer(context)) + except StopIteration as e: + raise InferenceError(node=caller.args[1], context=context) from e + if isinstance(class_bases, (node_classes.Tuple, node_classes.List)): + bases = [] + for base in class_bases.itered(): + inferred = next(base.infer(context=context), None) + if inferred: + bases.append( + node_classes.EvaluatedObject(original=base, value=inferred) + ) + result.bases = bases + else: + # There is currently no AST node that can represent an 'unknown' + # node (Uninferable is not an AST node), therefore we simply return Uninferable here + # although we know at least the name of the class. + return util.Uninferable + + # Get the members of the class + try: + members = next(caller.args[2].infer(context)) + except (InferenceError, StopIteration): + members = None + + if members and isinstance(members, node_classes.Dict): + for attr, value in members.items: + if isinstance(attr, node_classes.Const) and isinstance(attr.value, str): + result.locals[attr.value] = [value] + + result.parent = caller.parent + return result + + def infer_call_result(self, caller, context: InferenceContext | None = None): + """infer what a class is returning when called""" + if self.is_subtype_of("builtins.type", context) and len(caller.args) == 3: + result = self._infer_type_call(caller, context) + yield result + return + + dunder_call = None + try: + metaclass = self.metaclass(context=context) + if metaclass is not None: + # Only get __call__ if it's defined locally for the metaclass. + # Otherwise we will find ObjectModel.__call__ which will + # return an instance of the metaclass. Instantiating the class is + # handled later. + if "__call__" in metaclass.locals: + dunder_call = next(metaclass.igetattr("__call__", context)) + except (AttributeInferenceError, StopIteration): + pass + + if dunder_call and dunder_call.qname() != "builtins.type.__call__": + # Call type.__call__ if not set metaclass + # (since type is the default metaclass) + context = bind_context_to_node(context, self) + context.callcontext.callee = dunder_call + yield from dunder_call.infer_call_result(caller, context) + else: + yield self.instantiate_class() + + def scope_lookup(self, node, 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 AstroidManager().builtins_module + ) + if ( + any(node == base or base.parent_of(node) for base in self.bases) + or lookup_upper_frame + ): + # Handle the case where we have either a name + # in the bases of a class, which exists before + # the actual definition or the case where we have + # a Getattr node, with that name. + # + # name = ... + # class A(name): + # def name(self): ... + # + # import name + # class A(name.Name): + # def name(self): ... + + frame = self.parent.frame(future=True) + # line offset to avoid that class A(A) resolve the ancestor to + # the defined class + offset = -1 + else: + frame = self + return frame._scope_lookup(node, name, offset) + + @property + def basenames(self): + """The names of the parent classes + + Names are given in the order they appear in the class definition. + + :type: list(str) + """ + return [bnode.as_string() for bnode in self.bases] + + def ancestors( + self, recurs: bool = True, context: InferenceContext | None = None + ) -> Generator[ClassDef, None, None]: + """Iterate over the base classes in prefixed depth first order. + + :param recurs: Whether to recurse or return direct ancestors only. + + :returns: The base classes + """ + # FIXME: should be possible to choose the resolution order + # FIXME: inference make infinite loops possible here + yielded = {self} + if context is None: + context = InferenceContext() + if not self.bases and self.qname() != "builtins.object": + 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 InferenceError: + continue + + def local_attr_ancestors(self, name, context: InferenceContext | None = None): + """Iterate over the parents that define the given name. + + :param name: The name to find definitions for. + :type name: str + + :returns: The parents that define the given name. + :rtype: iterable(NodeNG) + """ + # Look up in the mro if we can. This will result in the + # attribute being looked up just as Python does it. + try: + ancestors = self.mro(context)[1:] + except MroError: + # Fallback to use ancestors, we can't determine + # a sane MRO. + ancestors = self.ancestors(context=context) + for astroid in ancestors: + if name in astroid: + yield astroid + + def instance_attr_ancestors(self, name, context: InferenceContext | None = None): + """Iterate over the parents that define the given name as an attribute. + + :param name: The name to find definitions for. + :type name: str + + :returns: The parents that define the given name as + an instance attribute. + :rtype: iterable(NodeNG) + """ + for astroid in self.ancestors(context=context): + if name in astroid.instance_attrs: + yield astroid + + def has_base(self, node) -> bool: + """Whether this class directly inherits from the given node. + + :param node: The node to check for. + :type node: NodeNG + + :returns: Whether this class directly inherits from the given node. + """ + return node in self.bases + + def local_attr(self, name, context: InferenceContext | None = None): + """Get the list of assign nodes associated to the given name. + + Assignments are looked for in both this class and in parents. + + :returns: The list of assignments to the given name. + :rtype: list(NodeNG) + + :raises AttributeInferenceError: If no attribute with this name + can be found in this class or parent classes. + """ + result = [] + if name in self.locals: + result = self.locals[name] + else: + class_node = next(self.local_attr_ancestors(name, context), None) + if class_node: + result = class_node.locals[name] + result = [n for n in result if not isinstance(n, node_classes.DelAttr)] + if result: + return result + raise AttributeInferenceError(target=self, attribute=name, context=context) + + def instance_attr(self, name, context: InferenceContext | None = None): + """Get the list of nodes associated to the given attribute name. + + Assignments are looked for in both this class and in parents. + + :returns: The list of assignments to the given name. + :rtype: list(NodeNG) + + :raises AttributeInferenceError: If no attribute with this name + can be found in this class or parent classes. + """ + # Return a copy, so we don't modify self.instance_attrs, + # which could lead to infinite loop. + values = list(self.instance_attrs.get(name, [])) + # get all values from parents + for class_node in self.instance_attr_ancestors(name, context): + values += class_node.instance_attrs[name] + values = [n for n in values if not isinstance(n, node_classes.DelAttr)] + if values: + return values + raise AttributeInferenceError(target=self, attribute=name, context=context) + + def instantiate_class(self) -> bases.Instance: + """Get an :class:`Instance` of the :class:`ClassDef` node. + + :returns: An :class:`Instance` of the :class:`ClassDef` node + """ + try: + if any(cls.name in EXCEPTION_BASE_CLASSES for cls in self.mro()): + # Subclasses of exceptions can be exception instances + return objects.ExceptionInstance(self) + except MroError: + pass + return bases.Instance(self) + + def getattr( + self, + name: str, + context: InferenceContext | None = None, + class_context: bool = True, + ) -> list[SuccessfulInferenceResult]: + """Get an attribute from this class, using Python's attribute semantic. + + This method doesn't look in the :attr:`instance_attrs` dictionary + since it is done by an :class:`Instance` proxy at inference time. + It may return an :class:`Uninferable` object if + the attribute has not been + found, but a ``__getattr__`` or ``__getattribute__`` method is defined. + If ``class_context`` is given, then it is considered that the + attribute is accessed from a class context, + e.g. ClassDef.attribute, otherwise it might have been accessed + from an instance as well. If ``class_context`` is used in that + case, then a lookup in the implicit metaclass and the explicit + metaclass will be done. + + :param name: The attribute to look for. + + :param class_context: Whether the attribute can be accessed statically. + + :returns: The attribute. + + :raises AttributeInferenceError: If the attribute cannot be inferred. + """ + if not name: + raise AttributeInferenceError(target=self, attribute=name, context=context) + + # don't modify the list in self.locals! + values: list[SuccessfulInferenceResult] = list(self.locals.get(name, [])) + for classnode in self.ancestors(recurs=True, context=context): + values += classnode.locals.get(name, []) + + if name in self.special_attributes and class_context and not values: + result = [self.special_attributes.lookup(name)] + if name == "__bases__": + # Need special treatment, since they are mutable + # and we need to return all the values. + result += values + return result + + if class_context: + values += self._metaclass_lookup_attribute(name, context) + + # Remove AnnAssigns without value, which are not attributes in the purest sense. + for value in values.copy(): + if isinstance(value, node_classes.AssignName): + stmt = value.statement(future=True) + if isinstance(stmt, node_classes.AnnAssign) and stmt.value is None: + values.pop(values.index(value)) + + if not values: + raise AttributeInferenceError(target=self, attribute=name, context=context) + + return values + + @lru_cache(maxsize=1024) # noqa + def _metaclass_lookup_attribute(self, name, context): + """Search the given name in the implicit and the explicit metaclass.""" + attrs = set() + implicit_meta = self.implicit_metaclass() + context = copy_context(context) + metaclass = self.metaclass(context=context) + for cls in (implicit_meta, metaclass): + if cls and cls != self and isinstance(cls, ClassDef): + cls_attributes = self._get_attribute_from_metaclass(cls, name, context) + attrs.update(set(cls_attributes)) + return attrs + + def _get_attribute_from_metaclass(self, cls, name, context): + try: + attrs = cls.getattr(name, context=context, class_context=True) + except AttributeInferenceError: + return + + for attr in bases._infer_stmts(attrs, context, frame=cls): + if not isinstance(attr, FunctionDef): + yield attr + continue + + if isinstance(attr, objects.Property): + yield attr + continue + if attr.type == "classmethod": + # If the method is a classmethod, then it will + # be bound to the metaclass, not to the class + # from where the attribute is retrieved. + # get_wrapping_class could return None, so just + # default to the current class. + frame = get_wrapping_class(attr) or self + yield bases.BoundMethod(attr, frame) + elif attr.type == "staticmethod": + yield attr + else: + yield bases.BoundMethod(attr, self) + + def igetattr( + self, + name: str, + context: InferenceContext | None = None, + class_context: bool = True, + ) -> Iterator[InferenceResult]: + """Infer the possible values of the given variable. + + :param name: The name of the variable to infer. + + :returns: The inferred possible values. + """ + # set lookup name since this is necessary to infer on import nodes for + # instance + context = copy_context(context) + context.lookupname = name + + metaclass = self.metaclass(context=context) + try: + attributes = self.getattr(name, context, class_context=class_context) + # If we have more than one attribute, make sure that those starting from + # the second one are from the same scope. This is to account for modifications + # to the attribute happening *after* the attribute's definition (e.g. AugAssigns on lists) + if len(attributes) > 1: + first_attr, attributes = attributes[0], attributes[1:] + first_scope = first_attr.scope() + attributes = [first_attr] + [ + attr + for attr in attributes + if attr.parent and attr.parent.scope() == first_scope + ] + + for inferred in bases._infer_stmts(attributes, context, frame=self): + # yield Uninferable object instead of descriptors when necessary + if not isinstance(inferred, node_classes.Const) and isinstance( + inferred, bases.Instance + ): + try: + inferred._proxied.getattr("__get__", context) + except AttributeInferenceError: + yield inferred + else: + yield util.Uninferable + elif isinstance(inferred, objects.Property): + function = inferred.function + if not class_context: + # Through an instance so we can solve the property + yield from function.infer_call_result( + caller=self, context=context + ) + # If we're in a class context, we need to determine if the property + # was defined in the metaclass (a derived class must be a subclass of + # the metaclass of all its bases), in which case we can resolve the + # property. If not, i.e. the property is defined in some base class + # instead, then we return the property object + elif metaclass and function.parent.scope() is metaclass: + # Resolve a property as long as it is not accessed through + # the class itself. + yield from function.infer_call_result( + caller=self, context=context + ) + else: + yield inferred + else: + yield function_to_method(inferred, self) + except AttributeInferenceError as error: + if not name.startswith("__") and self.has_dynamic_getattr(context): + # class handle some dynamic attributes, return a Uninferable object + yield util.Uninferable + else: + raise InferenceError( + str(error), target=self, attribute=name, context=context + ) from error + + def has_dynamic_getattr(self, context: InferenceContext | None = None) -> bool: + """Check if the class has a custom __getattr__ or __getattribute__. + + If any such method is found and it is not from + builtins, nor from an extension module, then the function + will return True. + + :returns: Whether the class has a custom __getattr__ or __getattribute__. + """ + + def _valid_getattr(node): + root = node.root() + return root.name != "builtins" and getattr(root, "pure_python", None) + + try: + return _valid_getattr(self.getattr("__getattr__", context)[0]) + except AttributeInferenceError: + # if self.newstyle: XXX cause an infinite recursion error + try: + getattribute = self.getattr("__getattribute__", context)[0] + return _valid_getattr(getattribute) + except AttributeInferenceError: + pass + return False + + def getitem(self, index, context: InferenceContext | None = None): + """Return the inference of a subscript. + + This is basically looking up the method in the metaclass and calling it. + + :returns: The inferred value of a subscript to this class. + :rtype: NodeNG + + :raises AstroidTypeError: If this class does not define a + ``__getitem__`` method. + """ + try: + methods = lookup(self, "__getitem__") + except AttributeInferenceError as exc: + if isinstance(self, ClassDef): + # subscripting a class definition may be + # achieved thanks to __class_getitem__ method + # which is a classmethod defined in the class + # that supports subscript and not in the metaclass + try: + methods = self.getattr("__class_getitem__") + # Here it is assumed that the __class_getitem__ node is + # a FunctionDef. One possible improvement would be to deal + # with more generic inference. + except AttributeInferenceError: + raise AstroidTypeError(node=self, context=context) from exc + else: + raise AstroidTypeError(node=self, context=context) from exc + + method = methods[0] + + # Create a new callcontext for providing index as an argument. + new_context = bind_context_to_node(context, self) + new_context.callcontext = CallContext(args=[index], callee=method) + + try: + return next(method.infer_call_result(self, new_context), util.Uninferable) + except AttributeError: + # Starting with python3.9, builtin types list, dict etc... + # are subscriptable thanks to __class_getitem___ classmethod. + # However in such case the method is bound to an EmptyNode and + # EmptyNode doesn't have infer_call_result method yielding to + # AttributeError + if ( + isinstance(method, node_classes.EmptyNode) + and self.pytype() == "builtins.type" + and PY39_PLUS + ): + return self + raise + except InferenceError: + return util.Uninferable + + def methods(self): + """Iterate over all of the method defined in this class and its parents. + + :returns: The methods defined on the class. + :rtype: iterable(FunctionDef) + """ + done = {} + for astroid in itertools.chain(iter((self,)), self.ancestors()): + for meth in astroid.mymethods(): + if meth.name in done: + continue + done[meth.name] = None + yield meth + + def mymethods(self): + """Iterate over all of the method defined in this class only. + + :returns: The methods defined on the class. + :rtype: iterable(FunctionDef) + """ + for member in self.values(): + if isinstance(member, FunctionDef): + yield member + + def implicit_metaclass(self): + """Get the implicit metaclass of the current class. + + For newstyle classes, this will return an instance of builtins.type. + For oldstyle classes, it will simply return None, since there's + no implicit metaclass there. + + :returns: The metaclass. + :rtype: builtins.type or None + """ + if self.newstyle: + return builtin_lookup("type")[1][0] + return None + + def declared_metaclass( + self, context: InferenceContext | None = None + ) -> NodeNG | None: + """Return the explicit declared metaclass for the current class. + + An explicit declared metaclass is defined + either by passing the ``metaclass`` keyword argument + in the class definition line (Python 3) or (Python 2) by + having a ``__metaclass__`` class attribute, or if there are + no explicit bases but there is a global ``__metaclass__`` variable. + + :returns: The metaclass of this class, + or None if one could not be found. + """ + for base in self.bases: + try: + for baseobj in base.infer(context=context): + if isinstance(baseobj, ClassDef) and baseobj.hide: + self._metaclass = baseobj._metaclass + self._metaclass_hack = True + break + except InferenceError: + pass + + if self._metaclass: + # Expects this from Py3k TreeRebuilder + try: + return next( + node + for node in self._metaclass.infer(context=context) + if not isinstance(node, util.UninferableBase) + ) + except (InferenceError, StopIteration): + return None + + return None + + def _find_metaclass( + self, seen: set[ClassDef] | None = None, context: InferenceContext | None = None + ) -> NodeNG | None: + if seen is None: + seen = set() + seen.add(self) + + klass = self.declared_metaclass(context=context) + if klass is None: + for parent in self.ancestors(context=context): + if parent not in seen: + klass = parent._find_metaclass(seen) + if klass is not None: + break + return klass + + def metaclass(self, context: InferenceContext | None = None) -> NodeNG | None: + """Get the metaclass of this class. + + If this class does not define explicitly a metaclass, + then the first defined metaclass in ancestors will be used + instead. + + :returns: The metaclass of this class. + """ + return self._find_metaclass(context=context) + + def has_metaclass_hack(self): + return self._metaclass_hack + + def _islots(self): + """Return an iterator with the inferred slots.""" + if "__slots__" not in self.locals: + return None + for slots in self.igetattr("__slots__"): + # check if __slots__ is a valid type + for meth in ITER_METHODS: + try: + slots.getattr(meth) + break + except AttributeInferenceError: + continue + else: + continue + + if isinstance(slots, node_classes.Const): + # a string. Ignore the following checks, + # but yield the node, only if it has a value + if slots.value: + yield slots + continue + if not hasattr(slots, "itered"): + # we can't obtain the values, maybe a .deque? + continue + + if isinstance(slots, node_classes.Dict): + values = [item[0] for item in slots.items] + else: + values = slots.itered() + if isinstance(values, util.UninferableBase): + continue + if not values: + # Stop the iteration, because the class + # has an empty list of slots. + return values + + for elt in values: + try: + for inferred in elt.infer(): + if not isinstance( + inferred, node_classes.Const + ) or not isinstance(inferred.value, str): + continue + if not inferred.value: + continue + yield inferred + except InferenceError: + continue + + return None + + def _slots(self): + if not self.newstyle: + raise NotImplementedError( + "The concept of slots is undefined for old-style classes." + ) + + slots = self._islots() + try: + first = next(slots) + except StopIteration as exc: + # The class doesn't have a __slots__ definition or empty slots. + if exc.args and exc.args[0] not in ("", None): + return exc.args[0] + return None + return [first] + 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( + mro: list[ClassDef], + ) -> Iterator[node_classes.NodeNG | None]: + for cls in mro: + # Not interested in object, since it can't have slots. + if cls.qname() == "builtins.object": + continue + try: + cls_slots = cls._slots() + except NotImplementedError: + continue + if cls_slots is not None: + yield from cls_slots + else: + yield None + + if not self.newstyle: + raise NotImplementedError( + "The concept of slots is undefined for old-style classes." + ) + + try: + mro = self.mro() + except MroError as e: + raise NotImplementedError( + "Cannot get slots while parsing mro fails." + ) from e + + slots = list(grouped_slots(mro)) + if not all(slot is not None for slot in slots): + return None + + return sorted(set(slots), key=lambda item: item.value) + + def _inferred_bases(self, context: InferenceContext | None = None): + # Similar with .ancestors, but the difference is when one base is inferred, + # only the first object is wanted. That's because + # we aren't interested in superclasses, as in the following + # example: + # + # class SomeSuperClass(object): pass + # class SomeClass(SomeSuperClass): pass + # class Test(SomeClass): pass + # + # Inferring SomeClass from the Test's bases will give + # us both SomeClass and SomeSuperClass, but we are interested + # only in SomeClass. + + if context is None: + context = InferenceContext() + if not self.bases and self.qname() != "builtins.object": + yield builtin_lookup("object")[1][0] + return + + for stmt in self.bases: + try: + # Find the first non-None inferred base value + baseobj = next( + b + for b in stmt.infer(context=context.clone()) + if not (isinstance(b, Const) and b.value is None) + ) + except (InferenceError, StopIteration): + continue + if isinstance(baseobj, bases.Instance): + baseobj = baseobj._proxied + if not isinstance(baseobj, ClassDef): + continue + if not baseobj.hide: + yield baseobj + else: + yield from baseobj.bases + + def _compute_mro(self, context: InferenceContext | None = None): + 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)) + clean_typing_generic_mro(unmerged_mro) + return _c3_merge(unmerged_mro, self, context) + + def mro(self, context: InferenceContext | None = None) -> list[ClassDef]: + """Get the method resolution order, using C3 linearization. + + :returns: The list of ancestors, sorted by the mro. + :rtype: list(NodeNG) + :raises DuplicateBasesError: Duplicate bases in the same class base + :raises InconsistentMroError: A class' MRO is inconsistent + """ + return self._compute_mro(context=context) + + def bool_value(self, context: InferenceContext | None = None) -> Literal[True]: + """Determine the boolean value of this node. + + :returns: The boolean value of this node. + For a :class:`ClassDef` this is always ``True``. + """ + return True + + def get_children(self): + if self.decorators is not None: + yield self.decorators + + yield from self.bases + if self.keywords is not None: + yield from self.keywords + yield from self.body + + @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)) + + def frame(self: _T, *, future: Literal[None, True] = None) -> _T: + """The node's frame node. + + A frame node is a :class:`Module`, :class:`FunctionDef`, + :class:`ClassDef` or :class:`Lambda`. + + :returns: The node itself. + """ + return self diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py new file mode 120000 index 00000000..8ea53675 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/scoped_nodes/utils.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/36/78/dd/91c6e6f73b3bd8772857d6ff844e8ff922d9ecc081f6ead4f965033558 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/nodes/utils.py b/venv/lib/python3.10/site-packages/astroid/nodes/utils.py new file mode 120000 index 00000000..07645464 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/nodes/utils.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/53/2e/b1/a28c272f25b00edb99e6a5298dbab5f4fda9508723d09d389216c7e476 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/objects.py b/venv/lib/python3.10/site-packages/astroid/objects.py new file mode 120000 index 00000000..d1651651 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/objects.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/f2/ba/0c/e77b16c05ceeb0022fcf79ce8f42da4045ddcc9c3400d7dbea60cccb9c \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/protocols.py b/venv/lib/python3.10/site-packages/astroid/protocols.py new file mode 100644 index 00000000..dcc9e2b8 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/protocols.py @@ -0,0 +1,980 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""This module contains a set of functions to handle python protocols for nodes +where it makes sense. +""" + +from __future__ import annotations + +import collections +import itertools +import operator as operator_mod +from collections.abc import Callable, Generator, Iterator, Sequence +from typing import Any, TypeVar + +from astroid import arguments, bases, decorators, helpers, nodes, util +from astroid.const import Context +from astroid.context import InferenceContext, copy_context +from astroid.exceptions import ( + AstroidIndexError, + AstroidTypeError, + AttributeInferenceError, + InferenceError, + NoDefault, +) +from astroid.nodes import node_classes +from astroid.typing import ( + ConstFactoryResult, + InferenceResult, + SuccessfulInferenceResult, +) + +raw_building = util.lazy_import("raw_building") +objects = util.lazy_import("objects") + + +_TupleListNodeT = TypeVar("_TupleListNodeT", nodes.Tuple, nodes.List) + + +def _reflected_name(name) -> str: + return "__r" + name[2:] + + +def _augmented_name(name) -> str: + 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: dict[str, Callable[[Any], Any]] = { + "+": operator_mod.pos, + "-": operator_mod.neg, + "~": operator_mod.invert, + "not": operator_mod.not_, +} + + +def _infer_unary_op(obj: Any, op: str) -> ConstFactoryResult: + """Perform unary operation on `obj`, unless it is `NotImplemented`. + + Can raise TypeError if operation is unsupported. + """ + if obj is NotImplemented: + value = obj + else: + func = _UNARY_OPERATORS[op] + value = func(obj) + return nodes.const_factory(value) + + +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: nodes.Const, + opnode: nodes.AugAssign | nodes.BinOp, + operator: str, + other: InferenceResult, + context: InferenceContext, + _: SuccessfulInferenceResult, +) -> Generator[ConstFactoryResult | util.UninferableBase, None, None]: + not_implemented = nodes.Const(NotImplemented) + if isinstance(other, nodes.Const): + if ( + operator == "**" + and isinstance(self.value, (int, float)) + and isinstance(other.value, (int, float)) + and (self.value > 1e5 or other.value > 1e5) + ): + yield not_implemented + return + try: + impl = BIN_OP_IMPL[operator] + try: + yield nodes.const_factory(impl(self.value, other.value)) + except TypeError: + # ArithmeticError is not enough: float >> float is a TypeError + yield not_implemented + except Exception: # pylint: disable=broad-except + yield util.Uninferable + except TypeError: + yield not_implemented + elif isinstance(self.value, str) and operator == "%": + # TODO(cpopa): implement string interpolation later on. + yield util.Uninferable + else: + yield not_implemented + + +nodes.Const.infer_binary_op = const_infer_binary_op + + +def _multiply_seq_by_int( + self: _TupleListNodeT, + opnode: nodes.AugAssign | nodes.BinOp, + other: nodes.Const, + context: InferenceContext, +) -> _TupleListNodeT: + node = self.__class__(parent=opnode) + filtered_elts = ( + helpers.safe_infer(elt, context) or util.Uninferable + for elt in self.elts + if not isinstance(elt, util.UninferableBase) + ) + node.elts = list(filtered_elts) * other.value + return node + + +def _filter_uninferable_nodes( + elts: Sequence[InferenceResult], context: InferenceContext +) -> Iterator[SuccessfulInferenceResult]: + for elt in elts: + if isinstance(elt, util.UninferableBase): + yield nodes.Unknown() + else: + for inferred in elt.infer(context): + if not isinstance(inferred, util.UninferableBase): + yield inferred + else: + yield nodes.Unknown() + + +@decorators.yes_if_nothing_inferred +def tl_infer_binary_op( + self: _TupleListNodeT, + opnode: nodes.AugAssign | nodes.BinOp, + operator: str, + other: InferenceResult, + context: InferenceContext, + method: SuccessfulInferenceResult, +) -> Generator[_TupleListNodeT | nodes.Const | util.UninferableBase, None, None]: + """Infer a binary operation on a tuple or list. + + The instance on which the binary operation is performed is a tuple + or list. This refers to the left-hand side of the operation, so: + 'tuple() + 1' or '[] + A()' + """ + # For tuples and list the boundnode is no longer the tuple or list instance + context.boundnode = None + not_implemented = nodes.Const(NotImplemented) + if isinstance(other, self.__class__) and operator == "+": + node = self.__class__(parent=opnode) + node.elts = list( + itertools.chain( + _filter_uninferable_nodes(self.elts, context), + _filter_uninferable_nodes(other.elts, context), + ) + ) + yield node + elif isinstance(other, nodes.Const) and operator == "*": + if not isinstance(other.value, int): + yield not_implemented + return + yield _multiply_seq_by_int(self, opnode, other, 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: bases.Instance | nodes.ClassDef, + opnode: nodes.AugAssign | nodes.BinOp, + operator: str, + other: InferenceResult, + context: InferenceContext, + method: SuccessfulInferenceResult, +) -> Generator[InferenceResult, None, None]: + return method.infer_call_result(self, context) + + +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 isinstance(part, util.UninferableBase): + continue + if not hasattr(part, "itered"): + continue + try: + itered = part.itered() + except TypeError: + continue + try: + if isinstance(itered[index], (nodes.Const, nodes.Name)): + itered = [part] + except IndexError: + pass + for stmt in itered: + index_node = nodes.Const(index) + try: + assigned = stmt.getitem(index_node, context) + except (AttributeError, AstroidTypeError, AstroidIndexError): + continue + if not assign_path: + # we achieved to resolved the assignment path, + # don't infer the last part + yield assigned + elif isinstance(assigned, util.UninferableBase): + break + else: + # we are not yet on the last part of the path + # search on each possibly inferred value + try: + yield from _resolve_looppart( + assigned.infer(context), assign_path, context + ) + except InferenceError: + break + + +@decorators.raise_if_nothing_inferred +def for_assigned_stmts( + self: nodes.For | nodes.Comprehension, + node: node_classes.AssignedStmtsPossibleNode = None, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + if isinstance(self, nodes.AsyncFor) or getattr(self, "is_async", False): + # Skip inferring of async code for now + return { + "node": self, + "unknown": node, + "assign_path": assign_path, + "context": context, + } + if assign_path is None: + for lst in self.iter.infer(context): + if isinstance(lst, (nodes.Tuple, nodes.List)): + yield from lst.elts + else: + yield from _resolve_looppart(self.iter.infer(context), assign_path, context) + return { + "node": self, + "unknown": node, + "assign_path": assign_path, + "context": context, + } + + +nodes.For.assigned_stmts = for_assigned_stmts +nodes.Comprehension.assigned_stmts = for_assigned_stmts + + +def sequence_assigned_stmts( + self: nodes.Tuple | nodes.List, + node: node_classes.AssignedStmtsPossibleNode = None, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + if assign_path is None: + assign_path = [] + try: + index = self.elts.index(node) # type: ignore[arg-type] + except ValueError as exc: + raise InferenceError( + "Tried to retrieve a node {node!r} which does not exist", + node=self, + assign_path=assign_path, + context=context, + ) from exc + + assign_path.insert(0, index) + return self.parent.assigned_stmts( + node=self, context=context, assign_path=assign_path + ) + + +nodes.Tuple.assigned_stmts = sequence_assigned_stmts +nodes.List.assigned_stmts = sequence_assigned_stmts + + +def assend_assigned_stmts( + self: nodes.AssignName | nodes.AssignAttr, + node: node_classes.AssignedStmtsPossibleNode = None, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + return self.parent.assigned_stmts(node=self, context=context) + + +nodes.AssignName.assigned_stmts = assend_assigned_stmts +nodes.AssignAttr.assigned_stmts = assend_assigned_stmts + + +def _arguments_infer_argname( + self, name: str | None, context: InferenceContext +) -> Generator[InferenceResult, None, None]: + # arguments information may be missing, in which case we can't do anything + # more + if not (self.arguments or self.vararg or self.kwarg): + yield util.Uninferable + return + + functype = self.parent.type + # first argument of instance/class method + if ( + self.arguments + and getattr(self.arguments[0], "name", None) == name + and functype != "staticmethod" + ): + cls = self.parent.parent.scope() + is_metaclass = isinstance(cls, nodes.ClassDef) and cls.type == "metaclass" + # If this is a metaclass, then the first argument will always + # be the class, not an instance. + if context.boundnode and isinstance(context.boundnode, bases.Instance): + cls = context.boundnode._proxied + if is_metaclass or functype == "classmethod": + yield cls + return + if functype == "method": + yield cls.instantiate_class() + return + + if context and context.callcontext: + callee = context.callcontext.callee + while hasattr(callee, "_proxied"): + callee = callee._proxied + if getattr(callee, "name", None) == self.parent.name: + call_site = arguments.CallSite(context.callcontext, context.extra_context) + yield from call_site.infer_argument(self.parent, name, context) + return + + if name == self.vararg: + vararg = nodes.const_factory(()) + vararg.parent = self + if not self.arguments and self.parent.name == "__init__": + cls = self.parent.parent.scope() + vararg.elts = [cls.instantiate_class()] + yield vararg + return + if name == self.kwarg: + kwarg = nodes.const_factory({}) + kwarg.parent = self + yield kwarg + return + # if there is a default value, yield it. And then yield Uninferable to reflect + # we can't guess given argument value + try: + context = copy_context(context) + yield from self.default_value(name).infer(context) + yield util.Uninferable + except NoDefault: + yield util.Uninferable + + +def arguments_assigned_stmts( + self: nodes.Arguments, + node: node_classes.AssignedStmtsPossibleNode = None, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + try: + node_name = node.name # type: ignore[union-attr] + except AttributeError: + # Added to handle edge cases where node.name is not defined. + # https://github.com/PyCQA/astroid/pull/1644#discussion_r901545816 + node_name = None # pragma: no cover + + if context and context.callcontext: + callee = context.callcontext.callee + while hasattr(callee, "_proxied"): + callee = callee._proxied + else: + return _arguments_infer_argname(self, node_name, context) + if node and getattr(callee, "name", None) == node.frame(future=True).name: + # reset call context/name + callcontext = context.callcontext + context = copy_context(context) + context.callcontext = None + args = arguments.CallSite(callcontext, context=context) + return args.infer_argument(self.parent, node_name, context) + return _arguments_infer_argname(self, node_name, context) + + +nodes.Arguments.assigned_stmts = arguments_assigned_stmts + + +@decorators.raise_if_nothing_inferred +def assign_assigned_stmts( + self: nodes.AugAssign | nodes.Assign | nodes.AnnAssign, + node: node_classes.AssignedStmtsPossibleNode = None, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + if not assign_path: + yield self.value + return None + yield from _resolve_assignment_parts( + self.value.infer(context), assign_path, context + ) + + return { + "node": self, + "unknown": node, + "assign_path": assign_path, + "context": context, + } + + +def assign_annassigned_stmts( + self: nodes.AnnAssign, + node: node_classes.AssignedStmtsPossibleNode = None, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + for inferred in assign_assigned_stmts(self, node, context, assign_path): + if inferred is None: + yield util.Uninferable + else: + yield inferred + + +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 (AstroidTypeError, AstroidIndexError): + return + + if not assigned: + return + + if not assign_path: + # we achieved to resolved the assignment path, don't infer the + # last part + yield assigned + elif isinstance(assigned, util.UninferableBase): + return + else: + # we are not yet on the last part of the path search on each + # possibly inferred value + try: + yield from _resolve_assignment_parts( + assigned.infer(context), assign_path, context + ) + except InferenceError: + return + + +@decorators.raise_if_nothing_inferred +def excepthandler_assigned_stmts( + self: nodes.ExceptHandler, + node: node_classes.AssignedStmtsPossibleNode = None, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + for assigned in node_classes.unpack_infer(self.type): + if isinstance(assigned, nodes.ClassDef): + assigned = objects.ExceptionInstance(assigned) + + yield assigned + return { + "node": self, + "unknown": node, + "assign_path": assign_path, + "context": context, + } + + +nodes.ExceptHandler.assigned_stmts = excepthandler_assigned_stmts + + +def _infer_context_manager(self, mgr, context): + try: + inferred = next(mgr.infer(context=context)) + except StopIteration as e: + raise InferenceError(node=mgr) from e + if isinstance(inferred, bases.Generator): + # Check if it is decorated with contextlib.contextmanager. + func = inferred.parent + if not func.decorators: + raise InferenceError( + "No decorators found on inferred generator %s", node=func + ) + + for decorator_node in func.decorators.nodes: + decorator = next(decorator_node.infer(context=context), None) + if isinstance(decorator, nodes.FunctionDef): + if decorator.qname() == _CONTEXTLIB_MGR: + break + else: + # It doesn't interest us. + raise InferenceError(node=func) + try: + yield next(inferred.infer_yield_types()) + except StopIteration as e: + raise InferenceError(node=func) from e + + elif isinstance(inferred, bases.Instance): + try: + enter = next(inferred.igetattr("__enter__", context=context)) + except (InferenceError, AttributeInferenceError, StopIteration) as exc: + raise InferenceError(node=inferred) from exc + if not isinstance(enter, bases.BoundMethod): + raise InferenceError(node=enter) + yield from enter.infer_call_result(self, context) + else: + raise InferenceError(node=mgr) + + +@decorators.raise_if_nothing_inferred +def with_assigned_stmts( + self: nodes.With, + node: node_classes.AssignedStmtsPossibleNode = None, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + """Infer names and other nodes from a *with* statement. + + This enables only inference for name binding in a *with* statement. + For instance, in the following code, inferring `func` will return + the `ContextManager` class, not whatever ``__enter__`` returns. + We are doing this intentionally, because we consider that the context + manager result is whatever __enter__ returns and what it is binded + using the ``as`` keyword. + + class ContextManager(object): + def __enter__(self): + return 42 + with ContextManager() as f: + pass + + # ContextManager().infer() will return ContextManager + # f.infer() will return 42. + + Arguments: + self: nodes.With + node: The target of the assignment, `as (a, b)` in `with foo as (a, b)`. + context: Inference context used for caching already inferred objects + assign_path: + A list of indices, where each index specifies what item to fetch from + the inference results. + """ + try: + mgr = next(mgr for (mgr, vars) in self.items if vars == node) + except StopIteration: + return None + if assign_path is None: + yield from _infer_context_manager(self, mgr, context) + else: + for result in _infer_context_manager(self, mgr, context): + # Walk the assign_path and get the item at the final index. + obj = result + for index in assign_path: + if not hasattr(obj, "elts"): + raise InferenceError( + "Wrong type ({targets!r}) for {node!r} assignment", + node=self, + targets=node, + assign_path=assign_path, + context=context, + ) + try: + obj = obj.elts[index] + except IndexError as exc: + raise InferenceError( + "Tried to infer a nonexistent target with index {index} " + "in {node!r}.", + node=self, + targets=node, + assign_path=assign_path, + context=context, + ) from exc + except TypeError as exc: + raise InferenceError( + "Tried to unpack a non-iterable value in {node!r}.", + node=self, + targets=node, + assign_path=assign_path, + context=context, + ) from exc + yield obj + return { + "node": self, + "unknown": node, + "assign_path": assign_path, + "context": context, + } + + +nodes.With.assigned_stmts = with_assigned_stmts + + +@decorators.raise_if_nothing_inferred +def named_expr_assigned_stmts( + self: nodes.NamedExpr, + node: node_classes.AssignedStmtsPossibleNode, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + """Infer names and other nodes from an assignment expression.""" + if self.target == node: + yield from self.value.infer(context=context) + else: + raise InferenceError( + "Cannot infer NamedExpr node {node!r}", + node=self, + assign_path=assign_path, + context=context, + ) + + +nodes.NamedExpr.assigned_stmts = named_expr_assigned_stmts + + +@decorators.yes_if_nothing_inferred +def starred_assigned_stmts( # noqa: C901 + self: nodes.Starred, + node: node_classes.AssignedStmtsPossibleNode = None, + context: InferenceContext | None = None, + assign_path: list[int] | None = None, +) -> Any: + """ + Arguments: + self: nodes.Starred + node: a node related to the current underlying Node. + context: Inference context used for caching already inferred objects + assign_path: + A list of indices, where each index specifies what item to fetch from + the inference results. + """ + + # pylint: disable=too-many-locals,too-many-statements + def _determine_starred_iteration_lookups( + starred: nodes.Starred, target: nodes.Tuple, lookups: list[tuple[int, int]] + ) -> None: + # Determine the lookups for the rhs of the iteration + itered = target.itered() + for index, element in enumerate(itered): + if ( + isinstance(element, nodes.Starred) + and element.value.name == starred.value.name + ): + lookups.append((index, len(itered))) + break + if isinstance(element, nodes.Tuple): + lookups.append((index, len(element.itered()))) + _determine_starred_iteration_lookups(starred, element, lookups) + + stmt = self.statement(future=True) + if not isinstance(stmt, (nodes.Assign, nodes.For)): + raise InferenceError( + "Statement {stmt!r} enclosing {node!r} must be an Assign or For node.", + node=self, + stmt=stmt, + unknown=node, + context=context, + ) + + if context is None: + context = InferenceContext() + + if isinstance(stmt, nodes.Assign): + value = stmt.value + lhs = stmt.targets[0] + if not isinstance(lhs, nodes.BaseContainer): + yield util.Uninferable + return + + if sum(1 for _ in lhs.nodes_of_class(nodes.Starred)) > 1: + raise InferenceError( + "Too many starred arguments in the assignment targets {lhs!r}.", + node=self, + targets=lhs, + unknown=node, + context=context, + ) + + try: + rhs = next(value.infer(context)) + except (InferenceError, StopIteration): + yield util.Uninferable + return + if isinstance(rhs, util.UninferableBase) or not hasattr(rhs, "itered"): + yield util.Uninferable + return + + try: + elts = collections.deque(rhs.itered()) # type: ignore[union-attr] + except TypeError: + yield util.Uninferable + return + + # Unpack iteratively the values from the rhs of the assignment, + # until the find the starred node. What will remain will + # be the list of values which the Starred node will represent + # This is done in two steps, from left to right to remove + # anything before the starred node and from right to left + # to remove anything after the starred node. + + for index, left_node in enumerate(lhs.elts): + if not isinstance(left_node, nodes.Starred): + if not elts: + break + elts.popleft() + continue + lhs_elts = collections.deque(reversed(lhs.elts[index:])) + for right_node in lhs_elts: + if not isinstance(right_node, nodes.Starred): + if not elts: + break + elts.pop() + continue + + # We're done unpacking. + packed = nodes.List( + ctx=Context.Store, + parent=self, + lineno=lhs.lineno, + col_offset=lhs.col_offset, + ) + packed.postinit(elts=list(elts)) + yield packed + break + + if isinstance(stmt, nodes.For): + try: + inferred_iterable = next(stmt.iter.infer(context=context)) + except (InferenceError, StopIteration): + yield util.Uninferable + return + if isinstance(inferred_iterable, util.UninferableBase) or not hasattr( + inferred_iterable, "itered" + ): + yield util.Uninferable + return + try: + itered = inferred_iterable.itered() # type: ignore[union-attr] + except TypeError: + yield util.Uninferable + return + + target = stmt.target + + if not isinstance(target, nodes.Tuple): + raise InferenceError( + "Could not make sense of this, the target must be a tuple", + context=context, + ) + + lookups: list[tuple[int, int]] = [] + _determine_starred_iteration_lookups(self, target, lookups) + if not lookups: + raise InferenceError( + "Could not make sense of this, needs at least a lookup", context=context + ) + + # Make the last lookup a slice, since that what we want for a Starred node + last_element_index, last_element_length = lookups[-1] + is_starred_last = last_element_index == (last_element_length - 1) + + lookup_slice = slice( + last_element_index, + None if is_starred_last else (last_element_length - last_element_index), + ) + last_lookup = lookup_slice + + for element in itered: + # We probably want to infer the potential values *for each* element in an + # iterable, but we can't infer a list of all values, when only a list of + # step values are expected: + # + # for a, *b in [...]: + # b + # + # *b* should now point to just the elements at that particular iteration step, + # which astroid can't know about. + + found_element = None + for index, lookup in enumerate(lookups): + if not hasattr(element, "itered"): + break + if index + 1 is len(lookups): + cur_lookup: slice | int = last_lookup + else: + # Grab just the index, not the whole length + cur_lookup = lookup[0] + try: + itered_inner_element = element.itered() + element = itered_inner_element[cur_lookup] + except IndexError: + break + except TypeError: + # Most likely the itered() call failed, cannot make sense of this + yield util.Uninferable + return + else: + found_element = element + + unpacked = nodes.List( + ctx=Context.Store, + parent=self, + lineno=self.lineno, + col_offset=self.col_offset, + ) + unpacked.postinit(elts=found_element or []) + yield unpacked + return + + yield util.Uninferable + + +nodes.Starred.assigned_stmts = starred_assigned_stmts + + +@decorators.yes_if_nothing_inferred +def match_mapping_assigned_stmts( + self: nodes.MatchMapping, + node: nodes.AssignName, + context: InferenceContext | None = None, + assign_path: None = None, +) -> Generator[nodes.NodeNG, None, None]: + """Return empty generator (return -> raises StopIteration) so inferred value + is Uninferable. + """ + return + yield + + +nodes.MatchMapping.assigned_stmts = match_mapping_assigned_stmts + + +@decorators.yes_if_nothing_inferred +def match_star_assigned_stmts( + self: nodes.MatchStar, + node: nodes.AssignName, + context: InferenceContext | None = None, + assign_path: None = None, +) -> Generator[nodes.NodeNG, None, None]: + """Return empty generator (return -> raises StopIteration) so inferred value + is Uninferable. + """ + return + yield + + +nodes.MatchStar.assigned_stmts = match_star_assigned_stmts + + +@decorators.yes_if_nothing_inferred +def match_as_assigned_stmts( + self: nodes.MatchAs, + node: nodes.AssignName, + context: InferenceContext | None = None, + assign_path: None = None, +) -> Generator[nodes.NodeNG, None, None]: + """Infer MatchAs as the Match subject if it's the only MatchCase pattern + else raise StopIteration to yield Uninferable. + """ + if ( + isinstance(self.parent, nodes.MatchCase) + and isinstance(self.parent.parent, nodes.Match) + and self.pattern is None + ): + yield self.parent.parent.subject + + +nodes.MatchAs.assigned_stmts = match_as_assigned_stmts diff --git a/venv/lib/python3.10/site-packages/astroid/raw_building.py b/venv/lib/python3.10/site-packages/astroid/raw_building.py new file mode 100644 index 00000000..4409a639 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/raw_building.py @@ -0,0 +1,643 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""this module contains a set of functions to create astroid trees from scratch +(build_* functions) or from living object (object_build_* functions) +""" + +from __future__ import annotations + +import builtins +import inspect +import io +import logging +import os +import sys +import types +import warnings +from collections.abc import Iterable +from contextlib import redirect_stderr, redirect_stdout +from typing import Any, Union + +from astroid import bases, nodes +from astroid.const import _EMPTY_OBJECT_MARKER, IS_PYPY +from astroid.manager import AstroidManager +from astroid.nodes import node_classes + +logger = logging.getLogger(__name__) + + +_FunctionTypes = Union[ + types.FunctionType, + types.MethodType, + types.BuiltinFunctionType, + types.WrapperDescriptorType, + types.MethodDescriptorType, + types.ClassMethodDescriptorType, +] + +# the keys of CONST_CLS eg python builtin types +_CONSTANTS = tuple(node_classes.CONST_CLS) +_BUILTINS = vars(builtins) +TYPE_NONE = type(None) +TYPE_NOTIMPLEMENTED = type(NotImplemented) +TYPE_ELLIPSIS = type(...) + + +def _attach_local_node(parent, node, name: str) -> None: + node.name = name # needed by add_local_node + parent.add_local_node(node) + + +def _add_dunder_class(func, member) -> None: + """Add a __class__ member to the given func node, if we can determine it.""" + python_cls = member.__class__ + cls_name = getattr(python_cls, "__name__", None) + if not cls_name: + return + cls_bases = [ancestor.__name__ for ancestor in python_cls.__bases__] + ast_klass = build_class(cls_name, cls_bases, python_cls.__doc__) + func.instance_attrs["__class__"] = [ast_klass] + + +def attach_dummy_node(node, name: str, runtime_object=_EMPTY_OBJECT_MARKER) -> None: + """create a dummy node and register it in the locals of the given + node with the specified name + """ + enode = nodes.EmptyNode() + enode.object = runtime_object + _attach_local_node(node, enode, name) + + +def attach_const_node(node, name: str, value) -> None: + """create a Const node and register it in the locals of the given + node with the specified name + """ + if name not in node.special_attributes: + _attach_local_node(node, nodes.const_factory(value), name) + + +def attach_import_node(node, modname: str, membername: str) -> None: + """create a ImportFrom node and register it in the locals of the given + node with the specified name + """ + from_node = nodes.ImportFrom(modname, [(membername, None)]) + _attach_local_node(node, from_node, membername) + + +def build_module(name: str, doc: str | None = None) -> nodes.Module: + """create and initialize an astroid Module node""" + node = nodes.Module(name, pure_python=False, package=False) + node.postinit( + body=[], + doc_node=nodes.Const(value=doc) if doc else None, + ) + return node + + +def build_class( + name: str, basenames: Iterable[str] = (), doc: str | None = None +) -> nodes.ClassDef: + """Create and initialize an astroid ClassDef node.""" + node = nodes.ClassDef(name) + node.postinit( + bases=[nodes.Name(name=base, parent=node) for base in basenames], + body=[], + decorators=None, + doc_node=nodes.Const(value=doc) if doc else None, + ) + return node + + +def build_function( + name: str, + args: list[str] | None = None, + posonlyargs: list[str] | None = None, + defaults: list[Any] | None = None, + doc: str | None = None, + kwonlyargs: list[str] | None = None, + kwonlydefaults: list[Any] | None = None, +) -> nodes.FunctionDef: + """create and initialize an astroid FunctionDef node""" + # first argument is now a list of decorators + func = nodes.FunctionDef(name) + argsnode = nodes.Arguments(parent=func) + + # If args is None we don't have any information about the signature + # (in contrast to when there are no arguments and args == []). We pass + # this to the builder to indicate this. + if args is not None: + arguments = [nodes.AssignName(name=arg, parent=argsnode) for arg in args] + else: + arguments = None + + default_nodes: list[nodes.NodeNG] | None = [] + if defaults is not None: + for default in defaults: + default_node = nodes.const_factory(default) + default_node.parent = argsnode + default_nodes.append(default_node) + else: + default_nodes = None + + kwonlydefault_nodes: list[nodes.NodeNG | None] | None = [] + if kwonlydefaults is not None: + for kwonlydefault in kwonlydefaults: + kwonlydefault_node = nodes.const_factory(kwonlydefault) + kwonlydefault_node.parent = argsnode + kwonlydefault_nodes.append(kwonlydefault_node) + else: + kwonlydefault_nodes = None + + argsnode.postinit( + args=arguments, + defaults=default_nodes, + kwonlyargs=[ + nodes.AssignName(name=arg, parent=argsnode) for arg in kwonlyargs or () + ], + kw_defaults=kwonlydefault_nodes, + annotations=[], + posonlyargs=[ + nodes.AssignName(name=arg, parent=argsnode) for arg in posonlyargs or () + ], + ) + func.postinit( + args=argsnode, + body=[], + doc_node=nodes.Const(value=doc) if doc else None, + ) + if args: + register_arguments(func) + return func + + +def build_from_import(fromname: str, names: list[str]) -> nodes.ImportFrom: + """create and initialize an astroid ImportFrom import statement""" + return nodes.ImportFrom(fromname, [(name, None) for name in names]) + + +def register_arguments(func: nodes.FunctionDef, args: list | None = None) -> None: + """add given arguments to local + + args is a list that may contains nested lists + (i.e. def func(a, (b, c, d)): ...) + """ + # If no args are passed in, get the args from the function. + if args is None: + if func.args.vararg: + func.set_local(func.args.vararg, func.args) + if func.args.kwarg: + func.set_local(func.args.kwarg, func.args) + args = func.args.args + # If the function has no args, there is nothing left to do. + if args is None: + return + for arg in args: + if isinstance(arg, nodes.AssignName): + func.set_local(arg.name, arg) + else: + register_arguments(func, arg.elts) + + +def object_build_class( + node: nodes.Module | nodes.ClassDef, member: type, localname: str +) -> nodes.ClassDef: + """create astroid for a living class object""" + basenames = [base.__name__ for base in member.__bases__] + return _base_class_object_build(node, member, basenames, localname=localname) + + +def _get_args_info_from_callable( + member: _FunctionTypes, +) -> tuple[list[str], list[str], list[Any], list[str], list[Any]]: + """Returns args, posonlyargs, defaults, kwonlyargs. + + :note: currently ignores the return annotation. + """ + signature = inspect.signature(member) + args: list[str] = [] + defaults: list[Any] = [] + posonlyargs: list[str] = [] + kwonlyargs: list[str] = [] + kwonlydefaults: list[Any] = [] + + for param_name, param in signature.parameters.items(): + if param.kind == inspect.Parameter.POSITIONAL_ONLY: + posonlyargs.append(param_name) + elif param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: + args.append(param_name) + elif param.kind == inspect.Parameter.VAR_POSITIONAL: + args.append(param_name) + elif param.kind == inspect.Parameter.VAR_KEYWORD: + args.append(param_name) + elif param.kind == inspect.Parameter.KEYWORD_ONLY: + kwonlyargs.append(param_name) + if param.default is not inspect.Parameter.empty: + kwonlydefaults.append(param.default) + continue + if param.default is not inspect.Parameter.empty: + defaults.append(param.default) + + return args, posonlyargs, defaults, kwonlyargs, kwonlydefaults + + +def object_build_function( + node: nodes.Module | nodes.ClassDef, member: _FunctionTypes, localname: str +) -> None: + """create astroid for a living function object""" + ( + args, + posonlyargs, + defaults, + kwonlyargs, + kwonly_defaults, + ) = _get_args_info_from_callable(member) + + func = build_function( + getattr(member, "__name__", None) or localname, + args, + posonlyargs, + defaults, + member.__doc__, + kwonlyargs=kwonlyargs, + kwonlydefaults=kwonly_defaults, + ) + + node.add_local_node(func, localname) + + +def object_build_datadescriptor( + node: nodes.Module | nodes.ClassDef, member: type, name: str +) -> nodes.ClassDef: + """create astroid for a living data descriptor object""" + return _base_class_object_build(node, member, [], name) + + +def object_build_methoddescriptor( + node: nodes.Module | nodes.ClassDef, + member: _FunctionTypes, + localname: str, +) -> None: + """create astroid for a living method descriptor object""" + # FIXME get arguments ? + func = build_function( + getattr(member, "__name__", None) or localname, doc=member.__doc__ + ) + node.add_local_node(func, localname) + _add_dunder_class(func, member) + + +def _base_class_object_build( + node: nodes.Module | nodes.ClassDef, + member: type, + basenames: list[str], + name: str | None = None, + localname: str | None = None, +) -> nodes.ClassDef: + """create astroid for a living class object, with a given set of base names + (e.g. ancestors) + """ + class_name = name or getattr(member, "__name__", None) or localname + assert isinstance(class_name, str) + klass = build_class( + class_name, + basenames, + member.__doc__, + ) + klass._newstyle = isinstance(member, type) + node.add_local_node(klass, localname) + try: + # limit the instantiation trick since it's too dangerous + # (such as infinite test execution...) + # this at least resolves common case such as Exception.args, + # OSError.errno + if issubclass(member, Exception): + instdict = member().__dict__ + else: + raise TypeError + except TypeError: + pass + else: + for item_name, obj in instdict.items(): + valnode = nodes.EmptyNode() + valnode.object = obj + valnode.parent = klass + valnode.lineno = 1 + klass.instance_attrs[item_name] = [valnode] + return klass + + +def _build_from_function( + node: nodes.Module | nodes.ClassDef, + name: str, + member: _FunctionTypes, + module: types.ModuleType, +) -> None: + # verify this is not an imported function + try: + code = member.__code__ # type: ignore[union-attr] + except AttributeError: + # Some implementations don't provide the code object, + # such as Jython. + code = None + filename = getattr(code, "co_filename", None) + if filename is None: + assert isinstance(member, object) + object_build_methoddescriptor(node, member, name) + elif filename != getattr(module, "__file__", None): + attach_dummy_node(node, name, member) + else: + object_build_function(node, member, name) + + +def _safe_has_attribute(obj, member: str) -> bool: + """Required because unexpected RunTimeError can be raised. + + See https://github.com/PyCQA/astroid/issues/1958 + """ + try: + return hasattr(obj, member) + except Exception: # pylint: disable=broad-except + return False + + +class InspectBuilder: + """class for building nodes from living object + + this is actually a really minimal representation, including only Module, + FunctionDef and ClassDef nodes and some others as guessed. + """ + + def __init__(self, manager_instance: AstroidManager | None = None) -> None: + self._manager = manager_instance or AstroidManager() + self._done: dict[types.ModuleType | type, nodes.Module | nodes.ClassDef] = {} + self._module: types.ModuleType + + def inspect_build( + self, + module: types.ModuleType, + modname: str | None = None, + path: str | None = None, + ) -> nodes.Module: + """build astroid from a living module (i.e. using inspect) + this is used when there is no python source code available (either + because it's a built-in module or because the .py is not available) + """ + self._module = module + if modname is None: + modname = module.__name__ + try: + node = build_module(modname, module.__doc__) + except AttributeError: + # in jython, java modules have no __doc__ (see #109562) + node = build_module(modname) + if path is None: + node.path = node.file = path + else: + node.path = [os.path.abspath(path)] + node.file = node.path[0] + node.name = modname + self._manager.cache_module(node) + node.package = hasattr(module, "__path__") + self._done = {} + self.object_build(node, module) + return node + + def object_build( + self, node: nodes.Module | nodes.ClassDef, obj: types.ModuleType | type + ) -> None: + """recursive method which create a partial ast from real objects + (only function, class, and method are handled) + """ + if obj in self._done: + return None + self._done[obj] = node + for name in dir(obj): + # inspect.ismethod() and inspect.isbuiltin() in PyPy return + # the opposite of what they do in CPython for __class_getitem__. + pypy__class_getitem__ = IS_PYPY and name == "__class_getitem__" + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + member = getattr(obj, name) + except AttributeError: + # damned ExtensionClass.Base, I know you're there ! + attach_dummy_node(node, name) + continue + if inspect.ismethod(member) and not pypy__class_getitem__: + member = member.__func__ + if inspect.isfunction(member): + _build_from_function(node, name, member, self._module) + elif inspect.isbuiltin(member) or pypy__class_getitem__: + if self.imported_member(node, member, name): + continue + object_build_methoddescriptor(node, member, name) + elif inspect.isclass(member): + if self.imported_member(node, member, name): + continue + if member in self._done: + class_node = self._done[member] + assert isinstance(class_node, nodes.ClassDef) + if class_node not in node.locals.get(name, ()): + node.add_local_node(class_node, name) + else: + class_node = object_build_class(node, member, name) + # recursion + self.object_build(class_node, member) + if name == "__class__" and class_node.parent is None: + class_node.parent = self._done[self._module] + elif inspect.ismethoddescriptor(member): + object_build_methoddescriptor(node, member, name) + elif inspect.isdatadescriptor(member): + object_build_datadescriptor(node, member, name) + elif isinstance(member, _CONSTANTS): + attach_const_node(node, name, member) + elif inspect.isroutine(member): + # This should be called for Jython, where some builtin + # methods aren't caught by isbuiltin branch. + _build_from_function(node, name, member, self._module) + elif _safe_has_attribute(member, "__all__"): + module = build_module(name) + _attach_local_node(node, module, name) + # recursion + self.object_build(module, member) + else: + # create an empty node so that the name is actually defined + attach_dummy_node(node, name, member) + return None + + def imported_member(self, node, member, name: str) -> bool: + """verify this is not an imported class or handle it""" + # /!\ some classes like ExtensionClass doesn't have a __module__ + # attribute ! Also, this may trigger an exception on badly built module + # (see http://www.logilab.org/ticket/57299 for instance) + try: + modname = getattr(member, "__module__", None) + except TypeError: + modname = None + if modname is None: + if name in {"__new__", "__subclasshook__"}: + # Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14) + # >>> print object.__new__.__module__ + # None + modname = builtins.__name__ + else: + attach_dummy_node(node, name, member) + return True + + # On PyPy during bootstrapping we infer _io while _module is + # builtins. In CPython _io names itself io, see http://bugs.python.org/issue18602 + # Therefore, this basically checks whether we are not in PyPy. + if modname == "_io" and not self._module.__name__ == "builtins": + return False + + real_name = {"gtk": "gtk_gtk"}.get(modname, modname) + + if real_name != self._module.__name__: + # check if it sounds valid and then add an import node, else use a + # dummy node + try: + with redirect_stderr(io.StringIO()) as stderr, redirect_stdout( + io.StringIO() + ) as stdout: + getattr(sys.modules[modname], name) + stderr_value = stderr.getvalue() + if stderr_value: + logger.error( + "Captured stderr while getting %s from %s:\n%s", + name, + sys.modules[modname], + stderr_value, + ) + stdout_value = stdout.getvalue() + if stdout_value: + logger.info( + "Captured stdout while getting %s from %s:\n%s", + name, + sys.modules[modname], + stdout_value, + ) + except (KeyError, AttributeError): + attach_dummy_node(node, name, member) + else: + attach_import_node(node, modname, name) + return True + return False + + +# astroid bootstrapping ###################################################### + +_CONST_PROXY: dict[type, nodes.ClassDef] = {} + + +def _set_proxied(const) -> nodes.ClassDef: + # TODO : find a nicer way to handle this situation; + return _CONST_PROXY[const.value.__class__] + + +def _astroid_bootstrapping() -> None: + """astroid bootstrapping the builtins module""" + # this boot strapping is necessary since we need the Const nodes to + # inspect_build builtins, and then we can proxy Const + builder = InspectBuilder() + astroid_builtin = builder.inspect_build(builtins) + + for cls, node_cls in node_classes.CONST_CLS.items(): + if cls is TYPE_NONE: + proxy = build_class("NoneType") + proxy.parent = astroid_builtin + elif cls is TYPE_NOTIMPLEMENTED: + proxy = build_class("NotImplementedType") + proxy.parent = astroid_builtin + elif cls is TYPE_ELLIPSIS: + proxy = build_class("Ellipsis") + proxy.parent = astroid_builtin + else: + proxy = astroid_builtin.getattr(cls.__name__)[0] + assert isinstance(proxy, nodes.ClassDef) + if cls in (dict, list, set, tuple): + node_cls._proxied = proxy + else: + _CONST_PROXY[cls] = proxy + + # Set the builtin module as parent for some builtins. + nodes.Const._proxied = property(_set_proxied) + + _GeneratorType = nodes.ClassDef(types.GeneratorType.__name__) + _GeneratorType.parent = astroid_builtin + generator_doc_node = ( + nodes.Const(value=types.GeneratorType.__doc__) + if types.GeneratorType.__doc__ + else None + ) + _GeneratorType.postinit( + bases=[], + body=[], + decorators=None, + doc_node=generator_doc_node, + ) + bases.Generator._proxied = _GeneratorType + builder.object_build(bases.Generator._proxied, types.GeneratorType) + + if hasattr(types, "AsyncGeneratorType"): + _AsyncGeneratorType = nodes.ClassDef(types.AsyncGeneratorType.__name__) + _AsyncGeneratorType.parent = astroid_builtin + async_generator_doc_node = ( + nodes.Const(value=types.AsyncGeneratorType.__doc__) + if types.AsyncGeneratorType.__doc__ + else None + ) + _AsyncGeneratorType.postinit( + bases=[], + body=[], + decorators=None, + doc_node=async_generator_doc_node, + ) + bases.AsyncGenerator._proxied = _AsyncGeneratorType + builder.object_build(bases.AsyncGenerator._proxied, types.AsyncGeneratorType) + + if hasattr(types, "UnionType"): + _UnionTypeType = nodes.ClassDef(types.UnionType.__name__) + _UnionTypeType.parent = astroid_builtin + union_type_doc_node = ( + nodes.Const(value=types.UnionType.__doc__) + if types.UnionType.__doc__ + else None + ) + _UnionTypeType.postinit( + bases=[], + body=[], + decorators=None, + doc_node=union_type_doc_node, + ) + bases.UnionType._proxied = _UnionTypeType + builder.object_build(bases.UnionType._proxied, types.UnionType) + + builtin_types = ( + types.GetSetDescriptorType, + types.GeneratorType, + types.MemberDescriptorType, + TYPE_NONE, + TYPE_NOTIMPLEMENTED, + types.FunctionType, + types.MethodType, + types.BuiltinFunctionType, + types.ModuleType, + types.TracebackType, + ) + for _type in builtin_types: + if _type.__name__ not in astroid_builtin: + klass = nodes.ClassDef(_type.__name__) + klass.parent = astroid_builtin + klass.postinit( + bases=[], + body=[], + decorators=None, + doc_node=nodes.Const(value=_type.__doc__) if _type.__doc__ else None, + ) + builder.object_build(klass, _type) + astroid_builtin[_type.__name__] = klass + + +_astroid_bootstrapping() diff --git a/venv/lib/python3.10/site-packages/astroid/rebuilder.py b/venv/lib/python3.10/site-packages/astroid/rebuilder.py new file mode 100644 index 00000000..6e996def --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/rebuilder.py @@ -0,0 +1,2110 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +"""This module contains utilities for rebuilding an _ast tree in +order to get a single Astroid representation. +""" + +from __future__ import annotations + +import ast +import sys +import token +from collections.abc import Callable, Generator +from io import StringIO +from tokenize import TokenInfo, generate_tokens +from typing import TYPE_CHECKING, TypeVar, Union, cast, overload + +from astroid import nodes +from astroid._ast import ParserModule, get_parser_module, parse_function_type_comment +from astroid.const import IS_PYPY, PY38, PY38_PLUS, PY39_PLUS, Context +from astroid.manager import AstroidManager +from astroid.nodes import NodeNG +from astroid.nodes.utils import Position +from astroid.typing import SuccessfulInferenceResult + +if sys.version_info >= (3, 8): + from typing import Final +else: + from typing_extensions import Final + + +REDIRECT: Final[dict[str, str]] = { + "arguments": "Arguments", + "comprehension": "Comprehension", + "ListCompFor": "Comprehension", + "GenExprFor": "Comprehension", + "excepthandler": "ExceptHandler", + "keyword": "Keyword", + "match_case": "MatchCase", +} + + +T_Doc = TypeVar( + "T_Doc", + "ast.Module", + "ast.ClassDef", + Union["ast.FunctionDef", "ast.AsyncFunctionDef"], +) +_FunctionT = TypeVar("_FunctionT", nodes.FunctionDef, nodes.AsyncFunctionDef) +_ForT = TypeVar("_ForT", nodes.For, nodes.AsyncFor) +_WithT = TypeVar("_WithT", nodes.With, nodes.AsyncWith) +NodesWithDocsType = Union[nodes.Module, nodes.ClassDef, nodes.FunctionDef] + + +# noinspection PyMethodMayBeStatic +class TreeRebuilder: + """Rebuilds the _ast tree to become an Astroid tree.""" + + def __init__( + self, + manager: AstroidManager, + parser_module: ParserModule | None = None, + data: str | None = None, + ) -> None: + self._manager = manager + self._data = data.split("\n") if data else None + self._global_names: list[dict[str, list[nodes.Global]]] = [] + self._import_from_nodes: list[nodes.ImportFrom] = [] + self._delayed_assattr: list[nodes.AssignAttr] = [] + self._visit_meths: dict[type[ast.AST], Callable[[ast.AST, NodeNG], NodeNG]] = {} + + if parser_module is None: + self._parser_module = get_parser_module() + else: + self._parser_module = parser_module + self._module = self._parser_module.module + + def _get_doc(self, node: T_Doc) -> tuple[T_Doc, ast.Constant | ast.Str | None]: + """Return the doc ast node.""" + try: + if node.body and isinstance(node.body[0], self._module.Expr): + first_value = node.body[0].value + if isinstance(first_value, self._module.Str) or ( + PY38_PLUS + and isinstance(first_value, self._module.Constant) + and isinstance(first_value.value, str) + ): + doc_ast_node = first_value + node.body = node.body[1:] + # The ast parser of python < 3.8 sets col_offset of multi-line strings to -1 + # as it is unable to determine the value correctly. We reset this to None. + if doc_ast_node.col_offset == -1: + doc_ast_node.col_offset = None + return node, doc_ast_node + except IndexError: + pass # ast built from scratch + return node, None + + def _get_context( + self, + node: ( + ast.Attribute + | ast.List + | ast.Name + | ast.Subscript + | ast.Starred + | ast.Tuple + ), + ) -> Context: + return self._parser_module.context_classes.get(type(node.ctx), Context.Load) + + def _get_position_info( + self, + node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef, + parent: nodes.ClassDef | nodes.FunctionDef | nodes.AsyncFunctionDef, + ) -> Position | None: + """Return position information for ClassDef and FunctionDef nodes. + + In contrast to AST positions, these only include the actual keyword(s) + and the class / function name. + + >>> @decorator + >>> async def some_func(var: int) -> None: + >>> ^^^^^^^^^^^^^^^^^^^ + """ + if not self._data: + return None + end_lineno: int | None = getattr(node, "end_lineno", None) + if node.body: + end_lineno = node.body[0].lineno + # pylint: disable-next=unsubscriptable-object + data = "\n".join(self._data[node.lineno - 1 : end_lineno]) + + start_token: TokenInfo | None = None + keyword_tokens: tuple[int, ...] = (token.NAME,) + if isinstance(parent, nodes.AsyncFunctionDef): + search_token = "async" + elif isinstance(parent, nodes.FunctionDef): + search_token = "def" + else: + search_token = "class" + + for t in generate_tokens(StringIO(data).readline): + if ( + start_token is not None + and t.type == token.NAME + and t.string == node.name + ): + break + if t.type in keyword_tokens: + if t.string == search_token: + start_token = t + continue + if t.string in {"def"}: + continue + start_token = None + else: + return None + + return Position( + lineno=node.lineno + start_token.start[0] - 1, + col_offset=start_token.start[1], + end_lineno=node.lineno + t.end[0] - 1, + end_col_offset=t.end[1], + ) + + def _fix_doc_node_position(self, node: NodesWithDocsType) -> None: + """Fix start and end position of doc nodes for Python < 3.8.""" + if not self._data or not node.doc_node or node.lineno is None: + return + if PY38_PLUS: + return + + lineno = node.lineno or 1 # lineno of modules is 0 + end_range: int | None = node.doc_node.lineno + if IS_PYPY and not PY39_PLUS: + end_range = None + # pylint: disable-next=unsubscriptable-object + data = "\n".join(self._data[lineno - 1 : end_range]) + + found_start, found_end = False, False + open_brackets = 0 + skip_token: set[int] = {token.NEWLINE, token.INDENT, token.NL, token.COMMENT} + + if isinstance(node, nodes.Module): + found_end = True + + for t in generate_tokens(StringIO(data).readline): + if found_end is False: + if ( + found_start is False + and t.type == token.NAME + and t.string in {"def", "class"} + ): + found_start = True + elif found_start is True and t.type == token.OP: + if t.exact_type == token.COLON and open_brackets == 0: + found_end = True + elif t.exact_type == token.LPAR: + open_brackets += 1 + elif t.exact_type == token.RPAR: + open_brackets -= 1 + continue + if t.type in skip_token: + continue + if t.type == token.STRING: + break + return + else: + return + + node.doc_node.lineno = lineno + t.start[0] - 1 + node.doc_node.col_offset = t.start[1] + node.doc_node.end_lineno = lineno + t.end[0] - 1 + node.doc_node.end_col_offset = t.end[1] + + def _reset_end_lineno(self, newnode: nodes.NodeNG) -> None: + """Reset end_lineno and end_col_offset attributes for PyPy 3.8. + + For some nodes, these are either set to -1 or only partially assigned. + To keep consistency across astroid and pylint, reset all. + + This has been fixed in PyPy 3.9. + For reference, an (incomplete) list of nodes with issues: + - ClassDef - For + - FunctionDef - While + - Call - If + - Decorators - TryExcept + - With - TryFinally + - Assign + """ + newnode.end_lineno = None + newnode.end_col_offset = None + for child_node in newnode.get_children(): + self._reset_end_lineno(child_node) + + def visit_module( + self, node: ast.Module, modname: str, modpath: str, package: bool + ) -> nodes.Module: + """Visit a Module node by returning a fresh instance of it. + + Note: Method not called by 'visit' + """ + node, doc_ast_node = self._get_doc(node) + newnode = nodes.Module( + name=modname, + file=modpath, + path=[modpath], + package=package, + parent=None, + ) + newnode.postinit( + [self.visit(child, newnode) for child in node.body], + doc_node=self.visit(doc_ast_node, newnode), + ) + self._fix_doc_node_position(newnode) + if IS_PYPY and PY38: + self._reset_end_lineno(newnode) + return newnode + + if TYPE_CHECKING: # noqa: C901 + + @overload + def visit(self, node: ast.arg, parent: NodeNG) -> nodes.AssignName: + ... + + @overload + def visit(self, node: ast.arguments, parent: NodeNG) -> nodes.Arguments: + ... + + @overload + def visit(self, node: ast.Assert, parent: NodeNG) -> nodes.Assert: + ... + + @overload + def visit( + self, node: ast.AsyncFunctionDef, parent: NodeNG + ) -> nodes.AsyncFunctionDef: + ... + + @overload + def visit(self, node: ast.AsyncFor, parent: NodeNG) -> nodes.AsyncFor: + ... + + @overload + def visit(self, node: ast.Await, parent: NodeNG) -> nodes.Await: + ... + + @overload + def visit(self, node: ast.AsyncWith, parent: NodeNG) -> nodes.AsyncWith: + ... + + @overload + def visit(self, node: ast.Assign, parent: NodeNG) -> nodes.Assign: + ... + + @overload + def visit(self, node: ast.AnnAssign, parent: NodeNG) -> nodes.AnnAssign: + ... + + @overload + def visit(self, node: ast.AugAssign, parent: NodeNG) -> nodes.AugAssign: + ... + + @overload + def visit(self, node: ast.BinOp, parent: NodeNG) -> nodes.BinOp: + ... + + @overload + def visit(self, node: ast.BoolOp, parent: NodeNG) -> nodes.BoolOp: + ... + + @overload + def visit(self, node: ast.Break, parent: NodeNG) -> nodes.Break: + ... + + @overload + def visit(self, node: ast.Call, parent: NodeNG) -> nodes.Call: + ... + + @overload + def visit(self, node: ast.ClassDef, parent: NodeNG) -> nodes.ClassDef: + ... + + @overload + def visit(self, node: ast.Continue, parent: NodeNG) -> nodes.Continue: + ... + + @overload + def visit(self, node: ast.Compare, parent: NodeNG) -> nodes.Compare: + ... + + @overload + def visit(self, node: ast.comprehension, parent: NodeNG) -> nodes.Comprehension: + ... + + @overload + def visit(self, node: ast.Delete, parent: NodeNG) -> nodes.Delete: + ... + + @overload + def visit(self, node: ast.Dict, parent: NodeNG) -> nodes.Dict: + ... + + @overload + def visit(self, node: ast.DictComp, parent: NodeNG) -> nodes.DictComp: + ... + + @overload + def visit(self, node: ast.Expr, parent: NodeNG) -> nodes.Expr: + ... + + @overload + def visit(self, node: ast.ExceptHandler, parent: NodeNG) -> nodes.ExceptHandler: + ... + + @overload + def visit(self, node: ast.For, parent: NodeNG) -> nodes.For: + ... + + @overload + def visit(self, node: ast.ImportFrom, parent: NodeNG) -> nodes.ImportFrom: + ... + + @overload + def visit(self, node: ast.FunctionDef, parent: NodeNG) -> nodes.FunctionDef: + ... + + @overload + def visit(self, node: ast.GeneratorExp, parent: NodeNG) -> nodes.GeneratorExp: + ... + + @overload + def visit(self, node: ast.Attribute, parent: NodeNG) -> nodes.Attribute: + ... + + @overload + def visit(self, node: ast.Global, parent: NodeNG) -> nodes.Global: + ... + + @overload + def visit(self, node: ast.If, parent: NodeNG) -> nodes.If: + ... + + @overload + def visit(self, node: ast.IfExp, parent: NodeNG) -> nodes.IfExp: + ... + + @overload + def visit(self, node: ast.Import, parent: NodeNG) -> nodes.Import: + ... + + @overload + def visit(self, node: ast.JoinedStr, parent: NodeNG) -> nodes.JoinedStr: + ... + + @overload + def visit( + self, node: ast.FormattedValue, parent: NodeNG + ) -> nodes.FormattedValue: + ... + + if sys.version_info >= (3, 8): + + @overload + def visit(self, node: ast.NamedExpr, parent: NodeNG) -> nodes.NamedExpr: + ... + + if sys.version_info < (3, 9): + # Not used in Python 3.9+ + @overload + def visit(self, node: ast.ExtSlice, parent: nodes.Subscript) -> nodes.Tuple: + ... + + @overload + def visit(self, node: ast.Index, parent: nodes.Subscript) -> NodeNG: + ... + + @overload + def visit(self, node: ast.keyword, parent: NodeNG) -> nodes.Keyword: + ... + + @overload + def visit(self, node: ast.Lambda, parent: NodeNG) -> nodes.Lambda: + ... + + @overload + def visit(self, node: ast.List, parent: NodeNG) -> nodes.List: + ... + + @overload + def visit(self, node: ast.ListComp, parent: NodeNG) -> nodes.ListComp: + ... + + @overload + def visit( + self, node: ast.Name, parent: NodeNG + ) -> nodes.Name | nodes.Const | nodes.AssignName | nodes.DelName: + ... + + @overload + def visit(self, node: ast.Nonlocal, parent: NodeNG) -> nodes.Nonlocal: + ... + + if sys.version_info < (3, 8): + # Not used in Python 3.8+ + @overload + def visit(self, node: ast.Ellipsis, parent: NodeNG) -> nodes.Const: + ... + + @overload + def visit(self, node: ast.NameConstant, parent: NodeNG) -> nodes.Const: + ... + + @overload + def visit(self, node: ast.Str, parent: NodeNG) -> nodes.Const: + ... + + @overload + def visit(self, node: ast.Bytes, parent: NodeNG) -> nodes.Const: + ... + + @overload + def visit(self, node: ast.Num, parent: NodeNG) -> nodes.Const: + ... + + @overload + def visit(self, node: ast.Constant, parent: NodeNG) -> nodes.Const: + ... + + @overload + def visit(self, node: ast.Pass, parent: NodeNG) -> nodes.Pass: + ... + + @overload + def visit(self, node: ast.Raise, parent: NodeNG) -> nodes.Raise: + ... + + @overload + def visit(self, node: ast.Return, parent: NodeNG) -> nodes.Return: + ... + + @overload + def visit(self, node: ast.Set, parent: NodeNG) -> nodes.Set: + ... + + @overload + def visit(self, node: ast.SetComp, parent: NodeNG) -> nodes.SetComp: + ... + + @overload + def visit(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice: + ... + + @overload + def visit(self, node: ast.Subscript, parent: NodeNG) -> nodes.Subscript: + ... + + @overload + def visit(self, node: ast.Starred, parent: NodeNG) -> nodes.Starred: + ... + + @overload + def visit( + self, node: ast.Try, parent: NodeNG + ) -> nodes.TryExcept | nodes.TryFinally: + ... + + @overload + def visit(self, node: ast.Tuple, parent: NodeNG) -> nodes.Tuple: + ... + + @overload + def visit(self, node: ast.UnaryOp, parent: NodeNG) -> nodes.UnaryOp: + ... + + @overload + def visit(self, node: ast.While, parent: NodeNG) -> nodes.While: + ... + + @overload + def visit(self, node: ast.With, parent: NodeNG) -> nodes.With: + ... + + @overload + def visit(self, node: ast.Yield, parent: NodeNG) -> nodes.Yield: + ... + + @overload + def visit(self, node: ast.YieldFrom, parent: NodeNG) -> nodes.YieldFrom: + ... + + if sys.version_info >= (3, 10): + + @overload + def visit(self, node: ast.Match, parent: NodeNG) -> nodes.Match: + ... + + @overload + def visit(self, node: ast.match_case, parent: NodeNG) -> nodes.MatchCase: + ... + + @overload + def visit(self, node: ast.MatchValue, parent: NodeNG) -> nodes.MatchValue: + ... + + @overload + def visit( + self, node: ast.MatchSingleton, parent: NodeNG + ) -> nodes.MatchSingleton: + ... + + @overload + def visit( + self, node: ast.MatchSequence, parent: NodeNG + ) -> nodes.MatchSequence: + ... + + @overload + def visit( + self, node: ast.MatchMapping, parent: NodeNG + ) -> nodes.MatchMapping: + ... + + @overload + def visit(self, node: ast.MatchClass, parent: NodeNG) -> nodes.MatchClass: + ... + + @overload + def visit(self, node: ast.MatchStar, parent: NodeNG) -> nodes.MatchStar: + ... + + @overload + def visit(self, node: ast.MatchAs, parent: NodeNG) -> nodes.MatchAs: + ... + + @overload + def visit(self, node: ast.MatchOr, parent: NodeNG) -> nodes.MatchOr: + ... + + @overload + def visit(self, node: ast.pattern, parent: NodeNG) -> nodes.Pattern: + ... + + @overload + def visit(self, node: ast.AST, parent: NodeNG) -> NodeNG: + ... + + @overload + def visit(self, node: None, parent: NodeNG) -> None: + ... + + def visit(self, node: ast.AST | None, parent: NodeNG) -> NodeNG | None: + if node is None: + return None + cls = node.__class__ + if cls in self._visit_meths: + visit_method = self._visit_meths[cls] + else: + cls_name = cls.__name__ + visit_name = "visit_" + REDIRECT.get(cls_name, cls_name).lower() + visit_method = getattr(self, visit_name) + self._visit_meths[cls] = visit_method + return visit_method(node, parent) + + def _save_assignment(self, node: nodes.AssignName | nodes.DelName) -> None: + """Save assignment situation since node.parent is not available yet.""" + if self._global_names and node.name in self._global_names[-1]: + node.root().set_local(node.name, node) + else: + assert node.parent + assert node.name + node.parent.set_local(node.name, node) + + def visit_arg(self, node: ast.arg, parent: NodeNG) -> nodes.AssignName: + """Visit an arg node by returning a fresh AssName instance.""" + return self.visit_assignname(node, parent, node.arg) + + def visit_arguments(self, node: ast.arguments, parent: NodeNG) -> nodes.Arguments: + """Visit an Arguments node by returning a fresh instance of it.""" + vararg: str | None = None + kwarg: str | None = None + newnode = nodes.Arguments( + node.vararg.arg if node.vararg else None, + node.kwarg.arg if node.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: NodeNG | None = None + kwargannotation: NodeNG | None = None + posonlyargs: list[nodes.AssignName] = [] + if node.vararg: + vararg = node.vararg.arg + varargannotation = self.visit(node.vararg.annotation, newnode) + if node.kwarg: + kwarg = node.kwarg.arg + kwargannotation = self.visit(node.kwarg.annotation, newnode) + + if PY38: + # In Python 3.8 'end_lineno' and 'end_col_offset' + # for 'kwonlyargs' don't include the annotation. + for arg in node.kwonlyargs: + if arg.annotation is not None: + arg.end_lineno = arg.annotation.end_lineno + arg.end_col_offset = arg.annotation.end_col_offset + + kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs] + kw_defaults = [self.visit(child, newnode) for child in node.kw_defaults] + annotations = [self.visit(arg.annotation, newnode) for arg in node.args] + kwonlyargs_annotations = [ + self.visit(arg.annotation, newnode) for arg in node.kwonlyargs + ] + + posonlyargs_annotations: list[NodeNG | None] = [] + if PY38_PLUS: + posonlyargs = [self.visit(child, newnode) for child in node.posonlyargs] + posonlyargs_annotations = [ + self.visit(arg.annotation, newnode) for arg in node.posonlyargs + ] + type_comment_args = [ + self.check_type_comment(child, parent=newnode) for child in node.args + ] + type_comment_kwonlyargs = [ + self.check_type_comment(child, parent=newnode) for child in node.kwonlyargs + ] + type_comment_posonlyargs: list[NodeNG | None] = [] + if PY38_PLUS: + type_comment_posonlyargs = [ + self.check_type_comment(child, parent=newnode) + for child in node.posonlyargs + ] + + newnode.postinit( + args=args, + defaults=defaults, + kwonlyargs=kwonlyargs, + posonlyargs=posonlyargs, + kw_defaults=kw_defaults, + annotations=annotations, + kwonlyargs_annotations=kwonlyargs_annotations, + posonlyargs_annotations=posonlyargs_annotations, + varargannotation=varargannotation, + kwargannotation=kwargannotation, + type_comment_args=type_comment_args, + type_comment_kwonlyargs=type_comment_kwonlyargs, + type_comment_posonlyargs=type_comment_posonlyargs, + ) + # save argument names in locals: + assert newnode.parent + if vararg: + newnode.parent.set_local(vararg, newnode) + if kwarg: + newnode.parent.set_local(kwarg, newnode) + return newnode + + def visit_assert(self, node: ast.Assert, parent: NodeNG) -> nodes.Assert: + """Visit a Assert node by returning a fresh instance of it.""" + newnode = nodes.Assert( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + msg: NodeNG | None = None + if node.msg: + msg = self.visit(node.msg, newnode) + newnode.postinit(self.visit(node.test, newnode), msg) + return newnode + + def check_type_comment( + self, + node: ( + ast.Assign | ast.arg | ast.For | ast.AsyncFor | ast.With | ast.AsyncWith + ), + parent: ( + nodes.Assign + | nodes.Arguments + | nodes.For + | nodes.AsyncFor + | nodes.With + | nodes.AsyncWith + ), + ) -> NodeNG | None: + type_comment = getattr(node, "type_comment", None) # Added in Python 3.8 + if not type_comment: + return None + + try: + type_comment_ast = self._parser_module.parse(type_comment) + except SyntaxError: + # Invalid type comment, just skip it. + return None + + # For '# type: # any comment' ast.parse returns a Module node, + # without any nodes in the body. + if not type_comment_ast.body: + return None + + type_object = self.visit(type_comment_ast.body[0], parent=parent) + if not isinstance(type_object, nodes.Expr): + return None + + return type_object.value + + def check_function_type_comment( + self, node: ast.FunctionDef | ast.AsyncFunctionDef, parent: NodeNG + ) -> tuple[NodeNG | None, list[NodeNG]] | None: + type_comment = getattr(node, "type_comment", None) # Added in Python 3.8 + 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 + + if not type_comment_ast: + return None + + returns: NodeNG | None = None + argtypes: list[NodeNG] = [ + self.visit(elem, parent) for elem in (type_comment_ast.argtypes or []) + ] + if type_comment_ast.returns: + returns = self.visit(type_comment_ast.returns, parent) + + return returns, argtypes + + def visit_asyncfunctiondef( + self, node: ast.AsyncFunctionDef, parent: NodeNG + ) -> nodes.AsyncFunctionDef: + return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent) + + def visit_asyncfor(self, node: ast.AsyncFor, parent: NodeNG) -> nodes.AsyncFor: + return self._visit_for(nodes.AsyncFor, node, parent) + + def visit_await(self, node: ast.Await, parent: NodeNG) -> nodes.Await: + newnode = nodes.Await( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit(value=self.visit(node.value, newnode)) + return newnode + + def visit_asyncwith(self, node: ast.AsyncWith, parent: NodeNG) -> nodes.AsyncWith: + return self._visit_with(nodes.AsyncWith, node, parent) + + def visit_assign(self, node: ast.Assign, parent: NodeNG) -> nodes.Assign: + """Visit a Assign node by returning a fresh instance of it.""" + newnode = nodes.Assign( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + type_annotation = self.check_type_comment(node, parent=newnode) + newnode.postinit( + targets=[self.visit(child, newnode) for child in node.targets], + value=self.visit(node.value, newnode), + type_annotation=type_annotation, + ) + return newnode + + def visit_annassign(self, node: ast.AnnAssign, parent: NodeNG) -> nodes.AnnAssign: + """Visit an AnnAssign node by returning a fresh instance of it.""" + newnode = nodes.AnnAssign( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + target=self.visit(node.target, newnode), + annotation=self.visit(node.annotation, newnode), + simple=node.simple, + value=self.visit(node.value, newnode), + ) + return newnode + + @overload + def visit_assignname( + self, node: ast.AST, parent: NodeNG, node_name: str + ) -> nodes.AssignName: + ... + + @overload + def visit_assignname(self, node: ast.AST, parent: NodeNG, node_name: None) -> None: + ... + + def visit_assignname( + self, node: ast.AST, parent: NodeNG, node_name: str | None + ) -> nodes.AssignName | None: + """Visit a node and return a AssignName node. + + Note: Method not called by 'visit' + """ + if node_name is None: + return None + newnode = nodes.AssignName( + name=node_name, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + self._save_assignment(newnode) + return newnode + + def visit_augassign(self, node: ast.AugAssign, parent: NodeNG) -> nodes.AugAssign: + """Visit a AugAssign node by returning a fresh instance of it.""" + newnode = nodes.AugAssign( + op=self._parser_module.bin_op_classes[type(node.op)] + "=", + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.target, newnode), self.visit(node.value, newnode) + ) + return newnode + + def visit_binop(self, node: ast.BinOp, parent: NodeNG) -> nodes.BinOp: + """Visit a BinOp node by returning a fresh instance of it.""" + newnode = nodes.BinOp( + op=self._parser_module.bin_op_classes[type(node.op)], + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.left, newnode), self.visit(node.right, newnode) + ) + return newnode + + def visit_boolop(self, node: ast.BoolOp, parent: NodeNG) -> nodes.BoolOp: + """Visit a BoolOp node by returning a fresh instance of it.""" + newnode = nodes.BoolOp( + op=self._parser_module.bool_op_classes[type(node.op)], + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit([self.visit(child, newnode) for child in node.values]) + return newnode + + def visit_break(self, node: ast.Break, parent: NodeNG) -> nodes.Break: + """Visit a Break node by returning a fresh instance of it.""" + return nodes.Break( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + + def visit_call(self, node: ast.Call, parent: NodeNG) -> nodes.Call: + """Visit a CallFunc node by returning a fresh instance of it.""" + newnode = nodes.Call( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + func=self.visit(node.func, newnode), + args=[self.visit(child, newnode) for child in node.args], + keywords=[self.visit(child, newnode) for child in node.keywords], + ) + return newnode + + def visit_classdef( + self, node: ast.ClassDef, parent: NodeNG, newstyle: bool = True + ) -> nodes.ClassDef: + """Visit a ClassDef node to become astroid.""" + node, doc_ast_node = self._get_doc(node) + newnode = nodes.ClassDef( + name=node.name, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + metaclass = None + for keyword in node.keywords: + if keyword.arg == "metaclass": + metaclass = self.visit(keyword, newnode).value + break + decorators = self.visit_decorators(node, newnode) + newnode.postinit( + [self.visit(child, newnode) for child in node.bases], + [self.visit(child, newnode) for child in node.body], + decorators, + newstyle, + metaclass, + [ + self.visit(kwd, newnode) + for kwd in node.keywords + if kwd.arg != "metaclass" + ], + position=self._get_position_info(node, newnode), + doc_node=self.visit(doc_ast_node, newnode), + ) + self._fix_doc_node_position(newnode) + return newnode + + def visit_continue(self, node: ast.Continue, parent: NodeNG) -> nodes.Continue: + """Visit a Continue node by returning a fresh instance of it.""" + return nodes.Continue( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + + def visit_compare(self, node: ast.Compare, parent: NodeNG) -> nodes.Compare: + """Visit a Compare node by returning a fresh instance of it.""" + newnode = nodes.Compare( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.left, newnode), + [ + ( + self._parser_module.cmp_op_classes[op.__class__], + self.visit(expr, newnode), + ) + for (op, expr) in zip(node.ops, node.comparators) + ], + ) + return newnode + + def visit_comprehension( + self, node: ast.comprehension, parent: NodeNG + ) -> nodes.Comprehension: + """Visit a Comprehension node by returning a fresh instance of it.""" + newnode = nodes.Comprehension(parent) + newnode.postinit( + self.visit(node.target, newnode), + self.visit(node.iter, newnode), + [self.visit(child, newnode) for child in node.ifs], + bool(node.is_async), + ) + return newnode + + def visit_decorators( + self, + node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef, + parent: NodeNG, + ) -> nodes.Decorators | None: + """Visit a Decorators node by returning a fresh instance of it. + + Note: Method not called by 'visit' + """ + if not node.decorator_list: + return None + # /!\ node is actually an _ast.FunctionDef node while + # parent is an astroid.nodes.FunctionDef node + if sys.version_info >= (3, 8): + # Set the line number of the first decorator for Python 3.8+. + lineno = node.decorator_list[0].lineno + end_lineno = node.decorator_list[-1].end_lineno + end_col_offset = node.decorator_list[-1].end_col_offset + else: + lineno = node.lineno + end_lineno = None + end_col_offset = None + newnode = nodes.Decorators( + lineno=lineno, + col_offset=node.col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + newnode.postinit([self.visit(child, newnode) for child in node.decorator_list]) + return newnode + + def visit_delete(self, node: ast.Delete, parent: NodeNG) -> nodes.Delete: + """Visit a Delete node by returning a fresh instance of it.""" + newnode = nodes.Delete( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit([self.visit(child, newnode) for child in node.targets]) + return newnode + + def _visit_dict_items( + self, node: ast.Dict, parent: NodeNG, newnode: nodes.Dict + ) -> Generator[tuple[NodeNG, NodeNG], None, None]: + for key, value in zip(node.keys, node.values): + rebuilt_key: NodeNG + rebuilt_value = self.visit(value, newnode) + if not key: + # Extended unpacking + rebuilt_key = nodes.DictUnpack( + lineno=rebuilt_value.lineno, + col_offset=rebuilt_value.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(rebuilt_value, "end_lineno", None), + end_col_offset=getattr(rebuilt_value, "end_col_offset", None), + parent=parent, + ) + else: + rebuilt_key = self.visit(key, newnode) + yield rebuilt_key, rebuilt_value + + def visit_dict(self, node: ast.Dict, parent: NodeNG) -> nodes.Dict: + """Visit a Dict node by returning a fresh instance of it.""" + newnode = nodes.Dict( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + items: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] = list( + self._visit_dict_items(node, parent, newnode) + ) + newnode.postinit(items) + return newnode + + def visit_dictcomp(self, node: ast.DictComp, parent: NodeNG) -> nodes.DictComp: + """Visit a DictComp node by returning a fresh instance of it.""" + newnode = nodes.DictComp( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.key, newnode), + self.visit(node.value, newnode), + [self.visit(child, newnode) for child in node.generators], + ) + return newnode + + def visit_expr(self, node: ast.Expr, parent: NodeNG) -> nodes.Expr: + """Visit a Expr node by returning a fresh instance of it.""" + newnode = nodes.Expr( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_excepthandler( + self, node: ast.ExceptHandler, parent: NodeNG + ) -> nodes.ExceptHandler: + """Visit an ExceptHandler node by returning a fresh instance of it.""" + newnode = nodes.ExceptHandler( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.type, newnode), + self.visit_assignname(node, newnode, node.name), + [self.visit(child, newnode) for child in node.body], + ) + return newnode + + @overload + def _visit_for( + self, cls: type[nodes.For], node: ast.For, parent: NodeNG + ) -> nodes.For: + ... + + @overload + def _visit_for( + self, cls: type[nodes.AsyncFor], node: ast.AsyncFor, parent: NodeNG + ) -> nodes.AsyncFor: + ... + + def _visit_for( + self, cls: type[_ForT], node: ast.For | ast.AsyncFor, parent: NodeNG + ) -> _ForT: + """Visit a For node by returning a fresh instance of it.""" + col_offset = node.col_offset + if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncFor) and self._data: + # pylint: disable-next=unsubscriptable-object + col_offset = self._data[node.lineno - 1].index("async") + + newnode = cls( + lineno=node.lineno, + col_offset=col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + type_annotation = self.check_type_comment(node, parent=newnode) + newnode.postinit( + target=self.visit(node.target, newnode), + iter=self.visit(node.iter, newnode), + body=[self.visit(child, newnode) for child in node.body], + orelse=[self.visit(child, newnode) for child in node.orelse], + type_annotation=type_annotation, + ) + return newnode + + def visit_for(self, node: ast.For, parent: NodeNG) -> nodes.For: + return self._visit_for(nodes.For, node, parent) + + def visit_importfrom( + self, node: ast.ImportFrom, parent: NodeNG + ) -> nodes.ImportFrom: + """Visit an ImportFrom node by returning a fresh instance of it.""" + names = [(alias.name, alias.asname) for alias in node.names] + newnode = nodes.ImportFrom( + fromname=node.module or "", + names=names, + level=node.level or None, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + # store From names to add them to locals after building + self._import_from_nodes.append(newnode) + return newnode + + @overload + def _visit_functiondef( + self, cls: type[nodes.FunctionDef], node: ast.FunctionDef, parent: NodeNG + ) -> nodes.FunctionDef: + ... + + @overload + def _visit_functiondef( + self, + cls: type[nodes.AsyncFunctionDef], + node: ast.AsyncFunctionDef, + parent: NodeNG, + ) -> nodes.AsyncFunctionDef: + ... + + def _visit_functiondef( + self, + cls: type[_FunctionT], + node: ast.FunctionDef | ast.AsyncFunctionDef, + parent: NodeNG, + ) -> _FunctionT: + """Visit an FunctionDef node to become astroid.""" + self._global_names.append({}) + node, doc_ast_node = self._get_doc(node) + + lineno = node.lineno + if PY38_PLUS 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( + name=node.name, + lineno=lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + decorators = self.visit_decorators(node, newnode) + returns: NodeNG | None + if node.returns: + returns = self.visit(node.returns, newnode) + else: + returns = None + + type_comment_args = type_comment_returns = None + type_comment_annotation = self.check_function_type_comment(node, newnode) + if type_comment_annotation: + type_comment_returns, type_comment_args = type_comment_annotation + newnode.postinit( + args=self.visit(node.args, newnode), + body=[self.visit(child, newnode) for child in node.body], + decorators=decorators, + returns=returns, + type_comment_returns=type_comment_returns, + type_comment_args=type_comment_args, + position=self._get_position_info(node, newnode), + doc_node=self.visit(doc_ast_node, newnode), + ) + self._fix_doc_node_position(newnode) + self._global_names.pop() + return newnode + + def visit_functiondef( + self, node: ast.FunctionDef, parent: NodeNG + ) -> nodes.FunctionDef: + return self._visit_functiondef(nodes.FunctionDef, node, parent) + + def visit_generatorexp( + self, node: ast.GeneratorExp, parent: NodeNG + ) -> nodes.GeneratorExp: + """Visit a GeneratorExp node by returning a fresh instance of it.""" + newnode = nodes.GeneratorExp( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.elt, newnode), + [self.visit(child, newnode) for child in node.generators], + ) + return newnode + + def visit_attribute( + self, node: ast.Attribute, parent: NodeNG + ) -> nodes.Attribute | nodes.AssignAttr | nodes.DelAttr: + """Visit an Attribute node by returning a fresh instance of it.""" + context = self._get_context(node) + newnode: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr + if context == Context.Del: + # FIXME : maybe we should reintroduce and visit_delattr ? + # for instance, deactivating assign_ctx + newnode = nodes.DelAttr( + attrname=node.attr, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + elif context == Context.Store: + newnode = nodes.AssignAttr( + attrname=node.attr, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + # Prohibit a local save if we are in an ExceptHandler. + if not isinstance(parent, nodes.ExceptHandler): + # mypy doesn't recognize that newnode has to be AssignAttr because it + # doesn't support ParamSpec + # See https://github.com/python/mypy/issues/8645 + self._delayed_assattr.append(newnode) # type: ignore[arg-type] + else: + newnode = nodes.Attribute( + attrname=node.attr, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_global(self, node: ast.Global, parent: NodeNG) -> nodes.Global: + """Visit a Global node to become astroid.""" + newnode = nodes.Global( + names=node.names, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + if self._global_names: # global at the module level, no effect + for name in node.names: + self._global_names[-1].setdefault(name, []).append(newnode) + return newnode + + def visit_if(self, node: ast.If, parent: NodeNG) -> nodes.If: + """Visit an If node by returning a fresh instance of it.""" + newnode = nodes.If( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.test, newnode), + [self.visit(child, newnode) for child in node.body], + [self.visit(child, newnode) for child in node.orelse], + ) + return newnode + + def visit_ifexp(self, node: ast.IfExp, parent: NodeNG) -> nodes.IfExp: + """Visit a IfExp node by returning a fresh instance of it.""" + newnode = nodes.IfExp( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.test, newnode), + self.visit(node.body, newnode), + self.visit(node.orelse, newnode), + ) + return newnode + + def visit_import(self, node: ast.Import, parent: NodeNG) -> nodes.Import: + """Visit a Import node by returning a fresh instance of it.""" + names = [(alias.name, alias.asname) for alias in node.names] + newnode = nodes.Import( + names=names, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + # save import names in parent's locals: + for name, asname in newnode.names: + name = asname or name + parent.set_local(name.split(".")[0], newnode) + return newnode + + def visit_joinedstr(self, node: ast.JoinedStr, parent: NodeNG) -> nodes.JoinedStr: + newnode = nodes.JoinedStr( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit([self.visit(child, newnode) for child in node.values]) + return newnode + + def visit_formattedvalue( + self, node: ast.FormattedValue, parent: NodeNG + ) -> nodes.FormattedValue: + newnode = nodes.FormattedValue( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + value=self.visit(node.value, newnode), + conversion=node.conversion, + format_spec=self.visit(node.format_spec, newnode), + ) + return newnode + + if sys.version_info >= (3, 8): + + def visit_namedexpr( + self, node: ast.NamedExpr, parent: NodeNG + ) -> nodes.NamedExpr: + newnode = nodes.NamedExpr( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.target, newnode), self.visit(node.value, newnode) + ) + return newnode + + if sys.version_info < (3, 9): + # Not used in Python 3.9+. + def visit_extslice( + self, node: ast.ExtSlice, parent: nodes.Subscript + ) -> nodes.Tuple: + """Visit an ExtSlice node by returning a fresh instance of Tuple.""" + # ExtSlice doesn't have lineno or col_offset information + newnode = nodes.Tuple(ctx=Context.Load, parent=parent) + newnode.postinit([self.visit(dim, newnode) for dim in node.dims]) + return newnode + + def visit_index(self, node: ast.Index, parent: nodes.Subscript) -> NodeNG: + """Visit a Index node by returning a fresh instance of NodeNG.""" + return self.visit(node.value, parent) + + def visit_keyword(self, node: ast.keyword, parent: NodeNG) -> nodes.Keyword: + """Visit a Keyword node by returning a fresh instance of it.""" + newnode = nodes.Keyword( + arg=node.arg, + # position attributes added in 3.9 + lineno=getattr(node, "lineno", None), + col_offset=getattr(node, "col_offset", None), + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_lambda(self, node: ast.Lambda, parent: NodeNG) -> nodes.Lambda: + """Visit a Lambda node by returning a fresh instance of it.""" + newnode = nodes.Lambda( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit(self.visit(node.args, newnode), self.visit(node.body, newnode)) + return newnode + + def visit_list(self, node: ast.List, parent: NodeNG) -> nodes.List: + """Visit a List node by returning a fresh instance of it.""" + context = self._get_context(node) + newnode = nodes.List( + ctx=context, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit([self.visit(child, newnode) for child in node.elts]) + return newnode + + def visit_listcomp(self, node: ast.ListComp, parent: NodeNG) -> nodes.ListComp: + """Visit a ListComp node by returning a fresh instance of it.""" + newnode = nodes.ListComp( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.elt, newnode), + [self.visit(child, newnode) for child in node.generators], + ) + return newnode + + def visit_name( + self, node: ast.Name, parent: NodeNG + ) -> nodes.Name | nodes.AssignName | nodes.DelName: + """Visit a Name node by returning a fresh instance of it.""" + context = self._get_context(node) + newnode: nodes.Name | nodes.AssignName | nodes.DelName + if context == Context.Del: + newnode = nodes.DelName( + name=node.id, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + elif context == Context.Store: + newnode = nodes.AssignName( + name=node.id, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + else: + newnode = nodes.Name( + name=node.id, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + # XXX REMOVE me : + if context in (Context.Del, Context.Store): # 'Aug' ?? + newnode = cast(Union[nodes.AssignName, nodes.DelName], newnode) + self._save_assignment(newnode) + return newnode + + def visit_nonlocal(self, node: ast.Nonlocal, parent: NodeNG) -> nodes.Nonlocal: + """Visit a Nonlocal node and return a new instance of it.""" + return nodes.Nonlocal( + names=node.names, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + + def visit_constant(self, node: ast.Constant, parent: NodeNG) -> nodes.Const: + """Visit a Constant node by returning a fresh instance of Const.""" + return nodes.Const( + value=node.value, + kind=node.kind, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + + if sys.version_info < (3, 8): + # Not used in Python 3.8+. + def visit_ellipsis(self, node: ast.Ellipsis, parent: NodeNG) -> nodes.Const: + """Visit an Ellipsis node by returning a fresh instance of Const.""" + return nodes.Const( + value=Ellipsis, + lineno=node.lineno, + col_offset=node.col_offset, + parent=parent, + ) + + def visit_nameconstant( + self, node: ast.NameConstant, parent: NodeNG + ) -> nodes.Const: + # For singleton values True / False / None + return nodes.Const( + node.value, + node.lineno, + node.col_offset, + parent, + ) + + def visit_str(self, node: ast.Str | ast.Bytes, parent: NodeNG) -> nodes.Const: + """Visit a String/Bytes node by returning a fresh instance of Const.""" + return nodes.Const( + node.s, + node.lineno, + node.col_offset, + parent, + ) + + visit_bytes = visit_str + + def visit_num(self, node: ast.Num, parent: NodeNG) -> nodes.Const: + """Visit a Num node by returning a fresh instance of Const.""" + return nodes.Const( + node.n, + node.lineno, + node.col_offset, + parent, + ) + + def visit_pass(self, node: ast.Pass, parent: NodeNG) -> nodes.Pass: + """Visit a Pass node by returning a fresh instance of it.""" + return nodes.Pass( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + + def visit_raise(self, node: ast.Raise, parent: NodeNG) -> nodes.Raise: + """Visit a Raise node by returning a fresh instance of it.""" + newnode = nodes.Raise( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + # no traceback; anyway it is not used in Pylint + newnode.postinit( + exc=self.visit(node.exc, newnode), + cause=self.visit(node.cause, newnode), + ) + return newnode + + def visit_return(self, node: ast.Return, parent: NodeNG) -> nodes.Return: + """Visit a Return node by returning a fresh instance of it.""" + newnode = nodes.Return( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + if node.value is not None: + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_set(self, node: ast.Set, parent: NodeNG) -> nodes.Set: + """Visit a Set node by returning a fresh instance of it.""" + newnode = nodes.Set( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit([self.visit(child, newnode) for child in node.elts]) + return newnode + + def visit_setcomp(self, node: ast.SetComp, parent: NodeNG) -> nodes.SetComp: + """Visit a SetComp node by returning a fresh instance of it.""" + newnode = nodes.SetComp( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.elt, newnode), + [self.visit(child, newnode) for child in node.generators], + ) + return newnode + + def visit_slice(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice: + """Visit a Slice node by returning a fresh instance of it.""" + newnode = nodes.Slice( + # position attributes added in 3.9 + lineno=getattr(node, "lineno", None), + col_offset=getattr(node, "col_offset", None), + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + lower=self.visit(node.lower, newnode), + upper=self.visit(node.upper, newnode), + step=self.visit(node.step, newnode), + ) + return newnode + + def visit_subscript(self, node: ast.Subscript, parent: NodeNG) -> nodes.Subscript: + """Visit a Subscript node by returning a fresh instance of it.""" + context = self._get_context(node) + newnode = nodes.Subscript( + ctx=context, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.value, newnode), self.visit(node.slice, newnode) + ) + return newnode + + def visit_starred(self, node: ast.Starred, parent: NodeNG) -> nodes.Starred: + """Visit a Starred node and return a new instance of it.""" + context = self._get_context(node) + newnode = nodes.Starred( + ctx=context, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_tryexcept(self, node: ast.Try, parent: NodeNG) -> nodes.TryExcept: + """Visit a TryExcept node by returning a fresh instance of it.""" + if sys.version_info >= (3, 8): + # TryExcept excludes the 'finally' but that will be included in the + # end_lineno from 'node'. Therefore, we check all non 'finally' + # children to find the correct end_lineno and column. + end_lineno = node.end_lineno + end_col_offset = node.end_col_offset + all_children: list[ast.AST] = [*node.body, *node.handlers, *node.orelse] + for child in reversed(all_children): + end_lineno = child.end_lineno + end_col_offset = child.end_col_offset + break + newnode = nodes.TryExcept( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=end_lineno, + end_col_offset=end_col_offset, + parent=parent, + ) + else: + 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_try( + self, node: ast.Try, parent: NodeNG + ) -> nodes.TryExcept | nodes.TryFinally | None: + # python 3.3 introduce a new Try node replacing + # TryFinally/TryExcept nodes + if node.finalbody: + newnode = nodes.TryFinally( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + body: list[NodeNG | nodes.TryExcept] + 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_trystar(self, node: ast.TryStar, parent: NodeNG) -> nodes.TryStar: + newnode = nodes.TryStar( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + body=[self.visit(n, newnode) for n in node.body], + handlers=[self.visit(n, newnode) for n in node.handlers], + orelse=[self.visit(n, newnode) for n in node.orelse], + finalbody=[self.visit(n, newnode) for n in node.finalbody], + ) + return newnode + + def visit_tuple(self, node: ast.Tuple, parent: NodeNG) -> nodes.Tuple: + """Visit a Tuple node by returning a fresh instance of it.""" + context = self._get_context(node) + newnode = nodes.Tuple( + ctx=context, + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit([self.visit(child, newnode) for child in node.elts]) + return newnode + + def visit_unaryop(self, node: ast.UnaryOp, parent: NodeNG) -> nodes.UnaryOp: + """Visit a UnaryOp node by returning a fresh instance of it.""" + newnode = nodes.UnaryOp( + op=self._parser_module.unary_op_classes[node.op.__class__], + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit(self.visit(node.operand, newnode)) + return newnode + + def visit_while(self, node: ast.While, parent: NodeNG) -> nodes.While: + """Visit a While node by returning a fresh instance of it.""" + newnode = nodes.While( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + newnode.postinit( + self.visit(node.test, newnode), + [self.visit(child, newnode) for child in node.body], + [self.visit(child, newnode) for child in node.orelse], + ) + return newnode + + @overload + def _visit_with( + self, cls: type[nodes.With], node: ast.With, parent: NodeNG + ) -> nodes.With: + ... + + @overload + def _visit_with( + self, cls: type[nodes.AsyncWith], node: ast.AsyncWith, parent: NodeNG + ) -> nodes.AsyncWith: + ... + + def _visit_with( + self, + cls: type[_WithT], + node: ast.With | ast.AsyncWith, + parent: NodeNG, + ) -> _WithT: + col_offset = node.col_offset + if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncWith) and self._data: + # pylint: disable-next=unsubscriptable-object + col_offset = self._data[node.lineno - 1].index("async") + + newnode = cls( + lineno=node.lineno, + col_offset=col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + + def visit_child(child: ast.withitem) -> tuple[NodeNG, NodeNG | None]: + expr = self.visit(child.context_expr, newnode) + var = self.visit(child.optional_vars, newnode) + return expr, var + + type_annotation = self.check_type_comment(node, parent=newnode) + newnode.postinit( + items=[visit_child(child) for child in node.items], + body=[self.visit(child, newnode) for child in node.body], + type_annotation=type_annotation, + ) + return newnode + + def visit_with(self, node: ast.With, parent: NodeNG) -> NodeNG: + return self._visit_with(nodes.With, node, parent) + + def visit_yield(self, node: ast.Yield, parent: NodeNG) -> NodeNG: + """Visit a Yield node by returning a fresh instance of it.""" + newnode = nodes.Yield( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + if node.value is not None: + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + def visit_yieldfrom(self, node: ast.YieldFrom, parent: NodeNG) -> NodeNG: + newnode = nodes.YieldFrom( + lineno=node.lineno, + col_offset=node.col_offset, + # end_lineno and end_col_offset added in 3.8 + end_lineno=getattr(node, "end_lineno", None), + end_col_offset=getattr(node, "end_col_offset", None), + parent=parent, + ) + if node.value is not None: + newnode.postinit(self.visit(node.value, newnode)) + return newnode + + if sys.version_info >= (3, 10): + + def visit_match(self, node: ast.Match, parent: NodeNG) -> nodes.Match: + newnode = nodes.Match( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + newnode.postinit( + subject=self.visit(node.subject, newnode), + cases=[self.visit(case, newnode) for case in node.cases], + ) + return newnode + + def visit_matchcase( + self, node: ast.match_case, parent: NodeNG + ) -> nodes.MatchCase: + newnode = nodes.MatchCase(parent=parent) + newnode.postinit( + pattern=self.visit(node.pattern, newnode), + guard=self.visit(node.guard, newnode), + body=[self.visit(child, newnode) for child in node.body], + ) + return newnode + + def visit_matchvalue( + self, node: ast.MatchValue, parent: NodeNG + ) -> nodes.MatchValue: + newnode = nodes.MatchValue( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + newnode.postinit(value=self.visit(node.value, newnode)) + return newnode + + def visit_matchsingleton( + self, node: ast.MatchSingleton, parent: NodeNG + ) -> nodes.MatchSingleton: + return nodes.MatchSingleton( + value=node.value, + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + + def visit_matchsequence( + self, node: ast.MatchSequence, parent: NodeNG + ) -> nodes.MatchSequence: + newnode = nodes.MatchSequence( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + newnode.postinit( + patterns=[self.visit(pattern, newnode) for pattern in node.patterns] + ) + return newnode + + def visit_matchmapping( + self, node: ast.MatchMapping, parent: NodeNG + ) -> nodes.MatchMapping: + newnode = nodes.MatchMapping( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + # Add AssignName node for 'node.name' + # https://bugs.python.org/issue43994 + newnode.postinit( + keys=[self.visit(child, newnode) for child in node.keys], + patterns=[self.visit(pattern, newnode) for pattern in node.patterns], + rest=self.visit_assignname(node, newnode, node.rest), + ) + return newnode + + def visit_matchclass( + self, node: ast.MatchClass, parent: NodeNG + ) -> nodes.MatchClass: + newnode = nodes.MatchClass( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + newnode.postinit( + cls=self.visit(node.cls, newnode), + patterns=[self.visit(pattern, newnode) for pattern in node.patterns], + kwd_attrs=node.kwd_attrs, + kwd_patterns=[ + self.visit(pattern, newnode) for pattern in node.kwd_patterns + ], + ) + return newnode + + def visit_matchstar( + self, node: ast.MatchStar, parent: NodeNG + ) -> nodes.MatchStar: + newnode = nodes.MatchStar( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + # Add AssignName node for 'node.name' + # https://bugs.python.org/issue43994 + newnode.postinit(name=self.visit_assignname(node, newnode, node.name)) + return newnode + + def visit_matchas(self, node: ast.MatchAs, parent: NodeNG) -> nodes.MatchAs: + newnode = nodes.MatchAs( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + # Add AssignName node for 'node.name' + # https://bugs.python.org/issue43994 + newnode.postinit( + pattern=self.visit(node.pattern, newnode), + name=self.visit_assignname(node, newnode, node.name), + ) + return newnode + + def visit_matchor(self, node: ast.MatchOr, parent: NodeNG) -> nodes.MatchOr: + newnode = nodes.MatchOr( + lineno=node.lineno, + col_offset=node.col_offset, + end_lineno=node.end_lineno, + end_col_offset=node.end_col_offset, + parent=parent, + ) + newnode.postinit( + patterns=[self.visit(pattern, newnode) for pattern in node.patterns] + ) + return newnode diff --git a/venv/lib/python3.10/site-packages/astroid/scoped_nodes.py b/venv/lib/python3.10/site-packages/astroid/scoped_nodes.py new file mode 100644 index 00000000..0e5ef130 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/scoped_nodes.py @@ -0,0 +1,35 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +# pylint: disable=unused-import + +import warnings + +from astroid.nodes.scoped_nodes import ( + AsyncFunctionDef, + ClassDef, + ComprehensionScope, + DictComp, + FunctionDef, + GeneratorExp, + Lambda, + ListComp, + LocalsDictNodeNG, + Module, + SetComp, + _is_metaclass, + builtin_lookup, + function_to_method, + get_wrapping_class, +) + +# We cannot create a __all__ here because it would create a circular import +# Please remove astroid/scoped_nodes.py|astroid/node_classes.py in autoflake +# exclude when removing this file. +warnings.warn( + "The 'astroid.scoped_nodes' module is deprecated and will be replaced by " + "'astroid.nodes' in astroid 3.0.0", + DeprecationWarning, + stacklevel=2, +) diff --git a/venv/lib/python3.10/site-packages/astroid/test_utils.py b/venv/lib/python3.10/site-packages/astroid/test_utils.py new file mode 120000 index 00000000..7dde9a13 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/test_utils.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ef/0c/95/2fc03c5984176e2c75993d39961f323b89179358118c20aeab07d5e1ff \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/transforms.py b/venv/lib/python3.10/site-packages/astroid/transforms.py new file mode 120000 index 00000000..934188e7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/transforms.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/7a/0e/db/d257a142efc6b051c2b0b754b0d36f6f160119c4c2d29eeb17610a2177 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/astroid/typing.py b/venv/lib/python3.10/site-packages/astroid/typing.py new file mode 100644 index 00000000..62d8995f --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/typing.py @@ -0,0 +1,71 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + +from __future__ import annotations + +import sys +from typing import TYPE_CHECKING, Any, Callable, Generator, TypeVar, Union + +if TYPE_CHECKING: + from astroid import bases, exceptions, nodes, transforms, util + from astroid.context import InferenceContext + from astroid.interpreter._import import spec + +if sys.version_info >= (3, 8): + from typing import TypedDict +else: + from typing_extensions import TypedDict + +_NodesT = TypeVar("_NodesT", bound="nodes.NodeNG") + + +class InferenceErrorInfo(TypedDict): + """Store additional Inference error information + raised with StopIteration exception. + """ + + node: nodes.NodeNG + context: InferenceContext | None + + +InferFn = Callable[..., Any] + + +class AstroidManagerBrain(TypedDict): + """Dictionary to store relevant information for a AstroidManager class.""" + + astroid_cache: dict[str, nodes.Module] + _mod_file_cache: dict[ + tuple[str, str | None], spec.ModuleSpec | exceptions.AstroidImportError + ] + _failed_import_hooks: list[Callable[[str], nodes.Module]] + always_load_extensions: bool + optimize_ast: bool + extension_package_whitelist: set[str] + _transform: transforms.TransformVisitor + + +InferenceResult = Union["nodes.NodeNG", "util.UninferableBase", "bases.Proxy"] +SuccessfulInferenceResult = Union["nodes.NodeNG", "bases.Proxy"] + +ConstFactoryResult = Union[ + "nodes.List", + "nodes.Set", + "nodes.Tuple", + "nodes.Dict", + "nodes.Const", + "nodes.EmptyNode", +] + +InferBinaryOp = Callable[ + [ + Union[_NodesT, "bases.Instance"], + Union["nodes.AugAssign", "nodes.BinOp"], + str, + InferenceResult, + "InferenceContext", + SuccessfulInferenceResult, + ], + Generator[InferenceResult, None, None], +] diff --git a/venv/lib/python3.10/site-packages/astroid/util.py b/venv/lib/python3.10/site-packages/astroid/util.py new file mode 100644 index 00000000..20ff810f --- /dev/null +++ b/venv/lib/python3.10/site-packages/astroid/util.py @@ -0,0 +1,162 @@ +# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html +# For details: https://github.com/PyCQA/astroid/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/astroid/blob/main/CONTRIBUTORS.txt + + +from __future__ import annotations + +import importlib +import sys +import warnings +from typing import Any + +import lazy_object_proxy + +if sys.version_info >= (3, 8): + from typing import Final, Literal +else: + from typing_extensions import Final, Literal + + +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: str) -> lazy_object_proxy.Proxy: + return lazy_object_proxy.Proxy( + lambda: importlib.import_module("." + module_name, "astroid") + ) + + +class UninferableBase: + """Special inference object, which is returned when inference fails. + + This is meant to be used as a singleton. Use astroid.util.Uninferable to access it. + """ + + def __repr__(self) -> Literal["Uninferable"]: + return "Uninferable" + + __str__ = __repr__ + + def __getattribute__(self, name: str) -> Any: + if name == "next": + raise AttributeError("next method should not be called") + if name.startswith("__") and name.endswith("__"): + return object.__getattribute__(self, name) + if name == "accept": + return object.__getattribute__(self, name) + return self + + def __call__(self, *args: Any, **kwargs: Any) -> UninferableBase: + return self + + def __bool__(self) -> Literal[False]: + return False + + __nonzero__ = __bool__ + + def accept(self, visitor): + return visitor.visit_uninferable(self) + + +Uninferable: Final = UninferableBase() + + +class BadOperationMessage: + """Object which describes a TypeError occurred somewhere in the inference chain. + + This is not an exception, but a container object which holds the types and + the error which occurred. + """ + + +class BadUnaryOperationMessage(BadOperationMessage): + """Object which describes operational failures on UnaryOps.""" + + def __init__(self, operand, op, error): + self.operand = operand + self.op = op + self.error = error + + @property + def _object_type_helper(self): + helpers = lazy_import("helpers") + return helpers.object_type + + def _object_type(self, obj): + objtype = self._object_type_helper(obj) + if isinstance(objtype, UninferableBase): + return None + + return objtype + + def __str__(self) -> str: + if hasattr(self.operand, "name"): + operand_type = self.operand.name + else: + object_type = self._object_type(self.operand) + if hasattr(object_type, "name"): + operand_type = object_type.name + else: + # Just fallback to as_string + operand_type = object_type.as_string() + + msg = "bad operand type for unary {}: {}" + return msg.format(self.op, operand_type) + + +class BadBinaryOperationMessage(BadOperationMessage): + """Object which describes type errors for BinOps.""" + + def __init__(self, left_type, op, right_type): + self.left_type = left_type + self.right_type = right_type + self.op = op + + def __str__(self) -> str: + msg = "unsupported operand type(s) for {}: {!r} and {!r}" + return msg.format(self.op, self.left_type.name, self.right_type.name) + + +def _instancecheck(cls, other) -> bool: + wrapped = cls.__wrapped__ + other_cls = other.__class__ + is_instance_of = wrapped is other_cls or issubclass(other_cls, wrapped) + warnings.warn( + "%r is deprecated and slated for removal in astroid " + "2.0, use %r instead" % (cls.__class__.__name__, wrapped.__name__), + PendingDeprecationWarning, + stacklevel=2, + ) + return is_instance_of + + +def 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 check_warnings_filter() -> bool: + """Return True if any other than the default DeprecationWarning filter is enabled. + + https://docs.python.org/3/library/warnings.html#default-warning-filter + """ + return any( + issubclass(DeprecationWarning, filter[2]) + and filter[0] != "ignore" + and filter[3] != "__main__" + for filter in warnings.filters + ) diff --git a/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/AUTHORS.rst b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/AUTHORS.rst new file mode 120000 index 00000000..3f46d909 --- /dev/null +++ b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/AUTHORS.rst @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/b6/24/cf/b1bcc697d76d5c23045976d6495d736a7d4cf81a16b628b1b38e862169 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/LICENSE b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/LICENSE new file mode 120000 index 00000000..2f19f41b --- /dev/null +++ b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/LICENSE @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/8d/1d/02/38e48543441914cab0741d4de3e070a1b8367f787af5f209afb60b096a \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/METADATA b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/METADATA new file mode 100644 index 00000000..0da18781 --- /dev/null +++ b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/METADATA @@ -0,0 +1,460 @@ +Metadata-Version: 2.1 +Name: autopep8 +Version: 2.0.2 +Summary: A tool that automatically formats Python code to conform to the PEP 8 style guide +Home-page: https://github.com/hhatto/autopep8 +Author: Hideo Hattori +Author-email: hhatto.jp@gmail.com +License: Expat License +Keywords: automation,pep8,format,pycodestyle +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Software Development :: Quality Assurance +Requires-Python: >=3.6 +License-File: LICENSE +License-File: AUTHORS.rst +Requires-Dist: pycodestyle (>=2.10.0) +Requires-Dist: tomli ; python_version < "3.11" + +======== +autopep8 +======== + +.. image:: https://img.shields.io/pypi/v/autopep8.svg + :target: https://pypi.org/project/autopep8/ + :alt: PyPI Version + +.. image:: https://github.com/hhatto/autopep8/workflows/Python%20package/badge.svg + :target: https://github.com/hhatto/autopep8/actions + :alt: Build status + +.. image:: https://codecov.io/gh/hhatto/autopep8/branch/main/graph/badge.svg + :target: https://codecov.io/gh/hhatto/autopep8 + :alt: Code Coverage + +autopep8 automatically formats Python code to conform to the `PEP 8`_ style +guide. It uses the pycodestyle_ utility to determine what parts of the code +needs to be formatted. autopep8 is capable of fixing most of the formatting +issues_ that can be reported by pycodestyle. + +.. _PEP 8: https://www.python.org/dev/peps/pep-0008/ +.. _issues: https://pycodestyle.readthedocs.org/en/latest/intro.html#error-codes + +.. contents:: + + +Installation +============ + +From pip:: + + $ pip install --upgrade autopep8 + +Consider using the ``--user`` option_. + +.. _option: https://pip.pypa.io/en/latest/user_guide/#user-installs + + +Requirements +============ + +autopep8 requires pycodestyle_. + +.. _pycodestyle: https://github.com/PyCQA/pycodestyle + + +Usage +===== + +To modify a file in place (with aggressive level 2):: + + $ autopep8 --in-place --aggressive --aggressive + +Before running autopep8. + +.. code-block:: python + + import math, sys; + + def example1(): + ####This is a long comment. This should be wrapped to fit within 72 characters. + some_tuple=( 1,2, 3,'a' ); + some_variable={'long':'Long code lines should be wrapped within 79 characters.', + 'other':[math.pi, 100,200,300,9876543210,'This is a long string that goes on'], + 'more':{'inner':'This whole logical line should be wrapped.',some_tuple:[1, + 20,300,40000,500000000,60000000000000000]}} + return (some_tuple, some_variable) + def example2(): return {'has_key() is deprecated':True}.has_key({'f':2}.has_key('')); + class Example3( object ): + def __init__ ( self, bar ): + #Comments should have a space after the hash. + if bar : bar+=1; bar=bar* bar ; return bar + else: + some_string = """ + Indentation in multiline strings should not be touched. + Only actual code should be reindented. + """ + return (sys.path, some_string) + +After running autopep8. + +.. code-block:: python + + import math + import sys + + + def example1(): + # This is a long comment. This should be wrapped to fit within 72 + # characters. + some_tuple = (1, 2, 3, 'a') + some_variable = { + 'long': 'Long code lines should be wrapped within 79 characters.', + 'other': [ + math.pi, + 100, + 200, + 300, + 9876543210, + 'This is a long string that goes on'], + 'more': { + 'inner': 'This whole logical line should be wrapped.', + some_tuple: [ + 1, + 20, + 300, + 40000, + 500000000, + 60000000000000000]}} + return (some_tuple, some_variable) + + + def example2(): return ('' in {'f': 2}) in {'has_key() is deprecated': True} + + + class Example3(object): + def __init__(self, bar): + # Comments should have a space after the hash. + if bar: + bar += 1 + bar = bar * bar + return bar + else: + some_string = """ + Indentation in multiline strings should not be touched. + Only actual code should be reindented. + """ + return (sys.path, some_string) + +Options:: + + usage: autopep8 [-h] [--version] [-v] [-d] [-i] [--global-config filename] + [--ignore-local-config] [-r] [-j n] [-p n] [-a] + [--experimental] [--exclude globs] [--list-fixes] + [--ignore errors] [--select errors] [--max-line-length n] + [--line-range line line] [--hang-closing] [--exit-code] + [files [files ...]] + + Automatically formats Python code to conform to the PEP 8 style guide. + + positional arguments: + files files to format or '-' for standard in + + optional arguments: + -h, --help show this help message and exit + --version show program's version number and exit + -v, --verbose print verbose messages; multiple -v result in more + verbose messages + -d, --diff print the diff for the fixed source + -i, --in-place make changes to files in place + --global-config filename + path to a global pep8 config file; if this file does + not exist then this is ignored (default: + ~/.config/pep8) + --ignore-local-config + don't look for and apply local config files; if not + passed, defaults are updated with any config files in + the project's root directory + -r, --recursive run recursively over directories; must be used with + --in-place or --diff + -j n, --jobs n number of parallel jobs; match CPU count if value is + less than 1 + -p n, --pep8-passes n + maximum number of additional pep8 passes (default: + infinite) + -a, --aggressive enable non-whitespace changes; multiple -a result in + more aggressive changes + --experimental enable experimental fixes + --exclude globs exclude file/directory names that match these comma- + separated globs + --list-fixes list codes for fixes; used by --ignore and --select + --ignore errors do not fix these errors/warnings (default: + E226,E24,W50,W690) + --select errors fix only these errors/warnings (e.g. E4,W) + --max-line-length n set maximum allowed line length (default: 79) + --line-range line line, --range line line + only fix errors found within this inclusive range of + line numbers (e.g. 1 99); line numbers are indexed at + 1 + --hang-closing hang-closing option passed to pycodestyle + --exit-code change to behavior of exit code. default behavior of + return value, 0 is no differences, 1 is error exit. + return 2 when add this option. 2 is exists + differences. + + +Features +======== + +autopep8 fixes the following issues_ reported by pycodestyle_:: + + E101 - Reindent all lines. + E11 - Fix indentation. + E121 - Fix indentation to be a multiple of four. + E122 - Add absent indentation for hanging indentation. + E123 - Align closing bracket to match opening bracket. + E124 - Align closing bracket to match visual indentation. + E125 - Indent to distinguish line from next logical line. + E126 - Fix over-indented hanging indentation. + E127 - Fix visual indentation. + E128 - Fix visual indentation. + E129 - Fix visual indentation. + E131 - Fix hanging indent for unaligned continuation line. + E133 - Fix missing indentation for closing bracket. + E20 - Remove extraneous whitespace. + E211 - Remove extraneous whitespace. + E22 - Fix extraneous whitespace around keywords. + E224 - Remove extraneous whitespace around operator. + E225 - Fix missing whitespace around operator. + E226 - Fix missing whitespace around arithmetic operator. + E227 - Fix missing whitespace around bitwise/shift operator. + E228 - Fix missing whitespace around modulo operator. + E231 - Add missing whitespace. + E241 - Fix extraneous whitespace around keywords. + E242 - Remove extraneous whitespace around operator. + E251 - Remove whitespace around parameter '=' sign. + E252 - Missing whitespace around parameter equals. + E26 - Fix spacing after comment hash for inline comments. + E265 - Fix spacing after comment hash for block comments. + E266 - Fix too many leading '#' for block comments. + E27 - Fix extraneous whitespace around keywords. + E301 - Add missing blank line. + E302 - Add missing 2 blank lines. + E303 - Remove extra blank lines. + E304 - Remove blank line following function decorator. + E305 - Expected 2 blank lines after end of function or class. + E306 - Expected 1 blank line before a nested definition. + E401 - Put imports on separate lines. + E402 - Fix module level import not at top of file + E501 - Try to make lines fit within --max-line-length characters. + E502 - Remove extraneous escape of newline. + E701 - Put colon-separated compound statement on separate lines. + E70 - Put semicolon-separated compound statement on separate lines. + E711 - Fix comparison with None. + E712 - Fix comparison with boolean. + E713 - Use 'not in' for test for membership. + E714 - Use 'is not' test for object identity. + E721 - Use "isinstance()" instead of comparing types directly. + E722 - Fix bare except. + E731 - Use a def when use do not assign a lambda expression. + W291 - Remove trailing whitespace. + W292 - Add a single newline at the end of the file. + W293 - Remove trailing whitespace on blank line. + W391 - Remove trailing blank lines. + W503 - Fix line break before binary operator. + W504 - Fix line break after binary operator. + W605 - Fix invalid escape sequence 'x'. + W690 - Fix various deprecated code (via lib2to3). + +autopep8 also fixes some issues not found by pycodestyle_. + +- Correct deprecated or non-idiomatic Python code (via ``lib2to3``). Use this + for making Python 2.7 code more compatible with Python 3. (This is triggered + if ``W690`` is enabled.) +- Normalize files with mixed line endings. +- Put a blank line between a class docstring and its first method + declaration. (Enabled with ``E301``.) +- Remove blank lines between a function declaration and its docstring. (Enabled + with ``E303``.) + +autopep8 avoids fixing some issues found by pycodestyle_. + +- ``E112``/``E113`` for non comments are reports of bad indentation that break + syntax rules. These should not be modified at all. +- ``E265``, which refers to spacing after comment hash, is ignored if the + comment looks like code. autopep8 avoids modifying these since they are not + real comments. If you really want to get rid of the pycodestyle_ warning, + consider just removing the commented-out code. (This can be automated via + eradicate_.) + +.. _eradicate: https://github.com/myint/eradicate + + +More advanced usage +=================== + +By default autopep8 only makes whitespace changes. Thus, by default, it does +not fix ``E711`` and ``E712``. (Changing ``x == None`` to ``x is None`` may +change the meaning of the program if ``x`` has its ``__eq__`` method +overridden.) Nor does it correct deprecated code ``W6``. To enable these +more aggressive fixes, use the ``--aggressive`` option:: + + $ autopep8 --aggressive + +Use multiple ``--aggressive`` to increase the aggressiveness level. For +example, ``E712`` requires aggressiveness level 2 (since ``x == True`` could be +changed to either ``x`` or ``x is True``, but autopep8 chooses the former). + +``--aggressive`` will also shorten lines more aggressively. It will also remove +trailing whitespace more aggressively. (Usually, we don't touch trailing +whitespace in docstrings and other multiline strings. And to do even more +aggressive changes to docstrings, use docformatter_.) + +.. _docformatter: https://github.com/myint/docformatter + +To enable only a subset of the fixes, use the ``--select`` option. For example, +to fix various types of indentation issues:: + + $ autopep8 --select=E1,W1 + +If the file being fixed is large, you may want to enable verbose progress +messages:: + + $ autopep8 -v + +Passing in ``--experimental`` enables the following functionality: + +- Shortens code lines by taking its length into account + +:: + +$ autopep8 --experimental + +Disabling line-by-line +---------------------- + +It is possible to disable autopep8 untill it it turned back on again in the file, using ``autopep8: off`` and then renabling ``autopep8: on``. + +.. code-block:: python + + # autopep8: off + [ + [23, 23, 13, 43], + [32, 34, 34, 34], + [56, 34, 34, 11], + [10, 10, 10, 10], + ] + # autopep8: on + +``fmt: off`` and ``fmt: on`` are also valid. + +Use as a module +=============== + +The simplest way of using autopep8 as a module is via the ``fix_code()`` +function: + + >>> import autopep8 + >>> autopep8.fix_code('x= 123\n') + 'x = 123\n' + +Or with options: + + >>> import autopep8 + >>> autopep8.fix_code('print( 123 )\n', + ... options={'ignore': ['E']}) + 'print( 123 )\n' + + +Configuration +============= + +By default, if ``$HOME/.config/pycodestyle`` (``~\.pycodestyle`` in Windows +environment) exists, it will be used as global configuration file. +Alternatively, you can specify the global configuration file with the +``--global-config`` option. + +Also, if ``setup.cfg``, ``tox.ini``, ``.pep8`` and ``.flake8`` files exist +in the directory where the target file exists, it will be used as the +configuration file. + +``pep8``, ``pycodestyle``, and ``flake8`` can be used as a section. + +configuration file example:: + + [pycodestyle] + max_line_length = 120 + ignore = E501 + +pyproject.toml +-------------- + +autopep8 can also use ``pyproject.toml``. +The section must be ``[tool.autopep8]``, and ``pyproject.toml`` takes precedence +over any other configuration files. + +configuration file example:: + + [tool.autopep8] + max_line_length = 120 + ignore = "E501,W6" # or ["E501", "W6"] + in-place = true + recursive = true + aggressive = 3 + + +Testing +======= + +Test cases are in ``test/test_autopep8.py``. They can be run directly via +``python test/test_autopep8.py`` or via tox_. The latter is useful for +testing against multiple Python interpreters. (We currently test against +CPython versions 3.7, 3.8, 3.9 and 3.10. We also test against PyPy.) + +.. _`tox`: https://pypi.org/project/tox/ + +Broad spectrum testing is available via ``test/acid.py``. This script runs +autopep8 against Python code and checks for correctness and completeness of the +code fixes. It can check that the bytecode remains identical. +``test/acid_pypi.py`` makes use of ``acid.py`` to test against the latest +released packages on PyPI. + + +Troubleshooting +=============== + +``pkg_resources.DistributionNotFound`` +-------------------------------------- + +If you are using an ancient version of ``setuptools``, you might encounter +``pkg_resources.DistributionNotFound`` when trying to run ``autopep8``. Try +upgrading ``setuptools`` to workaround this ``setuptools`` problem:: + + $ pip install --upgrade setuptools + +Use ``sudo`` if you are installing to the system. + + +Links +===== + +* PyPI_ +* GitHub_ +* `Travis CI`_ +* Coveralls_ + +.. _PyPI: https://pypi.org/project/autopep8/ +.. _GitHub: https://github.com/hhatto/autopep8 +.. _`Travis CI`: https://travis-ci.org/hhatto/autopep8 +.. _`Coveralls`: https://coveralls.io/r/hhatto/autopep8 diff --git a/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/RECORD b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/RECORD new file mode 100644 index 00000000..4f7a208a --- /dev/null +++ b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/RECORD @@ -0,0 +1,12 @@ +../../../bin/autopep8,sha256=kASgTXagaDaKm6hNa55E5y800bn7BxtbjAOCP1CCbq4,211 +__pycache__/autopep8.cpython-310.pyc,, +autopep8-2.0.2.dist-info/AUTHORS.rst,sha256=tiTPsbzGl9dtXCMEWXbWSV1zan1M-BoWtiixs46GIWk,2003 +autopep8-2.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +autopep8-2.0.2.dist-info/LICENSE,sha256=jR0COOSFQ0QZFMqwdB1N4-Bwobg2f3h69fIJr7YLCWo,1181 +autopep8-2.0.2.dist-info/METADATA,sha256=QQGKbaNzesgwKoYJtqGOQe2xgMsbknKc__UVP8WCM5Y,16753 +autopep8-2.0.2.dist-info/RECORD,, +autopep8-2.0.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +autopep8-2.0.2.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110 +autopep8-2.0.2.dist-info/entry_points.txt,sha256=zEduLXzN3YzTTZBwxjhEKW7PVLqSqVG8-ocCaCR3P4A,43 +autopep8-2.0.2.dist-info/top_level.txt,sha256=s2x-di3QBwGxr7kd5xErt2pom8dsFRdINbmwsOEgLfU,9 +autopep8.py,sha256=NXLsph7X3O_afaGx0_kATNxa7RKSf_BgvcZ1aoV6aJA,155555 diff --git a/venv/lib/python3.10/site-packages/typing_extensions-3.10.0.2.dist-info/REQUESTED b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/REQUESTED similarity index 100% rename from venv/lib/python3.10/site-packages/typing_extensions-3.10.0.2.dist-info/REQUESTED rename to venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/REQUESTED diff --git a/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/WHEEL b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/WHEEL new file mode 120000 index 00000000..0d3db369 --- /dev/null +++ b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/WHEEL @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/6d/bd/8e/b7db1c7251ca30e2c310763a076b227163a0ba08c529a26c4fbbf0310a \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/entry_points.txt new file mode 120000 index 00000000..9504a7b4 --- /dev/null +++ b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/entry_points.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/cc/47/6e/2d7ccddd8cd34d9070c63844296ecf54ba92a951bcfa87026824773f80 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/top_level.txt new file mode 120000 index 00000000..f8bc10c6 --- /dev/null +++ b/venv/lib/python3.10/site-packages/autopep8-2.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/b3/6c/7e/762dd00701b1afb91de7112bb76a689bc76c15174835b9b0b0e1202df5 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/autopep8.py b/venv/lib/python3.10/site-packages/autopep8.py new file mode 100644 index 00000000..573f7acc --- /dev/null +++ b/venv/lib/python3.10/site-packages/autopep8.py @@ -0,0 +1,4570 @@ +#!/usr/bin/env python + +# Copyright (C) 2010-2011 Hideo Hattori +# Copyright (C) 2011-2013 Hideo Hattori, Steven Myint +# Copyright (C) 2013-2016 Hideo Hattori, Steven Myint, Bill Wendling +# +# 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. + +# Copyright (C) 2006-2009 Johann C. Rocholl +# Copyright (C) 2009-2013 Florent Xicluna +# +# 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. + +"""Automatically formats Python code to conform to the PEP 8 style guide. + +Fixes that only need be done once can be added by adding a function of the form +"fix_(source)" to this module. They should return the fixed source code. +These fixes are picked up by apply_global_fixes(). + +Fixes that depend on pycodestyle should be added as methods to FixPEP8. See the +class documentation for more information. + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import argparse +import codecs +import collections +import copy +import difflib +import fnmatch +import inspect +import io +import itertools +import keyword +import locale +import os +import re +import signal +import sys +import textwrap +import token +import tokenize +import warnings +import ast +from configparser import ConfigParser as SafeConfigParser, Error + +import pycodestyle +from pycodestyle import STARTSWITH_INDENT_STATEMENT_REGEX + + +__version__ = '2.0.2' + + +CR = '\r' +LF = '\n' +CRLF = '\r\n' + + +PYTHON_SHEBANG_REGEX = re.compile(r'^#!.*\bpython[23]?\b\s*$') +LAMBDA_REGEX = re.compile(r'([\w.]+)\s=\slambda\s*([)(=\w,\s.]*):') +COMPARE_NEGATIVE_REGEX = re.compile(r'\b(not)\s+([^][)(}{]+?)\s+(in|is)\s') +COMPARE_NEGATIVE_REGEX_THROUGH = re.compile(r'\b(not\s+in|is\s+not)\s') +BARE_EXCEPT_REGEX = re.compile(r'except\s*:') +STARTSWITH_DEF_REGEX = re.compile(r'^(async\s+def|def)\s.*\):') +DOCSTRING_START_REGEX = re.compile(r'^u?r?(?P["\']{3})') +ENABLE_REGEX = re.compile(r'# *(fmt|autopep8): *on') +DISABLE_REGEX = re.compile(r'# *(fmt|autopep8): *off') + +EXIT_CODE_OK = 0 +EXIT_CODE_ERROR = 1 +EXIT_CODE_EXISTS_DIFF = 2 +EXIT_CODE_ARGPARSE_ERROR = 99 + +# For generating line shortening candidates. +SHORTEN_OPERATOR_GROUPS = frozenset([ + frozenset([',']), + frozenset(['%']), + frozenset([',', '(', '[', '{']), + frozenset(['%', '(', '[', '{']), + frozenset([',', '(', '[', '{', '%', '+', '-', '*', '/', '//']), + frozenset(['%', '+', '-', '*', '/', '//']), +]) + + +DEFAULT_IGNORE = 'E226,E24,W50,W690' # TODO: use pycodestyle.DEFAULT_IGNORE +DEFAULT_INDENT_SIZE = 4 +# these fixes conflict with each other, if the `--ignore` setting causes both +# to be enabled, disable both of them +CONFLICTING_CODES = ('W503', 'W504') + +# W602 is handled separately due to the need to avoid "with_traceback". +CODE_TO_2TO3 = { + 'E231': ['ws_comma'], + 'E721': ['idioms'], + 'W690': ['apply', + 'except', + 'exitfunc', + 'numliterals', + 'operator', + 'paren', + 'reduce', + 'renames', + 'standarderror', + 'sys_exc', + 'throw', + 'tuple_params', + 'xreadlines']} + + +if sys.platform == 'win32': # pragma: no cover + DEFAULT_CONFIG = os.path.expanduser(r'~\.pycodestyle') +else: + DEFAULT_CONFIG = os.path.join(os.getenv('XDG_CONFIG_HOME') or + os.path.expanduser('~/.config'), + 'pycodestyle') +# fallback, use .pep8 +if not os.path.exists(DEFAULT_CONFIG): # pragma: no cover + if sys.platform == 'win32': + DEFAULT_CONFIG = os.path.expanduser(r'~\.pep8') + else: + DEFAULT_CONFIG = os.path.join(os.path.expanduser('~/.config'), 'pep8') +PROJECT_CONFIG = ('setup.cfg', 'tox.ini', '.pep8', '.flake8') + + +MAX_PYTHON_FILE_DETECTION_BYTES = 1024 + + +def open_with_encoding(filename, mode='r', encoding=None, limit_byte_check=-1): + """Return opened file with a specific encoding.""" + if not encoding: + encoding = detect_encoding(filename, limit_byte_check=limit_byte_check) + + return io.open(filename, mode=mode, encoding=encoding, + newline='') # Preserve line endings + + +def detect_encoding(filename, limit_byte_check=-1): + """Return file encoding.""" + try: + with open(filename, 'rb') as input_file: + from lib2to3.pgen2 import tokenize as lib2to3_tokenize + encoding = lib2to3_tokenize.detect_encoding(input_file.readline)[0] + + with open_with_encoding(filename, encoding=encoding) as test_file: + test_file.read(limit_byte_check) + + return encoding + except (LookupError, SyntaxError, UnicodeDecodeError): + return 'latin-1' + + +def readlines_from_file(filename): + """Return contents of file.""" + with open_with_encoding(filename) as input_file: + return input_file.readlines() + + +def extended_blank_lines(logical_line, + blank_lines, + blank_before, + indent_level, + previous_logical): + """Check for missing blank lines after class declaration.""" + if previous_logical.startswith('def '): + if blank_lines and pycodestyle.DOCSTRING_REGEX.match(logical_line): + yield (0, 'E303 too many blank lines ({})'.format(blank_lines)) + elif pycodestyle.DOCSTRING_REGEX.match(previous_logical): + # Missing blank line between class docstring and method declaration. + if ( + indent_level and + not blank_lines and + not blank_before and + logical_line.startswith(('def ')) and + '(self' in logical_line + ): + yield (0, 'E301 expected 1 blank line, found 0') + + +pycodestyle.register_check(extended_blank_lines) + + +def continued_indentation(logical_line, tokens, indent_level, hang_closing, + indent_char, noqa): + """Override pycodestyle's function to provide indentation information.""" + first_row = tokens[0][2][0] + nrows = 1 + tokens[-1][2][0] - first_row + if noqa or nrows == 1: + return + + # indent_next tells us whether the next block is indented. Assuming + # that it is indented by 4 spaces, then we should not allow 4-space + # indents on the final continuation line. In turn, some other + # indents are allowed to have an extra 4 spaces. + indent_next = logical_line.endswith(':') + + row = depth = 0 + valid_hangs = ( + (DEFAULT_INDENT_SIZE,) + if indent_char != '\t' else (DEFAULT_INDENT_SIZE, + 2 * DEFAULT_INDENT_SIZE) + ) + + # Remember how many brackets were opened on each line. + parens = [0] * nrows + + # Relative indents of physical lines. + rel_indent = [0] * nrows + + # For each depth, collect a list of opening rows. + open_rows = [[0]] + # For each depth, memorize the hanging indentation. + hangs = [None] + + # Visual indents. + indent_chances = {} + last_indent = tokens[0][2] + indent = [last_indent[1]] + + last_token_multiline = None + line = None + last_line = '' + last_line_begins_with_multiline = False + for token_type, text, start, end, line in tokens: + + newline = row < start[0] - first_row + if newline: + row = start[0] - first_row + newline = (not last_token_multiline and + token_type not in (tokenize.NL, tokenize.NEWLINE)) + last_line_begins_with_multiline = last_token_multiline + + if newline: + # This is the beginning of a continuation line. + last_indent = start + + # Record the initial indent. + rel_indent[row] = pycodestyle.expand_indent(line) - indent_level + + # Identify closing bracket. + close_bracket = (token_type == tokenize.OP and text in ']})') + + # Is the indent relative to an opening bracket line? + for open_row in reversed(open_rows[depth]): + hang = rel_indent[row] - rel_indent[open_row] + hanging_indent = hang in valid_hangs + if hanging_indent: + break + if hangs[depth]: + hanging_indent = (hang == hangs[depth]) + + visual_indent = (not close_bracket and hang > 0 and + indent_chances.get(start[1])) + + if close_bracket and indent[depth]: + # Closing bracket for visual indent. + if start[1] != indent[depth]: + yield (start, 'E124 {}'.format(indent[depth])) + elif close_bracket and not hang: + # closing bracket matches indentation of opening bracket's line + if hang_closing: + yield (start, 'E133 {}'.format(indent[depth])) + elif indent[depth] and start[1] < indent[depth]: + if visual_indent is not True: + # Visual indent is broken. + yield (start, 'E128 {}'.format(indent[depth])) + elif (hanging_indent or + (indent_next and + rel_indent[row] == 2 * DEFAULT_INDENT_SIZE)): + # Hanging indent is verified. + if close_bracket and not hang_closing: + yield (start, 'E123 {}'.format(indent_level + + rel_indent[open_row])) + hangs[depth] = hang + elif visual_indent is True: + # Visual indent is verified. + indent[depth] = start[1] + elif visual_indent in (text, str): + # Ignore token lined up with matching one from a previous line. + pass + else: + one_indented = (indent_level + rel_indent[open_row] + + DEFAULT_INDENT_SIZE) + # Indent is broken. + if hang <= 0: + error = ('E122', one_indented) + elif indent[depth]: + error = ('E127', indent[depth]) + elif not close_bracket and hangs[depth]: + error = ('E131', one_indented) + elif hang > DEFAULT_INDENT_SIZE: + error = ('E126', one_indented) + else: + hangs[depth] = hang + error = ('E121', one_indented) + + yield (start, '{} {}'.format(*error)) + + # Look for visual indenting. + if ( + parens[row] and + token_type not in (tokenize.NL, tokenize.COMMENT) and + not indent[depth] + ): + indent[depth] = start[1] + indent_chances[start[1]] = True + # Deal with implicit string concatenation. + elif (token_type in (tokenize.STRING, tokenize.COMMENT) or + text in ('u', 'ur', 'b', 'br')): + indent_chances[start[1]] = str + # Special case for the "if" statement because len("if (") is equal to + # 4. + elif not indent_chances and not row and not depth and text == 'if': + indent_chances[end[1] + 1] = True + elif text == ':' and line[end[1]:].isspace(): + open_rows[depth].append(row) + + # Keep track of bracket depth. + if token_type == tokenize.OP: + if text in '([{': + depth += 1 + indent.append(0) + hangs.append(None) + if len(open_rows) == depth: + open_rows.append([]) + open_rows[depth].append(row) + parens[row] += 1 + elif text in ')]}' and depth > 0: + # Parent indents should not be more than this one. + prev_indent = indent.pop() or last_indent[1] + hangs.pop() + for d in range(depth): + if indent[d] > prev_indent: + indent[d] = 0 + for ind in list(indent_chances): + if ind >= prev_indent: + del indent_chances[ind] + del open_rows[depth + 1:] + depth -= 1 + if depth: + indent_chances[indent[depth]] = True + for idx in range(row, -1, -1): + if parens[idx]: + parens[idx] -= 1 + break + assert len(indent) == depth + 1 + if ( + start[1] not in indent_chances and + # This is for purposes of speeding up E121 (GitHub #90). + not last_line.rstrip().endswith(',') + ): + # Allow to line up tokens. + indent_chances[start[1]] = text + + last_token_multiline = (start[0] != end[0]) + if last_token_multiline: + rel_indent[end[0] - first_row] = rel_indent[row] + + last_line = line + + if ( + indent_next and + not last_line_begins_with_multiline and + pycodestyle.expand_indent(line) == indent_level + DEFAULT_INDENT_SIZE + ): + pos = (start[0], indent[0] + 4) + desired_indent = indent_level + 2 * DEFAULT_INDENT_SIZE + if visual_indent: + yield (pos, 'E129 {}'.format(desired_indent)) + else: + yield (pos, 'E125 {}'.format(desired_indent)) + + +del pycodestyle._checks['logical_line'][pycodestyle.continued_indentation] +pycodestyle.register_check(continued_indentation) + + +class FixPEP8(object): + + """Fix invalid code. + + Fixer methods are prefixed "fix_". The _fix_source() method looks for these + automatically. + + The fixer method can take either one or two arguments (in addition to + self). The first argument is "result", which is the error information from + pycodestyle. The second argument, "logical", is required only for + logical-line fixes. + + The fixer method can return the list of modified lines or None. An empty + list would mean that no changes were made. None would mean that only the + line reported in the pycodestyle error was modified. Note that the modified + line numbers that are returned are indexed at 1. This typically would + correspond with the line number reported in the pycodestyle error + information. + + [fixed method list] + - e111,e114,e115,e116 + - e121,e122,e123,e124,e125,e126,e127,e128,e129 + - e201,e202,e203 + - e211 + - e221,e222,e223,e224,e225 + - e231 + - e251,e252 + - e261,e262 + - e271,e272,e273,e274,e275 + - e301,e302,e303,e304,e305,e306 + - e401,e402 + - e502 + - e701,e702,e703,e704 + - e711,e712,e713,e714 + - e722 + - e731 + - w291 + - w503,504 + + """ + + def __init__(self, filename, + options, + contents=None, + long_line_ignore_cache=None): + self.filename = filename + if contents is None: + self.source = readlines_from_file(filename) + else: + sio = io.StringIO(contents) + self.source = sio.readlines() + self.options = options + self.indent_word = _get_indentword(''.join(self.source)) + + # collect imports line + self.imports = {} + for i, line in enumerate(self.source): + if (line.find("import ") == 0 or line.find("from ") == 0) and \ + line not in self.imports: + # collect only import statements that first appeared + self.imports[line] = i + + self.long_line_ignore_cache = ( + set() if long_line_ignore_cache is None + else long_line_ignore_cache) + + # Many fixers are the same even though pycodestyle categorizes them + # differently. + self.fix_e115 = self.fix_e112 + self.fix_e121 = self._fix_reindent + self.fix_e122 = self._fix_reindent + self.fix_e123 = self._fix_reindent + self.fix_e124 = self._fix_reindent + self.fix_e126 = self._fix_reindent + self.fix_e127 = self._fix_reindent + self.fix_e128 = self._fix_reindent + self.fix_e129 = self._fix_reindent + self.fix_e133 = self.fix_e131 + self.fix_e202 = self.fix_e201 + self.fix_e203 = self.fix_e201 + self.fix_e211 = self.fix_e201 + self.fix_e221 = self.fix_e271 + self.fix_e222 = self.fix_e271 + self.fix_e223 = self.fix_e271 + self.fix_e226 = self.fix_e225 + self.fix_e227 = self.fix_e225 + self.fix_e228 = self.fix_e225 + self.fix_e241 = self.fix_e271 + self.fix_e242 = self.fix_e224 + self.fix_e252 = self.fix_e225 + self.fix_e261 = self.fix_e262 + self.fix_e272 = self.fix_e271 + self.fix_e273 = self.fix_e271 + self.fix_e274 = self.fix_e271 + self.fix_e275 = self.fix_e271 + self.fix_e306 = self.fix_e301 + self.fix_e501 = ( + self.fix_long_line_logically if + options and (options.aggressive >= 2 or options.experimental) else + self.fix_long_line_physically) + self.fix_e703 = self.fix_e702 + self.fix_w292 = self.fix_w291 + self.fix_w293 = self.fix_w291 + + def _fix_source(self, results): + try: + (logical_start, logical_end) = _find_logical(self.source) + logical_support = True + except (SyntaxError, tokenize.TokenError): # pragma: no cover + logical_support = False + + completed_lines = set() + for result in sorted(results, key=_priority_key): + if result['line'] in completed_lines: + continue + + fixed_methodname = 'fix_' + result['id'].lower() + if hasattr(self, fixed_methodname): + fix = getattr(self, fixed_methodname) + + line_index = result['line'] - 1 + original_line = self.source[line_index] + + is_logical_fix = len(_get_parameters(fix)) > 2 + if is_logical_fix: + logical = None + if logical_support: + logical = _get_logical(self.source, + result, + logical_start, + logical_end) + if logical and set(range( + logical[0][0] + 1, + logical[1][0] + 1)).intersection( + completed_lines): + continue + + modified_lines = fix(result, logical) + else: + modified_lines = fix(result) + + if modified_lines is None: + # Force logical fixes to report what they modified. + assert not is_logical_fix + + if self.source[line_index] == original_line: + modified_lines = [] + + if modified_lines: + completed_lines.update(modified_lines) + elif modified_lines == []: # Empty list means no fix + if self.options.verbose >= 2: + print( + '---> Not fixing {error} on line {line}'.format( + error=result['id'], line=result['line']), + file=sys.stderr) + else: # We assume one-line fix when None. + completed_lines.add(result['line']) + else: + if self.options.verbose >= 3: + print( + "---> '{}' is not defined.".format(fixed_methodname), + file=sys.stderr) + + info = result['info'].strip() + print('---> {}:{}:{}:{}'.format(self.filename, + result['line'], + result['column'], + info), + file=sys.stderr) + + def fix(self): + """Return a version of the source code with PEP 8 violations fixed.""" + pep8_options = { + 'ignore': self.options.ignore, + 'select': self.options.select, + 'max_line_length': self.options.max_line_length, + 'hang_closing': self.options.hang_closing, + } + results = _execute_pep8(pep8_options, self.source) + + if self.options.verbose: + progress = {} + for r in results: + if r['id'] not in progress: + progress[r['id']] = set() + progress[r['id']].add(r['line']) + print('---> {n} issue(s) to fix {progress}'.format( + n=len(results), progress=progress), file=sys.stderr) + + if self.options.line_range: + start, end = self.options.line_range + results = [r for r in results + if start <= r['line'] <= end] + + self._fix_source(filter_results(source=''.join(self.source), + results=results, + aggressive=self.options.aggressive)) + + if self.options.line_range: + # If number of lines has changed then change line_range. + count = sum(sline.count('\n') + for sline in self.source[start - 1:end]) + self.options.line_range[1] = start + count - 1 + + return ''.join(self.source) + + def _fix_reindent(self, result): + """Fix a badly indented line. + + This is done by adding or removing from its initial indent only. + + """ + num_indent_spaces = int(result['info'].split()[1]) + line_index = result['line'] - 1 + target = self.source[line_index] + + self.source[line_index] = ' ' * num_indent_spaces + target.lstrip() + + def fix_e112(self, result): + """Fix under-indented comments.""" + line_index = result['line'] - 1 + target = self.source[line_index] + + if not target.lstrip().startswith('#'): + # Don't screw with invalid syntax. + return [] + + self.source[line_index] = self.indent_word + target + + def fix_e113(self, result): + """Fix unexpected indentation.""" + line_index = result['line'] - 1 + target = self.source[line_index] + indent = _get_indentation(target) + stripped = target.lstrip() + self.source[line_index] = indent[1:] + stripped + + def fix_e116(self, result): + """Fix over-indented comments.""" + line_index = result['line'] - 1 + target = self.source[line_index] + + indent = _get_indentation(target) + stripped = target.lstrip() + + if not stripped.startswith('#'): + # Don't screw with invalid syntax. + return [] + + self.source[line_index] = indent[1:] + stripped + + def fix_e117(self, result): + """Fix over-indented.""" + line_index = result['line'] - 1 + target = self.source[line_index] + + indent = _get_indentation(target) + if indent == '\t': + return [] + + stripped = target.lstrip() + + self.source[line_index] = indent[1:] + stripped + + def fix_e125(self, result): + """Fix indentation undistinguish from the next logical line.""" + num_indent_spaces = int(result['info'].split()[1]) + line_index = result['line'] - 1 + target = self.source[line_index] + + spaces_to_add = num_indent_spaces - len(_get_indentation(target)) + indent = len(_get_indentation(target)) + modified_lines = [] + + while len(_get_indentation(self.source[line_index])) >= indent: + self.source[line_index] = (' ' * spaces_to_add + + self.source[line_index]) + modified_lines.append(1 + line_index) # Line indexed at 1. + line_index -= 1 + + return modified_lines + + def fix_e131(self, result): + """Fix indentation undistinguish from the next logical line.""" + num_indent_spaces = int(result['info'].split()[1]) + line_index = result['line'] - 1 + target = self.source[line_index] + + spaces_to_add = num_indent_spaces - len(_get_indentation(target)) + + indent_length = len(_get_indentation(target)) + spaces_to_add = num_indent_spaces - indent_length + if num_indent_spaces == 0 and indent_length == 0: + spaces_to_add = 4 + + if spaces_to_add >= 0: + self.source[line_index] = (' ' * spaces_to_add + + self.source[line_index]) + else: + offset = abs(spaces_to_add) + self.source[line_index] = self.source[line_index][offset:] + + def fix_e201(self, result): + """Remove extraneous whitespace.""" + line_index = result['line'] - 1 + target = self.source[line_index] + offset = result['column'] - 1 + + fixed = fix_whitespace(target, + offset=offset, + replacement='') + + self.source[line_index] = fixed + + def fix_e224(self, result): + """Remove extraneous whitespace around operator.""" + target = self.source[result['line'] - 1] + offset = result['column'] - 1 + fixed = target[:offset] + target[offset:].replace('\t', ' ') + self.source[result['line'] - 1] = fixed + + def fix_e225(self, result): + """Fix missing whitespace around operator.""" + target = self.source[result['line'] - 1] + offset = result['column'] - 1 + fixed = target[:offset] + ' ' + target[offset:] + + # Only proceed if non-whitespace characters match. + # And make sure we don't break the indentation. + if ( + fixed.replace(' ', '') == target.replace(' ', '') and + _get_indentation(fixed) == _get_indentation(target) + ): + self.source[result['line'] - 1] = fixed + error_code = result.get('id', 0) + try: + ts = generate_tokens(fixed) + except (SyntaxError, tokenize.TokenError): + return + if not check_syntax(fixed.lstrip()): + return + errors = list( + pycodestyle.missing_whitespace_around_operator(fixed, ts)) + for e in reversed(errors): + if error_code != e[1].split()[0]: + continue + offset = e[0][1] + fixed = fixed[:offset] + ' ' + fixed[offset:] + self.source[result['line'] - 1] = fixed + else: + return [] + + def fix_e231(self, result): + """Add missing whitespace.""" + line_index = result['line'] - 1 + target = self.source[line_index] + offset = result['column'] + fixed = target[:offset].rstrip() + ' ' + target[offset:].lstrip() + self.source[line_index] = fixed + + def fix_e251(self, result): + """Remove whitespace around parameter '=' sign.""" + line_index = result['line'] - 1 + target = self.source[line_index] + + # This is necessary since pycodestyle sometimes reports columns that + # goes past the end of the physical line. This happens in cases like, + # foo(bar\n=None) + c = min(result['column'] - 1, + len(target) - 1) + + if target[c].strip(): + fixed = target + else: + fixed = target[:c].rstrip() + target[c:].lstrip() + + # There could be an escaped newline + # + # def foo(a=\ + # 1) + if fixed.endswith(('=\\\n', '=\\\r\n', '=\\\r')): + self.source[line_index] = fixed.rstrip('\n\r \t\\') + self.source[line_index + 1] = self.source[line_index + 1].lstrip() + return [line_index + 1, line_index + 2] # Line indexed at 1 + + self.source[result['line'] - 1] = fixed + + def fix_e262(self, result): + """Fix spacing after inline comment hash.""" + target = self.source[result['line'] - 1] + offset = result['column'] + + code = target[:offset].rstrip(' \t#') + comment = target[offset:].lstrip(' \t#') + + fixed = code + (' # ' + comment if comment.strip() else '\n') + + self.source[result['line'] - 1] = fixed + + def fix_e265(self, result): + """Fix spacing after block comment hash.""" + target = self.source[result['line'] - 1] + + indent = _get_indentation(target) + line = target.lstrip(' \t') + pos = next((index for index, c in enumerate(line) if c != '#')) + hashes = line[:pos] + comment = line[pos:].lstrip(' \t') + + # Ignore special comments, even in the middle of the file. + if comment.startswith('!'): + return + + fixed = indent + hashes + (' ' + comment if comment.strip() else '\n') + + self.source[result['line'] - 1] = fixed + + def fix_e266(self, result): + """Fix too many block comment hashes.""" + target = self.source[result['line'] - 1] + + # Leave stylistic outlined blocks alone. + if target.strip().endswith('#'): + return + + indentation = _get_indentation(target) + fixed = indentation + '# ' + target.lstrip('# \t') + + self.source[result['line'] - 1] = fixed + + def fix_e271(self, result): + """Fix extraneous whitespace around keywords.""" + line_index = result['line'] - 1 + target = self.source[line_index] + offset = result['column'] - 1 + + fixed = fix_whitespace(target, + offset=offset, + replacement=' ') + + if fixed == target: + return [] + else: + self.source[line_index] = fixed + + def fix_e301(self, result): + """Add missing blank line.""" + cr = '\n' + self.source[result['line'] - 1] = cr + self.source[result['line'] - 1] + + def fix_e302(self, result): + """Add missing 2 blank lines.""" + add_linenum = 2 - int(result['info'].split()[-1]) + offset = 1 + if self.source[result['line'] - 2].strip() == "\\": + offset = 2 + cr = '\n' * add_linenum + self.source[result['line'] - offset] = ( + cr + self.source[result['line'] - offset] + ) + + def fix_e303(self, result): + """Remove extra blank lines.""" + delete_linenum = int(result['info'].split('(')[1].split(')')[0]) - 2 + delete_linenum = max(1, delete_linenum) + + # We need to count because pycodestyle reports an offset line number if + # there are comments. + cnt = 0 + line = result['line'] - 2 + modified_lines = [] + while cnt < delete_linenum and line >= 0: + if not self.source[line].strip(): + self.source[line] = '' + modified_lines.append(1 + line) # Line indexed at 1 + cnt += 1 + line -= 1 + + return modified_lines + + def fix_e304(self, result): + """Remove blank line following function decorator.""" + line = result['line'] - 2 + if not self.source[line].strip(): + self.source[line] = '' + + def fix_e305(self, result): + """Add missing 2 blank lines after end of function or class.""" + add_delete_linenum = 2 - int(result['info'].split()[-1]) + cnt = 0 + offset = result['line'] - 2 + modified_lines = [] + if add_delete_linenum < 0: + # delete cr + add_delete_linenum = abs(add_delete_linenum) + while cnt < add_delete_linenum and offset >= 0: + if not self.source[offset].strip(): + self.source[offset] = '' + modified_lines.append(1 + offset) # Line indexed at 1 + cnt += 1 + offset -= 1 + else: + # add cr + cr = '\n' + # check comment line + while True: + if offset < 0: + break + line = self.source[offset].lstrip() + if not line: + break + if line[0] != '#': + break + offset -= 1 + offset += 1 + self.source[offset] = cr + self.source[offset] + modified_lines.append(1 + offset) # Line indexed at 1. + return modified_lines + + def fix_e401(self, result): + """Put imports on separate lines.""" + line_index = result['line'] - 1 + target = self.source[line_index] + offset = result['column'] - 1 + + if not target.lstrip().startswith('import'): + return [] + + indentation = re.split(pattern=r'\bimport\b', + string=target, maxsplit=1)[0] + fixed = (target[:offset].rstrip('\t ,') + '\n' + + indentation + 'import ' + target[offset:].lstrip('\t ,')) + self.source[line_index] = fixed + + def fix_e402(self, result): + (line_index, offset, target) = get_index_offset_contents(result, + self.source) + for i in range(1, 100): + line = "".join(self.source[line_index:line_index+i]) + try: + generate_tokens("".join(line)) + except (SyntaxError, tokenize.TokenError): + continue + break + if not (target in self.imports and self.imports[target] != line_index): + mod_offset = get_module_imports_on_top_of_file(self.source, + line_index) + self.source[mod_offset] = line + self.source[mod_offset] + for offset in range(i): + self.source[line_index+offset] = '' + + def fix_long_line_logically(self, result, logical): + """Try to make lines fit within --max-line-length characters.""" + if ( + not logical or + len(logical[2]) == 1 or + self.source[result['line'] - 1].lstrip().startswith('#') + ): + return self.fix_long_line_physically(result) + + start_line_index = logical[0][0] + end_line_index = logical[1][0] + logical_lines = logical[2] + + previous_line = get_item(self.source, start_line_index - 1, default='') + next_line = get_item(self.source, end_line_index + 1, default='') + + single_line = join_logical_line(''.join(logical_lines)) + + try: + fixed = self.fix_long_line( + target=single_line, + previous_line=previous_line, + next_line=next_line, + original=''.join(logical_lines)) + except (SyntaxError, tokenize.TokenError): + return self.fix_long_line_physically(result) + + if fixed: + for line_index in range(start_line_index, end_line_index + 1): + self.source[line_index] = '' + self.source[start_line_index] = fixed + return range(start_line_index + 1, end_line_index + 1) + + return [] + + def fix_long_line_physically(self, result): + """Try to make lines fit within --max-line-length characters.""" + line_index = result['line'] - 1 + target = self.source[line_index] + + previous_line = get_item(self.source, line_index - 1, default='') + next_line = get_item(self.source, line_index + 1, default='') + + try: + fixed = self.fix_long_line( + target=target, + previous_line=previous_line, + next_line=next_line, + original=target) + except (SyntaxError, tokenize.TokenError): + return [] + + if fixed: + self.source[line_index] = fixed + return [line_index + 1] + + return [] + + def fix_long_line(self, target, previous_line, + next_line, original): + cache_entry = (target, previous_line, next_line) + if cache_entry in self.long_line_ignore_cache: + return [] + + if target.lstrip().startswith('#'): + if self.options.aggressive: + # Wrap commented lines. + return shorten_comment( + line=target, + max_line_length=self.options.max_line_length, + last_comment=not next_line.lstrip().startswith('#')) + return [] + + fixed = get_fixed_long_line( + target=target, + previous_line=previous_line, + original=original, + indent_word=self.indent_word, + max_line_length=self.options.max_line_length, + aggressive=self.options.aggressive, + experimental=self.options.experimental, + verbose=self.options.verbose) + + if fixed and not code_almost_equal(original, fixed): + return fixed + + self.long_line_ignore_cache.add(cache_entry) + return None + + def fix_e502(self, result): + """Remove extraneous escape of newline.""" + (line_index, _, target) = get_index_offset_contents(result, + self.source) + self.source[line_index] = target.rstrip('\n\r \t\\') + '\n' + + def fix_e701(self, result): + """Put colon-separated compound statement on separate lines.""" + line_index = result['line'] - 1 + target = self.source[line_index] + c = result['column'] + + fixed_source = (target[:c] + '\n' + + _get_indentation(target) + self.indent_word + + target[c:].lstrip('\n\r \t\\')) + self.source[result['line'] - 1] = fixed_source + return [result['line'], result['line'] + 1] + + def fix_e702(self, result, logical): + """Put semicolon-separated compound statement on separate lines.""" + if not logical: + return [] # pragma: no cover + logical_lines = logical[2] + + # Avoid applying this when indented. + # https://docs.python.org/reference/compound_stmts.html + for line in logical_lines: + if (result['id'] == 'E702' and ':' in line + and STARTSWITH_INDENT_STATEMENT_REGEX.match(line)): + if self.options.verbose: + print( + '---> avoid fixing {error} with ' + 'other compound statements'.format(error=result['id']), + file=sys.stderr + ) + return [] + + line_index = result['line'] - 1 + target = self.source[line_index] + + if target.rstrip().endswith('\\'): + # Normalize '1; \\\n2' into '1; 2'. + self.source[line_index] = target.rstrip('\n \r\t\\') + self.source[line_index + 1] = self.source[line_index + 1].lstrip() + return [line_index + 1, line_index + 2] + + if target.rstrip().endswith(';'): + self.source[line_index] = target.rstrip('\n \r\t;') + '\n' + return [line_index + 1] + + offset = result['column'] - 1 + first = target[:offset].rstrip(';').rstrip() + second = (_get_indentation(logical_lines[0]) + + target[offset:].lstrip(';').lstrip()) + + # Find inline comment. + inline_comment = None + if target[offset:].lstrip(';').lstrip()[:2] == '# ': + inline_comment = target[offset:].lstrip(';') + + if inline_comment: + self.source[line_index] = first + inline_comment + else: + self.source[line_index] = first + '\n' + second + return [line_index + 1] + + def fix_e704(self, result): + """Fix multiple statements on one line def""" + (line_index, _, target) = get_index_offset_contents(result, + self.source) + match = STARTSWITH_DEF_REGEX.match(target) + if match: + self.source[line_index] = '{}\n{}{}'.format( + match.group(0), + _get_indentation(target) + self.indent_word, + target[match.end(0):].lstrip()) + + def fix_e711(self, result): + """Fix comparison with None.""" + (line_index, offset, target) = get_index_offset_contents(result, + self.source) + + right_offset = offset + 2 + if right_offset >= len(target): + return [] + + left = target[:offset].rstrip() + center = target[offset:right_offset] + right = target[right_offset:].lstrip() + + if center.strip() == '==': + new_center = 'is' + elif center.strip() == '!=': + new_center = 'is not' + else: + return [] + + self.source[line_index] = ' '.join([left, new_center, right]) + + def fix_e712(self, result): + """Fix (trivial case of) comparison with boolean.""" + (line_index, offset, target) = get_index_offset_contents(result, + self.source) + + # Handle very easy "not" special cases. + if re.match(r'^\s*if [\w."\'\[\]]+ == False:$', target): + self.source[line_index] = re.sub(r'if ([\w."\'\[\]]+) == False:', + r'if not \1:', target, count=1) + elif re.match(r'^\s*if [\w."\'\[\]]+ != True:$', target): + self.source[line_index] = re.sub(r'if ([\w."\'\[\]]+) != True:', + r'if not \1:', target, count=1) + else: + right_offset = offset + 2 + if right_offset >= len(target): + return [] + + left = target[:offset].rstrip() + center = target[offset:right_offset] + right = target[right_offset:].lstrip() + + # Handle simple cases only. + new_right = None + if center.strip() == '==': + if re.match(r'\bTrue\b', right): + new_right = re.sub(r'\bTrue\b *', '', right, count=1) + elif center.strip() == '!=': + if re.match(r'\bFalse\b', right): + new_right = re.sub(r'\bFalse\b *', '', right, count=1) + + if new_right is None: + return [] + + if new_right[0].isalnum(): + new_right = ' ' + new_right + + self.source[line_index] = left + new_right + + def fix_e713(self, result): + """Fix (trivial case of) non-membership check.""" + (line_index, offset, target) = get_index_offset_contents(result, + self.source) + + # to convert once 'not in' -> 'in' + before_target = target[:offset] + target = target[offset:] + match_notin = COMPARE_NEGATIVE_REGEX_THROUGH.search(target) + notin_pos_start, notin_pos_end = 0, 0 + if match_notin: + notin_pos_start = match_notin.start(1) + notin_pos_end = match_notin.end() + target = '{}{} {}'.format( + target[:notin_pos_start], 'in', target[notin_pos_end:]) + + # fix 'not in' + match = COMPARE_NEGATIVE_REGEX.search(target) + if match: + if match.group(3) == 'in': + pos_start = match.start(1) + new_target = '{5}{0}{1} {2} {3} {4}'.format( + target[:pos_start], match.group(2), match.group(1), + match.group(3), target[match.end():], before_target) + if match_notin: + # revert 'in' -> 'not in' + pos_start = notin_pos_start + offset + pos_end = notin_pos_end + offset - 4 # len('not ') + new_target = '{}{} {}'.format( + new_target[:pos_start], 'not in', new_target[pos_end:]) + self.source[line_index] = new_target + + def fix_e714(self, result): + """Fix object identity should be 'is not' case.""" + (line_index, offset, target) = get_index_offset_contents(result, + self.source) + + # to convert once 'is not' -> 'is' + before_target = target[:offset] + target = target[offset:] + match_isnot = COMPARE_NEGATIVE_REGEX_THROUGH.search(target) + isnot_pos_start, isnot_pos_end = 0, 0 + if match_isnot: + isnot_pos_start = match_isnot.start(1) + isnot_pos_end = match_isnot.end() + target = '{}{} {}'.format( + target[:isnot_pos_start], 'in', target[isnot_pos_end:]) + + match = COMPARE_NEGATIVE_REGEX.search(target) + if match: + if match.group(3).startswith('is'): + pos_start = match.start(1) + new_target = '{5}{0}{1} {2} {3} {4}'.format( + target[:pos_start], match.group(2), match.group(3), + match.group(1), target[match.end():], before_target) + if match_isnot: + # revert 'is' -> 'is not' + pos_start = isnot_pos_start + offset + pos_end = isnot_pos_end + offset - 4 # len('not ') + new_target = '{}{} {}'.format( + new_target[:pos_start], 'is not', new_target[pos_end:]) + self.source[line_index] = new_target + + def fix_e722(self, result): + """fix bare except""" + (line_index, _, target) = get_index_offset_contents(result, + self.source) + match = BARE_EXCEPT_REGEX.search(target) + if match: + self.source[line_index] = '{}{}{}'.format( + target[:result['column'] - 1], "except BaseException:", + target[match.end():]) + + def fix_e731(self, result): + """Fix do not assign a lambda expression check.""" + (line_index, _, target) = get_index_offset_contents(result, + self.source) + match = LAMBDA_REGEX.search(target) + if match: + end = match.end() + self.source[line_index] = '{}def {}({}): return {}'.format( + target[:match.start(0)], match.group(1), match.group(2), + target[end:].lstrip()) + + def fix_w291(self, result): + """Remove trailing whitespace.""" + fixed_line = self.source[result['line'] - 1].rstrip() + self.source[result['line'] - 1] = fixed_line + '\n' + + def fix_w391(self, _): + """Remove trailing blank lines.""" + blank_count = 0 + for line in reversed(self.source): + line = line.rstrip() + if line: + break + else: + blank_count += 1 + + original_length = len(self.source) + self.source = self.source[:original_length - blank_count] + return range(1, 1 + original_length) + + def fix_w503(self, result): + (line_index, _, target) = get_index_offset_contents(result, + self.source) + one_string_token = target.split()[0] + try: + ts = generate_tokens(one_string_token) + except (SyntaxError, tokenize.TokenError): + return + if not _is_binary_operator(ts[0][0], one_string_token): + return + # find comment + comment_index = 0 + found_not_comment_only_line = False + comment_only_linenum = 0 + for i in range(5): + # NOTE: try to parse code in 5 times + if (line_index - i) < 0: + break + from_index = line_index - i - 1 + if from_index < 0 or len(self.source) <= from_index: + break + to_index = line_index + 1 + strip_line = self.source[from_index].lstrip() + if ( + not found_not_comment_only_line and + strip_line and strip_line[0] == '#' + ): + comment_only_linenum += 1 + continue + found_not_comment_only_line = True + try: + ts = generate_tokens("".join(self.source[from_index:to_index])) + except (SyntaxError, tokenize.TokenError): + continue + newline_count = 0 + newline_index = [] + for index, t in enumerate(ts): + if t[0] in (tokenize.NEWLINE, tokenize.NL): + newline_index.append(index) + newline_count += 1 + if newline_count > 2: + tts = ts[newline_index[-3]:] + else: + tts = ts + old = [] + for t in tts: + if t[0] in (tokenize.NEWLINE, tokenize.NL): + newline_count -= 1 + if newline_count <= 1: + break + if tokenize.COMMENT == t[0] and old and old[0] != tokenize.NL: + comment_index = old[3][1] + break + old = t + break + i = target.index(one_string_token) + fix_target_line = line_index - 1 - comment_only_linenum + self.source[line_index] = '{}{}'.format( + target[:i], target[i + len(one_string_token):].lstrip()) + nl = find_newline(self.source[fix_target_line:line_index]) + before_line = self.source[fix_target_line] + bl = before_line.index(nl) + if comment_index: + self.source[fix_target_line] = '{} {} {}'.format( + before_line[:comment_index], one_string_token, + before_line[comment_index + 1:]) + else: + if before_line[:bl].endswith("#"): + # special case + # see: https://github.com/hhatto/autopep8/issues/503 + self.source[fix_target_line] = '{}{} {}'.format( + before_line[:bl-2], one_string_token, before_line[bl-2:]) + else: + self.source[fix_target_line] = '{} {}{}'.format( + before_line[:bl], one_string_token, before_line[bl:]) + + def fix_w504(self, result): + (line_index, _, target) = get_index_offset_contents(result, + self.source) + # NOTE: is not collect pointed out in pycodestyle==2.4.0 + comment_index = 0 + operator_position = None # (start_position, end_position) + for i in range(1, 6): + to_index = line_index + i + try: + ts = generate_tokens("".join(self.source[line_index:to_index])) + except (SyntaxError, tokenize.TokenError): + continue + newline_count = 0 + newline_index = [] + for index, t in enumerate(ts): + if _is_binary_operator(t[0], t[1]): + if t[2][0] == 1 and t[3][0] == 1: + operator_position = (t[2][1], t[3][1]) + elif t[0] == tokenize.NAME and t[1] in ("and", "or"): + if t[2][0] == 1 and t[3][0] == 1: + operator_position = (t[2][1], t[3][1]) + elif t[0] in (tokenize.NEWLINE, tokenize.NL): + newline_index.append(index) + newline_count += 1 + if newline_count > 2: + tts = ts[:newline_index[-3]] + else: + tts = ts + old = [] + for t in tts: + if tokenize.COMMENT == t[0] and old: + comment_row, comment_index = old[3] + break + old = t + break + if not operator_position: + return + target_operator = target[operator_position[0]:operator_position[1]] + + if comment_index and comment_row == 1: + self.source[line_index] = '{}{}'.format( + target[:operator_position[0]].rstrip(), + target[comment_index:]) + else: + self.source[line_index] = '{}{}{}'.format( + target[:operator_position[0]].rstrip(), + target[operator_position[1]:].lstrip(), + target[operator_position[1]:]) + + next_line = self.source[line_index + 1] + next_line_indent = 0 + m = re.match(r'\s*', next_line) + if m: + next_line_indent = m.span()[1] + self.source[line_index + 1] = '{}{} {}'.format( + next_line[:next_line_indent], target_operator, + next_line[next_line_indent:]) + + def fix_w605(self, result): + (line_index, offset, target) = get_index_offset_contents(result, + self.source) + self.source[line_index] = '{}\\{}'.format( + target[:offset + 1], target[offset + 1:]) + + +def get_module_imports_on_top_of_file(source, import_line_index): + """return import or from keyword position + + example: + > 0: import sys + 1: import os + 2: + 3: def function(): + """ + def is_string_literal(line): + if line[0] in 'uUbB': + line = line[1:] + if line and line[0] in 'rR': + line = line[1:] + return line and (line[0] == '"' or line[0] == "'") + + def is_future_import(line): + nodes = ast.parse(line) + for n in nodes.body: + if isinstance(n, ast.ImportFrom) and n.module == '__future__': + return True + return False + + def has_future_import(source): + offset = 0 + line = '' + for _, next_line in source: + for line_part in next_line.strip().splitlines(True): + line = line + line_part + try: + return is_future_import(line), offset + except SyntaxError: + continue + offset += 1 + return False, offset + + allowed_try_keywords = ('try', 'except', 'else', 'finally') + in_docstring = False + docstring_kind = '"""' + source_stream = iter(enumerate(source)) + for cnt, line in source_stream: + if not in_docstring: + m = DOCSTRING_START_REGEX.match(line.lstrip()) + if m is not None: + in_docstring = True + docstring_kind = m.group('kind') + remain = line[m.end(): m.endpos].rstrip() + if remain[-3:] == docstring_kind: # one line doc + in_docstring = False + continue + if in_docstring: + if line.rstrip()[-3:] == docstring_kind: + in_docstring = False + continue + + if not line.rstrip(): + continue + elif line.startswith('#'): + continue + + if line.startswith('import '): + if cnt == import_line_index: + continue + return cnt + elif line.startswith('from '): + if cnt == import_line_index: + continue + hit, offset = has_future_import( + itertools.chain([(cnt, line)], source_stream) + ) + if hit: + # move to the back + return cnt + offset + 1 + return cnt + elif pycodestyle.DUNDER_REGEX.match(line): + return cnt + elif any(line.startswith(kw) for kw in allowed_try_keywords): + continue + elif is_string_literal(line): + return cnt + else: + return cnt + return 0 + + +def get_index_offset_contents(result, source): + """Return (line_index, column_offset, line_contents).""" + line_index = result['line'] - 1 + return (line_index, + result['column'] - 1, + source[line_index]) + + +def get_fixed_long_line(target, previous_line, original, + indent_word=' ', max_line_length=79, + aggressive=False, experimental=False, verbose=False): + """Break up long line and return result. + + Do this by generating multiple reformatted candidates and then + ranking the candidates to heuristically select the best option. + + """ + indent = _get_indentation(target) + source = target[len(indent):] + assert source.lstrip() == source + assert not target.lstrip().startswith('#') + + # Check for partial multiline. + tokens = list(generate_tokens(source)) + + candidates = shorten_line( + tokens, source, indent, + indent_word, + max_line_length, + aggressive=aggressive, + experimental=experimental, + previous_line=previous_line) + + # Also sort alphabetically as a tie breaker (for determinism). + candidates = sorted( + sorted(set(candidates).union([target, original])), + key=lambda x: line_shortening_rank( + x, + indent_word, + max_line_length, + experimental=experimental)) + + if verbose >= 4: + print(('-' * 79 + '\n').join([''] + candidates + ['']), + file=wrap_output(sys.stderr, 'utf-8')) + + if candidates: + best_candidate = candidates[0] + + # Don't allow things to get longer. + if longest_line_length(best_candidate) > longest_line_length(original): + return None + + return best_candidate + + +def longest_line_length(code): + """Return length of longest line.""" + if len(code) == 0: + return 0 + return max(len(line) for line in code.splitlines()) + + +def join_logical_line(logical_line): + """Return single line based on logical line input.""" + indentation = _get_indentation(logical_line) + + return indentation + untokenize_without_newlines( + generate_tokens(logical_line.lstrip())) + '\n' + + +def untokenize_without_newlines(tokens): + """Return source code based on tokens.""" + text = '' + last_row = 0 + last_column = -1 + + for t in tokens: + token_string = t[1] + (start_row, start_column) = t[2] + (end_row, end_column) = t[3] + + if start_row > last_row: + last_column = 0 + if ( + (start_column > last_column or token_string == '\n') and + not text.endswith(' ') + ): + text += ' ' + + if token_string != '\n': + text += token_string + + last_row = end_row + last_column = end_column + + return text.rstrip() + + +def _find_logical(source_lines): + # Make a variable which is the index of all the starts of lines. + logical_start = [] + logical_end = [] + last_newline = True + parens = 0 + for t in generate_tokens(''.join(source_lines)): + if t[0] in [tokenize.COMMENT, tokenize.DEDENT, + tokenize.INDENT, tokenize.NL, + tokenize.ENDMARKER]: + continue + if not parens and t[0] in [tokenize.NEWLINE, tokenize.SEMI]: + last_newline = True + logical_end.append((t[3][0] - 1, t[2][1])) + continue + if last_newline and not parens: + logical_start.append((t[2][0] - 1, t[2][1])) + last_newline = False + if t[0] == tokenize.OP: + if t[1] in '([{': + parens += 1 + elif t[1] in '}])': + parens -= 1 + return (logical_start, logical_end) + + +def _get_logical(source_lines, result, logical_start, logical_end): + """Return the logical line corresponding to the result. + + Assumes input is already E702-clean. + + """ + row = result['line'] - 1 + col = result['column'] - 1 + ls = None + le = None + for i in range(0, len(logical_start), 1): + assert logical_end + x = logical_end[i] + if x[0] > row or (x[0] == row and x[1] > col): + le = x + ls = logical_start[i] + break + if ls is None: + return None + original = source_lines[ls[0]:le[0] + 1] + return ls, le, original + + +def get_item(items, index, default=None): + if 0 <= index < len(items): + return items[index] + + return default + + +def reindent(source, indent_size, leave_tabs=False): + """Reindent all lines.""" + reindenter = Reindenter(source, leave_tabs) + return reindenter.run(indent_size) + + +def code_almost_equal(a, b): + """Return True if code is similar. + + Ignore whitespace when comparing specific line. + + """ + split_a = split_and_strip_non_empty_lines(a) + split_b = split_and_strip_non_empty_lines(b) + + if len(split_a) != len(split_b): + return False + + for (index, _) in enumerate(split_a): + if ''.join(split_a[index].split()) != ''.join(split_b[index].split()): + return False + + return True + + +def split_and_strip_non_empty_lines(text): + """Return lines split by newline. + + Ignore empty lines. + + """ + return [line.strip() for line in text.splitlines() if line.strip()] + + +def refactor(source, fixer_names, ignore=None, filename=''): + """Return refactored code using lib2to3. + + Skip if ignore string is produced in the refactored code. + + """ + not_found_end_of_file_newline = source and source.rstrip("\r\n") == source + if not_found_end_of_file_newline: + input_source = source + "\n" + else: + input_source = source + + from lib2to3 import pgen2 + try: + new_text = refactor_with_2to3(input_source, + fixer_names=fixer_names, + filename=filename) + except (pgen2.parse.ParseError, + SyntaxError, + UnicodeDecodeError, + UnicodeEncodeError): + return source + + if ignore: + if ignore in new_text and ignore not in source: + return source + + if not_found_end_of_file_newline: + return new_text.rstrip("\r\n") + + return new_text + + +def code_to_2to3(select, ignore, where='', verbose=False): + fixes = set() + for code, fix in CODE_TO_2TO3.items(): + if code_match(code, select=select, ignore=ignore): + if verbose: + print('---> Applying {} fix for {}'.format(where, + code.upper()), + file=sys.stderr) + fixes |= set(fix) + return fixes + + +def fix_2to3(source, + aggressive=True, select=None, ignore=None, filename='', + where='global', verbose=False): + """Fix various deprecated code (via lib2to3).""" + if not aggressive: + return source + + select = select or [] + ignore = ignore or [] + + return refactor(source, + code_to_2to3(select=select, + ignore=ignore, + where=where, + verbose=verbose), + filename=filename) + + +def find_newline(source): + """Return type of newline used in source. + + Input is a list of lines. + + """ + assert not isinstance(source, str) + + counter = collections.defaultdict(int) + for line in source: + if line.endswith(CRLF): + counter[CRLF] += 1 + elif line.endswith(CR): + counter[CR] += 1 + elif line.endswith(LF): + counter[LF] += 1 + + return (sorted(counter, key=counter.get, reverse=True) or [LF])[0] + + +def _get_indentword(source): + """Return indentation type.""" + indent_word = ' ' # Default in case source has no indentation + try: + for t in generate_tokens(source): + if t[0] == token.INDENT: + indent_word = t[1] + break + except (SyntaxError, tokenize.TokenError): + pass + return indent_word + + +def _get_indentation(line): + """Return leading whitespace.""" + if line.strip(): + non_whitespace_index = len(line) - len(line.lstrip()) + return line[:non_whitespace_index] + + return '' + + +def get_diff_text(old, new, filename): + """Return text of unified diff between old and new.""" + newline = '\n' + diff = difflib.unified_diff( + old, new, + 'original/' + filename, + 'fixed/' + filename, + lineterm=newline) + + text = '' + for line in diff: + text += line + + # Work around missing newline (http://bugs.python.org/issue2142). + if text and not line.endswith(newline): + text += newline + r'\ No newline at end of file' + newline + + return text + + +def _priority_key(pep8_result): + """Key for sorting PEP8 results. + + Global fixes should be done first. This is important for things like + indentation. + + """ + priority = [ + # Fix multiline colon-based before semicolon based. + 'e701', + # Break multiline statements early. + 'e702', + # Things that make lines longer. + 'e225', 'e231', + # Remove extraneous whitespace before breaking lines. + 'e201', + # Shorten whitespace in comment before resorting to wrapping. + 'e262' + ] + middle_index = 10000 + lowest_priority = [ + # We need to shorten lines last since the logical fixer can get in a + # loop, which causes us to exit early. + 'e501', + ] + key = pep8_result['id'].lower() + try: + return priority.index(key) + except ValueError: + try: + return middle_index + lowest_priority.index(key) + 1 + except ValueError: + return middle_index + + +def shorten_line(tokens, source, indentation, indent_word, max_line_length, + aggressive=False, experimental=False, previous_line=''): + """Separate line at OPERATOR. + + Multiple candidates will be yielded. + + """ + for candidate in _shorten_line(tokens=tokens, + source=source, + indentation=indentation, + indent_word=indent_word, + aggressive=aggressive, + previous_line=previous_line): + yield candidate + + if aggressive: + for key_token_strings in SHORTEN_OPERATOR_GROUPS: + shortened = _shorten_line_at_tokens( + tokens=tokens, + source=source, + indentation=indentation, + indent_word=indent_word, + key_token_strings=key_token_strings, + aggressive=aggressive) + + if shortened is not None and shortened != source: + yield shortened + + if experimental: + for shortened in _shorten_line_at_tokens_new( + tokens=tokens, + source=source, + indentation=indentation, + max_line_length=max_line_length): + + yield shortened + + +def _shorten_line(tokens, source, indentation, indent_word, + aggressive=False, previous_line=''): + """Separate line at OPERATOR. + + The input is expected to be free of newlines except for inside multiline + strings and at the end. + + Multiple candidates will be yielded. + + """ + for (token_type, + token_string, + start_offset, + end_offset) in token_offsets(tokens): + + if ( + token_type == tokenize.COMMENT and + not is_probably_part_of_multiline(previous_line) and + not is_probably_part_of_multiline(source) and + not source[start_offset + 1:].strip().lower().startswith( + ('noqa', 'pragma:', 'pylint:')) + ): + # Move inline comments to previous line. + first = source[:start_offset] + second = source[start_offset:] + yield (indentation + second.strip() + '\n' + + indentation + first.strip() + '\n') + elif token_type == token.OP and token_string != '=': + # Don't break on '=' after keyword as this violates PEP 8. + + assert token_type != token.INDENT + + first = source[:end_offset] + + second_indent = indentation + if (first.rstrip().endswith('(') and + source[end_offset:].lstrip().startswith(')')): + pass + elif first.rstrip().endswith('('): + second_indent += indent_word + elif '(' in first: + second_indent += ' ' * (1 + first.find('(')) + else: + second_indent += indent_word + + second = (second_indent + source[end_offset:].lstrip()) + if ( + not second.strip() or + second.lstrip().startswith('#') + ): + continue + + # Do not begin a line with a comma + if second.lstrip().startswith(','): + continue + # Do end a line with a dot + if first.rstrip().endswith('.'): + continue + if token_string in '+-*/': + fixed = first + ' \\' + '\n' + second + else: + fixed = first + '\n' + second + + # Only fix if syntax is okay. + if check_syntax(normalize_multiline(fixed) + if aggressive else fixed): + yield indentation + fixed + + +def _is_binary_operator(token_type, text): + return ((token_type == tokenize.OP or text in ['and', 'or']) and + text not in '()[]{},:.;@=%~') + + +# A convenient way to handle tokens. +Token = collections.namedtuple('Token', ['token_type', 'token_string', + 'spos', 'epos', 'line']) + + +class ReformattedLines(object): + + """The reflowed lines of atoms. + + Each part of the line is represented as an "atom." They can be moved + around when need be to get the optimal formatting. + + """ + + ########################################################################### + # Private Classes + + class _Indent(object): + + """Represent an indentation in the atom stream.""" + + def __init__(self, indent_amt): + self._indent_amt = indent_amt + + def emit(self): + return ' ' * self._indent_amt + + @property + def size(self): + return self._indent_amt + + class _Space(object): + + """Represent a space in the atom stream.""" + + def emit(self): + return ' ' + + @property + def size(self): + return 1 + + class _LineBreak(object): + + """Represent a line break in the atom stream.""" + + def emit(self): + return '\n' + + @property + def size(self): + return 0 + + def __init__(self, max_line_length): + self._max_line_length = max_line_length + self._lines = [] + self._bracket_depth = 0 + self._prev_item = None + self._prev_prev_item = None + + def __repr__(self): + return self.emit() + + ########################################################################### + # Public Methods + + def add(self, obj, indent_amt, break_after_open_bracket): + if isinstance(obj, Atom): + self._add_item(obj, indent_amt) + return + + self._add_container(obj, indent_amt, break_after_open_bracket) + + def add_comment(self, item): + num_spaces = 2 + if len(self._lines) > 1: + if isinstance(self._lines[-1], self._Space): + num_spaces -= 1 + if len(self._lines) > 2: + if isinstance(self._lines[-2], self._Space): + num_spaces -= 1 + + while num_spaces > 0: + self._lines.append(self._Space()) + num_spaces -= 1 + self._lines.append(item) + + def add_indent(self, indent_amt): + self._lines.append(self._Indent(indent_amt)) + + def add_line_break(self, indent): + self._lines.append(self._LineBreak()) + self.add_indent(len(indent)) + + def add_line_break_at(self, index, indent_amt): + self._lines.insert(index, self._LineBreak()) + self._lines.insert(index + 1, self._Indent(indent_amt)) + + def add_space_if_needed(self, curr_text, equal=False): + if ( + not self._lines or isinstance( + self._lines[-1], (self._LineBreak, self._Indent, self._Space)) + ): + return + + prev_text = str(self._prev_item) + prev_prev_text = ( + str(self._prev_prev_item) if self._prev_prev_item else '') + + if ( + # The previous item was a keyword or identifier and the current + # item isn't an operator that doesn't require a space. + ((self._prev_item.is_keyword or self._prev_item.is_string or + self._prev_item.is_name or self._prev_item.is_number) and + (curr_text[0] not in '([{.,:}])' or + (curr_text[0] == '=' and equal))) or + + # Don't place spaces around a '.', unless it's in an 'import' + # statement. + ((prev_prev_text != 'from' and prev_text[-1] != '.' and + curr_text != 'import') and + + # Don't place a space before a colon. + curr_text[0] != ':' and + + # Don't split up ending brackets by spaces. + ((prev_text[-1] in '}])' and curr_text[0] not in '.,}])') or + + # Put a space after a colon or comma. + prev_text[-1] in ':,' or + + # Put space around '=' if asked to. + (equal and prev_text == '=') or + + # Put spaces around non-unary arithmetic operators. + ((self._prev_prev_item and + (prev_text not in '+-' and + (self._prev_prev_item.is_name or + self._prev_prev_item.is_number or + self._prev_prev_item.is_string)) and + prev_text in ('+', '-', '%', '*', '/', '//', '**', 'in'))))) + ): + self._lines.append(self._Space()) + + def previous_item(self): + """Return the previous non-whitespace item.""" + return self._prev_item + + def fits_on_current_line(self, item_extent): + return self.current_size() + item_extent <= self._max_line_length + + def current_size(self): + """The size of the current line minus the indentation.""" + size = 0 + for item in reversed(self._lines): + size += item.size + if isinstance(item, self._LineBreak): + break + + return size + + def line_empty(self): + return (self._lines and + isinstance(self._lines[-1], + (self._LineBreak, self._Indent))) + + def emit(self): + string = '' + for item in self._lines: + if isinstance(item, self._LineBreak): + string = string.rstrip() + string += item.emit() + + return string.rstrip() + '\n' + + ########################################################################### + # Private Methods + + def _add_item(self, item, indent_amt): + """Add an item to the line. + + Reflow the line to get the best formatting after the item is + inserted. The bracket depth indicates if the item is being + inserted inside of a container or not. + + """ + if self._prev_item and self._prev_item.is_string and item.is_string: + # Place consecutive string literals on separate lines. + self._lines.append(self._LineBreak()) + self._lines.append(self._Indent(indent_amt)) + + item_text = str(item) + if self._lines and self._bracket_depth: + # Adding the item into a container. + self._prevent_default_initializer_splitting(item, indent_amt) + + if item_text in '.,)]}': + self._split_after_delimiter(item, indent_amt) + + elif self._lines and not self.line_empty(): + # Adding the item outside of a container. + if self.fits_on_current_line(len(item_text)): + self._enforce_space(item) + + else: + # Line break for the new item. + self._lines.append(self._LineBreak()) + self._lines.append(self._Indent(indent_amt)) + + self._lines.append(item) + self._prev_item, self._prev_prev_item = item, self._prev_item + + if item_text in '([{': + self._bracket_depth += 1 + + elif item_text in '}])': + self._bracket_depth -= 1 + assert self._bracket_depth >= 0 + + def _add_container(self, container, indent_amt, break_after_open_bracket): + actual_indent = indent_amt + 1 + + if ( + str(self._prev_item) != '=' and + not self.line_empty() and + not self.fits_on_current_line( + container.size + self._bracket_depth + 2) + ): + + if str(container)[0] == '(' and self._prev_item.is_name: + # Don't split before the opening bracket of a call. + break_after_open_bracket = True + actual_indent = indent_amt + 4 + elif ( + break_after_open_bracket or + str(self._prev_item) not in '([{' + ): + # If the container doesn't fit on the current line and the + # current line isn't empty, place the container on the next + # line. + self._lines.append(self._LineBreak()) + self._lines.append(self._Indent(indent_amt)) + break_after_open_bracket = False + else: + actual_indent = self.current_size() + 1 + break_after_open_bracket = False + + if isinstance(container, (ListComprehension, IfExpression)): + actual_indent = indent_amt + + # Increase the continued indentation only if recursing on a + # container. + container.reflow(self, ' ' * actual_indent, + break_after_open_bracket=break_after_open_bracket) + + def _prevent_default_initializer_splitting(self, item, indent_amt): + """Prevent splitting between a default initializer. + + When there is a default initializer, it's best to keep it all on + the same line. It's nicer and more readable, even if it goes + over the maximum allowable line length. This goes back along the + current line to determine if we have a default initializer, and, + if so, to remove extraneous whitespaces and add a line + break/indent before it if needed. + + """ + if str(item) == '=': + # This is the assignment in the initializer. Just remove spaces for + # now. + self._delete_whitespace() + return + + if (not self._prev_item or not self._prev_prev_item or + str(self._prev_item) != '='): + return + + self._delete_whitespace() + prev_prev_index = self._lines.index(self._prev_prev_item) + + if ( + isinstance(self._lines[prev_prev_index - 1], self._Indent) or + self.fits_on_current_line(item.size + 1) + ): + # The default initializer is already the only item on this line. + # Don't insert a newline here. + return + + # Replace the space with a newline/indent combo. + if isinstance(self._lines[prev_prev_index - 1], self._Space): + del self._lines[prev_prev_index - 1] + + self.add_line_break_at(self._lines.index(self._prev_prev_item), + indent_amt) + + def _split_after_delimiter(self, item, indent_amt): + """Split the line only after a delimiter.""" + self._delete_whitespace() + + if self.fits_on_current_line(item.size): + return + + last_space = None + for current_item in reversed(self._lines): + if ( + last_space and + (not isinstance(current_item, Atom) or + not current_item.is_colon) + ): + break + else: + last_space = None + if isinstance(current_item, self._Space): + last_space = current_item + if isinstance(current_item, (self._LineBreak, self._Indent)): + return + + if not last_space: + return + + self.add_line_break_at(self._lines.index(last_space), indent_amt) + + def _enforce_space(self, item): + """Enforce a space in certain situations. + + There are cases where we will want a space where normally we + wouldn't put one. This just enforces the addition of a space. + + """ + if isinstance(self._lines[-1], + (self._Space, self._LineBreak, self._Indent)): + return + + if not self._prev_item: + return + + item_text = str(item) + prev_text = str(self._prev_item) + + # Prefer a space around a '.' in an import statement, and between the + # 'import' and '('. + if ( + (item_text == '.' and prev_text == 'from') or + (item_text == 'import' and prev_text == '.') or + (item_text == '(' and prev_text == 'import') + ): + self._lines.append(self._Space()) + + def _delete_whitespace(self): + """Delete all whitespace from the end of the line.""" + while isinstance(self._lines[-1], (self._Space, self._LineBreak, + self._Indent)): + del self._lines[-1] + + +class Atom(object): + + """The smallest unbreakable unit that can be reflowed.""" + + def __init__(self, atom): + self._atom = atom + + def __repr__(self): + return self._atom.token_string + + def __len__(self): + return self.size + + def reflow( + self, reflowed_lines, continued_indent, extent, + break_after_open_bracket=False, + is_list_comp_or_if_expr=False, + next_is_dot=False + ): + if self._atom.token_type == tokenize.COMMENT: + reflowed_lines.add_comment(self) + return + + total_size = extent if extent else self.size + + if self._atom.token_string not in ',:([{}])': + # Some atoms will need an extra 1-sized space token after them. + total_size += 1 + + prev_item = reflowed_lines.previous_item() + if ( + not is_list_comp_or_if_expr and + not reflowed_lines.fits_on_current_line(total_size) and + not (next_is_dot and + reflowed_lines.fits_on_current_line(self.size + 1)) and + not reflowed_lines.line_empty() and + not self.is_colon and + not (prev_item and prev_item.is_name and + str(self) == '(') + ): + # Start a new line if there is already something on the line and + # adding this atom would make it go over the max line length. + reflowed_lines.add_line_break(continued_indent) + else: + reflowed_lines.add_space_if_needed(str(self)) + + reflowed_lines.add(self, len(continued_indent), + break_after_open_bracket) + + def emit(self): + return self.__repr__() + + @property + def is_keyword(self): + return keyword.iskeyword(self._atom.token_string) + + @property + def is_string(self): + return self._atom.token_type == tokenize.STRING + + @property + def is_name(self): + return self._atom.token_type == tokenize.NAME + + @property + def is_number(self): + return self._atom.token_type == tokenize.NUMBER + + @property + def is_comma(self): + return self._atom.token_string == ',' + + @property + def is_colon(self): + return self._atom.token_string == ':' + + @property + def size(self): + return len(self._atom.token_string) + + +class Container(object): + + """Base class for all container types.""" + + def __init__(self, items): + self._items = items + + def __repr__(self): + string = '' + last_was_keyword = False + + for item in self._items: + if item.is_comma: + string += ', ' + elif item.is_colon: + string += ': ' + else: + item_string = str(item) + if ( + string and + (last_was_keyword or + (not string.endswith(tuple('([{,.:}]) ')) and + not item_string.startswith(tuple('([{,.:}])')))) + ): + string += ' ' + string += item_string + + last_was_keyword = item.is_keyword + return string + + def __iter__(self): + for element in self._items: + yield element + + def __getitem__(self, idx): + return self._items[idx] + + def reflow(self, reflowed_lines, continued_indent, + break_after_open_bracket=False): + last_was_container = False + for (index, item) in enumerate(self._items): + next_item = get_item(self._items, index + 1) + + if isinstance(item, Atom): + is_list_comp_or_if_expr = ( + isinstance(self, (ListComprehension, IfExpression))) + item.reflow(reflowed_lines, continued_indent, + self._get_extent(index), + is_list_comp_or_if_expr=is_list_comp_or_if_expr, + next_is_dot=(next_item and + str(next_item) == '.')) + if last_was_container and item.is_comma: + reflowed_lines.add_line_break(continued_indent) + last_was_container = False + else: # isinstance(item, Container) + reflowed_lines.add(item, len(continued_indent), + break_after_open_bracket) + last_was_container = not isinstance(item, (ListComprehension, + IfExpression)) + + if ( + break_after_open_bracket and index == 0 and + # Prefer to keep empty containers together instead of + # separating them. + str(item) == self.open_bracket and + (not next_item or str(next_item) != self.close_bracket) and + (len(self._items) != 3 or not isinstance(next_item, Atom)) + ): + reflowed_lines.add_line_break(continued_indent) + break_after_open_bracket = False + else: + next_next_item = get_item(self._items, index + 2) + if ( + str(item) not in ['.', '%', 'in'] and + next_item and not isinstance(next_item, Container) and + str(next_item) != ':' and + next_next_item and (not isinstance(next_next_item, Atom) or + str(next_item) == 'not') and + not reflowed_lines.line_empty() and + not reflowed_lines.fits_on_current_line( + self._get_extent(index + 1) + 2) + ): + reflowed_lines.add_line_break(continued_indent) + + def _get_extent(self, index): + """The extent of the full element. + + E.g., the length of a function call or keyword. + + """ + extent = 0 + prev_item = get_item(self._items, index - 1) + seen_dot = prev_item and str(prev_item) == '.' + while index < len(self._items): + item = get_item(self._items, index) + index += 1 + + if isinstance(item, (ListComprehension, IfExpression)): + break + + if isinstance(item, Container): + if prev_item and prev_item.is_name: + if seen_dot: + extent += 1 + else: + extent += item.size + + prev_item = item + continue + elif (str(item) not in ['.', '=', ':', 'not'] and + not item.is_name and not item.is_string): + break + + if str(item) == '.': + seen_dot = True + + extent += item.size + prev_item = item + + return extent + + @property + def is_string(self): + return False + + @property + def size(self): + return len(self.__repr__()) + + @property + def is_keyword(self): + return False + + @property + def is_name(self): + return False + + @property + def is_comma(self): + return False + + @property + def is_colon(self): + return False + + @property + def open_bracket(self): + return None + + @property + def close_bracket(self): + return None + + +class Tuple(Container): + + """A high-level representation of a tuple.""" + + @property + def open_bracket(self): + return '(' + + @property + def close_bracket(self): + return ')' + + +class List(Container): + + """A high-level representation of a list.""" + + @property + def open_bracket(self): + return '[' + + @property + def close_bracket(self): + return ']' + + +class DictOrSet(Container): + + """A high-level representation of a dictionary or set.""" + + @property + def open_bracket(self): + return '{' + + @property + def close_bracket(self): + return '}' + + +class ListComprehension(Container): + + """A high-level representation of a list comprehension.""" + + @property + def size(self): + length = 0 + for item in self._items: + if isinstance(item, IfExpression): + break + length += item.size + return length + + +class IfExpression(Container): + + """A high-level representation of an if-expression.""" + + +def _parse_container(tokens, index, for_or_if=None): + """Parse a high-level container, such as a list, tuple, etc.""" + + # Store the opening bracket. + items = [Atom(Token(*tokens[index]))] + index += 1 + + num_tokens = len(tokens) + while index < num_tokens: + tok = Token(*tokens[index]) + + if tok.token_string in ',)]}': + # First check if we're at the end of a list comprehension or + # if-expression. Don't add the ending token as part of the list + # comprehension or if-expression, because they aren't part of those + # constructs. + if for_or_if == 'for': + return (ListComprehension(items), index - 1) + + elif for_or_if == 'if': + return (IfExpression(items), index - 1) + + # We've reached the end of a container. + items.append(Atom(tok)) + + # If not, then we are at the end of a container. + if tok.token_string == ')': + # The end of a tuple. + return (Tuple(items), index) + + elif tok.token_string == ']': + # The end of a list. + return (List(items), index) + + elif tok.token_string == '}': + # The end of a dictionary or set. + return (DictOrSet(items), index) + + elif tok.token_string in '([{': + # A sub-container is being defined. + (container, index) = _parse_container(tokens, index) + items.append(container) + + elif tok.token_string == 'for': + (container, index) = _parse_container(tokens, index, 'for') + items.append(container) + + elif tok.token_string == 'if': + (container, index) = _parse_container(tokens, index, 'if') + items.append(container) + + else: + items.append(Atom(tok)) + + index += 1 + + return (None, None) + + +def _parse_tokens(tokens): + """Parse the tokens. + + This converts the tokens into a form where we can manipulate them + more easily. + + """ + + index = 0 + parsed_tokens = [] + + num_tokens = len(tokens) + while index < num_tokens: + tok = Token(*tokens[index]) + + assert tok.token_type != token.INDENT + if tok.token_type == tokenize.NEWLINE: + # There's only one newline and it's at the end. + break + + if tok.token_string in '([{': + (container, index) = _parse_container(tokens, index) + if not container: + return None + parsed_tokens.append(container) + else: + parsed_tokens.append(Atom(tok)) + + index += 1 + + return parsed_tokens + + +def _reflow_lines(parsed_tokens, indentation, max_line_length, + start_on_prefix_line): + """Reflow the lines so that it looks nice.""" + + if str(parsed_tokens[0]) == 'def': + # A function definition gets indented a bit more. + continued_indent = indentation + ' ' * 2 * DEFAULT_INDENT_SIZE + else: + continued_indent = indentation + ' ' * DEFAULT_INDENT_SIZE + + break_after_open_bracket = not start_on_prefix_line + + lines = ReformattedLines(max_line_length) + lines.add_indent(len(indentation.lstrip('\r\n'))) + + if not start_on_prefix_line: + # If splitting after the opening bracket will cause the first element + # to be aligned weirdly, don't try it. + first_token = get_item(parsed_tokens, 0) + second_token = get_item(parsed_tokens, 1) + + if ( + first_token and second_token and + str(second_token)[0] == '(' and + len(indentation) + len(first_token) + 1 == len(continued_indent) + ): + return None + + for item in parsed_tokens: + lines.add_space_if_needed(str(item), equal=True) + + save_continued_indent = continued_indent + if start_on_prefix_line and isinstance(item, Container): + start_on_prefix_line = False + continued_indent = ' ' * (lines.current_size() + 1) + + item.reflow(lines, continued_indent, break_after_open_bracket) + continued_indent = save_continued_indent + + return lines.emit() + + +def _shorten_line_at_tokens_new(tokens, source, indentation, + max_line_length): + """Shorten the line taking its length into account. + + The input is expected to be free of newlines except for inside + multiline strings and at the end. + + """ + # Yield the original source so to see if it's a better choice than the + # shortened candidate lines we generate here. + yield indentation + source + + parsed_tokens = _parse_tokens(tokens) + + if parsed_tokens: + # Perform two reflows. The first one starts on the same line as the + # prefix. The second starts on the line after the prefix. + fixed = _reflow_lines(parsed_tokens, indentation, max_line_length, + start_on_prefix_line=True) + if fixed and check_syntax(normalize_multiline(fixed.lstrip())): + yield fixed + + fixed = _reflow_lines(parsed_tokens, indentation, max_line_length, + start_on_prefix_line=False) + if fixed and check_syntax(normalize_multiline(fixed.lstrip())): + yield fixed + + +def _shorten_line_at_tokens(tokens, source, indentation, indent_word, + key_token_strings, aggressive): + """Separate line by breaking at tokens in key_token_strings. + + The input is expected to be free of newlines except for inside + multiline strings and at the end. + + """ + offsets = [] + for (index, _t) in enumerate(token_offsets(tokens)): + (token_type, + token_string, + start_offset, + end_offset) = _t + + assert token_type != token.INDENT + + if token_string in key_token_strings: + # Do not break in containers with zero or one items. + unwanted_next_token = { + '(': ')', + '[': ']', + '{': '}'}.get(token_string) + if unwanted_next_token: + if ( + get_item(tokens, + index + 1, + default=[None, None])[1] == unwanted_next_token or + get_item(tokens, + index + 2, + default=[None, None])[1] == unwanted_next_token + ): + continue + + if ( + index > 2 and token_string == '(' and + tokens[index - 1][1] in ',(%[' + ): + # Don't split after a tuple start, or before a tuple start if + # the tuple is in a list. + continue + + if end_offset < len(source) - 1: + # Don't split right before newline. + offsets.append(end_offset) + else: + # Break at adjacent strings. These were probably meant to be on + # separate lines in the first place. + previous_token = get_item(tokens, index - 1) + if ( + token_type == tokenize.STRING and + previous_token and previous_token[0] == tokenize.STRING + ): + offsets.append(start_offset) + + current_indent = None + fixed = None + for line in split_at_offsets(source, offsets): + if fixed: + fixed += '\n' + current_indent + line + + for symbol in '([{': + if line.endswith(symbol): + current_indent += indent_word + else: + # First line. + fixed = line + assert not current_indent + current_indent = indent_word + + assert fixed is not None + + if check_syntax(normalize_multiline(fixed) + if aggressive > 1 else fixed): + return indentation + fixed + + return None + + +def token_offsets(tokens): + """Yield tokens and offsets.""" + end_offset = 0 + previous_end_row = 0 + previous_end_column = 0 + for t in tokens: + token_type = t[0] + token_string = t[1] + (start_row, start_column) = t[2] + (end_row, end_column) = t[3] + + # Account for the whitespace between tokens. + end_offset += start_column + if previous_end_row == start_row: + end_offset -= previous_end_column + + # Record the start offset of the token. + start_offset = end_offset + + # Account for the length of the token itself. + end_offset += len(token_string) + + yield (token_type, + token_string, + start_offset, + end_offset) + + previous_end_row = end_row + previous_end_column = end_column + + +def normalize_multiline(line): + """Normalize multiline-related code that will cause syntax error. + + This is for purposes of checking syntax. + + """ + if line.startswith('def ') and line.rstrip().endswith(':'): + return line + ' pass' + elif line.startswith('return '): + return 'def _(): ' + line + elif line.startswith('@'): + return line + 'def _(): pass' + elif line.startswith('class '): + return line + ' pass' + elif line.startswith(('if ', 'elif ', 'for ', 'while ')): + return line + ' pass' + + return line + + +def fix_whitespace(line, offset, replacement): + """Replace whitespace at offset and return fixed line.""" + # Replace escaped newlines too + left = line[:offset].rstrip('\n\r \t\\') + right = line[offset:].lstrip('\n\r \t\\') + if right.startswith('#'): + return line + + return left + replacement + right + + +def _execute_pep8(pep8_options, source): + """Execute pycodestyle via python method calls.""" + class QuietReport(pycodestyle.BaseReport): + + """Version of checker that does not print.""" + + def __init__(self, options): + super(QuietReport, self).__init__(options) + self.__full_error_results = [] + + def error(self, line_number, offset, text, check): + """Collect errors.""" + code = super(QuietReport, self).error(line_number, + offset, + text, + check) + if code: + self.__full_error_results.append( + {'id': code, + 'line': line_number, + 'column': offset + 1, + 'info': text}) + + def full_error_results(self): + """Return error results in detail. + + Results are in the form of a list of dictionaries. Each + dictionary contains 'id', 'line', 'column', and 'info'. + + """ + return self.__full_error_results + + checker = pycodestyle.Checker('', lines=source, reporter=QuietReport, + **pep8_options) + checker.check_all() + return checker.report.full_error_results() + + +def _remove_leading_and_normalize(line, with_rstrip=True): + # ignore FF in first lstrip() + if with_rstrip: + return line.lstrip(' \t\v').rstrip(CR + LF) + '\n' + return line.lstrip(' \t\v') + + +class Reindenter(object): + + """Reindents badly-indented code to uniformly use four-space indentation. + + Released to the public domain, by Tim Peters, 03 October 2000. + + """ + + def __init__(self, input_text, leave_tabs=False): + sio = io.StringIO(input_text) + source_lines = sio.readlines() + + self.string_content_line_numbers = multiline_string_lines(input_text) + + # File lines, rstripped & tab-expanded. Dummy at start is so + # that we can use tokenize's 1-based line numbering easily. + # Note that a line is all-blank iff it is a newline. + self.lines = [] + for line_number, line in enumerate(source_lines, start=1): + # Do not modify if inside a multiline string. + if line_number in self.string_content_line_numbers: + self.lines.append(line) + else: + # Only expand leading tabs. + with_rstrip = line_number != len(source_lines) + if leave_tabs: + self.lines.append( + _get_indentation(line) + + _remove_leading_and_normalize(line, with_rstrip) + ) + else: + self.lines.append( + _get_indentation(line).expandtabs() + + _remove_leading_and_normalize(line, with_rstrip) + ) + + self.lines.insert(0, None) + self.index = 1 # index into self.lines of next line + self.input_text = input_text + + def run(self, indent_size=DEFAULT_INDENT_SIZE): + """Fix indentation and return modified line numbers. + + Line numbers are indexed at 1. + + """ + if indent_size < 1: + return self.input_text + + try: + stats = _reindent_stats(tokenize.generate_tokens(self.getline)) + except (SyntaxError, tokenize.TokenError): + return self.input_text + # Remove trailing empty lines. + lines = self.lines + # Sentinel. + stats.append((len(lines), 0)) + # Map count of leading spaces to # we want. + have2want = {} + # Program after transformation. + after = [] + # Copy over initial empty lines -- there's nothing to do until + # we see a line with *something* on it. + i = stats[0][0] + after.extend(lines[1:i]) + for i in range(len(stats) - 1): + thisstmt, thislevel = stats[i] + nextstmt = stats[i + 1][0] + have = _leading_space_count(lines[thisstmt]) + want = thislevel * indent_size + if want < 0: + # A comment line. + if have: + # An indented comment line. If we saw the same + # indentation before, reuse what it most recently + # mapped to. + want = have2want.get(have, -1) + if want < 0: + # Then it probably belongs to the next real stmt. + for j in range(i + 1, len(stats) - 1): + jline, jlevel = stats[j] + if jlevel >= 0: + if have == _leading_space_count(lines[jline]): + want = jlevel * indent_size + break + # Maybe it's a hanging comment like this one, + if want < 0: + # in which case we should shift it like its base + # line got shifted. + for j in range(i - 1, -1, -1): + jline, jlevel = stats[j] + if jlevel >= 0: + want = (have + _leading_space_count( + after[jline - 1]) - + _leading_space_count(lines[jline])) + break + if want < 0: + # Still no luck -- leave it alone. + want = have + else: + want = 0 + assert want >= 0 + have2want[have] = want + diff = want - have + if diff == 0 or have == 0: + after.extend(lines[thisstmt:nextstmt]) + else: + for line_number, line in enumerate(lines[thisstmt:nextstmt], + start=thisstmt): + if line_number in self.string_content_line_numbers: + after.append(line) + elif diff > 0: + if line == '\n': + after.append(line) + else: + after.append(' ' * diff + line) + else: + remove = min(_leading_space_count(line), -diff) + after.append(line[remove:]) + + return ''.join(after) + + def getline(self): + """Line-getter for tokenize.""" + if self.index >= len(self.lines): + line = '' + else: + line = self.lines[self.index] + self.index += 1 + return line + + +def _reindent_stats(tokens): + """Return list of (lineno, indentlevel) pairs. + + One for each stmt and comment line. indentlevel is -1 for comment + lines, as a signal that tokenize doesn't know what to do about them; + indeed, they're our headache! + + """ + find_stmt = 1 # Next token begins a fresh stmt? + level = 0 # Current indent level. + stats = [] + + for t in tokens: + token_type = t[0] + sline = t[2][0] + line = t[4] + + if token_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+)? + find_stmt = 1 + + elif token_type == tokenize.INDENT: + find_stmt = 1 + level += 1 + + elif token_type == tokenize.DEDENT: + find_stmt = 1 + level -= 1 + + elif token_type == tokenize.COMMENT: + if find_stmt: + stats.append((sline, -1)) + # But we're still looking for a new stmt, so leave + # find_stmt alone. + + elif token_type == tokenize.NL: + pass + + elif find_stmt: + # This is the first "real token" following a NEWLINE, so it + # must be the first token of the next program statement, or an + # ENDMARKER. + find_stmt = 0 + if line: # Not endmarker. + stats.append((sline, level)) + + return stats + + +def _leading_space_count(line): + """Return number of leading spaces in line.""" + i = 0 + while i < len(line) and line[i] == ' ': + i += 1 + return i + + +def refactor_with_2to3(source_text, fixer_names, filename=''): + """Use lib2to3 to refactor the source. + + Return the refactored source code. + + """ + from lib2to3.refactor import RefactoringTool + fixers = ['lib2to3.fixes.fix_' + name for name in fixer_names] + tool = RefactoringTool(fixer_names=fixers, explicit=fixers) + + from lib2to3.pgen2 import tokenize as lib2to3_tokenize + try: + # The name parameter is necessary particularly for the "import" fixer. + return str(tool.refactor_string(source_text, name=filename)) + except lib2to3_tokenize.TokenError: + return source_text + + +def check_syntax(code): + """Return True if syntax is okay.""" + try: + return compile(code, '', 'exec', dont_inherit=True) + except (SyntaxError, TypeError, ValueError): + return False + + +def find_with_line_numbers(pattern, contents): + """A wrapper around 're.finditer' to find line numbers. + + Returns a list of line numbers where pattern was found in contents. + """ + matches = list(re.finditer(pattern, contents)) + if not matches: + return [] + + end = matches[-1].start() + + # -1 so a failed `rfind` maps to the first line. + newline_offsets = { + -1: 0 + } + for line_num, m in enumerate(re.finditer(r'\n', contents), 1): + offset = m.start() + if offset > end: + break + newline_offsets[offset] = line_num + + def get_line_num(match, contents): + """Get the line number of string in a files contents. + + Failing to find the newline is OK, -1 maps to 0 + + """ + newline_offset = contents.rfind('\n', 0, match.start()) + return newline_offsets[newline_offset] + + return [get_line_num(match, contents) + 1 for match in matches] + + +def get_disabled_ranges(source): + """Returns a list of tuples representing the disabled ranges. + + If disabled and no re-enable will disable for rest of file. + + """ + enable_line_nums = find_with_line_numbers(ENABLE_REGEX, source) + disable_line_nums = find_with_line_numbers(DISABLE_REGEX, source) + total_lines = len(re.findall("\n", source)) + 1 + + enable_commands = {} + for num in enable_line_nums: + enable_commands[num] = True + for num in disable_line_nums: + enable_commands[num] = False + + disabled_ranges = [] + currently_enabled = True + disabled_start = None + + for line, commanded_enabled in sorted(enable_commands.items()): + if commanded_enabled is False and currently_enabled is True: + disabled_start = line + currently_enabled = False + elif commanded_enabled is True and currently_enabled is False: + disabled_ranges.append((disabled_start, line)) + currently_enabled = True + + if currently_enabled is False: + disabled_ranges.append((disabled_start, total_lines)) + + return disabled_ranges + + +def filter_disabled_results(result, disabled_ranges): + """Filter out reports based on tuple of disabled ranges. + + """ + line = result['line'] + for disabled_range in disabled_ranges: + if disabled_range[0] <= line <= disabled_range[1]: + return False + return True + + +def filter_results(source, results, aggressive): + """Filter out spurious reports from pycodestyle. + + If aggressive is True, we allow possibly unsafe fixes (E711, E712). + + """ + non_docstring_string_line_numbers = multiline_string_lines( + source, include_docstrings=False) + all_string_line_numbers = multiline_string_lines( + source, include_docstrings=True) + + commented_out_code_line_numbers = commented_out_code_lines(source) + + # Filter out the disabled ranges + disabled_ranges = get_disabled_ranges(source) + if disabled_ranges: + results = [ + result for result in results if filter_disabled_results( + result, + disabled_ranges, + ) + ] + + has_e901 = any(result['id'].lower() == 'e901' for result in results) + + for r in results: + issue_id = r['id'].lower() + + if r['line'] in non_docstring_string_line_numbers: + if issue_id.startswith(('e1', 'e501', 'w191')): + continue + + if r['line'] in all_string_line_numbers: + if issue_id in ['e501']: + continue + + # We must offset by 1 for lines that contain the trailing contents of + # multiline strings. + if not aggressive and (r['line'] + 1) in all_string_line_numbers: + # Do not modify multiline strings in non-aggressive mode. Remove + # trailing whitespace could break doctests. + if issue_id.startswith(('w29', 'w39')): + continue + + if aggressive <= 0: + if issue_id.startswith(('e711', 'e72', 'w6')): + continue + + if aggressive <= 1: + if issue_id.startswith(('e712', 'e713', 'e714')): + continue + + if aggressive <= 2: + if issue_id.startswith(('e704')): + continue + + if r['line'] in commented_out_code_line_numbers: + if issue_id.startswith(('e261', 'e262', 'e501')): + continue + + # Do not touch indentation if there is a token error caused by + # incomplete multi-line statement. Otherwise, we risk screwing up the + # indentation. + if has_e901: + if issue_id.startswith(('e1', 'e7')): + continue + + yield r + + +def multiline_string_lines(source, include_docstrings=False): + """Return line numbers that are within multiline strings. + + The line numbers are indexed at 1. + + Docstrings are ignored. + + """ + line_numbers = set() + previous_token_type = '' + try: + for t in generate_tokens(source): + token_type = t[0] + start_row = t[2][0] + end_row = t[3][0] + + if token_type == tokenize.STRING and start_row != end_row: + if ( + include_docstrings or + previous_token_type != tokenize.INDENT + ): + # We increment by one since we want the contents of the + # string. + line_numbers |= set(range(1 + start_row, 1 + end_row)) + + previous_token_type = token_type + except (SyntaxError, tokenize.TokenError): + pass + + return line_numbers + + +def commented_out_code_lines(source): + """Return line numbers of comments that are likely code. + + Commented-out code is bad practice, but modifying it just adds even + more clutter. + + """ + line_numbers = [] + try: + for t in generate_tokens(source): + token_type = t[0] + token_string = t[1] + start_row = t[2][0] + line = t[4] + + # Ignore inline comments. + if not line.lstrip().startswith('#'): + continue + + if token_type == tokenize.COMMENT: + stripped_line = token_string.lstrip('#').strip() + with warnings.catch_warnings(): + # ignore SyntaxWarning in Python3.8+ + # refs: + # https://bugs.python.org/issue15248 + # https://docs.python.org/3.8/whatsnew/3.8.html#other-language-changes + warnings.filterwarnings("ignore", category=SyntaxWarning) + if ( + ' ' in stripped_line and + '#' not in stripped_line and + check_syntax(stripped_line) + ): + line_numbers.append(start_row) + except (SyntaxError, tokenize.TokenError): + pass + + return line_numbers + + +def shorten_comment(line, max_line_length, last_comment=False): + """Return trimmed or split long comment line. + + If there are no comments immediately following it, do a text wrap. + Doing this wrapping on all comments in general would lead to jagged + comment text. + + """ + assert len(line) > max_line_length + line = line.rstrip() + + # PEP 8 recommends 72 characters for comment text. + indentation = _get_indentation(line) + '# ' + max_line_length = min(max_line_length, + len(indentation) + 72) + + MIN_CHARACTER_REPEAT = 5 + if ( + len(line) - len(line.rstrip(line[-1])) >= MIN_CHARACTER_REPEAT and + not line[-1].isalnum() + ): + # Trim comments that end with things like --------- + return line[:max_line_length] + '\n' + elif last_comment and re.match(r'\s*#+\s*\w+', line): + split_lines = textwrap.wrap(line.lstrip(' \t#'), + initial_indent=indentation, + subsequent_indent=indentation, + width=max_line_length, + break_long_words=False, + break_on_hyphens=False) + return '\n'.join(split_lines) + '\n' + + return line + '\n' + + +def normalize_line_endings(lines, newline): + """Return fixed line endings. + + All lines will be modified to use the most common line ending. + """ + line = [line.rstrip('\n\r') + newline for line in lines] + if line and lines[-1] == lines[-1].rstrip('\n\r'): + line[-1] = line[-1].rstrip('\n\r') + return line + + +def mutual_startswith(a, b): + return b.startswith(a) or a.startswith(b) + + +def code_match(code, select, ignore): + if ignore: + assert not isinstance(ignore, str) + for ignored_code in [c.strip() for c in ignore]: + if mutual_startswith(code.lower(), ignored_code.lower()): + return False + + if select: + assert not isinstance(select, str) + for selected_code in [c.strip() for c in select]: + if mutual_startswith(code.lower(), selected_code.lower()): + return True + return False + + return True + + +def fix_code(source, options=None, encoding=None, apply_config=False): + """Return fixed source code. + + "encoding" will be used to decode "source" if it is a byte string. + + """ + options = _get_options(options, apply_config) + # normalize + options.ignore = [opt.upper() for opt in options.ignore] + options.select = [opt.upper() for opt in options.select] + + # check ignore args + # NOTE: If W50x is not included, add W50x because the code + # correction result is indefinite. + ignore_opt = options.ignore + if not {"W50", "W503", "W504"} & set(ignore_opt): + options.ignore.append("W50") + + if not isinstance(source, str): + source = source.decode(encoding or get_encoding()) + + sio = io.StringIO(source) + return fix_lines(sio.readlines(), options=options) + + +def _get_options(raw_options, apply_config): + """Return parsed options.""" + if not raw_options: + return parse_args([''], apply_config=apply_config) + + if isinstance(raw_options, dict): + options = parse_args([''], apply_config=apply_config) + for name, value in raw_options.items(): + if not hasattr(options, name): + raise ValueError("No such option '{}'".format(name)) + + # Check for very basic type errors. + expected_type = type(getattr(options, name)) + if not isinstance(expected_type, (str, )): + if isinstance(value, (str, )): + raise ValueError( + "Option '{}' should not be a string".format(name)) + setattr(options, name, value) + else: + options = raw_options + + return options + + +def fix_lines(source_lines, options, filename=''): + """Return fixed source code.""" + # Transform everything to line feed. Then change them back to original + # before returning fixed source code. + original_newline = find_newline(source_lines) + tmp_source = ''.join(normalize_line_endings(source_lines, '\n')) + + # Keep a history to break out of cycles. + previous_hashes = set() + + if options.line_range: + # Disable "apply_local_fixes()" for now due to issue #175. + fixed_source = tmp_source + else: + # Apply global fixes only once (for efficiency). + fixed_source = apply_global_fixes(tmp_source, + options, + filename=filename) + + passes = 0 + long_line_ignore_cache = set() + while hash(fixed_source) not in previous_hashes: + if options.pep8_passes >= 0 and passes > options.pep8_passes: + break + passes += 1 + + previous_hashes.add(hash(fixed_source)) + + tmp_source = copy.copy(fixed_source) + + fix = FixPEP8( + filename, + options, + contents=tmp_source, + long_line_ignore_cache=long_line_ignore_cache) + + fixed_source = fix.fix() + + sio = io.StringIO(fixed_source) + return ''.join(normalize_line_endings(sio.readlines(), original_newline)) + + +def fix_file(filename, options=None, output=None, apply_config=False): + if not options: + options = parse_args([filename], apply_config=apply_config) + + original_source = readlines_from_file(filename) + + fixed_source = original_source + + if options.in_place or options.diff or output: + encoding = detect_encoding(filename) + + if output: + output = LineEndingWrapper(wrap_output(output, encoding=encoding)) + + fixed_source = fix_lines(fixed_source, options, filename=filename) + + if options.diff: + new = io.StringIO(fixed_source) + new = new.readlines() + diff = get_diff_text(original_source, new, filename) + if output: + output.write(diff) + output.flush() + elif options.jobs > 1: + diff = diff.encode(encoding) + return diff + elif options.in_place: + original = "".join(original_source).splitlines() + fixed = fixed_source.splitlines() + original_source_last_line = ( + original_source[-1].split("\n")[-1] if original_source else "" + ) + fixed_source_last_line = fixed_source.split("\n")[-1] + if original != fixed or ( + original_source_last_line != fixed_source_last_line + ): + with open_with_encoding(filename, 'w', encoding=encoding) as fp: + fp.write(fixed_source) + return fixed_source + return None + else: + if output: + output.write(fixed_source) + output.flush() + return fixed_source + + +def global_fixes(): + """Yield multiple (code, function) tuples.""" + for function in list(globals().values()): + if inspect.isfunction(function): + arguments = _get_parameters(function) + if arguments[:1] != ['source']: + continue + + code = extract_code_from_function(function) + if code: + yield (code, function) + + +def _get_parameters(function): + # pylint: disable=deprecated-method + if sys.version_info.major >= 3: + # We need to match "getargspec()", which includes "self" as the first + # value for methods. + # https://bugs.python.org/issue17481#msg209469 + if inspect.ismethod(function): + function = function.__func__ + + return list(inspect.signature(function).parameters) + else: + return inspect.getargspec(function)[0] + + +def apply_global_fixes(source, options, where='global', filename='', + codes=None): + """Run global fixes on source code. + + These are fixes that only need be done once (unlike those in + FixPEP8, which are dependent on pycodestyle). + + """ + if codes is None: + codes = [] + if any(code_match(code, select=options.select, ignore=options.ignore) + for code in ['E101', 'E111']): + source = reindent(source, + indent_size=options.indent_size, + leave_tabs=not ( + code_match( + 'W191', + select=options.select, + ignore=options.ignore) + ) + ) + + for (code, function) in global_fixes(): + if code_match(code, select=options.select, ignore=options.ignore): + if options.verbose: + print('---> Applying {} fix for {}'.format(where, + code.upper()), + file=sys.stderr) + source = function(source, + aggressive=options.aggressive) + + source = fix_2to3(source, + aggressive=options.aggressive, + select=options.select, + ignore=options.ignore, + filename=filename, + where=where, + verbose=options.verbose) + + return source + + +def extract_code_from_function(function): + """Return code handled by function.""" + if not function.__name__.startswith('fix_'): + return None + + code = re.sub('^fix_', '', function.__name__) + if not code: + return None + + try: + int(code[1:]) + except ValueError: + return None + + return code + + +def _get_package_version(): + packages = ["pycodestyle: {}".format(pycodestyle.__version__)] + return ", ".join(packages) + + +def create_parser(): + """Return command-line parser.""" + parser = argparse.ArgumentParser(description=docstring_summary(__doc__), + prog='autopep8') + parser.add_argument('--version', action='version', + version='%(prog)s {} ({})'.format( + __version__, _get_package_version())) + parser.add_argument('-v', '--verbose', action='count', + default=0, + help='print verbose messages; ' + 'multiple -v result in more verbose messages') + parser.add_argument('-d', '--diff', action='store_true', + help='print the diff for the fixed source') + parser.add_argument('-i', '--in-place', action='store_true', + help='make changes to files in place') + parser.add_argument('--global-config', metavar='filename', + default=DEFAULT_CONFIG, + help='path to a global pep8 config file; if this file ' + 'does not exist then this is ignored ' + '(default: {})'.format(DEFAULT_CONFIG)) + parser.add_argument('--ignore-local-config', action='store_true', + help="don't look for and apply local config files; " + 'if not passed, defaults are updated with any ' + "config files in the project's root directory") + parser.add_argument('-r', '--recursive', action='store_true', + help='run recursively over directories; ' + 'must be used with --in-place or --diff') + parser.add_argument('-j', '--jobs', type=int, metavar='n', default=1, + help='number of parallel jobs; ' + 'match CPU count if value is less than 1') + parser.add_argument('-p', '--pep8-passes', metavar='n', + default=-1, type=int, + help='maximum number of additional pep8 passes ' + '(default: infinite)') + parser.add_argument('-a', '--aggressive', action='count', default=0, + help='enable non-whitespace changes; ' + 'multiple -a result in more aggressive changes') + parser.add_argument('--experimental', action='store_true', + help='enable experimental fixes') + parser.add_argument('--exclude', metavar='globs', + help='exclude file/directory names that match these ' + 'comma-separated globs') + parser.add_argument('--list-fixes', action='store_true', + help='list codes for fixes; ' + 'used by --ignore and --select') + parser.add_argument('--ignore', metavar='errors', default='', + help='do not fix these errors/warnings ' + '(default: {})'.format(DEFAULT_IGNORE)) + parser.add_argument('--select', metavar='errors', default='', + help='fix only these errors/warnings (e.g. E4,W)') + parser.add_argument('--max-line-length', metavar='n', default=79, type=int, + help='set maximum allowed line length ' + '(default: %(default)s)') + parser.add_argument('--line-range', '--range', metavar='line', + default=None, type=int, nargs=2, + help='only fix errors found within this inclusive ' + 'range of line numbers (e.g. 1 99); ' + 'line numbers are indexed at 1') + parser.add_argument('--indent-size', default=DEFAULT_INDENT_SIZE, + type=int, help=argparse.SUPPRESS) + parser.add_argument('--hang-closing', action='store_true', + help='hang-closing option passed to pycodestyle') + parser.add_argument('--exit-code', action='store_true', + help='change to behavior of exit code.' + ' default behavior of return value, 0 is no ' + 'differences, 1 is error exit. return 2 when' + ' add this option. 2 is exists differences.') + parser.add_argument('files', nargs='*', + help="files to format or '-' for standard in") + + return parser + + +def _expand_codes(codes, ignore_codes): + """expand to individual E/W codes""" + ret = set() + + is_conflict = False + if all( + any( + conflicting_code.startswith(code) + for code in codes + ) + for conflicting_code in CONFLICTING_CODES + ): + is_conflict = True + + is_ignore_w503 = "W503" in ignore_codes + is_ignore_w504 = "W504" in ignore_codes + + for code in codes: + if code == "W": + if is_ignore_w503 and is_ignore_w504: + ret.update({"W1", "W2", "W3", "W505", "W6"}) + elif is_ignore_w503: + ret.update({"W1", "W2", "W3", "W504", "W505", "W6"}) + else: + ret.update({"W1", "W2", "W3", "W503", "W505", "W6"}) + elif code in ("W5", "W50"): + if is_ignore_w503 and is_ignore_w504: + ret.update({"W505"}) + elif is_ignore_w503: + ret.update({"W504", "W505"}) + else: + ret.update({"W503", "W505"}) + elif not (code in ("W503", "W504") and is_conflict): + ret.add(code) + + return ret + + +def parse_args(arguments, apply_config=False): + """Parse command-line options.""" + parser = create_parser() + args = parser.parse_args(arguments) + + if not args.files and not args.list_fixes: + parser.exit(EXIT_CODE_ARGPARSE_ERROR, 'incorrect number of arguments') + + args.files = [decode_filename(name) for name in args.files] + + if apply_config: + parser = read_config(args, parser) + # prioritize settings when exist pyproject.toml's tool.autopep8 section + try: + parser_with_pyproject_toml = read_pyproject_toml(args, parser) + except Exception: + parser_with_pyproject_toml = None + if parser_with_pyproject_toml: + parser = parser_with_pyproject_toml + args = parser.parse_args(arguments) + args.files = [decode_filename(name) for name in args.files] + + if '-' in args.files: + if len(args.files) > 1: + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + 'cannot mix stdin and regular files', + ) + + if args.diff: + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + '--diff cannot be used with standard input', + ) + + if args.in_place: + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + '--in-place cannot be used with standard input', + ) + + if args.recursive: + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + '--recursive cannot be used with standard input', + ) + + if len(args.files) > 1 and not (args.in_place or args.diff): + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + 'autopep8 only takes one filename as argument ' + 'unless the "--in-place" or "--diff" args are used', + ) + + if args.recursive and not (args.in_place or args.diff): + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + '--recursive must be used with --in-place or --diff', + ) + + if args.in_place and args.diff: + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + '--in-place and --diff are mutually exclusive', + ) + + if args.max_line_length <= 0: + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + '--max-line-length must be greater than 0', + ) + + if args.indent_size <= 0: + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + '--indent-size must be greater than 0', + ) + + if args.select: + args.select = _expand_codes( + _split_comma_separated(args.select), + (_split_comma_separated(args.ignore) if args.ignore else []) + ) + + if args.ignore: + args.ignore = _split_comma_separated(args.ignore) + if all( + not any( + conflicting_code.startswith(ignore_code) + for ignore_code in args.ignore + ) + for conflicting_code in CONFLICTING_CODES + ): + args.ignore.update(CONFLICTING_CODES) + elif not args.select: + if args.aggressive: + # Enable everything by default if aggressive. + args.select = {'E', 'W1', 'W2', 'W3', 'W6'} + else: + args.ignore = _split_comma_separated(DEFAULT_IGNORE) + + if args.exclude: + args.exclude = _split_comma_separated(args.exclude) + else: + args.exclude = {} + + if args.jobs < 1: + # Do not import multiprocessing globally in case it is not supported + # on the platform. + import multiprocessing + args.jobs = multiprocessing.cpu_count() + + if args.jobs > 1 and not (args.in_place or args.diff): + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + 'parallel jobs requires --in-place', + ) + + if args.line_range: + if args.line_range[0] <= 0: + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + '--range must be positive numbers', + ) + if args.line_range[0] > args.line_range[1]: + parser.exit( + EXIT_CODE_ARGPARSE_ERROR, + 'First value of --range should be less than or equal ' + 'to the second', + ) + + return args + + +def _get_normalize_options(args, config, section, option_list): + for (k, v) in config.items(section): + norm_opt = k.lstrip('-').replace('-', '_') + if not option_list.get(norm_opt): + continue + opt_type = option_list[norm_opt] + if opt_type is int: + if v.strip() == "auto": + # skip to special case + if args.verbose: + print(f"ignore config: {k}={v}") + continue + value = config.getint(section, k) + elif opt_type is bool: + value = config.getboolean(section, k) + else: + value = config.get(section, k) + yield norm_opt, k, value + + +def read_config(args, parser): + """Read both user configuration and local configuration.""" + config = SafeConfigParser() + + try: + if args.verbose and os.path.exists(args.global_config): + print("read config path: {}".format(args.global_config)) + config.read(args.global_config) + + if not args.ignore_local_config: + parent = tail = args.files and os.path.abspath( + os.path.commonprefix(args.files)) + while tail: + if config.read([os.path.join(parent, fn) + for fn in PROJECT_CONFIG]): + if args.verbose: + for fn in PROJECT_CONFIG: + config_file = os.path.join(parent, fn) + if not os.path.exists(config_file): + continue + print( + "read config path: {}".format( + os.path.join(parent, fn) + ) + ) + break + (parent, tail) = os.path.split(parent) + + defaults = {} + option_list = {o.dest: o.type or type(o.default) + for o in parser._actions} + + for section in ['pep8', 'pycodestyle', 'flake8']: + if not config.has_section(section): + continue + for norm_opt, k, value in _get_normalize_options( + args, config, section, option_list + ): + if args.verbose: + print("enable config: section={}, key={}, value={}".format( + section, k, value)) + defaults[norm_opt] = value + + parser.set_defaults(**defaults) + except Error: + # Ignore for now. + pass + + return parser + + +def read_pyproject_toml(args, parser): + """Read pyproject.toml and load configuration.""" + if sys.version_info >= (3, 11): + import tomllib + else: + import tomli as tomllib + + config = None + + if os.path.exists(args.global_config): + with open(args.global_config, "rb") as fp: + config = tomllib.load(fp) + + if not args.ignore_local_config: + parent = tail = args.files and os.path.abspath( + os.path.commonprefix(args.files)) + while tail: + pyproject_toml = os.path.join(parent, "pyproject.toml") + if os.path.exists(pyproject_toml): + with open(pyproject_toml, "rb") as fp: + config = tomllib.load(fp) + break + (parent, tail) = os.path.split(parent) + + if not config: + return None + + if config.get("tool", {}).get("autopep8") is None: + return None + + config = config.get("tool").get("autopep8") + + defaults = {} + option_list = {o.dest: o.type or type(o.default) + for o in parser._actions} + + TUPLED_OPTIONS = ("ignore", "select") + for (k, v) in config.items(): + norm_opt = k.lstrip('-').replace('-', '_') + if not option_list.get(norm_opt): + continue + if type(v) in (list, tuple) and norm_opt in TUPLED_OPTIONS: + value = ",".join(v) + else: + value = v + if args.verbose: + print("enable pyproject.toml config: " + "key={}, value={}".format(k, value)) + defaults[norm_opt] = value + + if defaults: + # set value when exists key-value in defaults dict + parser.set_defaults(**defaults) + + return parser + + +def _split_comma_separated(string): + """Return a set of strings.""" + return {text.strip() for text in string.split(',') if text.strip()} + + +def decode_filename(filename): + """Return Unicode filename.""" + if isinstance(filename, str): + return filename + + return filename.decode(sys.getfilesystemencoding()) + + +def supported_fixes(): + """Yield pep8 error codes that autopep8 fixes. + + Each item we yield is a tuple of the code followed by its + description. + + """ + yield ('E101', docstring_summary(reindent.__doc__)) + + instance = FixPEP8(filename=None, options=None, contents='') + for attribute in dir(instance): + code = re.match('fix_([ew][0-9][0-9][0-9])', attribute) + if code: + yield ( + code.group(1).upper(), + re.sub(r'\s+', ' ', + docstring_summary(getattr(instance, attribute).__doc__)) + ) + + for (code, function) in sorted(global_fixes()): + yield (code.upper() + (4 - len(code)) * ' ', + re.sub(r'\s+', ' ', docstring_summary(function.__doc__))) + + for code in sorted(CODE_TO_2TO3): + yield (code.upper() + (4 - len(code)) * ' ', + re.sub(r'\s+', ' ', docstring_summary(fix_2to3.__doc__))) + + +def docstring_summary(docstring): + """Return summary of docstring.""" + return docstring.split('\n')[0] if docstring else '' + + +def line_shortening_rank(candidate, indent_word, max_line_length, + experimental=False): + """Return rank of candidate. + + This is for sorting candidates. + + """ + if not candidate.strip(): + return 0 + + rank = 0 + lines = candidate.rstrip().split('\n') + + offset = 0 + if ( + not lines[0].lstrip().startswith('#') and + lines[0].rstrip()[-1] not in '([{' + ): + for (opening, closing) in ('()', '[]', '{}'): + # Don't penalize empty containers that aren't split up. Things like + # this "foo(\n )" aren't particularly good. + opening_loc = lines[0].find(opening) + closing_loc = lines[0].find(closing) + if opening_loc >= 0: + if closing_loc < 0 or closing_loc != opening_loc + 1: + offset = max(offset, 1 + opening_loc) + + current_longest = max(offset + len(x.strip()) for x in lines) + + rank += 4 * max(0, current_longest - max_line_length) + + rank += len(lines) + + # Too much variation in line length is ugly. + rank += 2 * standard_deviation(len(line) for line in lines) + + bad_staring_symbol = { + '(': ')', + '[': ']', + '{': '}'}.get(lines[0][-1]) + + if len(lines) > 1: + if ( + bad_staring_symbol and + lines[1].lstrip().startswith(bad_staring_symbol) + ): + rank += 20 + + for lineno, current_line in enumerate(lines): + current_line = current_line.strip() + + if current_line.startswith('#'): + continue + + for bad_start in ['.', '%', '+', '-', '/']: + if current_line.startswith(bad_start): + rank += 100 + + # Do not tolerate operators on their own line. + if current_line == bad_start: + rank += 1000 + + if ( + current_line.endswith(('.', '%', '+', '-', '/')) and + "': " in current_line + ): + rank += 1000 + + if current_line.endswith(('(', '[', '{', '.')): + # Avoid lonely opening. They result in longer lines. + if len(current_line) <= len(indent_word): + rank += 100 + + # Avoid the ugliness of ", (\n". + if ( + current_line.endswith('(') and + current_line[:-1].rstrip().endswith(',') + ): + rank += 100 + + # Avoid the ugliness of "something[\n" and something[index][\n. + if ( + current_line.endswith('[') and + len(current_line) > 1 and + (current_line[-2].isalnum() or current_line[-2] in ']') + ): + rank += 300 + + # Also avoid the ugliness of "foo.\nbar" + if current_line.endswith('.'): + rank += 100 + + if has_arithmetic_operator(current_line): + rank += 100 + + # Avoid breaking at unary operators. + if re.match(r'.*[(\[{]\s*[\-\+~]$', current_line.rstrip('\\ ')): + rank += 1000 + + if re.match(r'.*lambda\s*\*$', current_line.rstrip('\\ ')): + rank += 1000 + + if current_line.endswith(('%', '(', '[', '{')): + rank -= 20 + + # Try to break list comprehensions at the "for". + if current_line.startswith('for '): + rank -= 50 + + if current_line.endswith('\\'): + # If a line ends in \-newline, it may be part of a + # multiline string. In that case, we would like to know + # how long that line is without the \-newline. If it's + # longer than the maximum, or has comments, then we assume + # that the \-newline is an okay candidate and only + # penalize it a bit. + total_len = len(current_line) + lineno += 1 + while lineno < len(lines): + total_len += len(lines[lineno]) + + if lines[lineno].lstrip().startswith('#'): + total_len = max_line_length + break + + if not lines[lineno].endswith('\\'): + break + + lineno += 1 + + if total_len < max_line_length: + rank += 10 + else: + rank += 100 if experimental else 1 + + # Prefer breaking at commas rather than colon. + if ',' in current_line and current_line.endswith(':'): + rank += 10 + + # Avoid splitting dictionaries between key and value. + if current_line.endswith(':'): + rank += 100 + + rank += 10 * count_unbalanced_brackets(current_line) + + return max(0, rank) + + +def standard_deviation(numbers): + """Return standard deviation.""" + numbers = list(numbers) + if not numbers: + return 0 + mean = sum(numbers) / len(numbers) + return (sum((n - mean) ** 2 for n in numbers) / + len(numbers)) ** .5 + + +def has_arithmetic_operator(line): + """Return True if line contains any arithmetic operators.""" + for operator in pycodestyle.ARITHMETIC_OP: + if operator in line: + return True + + return False + + +def count_unbalanced_brackets(line): + """Return number of unmatched open/close brackets.""" + count = 0 + for opening, closing in ['()', '[]', '{}']: + count += abs(line.count(opening) - line.count(closing)) + + return count + + +def split_at_offsets(line, offsets): + """Split line at offsets. + + Return list of strings. + + """ + result = [] + + previous_offset = 0 + current_offset = 0 + for current_offset in sorted(offsets): + if current_offset < len(line) and previous_offset != current_offset: + result.append(line[previous_offset:current_offset].strip()) + previous_offset = current_offset + + result.append(line[current_offset:]) + + return result + + +class LineEndingWrapper(object): + + r"""Replace line endings to work with sys.stdout. + + It seems that sys.stdout expects only '\n' as the line ending, no matter + the platform. Otherwise, we get repeated line endings. + + """ + + def __init__(self, output): + self.__output = output + + def write(self, s): + self.__output.write(s.replace('\r\n', '\n').replace('\r', '\n')) + + def flush(self): + self.__output.flush() + + +def match_file(filename, exclude): + """Return True if file is okay for modifying/recursing.""" + base_name = os.path.basename(filename) + + if base_name.startswith('.'): + return False + + for pattern in exclude: + if fnmatch.fnmatch(base_name, pattern): + return False + if fnmatch.fnmatch(filename, pattern): + return False + + if not os.path.isdir(filename) and not is_python_file(filename): + return False + + return True + + +def find_files(filenames, recursive, exclude): + """Yield filenames.""" + while filenames: + name = filenames.pop(0) + if recursive and os.path.isdir(name): + for root, directories, children in os.walk(name): + filenames += [os.path.join(root, f) for f in children + if match_file(os.path.join(root, f), + exclude)] + directories[:] = [d for d in directories + if match_file(os.path.join(root, d), + exclude)] + else: + is_exclude_match = False + for pattern in exclude: + if fnmatch.fnmatch(name, pattern): + is_exclude_match = True + break + if not is_exclude_match: + yield name + + +def _fix_file(parameters): + """Helper function for optionally running fix_file() in parallel.""" + if parameters[1].verbose: + print('[file:{}]'.format(parameters[0]), file=sys.stderr) + try: + return fix_file(*parameters) + except IOError as error: + print(str(error), file=sys.stderr) + raise error + + +def fix_multiple_files(filenames, options, output=None): + """Fix list of files. + + Optionally fix files recursively. + + """ + results = [] + filenames = find_files(filenames, options.recursive, options.exclude) + if options.jobs > 1: + import multiprocessing + pool = multiprocessing.Pool(options.jobs) + rets = [] + for name in filenames: + ret = pool.apply_async(_fix_file, ((name, options),)) + rets.append(ret) + pool.close() + pool.join() + if options.diff: + for r in rets: + sys.stdout.write(r.get().decode()) + sys.stdout.flush() + results.extend([x.get() for x in rets if x is not None]) + else: + for name in filenames: + ret = _fix_file((name, options, output)) + if ret is None: + continue + if options.diff: + if ret != '': + results.append(ret) + elif options.in_place: + results.append(ret) + else: + original_source = readlines_from_file(name) + if "".join(original_source).splitlines() != ret.splitlines(): + results.append(ret) + return results + + +def is_python_file(filename): + """Return True if filename is Python file.""" + if filename.endswith('.py'): + return True + + try: + with open_with_encoding( + filename, + limit_byte_check=MAX_PYTHON_FILE_DETECTION_BYTES) as f: + text = f.read(MAX_PYTHON_FILE_DETECTION_BYTES) + if not text: + return False + first_line = text.splitlines()[0] + except (IOError, IndexError): + return False + + if not PYTHON_SHEBANG_REGEX.match(first_line): + return False + + return True + + +def is_probably_part_of_multiline(line): + """Return True if line is likely part of a multiline string. + + When multiline strings are involved, pep8 reports the error as being + at the start of the multiline string, which doesn't work for us. + + """ + return ( + '"""' in line or + "'''" in line or + line.rstrip().endswith('\\') + ) + + +def wrap_output(output, encoding): + """Return output with specified encoding.""" + return codecs.getwriter(encoding)(output.buffer + if hasattr(output, 'buffer') + else output) + + +def get_encoding(): + """Return preferred encoding.""" + return locale.getpreferredencoding() or sys.getdefaultencoding() + + +def main(argv=None, apply_config=True): + """Command-line entry.""" + if argv is None: + argv = sys.argv + + try: + # Exit on broken pipe. + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + except AttributeError: # pragma: no cover + # SIGPIPE is not available on Windows. + pass + + try: + args = parse_args(argv[1:], apply_config=apply_config) + + if args.list_fixes: + for code, description in sorted(supported_fixes()): + print('{code} - {description}'.format( + code=code, description=description)) + return EXIT_CODE_OK + + if args.files == ['-']: + assert not args.in_place + + encoding = sys.stdin.encoding or get_encoding() + read_stdin = sys.stdin.read() + fixed_stdin = fix_code(read_stdin, args, encoding=encoding) + + # LineEndingWrapper is unnecessary here due to the symmetry between + # standard in and standard out. + wrap_output(sys.stdout, encoding=encoding).write(fixed_stdin) + + if hash(read_stdin) != hash(fixed_stdin): + if args.exit_code: + return EXIT_CODE_EXISTS_DIFF + else: + if args.in_place or args.diff: + args.files = list(set(args.files)) + else: + assert len(args.files) == 1 + assert not args.recursive + + results = fix_multiple_files(args.files, args, sys.stdout) + if args.diff: + ret = any([len(ret) != 0 for ret in results]) + else: + # with in-place option + ret = any([ret is not None for ret in results]) + if args.exit_code and ret: + return EXIT_CODE_EXISTS_DIFF + except IOError: + return EXIT_CODE_ERROR + except KeyboardInterrupt: + return EXIT_CODE_ERROR # pragma: no cover + + +class CachedTokenizer(object): + + """A one-element cache around tokenize.generate_tokens(). + + Original code written by Ned Batchelder, in coverage.py. + + """ + + def __init__(self): + self.last_text = None + self.last_tokens = None + + def generate_tokens(self, text): + """A stand-in for tokenize.generate_tokens().""" + if text != self.last_text: + string_io = io.StringIO(text) + self.last_tokens = list( + tokenize.generate_tokens(string_io.readline) + ) + self.last_text = text + return self.last_tokens + + +_cached_tokenizer = CachedTokenizer() +generate_tokens = _cached_tokenizer.generate_tokens + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/LICENSE b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/LICENSE new file mode 120000 index 00000000..0feb4e60 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/LICENSE @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/7a/f3/41/4fed1e59b7b898ef122a76a7d8668775d95d0556c8ea76de44a5d9a197 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/METADATA b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/METADATA new file mode 120000 index 00000000..6b605b6c --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/METADATA @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/41/0d/77/e6a175ccf0c8c65d02720144624c94cbe79719f6f9833d57197decebf4 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/RECORD b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/RECORD new file mode 100644 index 00000000..763efbd2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/RECORD @@ -0,0 +1,94 @@ +../../../bin/get_objgraph,sha256=alA_HypFrCFdb_KtCGawwEw0FSj6UmUpi3IULIE85No,1641 +../../../bin/undill,sha256=4klKb3PfP6FqVmHafjybFpUA4ml7wmviFlbOcvzKWa8,577 +dill-0.3.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +dill-0.3.6.dist-info/LICENSE,sha256=evNBT-0eWbe4mO8SKnan2GaHddldBVbI6nbeRKXZoZc,1790 +dill-0.3.6.dist-info/METADATA,sha256=QQ135qF1zPDIxl0CcgFEYkyUy-eXGfb5gz1XGX3s6_Q,9847 +dill-0.3.6.dist-info/RECORD,, +dill-0.3.6.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +dill-0.3.6.dist-info/top_level.txt,sha256=HLSIyYIjQzJiBvs3_-16ntezE3j6mWGTW0DT1xDd7X0,5 +dill/__diff.py,sha256=_-xqkDp9hgCmL3OZWaVN9p3mfJHi0ITlFE_AGtVvK2A,7143 +dill/__info__.py,sha256=C_5MG2kSI1Q_5gmzDeDNGnS_mRQPzvcPIK3zNGvYOFM,10612 +dill/__init__.py,sha256=aieQ5jaTr-rV4SGbTOxes_BUR8afH1tQR-9HVlyjxNc,3797 +dill/__pycache__/__diff.cpython-310.pyc,, +dill/__pycache__/__info__.cpython-310.pyc,, +dill/__pycache__/__init__.cpython-310.pyc,, +dill/__pycache__/_dill.cpython-310.pyc,, +dill/__pycache__/_objects.cpython-310.pyc,, +dill/__pycache__/_shims.cpython-310.pyc,, +dill/__pycache__/detect.cpython-310.pyc,, +dill/__pycache__/logger.cpython-310.pyc,, +dill/__pycache__/objtypes.cpython-310.pyc,, +dill/__pycache__/pointers.cpython-310.pyc,, +dill/__pycache__/session.cpython-310.pyc,, +dill/__pycache__/settings.cpython-310.pyc,, +dill/__pycache__/source.cpython-310.pyc,, +dill/__pycache__/temp.cpython-310.pyc,, +dill/_dill.py,sha256=7nh6DAfekcE8cwEYz5mLGqI8uHmH0EmpCjjfZ9_y9LI,82292 +dill/_objects.py,sha256=orBDFlfDPrpk_1FzVVqmn0zexF7uygEVLr7B8WpPXoM,19368 +dill/_shims.py,sha256=FFZo7JJZfz35iFZ-aDY07llbDd9M5IlA-lZ7nPJ0x00,6635 +dill/detect.py,sha256=pGADKt4dptlr5hzJzE-YzqEdoyJIICM8sspYRQrx26Q,11091 +dill/logger.py,sha256=w98pwlNZRUeDrVEsbwQwFjZjdtr2-5lgU93ffSeubBo,11079 +dill/objtypes.py,sha256=l3lfnIox6YJQXN-kXejlajubu-rAAfZ8alQF73l2V3U,736 +dill/pointers.py,sha256=7NIitFkL91Z0-_kTBHXGEqL8seI9qWVouFbykgDlcUw,4467 +dill/session.py,sha256=LVJY9CqFpbw8ckL7YMPnhyn44gDBgSeMHtb9O3C774E,22500 +dill/settings.py,sha256=NuSlm5ItUpCLAU5LoCoLzNt15ku8_APsk1p3XCxYltY,630 +dill/source.py,sha256=BhhsnqcmaiCa6t1oOX8r25JHm69ko9dETnC35jjdbIU,45121 +dill/temp.py,sha256=p1JAbDeiCrfUDlrJhqvZSsNBN12vvfie7yIQZGPUQg8,8027 +dill/tests/__init__.py,sha256=vZabTjRnwYaN-v3uL2fJBqKqj0T7_9Omwy63JSigvTA,501 +dill/tests/__main__.py,sha256=AJNslg4q2QHuMmXME8y6mSazzjz5dSA_mEgLILEXGNo,899 +dill/tests/__pycache__/__init__.cpython-310.pyc,, +dill/tests/__pycache__/__main__.cpython-310.pyc,, +dill/tests/__pycache__/test_check.cpython-310.pyc,, +dill/tests/__pycache__/test_classdef.cpython-310.pyc,, +dill/tests/__pycache__/test_dataclasses.cpython-310.pyc,, +dill/tests/__pycache__/test_detect.cpython-310.pyc,, +dill/tests/__pycache__/test_dictviews.cpython-310.pyc,, +dill/tests/__pycache__/test_diff.cpython-310.pyc,, +dill/tests/__pycache__/test_extendpickle.cpython-310.pyc,, +dill/tests/__pycache__/test_fglobals.cpython-310.pyc,, +dill/tests/__pycache__/test_file.cpython-310.pyc,, +dill/tests/__pycache__/test_functions.cpython-310.pyc,, +dill/tests/__pycache__/test_functors.cpython-310.pyc,, +dill/tests/__pycache__/test_logger.cpython-310.pyc,, +dill/tests/__pycache__/test_mixins.cpython-310.pyc,, +dill/tests/__pycache__/test_module.cpython-310.pyc,, +dill/tests/__pycache__/test_moduledict.cpython-310.pyc,, +dill/tests/__pycache__/test_nested.cpython-310.pyc,, +dill/tests/__pycache__/test_objects.cpython-310.pyc,, +dill/tests/__pycache__/test_properties.cpython-310.pyc,, +dill/tests/__pycache__/test_pycapsule.cpython-310.pyc,, +dill/tests/__pycache__/test_recursive.cpython-310.pyc,, +dill/tests/__pycache__/test_registered.cpython-310.pyc,, +dill/tests/__pycache__/test_restricted.cpython-310.pyc,, +dill/tests/__pycache__/test_selected.cpython-310.pyc,, +dill/tests/__pycache__/test_session.cpython-310.pyc,, +dill/tests/__pycache__/test_source.cpython-310.pyc,, +dill/tests/__pycache__/test_temp.cpython-310.pyc,, +dill/tests/__pycache__/test_weakref.cpython-310.pyc,, +dill/tests/test_check.py,sha256=KjX6OG9mwHWPfN0pnNacJxP7F7uIiNupnn7T2pgUX0Q,1396 +dill/tests/test_classdef.py,sha256=iJR7b4yRc7t8v0G9kLMqZWJ0WzSy3zCKGuCoqNAlNSo,6100 +dill/tests/test_dataclasses.py,sha256=jeVqMLQIhWT_SubVNLb9qayiOF9qlyIyk_KG93kalzc,885 +dill/tests/test_detect.py,sha256=AQo_m3QZC87xQ6htJ3zYJcpRY3dQPGVAj5e6poBza8M,4083 +dill/tests/test_dictviews.py,sha256=EF2jtJjmCv-LR_jT_9n76JZgnE5qwCSf1K2t4LbI0jE,1337 +dill/tests/test_diff.py,sha256=CPDayNL_gO7ecRQ4O_sSdX03__GHEdIrlat30aWbmW0,2667 +dill/tests/test_extendpickle.py,sha256=LtrP8Qo2rdRvQrCaFxnsXaIvTzYfuByL4CDqVndLs10,1315 +dill/tests/test_fglobals.py,sha256=a3xGhcKEp7ly3YDE1zKTL0NWtp6Hz7NmmCD_2IPDDLA,1679 +dill/tests/test_file.py,sha256=qSBC3izDyB9oWXurwO70uwNca3bNW0dkz93Nw2aapso,13578 +dill/tests/test_functions.py,sha256=cWXR3Whje46jLTt3n9PQ1Fr2Jw3W5qf3gsuBlUThKVc,4119 +dill/tests/test_functors.py,sha256=Sc9npIl2jTaIWi9Q_1RP37mhLo1ipapilrKN8Pe9JzA,930 +dill/tests/test_logger.py,sha256=-x9MZVGWRRZZuyjqcgKRV1HRz0-m7O3FabOMoBR_E5Q,2380 +dill/tests/test_mixins.py,sha256=oVHtv1W3v7VgpiBKtJIR1nqa2D6oDK6Br4zbIfVuXOI,4007 +dill/tests/test_module.py,sha256=DPVOyxXr3hUX83_eZ-SXNQixr99CwzgF1pq51oBrkuc,1943 +dill/tests/test_moduledict.py,sha256=GQ-grHuJl1uwEarrhVFoizNvxcfR5pQJDLETfjj481M,1182 +dill/tests/test_nested.py,sha256=qzWuWw3NRkDkIJ7cG0N0DCRS5InED9lDo2CtT7jsTMk,3146 +dill/tests/test_objects.py,sha256=mIFBW_ukZnQtglAZzXfErk65DabsaB1r1PJremqB7pw,1834 +dill/tests/test_properties.py,sha256=1mBRrIeYAu_4x14viOtN8uMeefQ8Zwv6Ovvu3EPDg9g,1346 +dill/tests/test_pycapsule.py,sha256=_jc4a_RYedtQ-FbdToxz65agm57oKG99SeWUhkq_H6w,1412 +dill/tests/test_recursive.py,sha256=dy4abzceZVpAe0GaPrffeC2RAHm4a7UJU_bT_HvoqDE,4182 +dill/tests/test_registered.py,sha256=Ty40fZzPBImRvh6URt0P3aSeKc8UG171122LQ6-MA0I,1250 +dill/tests/test_restricted.py,sha256=fcEbNfLbb1ar4EuR_Np9QoOqdnLeRoXSQXnIMvp73Lg,783 +dill/tests/test_selected.py,sha256=8X7kdiEccwo0mhDQe7DNQqgx0KwepK1sRa8uSQ2E6V0,3218 +dill/tests/test_session.py,sha256=_1J2JJXo1eHuW2dbx-NyHpl6ZwL6ydl48oDMGFUq_L0,10156 +dill/tests/test_source.py,sha256=uj_VEaCbPguguTjF-bXG8llXwXnE7Tr5HbMQkeHGTdE,6036 +dill/tests/test_temp.py,sha256=w2Z-GDhxz3Gx1jXMWpnE1728s7i0eGfXwc1VNWGl1OQ,2619 +dill/tests/test_weakref.py,sha256=JZ_14HFPigx3YTqKHxSL2bNmnPQ87d5-8jQPhCuta_Y,1602 diff --git a/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/WHEEL b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/WHEEL new file mode 120000 index 00000000..7e89bc16 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/WHEEL @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/1b/5e/87/e00dc87a84269cead8578b9e6462928e18a95f1f3373c9eef451a5bcc0 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/top_level.txt new file mode 120000 index 00000000..10173762 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill-0.3.6.dist-info/top_level.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/1c/b4/88/c9822343326206fb37ffed7a9ed7b31378fa9961935b40d3d710dded7d \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/__diff.py b/venv/lib/python3.10/site-packages/dill/__diff.py new file mode 120000 index 00000000..b0aebe70 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/__diff.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ff/ec/6a/903a7d8600a62f739959a54df69de67c91e2d084e5144fc01ad56f2b60 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/__info__.py b/venv/lib/python3.10/site-packages/dill/__info__.py new file mode 120000 index 00000000..8915ea4a --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/__info__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/0b/fe/4c/1b691223543fe609b30de0cd1a74bf99140fcef70f20adf3346bd83853 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/__init__.py b/venv/lib/python3.10/site-packages/dill/__init__.py new file mode 120000 index 00000000..5588e2e6 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/6a/27/90/e63693afead5e1219b4cec5eb3f05447c69f1f5b5047ef47565ca3c4d7 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/__diff.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/__diff.cpython-310.pyc new file mode 100644 index 00000000..868425da Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/__diff.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/__info__.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/__info__.cpython-310.pyc new file mode 100644 index 00000000..b6e535f9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/__info__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..01517765 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/_dill.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/_dill.cpython-310.pyc new file mode 100644 index 00000000..75a6e92c Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/_dill.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/_objects.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/_objects.cpython-310.pyc new file mode 100644 index 00000000..fb0572d9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/_objects.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/_shims.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/_shims.cpython-310.pyc new file mode 100644 index 00000000..2c8ff2f3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/_shims.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/detect.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/detect.cpython-310.pyc new file mode 100644 index 00000000..83ebee07 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/detect.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/logger.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/logger.cpython-310.pyc new file mode 100644 index 00000000..d6bec022 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/logger.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/objtypes.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/objtypes.cpython-310.pyc new file mode 100644 index 00000000..96c9443d Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/objtypes.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/pointers.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/pointers.cpython-310.pyc new file mode 100644 index 00000000..7cdcb68f Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/pointers.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/session.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/session.cpython-310.pyc new file mode 100644 index 00000000..f783bb2e Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/session.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/settings.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/settings.cpython-310.pyc new file mode 100644 index 00000000..4fcc0b3c Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/settings.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/source.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/source.cpython-310.pyc new file mode 100644 index 00000000..faeb45f2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/source.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/__pycache__/temp.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/__pycache__/temp.cpython-310.pyc new file mode 100644 index 00000000..21725987 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/__pycache__/temp.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/_dill.py b/venv/lib/python3.10/site-packages/dill/_dill.py new file mode 120000 index 00000000..6edf1651 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/_dill.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ee/78/7a/0c07de91c13c730118cf998b1aa23cb87987d049a90a38df67dff2f4b2 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/_objects.py b/venv/lib/python3.10/site-packages/dill/_objects.py new file mode 120000 index 00000000..4a9ba73d --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/_objects.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a2/b0/43/1657c33eba64ff5173555aa69f4cdec45eeeca01152ebec1f16a4f5e83 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/_shims.py b/venv/lib/python3.10/site-packages/dill/_shims.py new file mode 120000 index 00000000..3ae86855 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/_shims.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/14/56/68/ec92597f3df988567e683634ee595b0ddf4ce48940fa567b9cf274c74d \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/detect.py b/venv/lib/python3.10/site-packages/dill/detect.py new file mode 120000 index 00000000..10ede08b --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/detect.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a4/60/03/2ade1da6d96be61cc9cc4f98cea11da3224820233cb2ca58450af1dba4 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/logger.py b/venv/lib/python3.10/site-packages/dill/logger.py new file mode 120000 index 00000000..fd98f41c --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/logger.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/c3/df/29/c25359454783ad512c6f043016366376daf6fb996053dddf7d27ae6c1a \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/objtypes.py b/venv/lib/python3.10/site-packages/dill/objtypes.py new file mode 120000 index 00000000..3e279d87 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/objtypes.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/97/79/5f/9c8a31e982505cdfa45de8e56a3b9bbbeac001f67c6a5405ef79765775 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/pointers.py b/venv/lib/python3.10/site-packages/dill/pointers.py new file mode 120000 index 00000000..ce39fa49 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/pointers.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ec/d2/22/b4590bf75674fbf9130475c612a2fcb1e23da96568b856f29200e5714c \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/session.py b/venv/lib/python3.10/site-packages/dill/session.py new file mode 120000 index 00000000..10f2b282 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/session.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/2d/52/58/f42a85a5bc3c7242fb60c3e78729f8e200c181278c1ed6fd3b70bbef81 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/settings.py b/venv/lib/python3.10/site-packages/dill/settings.py new file mode 120000 index 00000000..0a51d144 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/settings.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/36/e4/a5/9b922d52908b014e4ba02a0bccdb75e64bbcfc03ec935a775c2c5896d6 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/source.py b/venv/lib/python3.10/site-packages/dill/source.py new file mode 120000 index 00000000..68c59030 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/source.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/06/18/6c/9ea7266a209aeadd68397f2bdb92479baf64a3d7444e70b7e638dd6c85 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/temp.py b/venv/lib/python3.10/site-packages/dill/temp.py new file mode 120000 index 00000000..884a34e6 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/temp.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a7/52/40/6c37a20ab7d40e5ac986abd94ac341375dafbdf89eef22106463d4420f \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/__init__.py b/venv/lib/python3.10/site-packages/dill/tests/__init__.py new file mode 120000 index 00000000..18946c52 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/bd/96/9b/4e3467c1868dfafdee2f67c906a2aa8f44fbffd3a6c32eb72528a0bd30 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/__main__.py b/venv/lib/python3.10/site-packages/dill/tests/__main__.py new file mode 120000 index 00000000..5489b879 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/__main__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/00/93/6c/960e2ad901ee3265cc13ccba9926b3ce3cf975203f98480b20b11718da \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..a3d124f7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/__main__.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 00000000..1382ea32 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/__main__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_check.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_check.cpython-310.pyc new file mode 100644 index 00000000..eb221080 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_check.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_classdef.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_classdef.cpython-310.pyc new file mode 100644 index 00000000..59a630d3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_classdef.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_dataclasses.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_dataclasses.cpython-310.pyc new file mode 100644 index 00000000..3fa033f2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_dataclasses.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_detect.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_detect.cpython-310.pyc new file mode 100644 index 00000000..56ab11c3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_detect.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_dictviews.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_dictviews.cpython-310.pyc new file mode 100644 index 00000000..e2edbe3f Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_dictviews.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_diff.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_diff.cpython-310.pyc new file mode 100644 index 00000000..fe4235d3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_diff.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_extendpickle.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_extendpickle.cpython-310.pyc new file mode 100644 index 00000000..a94ff6b1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_extendpickle.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_fglobals.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_fglobals.cpython-310.pyc new file mode 100644 index 00000000..5499fe15 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_fglobals.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_file.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_file.cpython-310.pyc new file mode 100644 index 00000000..bf408216 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_file.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_functions.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_functions.cpython-310.pyc new file mode 100644 index 00000000..afe25317 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_functions.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_functors.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_functors.cpython-310.pyc new file mode 100644 index 00000000..4947c421 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_functors.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_logger.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_logger.cpython-310.pyc new file mode 100644 index 00000000..19a863f5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_logger.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_mixins.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_mixins.cpython-310.pyc new file mode 100644 index 00000000..5738ff83 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_mixins.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_module.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_module.cpython-310.pyc new file mode 100644 index 00000000..76520a44 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_module.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_moduledict.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_moduledict.cpython-310.pyc new file mode 100644 index 00000000..24a3c677 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_moduledict.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_nested.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_nested.cpython-310.pyc new file mode 100644 index 00000000..a52071ef Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_nested.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_objects.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_objects.cpython-310.pyc new file mode 100644 index 00000000..415dcd94 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_objects.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_properties.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_properties.cpython-310.pyc new file mode 100644 index 00000000..7090bd47 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_properties.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_pycapsule.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_pycapsule.cpython-310.pyc new file mode 100644 index 00000000..feacc4a4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_pycapsule.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_recursive.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_recursive.cpython-310.pyc new file mode 100644 index 00000000..1072ac0d Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_recursive.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_registered.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_registered.cpython-310.pyc new file mode 100644 index 00000000..0d72a073 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_registered.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_restricted.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_restricted.cpython-310.pyc new file mode 100644 index 00000000..42b698b1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_restricted.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_selected.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_selected.cpython-310.pyc new file mode 100644 index 00000000..73d9d160 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_selected.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_session.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_session.cpython-310.pyc new file mode 100644 index 00000000..4e8247dd Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_session.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_source.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_source.cpython-310.pyc new file mode 100644 index 00000000..3609623f Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_source.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_temp.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_temp.cpython-310.pyc new file mode 100644 index 00000000..c0a2ad41 Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_temp.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_weakref.cpython-310.pyc b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_weakref.cpython-310.pyc new file mode 100644 index 00000000..a192789c Binary files /dev/null and b/venv/lib/python3.10/site-packages/dill/tests/__pycache__/test_weakref.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_check.py b/venv/lib/python3.10/site-packages/dill/tests/test_check.py new file mode 120000 index 00000000..0f007844 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_check.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/2a/35/fa/386f66c0758f7cdd299cd69c2713fb17bb8888dba99e7ed3da98145f44 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_classdef.py b/venv/lib/python3.10/site-packages/dill/tests/test_classdef.py new file mode 120000 index 00000000..334e60b0 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_classdef.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/88/94/7b/6f8c9173bb7cbf41bd90b32a6562745b34b2df308a1ae0a8a8d025352a \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py b/venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py new file mode 120000 index 00000000..16e2bc35 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_dataclasses.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/8d/e5/6a/30b4088564ff4ae6d534b6fda9aca2385f6a97223293f286f7791a9737 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_detect.py b/venv/lib/python3.10/site-packages/dill/tests/test_detect.py new file mode 120000 index 00000000..a35162c0 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_detect.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/01/0a/3f/9b74190bcef143a86d277cd825ca516377503c65408f97baa680736bc3 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py b/venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py new file mode 120000 index 00000000..4fd4f0b0 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_dictviews.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/10/5d/a3/b498e60aff8b47f8d3ffd9fbe896609c4e6ac0249fd4adade0b6c8d231 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_diff.py b/venv/lib/python3.10/site-packages/dill/tests/test_diff.py new file mode 120000 index 00000000..4de7cfe4 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_diff.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/08/f0/da/c8d2ff80eede7114383bfb12757d37fff18711d22b95ab77d1a59b996d \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py b/venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py new file mode 120000 index 00000000..cbeb5f66 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_extendpickle.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/2e/da/cf/f10a36add46f42b09a1719ec5da22f4f361fb81c8be020ea56774bb35d \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py b/venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py new file mode 120000 index 00000000..be217b54 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_fglobals.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/6b/7c/46/85c284a7b972dd80c4d732932f4356b69e87cfb3669820ffd883c30cb0 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_file.py b/venv/lib/python3.10/site-packages/dill/tests/test_file.py new file mode 120000 index 00000000..8ab09598 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_file.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a9/20/42/de2cc3c81f68597babc0eef4bb035c6b76cd5b4764cfddcdc3669aa6ca \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_functions.py b/venv/lib/python3.10/site-packages/dill/tests/test_functions.py new file mode 120000 index 00000000..3ecbeb4d --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_functions.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/71/65/d1/dd68637b8ea32d3b779fd3d0d45af6270dd6e6a7f782cb819544e12957 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_functors.py b/venv/lib/python3.10/site-packages/dill/tests/test_functors.py new file mode 120000 index 00000000..311c0beb --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_functors.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/49/cf/67/a489768d36885a2f50ff544fdfb9a12e8d62a5aa6296b28df0f7bd2730 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_logger.py b/venv/lib/python3.10/site-packages/dill/tests/test_logger.py new file mode 120000 index 00000000..7780c0fa --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_logger.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/fb/1f/4c/655196451659bb28ea7202915751d1cf4fa6ecedc569b38ca0147f1394 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_mixins.py b/venv/lib/python3.10/site-packages/dill/tests/test_mixins.py new file mode 120000 index 00000000..51a18bc1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_mixins.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a1/51/ed/bf55b7bfb560a6204ab49211d67a9ad83ea80cae81af8cdb21f56e5ce2 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_module.py b/venv/lib/python3.10/site-packages/dill/tests/test_module.py new file mode 120000 index 00000000..a9f2e835 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_module.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/0c/f5/4e/cb15ebde1517f37fde67e4973508b1afdf42c33805d69ab9d6806b92e7 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py b/venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py new file mode 120000 index 00000000..685c7535 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_moduledict.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/19/0f/a0/ac7b89975bb011aaeb8551688b336fc5c7d1e694090cb1137e38f8f353 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_nested.py b/venv/lib/python3.10/site-packages/dill/tests/test_nested.py new file mode 120000 index 00000000..82563598 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_nested.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ab/35/ae/5b0dcd4640e4209edc1b43740c2452e489c40fd943a360ad4fb8ec4cc9 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_objects.py b/venv/lib/python3.10/site-packages/dill/tests/test_objects.py new file mode 120000 index 00000000..61233f40 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_objects.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/98/81/41/5bfba466742d825019cd77c4ae4eb90da6ec681d6bd4f26b7a6a81ee9c \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_properties.py b/venv/lib/python3.10/site-packages/dill/tests/test_properties.py new file mode 120000 index 00000000..965dd63d --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_properties.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/d6/60/51/ac879802eff8c75e2f88eb4df2e31e79f43c670bfa3afbeedc43c383d8 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py b/venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py new file mode 120000 index 00000000..f7ea2562 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_pycapsule.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/fe/37/38/6bf45879db50f856dd4e8c73eb96a09b9ee8286f7d49e594864abf1fac \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_recursive.py b/venv/lib/python3.10/site-packages/dill/tests/test_recursive.py new file mode 120000 index 00000000..0c5570ee --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_recursive.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/77/2e/1a/6f371e655a407b419a3eb7df782d910079b86bb50953f6d3fc7be8a831 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_registered.py b/venv/lib/python3.10/site-packages/dill/tests/test_registered.py new file mode 120000 index 00000000..da433c53 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_registered.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/4f/2e/34/7d9ccf048991be1e9446dd0fdda49e29cf141b5ef5d76d8b43af8c0342 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_restricted.py b/venv/lib/python3.10/site-packages/dill/tests/test_restricted.py new file mode 120000 index 00000000..e39d69dc --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_restricted.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/7d/c1/1b/35f2db6f56abe04b91fcda7d4283aa7672de4685d24179c832fa7bdcb8 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_selected.py b/venv/lib/python3.10/site-packages/dill/tests/test_selected.py new file mode 120000 index 00000000..5e9ca48d --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_selected.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/f1/7e/e4/76211c730a349a10d07bb0cd42a831d0ac1ea4ad6c45af2e490d84e95d \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_session.py b/venv/lib/python3.10/site-packages/dill/tests/test_session.py new file mode 120000 index 00000000..c54c0141 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_session.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ff/52/76/2495e8d5e1ee5b675bc7e3721e997a6702fac9d978f280cc18552afcbd \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_source.py b/venv/lib/python3.10/site-packages/dill/tests/test_source.py new file mode 120000 index 00000000..c9576478 --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_source.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ba/3f/d5/11a09b3e0ba0b938c5f9b5c6f25957c179c4ed3af91db31091e1c64dd1 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_temp.py b/venv/lib/python3.10/site-packages/dill/tests/test_temp.py new file mode 120000 index 00000000..6ab241da --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_temp.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/c3/66/7e/183871cf71b1d635cc5a99c4d7bdbcb3b8b47867d7c1cd553561a5d4e4 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/dill/tests/test_weakref.py b/venv/lib/python3.10/site-packages/dill/tests/test_weakref.py new file mode 120000 index 00000000..3510a67f --- /dev/null +++ b/venv/lib/python3.10/site-packages/dill/tests/test_weakref.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/25/9f/f5/e0714f8a0c77613a8a1f148bd9b3669cf43cedde7ef2340f842bad6bf6 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/LICENSE b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/LICENSE new file mode 120000 index 00000000..1dfa21bc --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/LICENSE @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/06/32/94/001c3d523dbacbab9dd54ac229982756ccaf592ca53240b2b0cdd82065 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/METADATA new file mode 120000 index 00000000..c0a737d1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/METADATA @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e4/d3/e1/5a90582a50b26ee9fa23561d179eb1527d59c903095547c391939692ce \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/RECORD new file mode 100644 index 00000000..2d4ae89c --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/RECORD @@ -0,0 +1,97 @@ +../../../bin/isort,sha256=F7XoMAqhaeGJ4AjNHn4taXPKSrf_6-kHcho4cIMs8dc,213 +../../../bin/isort-identify-imports,sha256=iltniqKoTDZFfyB6827Wbc_GZ9ai1ETPBjKi7StkcK4,247 +isort-5.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +isort-5.12.0.dist-info/LICENSE,sha256=BjKUABw9Uj26y6ud1UrCKZgnVsyvWSylMkCysM3YIGU,1089 +isort-5.12.0.dist-info/METADATA,sha256=5NPhWpBYKlCybun6I1YdF56xUn1ZyQMJVUfDkZOWks4,12970 +isort-5.12.0.dist-info/RECORD,, +isort-5.12.0.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88 +isort-5.12.0.dist-info/entry_points.txt,sha256=stP-G7UtFo06wllIxS1jKbEJpc4u_3WPiLh_r13BGcc,213 +isort/__init__.py,sha256=5S6lmnFHXlZbzl7ni97ZANzkXePqKPkRUaMJyul3dIo,871 +isort/__main__.py,sha256=iK0trzN9CCXpQX-XPZDZ9JVkm2Lc0q0oiAgsa6FkJb4,36 +isort/__pycache__/__init__.cpython-310.pyc,, +isort/__pycache__/__main__.cpython-310.pyc,, +isort/__pycache__/_version.cpython-310.pyc,, +isort/__pycache__/api.cpython-310.pyc,, +isort/__pycache__/comments.cpython-310.pyc,, +isort/__pycache__/core.cpython-310.pyc,, +isort/__pycache__/exceptions.cpython-310.pyc,, +isort/__pycache__/files.cpython-310.pyc,, +isort/__pycache__/format.cpython-310.pyc,, +isort/__pycache__/hooks.cpython-310.pyc,, +isort/__pycache__/identify.cpython-310.pyc,, +isort/__pycache__/io.cpython-310.pyc,, +isort/__pycache__/literal.cpython-310.pyc,, +isort/__pycache__/logo.cpython-310.pyc,, +isort/__pycache__/main.cpython-310.pyc,, +isort/__pycache__/output.cpython-310.pyc,, +isort/__pycache__/parse.cpython-310.pyc,, +isort/__pycache__/place.cpython-310.pyc,, +isort/__pycache__/profiles.cpython-310.pyc,, +isort/__pycache__/pylama_isort.cpython-310.pyc,, +isort/__pycache__/sections.cpython-310.pyc,, +isort/__pycache__/settings.cpython-310.pyc,, +isort/__pycache__/setuptools_commands.cpython-310.pyc,, +isort/__pycache__/sorting.cpython-310.pyc,, +isort/__pycache__/utils.cpython-310.pyc,, +isort/__pycache__/wrap.cpython-310.pyc,, +isort/__pycache__/wrap_modes.cpython-310.pyc,, +isort/_vendored/tomli/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072 +isort/_vendored/tomli/__init__.py,sha256=Y3N65pvphV_EF4k2qKiq_vYcohIUHhT05GzdRc0TOy8,213 +isort/_vendored/tomli/__pycache__/__init__.cpython-310.pyc,, +isort/_vendored/tomli/__pycache__/_parser.cpython-310.pyc,, +isort/_vendored/tomli/__pycache__/_re.cpython-310.pyc,, +isort/_vendored/tomli/_parser.py,sha256=fhOEEYZATanBBAn-hyy0Au_aZbdqXfdKB8mGTvI1W3k,21397 +isort/_vendored/tomli/_re.py,sha256=3r6TD3gNGFjgOsfpy8aLpxgvasL__pvaN2m1R5DTxeQ,2833 +isort/_vendored/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26 +isort/_version.py,sha256=pXTtYi-S-p8e00o2Ad-PNREL9wAQaPgQzk_c_jndLOw,72 +isort/api.py,sha256=kzuy8vxvy99ri4YoOFCbjkzRJOr2VsG8OXHjGq7VSts,26120 +isort/comments.py,sha256=6tLt0QRuSQvo-tpgTTM4oJKk-oqaE8MOTA95l89LtQQ,933 +isort/core.py,sha256=aO8DKqX-Mv70VQS1GBTVWPtSW9Uk1ig42nhD-Z0AbSs,22525 +isort/deprecated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +isort/deprecated/__pycache__/__init__.cpython-310.pyc,, +isort/deprecated/__pycache__/finders.cpython-310.pyc,, +isort/deprecated/finders.py,sha256=yWqeyfrRXw7Fu4W1uWmXt-tB-xW9kUrT7cSU7IRsKbs,14818 +isort/exceptions.py,sha256=xmUyF5uS1K_rtlMDWoaAmtD_PA0B82u6rqgxgEjUjcw,7060 +isort/files.py,sha256=3wRqIAAquCCTF5aPzpzoDsWBvrTy49vqG11hAFseJD8,1589 +isort/format.py,sha256=E9Og4mc7ajxyMAFmUlAK2ZmW7N75uexfY0c9q-zmyzA,5483 +isort/hooks.py,sha256=Ye-vm0Q4mLFxm1rNCLxdNxbEa7D-lO2UyL2vLnvje78,3338 +isort/identify.py,sha256=Q58HB4BRrOgdbiWuk6y_olcPnLaGIYazJyGvVheG7Y0,8373 +isort/io.py,sha256=ElUxFk-SBeMKVQ2nSVs4L1liDHHtf6umsTpNyjaGk1g,2216 +isort/literal.py,sha256=1fXUljG4ol2eKEikoLzorD_ir2v7Q3-3oKlxaitNfZ4,3713 +isort/logo.py,sha256=cL3al79O7O0G2viqRMRfBPp0qtRZmJw2nHSCZw8XWdQ,388 +isort/main.py,sha256=rewo3AYM76Ioj8s6owWNuWHWqBs7tk_dLqyLjLbG60o,46823 +isort/output.py,sha256=yVWZ9W8HCiXJVLFlYl4mp9h4peN7a_cNK4-QDdpzsdE,27804 +isort/parse.py,sha256=v9ymoaf6_v0LQexu7WBjvzi4GModzc2nWQ8u9n0auGk,25332 +isort/place.py,sha256=u3qN5rt_A2IcVZ5ndcGMohLZvsRWzVrJe3nikIf7S_4,5171 +isort/profiles.py,sha256=rbhZg1Tdtc2KQRcsYX-gzzpZrByC1lo_GxYMtC02kHE,2144 +isort/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +isort/pylama_isort.py,sha256=vNP7jAxZy7ryZR4hotynA4JCzAxLtbasT9AYpZiiClk,1308 +isort/sections.py,sha256=xG5bwU4tOIKUmeBBhZ45EIfjP8HgDOx796bPvD5zWCw,297 +isort/settings.py,sha256=bfdvb0EjNc6gtBGo3mSeKN7zmubBted81akc5eV70NE,35584 +isort/setuptools_commands.py,sha256=uzeBcGerTKtoy9tHo7O-jVeYzq3nQAWfluODpmR86jw,2297 +isort/sorting.py,sha256=m4Mbe1wq5RtUZIp6guuh6dCBRVD4igCGLXkaTO3l1iU,4515 +isort/stdlibs/__init__.py,sha256=QzfCru6IWo5L_ZYZ4lgm8P2J2I-VnFsqflcKpVPrm3c,93 +isort/stdlibs/__pycache__/__init__.cpython-310.pyc,, +isort/stdlibs/__pycache__/all.cpython-310.pyc,, +isort/stdlibs/__pycache__/py2.cpython-310.pyc,, +isort/stdlibs/__pycache__/py27.cpython-310.pyc,, +isort/stdlibs/__pycache__/py3.cpython-310.pyc,, +isort/stdlibs/__pycache__/py310.cpython-310.pyc,, +isort/stdlibs/__pycache__/py311.cpython-310.pyc,, +isort/stdlibs/__pycache__/py36.cpython-310.pyc,, +isort/stdlibs/__pycache__/py37.cpython-310.pyc,, +isort/stdlibs/__pycache__/py38.cpython-310.pyc,, +isort/stdlibs/__pycache__/py39.cpython-310.pyc,, +isort/stdlibs/all.py,sha256=n8Es1WK6UlupYyVvf1PDjGbionqix-afC3LkY8nzTcw,57 +isort/stdlibs/py2.py,sha256=dTgWTa7ggz1cwN8fuI9eIs9-5nTmkRxG_uO61CGwfXI,41 +isort/stdlibs/py27.py,sha256=QriKfttNSHsjaRtDfR5WXytjzf7Xi7p9lxiOOcmA2JM,4504 +isort/stdlibs/py3.py,sha256=jXtjkMP0a7HO59u9zHt91drh-Bg-gadpO0Tz3gl5yWk,145 +isort/stdlibs/py310.py,sha256=d9wONMDYRrmhB90Dk-9NOUeYZqDn242pC3fbhZNMvXY,3278 +isort/stdlibs/py311.py,sha256=8y60SHH83F4urISnu6VJxkdEuHQDmeZgTR6svbNBdZ4,3279 +isort/stdlibs/py36.py,sha256=iuXIDLcFrSviMMSOP4PoKWCG5BveMnZbFravpduSUss,3310 +isort/stdlibs/py37.py,sha256=dLxxRerCvb4O9vrifTg5KWgO0L3a6AQB13haK_tSBRw,3334 +isort/stdlibs/py38.py,sha256=xsUSUZD5XUYX0PIuf9A3bSMD4ZcbfPyzqJqCudSfhaU,3319 +isort/stdlibs/py39.py,sha256=tlVRkhoDpoNJxJd803NdTt_hgzzuukJMkUQMgpE-vlA,3307 +isort/utils.py,sha256=5EEZUfZyyWcJLk2qnNF8ObDib_qPU4zwEQvWjpKRgb0,2413 +isort/wrap.py,sha256=Ge_X8ZtcnKSgU_siuliFvTVyv0ITzPopmSFHtfvaEHA,6321 +isort/wrap_modes.py,sha256=3N-izkXBrd-QU2CyEbqxuKVHuuhB-ulqzDYF7DV6wNk,13569 diff --git a/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/WHEEL new file mode 120000 index 00000000..e2dc416b --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/WHEEL @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/bd/50/af/8dc9b1b9496d7fc718849d2c24c4432ebd57b0fbb110877c603cdbc826 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/entry_points.txt new file mode 120000 index 00000000..242dd969 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort-5.12.0.dist-info/entry_points.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/b2/d3/fe/1bb52d168d3ac25948c52d6329b109a5ce2eff758f88b87faf5dc119c7 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/__init__.py b/venv/lib/python3.10/site-packages/isort/__init__.py new file mode 120000 index 00000000..97447f46 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e5/2e/a5/9a71475e565bce5ee78bded900dce45de3ea28f91151a309cae977748a \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/__main__.py b/venv/lib/python3.10/site-packages/isort/__main__.py new file mode 120000 index 00000000..751d8a1a --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/__main__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/88/ad/2d/af337d0825e9417f973d90d9f495649b62dcd2ad2888082c6ba16425be \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..84aba9c8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/__main__.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 00000000..850b0912 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/__main__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/_version.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/_version.cpython-310.pyc new file mode 100644 index 00000000..d6d0f3f4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/_version.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/api.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/api.cpython-310.pyc new file mode 100644 index 00000000..cd33d4c7 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/api.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/comments.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/comments.cpython-310.pyc new file mode 100644 index 00000000..3e36615d Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/comments.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/core.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/core.cpython-310.pyc new file mode 100644 index 00000000..7a68f870 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/core.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/exceptions.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 00000000..36d86a8d Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/exceptions.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/files.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/files.cpython-310.pyc new file mode 100644 index 00000000..f05e08ef Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/files.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/format.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/format.cpython-310.pyc new file mode 100644 index 00000000..8fe4a37e Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/format.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/hooks.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/hooks.cpython-310.pyc new file mode 100644 index 00000000..d446e4a1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/hooks.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/identify.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/identify.cpython-310.pyc new file mode 100644 index 00000000..bd3e20b1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/identify.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/io.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/io.cpython-310.pyc new file mode 100644 index 00000000..1dec7b25 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/io.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/literal.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/literal.cpython-310.pyc new file mode 100644 index 00000000..7ae64103 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/literal.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/logo.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/logo.cpython-310.pyc new file mode 100644 index 00000000..ef0ccb09 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/logo.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/main.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/main.cpython-310.pyc new file mode 100644 index 00000000..a685c461 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/main.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/output.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/output.cpython-310.pyc new file mode 100644 index 00000000..9449077d Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/output.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/parse.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/parse.cpython-310.pyc new file mode 100644 index 00000000..caf70638 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/parse.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/place.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/place.cpython-310.pyc new file mode 100644 index 00000000..5f4ea0e1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/place.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/profiles.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/profiles.cpython-310.pyc new file mode 100644 index 00000000..4f99c9b9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/profiles.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/pylama_isort.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/pylama_isort.cpython-310.pyc new file mode 100644 index 00000000..5cf34a7a Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/pylama_isort.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/sections.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/sections.cpython-310.pyc new file mode 100644 index 00000000..308cdddd Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/sections.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/settings.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/settings.cpython-310.pyc new file mode 100644 index 00000000..5a971225 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/settings.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/setuptools_commands.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/setuptools_commands.cpython-310.pyc new file mode 100644 index 00000000..47482e93 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/setuptools_commands.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/sorting.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/sorting.cpython-310.pyc new file mode 100644 index 00000000..3616bc95 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/sorting.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/utils.cpython-310.pyc new file mode 100644 index 00000000..7a4bb89b Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/wrap.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/wrap.cpython-310.pyc new file mode 100644 index 00000000..1969761f Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/wrap.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/__pycache__/wrap_modes.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/__pycache__/wrap_modes.cpython-310.pyc new file mode 100644 index 00000000..ebd3e5ca Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/__pycache__/wrap_modes.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE new file mode 120000 index 00000000..ce1ba020 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/LICENSE @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/b8/08/16/b0d530b8accb4c2211783790984a6e3b61922c2b5ee92f3372ab2742fe \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py new file mode 120000 index 00000000..83bf9e3a --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/63/73/7a/e69be9855fc4178936a8a8aafef61ca212141e14f4e46cdd45cd133b2f \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..fa7ce303 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__pycache__/_parser.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__pycache__/_parser.cpython-310.pyc new file mode 100644 index 00000000..3cf1efe6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__pycache__/_parser.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__pycache__/_re.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__pycache__/_re.cpython-310.pyc new file mode 100644 index 00000000..e4ee444b Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/__pycache__/_re.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py new file mode 120000 index 00000000..750758e0 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/_parser.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/7e/13/84/1186404da9c10409fe872cb402efda65b76a5df74a07c9864ef2355b79 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py new file mode 120000 index 00000000..741a2858 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/_re.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/de/be/93/0f780d1858e03ac7e9cbc68ba7182f6ac2fffe9bda3769b54790d3c5e4 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed new file mode 120000 index 00000000..1d96da2a --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/_vendored/tomli/py.typed @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/f0/f8/f2/675695a10a5156fb7bd66bafbaae6a13e8d315990af862c792175e6e67 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/_version.py b/venv/lib/python3.10/site-packages/isort/_version.py new file mode 120000 index 00000000..1343eddb --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/_version.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a5/74/ed/622f92fa9f1ed34a3601df8f35110bf7001068f810ce4fdcfe39dd2cec \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/api.py b/venv/lib/python3.10/site-packages/isort/api.py new file mode 120000 index 00000000..8db5eb2e --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/api.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/93/3b/b2/f2fc6fcbdf6b8b862838509b8e4cd124eaf656c1bc3971e31aaed54adb \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/comments.py b/venv/lib/python3.10/site-packages/isort/comments.py new file mode 120000 index 00000000..1832ea10 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/comments.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ea/d2/ed/d1046e490be8fada604d3338a092a4fa8a9a13c30e4c0f7997cf4bb504 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/core.py b/venv/lib/python3.10/site-packages/isort/core.py new file mode 120000 index 00000000..0d04c747 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/core.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/68/ef/03/2aa5fe32fef45504b51814d558fb525bd524d62838da7843f99d006d2b \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/deprecated/__init__.py b/venv/lib/python3.10/site-packages/isort/deprecated/__init__.py new file mode 120000 index 00000000..05b4099d --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/deprecated/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e3/b0/c4/4298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/deprecated/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/deprecated/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..87d416c9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/deprecated/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/deprecated/__pycache__/finders.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/deprecated/__pycache__/finders.cpython-310.pyc new file mode 100644 index 00000000..a3e7bfb3 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/deprecated/__pycache__/finders.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/deprecated/finders.py b/venv/lib/python3.10/site-packages/isort/deprecated/finders.py new file mode 120000 index 00000000..b6f96fe5 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/deprecated/finders.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/c9/6a/9e/c9fad15f0ec5bb85b5b96997b7eb41fb15bd914ad3edc494ec846c29bb \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/exceptions.py b/venv/lib/python3.10/site-packages/isort/exceptions.py new file mode 120000 index 00000000..9b4af783 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/exceptions.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/c6/65/32/179b92d4afebb653035a86809ad0ff3c0d01f36bbaaea8318048d48dcc \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/files.py b/venv/lib/python3.10/site-packages/isort/files.py new file mode 120000 index 00000000..de46d34c --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/files.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/df/04/6a/20002ab8209317968fce9ce80ec581beb4f2e3dbea1b5d61005b1e243f \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/format.py b/venv/lib/python3.10/site-packages/isort/format.py new file mode 120000 index 00000000..9be19cc8 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/format.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/13/d3/a0/e2673b6a3c7230016652500ad99996ecdef9b9ec5f63473dabece6cb30 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/hooks.py b/venv/lib/python3.10/site-packages/isort/hooks.py new file mode 120000 index 00000000..11a5d2dd --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/hooks.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/61/ef/af/9b443898b1719b5acd08bc5d3716c46bb0fe94ed94c8bdaf2e7be37bbf \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/identify.py b/venv/lib/python3.10/site-packages/isort/identify.py new file mode 120000 index 00000000..80f28fe4 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/identify.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/43/9f/07/078051ace81d6e25ae93acbfa2570f9cb6862186b32721af561786ed8d \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/io.py b/venv/lib/python3.10/site-packages/isort/io.py new file mode 120000 index 00000000..5805065d --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/io.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/12/55/31/164f9205e30a550da7495b382f59620c71ed7faba6b13a4dca36869358 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/literal.py b/venv/lib/python3.10/site-packages/isort/literal.py new file mode 120000 index 00000000..3f759ad7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/literal.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/d5/f5/d4/9631b8a25d9e2848a4a0bce8ac3fe2af6bfb437fb7a0a9716a2b4d7d9e \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/logo.py b/venv/lib/python3.10/site-packages/isort/logo.py new file mode 120000 index 00000000..b4922b41 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/logo.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/70/bd/da/97bf4eeced06daf8aa44c45f04fa74aad459989c369c7482670f1759d4 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/main.py b/venv/lib/python3.10/site-packages/isort/main.py new file mode 120000 index 00000000..fb031e74 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/main.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ad/ec/28/dc060cefa2288fcb3aa3058db961d6a81b3bb64fdd2eac8b8cb6c6eb4a \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/output.py b/venv/lib/python3.10/site-packages/isort/output.py new file mode 120000 index 00000000..f04793af --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/output.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/c9/55/99/f56f070a25c954b165625e26a7d878a5e37b6bf70d2b8f900dda73b1d1 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/parse.py b/venv/lib/python3.10/site-packages/isort/parse.py new file mode 120000 index 00000000..0e3506c2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/parse.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/bf/dc/a6/a1a7fafefd0b41ec6eed6063bf38b818ca1dcdcda7590f2ef67d1ab869 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/place.py b/venv/lib/python3.10/site-packages/isort/place.py new file mode 120000 index 00000000..946c826c --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/place.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/bb/7a/8d/e6bb7f03621c559e6775c18ca212d9bec456cd5ac97b79e29087fb4bfe \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/profiles.py b/venv/lib/python3.10/site-packages/isort/profiles.py new file mode 120000 index 00000000..2f69c915 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/profiles.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ad/b8/59/8354ddb5cd8a41172c617fa0cf3a59ac1c82d65a3f1b160cb42d369071 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/py.typed b/venv/lib/python3.10/site-packages/isort/py.typed new file mode 120000 index 00000000..05b4099d --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/py.typed @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e3/b0/c4/4298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/pylama_isort.py b/venv/lib/python3.10/site-packages/isort/pylama_isort.py new file mode 120000 index 00000000..b6810c1d --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/pylama_isort.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/bc/d3/fb/8c0c59cbbaf2651e21a2dca7038242cc0c4bb5b6ac4fd018a598a20a59 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/sections.py b/venv/lib/python3.10/site-packages/isort/sections.py new file mode 120000 index 00000000..8ef6a850 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/sections.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/c4/6e/5b/c14e2d38829499e041859e391087e33fc1e00cec7bf7a6cfbc3e73582c \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/settings.py b/venv/lib/python3.10/site-packages/isort/settings.py new file mode 120000 index 00000000..67f69376 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/settings.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/6d/f7/6f/6f412335cea0b411a8de649e28def39ae6c1b5e77cd5a91ce5e57bd0d1 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/setuptools_commands.py b/venv/lib/python3.10/site-packages/isort/setuptools_commands.py new file mode 120000 index 00000000..adc97e29 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/setuptools_commands.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/bb/37/81/7067ab4cab68cbdb47a3b3be8d5798ceade740059f96e383a6647cea3c \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/sorting.py b/venv/lib/python3.10/site-packages/isort/sorting.py new file mode 120000 index 00000000..d7b632f2 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/sorting.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/9b/83/1b/7b5c2ae51b54648a7a82eba1e9d0814550f88a00862d791a4cede5d625 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py b/venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py new file mode 120000 index 00000000..217723c4 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/43/37/c2/aeee885a8e4bfd9619e25826f0fd89d88f959c5b2a7e570aa553eb9b77 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..7a8623b1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/all.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/all.cpython-310.pyc new file mode 100644 index 00000000..2615b832 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/all.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py2.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py2.cpython-310.pyc new file mode 100644 index 00000000..90746e42 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py2.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py27.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py27.cpython-310.pyc new file mode 100644 index 00000000..f7aa2643 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py27.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py3.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py3.cpython-310.pyc new file mode 100644 index 00000000..bfc49366 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py3.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py310.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py310.cpython-310.pyc new file mode 100644 index 00000000..202409aa Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py310.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py311.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py311.cpython-310.pyc new file mode 100644 index 00000000..2970e9a6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py311.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py36.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py36.cpython-310.pyc new file mode 100644 index 00000000..a3ca8229 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py36.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py37.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py37.cpython-310.pyc new file mode 100644 index 00000000..826a0cbf Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py37.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py38.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py38.cpython-310.pyc new file mode 100644 index 00000000..10a7b82b Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py38.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py39.cpython-310.pyc b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py39.cpython-310.pyc new file mode 100644 index 00000000..7f2d68e5 Binary files /dev/null and b/venv/lib/python3.10/site-packages/isort/stdlibs/__pycache__/py39.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/all.py b/venv/lib/python3.10/site-packages/isort/stdlibs/all.py new file mode 120000 index 00000000..3f6474fa --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/all.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/9f/c1/2c/d562ba525ba963256f7f53c38c66e2a27aa2c7e69f0b72e463c9f34dcc \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/py2.py b/venv/lib/python3.10/site-packages/isort/stdlibs/py2.py new file mode 120000 index 00000000..2bc33487 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/py2.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/75/38/16/4daee0833d5cc0df1fb88f5e22cf7ee674e6911c46fee3bad421b07d72 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/py27.py b/venv/lib/python3.10/site-packages/isort/stdlibs/py27.py new file mode 120000 index 00000000..0b913f11 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/py27.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/42/b8/8a/7edb4d487b23691b437d1e565f2b63cdfed78bba7d97188e39c980d893 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/py3.py b/venv/lib/python3.10/site-packages/isort/stdlibs/py3.py new file mode 120000 index 00000000..94777fca --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/py3.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/8d/7b/63/90c3f46bb1cee7dbbdcc7b7dd5dae1f8183e81a7693b44f3de0979c969 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/py310.py b/venv/lib/python3.10/site-packages/isort/stdlibs/py310.py new file mode 120000 index 00000000..3daa7a13 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/py310.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/77/dc/0e/34c0d846b9a107dd0393ef4d39479866a0e7db8da90b77db85934cbd76 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/py311.py b/venv/lib/python3.10/site-packages/isort/stdlibs/py311.py new file mode 120000 index 00000000..87c69816 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/py311.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/f3/2e/b4/4871fcdc5e2eac84a7bba549c64744b8740399e6604d1eacbdb341759e \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/py36.py b/venv/lib/python3.10/site-packages/isort/stdlibs/py36.py new file mode 120000 index 00000000..5a6cc594 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/py36.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/8a/e5/c8/0cb705ad2be230c48e3f83e8296086e41bde32765b16b6afa5db9252cb \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/py37.py b/venv/lib/python3.10/site-packages/isort/stdlibs/py37.py new file mode 120000 index 00000000..3a013818 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/py37.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/74/bc/71/45eac2bdbe0ef6fae27d383929680ed0bddae80401d7785a2bfb52051c \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/py38.py b/venv/lib/python3.10/site-packages/isort/stdlibs/py38.py new file mode 120000 index 00000000..486a19a3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/py38.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/c6/c5/12/5190f95d4617d0f22e7fd0376d2303e1971b7cfcb3a89a82b9d49f85a5 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/stdlibs/py39.py b/venv/lib/python3.10/site-packages/isort/stdlibs/py39.py new file mode 120000 index 00000000..e49c91c9 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/stdlibs/py39.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/b6/55/51/921a03a68349c4977cd3735d4edfe1833ceeba424c91440c82913ebe50 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/utils.py b/venv/lib/python3.10/site-packages/isort/utils.py new file mode 120000 index 00000000..a8f1f1e1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/utils.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/e4/41/19/51f672c967092e4daa9cd17c39b0e26ffa8f538cf0110bd68e929181bd \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/wrap.py b/venv/lib/python3.10/site-packages/isort/wrap.py new file mode 120000 index 00000000..eb478c7f --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/wrap.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/19/ef/d7/f19b5c9ca4a053fb22ba5885bd3572bf4213ccfa29992147b5fbda1070 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/isort/wrap_modes.py b/venv/lib/python3.10/site-packages/isort/wrap_modes.py new file mode 120000 index 00000000..5d6cdf41 --- /dev/null +++ b/venv/lib/python3.10/site-packages/isort/wrap_modes.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/dc/df/a2/ce45c1addf905360b211bab1b8a547bae841fae96acc3605ec357ac0d9 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/AUTHORS.rst b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/AUTHORS.rst new file mode 120000 index 00000000..f4984cbc --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/AUTHORS.rst @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/3a/fd/91/6ca93358cee2b092815b867691ac388490fb22330a7e04ed3c6f0dea19 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/LICENSE b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/LICENSE new file mode 120000 index 00000000..07a3dbb0 --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/LICENSE @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/33/8b/aa/61960205717e80723a54e69b7c072b31e6582841c0161af847e6d38c26 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/METADATA new file mode 120000 index 00000000..7ad0658b --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/METADATA @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/10/11/9d/4a545c8cf051cfb72a46e7a5bd05a393019d5832843e739b2f2d0248ee \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/RECORD new file mode 100644 index 00000000..ddb451b1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/RECORD @@ -0,0 +1,20 @@ +lazy_object_proxy-1.9.0.dist-info/AUTHORS.rst,sha256=Ov2RbKkzWM7isJKBW4Z2kaw4hJD7IjMKfgTtPG8N6hk,324 +lazy_object_proxy-1.9.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +lazy_object_proxy-1.9.0.dist-info/LICENSE,sha256=M4uqYZYCBXF-gHI6VOabfAcrMeZYKEHAFhr4R-bTjCY,1330 +lazy_object_proxy-1.9.0.dist-info/METADATA,sha256=EBGdSlRcjPBRz7cqRuelvQWjkwGdWDKEPnObLy0CSO4,7626 +lazy_object_proxy-1.9.0.dist-info/RECORD,, +lazy_object_proxy-1.9.0.dist-info/WHEEL,sha256=QwjtG__Tr_is1tZdi3L0jvlWC7dBh2fjo2b04csMQFA,225 +lazy_object_proxy-1.9.0.dist-info/top_level.txt,sha256=UNH-FQB-j_8bYqPz3gD90kHvaC42TQqY0thHSnbaa0k,18 +lazy_object_proxy/__init__.py,sha256=hpBDvXtBGrhj9HDaDtwRzx7U2U7By_XssLMW8vcAMr0,413 +lazy_object_proxy/__pycache__/__init__.cpython-310.pyc,, +lazy_object_proxy/__pycache__/_version.cpython-310.pyc,, +lazy_object_proxy/__pycache__/compat.cpython-310.pyc,, +lazy_object_proxy/__pycache__/simple.cpython-310.pyc,, +lazy_object_proxy/__pycache__/slots.cpython-310.pyc,, +lazy_object_proxy/__pycache__/utils.cpython-310.pyc,, +lazy_object_proxy/_version.py,sha256=B5BgVkogV6MI_tCbdfpSbGoouGPH9y7hECyPko-d6Jc,160 +lazy_object_proxy/cext.cpython-310-x86_64-linux-gnu.so,sha256=-A8PcHBuuBxKXFyw7pFfsupskV8CQZypEn-cqFGpG6w,185048 +lazy_object_proxy/compat.py,sha256=E4nvZpzl6xPY0nRrQcMwFQ129uPwqmhWpySv9hDFMOE,148 +lazy_object_proxy/simple.py,sha256=iwR-5eQ7Ul1PJpkEddbYW3eyuOdjqiTgMirwSseLDx8,8700 +lazy_object_proxy/slots.py,sha256=7smcTV9VJxew7n1cZLTTNLcBINXO8OcducgE4Kpv6-0,12261 +lazy_object_proxy/utils.py,sha256=byVtGaK0lAR1i7BdxqScUS1ad-Q_1yUGnG9uyOtNAI4,1213 diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/WHEEL new file mode 120000 index 00000000..710a00f6 --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/WHEEL @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/43/08/ed/1bffd3aff8acd6d65d8b72f48ef9560bb7418767e3a366f4e1cb0c4050 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/top_level.txt new file mode 120000 index 00000000..572d7581 --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy-1.9.0.dist-info/top_level.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/50/d1/fe/15007e8fff1b62a3f3de00fdd241ef682e364d0a98d2d8474a76da6b49 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/__init__.py b/venv/lib/python3.10/site-packages/lazy_object_proxy/__init__.py new file mode 120000 index 00000000..2ae1548b --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/86/90/43/bd7b411ab863f470da0edc11cf1ed4d94ec1cbf5ecb0b316f2f70032bd \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..0e3680ac Binary files /dev/null and b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/_version.cpython-310.pyc b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/_version.cpython-310.pyc new file mode 100644 index 00000000..83c8ae5d Binary files /dev/null and b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/_version.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/compat.cpython-310.pyc b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/compat.cpython-310.pyc new file mode 100644 index 00000000..2d6f5aac Binary files /dev/null and b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/compat.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/simple.cpython-310.pyc b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/simple.cpython-310.pyc new file mode 100644 index 00000000..a6a6f050 Binary files /dev/null and b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/simple.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/slots.cpython-310.pyc b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/slots.cpython-310.pyc new file mode 100644 index 00000000..a1650a70 Binary files /dev/null and b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/slots.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/utils.cpython-310.pyc new file mode 100644 index 00000000..5e3dc3db Binary files /dev/null and b/venv/lib/python3.10/site-packages/lazy_object_proxy/__pycache__/utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/_version.py b/venv/lib/python3.10/site-packages/lazy_object_proxy/_version.py new file mode 120000 index 00000000..9bb216ef --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy/_version.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/07/90/60/564a2057a308fed09b75fa526c6a28b863c7f72ee1102c8f928f9de897 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/cext.cpython-310-x86_64-linux-gnu.so b/venv/lib/python3.10/site-packages/lazy_object_proxy/cext.cpython-310-x86_64-linux-gnu.so new file mode 120000 index 00000000..d6a64a81 --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy/cext.cpython-310-x86_64-linux-gnu.so @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/f8/0f/0f/70706eb81c4a5c5cb0ee915fb2ea6c915f02419ca9127f9ca851a91bac \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/compat.py b/venv/lib/python3.10/site-packages/lazy_object_proxy/compat.py new file mode 120000 index 00000000..9b3b1a3d --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy/compat.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/13/89/ef/669ce5eb13d8d2746b41c330150d76f6e3f0aa6856a724aff610c530e1 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/simple.py b/venv/lib/python3.10/site-packages/lazy_object_proxy/simple.py new file mode 120000 index 00000000..6507cc3d --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy/simple.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/8b/04/7e/e5e43b525d4f26990475d6d85b77b2b8e763aa24e0322af04ac78b0f1f \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/slots.py b/venv/lib/python3.10/site-packages/lazy_object_proxy/slots.py new file mode 120000 index 00000000..c284c3f5 --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy/slots.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ee/c9/9c/4d5f552717b0ee7d5c64b4d334b70120d5cef0e71db9c804e0aa6febed \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/lazy_object_proxy/utils.py b/venv/lib/python3.10/site-packages/lazy_object_proxy/utils.py new file mode 120000 index 00000000..5832e40f --- /dev/null +++ b/venv/lib/python3.10/site-packages/lazy_object_proxy/utils.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/6f/25/6d/19a2b49404758bb05dc6a49c512d5a77e43fd725069c6f6ec8eb4d008e \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE new file mode 120000 index 00000000..fee8453a --- /dev/null +++ b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/LICENSE @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/10/fb/c0/03cbaf8a6b3cf7195b80dac96c86daf390039b2abf6e7b6e735afd7d74 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA new file mode 120000 index 00000000..0936a883 --- /dev/null +++ b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/METADATA @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a0/cc/54/fdcc387afd90db76132f7351838a5e6c74aa96b6c5fc3492b3e7297c11 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD new file mode 100644 index 00000000..69c4dbda --- /dev/null +++ b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/RECORD @@ -0,0 +1,9 @@ +__pycache__/mccabe.cpython-310.pyc,, +mccabe-0.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +mccabe-0.7.0.dist-info/LICENSE,sha256=EPvAA8uvims89xlbgNrJbIba85ADmyq_bntuc1r9fXQ,1221 +mccabe-0.7.0.dist-info/METADATA,sha256=oMxU_cw4ev2Q23YTL3NRg4pebHSqlrbF_DSSs-cpfBE,5035 +mccabe-0.7.0.dist-info/RECORD,, +mccabe-0.7.0.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110 +mccabe-0.7.0.dist-info/entry_points.txt,sha256=N2NH182GXTUyTm8r8XMgadb9C-CRa5dUr1k8OC91uGE,47 +mccabe-0.7.0.dist-info/top_level.txt,sha256=21cXuqZE-lpcfAqqANvX9EjI1ED1p8zcViv064u3RKA,7 +mccabe.py,sha256=g_kB8oPilNLemdOirPaZymQyyjqAH0kowrncUQaaw00,10654 diff --git a/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL new file mode 120000 index 00000000..ae483c49 --- /dev/null +++ b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/WHEEL @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/cf/d8/f4/c406bf26650a3299b3ef62b464600b48cfe7fb04159866e5797c765478 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt new file mode 120000 index 00000000..175060d0 --- /dev/null +++ b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/entry_points.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/37/63/47/d7cd865d35324e6f2bf1732069d6fd0be0916b9754af593c382f75b861 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt new file mode 120000 index 00000000..ea6a5c09 --- /dev/null +++ b/venv/lib/python3.10/site-packages/mccabe-0.7.0.dist-info/top_level.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/db/57/17/baa644fa5a5c7c0aaa00dbd7f448c8d440f5a7ccdc562bf4eb8bb744a0 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/mccabe.py b/venv/lib/python3.10/site-packages/mccabe.py new file mode 120000 index 00000000..99d05698 --- /dev/null +++ b/venv/lib/python3.10/site-packages/mccabe.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/83/f9/01/f283e294d2de99d3a2acf699ca6432ca3a801f4928c2b9dc51069ac34d \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/LICENSE b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/LICENSE new file mode 120000 index 00000000..13a82092 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/LICENSE @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/f7/72/29/5e81af3478d34e88e52d076200230ec7dd6a39e123bc5cb35aa66abdae \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/METADATA new file mode 120000 index 00000000..43ac1ca8 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/METADATA @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/78/f3/72/a6a96971fdbe0a53bcf21c764ace0370f98c64044a57105d6e47525a92 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/RECORD new file mode 100644 index 00000000..fcf4411d --- /dev/null +++ b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/RECORD @@ -0,0 +1,10 @@ +../../../bin/pycodestyle,sha256=wiLQp6983B4nR35BvVWUlRPK80csWxTG7hY0Ym5DTbI,216 +__pycache__/pycodestyle.cpython-310.pyc,, +pycodestyle-2.10.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pycodestyle-2.10.0.dist-info/LICENSE,sha256=93IpXoGvNHjTTojlLQdiACMOx91qOeEjvFyzWqZqva4,1254 +pycodestyle-2.10.0.dist-info/METADATA,sha256=ePNypqlpcf2-ClO88hx2Ss4DcPmMZARKVxBdbkdSWpI,31580 +pycodestyle-2.10.0.dist-info/RECORD,, +pycodestyle-2.10.0.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110 +pycodestyle-2.10.0.dist-info/entry_points.txt,sha256=MwLE0guIt64aEHtZzaErZQwFVsR4U1jmzn1L_C3RfXo,50 +pycodestyle-2.10.0.dist-info/top_level.txt,sha256=rHbIEiXmvsJ016mFcLVcF_d-dKgP3VdfOB6CWbivZug,12 +pycodestyle.py,sha256=ol8I_ngm_d7q7ruAFSEfKOnVYXvaGXxK0VZqKW32HQQ,102313 diff --git a/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/WHEEL new file mode 120000 index 00000000..ae483c49 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/WHEEL @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/cf/d8/f4/c406bf26650a3299b3ef62b464600b48cfe7fb04159866e5797c765478 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/entry_points.txt new file mode 120000 index 00000000..b16c9905 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/entry_points.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/33/02/c4/d20b88b7ae1a107b59cda12b650c0556c4785358e6ce7d4bfc2dd17d7a \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/top_level.txt new file mode 120000 index 00000000..5403445a --- /dev/null +++ b/venv/lib/python3.10/site-packages/pycodestyle-2.10.0.dist-info/top_level.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/ac/76/c8/1225e6bec274d7a98570b55c17f77e74a80fdd575f381e8259b8af66e8 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pycodestyle.py b/venv/lib/python3.10/site-packages/pycodestyle.py new file mode 120000 index 00000000..2691ff39 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pycodestyle.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a2/5f/08/fe7826fddeeaeebb8015211f28e9d5617bda197c4ad1566a296df61d04 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/CONTRIBUTORS.txt b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/CONTRIBUTORS.txt new file mode 100644 index 00000000..59af3e05 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/CONTRIBUTORS.txt @@ -0,0 +1,609 @@ +# This file is autocompleted by 'contributors-txt', +# using the configuration in 'script/.contributors_aliases.json'. +# Do not add new persons manually and only add information without +# using '-' as the line first character. +# Please verify that your change are stable if you modify manually. + +Ex-maintainers +-------------- +- Claudiu Popa +- Sylvain Thénault : main author / maintainer +- Torsten Marek + + +Maintainers +----------- +- Pierre Sassoulas +- Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> +- Marc Mueller <30130371+cdce8p@users.noreply.github.com> +- Jacob Walls +- Hippo91 +- Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> +- Andreas Finkler <3929834+DudeNr33@users.noreply.github.com> +- Matus Valo +- Dani Alcala <112832187+clavedeluna@users.noreply.github.com> +- Łukasz Rogalski +- Ashley Whetter +- Bryce Guinta +- Yu Shao, Pang <36848472+yushao2@users.noreply.github.com> +- Nick Drozd : performance improvements to astroid +- Dimitri Prybysh + * multiple-imports, not-iterable, not-a-mapping, various patches. +- Roy Williams (Lyft) + * added check for implementing __eq__ without implementing __hash__, + * Added Python 3 check for accessing Exception.message. + * Added Python 3 check for calling encode/decode with invalid codecs. + * Added Python 3 check for accessing sys.maxint. + * Added Python 3 check for bad import statements. + * Added Python 3 check for accessing deprecated methods on the 'string' module, + various patches. +- Florian Bruhin +- Arianna Yang + + +Contributors +------------ + +We would not be here without folks that contributed patches, pull requests, +issues and their time to pylint. We're incredibly grateful to all of these +contributors: + +- Emile Anclin (Logilab): python 3 support +- Michal Nowikowski : + * wrong-spelling-in-comment + * wrong-spelling-in-docstring + * parallel execution on multiple CPUs +- Julthep Nandakwang +- Bruno Daniel : check_docs extension. +- Sushobhit <31987769+sushobhit27@users.noreply.github.com> (sushobhit27) + * Added new check 'comparison-with-itself'. + * Added new check 'useless-import-alias'. + * Added support of annotations in missing-type-doc and missing-return-type-doc. + * Added new check 'comparison-with-callable'. + * Removed six package dependency. + * Added new check 'chained-comparison'. + * Added new check 'useless-object-inheritance'. +- Brett Cannon : + * Port source code to be Python 2/3 compatible + * Python 3 checker +- Laura Médioni (Logilab, on behalf of the CNES): + * misplaced-comparison-constant + * no-classmethod-decorator + * no-staticmethod-decorator + * too-many-nested-blocks, + * too-many-boolean-expressions + * unneeded-not + * wrong-import-order + * ungrouped-imports, + * wrong-import-position + * redefined-variable-type +- Harutaka Kawamura +- Alexandre Fayolle (Logilab): TkInter gui, documentation, debian support +- Ville Skyttä +- Julien Cristau (Logilab): python 3 support +- Adrien Di Mascio +- Moisés López (Vauxoo): + * Support for deprecated-modules in modules not installed, + * Refactor wrong-import-order to integrate it with `isort` library + * Add check too-complex with mccabe for cyclomatic complexity + * Refactor wrong-import-position to skip try-import and nested cases + * Add consider-merging-isinstance, superfluous-else-return + * Fix consider-using-ternary for 'True and True and True or True' case + * Add bad-docstring-quotes and docstring-first-line-empty + * Add missing-timeout +- Frank Harrison (doublethefish) +- Pierre-Yves David +- David Shea : invalid sequence and slice index +- Gunung P. Wibisono <55311527+gunungpw@users.noreply.github.com> +- Derek Gustafson +- Cezar Elnazli : deprecated-method +- Joseph Young <80432516+jpy-git@users.noreply.github.com> (jpy-git) +- Tim Martin +- Ollie <46904826+ollie-iterators@users.noreply.github.com> +- Tushar Sadhwani (tusharsadhwani) +- Nicolas Chauvat +- orSolocate <38433858+orSolocate@users.noreply.github.com> +- Radu Ciorba : not-context-manager and confusing-with-statement warnings. +- Holger Peters +- Cosmin Poieană : unichr-builtin and improvements to bad-open-mode. +- Steven Myint : duplicate-except. +- Peter Kolbus (Garmin) +- Luigi Bertaco Cristofolini (luigibertaco) +- Glenn Matthews : + * autogenerated documentation for optional extensions, + * bug fixes and enhancements for docparams (née check_docs) extension +- Vlad Temian : redundant-unittest-assert and the JSON reporter. +- Julien Jehannet +- Boris Feld +- Anthony Sottile +- Pedro Algarvio (s0undt3ch) +- Julien Palard +- David Liu (david-yz-liu) +- Dan Goldsmith : support for msg-template in HTML reporter. +- Buck Evan +- Zen Lee <53538590+zenlyj@users.noreply.github.com> +- Robert Hofer +- Mariatta Wijaya + * Added new check `logging-fstring-interpolation` + * Documentation typo fixes +- Jakub Wilk +- Eli Fine (eli88fine): Fixed false positive duplicate code warning for lines with symbols only +- Andrew Haigh (nelfin) +- Émile Crater +- Pavel Roskin +- David Gilman +- へーさん +- Yilei "Dolee" Yang +- Thomas Hisch +- Marianna Polatoglou : minor contribution for wildcard import check +- Manuel Vázquez Acosta +- Luis Escobar (Vauxoo): Add bad-docstring-quotes and docstring-first-line-empty +- Konstantina Saketou <56515303+ksaketou@users.noreply.github.com> +- Konstantin +- Jim Robertson +- Hugo van Kemenade +- Ethan Leba +- Enji Cooper +- Drum Ogilvie +- David Lindquist : logging-format-interpolation warning. +- Daniel Harding +- Anthony Truchet +- Alexander Todorov : + * added new error conditions to 'bad-super-call', + * Added new check for incorrect len(SEQUENCE) usage, + * Added new extension for comparison against empty string constants, + * Added new extension which detects comparing integers to zero, + * Added new useless-return checker, + * Added new try-except-raise checker +- Téo Bouvard +- Mihai Balint +- Mark Bell +- Levi Gruspe +- Jakub Kuczys +- Hornwitser : fix import graph +- Fureigh +- David Douard +- Daniel Balparda (Google): GPyLint maintainer (Google's pylint variant) +- Bastien Vallet (Djailla) +- Aru Sahni : Git ignoring, regex-based ignores +- Andreas Freimuth : fix indentation checking with tabs +- Alexandru Coman +- jpkotta +- Takahide Nojima +- Taewon D. Kim +- Stavros Ntentos <133706+stdedos@users.noreply.github.com> +- Sneaky Pete +- Sergey B Kirpichev +- Sandro Tosi : Debian packaging +- Rene Zhang +- Paul Lichtenberger +- Or Bahari +- Mr. Senko +- Mike Frysinger +- Martin von Gagern (Google): Added 'raising-format-tuple' warning. +- Martin Vielsmaier +- Martin Pool (Google): + * warnings for anomalous backslashes + * symbolic names for messages (like 'unused') + * etc. +- Martin Bašti + * Added new check for shallow copy of os.environ + * Added new check for useless `with threading.Lock():` statement +- Marcus Näslund (naslundx) +- Marco Pernigotti <7657251+mpernigo@users.noreply.github.com> +- Marco Forte +- James Addison <55152140+jayaddison@users.noreply.github.com> +- Ionel Maries Cristian +- Gergely Kalmár +- Damien Baty +- Benjamin Drung : contributing Debian Developer +- Anubhav <35621759+anubh-v@users.noreply.github.com> +- Antonio Quarta +- Andrew J. Simmons +- Alexey Pelykh +- wtracy +- jessebrennan +- chohner +- Tiago Honorato <61059243+tiagohonorato@users.noreply.github.com> +- Steven M. Vascellaro +- Robin Tweedie <70587124+robin-wayve@users.noreply.github.com> +- Roberto Leinardi : PyCharm plugin maintainer +- Ricardo Gemignani +- Pieter Engelbrecht +- Philipp Albrecht (pylbrecht) +- Nicolas Dickreuter +- Nick Bastin +- Nathaniel Manista : suspicious lambda checking +- Maksym Humetskyi (mhumetskyi) + * Fixed ignored empty functions by similarities checker with "ignore-signatures" option enabled + * Ignore function decorators signatures as well by similarities checker with "ignore-signatures" option enabled + * Ignore class methods and nested functions signatures as well by similarities checker with "ignore-signatures" option enabled +- Lucas Cimon +- Kylian +- Konstantin Manna +- Kai Mueller <15907922+kasium@users.noreply.github.com> +- Joshua Cannon +- John Leach +- James Morgensen : ignored-modules option applies to import errors. +- Jaehoon Hwang (jaehoonhwang) +- Huw Jones +- Gideon <87426140+GideonBear@users.noreply.github.com> +- Ganden Schaffner +- Frost Ming +- Federico Bond +- Erik Wright +- Erik Eriksson : Added overlapping-except error check. +- Dan Hemberger <846186+hemberger@users.noreply.github.com> +- Chris Rebert : unidiomatic-typecheck. +- Aurelien Campeas +- Alexander Pervakov +- Alain Leufroy +- Adam Williamson +- xmo-odoo +- tbennett0 +- omarandlorraine <64254276+omarandlorraine@users.noreply.github.com> +- craig-sh +- bernie gray +- Wes Turner (Google): added new check 'inconsistent-quotes' +- Tyler Thieding +- Tobias Hernstig <30827238+thernstig@users.noreply.github.com> +- Thomas Grainger +- Smixi +- Simu Toni +- Sergei Lebedev <185856+superbobry@users.noreply.github.com> +- Scott Worley +- Saugat Pachhai +- Rémi Cardona +- Rogdham +- Raphael Gaschignard +- Ram Rachum (cool-RR) +- Radostin Stoyanov +- Peter Bittner +- Paul Renvoisé +- PHeanEX +- Omega Weapon +- Nikolai Kristiansen +- Nick Pesce +- Nathan Marrow +- Mikhail Fesenko +- Matthew Suozzo +- Matthew Beckers <17108752+mattlbeck@users.noreply.github.com> (mattlbeck) +- Mark Roman Miller : fix inline defs in too-many-statements +- MalanB +- Mads Kiilerich +- Maarten ter Huurne +- Lefteris Karapetsas +- LCD 47 +- Justin Li +- John Kirkham +- Jens H. Nielsen +- Ioana Tagirta : fix bad thread instantiation check +- Ikraduya Edian : Added new checks 'consider-using-generator' and 'use-a-generator'. +- Hugues Bruant +- Harut +- Grygorii Iermolenko +- Grizzly Nyo +- Gabriel R. Sezefredo : Fixed "exception-escape" false positive with generators +- Filipe Brandenburger +- Fantix King (UChicago) +- Eric McDonald <221418+emcd@users.noreply.github.com> +- Elias Dorneles : minor adjust to config defaults and docs +- Derek Harland +- David Pursehouse +- Dave Bunten +- Daniel Mouritzen +- Daniel Miller +- Christoph Blessing <33834216+cblessing24@users.noreply.github.com> +- Chris Murray +- Chris Lamb +- Charles Hebert +- Carli Freudenberg (CarliJoy) + * Fixed issue 5281, added Unicode checker + * Improve non-ascii-name checker +- Bruce Dawson +- Brian Shaginaw : prevent error on exception check for functions +- Benny Mueller +- Ben James +- Ben Green +- Batuhan Taskaya +- Alvaro Frias +- Alexander Kapshuna +- Adam Parkin +- 谭九鼎 <109224573@qq.com> +- Łukasz Sznuk +- y2kbugger +- vinnyrose +- ttenhoeve-aa +- thinwybk +- syutbai +- sur.la.route <17788706+christopherpickering@users.noreply.github.com> +- sdet_liang +- paschich +- oittaa <8972248+oittaa@users.noreply.github.com> +- nyabkun <75878387+nyabkun@users.noreply.github.com> +- moxian +- mar-chi-pan +- lrjball <50599110+lrjball@users.noreply.github.com> +- laike9m +- kriek +- jaydesl <35102795+jaydesl@users.noreply.github.com> +- jab +- glmdgrielson <32415403+glmdgrielson@users.noreply.github.com> +- glegoux +- gaurikholkar +- flyingbot91 +- fly +- fahhem +- fadedDexofan +- epenet <6771947+epenet@users.noreply.github.com> +- danields +- cosven +- cordis-dev +- bluesheeptoken +- anatoly techtonik +- agutole +- Zeckie <49095968+Zeckie@users.noreply.github.com> +- Zeb Nicholls + * Made W9011 compatible with 'of' syntax in return types +- Yuval Langer +- Yury Gribov +- Yuri Bochkarev : Added epytext support to docparams extension. +- Youngsoo Sung +- Yory <39745367+yory8@users.noreply.github.com> +- Yoichi Nakayama +- Yeting Li (yetingli) +- Yannack +- Yann Dirson +- Yang Yang +- Xi Shen +- Will Shanks +- Viorel Știrbu : intern-builtin warning. +- VictorT +- Victor Jiajunsu <16359131+jiajunsu@users.noreply.github.com> +- Trevor Bekolay + * Added --list-msgs-enabled command +- Tomer Chachamu : simplifiable-if-expression +- Tomasz Magulski +- Tom +- Tim Hatch +- Tim Gates +- Thomas Benhamou +- Tanvi Moharir <74228962+tanvimoharir@users.noreply.github.com>: Fix for invalid toml config +- T.Rzepka +- Svetoslav Neykov +- Stéphane Wirtel : nonlocal-without-binding +- Stephen Longofono <8992396+SLongofono@users.noreply.github.com> +- Stanislav Levin +- Sorin Sbarnea +- Slavfox +- Skip Montanaro +- Sigurd Spieckermann <2206639+sisp@users.noreply.github.com> +- Shiv Venkatasubrahmanyam +- Sebastian Müller +- Sasha Bagan +- Sardorbek Imomaliev +- Santiago Castro +- Samuel Freilich (sfreilich) +- Samuel FORESTIER +- Sam Vermeiren <88253337+PaaEl@users.noreply.github.com> +- Ryan McGuire +- Ry4an Brase +- Ruro +- Roman Ivanov +- Robert Schweizer +- Reverb Chu +- Renat Galimov +- Rebecca Turner (9999years) +- Randall Leeds +- Ramon Saraiva +- Ramiro Leal-Cavazos (ramiro050): Fixed bug preventing pylint from working with Emacs tramp +- R. N. West <98110034+rnwst@users.noreply.github.com> +- Qwiddle13 <32040075+Qwiddle13@users.noreply.github.com> +- Quentin Young +- Prajwal Borkar +- Petr Pulc : require whitespace around annotations +- Peter Dawyndt +- Peter Dave Hello +- Peter Aronoff +- Paul Cochrane +- Patrik +- Pascal Corpet +- Pablo Galindo Salgado + * Fix false positive 'Non-iterable value' with async comprehensions. +- Osher De Paz +- Oisín Moran +- Obscuron +- Noam Yorav-Raphael +- Nir Soffer +- Niko Wenselowski +- Nikita Sobolev +- Nick Smith +- Ned Batchelder +- Natalie Serebryakova +- Moody +- Mitchell Young : minor adjustment to docparams +- Mitar +- Mike Fiedler (miketheman) +- Mike Bryant +- Michka Popoff +- Michal Vasilek +- Michael Scott Cuthbert +- Michael Kefeder +- Michael Hudson-Doyle +- Michael Giuffrida +- Melvin Hazeleger <31448155+melvio@users.noreply.github.com> +- Matěj Grabovský +- Matthijs Blom <19817960+MatthijsBlom@users.noreply.github.com> +- Matej Marušák +- Markus Siebenhaar <41283549+siehar@users.noreply.github.com> +- Marco Edward Gorelli : Documented Jupyter integration +- Marcin Kurczewski (rr-) +- Maik Röder +- Lumír 'Frenzy' Balhar +- Ludovic Aubry +- Louis Sautier +- Lorena Buciu <46202743+lorena-b@users.noreply.github.com> +- Logan Miller <14319179+komodo472@users.noreply.github.com> +- Kári Tristan Helgason +- Kurian Benoy <70306694+kurianbenoy-aot@users.noreply.github.com> +- Krzysztof Czapla +- Kraig Brockschmidt +- Kound +- KotlinIsland <65446343+KotlinIsland@users.noreply.github.com> +- Kosarchuk Sergey +- Konrad Weihmann <46938494+priv-kweihmann@users.noreply.github.com> +- Kian Meng, Ang +- Kevin Phillips +- Kevin Jing Qiu +- Kayran Schmidt <59456929+yumasheta@users.noreply.github.com> +- Karthik Nadig +- Jürgen Hermann +- Jérome Perrin +- Josselin Feist +- Jonathan Kotta +- John Paraskevopoulos : add 'differing-param-doc' and 'differing-type-doc' +- John McGehee +- John Gabriele +- John Belmonte +- Joffrey Mander +- Jochen Preusche +- Jeroen Seegers : + * Fixed `toml` dependency issue +- Jeremy Fleischman +- Jason Owen +- Jared Garst +- Jared Deckard +- Janne Rönkkö +- James Sinclair +- James M. Allen +- James Lingard +- James Broadhead +- Jakub Kulík +- Jakob Normark +- Jake Lishman +- Jacques Kvam +- Jace Browning : updated default report format with clickable paths +- JT Olds +- Hayden Richards <62866982+SupImDos@users.noreply.github.com> + * Fixed "no-self-use" for async methods + * Fixed "docparams" extension for async functions and methods +- Harshil <37377066+harshil21@users.noreply.github.com> +- Harry +- Grégoire <96051754+gregoire-mullvad@users.noreply.github.com> +- Grant Welch +- Giuseppe Valente +- Gary Tyler McLeod +- Felix von Drigalski +- Fabrice Douchant +- Fabio Natali +- Fabian Damken +- Eric Froemling +- Emmanuel Chaudron +- Elizabeth Bott <52465744+elizabethbott@users.noreply.github.com> +- Eisuke Kawashima +- Edward K. Ream +- Edgemaster +- Eddie Darling +- Drew Risinger +- Dr. Nick +- Don Jayamanne +- Dmytro Kyrychuk +- DetachHead <57028336+DetachHead@users.noreply.github.com> +- Denis Laxalde +- David Lawson +- David Cain +- Danny Hermes +- Daniele Procida +- Daniela Plascencia +- Daniel Werner +- Daniel R. Neal (danrneal) +- Daniel Draper +- Daniel Dorani (doranid) +- Daniel Brookman <53625739+dbrookman@users.noreply.github.com> +- Dan Garrette +- Damien Nozay +- Cubicpath +- Craig Citro +- Cosmo +- Clément Pit-Claudel +- Christopher Zurcher +- Christian Clauss +- Carl Crowder : don't evaluate the value of arguments for 'dangerous-default-value' +- Carey Metcalfe : demoted `try-except-raise` from error to warning +- Cameron Olechowski +- Calin Don +- Caio Carrara +- C.A.M. Gerlach +- Bruno P. Kinoshita +- Brice Chardin +- Brian C. Lane +- Brandon W Maister +- BioGeek +- Benjamin Graham +- Benedikt Morbach +- Ben Greiner +- Banjamin Freeman +- Athos Ribeiro : Fixed dict-keys-not-iterating false positive for inverse containment checks +- Arun Persaud +- Arthur Lutz +- Antonio Ossa +- Anthony VEREZ +- Anthony Tan +- Anthony Foglia (Google): Added simple string slots check. +- Anentropic +- Andy Young +- Andy Palmer <25123779+ninezerozeronine@users.noreply.github.com> +- Andrzej Klajnert +- Andrew Howe +- Andres Perez Hortal +- Andre Hora +- Alok Singh <8325708+alok@users.noreply.github.com> +- Allan Chandler <95424144+allanc65@users.noreply.github.com> (allanc65) + * Fixed issue 5452, false positive missing-param-doc for multi-line Google-style params +- Alex Jurkiewicz +- Alex Hearn +- Alan Evangelista +- Alan Chan +- Aivar Annamaa +- Aidan Haase <44787650+haasea@users.noreply.github.com> +- Ahirnish Pareek : 'keyword-arg-before-var-arg' check +- Adrian Chirieac +- Aditya Gupta (adityagupta1089) + * Added ignore_signatures to duplicate checker +- Adam Dangoor +- 243f6a88 85a308d3 <33170174+243f6a8885a308d313198a2e037@users.noreply.github.com> + + +Co-Author +--------- +The following persons were credited manually but did not commit themselves +under this name, or we did not manage to find their commits in the history. + +- Agustin Toledo +- Amaury Forgeot d'Arc: check names imported from a module exists in the module +- Anthony Tan +- Axel Muller +- Benjamin Niemann: allow block level enabling/disabling of messages +- Bernard Nauwelaerts +- Bill Wendling +- Brian van den Broek: windows installation documentation +- Craig Henriques +- D. Alphus (Alphadelta14) +- Daniil Kharkov +- Eero Vuojolahti +- Fabio Zadrozny +- Gauthier Sebaux +- James DesLauriers +- manderj +- Mirko Friedenhagen +- Nicholas Smith +- Nuzula H. Yudaka (Nuzhuka) +- Pek Chhan +- Peter Hammond +- Pierre Rouleau +- Richard Goodman: simplifiable-if-expression (with Tomer Chachamu) +- Sebastian Ulrich +- Takashi Hirashima +- Thomas Snowden: fix missing-docstring for inner functions +- Wolfgang Grafen +- Yannick Brehon diff --git a/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/INSTALLER b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/LICENSE b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/LICENSE new file mode 120000 index 00000000..d25752b4 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/LICENSE @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/f9/7b/14/080de8b8490d60eb3d620ebc419943e0779466d1dd0d5d6f68fe195dcd \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/METADATA b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/METADATA new file mode 100644 index 00000000..9e8cb4be --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/METADATA @@ -0,0 +1,275 @@ +Metadata-Version: 2.1 +Name: pylint +Version: 2.17.0 +Summary: python code static checker +Author-email: Python Code Quality Authority +License: GPL-2.0-or-later +Project-URL: Docs: User Guide, https://pylint.readthedocs.io/en/latest/ +Project-URL: Source Code, https://github.com/PyCQA/pylint +Project-URL: homepage, https://github.com/PyCQA/pylint +Project-URL: What's New, https://pylint.readthedocs.io/en/latest/whatsnew/2/ +Project-URL: Bug Tracker, https://github.com/PyCQA/pylint/issues +Project-URL: Discord Server, https://discord.com/invite/Egy6P8AMB5 +Project-URL: Docs: Contributor Guide, https://pylint.readthedocs.io/en/latest/development_guide/contributor_guide/index.html +Keywords: static code analysis,linter,python,lint +Classifier: Development Status :: 6 - Mature +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU General Public License v2 (GPLv2) +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Debuggers +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Topic :: Software Development :: Testing +Requires-Python: >=3.7.2 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: CONTRIBUTORS.txt +Requires-Dist: platformdirs (>=2.2.0) +Requires-Dist: astroid (<=2.17.0-dev0,>=2.15.0) +Requires-Dist: isort (<6,>=4.2.5) +Requires-Dist: mccabe (<0.8,>=0.6) +Requires-Dist: tomlkit (>=0.10.1) +Requires-Dist: typing-extensions (>=3.10.0) ; python_version < "3.10" +Requires-Dist: dill (>=0.2) ; python_version < "3.11" +Requires-Dist: tomli (>=1.1.0) ; python_version < "3.11" +Requires-Dist: dill (>=0.3.6) ; python_version >= "3.11" +Requires-Dist: colorama (>=0.4.5) ; sys_platform == "win32" +Provides-Extra: spelling +Requires-Dist: pyenchant (~=3.2) ; extra == 'spelling' +Provides-Extra: testutils +Requires-Dist: gitpython (>3) ; extra == 'testutils' + +`Pylint`_ +========= + +.. _`Pylint`: https://pylint.readthedocs.io/ + +.. This is used inside the doc to recover the start of the introduction + +.. image:: https://github.com/PyCQA/pylint/actions/workflows/tests.yaml/badge.svg?branch=main + :target: https://github.com/PyCQA/pylint/actions + +.. image:: https://codecov.io/gh/PyCQA/pylint/branch/main/graph/badge.svg?token=ZETEzayrfk + :target: https://codecov.io/gh/PyCQA/pylint + +.. image:: https://img.shields.io/pypi/v/pylint.svg + :alt: Pypi Package version + :target: https://pypi.python.org/pypi/pylint + +.. image:: https://readthedocs.org/projects/pylint/badge/?version=latest + :target: https://pylint.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + +.. image:: https://img.shields.io/badge/linting-pylint-yellowgreen + :target: https://github.com/PyCQA/pylint + +.. image:: https://results.pre-commit.ci/badge/github/PyCQA/pylint/main.svg + :target: https://results.pre-commit.ci/latest/github/PyCQA/pylint/main + :alt: pre-commit.ci status + +.. image:: https://bestpractices.coreinfrastructure.org/projects/6328/badge + :target: https://bestpractices.coreinfrastructure.org/projects/6328 + :alt: CII Best Practices + +.. image:: https://api.securityscorecards.dev/projects/github.com/PyCQA/pylint/badge + :target: https://api.securityscorecards.dev/projects/github.com/PyCQA/pylint + :alt: OpenSSF Scorecard + +.. image:: https://img.shields.io/discord/825463413634891776.svg + :target: https://discord.gg/qYxpadCgkx + :alt: Discord + +What is Pylint? +--------------- + +Pylint is a `static code analyser`_ for Python 2 or 3. The latest version supports Python +3.7.2 and above. + +.. _`static code analyser`: https://en.wikipedia.org/wiki/Static_code_analysis + +Pylint analyses your code without actually running it. It checks for errors, enforces a +coding standard, looks for `code smells`_, and can make suggestions about how the code +could be refactored. + +.. _`code smells`: https://martinfowler.com/bliki/CodeSmell.html + +Install +------- + +.. This is used inside the doc to recover the start of the short text for installation + +For command line use, pylint is installed with:: + + pip install pylint + +Or if you want to also check spelling with ``enchant`` (you might need to +`install the enchant C library `_): + +.. code-block:: sh + + pip install pylint[spelling] + +It can also be integrated in most editors or IDEs. More information can be found +`in the documentation`_. + +.. _in the documentation: https://pylint.readthedocs.io/en/latest/user_guide/installation/index.html + +.. This is used inside the doc to recover the end of the short text for installation + +What differentiate Pylint? +-------------------------- + +Pylint is not trusting your typing and is inferring the actual value of nodes (for a +start because there was no typing when pylint started off) using its internal code +representation (astroid). If your code is ``import logging as argparse``, Pylint +can check and know that ``argparse.error(...)`` is in fact a logging call and not an +argparse call. This makes pylint slower, but it also let pylint find more issues if +your code is not fully typed. + + [inference] is the killer feature that keeps us using [pylint] in our project despite how painfully slow it is. + - `Realist pylint user`_, 2022 + +.. _`Realist pylint user`: https://github.com/charliermarsh/ruff/issues/970#issuecomment-1381067064 + +pylint, not afraid of being a little slower than it already is, is also a lot more thorough than other linters. +There is more checks, some opinionated one that are deactivated by default, but can be enabled using configuration. + +How to use pylint +----------------- + +Pylint isn't smarter than you: it may warn you about things that you have +conscientiously done or check for some things that you don't care about. +During adoption, especially in a legacy project where pylint was never enforced, +it's best to start with the ``--errors-only`` flag, then disable +convention and refactor message with ``--disable=C,R`` and progressively +re-evaluate and re-enable messages as your priorities evolve. + +Pylint is highly configurable and permits to write plugins in order to add your +own checks (for example, for internal libraries or an internal rule). Pylint also has an +ecosystem of existing plugins for popular frameworks and third party libraries. + +.. note:: + + Pylint supports the Python standard library out of the box. Third-party + libraries are not always supported, so a plugin might be needed. A good place + to start is ``PyPI`` which often returns a plugin by searching for + ``pylint ``. `pylint-pydantic`_, `pylint-django`_ and + `pylint-sonarjson`_ are examples of such plugins. More information about plugins + and how to load them can be found at `plugins`_. + +.. _`plugins`: https://pylint.readthedocs.io/en/latest/development_guide/how_tos/plugins.html#plugins +.. _`pylint-pydantic`: https://pypi.org/project/pylint-pydantic +.. _`pylint-django`: https://github.com/PyCQA/pylint-django +.. _`pylint-sonarjson`: https://github.com/omegacen/pylint-sonarjson + +Advised linters alongside pylint +-------------------------------- + +Projects that you might want to use alongside pylint include ruff_ (**really** fast, +with builtin auto-fix, and a growing number of checks taken from popular +linters but implemented in ``rust``) or flake8_ (faster and simpler checks with very few false positives), +mypy_, pyright_ or pyre_ (typing checks), bandit_ (security oriented checks), black_ and +isort_ (auto-formatting), autoflake_ (automated removal of unused imports or variables), +pyupgrade_ (automated upgrade to newer python syntax) and pydocstringformatter_ (automated pep257). + +.. _ruff: https://github.com/charliermarsh/ruff +.. _flake8: https://github.com/PyCQA/flake8 +.. _bandit: https://github.com/PyCQA/bandit +.. _mypy: https://github.com/python/mypy +.. _pyright: https://github.com/microsoft/pyright +.. _pyre: https://github.com/facebook/pyre-check +.. _black: https://github.com/psf/black +.. _autoflake: https://github.com/myint/autoflake +.. _pyupgrade: https://github.com/asottile/pyupgrade +.. _pydocstringformatter: https://github.com/DanielNoord/pydocstringformatter +.. _isort: https://pycqa.github.io/isort/ + +Additional tools included in pylint +----------------------------------- + +Pylint ships with two additional tools: + +- pyreverse_ (standalone tool that generates package and class diagrams.) +- symilar_ (duplicate code finder that is also integrated in pylint) + +.. _pyreverse: https://pylint.readthedocs.io/en/latest/pyreverse.html +.. _symilar: https://pylint.readthedocs.io/en/latest/symilar.html + + +.. This is used inside the doc to recover the end of the introduction + +Contributing +------------ + +.. This is used inside the doc to recover the start of the short text for contribution + +We welcome all forms of contributions such as updates for documentation, new code, checking issues for duplicates or telling us +that we can close them, confirming that issues still exist, `creating issues because +you found a bug or want a feature`_, etc. Everything is much appreciated! + +Please follow the `code of conduct`_ and check `the Contributor Guides`_ if you want to +make a code contribution. + +.. _creating issues because you found a bug or want a feature: https://pylint.readthedocs.io/en/latest/contact.html#bug-reports-feedback +.. _code of conduct: https://github.com/PyCQA/pylint/blob/main/CODE_OF_CONDUCT.md +.. _the Contributor Guides: https://pylint.readthedocs.io/en/latest/development_guide/contribute.html + +.. This is used inside the doc to recover the end of the short text for contribution + +Show your usage +----------------- + +You can place this badge in your README to let others know your project uses pylint. + + .. image:: https://img.shields.io/badge/linting-pylint-yellowgreen + :target: https://github.com/PyCQA/pylint + +Learn how to add a badge to your documentation in the `the badge documentation`_. + +.. _the badge documentation: https://pylint.readthedocs.io/en/latest/user_guide/installation/badge.html + +License +------- + +pylint is, with a few exceptions listed below, `GPLv2 `_. + +The icon files are licensed under the `CC BY-SA 4.0 `_ license: + +- `doc/logo.png `_ +- `doc/logo.svg `_ + +Support +------- + +Please check `the contact information`_. + +.. _`the contact information`: https://pylint.readthedocs.io/en/latest/contact.html + +.. |tideliftlogo| image:: https://raw.githubusercontent.com/PyCQA/pylint/main/doc/media/Tidelift_Logos_RGB_Tidelift_Shorthand_On-White.png + :width: 200 + :alt: Tidelift + +.. list-table:: + :widths: 10 100 + + * - |tideliftlogo| + - Professional support for pylint is available as part of the `Tidelift + Subscription`_. Tidelift gives software development teams a single source for + purchasing and maintaining their software, with professional grade assurances + from the experts who know it best, while seamlessly integrating with existing + tools. + +.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-pylint?utm_source=pypi-pylint&utm_medium=referral&utm_campaign=readme diff --git a/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/RECORD b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/RECORD new file mode 100644 index 00000000..33f142ca --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/RECORD @@ -0,0 +1,383 @@ +../../../bin/epylint,sha256=WXnmMsLkfwTeNSclIhurpKoJhaw6akFfawqRlj9No6M,223 +../../../bin/pylint,sha256=_2VAon42GsU40B4qYbwk5Sn8LDdXcg8YUJ5IEDsxLqs,221 +../../../bin/pylint-config,sha256=r7-sx-S5CBRfvKrxy69SGmAu9natpHvKCas2BU2Agt4,237 +../../../bin/pyreverse,sha256=wLkWJauT7rmDxKjaABxEFj87a6CjnSq9zcjLxleKHt8,227 +../../../bin/symilar,sha256=I83sfwBpd5jmteH4ZTWcuo5ZDM8sle4-sWyH7K3Goxg,223 +pylint-2.17.0.dist-info/CONTRIBUTORS.txt,sha256=WIOlW055YsXTgxGgRbc_GyTHpQQK2WFi5sseXGOuwzc,28765 +pylint-2.17.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pylint-2.17.0.dist-info/LICENSE,sha256=-XsUCA3ouEkNYOs9Yg68QZlD4HeUZtHdDV1vaP4ZXc0,17984 +pylint-2.17.0.dist-info/METADATA,sha256=mbADTHl0AZGN-jg2sY5IVR_auKqW3bA2Dgp2nmn2uss,12010 +pylint-2.17.0.dist-info/RECORD,, +pylint-2.17.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pylint-2.17.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 +pylint-2.17.0.dist-info/entry_points.txt,sha256=R_YZVGBH-O2g1wyUuQ_VLkFgVfx9p_uQC5oMeQd7cu4,178 +pylint-2.17.0.dist-info/top_level.txt,sha256=j6Z9i__pIuaiCka6Ul9YIy6yI5aw5QbCtldLvZlMksE,7 +pylint/__init__.py,sha256=muhhaAzltLG6voQgcupp2tw3s1Lfh92H0GL2LePJ0ms,3562 +pylint/__main__.py,sha256=m1y5m7Ky8HtyNnE8ftGSnRSTDgv5suq8P-B4V_KD3JY,305 +pylint/__pkginfo__.py,sha256=VB7aJv4aG5O4J185_fSncTt9P7EK-Jx-yhr_QKSPAOU,1340 +pylint/__pycache__/__init__.cpython-310.pyc,, +pylint/__pycache__/__main__.cpython-310.pyc,, +pylint/__pycache__/__pkginfo__.cpython-310.pyc,, +pylint/__pycache__/constants.cpython-310.pyc,, +pylint/__pycache__/epylint.cpython-310.pyc,, +pylint/__pycache__/exceptions.cpython-310.pyc,, +pylint/__pycache__/graph.cpython-310.pyc,, +pylint/__pycache__/interfaces.cpython-310.pyc,, +pylint/__pycache__/typing.cpython-310.pyc,, +pylint/checkers/__init__.py,sha256=kmnHBr0XGOqcFpATa-jB7S6LSyywhaOHmO_lq361zYY,4367 +pylint/checkers/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/__pycache__/async.cpython-310.pyc,, +pylint/checkers/__pycache__/bad_chained_comparison.cpython-310.pyc,, +pylint/checkers/__pycache__/base_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/deprecated.cpython-310.pyc,, +pylint/checkers/__pycache__/design_analysis.cpython-310.pyc,, +pylint/checkers/__pycache__/dunder_methods.cpython-310.pyc,, +pylint/checkers/__pycache__/ellipsis_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/exceptions.cpython-310.pyc,, +pylint/checkers/__pycache__/format.cpython-310.pyc,, +pylint/checkers/__pycache__/imports.cpython-310.pyc,, +pylint/checkers/__pycache__/lambda_expressions.cpython-310.pyc,, +pylint/checkers/__pycache__/logging.cpython-310.pyc,, +pylint/checkers/__pycache__/mapreduce_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/method_args.cpython-310.pyc,, +pylint/checkers/__pycache__/misc.cpython-310.pyc,, +pylint/checkers/__pycache__/modified_iterating_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/nested_min_max.cpython-310.pyc,, +pylint/checkers/__pycache__/newstyle.cpython-310.pyc,, +pylint/checkers/__pycache__/non_ascii_names.cpython-310.pyc,, +pylint/checkers/__pycache__/raw_metrics.cpython-310.pyc,, +pylint/checkers/__pycache__/similar.cpython-310.pyc,, +pylint/checkers/__pycache__/spelling.cpython-310.pyc,, +pylint/checkers/__pycache__/stdlib.cpython-310.pyc,, +pylint/checkers/__pycache__/strings.cpython-310.pyc,, +pylint/checkers/__pycache__/threading_checker.cpython-310.pyc,, +pylint/checkers/__pycache__/typecheck.cpython-310.pyc,, +pylint/checkers/__pycache__/unicode.cpython-310.pyc,, +pylint/checkers/__pycache__/unsupported_version.cpython-310.pyc,, +pylint/checkers/__pycache__/utils.cpython-310.pyc,, +pylint/checkers/__pycache__/variables.cpython-310.pyc,, +pylint/checkers/async.py,sha256=lq-KntL033fmOAPi61taHmre1Lzt9YB5u8COp8i8dDQ,3923 +pylint/checkers/bad_chained_comparison.py,sha256=iPi29sCYJn6HC69qWmUI6UfuKclTyENhkTYrXHL3JAs,2228 +pylint/checkers/base/__init__.py,sha256=ok6VJ5ZgWkMFDKvKnjV4VRxWwkLcce2OgJjbacm1-DU,1568 +pylint/checkers/base/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/base/__pycache__/basic_checker.cpython-310.pyc,, +pylint/checkers/base/__pycache__/basic_error_checker.cpython-310.pyc,, +pylint/checkers/base/__pycache__/comparison_checker.cpython-310.pyc,, +pylint/checkers/base/__pycache__/docstring_checker.cpython-310.pyc,, +pylint/checkers/base/__pycache__/pass_checker.cpython-310.pyc,, +pylint/checkers/base/basic_checker.py,sha256=V2cy5rCLs12-fdOK5-vPgi9XE-wItr7oQ-e7Z1g1QNE,40291 +pylint/checkers/base/basic_error_checker.py,sha256=BeEjhDb8s2r-PUg4ZOk7m2R2tbPVjbmBHmvUWmuJYtE,22514 +pylint/checkers/base/comparison_checker.py,sha256=aCVA4qONEvKfRdXTFhZAtwrblw-Bg7X3UzA2SGJkYJk,13864 +pylint/checkers/base/docstring_checker.py,sha256=PEw0Vt-dVEtMzVGpvEqVq2K_ldafLiHXK8sVbGiQBrc,7915 +pylint/checkers/base/name_checker/__init__.py,sha256=AKe0lfMl3OlPl7JkIJw3FATqC2XoXJ_k6hegPB13CcY,689 +pylint/checkers/base/name_checker/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/base/name_checker/__pycache__/checker.cpython-310.pyc,, +pylint/checkers/base/name_checker/__pycache__/naming_style.cpython-310.pyc,, +pylint/checkers/base/name_checker/checker.py,sha256=xrp4KA9LGnrn_KiP-pjSdDz4AspPZARBo5bKhB5c5TA,26715 +pylint/checkers/base/name_checker/naming_style.py,sha256=RVz52JWRnZ1zmXuQ1CQE0p7BkWDszrazfE3zxDcO204,5881 +pylint/checkers/base/pass_checker.py,sha256=DETwu3tP8Dgq9ME6sYMPR3jOtB6q6KIFDtJu3qSqpBI,992 +pylint/checkers/base_checker.py,sha256=u5AA6oW4T59jD057I4w6MTdb_42UM8XKzKyd33ANv24,10931 +pylint/checkers/classes/__init__.py,sha256=mw61Ow15QOTyjJoZT9QuBmFdzPfWSlfgQyaWTsCHD4w,644 +pylint/checkers/classes/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/classes/__pycache__/class_checker.cpython-310.pyc,, +pylint/checkers/classes/__pycache__/special_methods_checker.cpython-310.pyc,, +pylint/checkers/classes/class_checker.py,sha256=4a3VjLvP3nnTS5qFynIeu5vlM1QNKoCzcFirwzlFq4c,90008 +pylint/checkers/classes/special_methods_checker.py,sha256=xKA4edfphI-8anLWB8pOgyJDsDnC9Cnds99hArRHlZs,15115 +pylint/checkers/deprecated.py,sha256=ejXPFB0dGqNIRhQhrYQtedYdt7nNRuPlK33TmS-AWLM,9661 +pylint/checkers/design_analysis.py,sha256=CvG3MS1pqbOGkkXEH081Z5Ziit5Ho5uc52Y15CPSfQc,22139 +pylint/checkers/dunder_methods.py,sha256=Tv1nJJVXhIKpZGPRpBaAj7c8ZjbtVyyaZSomsi-lvxo,3513 +pylint/checkers/ellipsis_checker.py,sha256=WgyzvN4jnGMnxw6C4HH1vr1sMRMDIHGvH_HHC4TRXrc,2014 +pylint/checkers/exceptions.py,sha256=CrM7UfJThsL11U9lj627MSmMi26MITiGqKdNSDgt5Dw,26673 +pylint/checkers/format.py,sha256=bTqUNP4u2JVMZ_cLNgnqzp4W5Sy9IaCi-cV47sVsPL4,27558 +pylint/checkers/imports.py,sha256=3saM-SZUBty8RKu39gkslWkCANWFzfBaswL2sASpmKw,41935 +pylint/checkers/lambda_expressions.py,sha256=F0RsaEnY5Xuc-XPtw_aGP5-VD3m7bgYAdPo6LIyjiOA,3462 +pylint/checkers/logging.py,sha256=Qz25dR1vng-BLfg7S_lxlyRJwG607IBgViAd-2s-Jks,15595 +pylint/checkers/mapreduce_checker.py,sha256=VXx73lb2rvxK8Sc8o1U-wdV99nU1YTksRWuw63IBDmE,1111 +pylint/checkers/method_args.py,sha256=ZtMy4zgw5-abJWcFechN7unR48QsM2rIYQUVA6TgBco,4734 +pylint/checkers/misc.py,sha256=5JJZLaUQWLKionvNkP24-KEd7juyD42GL65irtUuHyA,4987 +pylint/checkers/modified_iterating_checker.py,sha256=e4cMJznf4PoN5xy6IaWoL-JvPHRtwJkgtmYzYRsnyIc,7859 +pylint/checkers/nested_min_max.py,sha256=8Cg4hB0p5lMaosSERKbNlEdU6A8K8hNbTijbJ0SsyNU,3720 +pylint/checkers/newstyle.py,sha256=YMQ8JR6DEo52uWXvGQcY5yZK0sJBmYzk4GO6eARhI50,4567 +pylint/checkers/non_ascii_names.py,sha256=TlsMUsG-sLfmeEslU7J5L02exiWXnQEKexF5w_G36tQ,7146 +pylint/checkers/raw_metrics.py,sha256=jbFMkeIm7ZfDD2Cq1-Ccn8wJPR11fdIddkr67RLpbkQ,3900 +pylint/checkers/refactoring/__init__.py,sha256=eEApx4kmgKO13iOdK3FCAqEcx8sy28qOi9MMhfguriY,1114 +pylint/checkers/refactoring/__pycache__/__init__.cpython-310.pyc,, +pylint/checkers/refactoring/__pycache__/implicit_booleaness_checker.cpython-310.pyc,, +pylint/checkers/refactoring/__pycache__/not_checker.cpython-310.pyc,, +pylint/checkers/refactoring/__pycache__/recommendation_checker.cpython-310.pyc,, +pylint/checkers/refactoring/__pycache__/refactoring_checker.cpython-310.pyc,, +pylint/checkers/refactoring/implicit_booleaness_checker.py,sha256=ic1aEjQj3kPI2TJiOxC2XlMl5NBZqXSnYguF9iqtxNY,9399 +pylint/checkers/refactoring/not_checker.py,sha256=KxH7cB5VOsTvEiLQyfGBrLshjnavF4fcAH2rMyC3qD8,2818 +pylint/checkers/refactoring/recommendation_checker.py,sha256=_FnELtglopNgDAaPIybaazwXnogysHLO5oOz4mIboO8,18123 +pylint/checkers/refactoring/refactoring_checker.py,sha256=7eC5FL_N101z2UE_0l5JlGJyUd3OKA2MAGXuBomkZcA,97480 +pylint/checkers/similar.py,sha256=Itd8T7WI5yAtMfwhKC4U2hNvkZr2d7jszSz4KUOQ7vI,34091 +pylint/checkers/spelling.py,sha256=uBdRaESNN2xBmsix4FtS1AaeYqRjdweufLwGBzCzzjY,16556 +pylint/checkers/stdlib.py,sha256=psPpdMTXgdiIEiG-CXu6pPBkLf8MM7NXORJT6FO4o94,32028 +pylint/checkers/strings.py,sha256=ACe0tdWrGYCN-a3_ciTdIYRoQAT816DjT-12pcAHCPY,41242 +pylint/checkers/threading_checker.py,sha256=F0sg6RvpBEqw7ixqb0dKg4YmHY9YChAnRRLTsDlB3ns,1941 +pylint/checkers/typecheck.py,sha256=CeDx-z4RpHVVbSMcS6Wc0qJND03f2h8Awve_tyOugn8,88551 +pylint/checkers/unicode.py,sha256=dIwn8eLiaVAhcFr7CCww9FPMBwRbuxaYyHpHthW74NA,18480 +pylint/checkers/unsupported_version.py,sha256=XrvHbJdSDJydVRPX4_es1Yez4nbiXTyBw72FzebDcgg,2999 +pylint/checkers/utils.py,sha256=JaykqkifusF-V4-S5GFDmC6KN1ADG1nvlpgKAN1P-j8,77883 +pylint/checkers/variables.py,sha256=atnAaTLXQmU-v9YksiKW_-DHkrayKgFt-Co2_WZ8LoE,128712 +pylint/config/__init__.py,sha256=3hLtVfZSfHZP4UZMHOJBF9qCiQJPjHD3PJXQyrpHxrs,2493 +pylint/config/__pycache__/__init__.cpython-310.pyc,, +pylint/config/__pycache__/argument.cpython-310.pyc,, +pylint/config/__pycache__/arguments_manager.cpython-310.pyc,, +pylint/config/__pycache__/arguments_provider.cpython-310.pyc,, +pylint/config/__pycache__/callback_actions.cpython-310.pyc,, +pylint/config/__pycache__/config_file_parser.cpython-310.pyc,, +pylint/config/__pycache__/config_initialization.cpython-310.pyc,, +pylint/config/__pycache__/configuration_mixin.cpython-310.pyc,, +pylint/config/__pycache__/deprecation_actions.cpython-310.pyc,, +pylint/config/__pycache__/environment_variable.cpython-310.pyc,, +pylint/config/__pycache__/exceptions.cpython-310.pyc,, +pylint/config/__pycache__/find_default_config_files.cpython-310.pyc,, +pylint/config/__pycache__/help_formatter.cpython-310.pyc,, +pylint/config/__pycache__/option.cpython-310.pyc,, +pylint/config/__pycache__/option_manager_mixin.cpython-310.pyc,, +pylint/config/__pycache__/option_parser.cpython-310.pyc,, +pylint/config/__pycache__/options_provider_mixin.cpython-310.pyc,, +pylint/config/__pycache__/utils.cpython-310.pyc,, +pylint/config/_pylint_config/__init__.py,sha256=1XJbigFjUmI27j9zn9gZFYGaDc8Mo7O-AsMGz2qOrwY,561 +pylint/config/_pylint_config/__pycache__/__init__.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/generate_command.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/help_message.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/main.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/setup.cpython-310.pyc,, +pylint/config/_pylint_config/__pycache__/utils.cpython-310.pyc,, +pylint/config/_pylint_config/generate_command.py,sha256=hyXFQyeXNXbRE8XcOZaLkcSD8h34Q7niL0MLVIpsySw,1846 +pylint/config/_pylint_config/help_message.py,sha256=6pT8R4iJDOB63G_0_kOhXJDGjC6ON-xsxkWUAN3ThK4,1997 +pylint/config/_pylint_config/main.py,sha256=sjO5wFWxXvnBc0vWHPOfIECzEo2df1IlghFPO2HFhlc,845 +pylint/config/_pylint_config/setup.py,sha256=pxKjhOen7p_Pn4rBaWisWfoUsc-ieK4ymsrDySUNxwQ,1603 +pylint/config/_pylint_config/utils.py,sha256=7SOGhvGZtjDpWEleQpWRCDEBMFQhiggAQ1Y36qdSvN8,3754 +pylint/config/argument.py,sha256=A84YRzyj5x8Atn8QbuWcmzAAvPkRtjq_x6k94i6JOrY,14903 +pylint/config/arguments_manager.py,sha256=kv9MgniJhAD6mtssSoka27sAeqaKlFGW4KnJ3fgmrUs,32618 +pylint/config/arguments_provider.py,sha256=yS18CQkPYE2JUuq-DoUIi1yV1u9A4LQsOsoZ4lO5h5U,8361 +pylint/config/callback_actions.py,sha256=EjyJBc-0kA3Qt1SoL7_8uCQfuUvEGIkMz3xSn2KqN_Q,14157 +pylint/config/config_file_parser.py,sha256=jL7oLbErca91oPmRQ9bsKZ__xYC4jwH-YwxJjCK_j6o,4639 +pylint/config/config_initialization.py,sha256=OpB5FFBXMpL1tKKWhcd4qJviCzBL8Hv_JOHmATNE4jc,5079 +pylint/config/configuration_mixin.py,sha256=JorNEM42b1kX07EYLG-HDPEFgk1wS6JTA4KTON9t-XY,1586 +pylint/config/deprecation_actions.py,sha256=XOZwcg_AxztjGv-5rERmEINuuzq85FFB2xEeIr9Dk2I,2947 +pylint/config/environment_variable.py,sha256=at9U9TSIQdf0yJpGaXx7sW9wvCF8wMQy_VmbbkAzOoQ,405 +pylint/config/exceptions.py,sha256=baU1rnYj_bAV1IF-AVF-F8S-aOgIuCdg2PxUyCPgoWo,816 +pylint/config/find_default_config_files.py,sha256=RTlwj9W6Acutyw2tuzQZ8TveneWJazhlf06UrcbhZaw,4155 +pylint/config/help_formatter.py,sha256=kDCwCvdC1TlMNoH92Srf8T9naagP6SkRPg_oin7VH_I,2688 +pylint/config/option.py,sha256=kttkR9YlqRc3mQq4lWB8PWlAzd0Fl4sKP2M0M-1FW3w,8515 +pylint/config/option_manager_mixin.py,sha256=bkppBYK7tfrv7yniMa0fpWG7h_zCFeQIY2sCamg9MzA,14829 +pylint/config/option_parser.py,sha256=ArpaynRxJjz5quey67Txm0n1_5-akTHEtBQ6ZhicLO0,2069 +pylint/config/options_provider_mixin.py,sha256=C5w5uPInk4RDWGBC-Y2qspoXa2LJdmSMmE4ddcm5ctM,4619 +pylint/config/utils.py,sha256=G36JSPYvQ0OZar8Me2w4EgTEIbhCjUECzLHM3198wAk,9465 +pylint/constants.py,sha256=ZgQd39duCbhpdrQyvGCoNJGsvole9ynRItiJ6AMTycE,10130 +pylint/epylint.py,sha256=E0UDINwDQRXWfs7sCDMmWYSWvk4xBp9faBhERmGERBc,7348 +pylint/exceptions.py,sha256=lWw5Tj0YdCnKWu9Bk1Ukd5f1qonOWXxayCYiO7v0GX0,1716 +pylint/extensions/__init__.py,sha256=1uuAFHCmgaWI3SV76dXPgkjrkPTWv0GPojiC5vtYTug,575 +pylint/extensions/__pycache__/__init__.cpython-310.pyc,, +pylint/extensions/__pycache__/_check_docs_utils.cpython-310.pyc,, +pylint/extensions/__pycache__/bad_builtin.cpython-310.pyc,, +pylint/extensions/__pycache__/broad_try_clause.cpython-310.pyc,, +pylint/extensions/__pycache__/check_elif.cpython-310.pyc,, +pylint/extensions/__pycache__/code_style.cpython-310.pyc,, +pylint/extensions/__pycache__/comparetozero.cpython-310.pyc,, +pylint/extensions/__pycache__/comparison_placement.cpython-310.pyc,, +pylint/extensions/__pycache__/confusing_elif.cpython-310.pyc,, +pylint/extensions/__pycache__/consider_refactoring_into_while_condition.cpython-310.pyc,, +pylint/extensions/__pycache__/consider_ternary_expression.cpython-310.pyc,, +pylint/extensions/__pycache__/dict_init_mutate.cpython-310.pyc,, +pylint/extensions/__pycache__/docparams.cpython-310.pyc,, +pylint/extensions/__pycache__/docstyle.cpython-310.pyc,, +pylint/extensions/__pycache__/dunder.cpython-310.pyc,, +pylint/extensions/__pycache__/empty_comment.cpython-310.pyc,, +pylint/extensions/__pycache__/emptystring.cpython-310.pyc,, +pylint/extensions/__pycache__/eq_without_hash.cpython-310.pyc,, +pylint/extensions/__pycache__/for_any_all.cpython-310.pyc,, +pylint/extensions/__pycache__/magic_value.cpython-310.pyc,, +pylint/extensions/__pycache__/mccabe.cpython-310.pyc,, +pylint/extensions/__pycache__/no_self_use.cpython-310.pyc,, +pylint/extensions/__pycache__/overlapping_exceptions.cpython-310.pyc,, +pylint/extensions/__pycache__/private_import.cpython-310.pyc,, +pylint/extensions/__pycache__/redefined_loop_name.cpython-310.pyc,, +pylint/extensions/__pycache__/redefined_variable_type.cpython-310.pyc,, +pylint/extensions/__pycache__/set_membership.cpython-310.pyc,, +pylint/extensions/__pycache__/typing.cpython-310.pyc,, +pylint/extensions/__pycache__/while_used.cpython-310.pyc,, +pylint/extensions/_check_docs_utils.py,sha256=gRYf-CdgDXrkZVWuBSnsTJPhaxuh8dLEHXxkOBNPnb0,26360 +pylint/extensions/bad_builtin.py,sha256=0oQMXkDwzhmrdES8xCISq8G5KHliyWYdqszAqef7On4,2269 +pylint/extensions/broad_try_clause.py,sha256=zgBEPWz_X4M_v9R10o_1XBBu3L0-TluxZ6eohDy28uA,2302 +pylint/extensions/check_elif.py,sha256=A8SPFAvsKcnvjkQPldDWFOK8-k2S55i67_U5hR2SiRc,2139 +pylint/extensions/code_style.py,sha256=gkfnO3ZkHu68Pd2_eDKtX1vxkvkvosfF6Wj1izdT0T8,12807 +pylint/extensions/comparetozero.py,sha256=HL5t7tWEoshz5VgWkfKsWY4kGRZoU9ER1kwLgd8e6f8,3168 +pylint/extensions/comparison_placement.py,sha256=S56QaqDsD_T5hch_f5FBVqY046IYBHUB1GBwZ48OSUQ,2352 +pylint/extensions/confusing_elif.py,sha256=Gnu2Et7aBoEeaYpDR6TWzZ5406cYs0yV-Fa-2BOuxug,2038 +pylint/extensions/consider_refactoring_into_while_condition.py,sha256=paSJWRAGuXJJnNa-dUbW8t6r9qnuCvondZ86nMAAZlM,3312 +pylint/extensions/consider_ternary_expression.py,sha256=RmERqrmtSY6sSImO2vP0wKPSMN_GRZHYvR-JprJzhS4,1698 +pylint/extensions/dict_init_mutate.py,sha256=cssSm-KCU3SgZgPsgvKSFyZ5xwj1kDu8yqP4ENDoLUE,2111 +pylint/extensions/docparams.py,sha256=6toBQQFf7xMsdQLtAwnX2QltRBz3clb0_YNz55srU40,25905 +pylint/extensions/docstyle.py,sha256=hAjw2yZquc-_bOPfU-OiQXdCEH-hLuIOB5AIiNOi4Vw,2943 +pylint/extensions/dunder.py,sha256=BWoz7EknGY2n3XP4gOr2n7EVHQoB12HEi-qXrdNO22g,2386 +pylint/extensions/empty_comment.py,sha256=6J3vWb1VsgqaihWB7iiLX9nWVMriY_mwMRgF55Qq8o8,1955 +pylint/extensions/emptystring.py,sha256=qcn-L25VhWMcGpwhtYl07V2vSt8fEEFwJWNUS9Goaec,2972 +pylint/extensions/eq_without_hash.py,sha256=MtJJ6-oUy2PJhmSBMCs8bAXXsIxzJwFTycdrdXXVyCs,1455 +pylint/extensions/for_any_all.py,sha256=NcmolEjBWD-aKSWmahpoaBOD-stJ8RNSnQB4OlHxr9Y,5825 +pylint/extensions/magic_value.py,sha256=ASwBW37SfUcDKgjQHydWtlfRII4QvB-vRrRZbeoBMPM,4238 +pylint/extensions/mccabe.py,sha256=IvPzKWs61Ro4iG6UiuWEJiCjIxfQ5RCouEhca3v6lRQ,7056 +pylint/extensions/no_self_use.py,sha256=-X4JaD97dlopbIlVUZwhtkJPTbhmU1axzXdcnMORzw0,3711 +pylint/extensions/overlapping_exceptions.py,sha256=De23ttHWJGotF0IxJuAxmU2lDnvAhOGJQED1UNar7bI,3340 +pylint/extensions/private_import.py,sha256=vxwz_YvnYMmO--vZjh36ByfGtjfpd4h7Cq1CbXMVXqo,11267 +pylint/extensions/redefined_loop_name.py,sha256=oQdXJptg0vz1jHZLhbvc_Jxax1GFNqLs8nCETfFDf7Q,3220 +pylint/extensions/redefined_variable_type.py,sha256=nn1TXwv0KMWyWeYbKuXpc--s2SgO9ZI3WBk--0zXSkw,4095 +pylint/extensions/set_membership.py,sha256=QM0OIvSX3Bb5aKndEOyHyc-ipspTp7wAnD-WkM4kLNA,1796 +pylint/extensions/typing.py,sha256=LJGyxtvXGGe-sO-CAs7QIS5bYWAWXaSvtv6GbuM5rKA,20390 +pylint/extensions/while_used.py,sha256=mPJM_99syc_U9obFYU_IoXzHlUHp80NTQ1cM4YljiZg,1093 +pylint/graph.py,sha256=8K9pHvoWupWOyHdRqzE_COXmUtMAD58eqGfWlOhSdtI,7112 +pylint/interfaces.py,sha256=1uZo1-eJoo_sct002nVnlgNKuf9fddypK-Fth3Laqh0,4058 +pylint/lint/__init__.py,sha256=PkPQqUEkDJ2UvZkxO2nB-W2f6o_zvgptqju-ktqCnVE,1499 +pylint/lint/__pycache__/__init__.cpython-310.pyc,, +pylint/lint/__pycache__/base_options.cpython-310.pyc,, +pylint/lint/__pycache__/caching.cpython-310.pyc,, +pylint/lint/__pycache__/expand_modules.cpython-310.pyc,, +pylint/lint/__pycache__/message_state_handler.cpython-310.pyc,, +pylint/lint/__pycache__/parallel.cpython-310.pyc,, +pylint/lint/__pycache__/pylinter.cpython-310.pyc,, +pylint/lint/__pycache__/report_functions.cpython-310.pyc,, +pylint/lint/__pycache__/run.cpython-310.pyc,, +pylint/lint/__pycache__/utils.cpython-310.pyc,, +pylint/lint/base_options.py,sha256=_S_GesVaavfDY6ref8F4M0pSynhj1dI57-zOXolmqkE,21450 +pylint/lint/caching.py,sha256=lCu3f08nROn1T30RapBsd7tIOaMNN2FfdRQGxR2GCdc,2380 +pylint/lint/expand_modules.py,sha256=nM7bWn_fWENEctwzZwNUM4KHWkVH7sEP7mf5IlOpX9E,7093 +pylint/lint/message_state_handler.py,sha256=lrWxkntvnVlFxXXIefRzY9ilxkGOmSvCUpngQerc0hA,17484 +pylint/lint/parallel.py,sha256=X9U6JT8HDx_nS7VPAPKOe9GMpUKoNppihvsddIK82zU,6558 +pylint/lint/pylinter.py,sha256=KjAJ5eF9oIr-XerP63W9MJR5gl4ksxQuN4RmkN6k-jc,53613 +pylint/lint/report_functions.py,sha256=-3IpMwyGtkxKOgdI4xys6Kw6P6GQW0dMjAruGDvA9No,2945 +pylint/lint/run.py,sha256=oA3c6t5mPOBK-T0y7guOX-p9apiX6I0zkFVDwjKQNl8,8957 +pylint/lint/utils.py,sha256=WssKoXUnlvqBYFnXisFEOXY2R8KeQ4e31iAsLob-dMU,4822 +pylint/message/__init__.py,sha256=OriAknwmd-zP1XxtaAZdks1rUxwIXTLjYD-Z64SJHsU,622 +pylint/message/__pycache__/__init__.cpython-310.pyc,, +pylint/message/__pycache__/_deleted_message_ids.cpython-310.pyc,, +pylint/message/__pycache__/message.cpython-310.pyc,, +pylint/message/__pycache__/message_definition.cpython-310.pyc,, +pylint/message/__pycache__/message_definition_store.cpython-310.pyc,, +pylint/message/__pycache__/message_id_store.cpython-310.pyc,, +pylint/message/_deleted_message_ids.py,sha256=_ZqD_BFwAhGFWd0300P7UUVZSR1p41o0gIdaePLhdQo,7614 +pylint/message/message.py,sha256=zK3jJgj7X4MYXM-v_pXhEAOSslGDoygZvIKMWwm44-8,2759 +pylint/message/message_definition.py,sha256=BSIpv0cvujQToi-9zLXn5247js6uvcS39UbJkex-k3I,5574 +pylint/message/message_definition_store.py,sha256=pz6CqY97Qt6E6fRH8HC3gGn6WGqnA6MafsYfWf6e3M0,5100 +pylint/message/message_id_store.py,sha256=O3l-UJ5FF0hIgtNvvqrGp8xtNDq-8cphfs7EOVNZdKQ,6447 +pylint/pyreverse/__init__.py,sha256=UUSMmrmRuB0ez663hA0gOeXaTsTBQaqhlR6Rzca2taM,274 +pylint/pyreverse/__pycache__/__init__.cpython-310.pyc,, +pylint/pyreverse/__pycache__/diadefslib.cpython-310.pyc,, +pylint/pyreverse/__pycache__/diagrams.cpython-310.pyc,, +pylint/pyreverse/__pycache__/dot_printer.cpython-310.pyc,, +pylint/pyreverse/__pycache__/inspector.cpython-310.pyc,, +pylint/pyreverse/__pycache__/main.cpython-310.pyc,, +pylint/pyreverse/__pycache__/mermaidjs_printer.cpython-310.pyc,, +pylint/pyreverse/__pycache__/plantuml_printer.cpython-310.pyc,, +pylint/pyreverse/__pycache__/printer.cpython-310.pyc,, +pylint/pyreverse/__pycache__/printer_factory.cpython-310.pyc,, +pylint/pyreverse/__pycache__/utils.cpython-310.pyc,, +pylint/pyreverse/__pycache__/vcg_printer.cpython-310.pyc,, +pylint/pyreverse/__pycache__/writer.cpython-310.pyc,, +pylint/pyreverse/diadefslib.py,sha256=ffJGQ7o3YwwAn9TdyrMpuW83F-Xyli-crCWGDwfXctY,8707 +pylint/pyreverse/diagrams.py,sha256=0od7HUDzBwlZB7CXtsUJXhYC_HIYX_CooPBjaTs-CyU,10878 +pylint/pyreverse/dot_printer.py,sha256=iLizsuDl0BLFkrNi5Hn2AfT5S0sFPVCW9CXl7dnVxWM,6069 +pylint/pyreverse/inspector.py,sha256=zVMiNs8xJBTjSesU3sq_GxV_j4hLi2r-yoAyQtft9HU,15076 +pylint/pyreverse/main.py,sha256=di7WGwfdpV8bRaDAnzABVxY4HfSiqU8iRN4EjI5sMOw,8968 +pylint/pyreverse/mermaidjs_printer.py,sha256=ANtrSa3mXZQUuKcqr9zm0Bbn2zaqddxuZI6CrkrJtew,3552 +pylint/pyreverse/plantuml_printer.py,sha256=6sp5wMD01Zk44RpFPSc4S2VMY6rz206cJqabSKQE-mk,3643 +pylint/pyreverse/printer.py,sha256=KUSbR9MWpi0eRlCotohEu91J3tMVSkpuwCaiGL99EjQ,3734 +pylint/pyreverse/printer_factory.py,sha256=lX9NG8dNQU9gVdBLaV6Pqo890cX4tVCOKZ7Ay0SpmfQ,900 +pylint/pyreverse/utils.py,sha256=mWDR5G-ldrQjGY2UMrriKMdiZdQQcBKi8qpOTLDlajw,8324 +pylint/pyreverse/vcg_printer.py,sha256=und-OhYEbtfta07D2SxcSjZ4bxpbCVHVMecAT5_Opfo,8880 +pylint/pyreverse/writer.py,sha256=5Ol0AjJP9itReMCwW9jPM-23vtKEk1g5DMcmJwIbu1g,6220 +pylint/reporters/__init__.py,sha256=KhZuqHuenXk0bhtsBvZRgRXt9CKuQJQUYPYKcPDNCeY,1026 +pylint/reporters/__pycache__/__init__.cpython-310.pyc,, +pylint/reporters/__pycache__/base_reporter.cpython-310.pyc,, +pylint/reporters/__pycache__/collecting_reporter.cpython-310.pyc,, +pylint/reporters/__pycache__/json_reporter.cpython-310.pyc,, +pylint/reporters/__pycache__/multi_reporter.cpython-310.pyc,, +pylint/reporters/__pycache__/reports_handler_mix_in.cpython-310.pyc,, +pylint/reporters/__pycache__/text.cpython-310.pyc,, +pylint/reporters/base_reporter.py,sha256=kJHMnoJH4OUe7Rm6w4_AHlJAWsjrQGgUSA1aYiOl_dE,3509 +pylint/reporters/collecting_reporter.py,sha256=-ZKVYrqfBDguGSxiTM1lZwZ7GpO90mx1_SN9MeJZsbk,725 +pylint/reporters/json_reporter.py,sha256=xsrutb9nLQ6m5HZzYyXbwE6xlrf4nnbHwzf-W4dPouI,3770 +pylint/reporters/multi_reporter.py,sha256=tOdC0CweV2Fb5XJb2uUr_90HYWqeInwG3agrzNjsXsE,3761 +pylint/reporters/reports_handler_mix_in.py,sha256=fOFMxrp54R2hVxYOqANVDlInH8jAI7tfEJ5h79TvC78,3067 +pylint/reporters/text.py,sha256=fDiOnoO6E3XFz5HtMs2zDo1qWWP2odff8VJtybqu694,10958 +pylint/reporters/ureports/__init__.py,sha256=ljyZntDHXZOMHLvRnrDJMs8Ys0B9OK7YoFrDhawcOa8,310 +pylint/reporters/ureports/__pycache__/__init__.cpython-310.pyc,, +pylint/reporters/ureports/__pycache__/base_writer.cpython-310.pyc,, +pylint/reporters/ureports/__pycache__/nodes.cpython-310.pyc,, +pylint/reporters/ureports/__pycache__/text_writer.cpython-310.pyc,, +pylint/reporters/ureports/base_writer.py,sha256=FyAmZGHeTeUBZ6_9xdlJOgbpuXQVCme-Q6FqT0sLnEI,3430 +pylint/reporters/ureports/nodes.py,sha256=WYjaA7lxztMSHMpo4r6Cy9sKr-3Q9z-RuhPMebhOWvM,5245 +pylint/reporters/ureports/text_writer.py,sha256=2McJv44uWKX1paFB0ZqQKskWLG1okf-SJTYgOOG5P_U,3606 +pylint/testutils/__init__.py,sha256=6KHmKni7nfNUXyIgksoUGoLgXCCEwrapVjlwqxCHdjI,1309 +pylint/testutils/__pycache__/__init__.cpython-310.pyc,, +pylint/testutils/__pycache__/_run.cpython-310.pyc,, +pylint/testutils/__pycache__/checker_test_case.cpython-310.pyc,, +pylint/testutils/__pycache__/configuration_test.cpython-310.pyc,, +pylint/testutils/__pycache__/constants.cpython-310.pyc,, +pylint/testutils/__pycache__/decorator.cpython-310.pyc,, +pylint/testutils/__pycache__/functional_test_file.cpython-310.pyc,, +pylint/testutils/__pycache__/get_test_info.cpython-310.pyc,, +pylint/testutils/__pycache__/global_test_linter.cpython-310.pyc,, +pylint/testutils/__pycache__/lint_module_test.cpython-310.pyc,, +pylint/testutils/__pycache__/output_line.cpython-310.pyc,, +pylint/testutils/__pycache__/pyreverse.cpython-310.pyc,, +pylint/testutils/__pycache__/reporter_for_tests.cpython-310.pyc,, +pylint/testutils/__pycache__/tokenize_str.cpython-310.pyc,, +pylint/testutils/__pycache__/unittest_linter.cpython-310.pyc,, +pylint/testutils/__pycache__/utils.cpython-310.pyc,, +pylint/testutils/_primer/__init__.py,sha256=1GhDjAf3SwghA2NcoV2yNCW0_OOKZv6ZDR--l5PjFE8,379 +pylint/testutils/_primer/__pycache__/__init__.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/package_to_lint.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer_command.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer_compare_command.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer_prepare_command.cpython-310.pyc,, +pylint/testutils/_primer/__pycache__/primer_run_command.cpython-310.pyc,, +pylint/testutils/_primer/package_to_lint.py,sha256=RJbQbzyoDrNIXPajKyr4wDEIE9WD9Wg8oFSvcJxBj_A,4748 +pylint/testutils/_primer/primer.py,sha256=Lk9GU6CpT8KsbCnH1IRdooMuf0XlAXSUzTqgV7LdwZg,4063 +pylint/testutils/_primer/primer_command.py,sha256=SrEkTgkgDSxnNBxH1b6vzVQSlZZsaunOxa2VWZS-wb4,1110 +pylint/testutils/_primer/primer_compare_command.py,sha256=NAFn_C_XHhDC2uf20JEvrl9KQvqLfuJuHrHJ3pAZDQw,6422 +pylint/testutils/_primer/primer_prepare_command.py,sha256=EK4BfVwgRSDYFuONpjdHHjpubL9Gfv6POrZMw2r-AzQ,1947 +pylint/testutils/_primer/primer_run_command.py,sha256=2YDnib3yFo-ZCxYcIXZdK15XvBsTNc520s1S1BMiCz8,4140 +pylint/testutils/_run.py,sha256=VWk0xwotTdu6-2gn-8Eq7Go4nH9VZKwFd72XiH0WCCI,1549 +pylint/testutils/checker_test_case.py,sha256=IktDgF3pg-yiFiI16jts7-ecyJ7yOE1WKLQm1j-0ZJY,4377 +pylint/testutils/configuration_test.py,sha256=UpQ1GFlK3Aog75OUtL7ebiZYCtY0n2O_38_J94nFB9I,6036 +pylint/testutils/constants.py,sha256=nggFYQ10qQ44P4MuxsJ4ugTQ8M8Y7Z2hy3XHV8STPhE,1145 +pylint/testutils/decorator.py,sha256=22iVCXAV93lV5TTaDY5Z7lMIlmp3_opGiO5ITnGoQDY,1252 +pylint/testutils/functional/__init__.py,sha256=DaAh5XAzIhZkGUn2ZADAWJHSThraZvVaFNEU9UeL_5Y,790 +pylint/testutils/functional/__pycache__/__init__.cpython-310.pyc,, +pylint/testutils/functional/__pycache__/find_functional_tests.cpython-310.pyc,, +pylint/testutils/functional/__pycache__/lint_module_output_update.cpython-310.pyc,, +pylint/testutils/functional/__pycache__/test_file.cpython-310.pyc,, +pylint/testutils/functional/find_functional_tests.py,sha256=T9l4fuWZGVFFYf-WrpM2O8lq0jbuccpSN9buWf0Tm6I,3001 +pylint/testutils/functional/lint_module_output_update.py,sha256=Mx59vaJsxz-jPT4lzu6TsSG7JsCLxP9KBgMqiBWq9TA,2143 +pylint/testutils/functional/test_file.py,sha256=KLCKwlgyPeRR7BxIa70g5Cjj293CUcZRPe_akU1TUf8,3806 +pylint/testutils/functional_test_file.py,sha256=PsQq5Kp7X1tZLc4qhnC5OC34wgZRXXAzsjtBCphXW7A,635 +pylint/testutils/get_test_info.py,sha256=a3lQTkaIDk90QPSgBOt3deXn9XlzxKA_hbdK4_-tMVk,2127 +pylint/testutils/global_test_linter.py,sha256=ksZfQa2oxnRgzc6OBbzhhk2tM8SoxxDg0zfjhp_we20,685 +pylint/testutils/lint_module_test.py,sha256=0EzoQTPHYBNXpy8rBqQrgT5mfJSbmLc0u-hRqw6ahnI,12801 +pylint/testutils/output_line.py,sha256=RDMrPmemcWQXsiQ6ejMrjsOAvlUr12MaCv_uhAI6r5k,5897 +pylint/testutils/pyreverse.py,sha256=hDjoTVuFvwX-k_WJvmeS8kx00gxu8087ivlLaKyt--U,4194 +pylint/testutils/reporter_for_tests.py,sha256=06Gt7xqMWs2yp6rL08auUaLq-ABpPpJxjTCIB2Q_kxc,2306 +pylint/testutils/testing_pylintrc,sha256=wbnx5xK0-nhu1ZaFz8bVb5cdx6E6L-CuHaqSr6LV6QY,198 +pylint/testutils/tokenize_str.py,sha256=xV1BcMrl_VmkXVgKrU44OrYmTmkfbyv_ojDzMjxdEnI,447 +pylint/testutils/unittest_linter.py,sha256=QZ3qfFSRYZ2Q1aVL4pe_0fIZn6MdrASMNuIL4uRi4tc,2795 +pylint/testutils/utils.py,sha256=diP1uunxhC-USoXKs2CZFOVvGHQE4jaNF3gSJ11Ps7E,3133 +pylint/typing.py,sha256=7ossvI0DB1vm9rWpMbu5fSnvIDauVsWbsC96HW0BPtE,3250 +pylint/utils/__init__.py,sha256=IG9wBnlAbF9N1TUi8lQT3XZhluAg5XQ3KHqrzx6i4SY,1382 +pylint/utils/__pycache__/__init__.cpython-310.pyc,, +pylint/utils/__pycache__/ast_walker.cpython-310.pyc,, +pylint/utils/__pycache__/docs.cpython-310.pyc,, +pylint/utils/__pycache__/file_state.cpython-310.pyc,, +pylint/utils/__pycache__/linterstats.cpython-310.pyc,, +pylint/utils/__pycache__/pragma_parser.cpython-310.pyc,, +pylint/utils/__pycache__/utils.cpython-310.pyc,, +pylint/utils/ast_walker.py,sha256=7cQN2XIIZkLvzvsr6YMRjUwWviCMvMm_P4ORciLdxw0,4235 +pylint/utils/docs.py,sha256=wbCkopZIthCXIgzbd2dIng85Of-QNEhNWFyjainKjSI,3854 +pylint/utils/file_state.py,sha256=-bYuMbWzUzRxNi5bo9xltQ8mup2HgEEyWg9zV2pVmoA,11330 +pylint/utils/linterstats.py,sha256=9GSxKj4d_YstK6nsnIhjQuBlcbqkqvWtyx0w-52a-5g,12554 +pylint/utils/pragma_parser.py,sha256=cP-yUuOEf-gHGKAVqwodgaXEyOmaDksGOycUNvtqPf0,5054 +pylint/utils/utils.py,sha256=fMUbcmHAYJnBROkWD48JIsRF4GLqdc_2IL5PvfI_wmE,13267 diff --git a/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/REQUESTED b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/REQUESTED new file mode 100644 index 00000000..e69de29b diff --git a/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/WHEEL b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/WHEEL new file mode 120000 index 00000000..7e89bc16 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/WHEEL @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/1b/5e/87/e00dc87a84269cead8578b9e6462928e18a95f1f3373c9eef451a5bcc0 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/entry_points.txt b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/entry_points.txt new file mode 120000 index 00000000..9f819330 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/entry_points.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/47/f6/19/546047f8eda0d70c94b90fd52e416055fc7da7fb900b9a0c79077b72ee \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/top_level.txt b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/top_level.txt new file mode 120000 index 00000000..7c8dded1 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint-2.17.0.dist-info/top_level.txt @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/8f/a6/7d/8bffe922e6a20a46ba525f58232eb22396b0e506c2b6574bbd994c92c1 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint/__init__.py b/venv/lib/python3.10/site-packages/pylint/__init__.py new file mode 120000 index 00000000..eb075f3a --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/9a/e8/61/680ce5b4b1babe842072ea69dadc37b352df87dd87d062f62de3c9d26b \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint/__main__.py b/venv/lib/python3.10/site-packages/pylint/__main__.py new file mode 120000 index 00000000..27408ae0 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/__main__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/9b/5c/b9/9bb2b2f07b7236713c7ed1929d14930e0bf9b2eabc3fe07857f283dc96 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint/__pkginfo__.py b/venv/lib/python3.10/site-packages/pylint/__pkginfo__.py new file mode 100644 index 00000000..4a6eb609 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/__pkginfo__.py @@ -0,0 +1,43 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt + +"""This module exists for compatibility reasons. + +It's updated via tbump, do not modify. +""" + +from __future__ import annotations + +__version__ = "2.17.0" + + +def get_numversion_from_version(v: str) -> tuple[int, int, int]: + """Kept for compatibility reason. + + See https://github.com/PyCQA/pylint/issues/4399 + https://github.com/PyCQA/pylint/issues/4420, + """ + version = v.replace("pylint-", "") + result_version = [] + for number in version.split(".")[0:3]: + try: + result_version.append(int(number)) + except ValueError: + current_number = "" + for char in number: + if char.isdigit(): + current_number += char + else: + break + try: + result_version.append(int(current_number)) + except ValueError: + result_version.append(0) + while len(result_version) != 3: + result_version.append(0) + + return tuple(result_version) # type: ignore[return-value] # mypy can't infer the length + + +numversion = get_numversion_from_version(__version__) diff --git a/venv/lib/python3.10/site-packages/pylint/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..0164b0f2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/__pycache__/__main__.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/__pycache__/__main__.cpython-310.pyc new file mode 100644 index 00000000..4f05fc52 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/__pycache__/__main__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/__pycache__/__pkginfo__.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/__pycache__/__pkginfo__.cpython-310.pyc new file mode 100644 index 00000000..f4947eed Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/__pycache__/__pkginfo__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/__pycache__/constants.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/__pycache__/constants.cpython-310.pyc new file mode 100644 index 00000000..a3b95073 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/__pycache__/constants.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/__pycache__/epylint.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/__pycache__/epylint.cpython-310.pyc new file mode 100644 index 00000000..614467fa Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/__pycache__/epylint.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/__pycache__/exceptions.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 00000000..23a43772 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/__pycache__/exceptions.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/__pycache__/graph.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/__pycache__/graph.cpython-310.pyc new file mode 100644 index 00000000..9a45ceca Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/__pycache__/graph.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/__pycache__/interfaces.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/__pycache__/interfaces.cpython-310.pyc new file mode 100644 index 00000000..623ab835 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/__pycache__/interfaces.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/__pycache__/typing.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/__pycache__/typing.cpython-310.pyc new file mode 100644 index 00000000..d6b64768 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/__pycache__/typing.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__init__.py b/venv/lib/python3.10/site-packages/pylint/checkers/__init__.py new file mode 120000 index 00000000..b9c1a503 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/92/69/c7/06bd1718ea9c1690136be8c1ed2e8b4b2cb085a38798efe5ab7eb5cd86 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..1f7f18c9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/async.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/async.cpython-310.pyc new file mode 100644 index 00000000..769a7cf1 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/async.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/bad_chained_comparison.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/bad_chained_comparison.cpython-310.pyc new file mode 100644 index 00000000..1c76a787 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/bad_chained_comparison.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/base_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/base_checker.cpython-310.pyc new file mode 100644 index 00000000..35f1319e Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/base_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/deprecated.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/deprecated.cpython-310.pyc new file mode 100644 index 00000000..806c7e8e Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/deprecated.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-310.pyc new file mode 100644 index 00000000..27d11337 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/design_analysis.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/dunder_methods.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/dunder_methods.cpython-310.pyc new file mode 100644 index 00000000..415d51f6 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/dunder_methods.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/ellipsis_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/ellipsis_checker.cpython-310.pyc new file mode 100644 index 00000000..32fe68fe Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/ellipsis_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/exceptions.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/exceptions.cpython-310.pyc new file mode 100644 index 00000000..257ebcdb Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/exceptions.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/format.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/format.cpython-310.pyc new file mode 100644 index 00000000..544c0fd9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/format.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/imports.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/imports.cpython-310.pyc new file mode 100644 index 00000000..0f75bfd2 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/imports.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/lambda_expressions.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/lambda_expressions.cpython-310.pyc new file mode 100644 index 00000000..13e42827 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/lambda_expressions.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/logging.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/logging.cpython-310.pyc new file mode 100644 index 00000000..daa42dfd Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/logging.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/mapreduce_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/mapreduce_checker.cpython-310.pyc new file mode 100644 index 00000000..972c4eba Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/mapreduce_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/method_args.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/method_args.cpython-310.pyc new file mode 100644 index 00000000..b23b077a Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/method_args.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/misc.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/misc.cpython-310.pyc new file mode 100644 index 00000000..c9facd99 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/misc.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/modified_iterating_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/modified_iterating_checker.cpython-310.pyc new file mode 100644 index 00000000..309c2423 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/modified_iterating_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/nested_min_max.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/nested_min_max.cpython-310.pyc new file mode 100644 index 00000000..c86d47e8 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/nested_min_max.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/newstyle.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/newstyle.cpython-310.pyc new file mode 100644 index 00000000..75298577 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/newstyle.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/non_ascii_names.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/non_ascii_names.cpython-310.pyc new file mode 100644 index 00000000..f1af4276 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/non_ascii_names.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-310.pyc new file mode 100644 index 00000000..4371f83a Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/raw_metrics.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/similar.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/similar.cpython-310.pyc new file mode 100644 index 00000000..ae7b683d Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/similar.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/spelling.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/spelling.cpython-310.pyc new file mode 100644 index 00000000..07bd5f24 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/spelling.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/stdlib.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/stdlib.cpython-310.pyc new file mode 100644 index 00000000..219e6585 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/stdlib.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/strings.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/strings.cpython-310.pyc new file mode 100644 index 00000000..aae81ff9 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/strings.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/threading_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/threading_checker.cpython-310.pyc new file mode 100644 index 00000000..40d8305a Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/threading_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/typecheck.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/typecheck.cpython-310.pyc new file mode 100644 index 00000000..be21a8e4 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/typecheck.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/unicode.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/unicode.cpython-310.pyc new file mode 100644 index 00000000..92681c63 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/unicode.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/unsupported_version.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/unsupported_version.cpython-310.pyc new file mode 100644 index 00000000..158c3031 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/unsupported_version.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/utils.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/utils.cpython-310.pyc new file mode 100644 index 00000000..e0397398 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/utils.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/variables.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/variables.cpython-310.pyc new file mode 100644 index 00000000..473f7f9a Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/__pycache__/variables.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/async.py b/venv/lib/python3.10/site-packages/pylint/checkers/async.py new file mode 100644 index 00000000..b06ec60c --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/async.py @@ -0,0 +1,96 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt + +"""Checker for anything related to the async protocol (PEP 492).""" + +from __future__ import annotations + +import sys +from typing import TYPE_CHECKING + +import astroid +from astroid import nodes, util + +from pylint import checkers +from pylint.checkers import utils as checker_utils +from pylint.checkers.utils import decorated_with + +if TYPE_CHECKING: + from pylint.lint import PyLinter + + +class AsyncChecker(checkers.BaseChecker): + name = "async" + msgs = { + "E1700": ( + "Yield inside async function", + "yield-inside-async-function", + "Used when an `yield` or `yield from` statement is " + "found inside an async function.", + {"minversion": (3, 5)}, + ), + "E1701": ( + "Async context manager '%s' doesn't implement __aenter__ and __aexit__.", + "not-async-context-manager", + "Used when an async context manager is used with an object " + "that does not implement the async context management protocol.", + {"minversion": (3, 5)}, + ), + } + + def open(self) -> None: + self._mixin_class_rgx = self.linter.config.mixin_class_rgx + self._async_generators = ["contextlib.asynccontextmanager"] + + @checker_utils.only_required_for_messages("yield-inside-async-function") + def visit_asyncfunctiondef(self, node: nodes.AsyncFunctionDef) -> None: + for child in node.nodes_of_class(nodes.Yield): + if child.scope() is node and ( + sys.version_info[:2] == (3, 5) or isinstance(child, nodes.YieldFrom) + ): + self.add_message("yield-inside-async-function", node=child) + + @checker_utils.only_required_for_messages("not-async-context-manager") + def visit_asyncwith(self, node: nodes.AsyncWith) -> None: + for ctx_mgr, _ in node.items: + inferred = checker_utils.safe_infer(ctx_mgr) + if inferred is None or isinstance(inferred, util.UninferableBase): + continue + + if isinstance(inferred, nodes.AsyncFunctionDef): + # Check if we are dealing with a function decorated + # with contextlib.asynccontextmanager. + if decorated_with(inferred, self._async_generators): + continue + elif isinstance(inferred, astroid.bases.AsyncGenerator): + # Check if we are dealing with a function decorated + # with contextlib.asynccontextmanager. + if decorated_with(inferred.parent, self._async_generators): + continue + else: + try: + inferred.getattr("__aenter__") + inferred.getattr("__aexit__") + except astroid.exceptions.NotFoundError: + if isinstance(inferred, astroid.Instance): + # If we do not know the bases of this class, + # just skip it. + if not checker_utils.has_known_bases(inferred): + continue + # Ignore mixin classes if they match the rgx option. + if ( + "not-async-context-manager" + in self.linter.config.ignored_checks_for_mixins + and self._mixin_class_rgx.match(inferred.name) + ): + continue + else: + continue + self.add_message( + "not-async-context-manager", node=node, args=(inferred.name,) + ) + + +def register(linter: PyLinter) -> None: + linter.register_checker(AsyncChecker(linter)) diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py b/venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py new file mode 100644 index 00000000..8c3aeb9c --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/bad_chained_comparison.py @@ -0,0 +1,60 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from astroid import nodes + +from pylint.checkers import BaseChecker +from pylint.interfaces import HIGH + +if TYPE_CHECKING: + from pylint.lint import PyLinter + +COMPARISON_OP = frozenset(("<", "<=", ">", ">=", "!=", "==")) +IDENTITY_OP = frozenset(("is", "is not")) +MEMBERSHIP_OP = frozenset(("in", "not in")) + + +class BadChainedComparisonChecker(BaseChecker): + """Checks for unintentional usage of chained comparison.""" + + name = "bad-chained-comparison" + msgs = { + "W3601": ( + "Suspicious %s-part chained comparison using semantically incompatible operators (%s)", + "bad-chained-comparison", + "Used when there is a chained comparison where one expression is part " + "of two comparisons that belong to different semantic groups " + '("<" does not mean the same thing as "is", chaining them in ' + '"0 < x is None" is probably a mistake).', + ) + } + + def _has_diff_semantic_groups(self, operators: list[str]) -> bool: + # Check if comparison operators are in the same semantic group + for semantic_group in (COMPARISON_OP, IDENTITY_OP, MEMBERSHIP_OP): + if operators[0] in semantic_group: + group = semantic_group + return not all(o in group for o in operators) + + def visit_compare(self, node: nodes.Compare) -> None: + operators = sorted({op[0] for op in node.ops}) + if self._has_diff_semantic_groups(operators): + num_parts = f"{len(node.ops)}" + incompatibles = ( + ", ".join(f"'{o}'" for o in operators[:-1]) + f" and '{operators[-1]}'" + ) + self.add_message( + "bad-chained-comparison", + node=node, + args=(num_parts, incompatibles), + confidence=HIGH, + ) + + +def register(linter: PyLinter) -> None: + linter.register_checker(BadChainedComparisonChecker(linter)) diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py b/venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py new file mode 120000 index 00000000..1ceb70fe --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/base/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/a2/4e/95/2796605a43050cabca9e3578551c56c242dc71ed8e8098db69c9b5f835 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..5c22b239 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/basic_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/basic_checker.cpython-310.pyc new file mode 100644 index 00000000..4b11936d Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/basic_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/basic_error_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/basic_error_checker.cpython-310.pyc new file mode 100644 index 00000000..78e8c736 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/basic_error_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/comparison_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/comparison_checker.cpython-310.pyc new file mode 100644 index 00000000..03d96beb Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/comparison_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/docstring_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/docstring_checker.cpython-310.pyc new file mode 100644 index 00000000..de21ec7d Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/docstring_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/pass_checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/pass_checker.cpython-310.pyc new file mode 100644 index 00000000..492b9809 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/base/__pycache__/pass_checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py b/venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py new file mode 100644 index 00000000..08d582b7 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/base/basic_checker.py @@ -0,0 +1,966 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt + +"""Basic checker for Python code.""" + +from __future__ import annotations + +import collections +import itertools +import sys +from collections.abc import Iterator +from typing import TYPE_CHECKING, cast + +import astroid +from astroid import nodes, objects, util + +from pylint import utils as lint_utils +from pylint.checkers import BaseChecker, utils +from pylint.interfaces import HIGH, INFERENCE, Confidence +from pylint.reporters.ureports import nodes as reporter_nodes +from pylint.utils import LinterStats + +if TYPE_CHECKING: + from pylint.lint.pylinter import PyLinter + +if sys.version_info >= (3, 8): + from typing import Literal +else: + from typing_extensions import Literal + + +class _BasicChecker(BaseChecker): + """Permits separating multiple checks with the same checker name into + classes/file. + """ + + name = "basic" + + +REVERSED_PROTOCOL_METHOD = "__reversed__" +SEQUENCE_PROTOCOL_METHODS = ("__getitem__", "__len__") +REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS, (REVERSED_PROTOCOL_METHOD,)) +# A mapping from qname -> symbol, to be used when generating messages +# about dangerous default values as arguments +DEFAULT_ARGUMENT_SYMBOLS = dict( + zip( + [".".join(["builtins", x]) for x in ("set", "dict", "list")], + ["set()", "{}", "[]"], + ), + **{ + x: f"{x}()" + for x in ( + "collections.deque", + "collections.ChainMap", + "collections.Counter", + "collections.OrderedDict", + "collections.defaultdict", + "collections.UserDict", + "collections.UserList", + ) + }, +) + + +def report_by_type_stats( + sect: reporter_nodes.Section, + stats: LinterStats, + old_stats: LinterStats | None, +) -> None: + """Make a report of. + + * percentage of different types documented + * percentage of different types with a bad name + """ + # percentage of different types documented and/or with a bad name + nice_stats: dict[str, dict[str, str]] = {} + for node_type in ("module", "class", "method", "function"): + node_type = cast(Literal["function", "class", "method", "module"], node_type) + total = stats.get_node_count(node_type) + nice_stats[node_type] = {} + if total != 0: + undocumented_node = stats.get_undocumented(node_type) + documented = total - undocumented_node + percent = (documented * 100.0) / total + nice_stats[node_type]["percent_documented"] = f"{percent:.2f}" + badname_node = stats.get_bad_names(node_type) + percent = (badname_node * 100.0) / total + nice_stats[node_type]["percent_badname"] = f"{percent:.2f}" + lines = ["type", "number", "old number", "difference", "%documented", "%badname"] + for node_type in ("module", "class", "method", "function"): + node_type = cast(Literal["function", "class", "method", "module"], node_type) + new = stats.get_node_count(node_type) + old = old_stats.get_node_count(node_type) if old_stats else None + diff_str = lint_utils.diff_string(old, new) if old else None + lines += [ + node_type, + str(new), + str(old) if old else "NC", + diff_str if diff_str else "NC", + nice_stats[node_type].get("percent_documented", "0"), + nice_stats[node_type].get("percent_badname", "0"), + ] + sect.append(reporter_nodes.Table(children=lines, cols=6, rheaders=1)) + + +# pylint: disable-next = too-many-public-methods +class BasicChecker(_BasicChecker): + """Basic checker. + + Checks for : + * doc strings + * number of arguments, local variables, branches, returns and statements in + functions, methods + * required module attributes + * dangerous default values as arguments + * redefinition of function / method / class + * uses of the global statement + """ + + name = "basic" + msgs = { + "W0101": ( + "Unreachable code", + "unreachable", + 'Used when there is some code behind a "return" or "raise" ' + "statement, which will never be accessed.", + ), + "W0102": ( + "Dangerous default value %s as argument", + "dangerous-default-value", + "Used when a mutable value as list or dictionary is detected in " + "a default value for an argument.", + ), + "W0104": ( + "Statement seems to have no effect", + "pointless-statement", + "Used when a statement doesn't have (or at least seems to) any effect.", + ), + "W0105": ( + "String statement has no effect", + "pointless-string-statement", + "Used when a string is used as a statement (which of course " + "has no effect). This is a particular case of W0104 with its " + "own message so you can easily disable it if you're using " + "those strings as documentation, instead of comments.", + ), + "W0106": ( + 'Expression "%s" is assigned to nothing', + "expression-not-assigned", + "Used when an expression that is not a function call is assigned " + "to nothing. Probably something else was intended.", + ), + "W0108": ( + "Lambda may not be necessary", + "unnecessary-lambda", + "Used when the body of a lambda expression is a function call " + "on the same argument list as the lambda itself; such lambda " + "expressions are in all but a few cases replaceable with the " + "function being called in the body of the lambda.", + ), + "W0109": ( + "Duplicate key %r in dictionary", + "duplicate-key", + "Used when a dictionary expression binds the same key multiple times.", + ), + "W0122": ( + "Use of exec", + "exec-used", + "Raised when the 'exec' statement is used. It's dangerous to use this " + "function for a user input, and it's also slower than actual code in " + "general. This doesn't mean you should never use it, but you should " + "consider alternatives first and restrict the functions available.", + ), + "W0123": ( + "Use of eval", + "eval-used", + 'Used when you use the "eval" function, to discourage its ' + "usage. Consider using `ast.literal_eval` for safely evaluating " + "strings containing Python expressions " + "from untrusted sources.", + ), + "W0150": ( + "%s statement in finally block may swallow exception", + "lost-exception", + "Used when a break or a return statement is found inside the " + "finally clause of a try...finally block: the exceptions raised " + "in the try clause will be silently swallowed instead of being " + "re-raised.", + ), + "W0199": ( + "Assert called on a populated tuple. Did you mean 'assert x,y'?", + "assert-on-tuple", + "A call of assert on a tuple will always evaluate to true if " + "the tuple is not empty, and will always evaluate to false if " + "it is.", + ), + "W0124": ( + 'Following "as" with another context manager looks like a tuple.', + "confusing-with-statement", + "Emitted when a `with` statement component returns multiple values " + "and uses name binding with `as` only for a part of those values, " + "as in with ctx() as a, b. This can be misleading, since it's not " + "clear if the context manager returns a tuple or if the node without " + "a name binding is another context manager.", + ), + "W0125": ( + "Using a conditional statement with a constant value", + "using-constant-test", + "Emitted when a conditional statement (If or ternary if) " + "uses a constant value for its test. This might not be what " + "the user intended to do.", + ), + "W0126": ( + "Using a conditional statement with potentially wrong function or method call due to " + "missing parentheses", + "missing-parentheses-for-call-in-test", + "Emitted when a conditional statement (If or ternary if) " + "seems to wrongly call a function due to missing parentheses", + ), + "W0127": ( + "Assigning the same variable %r to itself", + "self-assigning-variable", + "Emitted when we detect that a variable is assigned to itself", + ), + "W0128": ( + "Redeclared variable %r in assignment", + "redeclared-assigned-name", + "Emitted when we detect that a variable was redeclared in the same assignment.", + ), + "E0111": ( + "The first reversed() argument is not a sequence", + "bad-reversed-sequence", + "Used when the first argument to reversed() builtin " + "isn't a sequence (does not implement __reversed__, " + "nor __getitem__ and __len__", + ), + "E0119": ( + "format function is not called on str", + "misplaced-format-function", + "Emitted when format function is not called on str object. " + 'e.g doing print("value: {}").format(123) instead of ' + 'print("value: {}".format(123)). This might not be what the user ' + "intended to do.", + ), + "W0129": ( + "Assert statement has a string literal as its first argument. The assert will %s fail.", + "assert-on-string-literal", + "Used when an assert statement has a string literal as its first argument, which will " + "cause the assert to always pass.", + ), + "W0130": ( + "Duplicate value %r in set", + "duplicate-value", + "This message is emitted when a set contains the same value two or more times.", + ), + "W0131": ( + "Named expression used without context", + "named-expr-without-context", + "Emitted if named expression is used to do a regular assignment " + "outside a context like if, for, while, or a comprehension.", + ), + "W0133": ( + "Exception statement has no effect", + "pointless-exception-statement", + "Used when an exception is created without being assigned, raised or returned " + "for subsequent use elsewhere.", + ), + } + + reports = (("RP0101", "Statistics by type", report_by_type_stats),) + + def __init__(self, linter: PyLinter) -> None: + super().__init__(linter) + self._tryfinallys: list[nodes.TryFinally] | None = None + + def open(self) -> None: + """Initialize visit variables and statistics.""" + py_version = self.linter.config.py_version + self._py38_plus = py_version >= (3, 8) + self._tryfinallys = [] + self.linter.stats.reset_node_count() + + @utils.only_required_for_messages( + "using-constant-test", "missing-parentheses-for-call-in-test" + ) + def visit_if(self, node: nodes.If) -> None: + self._check_using_constant_test(node, node.test) + + @utils.only_required_for_messages( + "using-constant-test", "missing-parentheses-for-call-in-test" + ) + def visit_ifexp(self, node: nodes.IfExp) -> None: + self._check_using_constant_test(node, node.test) + + @utils.only_required_for_messages( + "using-constant-test", "missing-parentheses-for-call-in-test" + ) + def visit_comprehension(self, node: nodes.Comprehension) -> None: + if node.ifs: + for if_test in node.ifs: + self._check_using_constant_test(node, if_test) + + def _check_using_constant_test( + self, + node: nodes.If | nodes.IfExp | nodes.Comprehension, + test: nodes.NodeNG | None, + ) -> None: + const_nodes = ( + nodes.Module, + nodes.GeneratorExp, + nodes.Lambda, + nodes.FunctionDef, + nodes.ClassDef, + astroid.bases.Generator, + astroid.UnboundMethod, + astroid.BoundMethod, + nodes.Module, + ) + structs = (nodes.Dict, nodes.Tuple, nodes.Set, nodes.List) + + # These nodes are excepted, since they are not constant + # values, requiring a computation to happen. + except_nodes = ( + nodes.Call, + nodes.BinOp, + nodes.BoolOp, + nodes.UnaryOp, + nodes.Subscript, + ) + inferred = None + emit = isinstance(test, (nodes.Const,) + structs + const_nodes) + maybe_generator_call = None + if not isinstance(test, except_nodes): + inferred = utils.safe_infer(test) + if isinstance(inferred, util.UninferableBase) and isinstance( + test, nodes.Name + ): + emit, maybe_generator_call = BasicChecker._name_holds_generator(test) + + # Emit if calling a function that only returns GeneratorExp (always tests True) + elif isinstance(test, nodes.Call): + maybe_generator_call = test + if maybe_generator_call: + inferred_call = utils.safe_infer(maybe_generator_call.func) + if isinstance(inferred_call, nodes.FunctionDef): + # Can't use all(x) or not any(not x) for this condition, because it + # will return True for empty generators, which is not what we want. + all_returns_were_generator = None + for return_node in inferred_call._get_return_nodes_skip_functions(): + if not isinstance(return_node.value, nodes.GeneratorExp): + all_returns_were_generator = False + break + all_returns_were_generator = True + if all_returns_were_generator: + self.add_message( + "using-constant-test", node=node, confidence=INFERENCE + ) + return + + if emit: + self.add_message("using-constant-test", node=test, confidence=INFERENCE) + elif isinstance(inferred, const_nodes): + # If the constant node is a FunctionDef or Lambda then + # it may be an illicit function call due to missing parentheses + call_inferred = None + try: + # Just forcing the generator to infer all elements. + # astroid.exceptions.InferenceError are false positives + # see https://github.com/PyCQA/pylint/pull/8185 + if isinstance(inferred, nodes.FunctionDef): + call_inferred = list(inferred.infer_call_result()) + elif isinstance(inferred, nodes.Lambda): + call_inferred = list(inferred.infer_call_result(node)) + except astroid.InferenceError: + call_inferred = None + if call_inferred: + self.add_message( + "missing-parentheses-for-call-in-test", + node=test, + confidence=INFERENCE, + ) + self.add_message("using-constant-test", node=test, confidence=INFERENCE) + + @staticmethod + def _name_holds_generator(test: nodes.Name) -> tuple[bool, nodes.Call | None]: + """Return whether `test` tests a name certain to hold a generator, or optionally + a call that should be then tested to see if *it* returns only generators. + """ + assert isinstance(test, nodes.Name) + emit = False + maybe_generator_call = None + lookup_result = test.frame(future=True).lookup(test.name) + if not lookup_result: + return emit, maybe_generator_call + maybe_generator_assigned = ( + isinstance(assign_name.parent.value, nodes.GeneratorExp) + for assign_name in lookup_result[1] + if isinstance(assign_name.parent, nodes.Assign) + ) + first_item = next(maybe_generator_assigned, None) + if first_item is not None: + # Emit if this variable is certain to hold a generator + if all(itertools.chain((first_item,), maybe_generator_assigned)): + emit = True + # If this variable holds the result of a call, save it for next test + elif ( + len(lookup_result[1]) == 1 + and isinstance(lookup_result[1][0].parent, nodes.Assign) + and isinstance(lookup_result[1][0].parent.value, nodes.Call) + ): + maybe_generator_call = lookup_result[1][0].parent.value + return emit, maybe_generator_call + + def visit_module(self, _: nodes.Module) -> None: + """Check module name, docstring and required arguments.""" + self.linter.stats.node_count["module"] += 1 + + def visit_classdef(self, _: nodes.ClassDef) -> None: + """Check module name, docstring and redefinition + increment branch counter. + """ + self.linter.stats.node_count["klass"] += 1 + + @utils.only_required_for_messages( + "pointless-statement", + "pointless-exception-statement", + "pointless-string-statement", + "expression-not-assigned", + "named-expr-without-context", + ) + def visit_expr(self, node: nodes.Expr) -> None: + """Check for various kind of statements without effect.""" + expr = node.value + if isinstance(expr, nodes.Const) and isinstance(expr.value, str): + # treat string statement in a separated message + # Handle PEP-257 attribute docstrings. + # An attribute docstring is defined as being a string right after + # an assignment at the module level, class level or __init__ level. + scope = expr.scope() + if isinstance(scope, (nodes.ClassDef, nodes.Module, nodes.FunctionDef)): + if isinstance(scope, nodes.FunctionDef) and scope.name != "__init__": + pass + else: + sibling = expr.previous_sibling() + if ( + sibling is not None + and sibling.scope() is scope + and isinstance(sibling, (nodes.Assign, nodes.AnnAssign)) + ): + return + self.add_message("pointless-string-statement", node=node) + return + + # Warn W0133 for exceptions that are used as statements + if isinstance(expr, nodes.Call): + name = "" + if isinstance(expr.func, nodes.Name): + name = expr.func.name + elif isinstance(expr.func, nodes.Attribute): + name = expr.func.attrname + + # Heuristic: only run inference for names that begin with an uppercase char + # This reduces W0133's coverage, but retains acceptable runtime performance + # For more details, see: https://github.com/PyCQA/pylint/issues/8073 + inferred = utils.safe_infer(expr) if name[:1].isupper() else None + if isinstance(inferred, objects.ExceptionInstance): + self.add_message( + "pointless-exception-statement", node=node, confidence=INFERENCE + ) + return + + # Ignore if this is : + # * the unique child of a try/except body + # * a yield statement + # * an ellipsis (which can be used on Python 3 instead of pass) + # warn W0106 if we have any underlying function call (we can't predict + # side effects), else pointless-statement + if ( + isinstance(expr, (nodes.Yield, nodes.Await)) + or (isinstance(node.parent, nodes.TryExcept) and node.parent.body == [node]) + or (isinstance(expr, nodes.Const) and expr.value is Ellipsis) + ): + return + if isinstance(expr, nodes.NamedExpr): + self.add_message("named-expr-without-context", node=node, confidence=HIGH) + elif any(expr.nodes_of_class(nodes.Call)): + self.add_message( + "expression-not-assigned", node=node, args=expr.as_string() + ) + else: + self.add_message("pointless-statement", node=node) + + @staticmethod + def _filter_vararg( + node: nodes.Lambda, call_args: list[nodes.NodeNG] + ) -> Iterator[nodes.NodeNG]: + # Return the arguments for the given call which are + # not passed as vararg. + for arg in call_args: + if isinstance(arg, nodes.Starred): + if ( + isinstance(arg.value, nodes.Name) + and arg.value.name != node.args.vararg + ): + yield arg + else: + yield arg + + @staticmethod + def _has_variadic_argument( + args: list[nodes.Starred | nodes.Keyword], variadic_name: str + ) -> bool: + return not args or any( + isinstance(a.value, nodes.Name) + and a.value.name != variadic_name + or not isinstance(a.value, nodes.Name) + for a in args + ) + + @utils.only_required_for_messages("unnecessary-lambda") + def visit_lambda(self, node: nodes.Lambda) -> None: + """Check whether the lambda is suspicious.""" + # if the body of the lambda is a call expression with the same + # argument list as the lambda itself, then the lambda is + # possibly unnecessary and at least suspicious. + if node.args.defaults: + # If the arguments of the lambda include defaults, then a + # judgment cannot be made because there is no way to check + # that the defaults defined by the lambda are the same as + # the defaults defined by the function called in the body + # of the lambda. + return + call = node.body + if not isinstance(call, nodes.Call): + # The body of the lambda must be a function call expression + # for the lambda to be unnecessary. + return + if isinstance(node.body.func, nodes.Attribute) and isinstance( + node.body.func.expr, nodes.Call + ): + # Chained call, the intermediate call might + # return something else (but we don't check that, yet). + return + + call_site = astroid.arguments.CallSite.from_call(call) + ordinary_args = list(node.args.args) + new_call_args = list(self._filter_vararg(node, call.args)) + if node.args.kwarg: + if self._has_variadic_argument(call.kwargs, node.args.kwarg): + return + + if node.args.vararg: + if self._has_variadic_argument(call.starargs, node.args.vararg): + return + elif call.starargs: + return + + if call.keywords: + # Look for additional keyword arguments that are not part + # of the lambda's signature + lambda_kwargs = {keyword.name for keyword in node.args.defaults} + if len(lambda_kwargs) != len(call_site.keyword_arguments): + # Different lengths, so probably not identical + return + if set(call_site.keyword_arguments).difference(lambda_kwargs): + return + + # The "ordinary" arguments must be in a correspondence such that: + # ordinary_args[i].name == call.args[i].name. + if len(ordinary_args) != len(new_call_args): + return + for arg, passed_arg in zip(ordinary_args, new_call_args): + if not isinstance(passed_arg, nodes.Name): + return + if arg.name != passed_arg.name: + return + + self.add_message("unnecessary-lambda", line=node.fromlineno, node=node) + + @utils.only_required_for_messages("dangerous-default-value") + def visit_functiondef(self, node: nodes.FunctionDef) -> None: + """Check function name, docstring, arguments, redefinition, + variable names, max locals. + """ + if node.is_method(): + self.linter.stats.node_count["method"] += 1 + else: + self.linter.stats.node_count["function"] += 1 + self._check_dangerous_default(node) + + visit_asyncfunctiondef = visit_functiondef + + def _check_dangerous_default(self, node: nodes.FunctionDef) -> None: + """Check for dangerous default values as arguments.""" + + def is_iterable(internal_node: nodes.NodeNG) -> bool: + return isinstance(internal_node, (nodes.List, nodes.Set, nodes.Dict)) + + defaults = (node.args.defaults or []) + (node.args.kw_defaults or []) + for default in defaults: + if not default: + continue + try: + value = next(default.infer()) + except astroid.InferenceError: + continue + + if ( + isinstance(value, astroid.Instance) + and value.qname() in DEFAULT_ARGUMENT_SYMBOLS + ): + if value is default: + msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()] + elif isinstance(value, astroid.Instance) or is_iterable(value): + # We are here in the following situation(s): + # * a dict/set/list/tuple call which wasn't inferred + # to a syntax node ({}, () etc.). This can happen + # when the arguments are invalid or unknown to + # the inference. + # * a variable from somewhere else, which turns out to be a list + # or a dict. + if is_iterable(default): + msg = value.pytype() + elif isinstance(default, nodes.Call): + msg = f"{value.name}() ({value.qname()})" + else: + msg = f"{default.as_string()} ({value.qname()})" + else: + # this argument is a name + msg = f"{default.as_string()} ({DEFAULT_ARGUMENT_SYMBOLS[value.qname()]})" + self.add_message("dangerous-default-value", node=node, args=(msg,)) + + @utils.only_required_for_messages("unreachable", "lost-exception") + def visit_return(self, node: nodes.Return) -> None: + """Return node visitor. + + 1 - check if the node has a right sibling (if so, that's some + unreachable code) + 2 - check if the node is inside the 'finally' clause of a 'try...finally' + block + """ + self._check_unreachable(node) + # Is it inside final body of a try...finally block ? + self._check_not_in_finally(node, "return", (nodes.FunctionDef,)) + + @utils.only_required_for_messages("unreachable") + def visit_continue(self, node: nodes.Continue) -> None: + """Check is the node has a right sibling (if so, that's some unreachable + code). + """ + self._check_unreachable(node) + + @utils.only_required_for_messages("unreachable", "lost-exception") + def visit_break(self, node: nodes.Break) -> None: + """Break node visitor. + + 1 - check if the node has a right sibling (if so, that's some + unreachable code) + 2 - check if the node is inside the 'finally' clause of a 'try...finally' + block + """ + # 1 - Is it right sibling ? + self._check_unreachable(node) + # 2 - Is it inside final body of a try...finally block ? + self._check_not_in_finally(node, "break", (nodes.For, nodes.While)) + + @utils.only_required_for_messages("unreachable") + def visit_raise(self, node: nodes.Raise) -> None: + """Check if the node has a right sibling (if so, that's some unreachable + code). + """ + self._check_unreachable(node) + + def _check_misplaced_format_function(self, call_node: nodes.Call) -> None: + if not isinstance(call_node.func, nodes.Attribute): + return + if call_node.func.attrname != "format": + return + + expr = utils.safe_infer(call_node.func.expr) + if isinstance(expr, util.UninferableBase): + return + if not expr: + # we are doubtful on inferred type of node, so here just check if format + # was called on print() + call_expr = call_node.func.expr + if not isinstance(call_expr, nodes.Call): + return + if ( + isinstance(call_expr.func, nodes.Name) + and call_expr.func.name == "print" + ): + self.add_message("misplaced-format-function", node=call_node) + + @utils.only_required_for_messages( + "eval-used", + "exec-used", + "bad-reversed-sequence", + "misplaced-format-function", + "unreachable", + ) + def visit_call(self, node: nodes.Call) -> None: + """Visit a Call node.""" + if utils.is_terminating_func(node): + self._check_unreachable(node, confidence=INFERENCE) + self._check_misplaced_format_function(node) + if isinstance(node.func, nodes.Name): + name = node.func.name + # ignore the name if it's not a builtin (i.e. not defined in the + # locals nor globals scope) + if not (name in node.frame(future=True) or name in node.root()): + if name == "exec": + self.add_message("exec-used", node=node) + elif name == "reversed": + self._check_reversed(node) + elif name == "eval": + self.add_message("eval-used", node=node) + + @utils.only_required_for_messages("assert-on-tuple", "assert-on-string-literal") + def visit_assert(self, node: nodes.Assert) -> None: + """Check whether assert is used on a tuple or string literal.""" + if isinstance(node.test, nodes.Tuple) and len(node.test.elts) > 0: + self.add_message("assert-on-tuple", node=node, confidence=HIGH) + + if isinstance(node.test, nodes.Const) and isinstance(node.test.value, str): + if node.test.value: + when = "never" + else: + when = "always" + self.add_message("assert-on-string-literal", node=node, args=(when,)) + + @utils.only_required_for_messages("duplicate-key") + def visit_dict(self, node: nodes.Dict) -> None: + """Check duplicate key in dictionary.""" + keys = set() + for k, _ in node.items: + if isinstance(k, nodes.Const): + key = k.value + elif isinstance(k, nodes.Attribute): + key = k.as_string() + else: + continue + if key in keys: + self.add_message("duplicate-key", node=node, args=key) + keys.add(key) + + @utils.only_required_for_messages("duplicate-value") + def visit_set(self, node: nodes.Set) -> None: + """Check duplicate value in set.""" + values = set() + for v in node.elts: + if isinstance(v, nodes.Const): + value = v.value + else: + continue + if value in values: + self.add_message( + "duplicate-value", node=node, args=value, confidence=HIGH + ) + values.add(value) + + def visit_tryfinally(self, node: nodes.TryFinally) -> None: + """Update try...finally flag.""" + assert self._tryfinallys is not None + self._tryfinallys.append(node) + + def leave_tryfinally(self, _: nodes.TryFinally) -> None: + """Update try...finally flag.""" + assert self._tryfinallys is not None + self._tryfinallys.pop() + + def _check_unreachable( + self, + node: nodes.Return | nodes.Continue | nodes.Break | nodes.Raise | nodes.Call, + confidence: Confidence = HIGH, + ) -> None: + """Check unreachable code.""" + unreachable_statement = node.next_sibling() + if unreachable_statement is not None: + if ( + isinstance(node, nodes.Return) + and isinstance(unreachable_statement, nodes.Expr) + and isinstance(unreachable_statement.value, nodes.Yield) + ): + # Don't add 'unreachable' for empty generators. + # Only add warning if 'yield' is followed by another node. + unreachable_statement = unreachable_statement.next_sibling() + if unreachable_statement is None: + return + self.add_message( + "unreachable", node=unreachable_statement, confidence=confidence + ) + + def _check_not_in_finally( + self, + node: nodes.Break | nodes.Return, + node_name: str, + breaker_classes: tuple[nodes.NodeNG, ...] = (), + ) -> None: + """Check that a node is not inside a 'finally' clause of a + 'try...finally' statement. + + If we find a parent which type is in breaker_classes before + a 'try...finally' block we skip the whole check. + """ + # if self._tryfinallys is empty, we're not an in try...finally block + if not self._tryfinallys: + return + # the node could be a grand-grand...-child of the 'try...finally' + _parent = node.parent + _node = node + while _parent and not isinstance(_parent, breaker_classes): + if hasattr(_parent, "finalbody") and _node in _parent.finalbody: + self.add_message("lost-exception", node=node, args=node_name) + return + _node = _parent + _parent = _node.parent + + def _check_reversed(self, node: nodes.Call) -> None: + """Check that the argument to `reversed` is a sequence.""" + try: + argument = utils.safe_infer(utils.get_argument_from_call(node, position=0)) + except utils.NoSuchArgumentError: + pass + else: + if isinstance(argument, util.UninferableBase): + return + if argument is None: + # Nothing was inferred. + # Try to see if we have iter(). + if isinstance(node.args[0], nodes.Call): + try: + func = next(node.args[0].func.infer()) + except astroid.InferenceError: + return + if getattr( + func, "name", None + ) == "iter" and utils.is_builtin_object(func): + self.add_message("bad-reversed-sequence", node=node) + return + + if isinstance(argument, (nodes.List, nodes.Tuple)): + return + + # dicts are reversible, but only from Python 3.8 onward. Prior to + # that, any class based on dict must explicitly provide a + # __reversed__ method + if not self._py38_plus and isinstance(argument, astroid.Instance): + if any( + ancestor.name == "dict" and utils.is_builtin_object(ancestor) + for ancestor in itertools.chain( + (argument._proxied,), argument._proxied.ancestors() + ) + ): + try: + argument.locals[REVERSED_PROTOCOL_METHOD] + except KeyError: + self.add_message("bad-reversed-sequence", node=node) + return + + if hasattr(argument, "getattr"): + # everything else is not a proper sequence for reversed() + for methods in REVERSED_METHODS: + for meth in methods: + try: + argument.getattr(meth) + except astroid.NotFoundError: + break + else: + break + else: + self.add_message("bad-reversed-sequence", node=node) + else: + self.add_message("bad-reversed-sequence", node=node) + + @utils.only_required_for_messages("confusing-with-statement") + def visit_with(self, node: nodes.With) -> None: + # a "with" statement with multiple managers corresponds + # to one AST "With" node with multiple items + pairs = node.items + if pairs: + for prev_pair, pair in zip(pairs, pairs[1:]): + if isinstance(prev_pair[1], nodes.AssignName) and ( + pair[1] is None and not isinstance(pair[0], nodes.Call) + ): + # Don't emit a message if the second is a function call + # there's no way that can be mistaken for a name assignment. + # If the line number doesn't match + # we assume it's a nested "with". + self.add_message("confusing-with-statement", node=node) + + def _check_self_assigning_variable(self, node: nodes.Assign) -> None: + # Detect assigning to the same variable. + + scope = node.scope() + scope_locals = scope.locals + + rhs_names = [] + targets = node.targets + if isinstance(targets[0], nodes.Tuple): + if len(targets) != 1: + # A complex assignment, so bail out early. + return + targets = targets[0].elts + if len(targets) == 1: + # Unpacking a variable into the same name. + return + + if isinstance(node.value, nodes.Name): + if len(targets) != 1: + return + rhs_names = [node.value] + elif isinstance(node.value, nodes.Tuple): + rhs_count = len(node.value.elts) + if len(targets) != rhs_count or rhs_count == 1: + return + rhs_names = node.value.elts + + for target, lhs_name in zip(targets, rhs_names): + if not isinstance(lhs_name, nodes.Name): + continue + if not isinstance(target, nodes.AssignName): + continue + # Check that the scope is different from a class level, which is usually + # a pattern to expose module level attributes as class level ones. + if isinstance(scope, nodes.ClassDef) and target.name in scope_locals: + continue + if target.name == lhs_name.name: + self.add_message( + "self-assigning-variable", args=(target.name,), node=target + ) + + def _check_redeclared_assign_name(self, targets: list[nodes.NodeNG | None]) -> None: + dummy_variables_rgx = self.linter.config.dummy_variables_rgx + + for target in targets: + if not isinstance(target, nodes.Tuple): + continue + + found_names = [] + for element in target.elts: + if isinstance(element, nodes.Tuple): + self._check_redeclared_assign_name([element]) + elif isinstance(element, nodes.AssignName) and element.name != "_": + if dummy_variables_rgx and dummy_variables_rgx.match(element.name): + return + found_names.append(element.name) + + names = collections.Counter(found_names) + for name, count in names.most_common(): + if count > 1: + self.add_message( + "redeclared-assigned-name", args=(name,), node=target + ) + + @utils.only_required_for_messages( + "self-assigning-variable", "redeclared-assigned-name" + ) + def visit_assign(self, node: nodes.Assign) -> None: + self._check_self_assigning_variable(node) + self._check_redeclared_assign_name(node.targets) + + @utils.only_required_for_messages("redeclared-assigned-name") + def visit_for(self, node: nodes.For) -> None: + self._check_redeclared_assign_name([node.target]) diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py b/venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py new file mode 100644 index 00000000..25038247 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/base/basic_error_checker.py @@ -0,0 +1,582 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt + +"""Basic Error checker from the basic checker.""" + +from __future__ import annotations + +import itertools +from collections.abc import Iterator +from typing import Any + +import astroid +from astroid import nodes +from astroid.typing import InferenceResult + +from pylint.checkers import utils +from pylint.checkers.base.basic_checker import _BasicChecker +from pylint.checkers.utils import infer_all +from pylint.interfaces import HIGH + +ABC_METACLASSES = {"_py_abc.ABCMeta", "abc.ABCMeta"} # Python 3.7+, +# List of methods which can be redefined +REDEFINABLE_METHODS = frozenset(("__module__",)) +TYPING_FORWARD_REF_QNAME = "typing.ForwardRef" + + +def _get_break_loop_node(break_node: nodes.Break) -> nodes.For | nodes.While | None: + """Returns the loop node that holds the break node in arguments. + + Args: + break_node (astroid.Break): the break node of interest. + + Returns: + astroid.For or astroid.While: the loop node holding the break node. + """ + loop_nodes = (nodes.For, nodes.While) + parent = break_node.parent + while not isinstance(parent, loop_nodes) or break_node in getattr( + parent, "orelse", [] + ): + break_node = parent + parent = parent.parent + if parent is None: + break + return parent + + +def _loop_exits_early(loop: nodes.For | nodes.While) -> bool: + """Returns true if a loop may end with a break statement. + + Args: + loop (astroid.For, astroid.While): the loop node inspected. + + Returns: + bool: True if the loop may end with a break statement, False otherwise. + """ + loop_nodes = (nodes.For, nodes.While) + definition_nodes = (nodes.FunctionDef, nodes.ClassDef) + inner_loop_nodes: list[nodes.For | nodes.While] = [ + _node + for _node in loop.nodes_of_class(loop_nodes, skip_klass=definition_nodes) + if _node != loop + ] + return any( + _node + for _node in loop.nodes_of_class(nodes.Break, skip_klass=definition_nodes) + if _get_break_loop_node(_node) not in inner_loop_nodes + ) + + +def _has_abstract_methods(node: nodes.ClassDef) -> bool: + """Determine if the given `node` has abstract methods. + + The methods should be made abstract by decorating them + with `abc` decorators. + """ + return len(utils.unimplemented_abstract_methods(node)) > 0 + + +def redefined_by_decorator(node: nodes.FunctionDef) -> bool: + """Return True if the object is a method redefined via decorator. + + For example: + @property + def x(self): return self._x + @x.setter + def x(self, value): self._x = value + """ + if node.decorators: + for decorator in node.decorators.nodes: + if ( + isinstance(decorator, nodes.Attribute) + and getattr(decorator.expr, "name", None) == node.name + ): + return True + return False + + +class BasicErrorChecker(_BasicChecker): + msgs = { + "E0100": ( + "__init__ method is a generator", + "init-is-generator", + "Used when the special class method __init__ is turned into a " + "generator by a yield in its body.", + ), + "E0101": ( + "Explicit return in __init__", + "return-in-init", + "Used when the special class method __init__ has an explicit " + "return value.", + ), + "E0102": ( + "%s already defined line %s", + "function-redefined", + "Used when a function / class / method is redefined.", + ), + "E0103": ( + "%r not properly in loop", + "not-in-loop", + "Used when break or continue keywords are used outside a loop.", + ), + "E0104": ( + "Return outside function", + "return-outside-function", + 'Used when a "return" statement is found outside a function or method.', + ), + "E0105": ( + "Yield outside function", + "yield-outside-function", + 'Used when a "yield" statement is found outside a function or method.', + ), + "E0106": ( + "Return with argument inside generator", + "return-arg-in-generator", + 'Used when a "return" statement with an argument is found ' + "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, remove the else and" + " de-indent all the code inside it", + "useless-else-on-loop", + "Loops should only have an else clause if they can exit early " + "with a break statement, otherwise the statements under else " + "should be on the same scope as the loop itself.", + ), + "E0112": ( + "More than one starred expression in assignment", + "too-many-star-expressions", + "Emitted when there are more than one starred " + "expressions (`*x`) in an assignment. This is a SyntaxError.", + ), + "E0113": ( + "Starred assignment target must be in a list or tuple", + "invalid-star-assignment-target", + "Emitted when a star expression is used as a starred assignment target.", + ), + "E0114": ( + "Can use starred expression only in assignment target", + "star-needs-assignment-target", + "Emitted when a star expression is not used in an assignment target.", + ), + "E0115": ( + "Name %r is nonlocal and global", + "nonlocal-and-global", + "Emitted when a name is both nonlocal and global.", + ), + "E0116": ( + "'continue' not supported inside 'finally' clause", + "continue-in-finally", + "Emitted when the `continue` keyword is found " + "inside a finally clause, which is a SyntaxError.", + ), + "E0117": ( + "nonlocal name %s found without binding", + "nonlocal-without-binding", + "Emitted when a nonlocal variable does not have an attached " + "name somewhere in the parent scopes", + ), + "E0118": ( + "Name %r is used prior to global declaration", + "used-prior-global-declaration", + "Emitted when a name is used prior a global declaration, " + "which results in an error since Python 3.6.", + {"minversion": (3, 6)}, + ), + } + + def open(self) -> None: + py_version = self.linter.config.py_version + self._py38_plus = py_version >= (3, 8) + + @utils.only_required_for_messages("function-redefined") + def visit_classdef(self, node: nodes.ClassDef) -> None: + self._check_redefinition("class", node) + + def _too_many_starred_for_tuple(self, assign_tuple: nodes.Tuple) -> bool: + starred_count = 0 + for elem in assign_tuple.itered(): + if isinstance(elem, nodes.Tuple): + return self._too_many_starred_for_tuple(elem) + if isinstance(elem, nodes.Starred): + starred_count += 1 + return starred_count > 1 + + @utils.only_required_for_messages( + "too-many-star-expressions", "invalid-star-assignment-target" + ) + def visit_assign(self, node: nodes.Assign) -> None: + # Check *a, *b = ... + assign_target = node.targets[0] + # Check *a = b + if isinstance(node.targets[0], nodes.Starred): + self.add_message("invalid-star-assignment-target", node=node) + + if not isinstance(assign_target, nodes.Tuple): + return + if self._too_many_starred_for_tuple(assign_target): + self.add_message("too-many-star-expressions", node=node) + + @utils.only_required_for_messages("star-needs-assignment-target") + def visit_starred(self, node: nodes.Starred) -> None: + """Check that a Starred expression is used in an assignment target.""" + if isinstance(node.parent, nodes.Call): + # f(*args) is converted to Call(args=[Starred]), so ignore + # them for this check. + return + if isinstance(node.parent, (nodes.List, nodes.Tuple, nodes.Set, nodes.Dict)): + # PEP 448 unpacking. + return + + stmt = node.statement(future=True) + if not isinstance(stmt, nodes.Assign): + return + + if stmt.value is node or stmt.value.parent_of(node): + self.add_message("star-needs-assignment-target", node=node) + + @utils.only_required_for_messages( + "init-is-generator", + "return-in-init", + "function-redefined", + "return-arg-in-generator", + "duplicate-argument-name", + "nonlocal-and-global", + "used-prior-global-declaration", + ) + def visit_functiondef(self, node: nodes.FunctionDef) -> None: + self._check_nonlocal_and_global(node) + self._check_name_used_prior_global(node) + if not redefined_by_decorator( + node + ) and not utils.is_registered_in_singledispatch_function(node): + self._check_redefinition(node.is_method() and "method" or "function", node) + # checks for max returns, branch, return in __init__ + returns = node.nodes_of_class( + nodes.Return, skip_klass=(nodes.FunctionDef, nodes.ClassDef) + ) + if node.is_method() and node.name == "__init__": + if node.is_generator(): + self.add_message("init-is-generator", node=node) + else: + values = [r.value for r in returns] + # Are we returning anything but None from constructors + if any(v for v in values if not utils.is_none(v)): + self.add_message("return-in-init", node=node) + # Check for duplicate names by clustering args with same name for detailed report + arg_clusters = {} + arguments: Iterator[Any] = filter(None, [node.args.args, node.args.kwonlyargs]) + for arg in itertools.chain.from_iterable(arguments): + if arg.name in arg_clusters: + self.add_message( + "duplicate-argument-name", + node=arg, + args=(arg.name,), + confidence=HIGH, + ) + else: + arg_clusters[arg.name] = arg + + visit_asyncfunctiondef = visit_functiondef + + def _check_name_used_prior_global(self, node: nodes.FunctionDef) -> None: + scope_globals = { + name: child + for child in node.nodes_of_class(nodes.Global) + for name in child.names + if child.scope() is node + } + + if not scope_globals: + return + + for node_name in node.nodes_of_class(nodes.Name): + if node_name.scope() is not node: + continue + + name = node_name.name + corresponding_global = scope_globals.get(name) + if not corresponding_global: + continue + + global_lineno = corresponding_global.fromlineno + if global_lineno and global_lineno > node_name.fromlineno: + self.add_message( + "used-prior-global-declaration", node=node_name, args=(name,) + ) + + def _check_nonlocal_and_global(self, node: nodes.FunctionDef) -> None: + """Check that a name is both nonlocal and global.""" + + def same_scope(current: nodes.Global | nodes.Nonlocal) -> bool: + return current.scope() is node + + from_iter = itertools.chain.from_iterable + nonlocals = set( + from_iter( + child.names + for child in node.nodes_of_class(nodes.Nonlocal) + if same_scope(child) + ) + ) + + if not nonlocals: + return + + global_vars = set( + from_iter( + child.names + for child in node.nodes_of_class(nodes.Global) + if same_scope(child) + ) + ) + for name in nonlocals.intersection(global_vars): + self.add_message("nonlocal-and-global", args=(name,), node=node) + + @utils.only_required_for_messages("return-outside-function") + def visit_return(self, node: nodes.Return) -> None: + if not isinstance(node.frame(future=True), nodes.FunctionDef): + self.add_message("return-outside-function", node=node) + + @utils.only_required_for_messages("yield-outside-function") + def visit_yield(self, node: nodes.Yield) -> None: + self._check_yield_outside_func(node) + + @utils.only_required_for_messages("yield-outside-function") + def visit_yieldfrom(self, node: nodes.YieldFrom) -> None: + self._check_yield_outside_func(node) + + @utils.only_required_for_messages("not-in-loop", "continue-in-finally") + def visit_continue(self, node: nodes.Continue) -> None: + self._check_in_loop(node, "continue") + + @utils.only_required_for_messages("not-in-loop") + def visit_break(self, node: nodes.Break) -> None: + self._check_in_loop(node, "break") + + @utils.only_required_for_messages("useless-else-on-loop") + def visit_for(self, node: nodes.For) -> None: + self._check_else_on_loop(node) + + @utils.only_required_for_messages("useless-else-on-loop") + def visit_while(self, node: nodes.While) -> None: + self._check_else_on_loop(node) + + @utils.only_required_for_messages("nonexistent-operator") + def visit_unaryop(self, node: nodes.UnaryOp) -> None: + """Check use of the non-existent ++ and -- operators.""" + if ( + (node.op in "+-") + and isinstance(node.operand, nodes.UnaryOp) + and (node.operand.op == node.op) + and (node.col_offset + 1 == node.operand.col_offset) + ): + self.add_message("nonexistent-operator", node=node, args=node.op * 2) + + def _check_nonlocal_without_binding(self, node: nodes.Nonlocal, name: str) -> None: + current_scope = node.scope() + while current_scope.parent is not None: + if not isinstance(current_scope, (nodes.ClassDef, nodes.FunctionDef)): + self.add_message("nonlocal-without-binding", args=(name,), node=node) + return + + # Search for `name` in the parent scope if: + # `current_scope` is the same scope in which the `nonlocal` name is declared + # or `name` is not in `current_scope.locals`. + if current_scope is node.scope() or name not in current_scope.locals: + current_scope = current_scope.parent.scope() + continue + + # Okay, found it. + return + + if not isinstance(current_scope, nodes.FunctionDef): + self.add_message( + "nonlocal-without-binding", args=(name,), node=node, confidence=HIGH + ) + + @utils.only_required_for_messages("nonlocal-without-binding") + def visit_nonlocal(self, node: nodes.Nonlocal) -> None: + for name in node.names: + self._check_nonlocal_without_binding(node, name) + + @utils.only_required_for_messages("abstract-class-instantiated") + def visit_call(self, node: nodes.Call) -> None: + """Check instantiating abstract class with + abc.ABCMeta as metaclass. + """ + for inferred in infer_all(node.func): + self._check_inferred_class_is_abstract(inferred, node) + + def _check_inferred_class_is_abstract( + self, inferred: InferenceResult, node: nodes.Call + ) -> None: + if not isinstance(inferred, nodes.ClassDef): + return + + klass = utils.node_frame_class(node) + if klass is inferred: + # Don't emit the warning if the class is instantiated + # in its own body or if the call is not an instance + # creation. If the class is instantiated into its own + # body, we're expecting that it knows what it is doing. + return + + # __init__ was called + abstract_methods = _has_abstract_methods(inferred) + + if not abstract_methods: + return + + metaclass = inferred.metaclass() + + if metaclass is None: + # Python 3.4 has `abc.ABC`, which won't be detected + # by ClassNode.metaclass() + for ancestor in inferred.ancestors(): + if ancestor.qname() == "abc.ABC": + self.add_message( + "abstract-class-instantiated", args=(inferred.name,), node=node + ) + break + + return + + if metaclass.qname() in ABC_METACLASSES: + self.add_message( + "abstract-class-instantiated", args=(inferred.name,), node=node + ) + + def _check_yield_outside_func(self, node: nodes.Yield) -> None: + if not isinstance(node.frame(future=True), (nodes.FunctionDef, nodes.Lambda)): + self.add_message("yield-outside-function", node=node) + + def _check_else_on_loop(self, node: nodes.For | nodes.While) -> None: + """Check that any loop with an else clause has a break statement.""" + if node.orelse and not _loop_exits_early(node): + self.add_message( + "useless-else-on-loop", + node=node, + # This is not optimal, but the line previous + # to the first statement in the else clause + # will usually be the one that contains the else:. + line=node.orelse[0].lineno - 1, + ) + + def _check_in_loop( + self, node: nodes.Continue | nodes.Break, node_name: str + ) -> None: + """Check that a node is inside a for or while loop.""" + for parent in node.node_ancestors(): + if isinstance(parent, (nodes.For, nodes.While)): + if node not in parent.orelse: + return + + if isinstance(parent, (nodes.ClassDef, nodes.FunctionDef)): + break + if ( + isinstance(parent, nodes.TryFinally) + and node in parent.finalbody + and isinstance(node, nodes.Continue) + and not self._py38_plus + ): + self.add_message("continue-in-finally", node=node) + + self.add_message("not-in-loop", node=node, args=node_name) + + def _check_redefinition( + self, redeftype: str, node: nodes.Call | nodes.FunctionDef + ) -> None: + """Check for redefinition of a function / method / class name.""" + parent_frame = node.parent.frame(future=True) + + # Ignore function stubs created for type information + redefinitions = [ + i + for i in parent_frame.locals[node.name] + if not (isinstance(i.parent, nodes.AnnAssign) and i.parent.simple) + ] + defined_self = next( + (local for local in redefinitions if not utils.is_overload_stub(local)), + node, + ) + if defined_self is not node and not astroid.are_exclusive(node, defined_self): + # Additional checks for methods which are not considered + # redefined, since they are already part of the base API. + if ( + isinstance(parent_frame, nodes.ClassDef) + and node.name in REDEFINABLE_METHODS + ): + return + + # Skip typing.overload() functions. + if utils.is_overload_stub(node): + return + + # Exempt functions redefined on a condition. + if isinstance(node.parent, nodes.If): + # Exempt "if not " cases + if ( + isinstance(node.parent.test, nodes.UnaryOp) + and node.parent.test.op == "not" + and isinstance(node.parent.test.operand, nodes.Name) + and node.parent.test.operand.name == node.name + ): + return + + # Exempt "if is not None" cases + # pylint: disable=too-many-boolean-expressions + if ( + isinstance(node.parent.test, nodes.Compare) + and isinstance(node.parent.test.left, nodes.Name) + and node.parent.test.left.name == node.name + and node.parent.test.ops[0][0] == "is" + and isinstance(node.parent.test.ops[0][1], nodes.Const) + and node.parent.test.ops[0][1].value is None + ): + return + + # Check if we have forward references for this node. + try: + redefinition_index = redefinitions.index(node) + except ValueError: + pass + else: + for redefinition in redefinitions[:redefinition_index]: + inferred = utils.safe_infer(redefinition) + if ( + inferred + and isinstance(inferred, astroid.Instance) + and inferred.qname() == TYPING_FORWARD_REF_QNAME + ): + return + + dummy_variables_rgx = self.linter.config.dummy_variables_rgx + if dummy_variables_rgx and dummy_variables_rgx.match(node.name): + return + self.add_message( + "function-redefined", + node=node, + args=(redeftype, defined_self.fromlineno), + ) diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py b/venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py new file mode 120000 index 00000000..804d9267 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/base/comparison_checker.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/68/25/40/e2a38d12f29f45d5d3161640b70adb970f8183b5f75330364862646099 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py b/venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py new file mode 120000 index 00000000..fb376558 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/base/docstring_checker.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/3c/4c/34/56df9d544b4ccd51a9bc4a95ab62bf95d69f2e21d72bcb156c689006b7 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py new file mode 120000 index 00000000..7cf30333 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__init__.py @@ -0,0 +1 @@ +/home/runner/.cache/pip/pool/00/a7/b4/95f325dce94f97b264209c371404ea0b65e85c9fe4ea17a03c1d7709c6 \ No newline at end of file diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__pycache__/__init__.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 00000000..445d686f Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__pycache__/__init__.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__pycache__/checker.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__pycache__/checker.cpython-310.pyc new file mode 100644 index 00000000..879b4915 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__pycache__/checker.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__pycache__/naming_style.cpython-310.pyc b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__pycache__/naming_style.cpython-310.pyc new file mode 100644 index 00000000..290fdd97 Binary files /dev/null and b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/__pycache__/naming_style.cpython-310.pyc differ diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py new file mode 100644 index 00000000..1341edc9 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/checker.py @@ -0,0 +1,686 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt + +"""Basic checker for Python code.""" + +from __future__ import annotations + +import argparse +import collections +import itertools +import re +import sys +from collections.abc import Iterable +from enum import Enum, auto +from re import Pattern +from typing import TYPE_CHECKING, Tuple + +import astroid +from astroid import nodes + +from pylint import constants, interfaces +from pylint.checkers import utils +from pylint.checkers.base.basic_checker import _BasicChecker +from pylint.checkers.base.name_checker.naming_style import ( + KNOWN_NAME_TYPES, + KNOWN_NAME_TYPES_WITH_STYLE, + NAMING_STYLES, + _create_naming_options, +) +from pylint.checkers.utils import is_property_deleter, is_property_setter +from pylint.typing import Options + +if TYPE_CHECKING: + from pylint.lint.pylinter import PyLinter + +_BadNamesTuple = Tuple[nodes.NodeNG, str, str, interfaces.Confidence] + +# Default patterns for name types that do not have styles +DEFAULT_PATTERNS = { + "typevar": re.compile( + r"^_{0,2}(?!T[A-Z])(?:[A-Z]+|(?:[A-Z]+[a-z]+)+T?(? tuple[set[str], set[str]]: + """Returns a tuple of property classes and names. + + Property classes are fully qualified, such as 'abc.abstractproperty' and + property names are the actual names, such as 'abstract_property'. + """ + property_classes = {BUILTIN_PROPERTY} + property_names: set[str] = set() # Not returning 'property', it has its own check. + if config is not None: + property_classes.update(config.property_classes) + property_names.update( + prop.rsplit(".", 1)[-1] for prop in config.property_classes + ) + return property_classes, property_names + + +def _redefines_import(node: nodes.AssignName) -> bool: + """Detect that the given node (AssignName) is inside an + exception handler and redefines an import from the tryexcept body. + + Returns True if the node redefines an import, False otherwise. + """ + current = node + while current and not isinstance(current.parent, nodes.ExceptHandler): + current = current.parent + if not current or not utils.error_of_type(current.parent, ImportError): + return False + try_block = current.parent.parent + for import_node in try_block.nodes_of_class((nodes.ImportFrom, nodes.Import)): + for name, alias in import_node.names: + if alias: + if alias == node.name: + return True + elif name == node.name: + return True + return False + + +def _determine_function_name_type( + node: nodes.FunctionDef, config: argparse.Namespace +) -> str: + """Determine the name type whose regex the function's name should match. + + :param node: A function node. + :param config: Configuration from which to pull additional property classes. + + :returns: One of ('function', 'method', 'attr') + """ + property_classes, property_names = _get_properties(config) + if not node.is_method(): + return "function" + + if is_property_setter(node) or is_property_deleter(node): + # If the function is decorated using the prop_method.{setter,getter} + # form, treat it like an attribute as well. + return "attr" + + decorators = node.decorators.nodes if node.decorators else [] + for decorator in decorators: + # If the function is a property (decorated with @property + # or @abc.abstractproperty), the name type is 'attr'. + if isinstance(decorator, nodes.Name) or ( + isinstance(decorator, nodes.Attribute) + and decorator.attrname in property_names + ): + inferred = utils.safe_infer(decorator) + if ( + inferred + and hasattr(inferred, "qname") + and inferred.qname() in property_classes + ): + return "attr" + return "method" + + +# Name categories that are always consistent with all naming conventions. +EXEMPT_NAME_CATEGORIES = {"exempt", "ignore"} + + +def _is_multi_naming_match( + match: re.Match[str] | None, node_type: str, confidence: interfaces.Confidence +) -> bool: + return ( + match is not None + and match.lastgroup is not None + and match.lastgroup not in EXEMPT_NAME_CATEGORIES + and (node_type != "method" or confidence != interfaces.INFERENCE_FAILURE) + ) + + +class NameChecker(_BasicChecker): + msgs = { + "C0103": ( + '%s name "%s" doesn\'t conform to %s', + "invalid-name", + "Used when the name doesn't conform to naming rules " + "associated to its type (constant, variable, class...).", + ), + "C0104": ( + 'Disallowed name "%s"', + "disallowed-name", + "Used when the name matches bad-names or bad-names-rgxs- (unauthorized names).", + { + "old_names": [ + ("C0102", "blacklisted-name"), + ] + }, + ), + "C0105": ( + "Type variable name does not reflect variance%s", + "typevar-name-incorrect-variance", + "Emitted when a TypeVar name doesn't reflect its type variance. " + "According to PEP8, it is recommended to add suffixes '_co' and " + "'_contra' to the variables used to declare covariant or " + "contravariant behaviour respectively. Invariant (default) variables " + "do not require a suffix. The message is also emitted when invariant " + "variables do have a suffix.", + ), + "C0131": ( + "TypeVar cannot be both covariant and contravariant", + "typevar-double-variance", + 'Emitted when both the "covariant" and "contravariant" ' + 'keyword arguments are set to "True" in a TypeVar.', + ), + "C0132": ( + 'TypeVar name "%s" does not match assigned variable name "%s"', + "typevar-name-mismatch", + "Emitted when a TypeVar is assigned to a variable " + "that does not match its name argument.", + ), + } + + _options: Options = ( + ( + "good-names", + { + "default": ("i", "j", "k", "ex", "Run", "_"), + "type": "csv", + "metavar": "", + "help": "Good variable names which should always be accepted," + " separated by a comma.", + }, + ), + ( + "good-names-rgxs", + { + "default": "", + "type": "regexp_csv", + "metavar": "", + "help": "Good variable names regexes, separated by a comma. If names match any regex," + " they will always be accepted", + }, + ), + ( + "bad-names", + { + "default": ("foo", "bar", "baz", "toto", "tutu", "tata"), + "type": "csv", + "metavar": "", + "help": "Bad variable names which should always be refused, " + "separated by a comma.", + }, + ), + ( + "bad-names-rgxs", + { + "default": "", + "type": "regexp_csv", + "metavar": "", + "help": "Bad variable names regexes, separated by a comma. If names match any regex," + " they will always be refused", + }, + ), + ( + "name-group", + { + "default": (), + "type": "csv", + "metavar": "", + "help": ( + "Colon-delimited sets of names that determine each" + " other's naming style when the name regexes" + " allow several styles." + ), + }, + ), + ( + "include-naming-hint", + { + "default": False, + "type": "yn", + "metavar": "", + "help": "Include a hint for the correct naming format with invalid-name.", + }, + ), + ( + "property-classes", + { + "default": ("abc.abstractproperty",), + "type": "csv", + "metavar": "", + "help": "List of decorators that produce properties, such as " + "abc.abstractproperty. Add to this list to register " + "other decorators that produce valid properties. " + "These decorators are taken in consideration only for invalid-name.", + }, + ), + ) + options: Options = _options + _create_naming_options() + + def __init__(self, linter: PyLinter) -> None: + super().__init__(linter) + self._name_group: dict[str, str] = {} + self._bad_names: dict[str, dict[str, list[_BadNamesTuple]]] = {} + self._name_regexps: dict[str, re.Pattern[str]] = {} + self._name_hints: dict[str, str] = {} + self._good_names_rgxs_compiled: list[re.Pattern[str]] = [] + self._bad_names_rgxs_compiled: list[re.Pattern[str]] = [] + + def open(self) -> None: + self.linter.stats.reset_bad_names() + for group in self.linter.config.name_group: + for name_type in group.split(":"): + self._name_group[name_type] = f"group_{group}" + + regexps, hints = self._create_naming_rules() + self._name_regexps = regexps + self._name_hints = hints + self._good_names_rgxs_compiled = [ + re.compile(rgxp) for rgxp in self.linter.config.good_names_rgxs + ] + self._bad_names_rgxs_compiled = [ + re.compile(rgxp) for rgxp in self.linter.config.bad_names_rgxs + ] + + def _create_naming_rules(self) -> tuple[dict[str, Pattern[str]], dict[str, str]]: + regexps: dict[str, Pattern[str]] = {} + hints: dict[str, str] = {} + + for name_type in KNOWN_NAME_TYPES: + if name_type in KNOWN_NAME_TYPES_WITH_STYLE: + naming_style_name = getattr( + self.linter.config, f"{name_type}_naming_style" + ) + regexps[name_type] = NAMING_STYLES[naming_style_name].get_regex( + name_type + ) + else: + naming_style_name = "predefined" + regexps[name_type] = DEFAULT_PATTERNS[name_type] + + custom_regex_setting_name = f"{name_type}_rgx" + custom_regex = getattr(self.linter.config, custom_regex_setting_name, None) + if custom_regex is not None: + regexps[name_type] = custom_regex + + if custom_regex is not None: + hints[name_type] = f"{custom_regex.pattern!r} pattern" + else: + hints[name_type] = f"{naming_style_name} naming style" + + return regexps, hints + + @utils.only_required_for_messages("disallowed-name", "invalid-name") + def visit_module(self, node: nodes.Module) -> None: + self._check_name("module", node.name.split(".")[-1], node) + self._bad_names = {} + + def leave_module(self, _: nodes.Module) -> None: + for all_groups in self._bad_names.values(): + if len(all_groups) < 2: + continue + groups: collections.defaultdict[ + int, list[list[_BadNamesTuple]] + ] = collections.defaultdict(list) + min_warnings = sys.maxsize + prevalent_group, _ = max(all_groups.items(), key=lambda item: len(item[1])) + for group in all_groups.values(): + groups[len(group)].append(group) + min_warnings = min(len(group), min_warnings) + if len(groups[min_warnings]) > 1: + by_line = sorted( + groups[min_warnings], + key=lambda group: min( # type: ignore[no-any-return] + warning[0].lineno + for warning in group + if warning[0].lineno is not None + ), + ) + warnings: Iterable[_BadNamesTuple] = itertools.chain(*by_line[1:]) + else: + warnings = groups[min_warnings][0] + for args in warnings: + self._raise_name_warning(prevalent_group, *args) + + @utils.only_required_for_messages("disallowed-name", "invalid-name") + def visit_classdef(self, node: nodes.ClassDef) -> None: + self._check_name("class", node.name, node) + for attr, anodes in node.instance_attrs.items(): + if not any(node.instance_attr_ancestors(attr)): + self._check_name("attr", attr, anodes[0]) + + @utils.only_required_for_messages("disallowed-name", "invalid-name") + def visit_functiondef(self, node: nodes.FunctionDef) -> None: + # Do not emit any warnings if the method is just an implementation + # of a base class method. + confidence = interfaces.HIGH + if node.is_method(): + if utils.overrides_a_method(node.parent.frame(future=True), node.name): + return + confidence = ( + interfaces.INFERENCE + if utils.has_known_bases(node.parent.frame(future=True)) + else interfaces.INFERENCE_FAILURE + ) + + self._check_name( + _determine_function_name_type(node, config=self.linter.config), + node.name, + node, + confidence, + ) + # Check argument names + args = node.args.args + if args is not None: + self._recursive_check_names(args) + + visit_asyncfunctiondef = visit_functiondef + + @utils.only_required_for_messages( + "disallowed-name", + "invalid-name", + "typevar-name-incorrect-variance", + "typevar-double-variance", + "typevar-name-mismatch", + ) + def visit_assignname( # pylint: disable=too-many-branches + self, node: nodes.AssignName + ) -> None: + """Check module level assigned names.""" + frame = node.frame(future=True) + assign_type = node.assign_type() + + # Check names defined in comprehensions + if isinstance(assign_type, nodes.Comprehension): + self._check_name("inlinevar", node.name, node) + + # Check names defined in module scope + elif isinstance(frame, nodes.Module): + # Check names defined in Assign nodes + if isinstance(assign_type, nodes.Assign): + inferred_assign_type = utils.safe_infer(assign_type.value) + + # Check TypeVar's and TypeAliases assigned alone or in tuple assignment + if isinstance(node.parent, nodes.Assign): + if self._assigns_typevar(assign_type.value): + self._check_name("typevar", assign_type.targets[0].name, node) + return + if self._assigns_typealias(assign_type.value): + self._check_name("typealias", assign_type.targets[0].name, node) + return + + if ( + isinstance(node.parent, nodes.Tuple) + and isinstance(assign_type.value, nodes.Tuple) + # protect against unbalanced tuple unpacking + and node.parent.elts.index(node) < len(assign_type.value.elts) + ): + assigner = assign_type.value.elts[node.parent.elts.index(node)] + if self._assigns_typevar(assigner): + self._check_name( + "typevar", + assign_type.targets[0] + .elts[node.parent.elts.index(node)] + .name, + node, + ) + return + if self._assigns_typealias(assigner): + self._check_name( + "typealias", + assign_type.targets[0] + .elts[node.parent.elts.index(node)] + .name, + node, + ) + return + + # Check classes (TypeVar's are classes so they need to be excluded first) + elif isinstance(inferred_assign_type, nodes.ClassDef): + self._check_name("class", node.name, node) + + # Don't emit if the name redefines an import in an ImportError except handler. + elif not _redefines_import(node) and isinstance( + inferred_assign_type, nodes.Const + ): + self._check_name("const", node.name, node) + else: + self._check_name( + "variable", node.name, node, disallowed_check_only=True + ) + + # Check names defined in AnnAssign nodes + elif isinstance(assign_type, nodes.AnnAssign): + if utils.is_assign_name_annotated_with(node, "Final"): + self._check_name("const", node.name, node) + elif self._assigns_typealias(assign_type.annotation): + self._check_name("typealias", node.name, node) + + # Check names defined in function scopes + elif isinstance(frame, nodes.FunctionDef): + # global introduced variable aren't in the function locals + if node.name in frame and node.name not in frame.argnames(): + if not _redefines_import(node): + self._check_name("variable", node.name, node) + + # Check names defined in class scopes + elif isinstance(frame, nodes.ClassDef): + if not list(frame.local_attr_ancestors(node.name)): + for ancestor in frame.ancestors(): + if utils.is_enum(ancestor) or utils.is_assign_name_annotated_with( + node, "Final" + ): + self._check_name("class_const", node.name, node) + break + else: + self._check_name("class_attribute", node.name, node) + + def _recursive_check_names(self, args: list[nodes.AssignName]) -> None: + """Check names in a possibly recursive list .""" + for arg in args: + self._check_name("argument", arg.name, arg) + + def _find_name_group(self, node_type: str) -> str: + return self._name_group.get(node_type, node_type) + + def _raise_name_warning( + self, + prevalent_group: str | None, + node: nodes.NodeNG, + node_type: str, + name: str, + confidence: interfaces.Confidence, + warning: str = "invalid-name", + ) -> None: + type_label = constants.HUMAN_READABLE_TYPES[node_type] + hint = self._name_hints[node_type] + if prevalent_group: + # This happens in the multi naming match case. The expected + # prevalent group needs to be spelled out to make the message + # correct. + hint = f"the `{prevalent_group}` group in the {hint}" + if self.linter.config.include_naming_hint: + hint += f" ({self._name_regexps[node_type].pattern!r} pattern)" + args = ( + (type_label.capitalize(), name, hint) + if warning == "invalid-name" + else (type_label.capitalize(), name) + ) + + self.add_message(warning, node=node, args=args, confidence=confidence) + self.linter.stats.increase_bad_name(node_type, 1) + + def _name_allowed_by_regex(self, name: str) -> bool: + return name in self.linter.config.good_names or any( + pattern.match(name) for pattern in self._good_names_rgxs_compiled + ) + + def _name_disallowed_by_regex(self, name: str) -> bool: + return name in self.linter.config.bad_names or any( + pattern.match(name) for pattern in self._bad_names_rgxs_compiled + ) + + def _check_name( + self, + node_type: str, + name: str, + node: nodes.NodeNG, + confidence: interfaces.Confidence = interfaces.HIGH, + disallowed_check_only: bool = False, + ) -> None: + """Check for a name using the type's regexp.""" + + def _should_exempt_from_invalid_name(node: nodes.NodeNG) -> bool: + if node_type == "variable": + inferred = utils.safe_infer(node) + if isinstance(inferred, nodes.ClassDef): + return True + return False + + if self._name_allowed_by_regex(name=name): + return + if self._name_disallowed_by_regex(name=name): + self.linter.stats.increase_bad_name(node_type, 1) + self.add_message( + "disallowed-name", node=node, args=name, confidence=interfaces.HIGH + ) + return + regexp = self._name_regexps[node_type] + match = regexp.match(name) + + if _is_multi_naming_match(match, node_type, confidence): + name_group = self._find_name_group(node_type) + bad_name_group = self._bad_names.setdefault(name_group, {}) + # Ignored because this is checked by the if statement + warnings = bad_name_group.setdefault(match.lastgroup, []) # type: ignore[union-attr, arg-type] + warnings.append((node, node_type, name, confidence)) + + if ( + match is None + and not disallowed_check_only + and not _should_exempt_from_invalid_name(node) + ): + self._raise_name_warning(None, node, node_type, name, confidence) + + # Check TypeVar names for variance suffixes + if node_type == "typevar": + self._check_typevar(name, node) + + @staticmethod + def _assigns_typevar(node: nodes.NodeNG | None) -> bool: + """Check if a node is assigning a TypeVar.""" + if isinstance(node, astroid.Call): + inferred = utils.safe_infer(node.func) + if ( + isinstance(inferred, astroid.ClassDef) + and inferred.qname() in TYPE_VAR_QNAME + ): + return True + return False + + @staticmethod + def _assigns_typealias(node: nodes.NodeNG | None) -> bool: + """Check if a node is assigning a TypeAlias.""" + inferred = utils.safe_infer(node) + if isinstance(inferred, nodes.ClassDef): + if inferred.qname() == ".Union": + return True + elif isinstance(inferred, nodes.FunctionDef): + if inferred.qname() == "typing.TypeAlias": + return True + return False + + def _check_typevar(self, name: str, node: nodes.AssignName) -> None: + """Check for TypeVar lint violations.""" + if isinstance(node.parent, nodes.Assign): + keywords = node.assign_type().value.keywords + args = node.assign_type().value.args + elif isinstance(node.parent, nodes.Tuple): + keywords = ( + node.assign_type().value.elts[node.parent.elts.index(node)].keywords + ) + args = node.assign_type().value.elts[node.parent.elts.index(node)].args + + variance = TypeVarVariance.invariant + name_arg = None + for kw in keywords: + if variance == TypeVarVariance.double_variant: + pass + elif kw.arg == "covariant" and kw.value.value: + variance = ( + TypeVarVariance.covariant + if variance != TypeVarVariance.contravariant + else TypeVarVariance.double_variant + ) + elif kw.arg == "contravariant" and kw.value.value: + variance = ( + TypeVarVariance.contravariant + if variance != TypeVarVariance.covariant + else TypeVarVariance.double_variant + ) + + if kw.arg == "name" and isinstance(kw.value, nodes.Const): + name_arg = kw.value.value + + if name_arg is None and args and isinstance(args[0], nodes.Const): + name_arg = args[0].value + + if variance == TypeVarVariance.double_variant: + self.add_message( + "typevar-double-variance", + node=node, + confidence=interfaces.INFERENCE, + ) + self.add_message( + "typevar-name-incorrect-variance", + node=node, + args=("",), + confidence=interfaces.INFERENCE, + ) + elif variance == TypeVarVariance.covariant and not name.endswith("_co"): + suggest_name = f"{re.sub('_contra$', '', name)}_co" + self.add_message( + "typevar-name-incorrect-variance", + node=node, + args=(f'. "{name}" is covariant, use "{suggest_name}" instead'), + confidence=interfaces.INFERENCE, + ) + elif variance == TypeVarVariance.contravariant and not name.endswith("_contra"): + suggest_name = f"{re.sub('_co$', '', name)}_contra" + self.add_message( + "typevar-name-incorrect-variance", + node=node, + args=(f'. "{name}" is contravariant, use "{suggest_name}" instead'), + confidence=interfaces.INFERENCE, + ) + elif variance == TypeVarVariance.invariant and ( + name.endswith("_co") or name.endswith("_contra") + ): + suggest_name = re.sub("_contra$|_co$", "", name) + self.add_message( + "typevar-name-incorrect-variance", + node=node, + args=(f'. "{name}" is invariant, use "{suggest_name}" instead'), + confidence=interfaces.INFERENCE, + ) + + if name_arg is not None and name_arg != name: + self.add_message( + "typevar-name-mismatch", + node=node, + args=(name_arg, name), + confidence=interfaces.INFERENCE, + ) diff --git a/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py new file mode 100644 index 00000000..8ccec428 --- /dev/null +++ b/venv/lib/python3.10/site-packages/pylint/checkers/base/name_checker/naming_style.py @@ -0,0 +1,185 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt + +from __future__ import annotations + +import re +from re import Pattern + +from pylint import constants +from pylint.typing import OptionDict, Options + + +class NamingStyle: + """Class to register all accepted forms of a single naming style. + + It may seem counter-intuitive that single naming style has multiple "accepted" + forms of regular expressions, but we need to special-case stuff like dunder + names in method names. + """ + + ANY: Pattern[str] = re.compile(".*") + CLASS_NAME_RGX: Pattern[str] = ANY + MOD_NAME_RGX: Pattern[str] = ANY + CONST_NAME_RGX: Pattern[str] = ANY + COMP_VAR_RGX: Pattern[str] = ANY + DEFAULT_NAME_RGX: Pattern[str] = ANY + CLASS_ATTRIBUTE_RGX: Pattern[str] = ANY + + @classmethod + def get_regex(cls, name_type: str) -> Pattern[str]: + return { + "module": cls.MOD_NAME_RGX, + "const": cls.CONST_NAME_RGX, + "class": cls.CLASS_NAME_RGX, + "function": cls.DEFAULT_NAME_RGX, + "method": cls.DEFAULT_NAME_RGX, + "attr": cls.DEFAULT_NAME_RGX, + "argument": cls.DEFAULT_NAME_RGX, + "variable": cls.DEFAULT_NAME_RGX, + "class_attribute": cls.CLASS_ATTRIBUTE_RGX, + "class_const": cls.CONST_NAME_RGX, + "inlinevar": cls.COMP_VAR_RGX, + }[name_type] + + +class SnakeCaseStyle(NamingStyle): + """Regex rules for snake_case naming style.""" + + CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]+$") + MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]*$") + CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]*|__.*__)$") + COMP_VAR_RGX = CLASS_NAME_RGX + DEFAULT_NAME_RGX = re.compile( + r"([^\W\dA-Z][^\WA-Z]{2,}|_[^\WA-Z]*|__[^\WA-Z\d_][^\WA-Z]+__)$" + ) + CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]{2,}|__.*__)$") + + +class CamelCaseStyle(NamingStyle): + """Regex rules for camelCase naming style.""" + + CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]+$") + MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]*$") + CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]*|__.*__)$") + COMP_VAR_RGX = MOD_NAME_RGX + DEFAULT_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]{2,}|__[^\W\dA-Z_]\w+__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\W_]{2,}|__.*__)$") + + +class PascalCaseStyle(NamingStyle): + """Regex rules for PascalCase naming style.""" + + CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\W_]+$") + MOD_NAME_RGX = CLASS_NAME_RGX + CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]*|__.*__)$") + COMP_VAR_RGX = CLASS_NAME_RGX + DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]{2,}|__[^\W\dA-Z_]\w+__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\W_]{2,}$") + + +class UpperCaseStyle(NamingStyle): + """Regex rules for UPPER_CASE naming style.""" + + CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\Wa-z]+$") + MOD_NAME_RGX = CLASS_NAME_RGX + CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]*|__.*__)$") + COMP_VAR_RGX = CLASS_NAME_RGX + DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]{2,}|__[^\W\dA-Z_]\w+__)$") + CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\Wa-z]{2,}$") + + +class AnyStyle(NamingStyle): + pass + + +NAMING_STYLES = { + "snake_case": SnakeCaseStyle, + "camelCase": CamelCaseStyle, + "PascalCase": PascalCaseStyle, + "UPPER_CASE": UpperCaseStyle, + "any": AnyStyle, +} + +# Name types that have a style option +KNOWN_NAME_TYPES_WITH_STYLE = { + "module", + "const", + "class", + "function", + "method", + "attr", + "argument", + "variable", + "class_attribute", + "class_const", + "inlinevar", +} + + +DEFAULT_NAMING_STYLES = { + "module": "snake_case", + "const": "UPPER_CASE", + "class": "PascalCase", + "function": "snake_case", + "method": "snake_case", + "attr": "snake_case", + "argument": "snake_case", + "variable": "snake_case", + "class_attribute": "any", + "class_const": "UPPER_CASE", + "inlinevar": "any", +} + + +# Name types that have a 'rgx' option +KNOWN_NAME_TYPES = { + *KNOWN_NAME_TYPES_WITH_STYLE, + "typevar", + "typealias", +} + + +def _create_naming_options() -> Options: + name_options: list[tuple[str, OptionDict]] = [] + for name_type in sorted(KNOWN_NAME_TYPES): + human_readable_name = constants.HUMAN_READABLE_TYPES[name_type] + name_type_hyphened = name_type.replace("_", "-") + + help_msg = f"Regular expression matching correct {human_readable_name} names. " + if name_type in KNOWN_NAME_TYPES_WITH_STYLE: + help_msg += f"Overrides {name_type_hyphened}-naming-style. " + help_msg += ( + f"If left empty, {human_readable_name} names will be checked " + "with the set naming style." + ) + + # Add style option for names that support it + if name_type in KNOWN_NAME_TYPES_WITH_STYLE: + default_style = DEFAULT_NAMING_STYLES[name_type] + name_options.append( + ( + f"{name_type_hyphened}-naming-style", + { + "default": default_style, + "type": "choice", + "choices": list(NAMING_STYLES.keys()), + "metavar": "