Re: [PATCH v4 1/2] scripts: generate_rust_analyzer.py: add versioning infrastructure

From: Jesung Yang

Date: Wed Apr 29 2026 - 10:39:07 EST


On Tue, Apr 28, 2026 at 10:35 PM Jesung Yang <y.j3ms.n@xxxxxxxxx> wrote:
> On Mon, Apr 27, 2026 at 2:23 PM Tamir Duberstein <tamird@xxxxxxxxxx> wrote:
[...]
> >
> > ```
> > T = t.TypeVar('T')
> > if sys.version_info < (3, 10):
> > def wrapper(func: t.Callable[..., T]) -> t.Callable[..., T]:
> > def wrapper(*args: t.Any, **kwargs: t.Any) -> T:
> > return func(*args, **kwargs)
> > return wrapper
> > else:
> > P = t.ParamSpec('P')
> >
> > def wrapper(func: t.Callable[P, T]) -> t.Callable[P, T]:
> > def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
> > return func(*args, **kwargs)
> > return wrapper
> > ```
> >
> > This also works and type checks in both versions. Of course, the type
> > information is discarded on 3.9, but as long as we run mypy on 3.10 we
> > are OK.
>
> I'd go for the latter, since the former would require Python 3.9 users
> to install an extra dependency to run `make LLVM=1 rust-analyzer`.

OK, I did some more investigation and it appears that `ParamSpec` is
generally intended for "preserve and forward" behavior; IIUC, we can't
modify an arbitrary parameter type with it. To quote the docs:

They are used to forward the parameter types of one callable to
another callable -- a pattern commonly found in higher order functions
and decorators [1].

`Concatenate` seems to be one possible way to transform parameter types:

`Concatenate` can be used in conjunction with `Callable` and
`ParamSpec` to annotate a higher-order callable which adds, removes,
or transforms parameters of another callable [2].

However, it requires the parameter whose type we want to modify to
occupy a fixed position. In our case, we would need to, for example,
move the `deps` parameter to the first positional argument in each
`append_*` function to ensure its placement is consistent across all of
them.

Given the above, I now think the simpler approach is to 1) modify
the type of `deps` to `List[Optional[Dependency]]`, and 2) directly
embed the filtering logic inside each `append_*` function:

-------------------------------------------------
def filter_deps(deps: List[Optional[Dependency]]) -> List[Dependency]:
return [dep for dep in deps if dep is not None]

def append_crate(
...,
deps: List[Optional[Dependency]],
...,
) -> Dependency:
return register_crate(
build_crate(
display_name,
root_module,
deps=filter_deps(deps),
cfg=cfg,
crate_attrs=crate_attrs,
is_workspace_member=is_workspace_member,
edition=edition,
)
)

# ...
-------------------------------------------------

(The commit using the above approach can be found at [3].)

Similar to the decorator approach, this requires less cleanup compared
to the original `sysroot_deps` version, since we're only touching the
function definitions rather than call sites.

Of course we will need to revert the parameter type when the time comes,
but in my view, this is much more straightforward than the decorator
approach, which needs extra setup to work generally.

What do you think?

[1] https://docs.python.org/3/library/typing.html#typing.ParamSpec
[2] https://docs.python.org/3/library/typing.html#typing.Concatenate
[3] https://github.com/J3m3/linux/commit/ada5d5af9348

Best regards,
Jesung