Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions mypy/exprtotype.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from collections.abc import Callable

from mypy.fastparse import parse_type_string
from mypy.nodes import (
MISSING_FALLBACK,
BytesExpr,
Expand All @@ -30,6 +29,7 @@
get_member_expr_fullname,
)
from mypy.options import Options
from mypy.parse import parse_type_string
from mypy.types import (
ANNOTATED_TYPE_NAMES,
AnyType,
Expand Down Expand Up @@ -221,9 +221,9 @@ def expr_to_unanalyzed_type(
column=expr.column,
)
elif isinstance(expr, StrExpr):
return parse_type_string(expr.value, "builtins.str", expr.line, expr.column)
return parse_type_string(expr, options)
elif isinstance(expr, BytesExpr):
return parse_type_string(expr.value, "builtins.bytes", expr.line, expr.column)
return RawExpressionType(expr.value, "builtins.bytes", expr.line, expr.column)
elif isinstance(expr, UnaryExpr):
typ = expr_to_unanalyzed_type(
expr.expr, options, allow_new_syntax, lookup_qualified=lookup_qualified
Expand Down
17 changes: 17 additions & 0 deletions mypy/nativeparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,23 @@ def native_parse(
return node, errors, ignores


def native_parse_type_string(
expr_string: str, line: int, column: int, end_line: int, end_column: int, options: Options
) -> ProperType:
"""Try to parse a string literal as a type expression (i.e. resolve a forward reference).

If parsing fails, a RawExpressionType will be returned.
"""
ast_bytes = ast_serialize.parse_type_string(
expr_string, (line, column, end_line, end_column), cache_version=5
)
state = State(options)
data = ReadBuffer(ast_bytes)
ret = read_type(state, data)
assert isinstance(ret, ProperType)
return ret


def expect_end_tag(data: ReadBuffer) -> None:
assert read_tag(data) == END_TAG

Expand Down
20 changes: 19 additions & 1 deletion mypy/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from mypy import errorcodes as codes
from mypy.cache import read_int
from mypy.errors import Errors
from mypy.nodes import FileRawData, MypyFile, ParseError
from mypy.nodes import FileRawData, MypyFile, ParseError, StrExpr
from mypy.options import Options
from mypy.types import ProperType


def parse(
Expand Down Expand Up @@ -115,3 +116,20 @@ def report_parse_error(error: ParseError, errors: Errors) -> None:
# Fallback to [syntax] for backwards compatibility.
error_code = codes.error_codes.get(error_code) or codes.SYNTAX
errors.report(error["line"], error["column"], message, blocker=is_blocker, code=error_code)


def parse_type_string(expr: StrExpr, options: Options) -> ProperType:
if options.native_parser:
import mypy.nativeparse

return mypy.nativeparse.native_parse_type_string(
expr.value,
expr.line,
expr.column,
expr.end_line or expr.line,
expr.end_column or expr.column,
options,
)
import mypy.fastparse

return mypy.fastparse.parse_type_string(expr.value, "builtins.str", expr.line, expr.column)
17 changes: 9 additions & 8 deletions test-data/unit/check-literal.test
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ accepts_bytes(c_alias)
[builtins fixtures/tuple.pyi]
[out]

[case testLiteralMixingUnicodeAndBytesPython3ForwardStrings_no_native_parse]
[case testLiteralMixingUnicodeAndBytesPython3ForwardStrings]
from typing import Literal, TypeVar, Generic

a_unicode_wrapper: u"Literal[u'foo']"
Expand All @@ -235,7 +235,7 @@ a_str_wrapper: "Literal[u'foo']"
b_str_wrapper: "Literal['foo']"
c_str_wrapper: "Literal[b'foo']"

# In Python 3, forward references MUST be str, not bytes
# In Python 3, forward references MUST be str, not bytes.
a_bytes_wrapper: b"Literal[u'foo']" # E: Invalid type comment or annotation
b_bytes_wrapper: b"Literal['foo']" # E: Invalid type comment or annotation
c_bytes_wrapper: b"Literal[b'foo']" # E: Invalid type comment or annotation
Expand Down Expand Up @@ -265,9 +265,10 @@ a_str_wrapper_alias: AStrWrapperAlias
b_str_wrapper_alias: BStrWrapperAlias
c_str_wrapper_alias: CStrWrapperAlias

ABytesWrapperAlias = Wrap[b"Literal[u'foo']"]
BBytesWrapperAlias = Wrap[b"Literal['foo']"]
CBytesWrapperAlias = Wrap[b"Literal[b'foo']"]
# Bytes literals are not valid as forward references.
ABytesWrapperAlias = Wrap[b"Literal[u'foo']"] # E: Invalid type comment or annotation
BBytesWrapperAlias = Wrap[b"Literal['foo']"] # E: Invalid type comment or annotation
CBytesWrapperAlias = Wrap[b"Literal[b'foo']"] # E: Invalid type comment or annotation
a_bytes_wrapper_alias: ABytesWrapperAlias
b_bytes_wrapper_alias: BBytesWrapperAlias
c_bytes_wrapper_alias: CBytesWrapperAlias
Expand All @@ -282,9 +283,9 @@ reveal_type(a_str_wrapper_alias) # N: Revealed type is "__main__.Wrap[Liter
reveal_type(b_str_wrapper_alias) # N: Revealed type is "__main__.Wrap[Literal['foo']]"
reveal_type(c_str_wrapper_alias) # N: Revealed type is "__main__.Wrap[Literal[b'foo']]"

reveal_type(a_bytes_wrapper_alias) # N: Revealed type is "__main__.Wrap[Literal['foo']]"
reveal_type(b_bytes_wrapper_alias) # N: Revealed type is "__main__.Wrap[Literal['foo']]"
reveal_type(c_bytes_wrapper_alias) # N: Revealed type is "__main__.Wrap[Literal[b'foo']]"
reveal_type(a_bytes_wrapper_alias) # N: Revealed type is "__main__.Wrap[Any]"
reveal_type(b_bytes_wrapper_alias) # N: Revealed type is "__main__.Wrap[Any]"
reveal_type(c_bytes_wrapper_alias) # N: Revealed type is "__main__.Wrap[Any]"
[builtins fixtures/tuple.pyi]
[out]

Expand Down
Loading