Skip to content

Comments

[ty] Support typing.TypeAliasType#18156

Merged
sharkdp merged 3 commits intomainfrom
david/bare-type-alias-type
May 19, 2025
Merged

[ty] Support typing.TypeAliasType#18156
sharkdp merged 3 commits intomainfrom
david/bare-type-alias-type

Conversation

@sharkdp
Copy link
Contributor

@sharkdp sharkdp commented May 17, 2025

Summary

Support direct uses of typing.TypeAliasType, as in:

from typing import TypeAliasType

IntOrStr = TypeAliasType("IntOrStr", int | str)

def f(x: IntOrStr) -> None:
    reveal_type(x)  # revealed: int | str

closes astral-sh/ty#392

Ecosystem

The new false positive here:

+ error[invalid-type-form] altair/utils/core.py:49:53: The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`

comes from the fact that we infer the second argument as a type expression now. We silence false positives for PEP695 ParamSpecs, but not for P = ParamSpec("P") inside Callable[P, ...].

Test Plan

New Markdown tests

@sharkdp sharkdp added the ty Multi-file analysis & type inference label May 17, 2025
@github-actions
Copy link
Contributor

mypy_primer results

Changes were detected when running on open source projects
altair (https://github.com/vega/altair)
+ error[invalid-type-form] altair/utils/core.py:49:53: The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`
- error[invalid-argument-type] altair/utils/core.py:48:58: Argument to bound method `__init__` is incorrect: Expected `tuple[TypeVar | ParamSpec | TypeVarTuple, ...]`, found `tuple[typing.TypeVar]`
- error[invalid-argument-type] altair/utils/core.py:49:60: Argument to bound method `__init__` is incorrect: Expected `tuple[TypeVar | ParamSpec | TypeVarTuple, ...]`, found `tuple[ParamSpec, typing.TypeVar]`
- error[invalid-argument-type] altair/utils/core.py:53:56: Argument to bound method `__init__` is incorrect: Expected `tuple[TypeVar | ParamSpec | TypeVarTuple, ...]`, found `tuple[typing.TypeVar, typing.TypeVar]`
- error[invalid-argument-type] altair/utils/core.py:56:54: Argument to bound method `__init__` is incorrect: Expected `tuple[TypeVar | ParamSpec | TypeVarTuple, ...]`, found `tuple[typing.TypeVar, ParamSpec, typing.TypeVar]`
- error[invalid-argument-type] altair/utils/plugin_registry.py:25:52: Argument to bound method `__init__` is incorrect: Expected `tuple[TypeVar | ParamSpec | TypeVarTuple, ...]`, found `tuple[typing.TypeVar]`
- error[invalid-argument-type] altair/utils/schemapi.py:903:56: Argument to bound method `__init__` is incorrect: Expected `tuple[TypeVar | ParamSpec | TypeVarTuple, ...]`, found `tuple[typing.TypeVar]`
- error[invalid-argument-type] altair/vegalite/v5/schema/_typing.py:105:61: Argument to bound method `__init__` is incorrect: Expected `tuple[TypeVar | ParamSpec | TypeVarTuple, ...]`, found `tuple[typing.TypeVar]`
- error[invalid-context-manager] tests/vegalite/v5/test_api.py:1409:10: Object of type `Unknown | PluginEnabler[@Todo(unknown type subscript), ThemeConfig]` cannot be used with `with` because it does not correctly implement `__exit__`
+ error[invalid-context-manager] tests/vegalite/v5/test_api.py:1409:10: Object of type `Unknown | PluginEnabler[@Todo(Generic PEP-695 type alias), ThemeConfig]` cannot be used with `with` because it does not correctly implement `__exit__`
- error[invalid-context-manager] tests/vegalite/v5/test_api.py:1414:10: Object of type `Unknown | PluginEnabler[@Todo(unknown type subscript), ThemeConfig]` cannot be used with `with` because it does not correctly implement `__exit__`
+ error[invalid-context-manager] tests/vegalite/v5/test_api.py:1414:10: Object of type `Unknown | PluginEnabler[@Todo(Generic PEP-695 type alias), ThemeConfig]` cannot be used with `with` because it does not correctly implement `__exit__`
- error[invalid-context-manager] tests/vegalite/v5/test_api.py:1420:10: Object of type `Unknown | PluginEnabler[@Todo(unknown type subscript), ThemeConfig]` cannot be used with `with` because it does not correctly implement `__exit__`
+ error[invalid-context-manager] tests/vegalite/v5/test_api.py:1420:10: Object of type `Unknown | PluginEnabler[@Todo(Generic PEP-695 type alias), ThemeConfig]` cannot be used with `with` because it does not correctly implement `__exit__`
- error[invalid-context-manager] tests/vegalite/v5/test_theme.py:48:14: Object of type `Unknown | PluginEnabler[@Todo(unknown type subscript), ThemeConfig]` cannot be used with `with` because it does not correctly implement `__exit__`
+ error[invalid-context-manager] tests/vegalite/v5/test_theme.py:48:14: Object of type `Unknown | PluginEnabler[@Todo(Generic PEP-695 type alias), ThemeConfig]` cannot be used with `with` because it does not correctly implement `__exit__`
- Found 1349 diagnostics
+ Found 1343 diagnostics

pub(crate) fn definition(&self, db: &'db dyn Db) -> Option<Definition<'db>> {
match self {
TypeAliasType::PEP695(type_alias) => Some(type_alias.definition(db)),
TypeAliasType::Bare(_) => None,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used for go-to-type-definition. It seems more involved to provide the original definition here, as we would need to keep track of it through various Type operations and the calling machinery.

@sharkdp sharkdp force-pushed the david/bare-type-alias-type branch 2 times, most recently from a66b2fd to c89a7f2 Compare May 17, 2025 20:31
@sharkdp sharkdp force-pushed the david/bare-type-alias-type branch from c89a7f2 to 2c0a65d Compare May 17, 2025 20:36
@sharkdp sharkdp marked this pull request as ready for review May 17, 2025 20:36
const KNOWN_FAILURES: &[(&str, bool, bool)] = &[
// Fails with too-many-cycle-iterations due to a self-referential
// type alias, see https://github.com/astral-sh/ty/issues/256
("crates/ruff_linter/resources/test/fixtures/pyflakes/F401_34.py", true, true),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unfortunate, but that file could just as easily have been written using PEP 695 type aliases …

Comment on lines 912 to 923
Some(KnownClass::TypeAliasType) => {
if let [Some(name), Some(value), ..] = overload.parameter_types() {
// TODO: emit a diagnostic if the name is not a string literal
if let Some(name) = name.into_string_literal() {
overload.set_return_type(Type::KnownInstance(
KnownInstanceType::TypeAliasType(TypeAliasType::Bare(
BareTypeAliasType::new(db, name.value(db), value),
)),
));
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you move this construction logic into infer.rs, like we do for legacy typevars, you'll be able to get the Definition of the assignment statement, at least for the case where a call to the TypeAliasType constructor is being immediately assigned to a variable. That will let you return a non-None result for definition up above for bare type aliases too.

let assigned_to = (self.index)
.try_expression(call_expression_node)
.and_then(|expr| expr.assigned_to(self.db()));

(Moving the construction logic also made it easier to emit diagnostics for legacy typevar construction, which might help you discharge the TODO comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you — I didn't realize we had call-related code in infer.rs, too. That was really helpful. TODO comment is also resolved.

/// from typing import TypeAliasType
///
/// IntOrStr = TypeAliasType("IntOrStr", int | str) # okay
/// NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name must be a string literal
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure if we should have the same strict requirements here as for legacy TypeVar constructions. I couldn't find anything in the spec, but pyright also emits diagnostics, if a TypeAliasType is not immediately assigned to a variable, for example.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me!

@sharkdp sharkdp force-pushed the david/bare-type-alias-type branch from d9729e6 to 4852009 Compare May 19, 2025 07:47
### Links
* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L86)
* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L87)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you have to update these manually?

Whew, you did not! Looks like this was auto-generated from diagnostic.rs

[Not for this PR] We might consider marking this as a generated file in .gitattributes to hide them in PR diffs

/// from typing import TypeAliasType
///
/// IntOrStr = TypeAliasType("IntOrStr", int | str) # okay
/// NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name must be a string literal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me!

@sharkdp sharkdp merged commit 4c889d5 into main May 19, 2025
35 checks passed
@sharkdp sharkdp deleted the david/bare-type-alias-type branch May 19, 2025 14:36
sharkdp added a commit that referenced this pull request May 19, 2025
dcreager added a commit that referenced this pull request May 19, 2025
* main:
  [ty] Infer function call typevars in both directions (#18155)
  Add rustfmt.toml file (#18197)
  [ty] Update mypy primer (#18196)
  [ty] Mark generated files as such in .gitattributes (#18195)
  [ty] Support `typing.TypeAliasType` (#18156)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

support for bare TypeAliasType

3 participants