Skip to content

Commit 4b957ea

Browse files
committed
more tests
1 parent f3a56c3 commit 4b957ea

File tree

2 files changed

+46
-0
lines changed

2 files changed

+46
-0
lines changed

crates/red_knot_python_semantic/resources/mdtest/protocols.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,26 @@ def g(obj: Callable[[int], str], obj2: CallMeMaybe, obj3: Callable[[str], str]):
14371437
obj3 = obj2 # error: [invalid-assignment]
14381438
```
14391439

1440+
## Protocols are never singleton types, and are never single-valued types
1441+
1442+
It *might* be possible to have a singleton protocol-instance type...?
1443+
1444+
For example, `WeirdAndWacky` in the following snippet only has a single possible inhabitant: `None`!
1445+
It is thus a singleton type. However, going out of our way to recognise it as such is probably not
1446+
worth it. Such cases should anyway be exceedingly rare and/or contrived.
1447+
1448+
```py
1449+
from typing import Protocol, Callable
1450+
from knot_extensions import is_singleton, is_single_valued
1451+
1452+
class WeirdAndWacky(Protocol):
1453+
@property
1454+
def __class__(self) -> Callable[[], None]: ...
1455+
1456+
reveal_type(is_singleton(WeirdAndWacky)) # revealed: Literal[False]
1457+
reveal_type(is_single_valued(WeirdAndWacky)) # revealed: Literal[False]
1458+
```
1459+
14401460
## Integration test: `typing.SupportsIndex` and `typing.Sized`
14411461

14421462
`typing.SupportsIndex` and `typing.Sized` are two protocols that are very commonly used in the wild.

crates/red_knot_python_semantic/src/types/display.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,7 @@ mod tests {
783783
use ruff_python_ast::name::Name;
784784

785785
use crate::db::tests::setup_db;
786+
use crate::symbol::typing_extensions_symbol;
786787
use crate::types::{
787788
KnownClass, Parameter, Parameters, Signature, SliceLiteralType, StringLiteralType, Type,
788789
};
@@ -854,6 +855,31 @@ mod tests {
854855
);
855856
}
856857

858+
#[test]
859+
fn synthesized_protocol_display() {
860+
let db = setup_db();
861+
862+
// Call `.normalized()` to turn the class-based protocol into a nameless synthesized one.
863+
let supports_index_synthesized = KnownClass::SupportsIndex.to_instance(&db).normalized(&db);
864+
assert_eq!(
865+
supports_index_synthesized.display(&db).to_string(),
866+
"<Protocol with members '__index__'>"
867+
);
868+
869+
let iterator_synthesized = typing_extensions_symbol(&db, "Iterator")
870+
.symbol
871+
.ignore_possibly_unbound()
872+
.unwrap()
873+
.to_instance(&db)
874+
.unwrap()
875+
.normalized(&db); // Call `.normalized()` to turn the class-based protocol into a nameless synthesized one.
876+
877+
assert_eq!(
878+
iterator_synthesized.display(&db).to_string(),
879+
"<Protocol with members '__iter__', '__next__'>"
880+
);
881+
}
882+
857883
fn display_signature<'db>(
858884
db: &dyn Db,
859885
parameters: impl IntoIterator<Item = Parameter<'db>>,

0 commit comments

Comments
 (0)