Add support for Literal, Type, and TypeVar

This commit is contained in:
Charlie Marsh 2022-09-08 10:51:36 -04:00
parent fc5f34c76f
commit 44ea80c7f8
3 changed files with 72 additions and 11 deletions

View File

@ -11,12 +11,14 @@ import multiprocessing.pool
import multiprocessing.process import multiprocessing.process
import logging.config import logging.config
import logging.handlers import logging.handlers
from typing import NamedTuple, Dict, Type, TypeVar, List, Set
from blah import ClassA, ClassB, ClassC from blah import ClassA, ClassB, ClassC
class X: class X:
datetime: datetime datetime: datetime
foo: Type["NamedTuple"]
def a(self) -> "namedtuple": def a(self) -> "namedtuple":
x = os.environ["1"] x = os.environ["1"]
@ -26,3 +28,7 @@ class X:
__all__ = ["ClassA"] + ["ClassB"] __all__ = ["ClassA"] + ["ClassB"]
__all__ += ["ClassC"] __all__ += ["ClassC"]
X = TypeVar("X")
Y = TypeVar("Y", bound="Dict")
Z = TypeVar("Z", "List", "Set")

View File

@ -36,7 +36,13 @@ class Foo:
ANNOTATED_CLASS_VAR: int = 2 ANNOTATED_CLASS_VAR: int = 2
from typing import Literal
class Class: class Class:
copy_on_model_validation: Literal["none", "deep", "shallow"]
post_init_call: Literal["before_validation", "after_validation"]
def __init__(self): def __init__(self):
Class Class

View File

@ -4,7 +4,7 @@ use std::path::Path;
use itertools::izip; use itertools::izip;
use rustpython_parser::ast::{ use rustpython_parser::ast::{
Arg, Arguments, Cmpop, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind, Arg, Arguments, Cmpop, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprContext, ExprKind,
Location, Stmt, StmtKind, Suite, Unaryop, KeywordData, Location, Stmt, StmtKind, Suite, Unaryop,
}; };
use rustpython_parser::parser; use rustpython_parser::parser;
@ -40,6 +40,7 @@ struct Checker<'a> {
// Derivative state. // Derivative state.
in_f_string: bool, in_f_string: bool,
in_annotation: bool, in_annotation: bool,
in_literal: bool,
seen_non_import: bool, seen_non_import: bool,
seen_docstring: bool, seen_docstring: bool,
} }
@ -67,6 +68,7 @@ impl<'a> Checker<'a> {
deferred_lambdas: vec![], deferred_lambdas: vec![],
in_f_string: false, in_f_string: false,
in_annotation: false, in_annotation: false,
in_literal: false,
seen_non_import: false, seen_non_import: false,
seen_docstring: false, seen_docstring: false,
} }
@ -87,6 +89,14 @@ fn convert_to_value(expr: &Expr) -> Option<DictionaryKey> {
} }
} }
fn match_name_or_attr(expr: &Expr, target: &str) -> bool {
match &expr.node {
ExprKind::Attribute { attr, .. } => target == attr,
ExprKind::Name { id, .. } => target == id,
_ => false,
}
}
impl<'a, 'b> Visitor<'b> for Checker<'a> impl<'a, 'b> Visitor<'b> for Checker<'a>
where where
'b: 'a, 'b: 'a,
@ -491,17 +501,23 @@ where
} }
fn visit_annotation(&mut self, expr: &'b Expr) { fn visit_annotation(&mut self, expr: &'b Expr) {
let initial = self.in_annotation; let prev_in_annotation = self.in_annotation;
self.in_annotation = true; self.in_annotation = true;
self.visit_expr(expr); self.visit_expr(expr);
self.in_annotation = initial; self.in_annotation = prev_in_annotation;
} }
fn visit_expr(&mut self, expr: &'b Expr) { fn visit_expr(&mut self, expr: &'b Expr) {
let initial = self.in_f_string; let prev_in_f_string = self.in_f_string;
let prev_in_literal = self.in_literal;
// Pre-visit. // Pre-visit.
match &expr.node { match &expr.node {
ExprKind::Subscript { value, .. } => {
if match_name_or_attr(value, "Literal") {
self.in_literal = true;
}
}
ExprKind::Name { ctx, .. } => match ctx { ExprKind::Name { ctx, .. } => match ctx {
ExprContext::Load => self.handle_node_load(expr), ExprContext::Load => self.handle_node_load(expr),
ExprContext::Store => { ExprContext::Store => {
@ -542,7 +558,6 @@ where
} }
} }
} }
ExprKind::Dict { keys, .. } => { ExprKind::Dict { keys, .. } => {
if self.settings.select.contains(&CheckCode::F601) if self.settings.select.contains(&CheckCode::F601)
|| self.settings.select.contains(&CheckCode::F602) || self.settings.select.contains(&CheckCode::F602)
@ -743,7 +758,9 @@ where
ExprKind::Constant { ExprKind::Constant {
value: Constant::Str(value), value: Constant::Str(value),
.. ..
} if self.in_annotation => self.deferred_annotations.push(value), } if self.in_annotation && !self.in_literal => {
self.deferred_annotations.push(value);
}
_ => {} _ => {}
}; };
@ -756,6 +773,39 @@ where
self.parent_stack.clone(), self.parent_stack.clone(),
)); ));
} }
ExprKind::Call {
func,
args,
keywords,
} => {
if match_name_or_attr(func, "TypeVar") {
self.visit_expr(func);
for expr in &args[1..] {
self.visit_annotation(expr);
}
for keyword in &keywords[..] {
let KeywordData { arg, value } = &keyword.node;
if let Some(id) = arg {
if id == "bound" {
self.visit_annotation(value);
} else {
self.visit_expr(value);
}
}
}
} else {
visitor::walk_expr(self, expr);
}
}
ExprKind::Subscript { value, slice, ctx } => {
if match_name_or_attr(value, "Type") {
self.visit_expr(value);
self.visit_annotation(slice);
self.visit_expr_context(ctx);
} else {
visitor::walk_expr(self, expr);
}
}
_ => visitor::walk_expr(self, expr), _ => visitor::walk_expr(self, expr),
} }
@ -767,11 +817,10 @@ where
| ExprKind::SetComp { .. } => { | ExprKind::SetComp { .. } => {
self.pop_scope(); self.pop_scope();
} }
ExprKind::JoinedStr { .. } => {
self.in_f_string = initial;
}
_ => {} _ => {}
}; };
self.in_literal = prev_in_literal;
self.in_f_string = prev_in_f_string;
} }
fn visit_excepthandler(&mut self, excepthandler: &'b Excepthandler) { fn visit_excepthandler(&mut self, excepthandler: &'b Excepthandler) {
@ -1227,10 +1276,10 @@ pub fn check_ast(
} }
// Check any deferred statements. // Check any deferred statements.
let mut allocator = vec![];
checker.check_deferred_annotations(path, &mut allocator);
checker.check_deferred_functions(); checker.check_deferred_functions();
checker.check_deferred_lambdas(); checker.check_deferred_lambdas();
let mut allocator = vec![];
checker.check_deferred_annotations(path, &mut allocator);
// Reset the scope to module-level, and check all consumed scopes. // Reset the scope to module-level, and check all consumed scopes.
checker.scope_stack = vec![GLOBAL_SCOPE_INDEX]; checker.scope_stack = vec![GLOBAL_SCOPE_INDEX];