Skip to content

Commit

Permalink
Revert "Fix Literal strings containing pipe characters" (python#17638)
Browse files Browse the repository at this point in the history
Reverts python#17148

(cherry picked from commit bc39f17)
  • Loading branch information
ilevkivskyi authored and md384 committed Aug 14, 2024
1 parent 8a2a42f commit 214b1a2
Show file tree
Hide file tree
Showing 15 changed files with 90 additions and 142 deletions.
11 changes: 8 additions & 3 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,14 @@ def parse_type_string(
"""
try:
_, node = parse_type_comment(f"({expr_string})", line=line, column=column, errors=None)
return RawExpressionType(expr_string, expr_fallback_name, line, column, node=node)
if isinstance(node, UnboundType) and node.original_str_expr is None:
node.original_str_expr = expr_string
node.original_str_fallback = expr_fallback_name
return node
elif isinstance(node, UnionType):
return node
else:
return RawExpressionType(expr_string, expr_fallback_name, line, column)
except (SyntaxError, ValueError):
# Note: the parser will raise a `ValueError` instead of a SyntaxError if
# the string happens to contain things like \x00.
Expand Down Expand Up @@ -1046,8 +1053,6 @@ def set_type_optional(self, type: Type | None, initializer: Expression | None) -
return
# Indicate that type should be wrapped in an Optional if arg is initialized to None.
optional = isinstance(initializer, NameExpr) and initializer.name == "None"
if isinstance(type, RawExpressionType) and type.node is not None:
type = type.node
if isinstance(type, UnboundType):
type.optional = optional

Expand Down
31 changes: 13 additions & 18 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3437,10 +3437,10 @@ def analyze_typeddict_assign(self, s: AssignmentStmt) -> bool:
def analyze_lvalues(self, s: AssignmentStmt) -> None:
# We cannot use s.type, because analyze_simple_literal_type() will set it.
explicit = s.unanalyzed_type is not None
final_type = self.unwrap_final_type(s.unanalyzed_type)
if final_type is not None:
if self.is_final_type(s.unanalyzed_type):
# We need to exclude bare Final.
if not final_type.args:
assert isinstance(s.unanalyzed_type, UnboundType)
if not s.unanalyzed_type.args:
explicit = False

if s.rvalue:
Expand Down Expand Up @@ -3506,19 +3506,19 @@ def unwrap_final(self, s: AssignmentStmt) -> bool:
Returns True if Final[...] was present.
"""
final_type = self.unwrap_final_type(s.unanalyzed_type)
if final_type is None:
if not s.unanalyzed_type or not self.is_final_type(s.unanalyzed_type):
return False
if len(final_type.args) > 1:
self.fail("Final[...] takes at most one type argument", final_type)
assert isinstance(s.unanalyzed_type, UnboundType)
if len(s.unanalyzed_type.args) > 1:
self.fail("Final[...] takes at most one type argument", s.unanalyzed_type)
invalid_bare_final = False
if not final_type.args:
if not s.unanalyzed_type.args:
s.type = None
if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs:
invalid_bare_final = True
self.fail("Type in Final[...] can only be omitted if there is an initializer", s)
else:
s.type = final_type.args[0]
s.type = s.unanalyzed_type.args[0]

if s.type is not None and self.is_classvar(s.type):
self.fail("Variable should not be annotated with both ClassVar and Final", s)
Expand Down Expand Up @@ -4937,18 +4937,13 @@ def is_classvar(self, typ: Type) -> bool:
return False
return sym.node.fullname == "typing.ClassVar"

def unwrap_final_type(self, typ: Type | None) -> UnboundType | None:
if typ is None:
return None
typ = typ.resolve_string_annotation()
def is_final_type(self, typ: Type | None) -> bool:
if not isinstance(typ, UnboundType):
return None
return False
sym = self.lookup_qualified(typ.name, typ)
if not sym or not sym.node:
return None
if sym.node.fullname in FINAL_TYPE_NAMES:
return typ
return None
return False
return sym.node.fullname in FINAL_TYPE_NAMES

def fail_invalid_classvar(self, context: Context) -> None:
self.fail(message_registry.CLASS_VAR_OUTSIDE_OF_CLASS, context)
Expand Down
3 changes: 1 addition & 2 deletions mypy/server/astmerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,8 +507,7 @@ def visit_typeddict_type(self, typ: TypedDictType) -> None:
typ.fallback.accept(self)

def visit_raw_expression_type(self, t: RawExpressionType) -> None:
if t.node is not None:
t.node.accept(self)
pass

def visit_literal_type(self, typ: LiteralType) -> None:
typ.fallback.accept(self)
Expand Down
16 changes: 4 additions & 12 deletions mypy/stubutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,7 @@
from mypy.modulefinder import ModuleNotFoundReason
from mypy.moduleinspect import InspectError, ModuleInspect
from mypy.stubdoc import ArgSig, FunctionSig
from mypy.types import (
AnyType,
NoneType,
RawExpressionType,
Type,
TypeList,
TypeStrVisitor,
UnboundType,
UnionType,
)
from mypy.types import AnyType, NoneType, Type, TypeList, TypeStrVisitor, UnboundType, UnionType

# Modules that may fail when imported, or that may have side effects (fully qualified).
NOT_IMPORTABLE_MODULES = ()
Expand Down Expand Up @@ -302,11 +293,12 @@ def args_str(self, args: Iterable[Type]) -> str:
The main difference from list_str is the preservation of quotes for string
arguments
"""
types = ["builtins.bytes", "builtins.str"]
res = []
for arg in args:
arg_str = arg.accept(self)
if isinstance(arg, RawExpressionType):
res.append(repr(arg.literal_value))
if isinstance(arg, UnboundType) and arg.original_str_fallback in types:
res.append(f"'{arg_str}'")
else:
res.append(arg_str)
return ", ".join(res)
Expand Down
4 changes: 0 additions & 4 deletions mypy/type_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,6 @@ def visit_typeddict_type(self, t: TypedDictType) -> T:
return self.query_types(t.items.values())

def visit_raw_expression_type(self, t: RawExpressionType) -> T:
if t.node is not None:
return t.node.accept(self)
return self.strategy([])

def visit_literal_type(self, t: LiteralType) -> T:
Expand Down Expand Up @@ -524,8 +522,6 @@ def visit_typeddict_type(self, t: TypedDictType) -> bool:
return self.query_types(list(t.items.values()))

def visit_raw_expression_type(self, t: RawExpressionType) -> bool:
if t.node is not None:
return t.node.accept(self)
return self.default

def visit_literal_type(self, t: LiteralType) -> bool:
Expand Down
21 changes: 13 additions & 8 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,6 @@ def visit_callable_type(
return ret

def anal_type_guard(self, t: Type) -> Type | None:
t = t.resolve_string_annotation()
if isinstance(t, UnboundType):
sym = self.lookup_qualified(t.name, t)
if sym is not None and sym.node is not None:
Expand All @@ -1126,7 +1125,6 @@ def anal_type_guard_arg(self, t: UnboundType, fullname: str) -> Type | None:
return None

def anal_type_is(self, t: Type) -> Type | None:
t = t.resolve_string_annotation()
if isinstance(t, UnboundType):
sym = self.lookup_qualified(t.name, t)
if sym is not None and sym.node is not None:
Expand All @@ -1144,7 +1142,6 @@ def anal_type_is_arg(self, t: UnboundType, fullname: str) -> Type | None:

def anal_star_arg_type(self, t: Type, kind: ArgKind, nested: bool) -> Type:
"""Analyze signature argument type for *args and **kwargs argument."""
t = t.resolve_string_annotation()
if isinstance(t, UnboundType) and t.name and "." in t.name and not t.args:
components = t.name.split(".")
tvar_name = ".".join(components[:-1])
Expand Down Expand Up @@ -1235,8 +1232,6 @@ def visit_raw_expression_type(self, t: RawExpressionType) -> Type:
# make signatures like "foo(x: 20) -> None" legal, we can change
# this method so it generates and returns an actual LiteralType
# instead.
if t.node is not None:
return t.node.accept(self)

if self.report_invalid_types:
if t.base_type_name in ("builtins.int", "builtins.bool"):
Expand Down Expand Up @@ -1499,7 +1494,6 @@ def analyze_callable_args(
invalid_unpacks: list[Type] = []
second_unpack_last = False
for i, arg in enumerate(arglist.items):
arg = arg.resolve_string_annotation()
if isinstance(arg, CallableArgument):
args.append(arg.typ)
names.append(arg.name)
Expand Down Expand Up @@ -1580,6 +1574,18 @@ def analyze_literal_type(self, t: UnboundType) -> Type:
return UnionType.make_union(output, line=t.line)

def analyze_literal_param(self, idx: int, arg: Type, ctx: Context) -> list[Type] | None:
# This UnboundType was originally defined as a string.
if isinstance(arg, UnboundType) and arg.original_str_expr is not None:
assert arg.original_str_fallback is not None
return [
LiteralType(
value=arg.original_str_expr,
fallback=self.named_type(arg.original_str_fallback),
line=arg.line,
column=arg.column,
)
]

# If arg is an UnboundType that was *not* originally defined as
# a string, try expanding it in case it's a type alias or something.
if isinstance(arg, UnboundType):
Expand Down Expand Up @@ -2564,8 +2570,7 @@ def visit_typeddict_type(self, t: TypedDictType) -> None:
self.process_types(list(t.items.values()))

def visit_raw_expression_type(self, t: RawExpressionType) -> None:
if t.node is not None:
t.node.accept(self)
pass

def visit_literal_type(self, t: LiteralType) -> None:
pass
Expand Down
Loading

0 comments on commit 214b1a2

Please sign in to comment.