provinzkraut / unasyncd

Transforming asynchronous to synchronous Python code
MIT License
18 stars 3 forks source link

Re-introduce removal of unused imports #4

Closed provinzkraut closed 1 year ago

provinzkraut commented 1 year ago

Removal of unused imports was removed in #1, since it was quite rough around the edges, not very performant, and its use discouraged.

The reason for all of this was mainly that such a feature isn't all that easy to get right, and I don't think it serves anyone if unasyncd re-implements something that other tools have already solved.

However, due to the nature of unasyncd, not removing unused imports that have become unused as a result of the transformation can cause some issues.

For example, running unasyncd within pre-commit before a tool that removes said imports, pre-commit will never reach a stable state, since it's going to be stuck in a loop of "Unasyncd transforms > unused imports are removed", since after the imports are removed, and unasyncd is run again, the state of the target file will not match what unasyncd expects, and it will re-apply the transfomations, adding back the unused imports.

The current solution to this problem is to structure code in such a way that imports won't get removed, e.g. by adding the needed synced version imports to the async file. This is far from ideal, so a better solution should be implemented.

Solutions

  1. Change unasyncd's transformation model, to perform transformations on the target, rather than the source. This would make the whole process more stable, as the target file could be edited, and unless it's actually out of sync with the requirements, nothing will be changed
  2. Integrate a 3rd party tool to remove unused imports, making unasyncd's output stable in that regard. While this may introduce unnecessary overhead in case this action is already performed somewhere in the toolchain, this could be kept at an insignificant level if ruff were to be chosen for this, thanks to its extremely high performance

Of the two approaches, I think 1) would be the "correct" solution in the long term, but a lot of things get more complex (exclusions for example would have to track fully qualified names across the transformations), and less performant, since scanning and transforming couldn't be done simultaneous anymore.

For the time being, it seems to me that 2) is the right way to go about this.