A Python Parser

Overview

parso - A Python Parser

Travis CI build status Coverage Status PyPI Downloads

https://raw.githubusercontent.com/davidhalter/parso/master/docs/_static/logo_characters.png

Parso is a Python parser that supports error recovery and round-trip parsing for different Python versions (in multiple Python versions). Parso is also able to list multiple syntax errors in your python file.

Parso has been battle-tested by jedi. It was pulled out of jedi to be useful for other projects as well.

Parso consists of a small API to parse Python and analyse the syntax tree.

A simple example:

>>> import parso
>>> module = parso.parse('hello + 1', version="3.9")
>>> expr = module.children[0]
>>> expr
PythonNode(arith_expr, [<Name: hello@1,0>, <Operator: +>, <Number: 1>])
>>> print(expr.get_code())
hello + 1
>>> name = expr.children[0]
>>> name
<Name: hello@1,0>
>>> name.end_pos
(1, 5)
>>> expr.end_pos
(1, 9)

To list multiple issues:

>>> grammar = parso.load_grammar()
>>> module = grammar.parse('foo +\nbar\ncontinue')
>>> error1, error2 = grammar.iter_errors(module)
>>> error1.message
'SyntaxError: invalid syntax'
>>> error2.message
"SyntaxError: 'continue' not properly in loop"

Resources

Installation

pip install parso

Future

  • There will be better support for refactoring and comments. Stay tuned.
  • There's a WIP PEP8 validator. It's however not in a good shape, yet.

Known Issues

  • async/await are already used as keywords in Python3.6.
  • from __future__ import print_function is not ignored.

Acknowledgements

  • Guido van Rossum (@gvanrossum) for creating the parser generator pgen2 (originally used in lib2to3).
  • Salome Schneider for the extremely awesome parso logo.
Comments
  • Make the _split_prefix public and documented to make comment parsing easy.

    Make the _split_prefix public and documented to make comment parsing easy.

    Comments being baked into the prefix member of nodes is unintuitive and awkward to use for parsing I think. It would make much more sense to have a Comment class.

    (This has come up while I'm working on mutmut).

    opened by boxed 20
  • Drop support for EOL Python 2.6 and 3.3

    Drop support for EOL Python 2.6 and 3.3

    Python 2.6 and 3.3 are EOL and no longer receiving security updates (or any updates) from the core Python team.

    They're also little used.

    Here's the pip installs for parso from PyPI for last month:

    | python_version | percent | download_count | | -------------- | ------: | -------------: | | 3.6 | 55.88% | 188,129 | | 3.5 | 23.47% | 79,005 | | 3.4 | 16.04% | 53,990 | | 2.7 | 4.01% | 13,504 | | 3.7 | 0.37% | 1,259 | | 3.3 | 0.23% | 773 | | 2.6 | 0.00% | 13 | | 3.8 | 0.00% | 6 |

    Source: pypinfo --start-date -39 --end-date -10 --percent --pip --markdown parso pyversion

    opened by hugovk 15
  • AssertionError thrown on autocomplete keyword `from`

    AssertionError thrown on autocomplete keyword `from`

    Running from a pipenv managed virtualenv (Python 3.6.5).

    I'm using Neovim v0.3.1 with deoplete-jedi. Trying to autocomplete after typing any keyword throws this exception:

    [deoplete] Traceback (most recent call last):
      File "/home/sett/.vim/plugged/deoplete.nvim/rplugin/python3/deoplete/child.py", line 179, in _gather_results
        result = self._get_result(context, source)
      File "/home/sett/.vim/plugged/deoplete.nvim/rplugin/python3/deoplete/child.py", line 234, in _get_result
        ctx['candidates'] = source.gather_candidates(ctx)
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/sources/deoplete_jedi/profiler.py", line 37, in wrapper
        return func(self, *args, **kwargs)
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/sources/deoplete_jedi.py", line 167, in gather_candidates
        environment=self._env)
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/sources/deoplete_jedi/profiler.py", line 37, in wrapper
        return func(self, *args, **kwargs)
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/sources/deoplete_jedi.py", line 119, in get_script
        return jedi.Script(source, line, col, filename, environment=self._env)
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/vendored/jedi/jedi/api/__init__.py", line 118, in __init__
        cache_path=settings.cache_directory,
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/vendored/jedi/jedi/evaluate/__init__.py", line 388, in parse_and_get_code
        return self.grammar.parse(code=code, path=path, **kwargs), code
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/vendored/parso/parso/grammar.py", line 76, in parse
        return self._parse(code=code, **kwargs)
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/vendored/parso/parso/grammar.py", line 126, in _parse
        new_lines=lines
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/vendored/parso/parso/python/diff.py", line 236, in update
        self._copy_from_old_parser(line_offset, i2, j2)
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/vendored/parso/parso/python/diff.py", line 289, in _copy_from_old_parser
        line_offset
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/vendored/parso/parso/python/diff.py", line 598, in copy_nodes
        self.prefix,
      File "/home/sett/.vim/plugged/deoplete-jedi/rplugin/python3/deoplete/vendored/parso/parso/python/diff.py", line 686, in _copy_nodes
        assert last_line_offset_leaf == ':'
    AssertionError
    Error from jedi: AssertionError().  Use :messages / see above for error details.
    

    Simply removing/commenting offending line (line 686, diff.py) makes it work normally.

    opened by sw00 13
  • diff.py logs to root logger

    diff.py logs to root logger

    in various places in https://github.com/davidhalter/parso/blob/master/parso/python/diff.py, logging is done using the root logger instead of creating a separate logger using logging.getLogger. this makes it difficult to customize the logging behavior of parso separately from other loggers in one's app.

    example:

    https://github.com/davidhalter/parso/blob/0ec02e1d7feb8e79a238f79a06aa1749f02794c4/parso/python/diff.py#L119

    opened by density 13
  • Usability issue: complicated to separate between multiplication/pow and argument unpacking

    Usability issue: complicated to separate between multiplication/pow and argument unpacking

    The * in foo * bar produces produces an operator AST node with value *, same as the * in foo(*bar) even though these are radically different things. The same goes for **. It would be much nicer if these were differentiated in the AST clearly.

    This was found when working on mutmut.

    opened by boxed 11
  • Increase the usage of augmented assignment statements

    Increase the usage of augmented assignment statements

    :eyes: Some source code analysis tools can help to find opportunities for improving software components. :thought_balloon: I propose to increase the usage of augmented assignment statements accordingly.

    Would you like to integrate anything from a transformation result which can be generated by a command like the following? (:point_right: Please check also for questionable change suggestions because of an evolving search pattern.)

    [Markus_Elfring@fedora lokal]$ perl -p -i.orig -0777 -e 's/^(?<indentation>\s+)(?<target>\S+)\s*=\s*\k<target>[ \t]*(?<operator>[+\-%&|^@]|\*\*?|\/\/?|<<|>>)/$+{indentation}$+{target} $+{operator}=/gm' $(find ~/Projekte/parso/lokal -name '*.py')
    

    How do you think about to improve six source code places? :thinking:


    :crystal_ball: How would a corresponding refactoring be achieved by the means of the software “parso”?

    opened by elfring 10
  • Named unicode escapes with spaces marked as syntax error in f-strings

    Named unicode escapes with spaces marked as syntax error in f-strings

    Given the following code:

    def bullet(text: str) -> str:
        return f'\N{BULLET} {text}'
    
    
    def error(text: str) -> str:
        return f'\N{NO ENTRY} {text}'
    

    A syntax error is marked for ENTRY but it is valid Python code.

    bug 
    opened by bryanforbes 10
  • Dropping Python <3.6

    Dropping Python <3.6

    I'm currently trying to get rid of a lot of old code. The next release will not feature the old Pythons anymore :tada: !

    IMO we should also drop Python 2 parsing. Do you agree @isidentical ? (Also don't review yet, a lot is going to change)

    I guess after merging this branch anyone can feel free to make various Python 3 improvements.

    opened by davidhalter 9
  • Clear inactive cache files

    Clear inactive cache files

    Cache files that weren't accessed in the last 30 days will be automatically garbage collected. This collection happens when the save_module is called via a lock system that would make it happen only one time per day. Resolves #120

    opened by isidentical 9
  • Parso Cache Garbage Collection

    Parso Cache Garbage Collection

    Currently people using parso might be using a lot of disk space after a while. ~/.cache/parso might get quite big. (or whatever it is on other OS's, see parso/cache.py.

    IMO we should probably clean up in parso.cache.save_module. That function should just check "once a day" if files are older than a month. In that case they should be deleted. It should be fine to delete stuff that we might parse again, since the cache is not extremely important and just there to speed up some things.

    As a small additional thing, we should probably use scandir, see also https://github.com/davidhalter/jedi/blob/7fd5c8af8ff04711599fea10ca8babe51b280464/jedi/_compatibility.py#L29-L47 for Python 2.7/3.5. However we could also just remove Python 2.7/3.5 support for this feature. I don't plan on maintaining it for a long time.

    opened by davidhalter 9
  • Possible misuse of `hashlib.sha256`

    Possible misuse of `hashlib.sha256`

    Parso includes calls to hashlib.sha256 with an argument.

    The documentation doesn't mention arguments. In my Windows Python 3.8 it does take arguments, but here is a log from a package build in Fedora, which failed with:

    parser = <class 'parso.python.parser.Parser'>
    diff_parser = <class 'parso.python.diff.DiffParser'>
        def __init__(self, text, tokenizer, parser=BaseParser, diff_parser=None):
            self._pgen_grammar = generate_grammar(
                text,
                token_namespace=self._get_token_namespace()
            )
            self._parser = parser
            self._tokenizer = tokenizer
            self._diff_parser = diff_parser
    >       self._hashed = hashlib.sha256(text.encode("utf-8")).hexdigest()
    E       TypeError: sha256() takes no arguments
    /usr/lib/python3.8/site-packages/parso/grammar.py:39: TypeError
    
    opened by aivarannamaa 9
  • Walrus causes unnecessary SyntaxError

    Walrus causes unnecessary SyntaxError

    This is probably simple to fix, see:

    >>> import jedi
    >>> x = jedi.Script('f(a := 1, b)\n').get_syntax_errors()
    >>> x[0].get_message()
    'SyntaxError: positional argument follows keyword argument'
    >>> jedi.Script('f(a := 1)\n').get_syntax_errors()
    []
    
    opened by davidhalter 0
  • KeyError: ReservedString(})

    KeyError: ReservedString(})

    I've tried to apply mutmut to PyPDF2 and failed. Initially I thought the issue was with mutmut ( https://github.com/boxed/mutmut/issues/252 ) , but I guess it's a parso bug.

    Minimal example

    import parso
    
    parso.parse('stream.write(b_(rf"\{ord_(c):0>3o}"))', error_recovery=False)
    

    Traceback

    Traceback (most recent call last):
      File "/home/moose/.pyenv/versions/3.10.2/lib/python3.10/site-packages/parso/parser.py", line 180, in _add_token
        plan = stack[-1].dfa.transitions[transition]
    KeyError: ReservedString(})
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/moose/.pyenv/versions/3.10.2/lib/python3.10/site-packages/parso/__init__.py", line 58, in parse
        return grammar.parse(code, **kwargs)
      File "/home/moose/.pyenv/versions/3.10.2/lib/python3.10/site-packages/parso/grammar.py", line 156, in parse
        root_node = p.parse(tokens=tokens)
      File "/home/moose/.pyenv/versions/3.10.2/lib/python3.10/site-packages/parso/python/parser.py", line 79, in parse
        return super().parse(tokens)
      File "/home/moose/.pyenv/versions/3.10.2/lib/python3.10/site-packages/parso/parser.py", line 129, in parse
        self._add_token(token)
      File "/home/moose/.pyenv/versions/3.10.2/lib/python3.10/site-packages/parso/parser.py", line 186, in _add_token
        self.error_recovery(token)
      File "/home/moose/.pyenv/versions/3.10.2/lib/python3.10/site-packages/parso/python/parser.py", line 140, in error_recovery
        return super().error_recovery(token)
      File "/home/moose/.pyenv/versions/3.10.2/lib/python3.10/site-packages/parso/parser.py", line 152, in error_recovery
        raise ParserSyntaxError('SyntaxError: invalid syntax', error_leaf)
    parso.parser.ParserSyntaxError: ('SyntaxError: invalid syntax', <ErrorLeaf: PythonTokenTypes.OP:'}', (1, 33)>)
    

    Environment

    $ python --version     
    Python 3.10.2
    
    >>> parso.__version__
    '0.8.3'
    
    bug 
    opened by MartinThoma 1
  • Python 3.10: Parenthesized context managers

    Python 3.10: Parenthesized context managers

    Besides match case, python 3.9 alpha6 / 3.10 also added parenthesized imports and context-managers. Parenthesized imports works no problem in jedi, but parenthesized context managers lead to a syntax error:

    >>> import jedi
    >>> source = '''
    ... with (
    ...     open('/dev/stdout', 'w') as a,
    ...     open('/dev/stdout', 'w') as b,
    ... ):
    ...     a.write('a')
    ...     b.write('b')
    ... '''
    >>> script = jedi.Script(source, path='example.py')
    >>> [print(x, x.get_message()) for x in script.get_syntax_errors()]
    <SyntaxError from=(3, 29) to=(3, 31)> SyntaxError: invalid syntax
    <SyntaxError from=(4, 29) to=(4, 31)> SyntaxError: invalid syntax
    <SyntaxError from=(5, 0) to=(5, 1)> SyntaxError: invalid syntax
    <SyntaxError from=(6, 0) to=(6, 4)> IndentationError: unexpected indent
    

    See:

    • https://docs.python.org/3/whatsnew/3.10.html#parenthesized-context-managers

    Probably relates to:

    • davidhalter/jedi#1830
    opened by gfokkema 3
  • raw-format string will be recognized as an error

    raw-format string will be recognized as an error

    Code snippet:

    im.convert('1').save(rf'script\temp\{ord(c)}.png')
                                               ^ SyntaxError: invalid syntax *jedi*
    

    rf - a convenient way of invoiding anomalous backslash in string, isn't it?

    bug 
    opened by eternal-io 3
  • Document the refactoring API

    Document the refactoring API

    I'm starting to look for a library to do source transformations and I think parso could handle it via its parso.Grammar.refactor() method, but there's not docs on how to use it.

    help wanted 
    opened by brettcannon 11
Optional static typing for Python 3 and 2 (PEP 484)

Mypy: Optional Static Typing for Python Got a question? Join us on Gitter! We don't have a mailing list; but we are always happy to answer questions o

Python 14.4k Jan 8, 2023
A simple program which checks Python source files for errors

Pyflakes A simple program which checks Python source files for errors. Pyflakes analyzes programs and detects various errors. It works by parsing the

Python Code Quality Authority 1.2k Dec 30, 2022
Performant type-checking for python.

Pyre is a performant type checker for Python compliant with PEP 484. Pyre can analyze codebases with millions of lines of code incrementally – providi

Facebook 6.2k Jan 4, 2023
A static type analyzer for Python code

pytype - ?? ✔ Pytype checks and infers types for your Python code - without requiring type annotations. Pytype can: Lint plain Python code, flagging c

Google 4k Dec 31, 2022
The strictest and most opinionated python linter ever!

wemake-python-styleguide Welcome to the strictest and most opinionated python linter ever. wemake-python-styleguide is actually a flake8 plugin with s

wemake.services 2.1k Jan 1, 2023
Static type checker for Python

Static type checker for Python Speed Pyright is a fast type checker meant for large Python source bases. It can run in a “watch” mode and performs fas

Microsoft 9.2k Jan 3, 2023
Tool to check the completeness of MANIFEST.in for Python packages

check-manifest Are you a Python developer? Have you uploaded packages to the Python Package Index? Have you accidentally uploaded broken packages with

Marius Gedminas 270 Dec 26, 2022
A python documentation linter which checks that the docstring description matches the definition.

Darglint A functional docstring linter which checks whether a docstring's description matches the actual function/method implementation. Darglint expe

Terrence Reilly 463 Dec 31, 2022
Flake8 plugin that checks import order against various Python Style Guides

flake8-import-order A flake8 and Pylama plugin that checks the ordering of your imports. It does not check anything else about the imports. Merely tha

Python Code Quality Authority 270 Nov 24, 2022
Flake8 extension for checking quotes in python

Flake8 Extension to lint for quotes. Major update in 2.0.0 We automatically encourage avoiding escaping quotes as per PEP 8. To disable this, use --no

Zachary Heller 157 Dec 13, 2022
Check for python builtins being used as variables or parameters

Flake8 Builtins plugin Check for python builtins being used as variables or parameters. Imagine some code like this: def max_values(list, list2):

Gil Forcada Codinachs 98 Jan 8, 2023
flake8 plugin to run black for checking Python coding style

flake8-black Introduction This is an MIT licensed flake8 plugin for validating Python code style with the command line code formatting tool black. It

Peter Cock 146 Dec 15, 2022
Custom Python linting through AST expressions

bellybutton bellybutton is a customizable, easy-to-configure linting engine for Python. What is this good for? Tools like pylint and flake8 provide, o

H. Chase Stevens 249 Dec 31, 2022
Unbearably fast O(1) runtime type-checking in pure Python.

Look for the bare necessities, the simple bare necessities. Forget about your worries and your strife. — The Jungle Book.

beartype 1.4k Jan 1, 2023
Naming Convention checker for Python

PEP 8 Naming Conventions Check your code against PEP 8 naming conventions. This module provides a plugin for flake8, the Python code checker. (It repl

Python Code Quality Authority 411 Dec 23, 2022
Code audit tool for python.

Pylama Code audit tool for Python and JavaScript. Pylama wraps these tools: pycodestyle (formerly pep8) © 2012-2013, Florent Xicluna; pydocstyle (form

Kirill Klenov 967 Jan 7, 2023
Flake8 extension for enforcing trailing commas in python

Flake8 Extension to enforce better comma placement. Usage If you are using flake8 it's as easy as: pip install flake8-commas Now you can avoid those a

Python Code Quality Authority 127 Sep 3, 2022
Tool for pinpointing circular imports in Python. Find cyclic imports in any project

Pycycle: Find and fix circular imports in python projects Pycycle is an experimental project that aims to help python developers fix their circular de

Vadim Kravcenko 311 Dec 15, 2022
Reference implementation of sentinels for the Python stdlib

Sentinels This is a reference implementation of a utility for the definition of sentinel values in Python. This also includes a draft PEP for the incl

Tal Einat 22 Aug 27, 2022