So, I ended up tabling the "remove defaults" idea in favor of putting in a feature request to Mypy to see where that goes. I can anticipate some potential objections to it, and I'm not sure how to address them, so I need people who understand Mypy better than I do to discuss it some.
There are two major reasons I swung around to a feature request:
- What I was trying to accomplish with the code changes was to make a worse version of attrs.evolve.
- I couldn't figure out a good way to get a linting framework to do what I wanted.
- Semgrep doesn't seem to support regex operators in token names, so if I want to account for all of the different reasons that the rule might or might not apply, I'd need a combinatorial explosion of rules. Also, I couldn't find a way to make it detect "a type expression that uses any typevar".
- Although Pylint gets me as far as detecting attrs classes, it's not obvious to me whether the information I actually want is present in the AST.
- I honestly didn't even try with flake8.
My guess is that, with some effort, I could get Pylint working nicely with this idea, but I'd rather focus on the feature request for now.
Let's see about chasing down the information that got me down this tangent in the first place...
- I want to put a Placeholder into an Executable, and I have a means of taking it back out, given the proper data.
- At the other end of the pipeline is the ParametricCommandMeta, which needs to carry out updates to a CommandBuilder.
- The CommandBuilder needs the ability to update an Executable, which we have, and a command.Metadata, which we don't. Yet.
- The Metadata is the most troublesome to update, because it requires conditionally mapping the Executable update over three different data structures.
In terms of raw volume of code, it's not clear that doing this would constitute a win, but it would mean moving code out of the more data-y modules, so I at least want to evaluate how much of a mess it would make.
Let's treat the top-level data structures as mostly straightforward, and focus on the type fiddling.
- Args is one of the baselines, because it just has a MaybeExecutable.
- EnvVars looks a little scarier due to the use of EnvVar, but I can just map a lambda over that, no problem.
- IOSet is mainly confusing because it uses a more restrictive set of types in the left-hand side of the union.
Now, the thing that hopefully makes this less confusing than the existing functions is, I don't need special handling for different entry point types. Everything just gets passed through, no questions asked.
Before I mess around with new code, I want to investigate using the techniques from this comment in the existing code.
(Update: Things aren't currently looking great for the Mypy feature request, so I'm trying to nail down what I'd want out of a plugin. Like, the brief is, the constructor for a generic attrs class shouldn't have any optional arguments, and the question is just how to express that in terms of whatever I use to lint.)
Anyway, back to messing with the code...
- There's some nested code that doesn't need updating because it doesn't need overload.
- There's some code that can't work with this because I need to key off of the type parameters, but fortunately, this isn't an all-or-nothing thing.
So, I've proved that this still works, and the behavior is preserved (up to and including the mystery errors from recent Mypy versions).
At the moment, I just want to wrap up and get to bed, but this was nice for proving out the approach for handling the placeholder updates. I'll try to do that in the next few days, and hopefully it'll get me some data points for those mystery errors.