Skip to content

Commit

Permalink
[red-knot] Migrate bool/str/repr unit tests to Markdown tests (#…
Browse files Browse the repository at this point in the history
…15534)

## Summary

Part of #15397.

## Test Plan

Markdown tests.
  • Loading branch information
InSyncWithFoo authored Jan 16, 2025
1 parent e2da33a commit 2e6729d
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def _(flag: bool):
foo_3: LiteralString = "foo" * 1_000_000_000
bar_3: str = foo_2 # fine

baz_1: str = str()
baz_1: str = repr(object())
qux_1: LiteralString = baz_1 # error: [invalid-assignment]

baz_2: LiteralString = "baz" * 1_000_000_000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
## Expression

```py
x = 0
y = str()
z = False

reveal_type(f"hello") # revealed: Literal["hello"]
reveal_type(f"h {x}") # revealed: Literal["h 0"]
reveal_type("one " f"single " f"literal") # revealed: Literal["one single literal"]
reveal_type("first " f"second({x})" f" third") # revealed: Literal["first second(0) third"]
reveal_type(f"-{y}-") # revealed: str
reveal_type(f"-{y}-" f"--" "--") # revealed: str
reveal_type(f"{z} == {False} is {True}") # revealed: Literal["False == False is True"]
from typing_extensions import Literal

def _(x: Literal[0], y: str, z: Literal[False]):
reveal_type(f"hello") # revealed: Literal["hello"]
reveal_type(f"h {x}") # revealed: Literal["h 0"]
reveal_type("one " f"single " f"literal") # revealed: Literal["one single literal"]
reveal_type("first " f"second({x})" f" third") # revealed: Literal["first second(0) third"]
reveal_type(f"-{y}-") # revealed: str
reveal_type(f"-{y}-" f"--" "--") # revealed: str
reveal_type(f"{z} == {False} is {True}") # revealed: Literal["False == False is True"]
```

## Conversion Flags
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# `__str__` and `__repr__`

```py
from typing_extensions import Literal, LiteralString

def _(
a: Literal[1],
b: Literal[True],
c: Literal[False],
d: Literal["ab'cd"],
e: LiteralString,
f: int,
):
reveal_type(str(a)) # revealed: Literal["1"]
reveal_type(str(b)) # revealed: Literal["True"]
reveal_type(str(c)) # revealed: Literal["False"]
reveal_type(str(d)) # revealed: Literal["ab'cd"]
reveal_type(str(e)) # revealed: LiteralString
reveal_type(str(f)) # revealed: str

reveal_type(repr(a)) # revealed: Literal["1"]
reveal_type(repr(b)) # revealed: Literal["True"]
reveal_type(repr(c)) # revealed: Literal["False"]
reveal_type(repr(d)) # revealed: Literal["'ab\\'cd'"]
reveal_type(repr(e)) # revealed: LiteralString
reveal_type(repr(f)) # revealed: str
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Truthiness

```py
from typing_extensions import Literal, LiteralString
from knot_extensions import AlwaysFalsy, AlwaysTruthy

def _(
a: Literal[1],
b: Literal[-1],
c: Literal["foo"],
d: tuple[Literal[0]],
e: Literal[1, 2],
f: AlwaysTruthy,
):
reveal_type(bool(a)) # revealed: Literal[True]
reveal_type(bool(b)) # revealed: Literal[True]
reveal_type(bool(c)) # revealed: Literal[True]
reveal_type(bool(d)) # revealed: Literal[True]
reveal_type(bool(e)) # revealed: Literal[True]
reveal_type(bool(f)) # revealed: Literal[True]

def _(
a: tuple[()],
b: Literal[0],
c: Literal[""],
d: Literal[b""],
e: Literal[0, 0],
f: AlwaysFalsy,
):
reveal_type(bool(a)) # revealed: Literal[False]
reveal_type(bool(b)) # revealed: Literal[False]
reveal_type(bool(c)) # revealed: Literal[False]
reveal_type(bool(d)) # revealed: Literal[False]
reveal_type(bool(e)) # revealed: Literal[False]
reveal_type(bool(f)) # revealed: Literal[False]

def _(
a: str,
b: Literal[1, 0],
c: str | Literal[0],
d: str | Literal[1],
):
reveal_type(bool(a)) # revealed: bool
reveal_type(bool(b)) # revealed: bool
reveal_type(bool(c)) # revealed: bool
reveal_type(bool(d)) # revealed: bool
```
72 changes: 19 additions & 53 deletions crates/red_knot_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2013,6 +2013,14 @@ impl<'db> Type<'db> {
CallOutcome::callable(binding)
}

Some(KnownFunction::Repr) => {
if let Some(first_arg) = binding.one_parameter_ty() {
binding.set_return_ty(first_arg.repr(db));
};

CallOutcome::callable(binding)
}

Some(KnownFunction::AssertType) => {
let Some((_, asserted_ty)) = binding.two_parameter_tys() else {
return CallOutcome::callable(binding);
Expand Down Expand Up @@ -2050,6 +2058,12 @@ impl<'db> Type<'db> {
.first_argument()
.map(|arg| arg.bool(db).into_type(db))
.unwrap_or(Type::BooleanLiteral(false)),

Some(KnownClass::Str) => arguments
.first_argument()
.map(|arg| arg.str(db))
.unwrap_or(Type::string_literal(db, "")),

_ => Type::Instance(InstanceType { class }),
}))
}
Expand Down Expand Up @@ -3389,6 +3403,8 @@ pub enum KnownFunction {
RevealType,
/// `builtins.len`
Len,
/// `builtins.repr`
Repr,
/// `typing(_extensions).final`
Final,

Expand Down Expand Up @@ -3436,6 +3452,7 @@ impl KnownFunction {
"issubclass" => Self::ConstraintFunction(KnownConstraintFunction::IsSubclass),
"reveal_type" => Self::RevealType,
"len" => Self::Len,
"repr" => Self::Repr,
"final" => Self::Final,
"no_type_check" => Self::NoTypeCheck,
"assert_type" => Self::AssertType,
Expand Down Expand Up @@ -3464,7 +3481,7 @@ impl KnownFunction {
module.is_builtins()
}
},
Self::Len => module.is_builtins(),
Self::Len | Self::Repr => module.is_builtins(),
Self::AssertType | Self::Cast | Self::RevealType | Self::Final | Self::NoTypeCheck => {
matches!(module, KnownModule::Typing | KnownModule::TypingExtensions)
}
Expand Down Expand Up @@ -3496,6 +3513,7 @@ impl KnownFunction {

Self::ConstraintFunction(_)
| Self::Len
| Self::Repr
| Self::Final
| Self::NoTypeCheck
| Self::RevealType
Expand Down Expand Up @@ -4625,58 +4643,6 @@ pub(crate) mod tests {
assert!(!b.is_gradual_equivalent_to(&db, a));
}

#[test_case(Ty::IntLiteral(1); "is_int_literal_truthy")]
#[test_case(Ty::IntLiteral(-1))]
#[test_case(Ty::StringLiteral("foo"))]
#[test_case(Ty::Tuple(vec![Ty::IntLiteral(0)]))]
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(2)]))]
fn is_truthy(ty: Ty) {
let db = setup_db();
assert_eq!(ty.into_type(&db).bool(&db), Truthiness::AlwaysTrue);
}

#[test_case(Ty::Tuple(vec![]))]
#[test_case(Ty::IntLiteral(0))]
#[test_case(Ty::StringLiteral(""))]
#[test_case(Ty::Union(vec![Ty::IntLiteral(0), Ty::IntLiteral(0)]))]
fn is_falsy(ty: Ty) {
let db = setup_db();
assert_eq!(ty.into_type(&db).bool(&db), Truthiness::AlwaysFalse);
}

#[test_case(Ty::BuiltinInstance("str"))]
#[test_case(Ty::Union(vec![Ty::IntLiteral(1), Ty::IntLiteral(0)]))]
#[test_case(Ty::Union(vec![Ty::BuiltinInstance("str"), Ty::IntLiteral(0)]))]
#[test_case(Ty::Union(vec![Ty::BuiltinInstance("str"), Ty::IntLiteral(1)]))]
fn boolean_value_is_unknown(ty: Ty) {
let db = setup_db();
assert_eq!(ty.into_type(&db).bool(&db), Truthiness::Ambiguous);
}

#[test_case(Ty::IntLiteral(1), Ty::StringLiteral("1"))]
#[test_case(Ty::BooleanLiteral(true), Ty::StringLiteral("True"))]
#[test_case(Ty::BooleanLiteral(false), Ty::StringLiteral("False"))]
#[test_case(Ty::StringLiteral("ab'cd"), Ty::StringLiteral("ab'cd"))] // no quotes
#[test_case(Ty::LiteralString, Ty::LiteralString)]
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str"))]
fn has_correct_str(ty: Ty, expected: Ty) {
let db = setup_db();

assert_eq!(ty.into_type(&db).str(&db), expected.into_type(&db));
}

#[test_case(Ty::IntLiteral(1), Ty::StringLiteral("1"))]
#[test_case(Ty::BooleanLiteral(true), Ty::StringLiteral("True"))]
#[test_case(Ty::BooleanLiteral(false), Ty::StringLiteral("False"))]
#[test_case(Ty::StringLiteral("ab'cd"), Ty::StringLiteral("'ab\\'cd'"))] // single quotes
#[test_case(Ty::LiteralString, Ty::LiteralString)]
#[test_case(Ty::BuiltinInstance("int"), Ty::BuiltinInstance("str"))]
fn has_correct_repr(ty: Ty, expected: Ty) {
let db = setup_db();

assert_eq!(ty.into_type(&db).repr(&db), expected.into_type(&db));
}

#[test]
fn typing_vs_typeshed_no_default() {
let db = TestDbBuilder::new()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ TypeOf: _SpecialForm
# Ideally, these would be annotated using `TypeForm`, but that has not been
# standardized yet (https://peps.python.org/pep-0747).
def is_equivalent_to(type_a: Any, type_b: Any) -> bool: ...
def is_subtype_of(type_derived: Any, typ_ebase: Any) -> bool: ...
def is_subtype_of(type_derived: Any, type_base: Any) -> bool: ...
def is_assignable_to(type_target: Any, type_source: Any) -> bool: ...
def is_disjoint_from(type_a: Any, type_b: Any) -> bool: ...
def is_fully_static(type: Any) -> bool: ...
Expand Down

0 comments on commit 2e6729d

Please sign in to comment.