Skip to content

Commit

Permalink
pyright 1.1.364.
Browse files Browse the repository at this point in the history
This commit generalizes types pertaining to abstract syntax trees (ASTs)
across the @beartype codebase to support `pyright 1.1.364`, which
appears to have gone *really* hard on AST types. In theory, this commit
restores our GitHub Actions-based continuous integration (CI) pipeline
to worky. In theory. In theory, @leycec also has muscles. *In theory.*
(*Theoretical theorems bring binderless reams of dreams!*)
  • Loading branch information
leycec committed May 24, 2024
1 parent b5178bc commit 791602d
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 27 deletions.
56 changes: 56 additions & 0 deletions beartype/_check/code/codemake.py
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,62 @@ def _enqueue_hint_child(pith_child_expr: str) -> str:
# Else, this hint is *NOT* a mapping.
#
# ..........{ REITERABLES }............
#FIXME: This logic is an almost one-for-one copy of the
#"hint_curr_sign in HINT_SIGNS_SEQUENCE_ARGS_1" block above.
#Unify as follows:
#* In the existing "datapepsigns" submodule:
# * Define a new "HintSignTupleFixed" sign in the standard way.
# * The existing "HintSignTuple" sign should be preserved as is
# but *ONLY* match variable-length tuple type hints. Why?
# Because this sign naturally matches the unsubscripted
# "typing.Tuple" attribute, which is semantically equivalent
# to the "typing.Tuple[object, ...]" type hint, which is the
# widest possible variable-length tuple type hint.
# * Refactor the get_hint_pep_sign() getter to assign *ALL*
# fixed-length tuple type hints the "HintSignTupleFixed" sign
# rather than the "HintSignTuple" sign.
# * Refactor logic elsewhere to explicitly match
# "HintSignTupleFixed" rather than matching "HintSignTuple"
# and then attempting to disambiguate fixed-length tuple type
# hints from that by inspecting for "Ellipses" singletons. In
# theory, the only two places in the codebase this happens
# *SHOULD* be:
# * This submodule.
# * Some "beartype.door" submodule specific to tuples.
#* In the existing "codesnipstr" submodule:
# * Define a new
# "HINT_SIGN_TO_CODE_PEP484585_CONTAINER_ARGS_1_format"
# dictionary global resembling:
# # Initialized by the _init() function defined below.
# HINT_SIGN_TO_CODE_PEP484585_CONTAINER_ARGS_1_format: Dict[HintSign, str] = {}
# def _init() -> None:
# _CODE_PEP484585_COLLECTION_ARGS_1 = '''(
# _{indent_curr} # True only if this pith is of this collection type *AND*...
# _{indent_curr} isinstance({pith_curr_assign_expr}, {hint_curr_expr}) and
# _{indent_curr} # True only if either this collection is empty *OR* this collection
# _{indent_curr} # is both non-empty and the first item satisfies this hint.
# _{indent_curr} (not {pith_curr_var_name} or {hint_child_placeholder})
# _{indent_curr})'''
# _CODE_PEP484585_COLLECTION_ARGS_1_format = (
# _CODE_PEP484585_COLLECTION_ARGS_1.format)
#
# for hint_sign in (
# HINT_SIGNS_REITERABLE_ARGS_1 |
# HINT_SIGNS_SEQUENCE_ARGS_1 |
# ):
# HINT_SIGN_TO_CODE_PEP484585_CONTAINER_ARGS_1_format[
# hint_sign] = _CODE_PEP484585_COLLECTION_ARGS_1_format
# * Generalize the existing
# "CODE_PEP484585_REITERABLE_ARGS_1_PITH_CHILD_EXPR" and
# "CODE_PEP484585_SEQUENCE_ARGS_1_PITH_CHILD_EXPR" string
# globals similarly.
# * Refactor logic in this submodule to leverage the
# "HINT_SIGN_TO_CODE_PEP484585_CONTAINER_ARGS_1_format"
# dictionary.
# * Remove the now-obsolete family of
# "CODE_PEP484585_REITERABLE_ARGS_1*" and
# "CODE_PEP484585_SEQUENCE_ARGS_1*" string globals.

# If this hint is a single-argument reiterable (e.g.,
# "set[str]")...
elif hint_curr_sign in HINT_SIGNS_REITERABLE_ARGS_1:
Expand Down
41 changes: 22 additions & 19 deletions beartype/_util/ast/utilastmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
ImportFrom,
Name,
alias,
expr,
keyword,
)
from beartype.roar import BeartypeClawImportAstException
Expand Down Expand Up @@ -140,7 +141,7 @@ def make_node_object_attr_load(
Unqualified basename of the attribute of this object to be accessed.
node_sibling : AST
Sibling node to copy source code metadata from.
node_obj : Optional[AST]
node_obj : Optional[expr]
Either:
* If the caller prefers supplying the name node accessing the parent
Expand Down Expand Up @@ -201,8 +202,8 @@ def make_node_object_attr_load(
# Else, the caller passed *NO* unqualified basename of that object.
#
# In any case, the "node_obj" variable is now the desired object node.
assert isinstance(node_obj, AST), (
f'{repr(node_obj)} not AST node.')
assert isinstance(node_obj, expr), (
f'{repr(node_obj)} not AST expression node.')

# Object attribute node accessing this attribute of this object.
node_attribute_load = Attribute(
Expand Down Expand Up @@ -257,7 +258,7 @@ def make_node_call(
node_sibling: AST,

# Optional parameters.
nodes_args: NodesList = LIST_EMPTY,
nodes_args: List[expr] = LIST_EMPTY,
nodes_kwargs: List[keyword] = LIST_EMPTY,
) -> Call:
'''
Expand All @@ -272,11 +273,11 @@ def make_node_call(
Fully-qualified name of the module to import this attribute from.
node_sibling : AST
Sibling node to copy source code metadata from.
nodes_args : NodesList, optional
List of zero or more **positional parameter AST nodes** comprising the
tuple of all positional parameters to be passed to this call. Defaults
to the empty list.
nodes_kwargs : NodesList, optional
nodes_args : List[expr], optional
List of zero or more **positional parameter AST expression nodes**
comprising the tuple of all positional parameters to be passed to this
call. Defaults to the empty list.
nodes_kwargs : List[keyword], optional
List of zero or more **keyword parameter AST nodes** comprising the
dictionary of all keyword parameters to be passed to this call. Defaults
to the empty list.
Expand All @@ -289,11 +290,11 @@ def make_node_call(
assert isinstance(nodes_args, list), f'{repr(nodes_args)} not list.'
assert isinstance(nodes_kwargs, list), f'{repr(nodes_kwargs)} not list.'
assert all(
isinstance(node_args, AST) for node_args in nodes_args), (
f'{repr(nodes_args)} not list of AST nodes.')
isinstance(node_args, expr) for node_args in nodes_args), (
f'{repr(nodes_args)} not list of AST expression nodes.')
assert all(
isinstance(node_kwargs, keyword) for node_kwargs in nodes_kwargs), (
f'{repr(nodes_kwargs)} not list of keyword nodes.')
f'{repr(nodes_kwargs)} not list of AST keyword nodes.')

# Child node referencing the callable to be called.
node_func_name = make_node_name_load(
Expand All @@ -315,7 +316,7 @@ def make_node_call(
# ....................{ FACTORIES ~ call : arg }....................
#FIXME: Unit test us up, please.
def make_node_kwarg(
kwarg_name: str, kwarg_value: AST, node_sibling: AST) -> keyword:
kwarg_name: str, kwarg_value: expr, node_sibling: AST) -> keyword:
'''
Create and return a new **keyword argument abstract syntax tree (AST) node**
(i.e., node encapsulating a keyword argument of a call to an arbitrary
Expand All @@ -326,8 +327,8 @@ def make_node_kwarg(
----------
kwarg_name : str
Name of this keyword argument.
kwarg_value : AST
Node passing the value of this keyword argument.
kwarg_value : expr
Expression node passing the value of this keyword argument.
node_sibling : AST
Sibling node to copy source code metadata from.
Expand All @@ -337,7 +338,8 @@ def make_node_kwarg(
Keyword node passing a keyword argument with this name and value.
'''
assert isinstance(kwarg_name, str), f'{repr(kwarg_name)} not string.'
assert isinstance(kwarg_value, AST), f'{repr(kwarg_value)} not AST node.'
assert isinstance(kwarg_value, expr), (
f'{repr(kwarg_value)} not AST expression node.')

# Child node encapsulating this keyword argument.
node_kwarg = keyword(arg=kwarg_name, value=kwarg_value)
Expand Down Expand Up @@ -380,7 +382,7 @@ def make_node_str(text: str, node_sibling: AST) -> Constant:

# ....................{ FACTORIES ~ literal : f-string }....................
#FIXME: Unit test us up, please.
def make_node_fstr_field(node_expr: AST, node_sibling: AST) -> FormattedValue:
def make_node_fstr_field(node_expr: expr, node_sibling: AST) -> FormattedValue:
'''
Create and return a new **f-string formatting field abstract syntax tree
(AST) node** (i.e., node embedding the substring created and returned by the
Expand All @@ -399,7 +401,7 @@ def make_node_fstr_field(node_expr: AST, node_sibling: AST) -> FormattedValue:
Parameters
----------
node_expr : AST
node_expr : expr
Formatting field to be embedded in some parent f-string node.
node_sibling : AST
Sibling node to copy source code metadata from.
Expand All @@ -409,7 +411,8 @@ def make_node_fstr_field(node_expr: AST, node_sibling: AST) -> FormattedValue:
Name
Name node accessing this attribute in the current lexical scope.
'''
assert isinstance(node_expr, AST), f'{repr(node_expr)} not AST node.'
assert isinstance(node_expr, expr), (
f'{repr(node_expr)} not AST expression node.')

# Child node encapsulating a formatting field "{node_expr.value}" in some
# parent node encapsulating an f-string embedding this field. For unknown
Expand Down
4 changes: 2 additions & 2 deletions beartype/_util/ast/utilastmunge.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def copy_node_metadata(
f'{repr(node_trg_cur)} not AST node.')

# Copy all source code metadata from this source to target node.
node_trg_cur.lineno = node_src.lineno
node_trg_cur.col_offset = node_src.col_offset
node_trg_cur.lineno = node_src.lineno # type: ignore[attr-defined]
node_trg_cur.col_offset = node_src.col_offset # type: ignore[attr-defined]
node_trg_cur.end_lineno = node_src.end_lineno # type: ignore[attr-defined]
node_trg_cur.end_col_offset = node_src.end_col_offset # type: ignore[attr-defined]
8 changes: 4 additions & 4 deletions beartype/claw/_ast/_clawastutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ def _make_node_keyword_conf_beartype(self, node_sibling: AST) -> keyword:
node_sibling=node_sibling,
)

# Node encapsulating the indexation of a dictionary by the
# Expression node encapsulating the indexation of a dictionary by the
# fully-qualified name of the current module.
node_module_name_index: AST = None # type: ignore[assignment]
node_module_name_index: expr = None # type: ignore[assignment]

# If the active Python interpreter targets Python >= 3.9...
if IS_PYTHON_AT_LEAST_3_9:
Expand All @@ -217,7 +217,7 @@ def _make_node_keyword_conf_beartype(self, node_sibling: AST) -> keyword:
# Create this node in a manner specific to Python 3.8, which
# requires an additional intermediary node *NOT* required under
# Python >= 3.9.
node_module_name_index = Index(value=node_module_name)
node_module_name_index = Index(value=node_module_name) # type: ignore

# Copy all source code metadata (e.g., line numbers) from this
# sibling node onto this new node.
Expand All @@ -232,7 +232,7 @@ def _make_node_keyword_conf_beartype(self, node_sibling: AST) -> keyword:
# runtime during module importation.
node_conf = Subscript(
value=node_module_name_to_conf,
slice=node_module_name_index,
slice=node_module_name_index, # pyright: ignore
ctx=NODE_CONTEXT_LOAD,
)

Expand Down
6 changes: 4 additions & 2 deletions beartype/claw/_ast/pep/clawastpep695.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@
)
from beartype.claw._clawmagic import (
BEARTYPE_HINT_PEP695_FORWARDREF_ITER_FUNC_NAME)
from beartype._data.hint.datahinttyping import NodeVisitResult
from beartype._data.ast.dataast import NODE_CONTEXT_STORE
from beartype._data.hint.datahinttyping import NodeVisitResult
from beartype._data.kind.datakindsequence import LIST_EMPTY
from beartype._util.ast.utilastmunge import copy_node_metadata
from beartype._util.ast.utilastmake import (
make_node_object_attr_load,
Expand Down Expand Up @@ -258,7 +259,7 @@ def visit_TypeAlias(self, node: 'ast.TypeAlias') -> NodeVisitResult: # type: ig
# Assign the key of the returned dictionary whose name is given by
# the "BeartypeForwardRefABC.__name_beartype__" class variable of
# this proxy, stored in the scratch variable.
slice=node_forwardref_name_load,
slice=node_forwardref_name_load, # pyright: ignore
ctx=NODE_CONTEXT_STORE,
)

Expand Down Expand Up @@ -286,6 +287,7 @@ def visit_TypeAlias(self, node: 'ast.TypeAlias') -> NodeVisitResult: # type: ig
target=node_scratch_var_name_store,
iter=node_forwardref_iter_call,
body=[node_forwardref_define],
orelse=LIST_EMPTY,
)

# Copy all source code metadata from this type alias node onto *ALL*
Expand Down
11 changes: 11 additions & 0 deletions beartype_test/a00_unit/data/hint/pep/proposal/data_pep484.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,7 @@ def hints_pep484_meta() -> 'List[HintPepMetadata]':
),
),

#FIXME: Pick up tomorrow from here, please. O_o
# # Collection of ignorable items.
# HintPepMetadata(
# hint=Collection[object],
Expand Down Expand Up @@ -1691,6 +1692,16 @@ def hints_pep484_meta() -> 'List[HintPepMetadata]':
),
),

#FIXME: Pick up tomorrow from here, please. Notably, we still need to
#explicitly test:
# * `typing.Deque[...]`.
# * `typing.FrozenSet[...]`.
# * `typing.KeysView[...]`.
# * `typing.MutableSet[...]`.
# * `typing.Set[...]`.
# * `typing.ValuesView[...]`.
#FIXME: Also test PEP 585-specific variants of these type hints! *sigh*

# ................{ SEQUENCE ~ list }................
# Unsubscripted "List" attribute.
HintPepMetadata(
Expand Down

0 comments on commit 791602d

Please sign in to comment.