Skip to content

Commit

Permalink
[red-knot] knot_extensions Python API (#15103)
Browse files Browse the repository at this point in the history
## Summary

Adds a type-check-time Python API that allows us to create and
manipulate types and to test various of their properties. For example,
this can be used to write a Markdown test to make sure that `A & B` is a
subtype of `A` and `B`, but not of an unrelated class `C` (something
that requires quite a bit more code to do in Rust):
```py
from knot_extensions import Intersection, is_subtype_of, static_assert

class A: ...
class B: ...

type AB = Intersection[A, B]

static_assert(is_subtype_of(AB, A))
static_assert(is_subtype_of(AB, B))

class C: ...
static_assert(not is_subtype_of(AB, C))
```

I think this functionality is also helpful for interactive debugging
sessions, in order to query various properties of Red Knot's type
system. Which is something that otherwise requires a custom Rust unit
test, some boilerplate code and constant re-compilation.

## Test Plan

- New Markdown tests
- Tested the modified typeshed_sync workflow locally
  • Loading branch information
sharkdp authored Jan 8, 2025
1 parent 03ff883 commit 235fdfc
Show file tree
Hide file tree
Showing 15 changed files with 826 additions and 17 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/sync_typeshed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ jobs:
cp -r typeshed/stdlib ruff/crates/red_knot_vendored/vendor/typeshed/stdlib
rm -rf ruff/crates/red_knot_vendored/vendor/typeshed/stdlib/@tests
git -C typeshed rev-parse HEAD > ruff/crates/red_knot_vendored/vendor/typeshed/source_commit.txt
# Patch the typeshed stubs to include `knot_extensions`
ln -s ../../../knot_extensions/knot_extensions.pyi ruff/crates/red_knot_vendored/vendor/typeshed/stdlib/
echo "# Patch applied for red_knot:" >> ruff/crates/red_knot_vendored/vendor/typeshed/stdlib/VERSIONS
echo "knot_extensions: 3.0-" >> ruff/crates/red_knot_vendored/vendor/typeshed/stdlib/VERSIONS
- name: Commit the changes
id: commit
if: ${{ steps.sync.outcome == 'success' }}
Expand Down
64 changes: 64 additions & 0 deletions crates/red_knot_python_semantic/resources/mdtest/call/function.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,67 @@ def f(x: int) -> int:
# error: 18 [parameter-already-assigned] "Multiple values provided for parameter `x` of function `f`"
reveal_type(f(1, x=2)) # revealed: int
```

## Special functions

Some functions require special handling in type inference. Here, we make sure that we still emit
proper diagnostics in case of missing or superfluous arguments.

### `reveal_type`

```py
from typing_extensions import reveal_type

# error: [missing-argument] "No argument provided for required parameter `obj` of function `reveal_type`"
reveal_type() # revealed: Unknown

# error: [too-many-positional-arguments] "Too many positional arguments to function `reveal_type`: expected 1, got 2"
reveal_type(1, 2) # revealed: Literal[1]
```

### `static_assert`

```py
from knot_extensions import static_assert

# error: [missing-argument] "No argument provided for required parameter `condition` of function `static_assert`"
# error: [static-assert-error]
static_assert()

# error: [too-many-positional-arguments] "Too many positional arguments to function `static_assert`: expected 2, got 3"
static_assert(True, 2, 3)
```

### `len`

```py
# error: [missing-argument] "No argument provided for required parameter `obj` of function `len`"
len()

# error: [too-many-positional-arguments] "Too many positional arguments to function `len`: expected 1, got 2"
len([], 1)
```

### Type API predicates

```py
from knot_extensions import is_subtype_of, is_fully_static

# error: [missing-argument]
is_subtype_of()

# error: [missing-argument]
is_subtype_of(int)

# error: [too-many-positional-arguments]
is_subtype_of(int, int, int)

# error: [too-many-positional-arguments]
is_subtype_of(int, int, int, int)

# error: [missing-argument]
is_fully_static()

# error: [too-many-positional-arguments]
is_fully_static(int, int)
```
Loading

0 comments on commit 235fdfc

Please sign in to comment.