-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[red-knot] Test setup utilities #13789
Comments
Correct -- for now, the best you can do is |
(I edited your post to number your suggestions so it would be easier to discuss them, hope that's okay :-) Your proposals (4) and (5) both involve injecting some sort of "magic" function into the namespace that we could use without any imports, which would then create instances of the types required for the test. My first instinct was that I didn't much like the idea, because in general I'd like the test snippets to be as close as possible to executable Python code. I think it's useful to keep a close resemblance between our test snippets and user code we'll actually be running on. I also think keeping our test snippets as close as possible to executable Python makes them much easier for us and external contributors to understand. However, I then realised that this isn't really that different to what we already do with The key differences with
Yes, I think I agree. Mypy has quite an extensive test suite that works in a similar way to our new framework, and they've managed to do without a One way that mypy test snippets do differ from executable Python is in their use of "fixture stubs". Rather than using their full vendored stdlib typeshed stubs (which is what they use for checking user code), in their tests they use a radically simplified version of typeshed. This speeds up their tests a lot, but it is very frequently a source of confusion for mypy developers and contributors, who often think they've fixed a bug only to realise that the type inference their users are seeing for standard-library functions is very different to the type inference they thought they had asserted in their test snippets. |
Just for the sake of discussion, another possibility here is to allow "layering" files, so in a Markdown header section you can provide a file that will be shared by all sub-tests within that section. So this would let you write your own little utilities (or simply type-annotated variables in a stub file) and import/reuse them in a bunch of related tests. Downsides are less locality of tests, and more complexity in understanding the structure and behavior of a test. I also don't want to do anything here that's specifically motivated by limitations we should lift soon, like not understanding function arguments, or unions in annotations. I think on the whole my preference is also defining functions with typed arguments, in most cases. |
Not that I think anyone wants the class conjure[T]:
def __new__(cls) -> T:
raise TypeError(f"Can not actually conjure up values of type T")
x = conjure[int | None]()
reveal_type(x) # int | None |
I'm actually tentatively closing this, as it seems like we all agree on using the function-parameter approach once we support it. |
This recently came up in a discussion. A lot of red-knot tests require some form of "setup" in the sense that they create variables of a particular type. This is not always straightforward. For example, to create a variable of type
int
, you need to make sure that it doesn't end up as aLiteralInt[…]
. So some tests use a pattern like this:To create a variable with a union type, a lot of tests follow a pattern like
It's unclear to me if this really requires any action, but I thought it might make sense to discuss this in a bit more detail. Here are some approaches (some less generic than others) that I could think of.
1.
def f() -> MyDesiredType; x = f()
Upsides:
MyDesiredType
directlyDownsides:
2.
def f(x: MyDesiredType): …
Upsides:
MyDesiredType
directlyDownsides:
3.
a if flag or b
(only relevant for union types)
Upsides:
flag
beforehandDownsides:
flag
into the test environment somehow.flag
?!)4. Helper functions like
one_of(a, b)
We could inject new functions, just for testing purposes. For example, we might have a function similar to
to easily create union types
Upsides(?):
x = one_of(1, None)
is slightly more readable thanx = 1 if flag else None
(but only if you know whatone_of
does)Downsides:
5. A magic
conjure
functionI'm not even sure if this is technically possible, but other languages have ways to create values of type
T
out of nothing. Not actually, of course. But for the purpose of doing interesting things at "type check time". For example, C++ hasstd::declval<T>()
. Rust haslet x: T = todo!()
. Functional languages haveabsurd :: ⊥-> T
.You can't specify explicit generic parameters in a function call in Python (?), so we couldn't do something like
x = conjure[int | None]()
, but maybe there is some way to create a construct conceptually similar toI think I would personally prefer the simple
def f(x: MyDesiredType): …
approach, once we make that work.The text was updated successfully, but these errors were encountered: