I spent a lot of today smacking my head into Mypy plugin stuff, and I think things are coming together and making more sense to me. I've got better error messages and the signatures being returned are in better shape.
From all of this, I now understand that I'm missing a specific case:
saltate(typ: type[T], inst: Any, **changes: Any), when T is an attrs class, should get typed like: saltate(typ: type[T], inst: Any, **changs: <the attributes of T>) -> T
So, let's see if I can untangle that from the current plugin code.
Okay, that wasn't too bad.
I basically had to split apart a fancy conditional, and move a line up in the function, so I could construct a mediumly-permissive signature. The result will sometimes construct somewhat peculiar signatures, but those edge cases should be accompanied by a type checking failure, so I don't believe there's a serious problem with that.
There are a few big misses remaining in the plugin code that will be filled in by the positive test cases, but I want to focus on the last thing I don't quite understand:
Is the length of ctx.args determined solely by the function declaration, or does it somehow depend on the call being processed by the get_function_signature_hook?
If it's the former, I don't understand how I'd write tests for that branch. If it's the latter, I still haven't figured out how to go down that branch. I'm going to take the slightly spicy option of just asserting that my library method will always have exactly three arguments, from the perspective of names available within it.
Anyway, once I have coverage on the hook function locked down, I'm going to work on expanding coverage in the helper functions. This is likely to result in commenting out some blocks or something, but we'll see how that all goes when I take the time to do this stuff. I'm also going to have to comment all of this code thoroughly.
But right now, I'm going to wind down.