Skip to content
Merged
21 changes: 17 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
name: Build

on: [push, pull_request]
on:
push:
pull_request:
workflow_call:
inputs:
debug_ssh_session:
required: false
type: boolean


env:
PYTEST_ADDOPTS: --color=yes
Expand All @@ -17,12 +25,17 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
pip install .[qa]
pip install .[qa] .[testing]
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ inputs.debug_ssh_session }}
with:
limit-access-to-actor: true
- name: Run Flake8
# Ignore F401, which are unused imports. flake8 is a primitive tool and is sometimes wrong.
run: flake8 --extend-ignore F401 parso test/*.py setup.py scripts/
- name: Run Mypy
run: mypy parso setup.py
- name: Run Zuban
run: zuban check
test:
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/debug_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Debug CI

on:
workflow_dispatch:

jobs:
tests:
uses: ./.github/workflows/tests.yml
with:
all_tests: true
debug_ssh_session: true
secrets: inherit
7 changes: 3 additions & 4 deletions parso/grammar.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import hashlib
import os
from typing import Generic, TypeVar, Union, Dict, Optional, Any
from typing import Generic, TypeVar, Union, Dict, Optional, Any, Iterator
from pathlib import Path

from parso._compatibility import is_pypy
from parso.pgen2 import generate_grammar
from parso.utils import split_lines, python_bytes_to_unicode, \
PythonVersionInfo, parse_version_string
from parso.python.diff import DiffParser
from parso.python.tokenize import tokenize_lines, tokenize
from parso.python.tokenize import tokenize_lines, tokenize, PythonToken
from parso.python.token import PythonTokenTypes
from parso.cache import parser_cache, load_module, try_to_save_module
from parso.parser import BaseParser
Expand Down Expand Up @@ -223,7 +223,7 @@ def __init__(self, version_info: PythonVersionInfo, bnf_text: str):
)
self.version_info = version_info

def _tokenize_lines(self, lines, **kwargs):
def _tokenize_lines(self, lines, **kwargs) -> Iterator[PythonToken]:
return tokenize_lines(lines, version_info=self.version_info, **kwargs)

def _tokenize(self, code):
Expand Down Expand Up @@ -255,7 +255,6 @@ def load_grammar(*, version: str = None, path: str = None):
'grammar%s%s.txt' % (version_info.major, version_info.minor)
)

global _loaded_grammars
path = os.path.join(os.path.dirname(__file__), file)
try:
return _loaded_grammars[path]
Expand Down
8 changes: 4 additions & 4 deletions parso/normalizer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from contextlib import contextmanager
from typing import Dict, List
from typing import Dict, List, Any


class _NormalizerMeta(type):
rule_value_classes: Any
rule_type_classes: Any

def __new__(cls, name, bases, dct):
new_cls = type.__new__(cls, name, bases, dct)
new_cls.rule_value_classes = {}
Expand Down Expand Up @@ -109,9 +112,6 @@ class NormalizerConfig:
normalizer_class = Normalizer

def create_normalizer(self, grammar):
if self.normalizer_class is None:
return None

return self.normalizer_class(grammar, self)


Expand Down
8 changes: 4 additions & 4 deletions parso/pgen2/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,14 @@ def __init__(self, from_rule: str, nfa_set: Set[NFAState], final: NFAState):
self.from_rule = from_rule
self.nfa_set = nfa_set
# map from terminals/nonterminals to DFAState
self.arcs: Mapping[str, DFAState] = {}
self.arcs: dict[str, DFAState] = {}
# In an intermediary step we set these nonterminal arcs (which has the
# same structure as arcs). These don't contain terminals anymore.
self.nonterminal_arcs: Mapping[str, DFAState] = {}
self.nonterminal_arcs: dict[str, DFAState] = {}

# Transitions are basically the only thing that the parser is using
# with is_final. Everyting else is purely here to create a parser.
self.transitions: Mapping[Union[_TokenTypeT, ReservedString], DFAPlan] = {}
self.transitions: dict[Union[_TokenTypeT, ReservedString], DFAPlan] = {}
self.is_final = final in nfa_set

def add_arc(self, next_, label):
Expand Down Expand Up @@ -261,7 +261,7 @@ def generate_grammar(bnf_grammar: str, token_namespace) -> Grammar:
if start_nonterminal is None:
start_nonterminal = nfa_a.from_rule

reserved_strings: Mapping[str, ReservedString] = {}
reserved_strings: dict[str, ReservedString] = {}
for nonterminal, dfas in rule_to_dfas.items():
for dfa_state in dfas:
for terminal_or_nonterminal, next_dfa in dfa_state.arcs.items():
Expand Down
2 changes: 1 addition & 1 deletion parso/python/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,6 @@ def close(self):
end_pos[0] += len(lines) - 1
end_pos[1] = len(lines[-1])

endmarker = EndMarker('', tuple(end_pos), self.prefix + self._prefix_remainder)
endmarker = EndMarker('', (end_pos[0], end_pos[1]), self.prefix + self._prefix_remainder)
endmarker.parent = self._module
self._module.children.append(endmarker)
4 changes: 2 additions & 2 deletions parso/python/pep8.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ def _analyse_non_prefix(self, leaf):
elif leaf.parent.type == 'function' and leaf.parent.name == leaf:
self.add_issue(leaf, 743, message % 'function')
else:
self.add_issuadd_issue(741, message % 'variables', leaf)
self.add_issue(741, message % 'variables', leaf)
elif leaf.value == ':':
if isinstance(leaf.parent, (Flow, Scope)) and leaf.parent.type != 'lambdef':
next_leaf = leaf.get_next_leaf()
Expand Down Expand Up @@ -764,4 +764,4 @@ class BlankLineAtEnd(Rule):
message = 'Blank line at end of file'

def is_issue(self, leaf):
return self._newline_count >= 2
return False # TODO return self._newline_count >= 2
28 changes: 14 additions & 14 deletions parso/python/tokenize.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import itertools as _itertools
from codecs import BOM_UTF8
from typing import NamedTuple, Tuple, Iterator, Iterable, List, Dict, \
Pattern, Set
Pattern, Set, Any

from parso.python.token import PythonTokenTypes
from parso.utils import split_lines, PythonVersionInfo, parse_version_string
Expand Down Expand Up @@ -47,12 +47,12 @@ class TokenCollection(NamedTuple):
endpats: Dict[str, Pattern]
whitespace: Pattern
fstring_pattern_map: Dict[str, str]
always_break_tokens: Tuple[str]
always_break_tokens: Set[str]


BOM_UTF8_STRING = BOM_UTF8.decode('utf-8')

_token_collection_cache: Dict[PythonVersionInfo, TokenCollection] = {}
_token_collection_cache: Dict[Tuple[int, int], TokenCollection] = {}


def group(*choices, capture=False, **kwargs):
Expand Down Expand Up @@ -249,15 +249,15 @@ def end_pos(self) -> Tuple[int, int]:
class PythonToken(Token):
def __repr__(self):
return ('TokenInfo(type=%s, string=%r, start_pos=%r, prefix=%r)' %
self._replace(type=self.type.name))
self._replace(type=self.type.name)) # type: ignore[arg-type]


class FStringNode:
def __init__(self, quote):
self.quote = quote
self.parentheses_count = 0
self.previous_lines = ''
self.last_string_start_pos = None
self.last_string_start_pos: Any = None
# In the syntax there can be multiple format_spec's nested:
# {x:{y:3}}
self.format_spec_count = 0
Expand Down Expand Up @@ -340,7 +340,7 @@ def _find_fstring_string(endpats, fstring_stack, line, lnum, pos):


def tokenize(
code: str, *, version_info: PythonVersionInfo, start_pos: Tuple[int, int] = (1, 0)
code: str, *, version_info: Tuple[int, int], start_pos: Tuple[int, int] = (1, 0)
) -> Iterator[PythonToken]:
"""Generate tokens from a the source code (string)."""
lines = split_lines(code, keepends=True)
Expand All @@ -363,7 +363,7 @@ def wrapper(*args, **kwargs):
def tokenize_lines(
lines: Iterable[str],
*,
version_info: PythonVersionInfo,
version_info: Tuple[int, int],
indents: List[int] = None,
start_pos: Tuple[int, int] = (1, 0),
is_first_token=True,
Expand Down Expand Up @@ -444,7 +444,7 @@ def dedent_if_necessary(start):
if string:
yield PythonToken(
FSTRING_STRING, string,
tos.last_string_start_pos,
tos.last_string_start_pos, # type: ignore[arg-type]
# Never has a prefix because it can start anywhere and
# include whitespace.
prefix=''
Expand Down Expand Up @@ -496,8 +496,8 @@ def dedent_if_necessary(start):
initial = token[0]
else:
match = whitespace.match(line, pos)
initial = line[match.end()]
start = match.end()
initial = line[match.end()] # type: ignore[union-attr]
start = match.end() # type: ignore[union-attr]
spos = (lnum, start)

if new_line and initial not in '\r\n#' and (initial != '\\' or pseudomatch is None):
Expand All @@ -512,12 +512,12 @@ def dedent_if_necessary(start):
if not pseudomatch: # scan for tokens
match = whitespace.match(line, pos)
if new_line and paren_level == 0 and not fstring_stack:
yield from dedent_if_necessary(match.end())
pos = match.end()
yield from dedent_if_necessary(match.end()) # type: ignore[union-attr]
pos = match.end() # type: ignore[union-attr]
new_line = False
yield PythonToken(
ERRORTOKEN, line[pos], (lnum, pos),
additional_prefix + match.group(0)
additional_prefix + match.group(0) # type: ignore[union-attr]
)
additional_prefix = ''
pos += 1
Expand Down Expand Up @@ -586,7 +586,7 @@ def dedent_if_necessary(start):
# backslash and is continued.
contstr_start = lnum, start
endprog = (endpats.get(initial) or endpats.get(token[1])
or endpats.get(token[2]))
or endpats.get(token[2])) # type: ignore[assignment]
contstr = line[start:]
contline = line
break
Expand Down
23 changes: 15 additions & 8 deletions parso/python/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,8 @@
"""

import re
try:
from collections.abc import Mapping
except ImportError:
from collections import Mapping
from typing import Tuple
from collections.abc import Mapping
from typing import Tuple, Any

from parso.tree import Node, BaseNode, Leaf, ErrorNode, ErrorLeaf, search_ancestor # noqa
from parso.python.prefix import split_prefix
Expand All @@ -70,6 +67,9 @@

class DocstringMixin:
__slots__ = ()
type: str
children: "list[Any]"
parent: Any

def get_doc_node(self):
"""
Expand Down Expand Up @@ -101,6 +101,7 @@ class PythonMixin:
Some Python specific utilities.
"""
__slots__ = ()
children: "list[Any]"

def get_name_of_position(self, position):
"""
Expand Down Expand Up @@ -219,7 +220,7 @@ def get_definition(self, import_name_always=False, include_setitem=False):
type_ = node.type

if type_ in ('funcdef', 'classdef'):
if self == node.name:
if self == node.name: # type: ignore[union-attr]
return node
return None

Expand All @@ -232,7 +233,7 @@ def get_definition(self, import_name_always=False, include_setitem=False):
if node.type == 'suite':
return None
if node.type in _GET_DEFINITION_TYPES:
if self in node.get_defined_names(include_setitem):
if self in node.get_defined_names(include_setitem): # type: ignore[attr-defined]
return node
if import_name_always and node.type in _IMPORTS:
return node
Expand Down Expand Up @@ -296,6 +297,7 @@ class FStringEnd(PythonLeaf):

class _StringComparisonMixin:
__slots__ = ()
value: Any

def __eq__(self, other):
"""
Expand Down Expand Up @@ -368,7 +370,7 @@ def get_suite(self):

def __repr__(self):
try:
name = self.name.value
name = self.name.value # type: ignore[attr-defined]
except AttributeError:
name = ''

Expand Down Expand Up @@ -794,6 +796,8 @@ def get_test_node_from_name(self, name):

class Import(PythonBaseNode):
__slots__ = ()
get_paths: Any
_aliases: Any

def get_path_for_name(self, name):
"""
Expand All @@ -818,6 +822,9 @@ def is_nested(self):
def is_star_import(self):
return self.children[-1] == '*'

def get_defined_names(self):
raise NotImplementedError("Use ImportFrom or ImportName")


class ImportFrom(Import):
type = 'import_from'
Expand Down
9 changes: 2 additions & 7 deletions parso/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ def search_ancestor(node: 'NodeOrLeaf', *node_types: str) -> 'Optional[BaseNode]
:param node: The ancestors of this node will be checked.
:param node_types: type names that are searched for.
"""
n = node.parent
while n is not None:
if n.type in node_types:
return n
n = n.parent
return None
return node.search_ancestor(*node_types)


class NodeOrLeaf:
Expand Down Expand Up @@ -371,7 +366,7 @@ class BaseNode(NodeOrLeaf):
"""
__slots__ = ('children',)

def __init__(self, children: List[NodeOrLeaf]) -> None:
def __init__(self, children) -> None:
self.children = children
"""
A list of :class:`NodeOrLeaf` child nodes.
Expand Down
4 changes: 2 additions & 2 deletions parso/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
from ast import literal_eval
from functools import total_ordering
from typing import NamedTuple, Sequence, Union
from typing import NamedTuple, Union

# The following is a list in Python that are line breaks in str.splitlines, but
# not in Python. In Python only \r (Carriage Return, 0xD) and \n (Line Feed,
Expand All @@ -26,7 +26,7 @@ class Version(NamedTuple):
micro: int


def split_lines(string: str, keepends: bool = False) -> Sequence[str]:
def split_lines(string: str, keepends: bool = False) -> "list[str]":
r"""
Intended for Python code. In contrast to Python's :py:meth:`str.splitlines`,
looks at form feeds and other special characters as normal text. Just
Expand Down
Loading