Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle fixers by short name as well as long name #164

Merged
merged 2 commits into from Apr 11, 2018

Conversation

MartinFalatic
Copy link
Contributor

In 2to3, fixers are short names such as reduce. In python-modernize, names are long-form such as lib2to3.fixes.fix_reduce. This fix allows both naming conventions to be used.

Also allows fixers to be listed as comma-separated values.

Also improved output to show more detailed initialization information.

Fixes #27 #163

In `2to3`, fixers are short names such as `reduce`. In Modernize,
names are long-form such as `lib2to3.fixes.fix_reduce`.
This fix allows both naming conventions to be used.

Allow fixers to be listed as comma-separated values

Improved output to show more detailed initialization information.
@takluyver
Copy link
Contributor

I think we have some fixers in libmodernize with the same names as fixers in 2to3 - we should make sure that when the short name is used, the modernize fixer is selected over the 2to3 one. I haven't checked if that works correctly with this code, but could you take a look? :-)

@MartinFalatic
Copy link
Contributor Author

This doesn't happen thanks to lib2to3_fix_names which remains part of __init__.py. That is where names handled by modernize are currently being excluded from the 2to3 list so only the modernize version appears (otherwise you'd already be running into this problem).

@takluyver
Copy link
Contributor

Gotcha, thanks

for fixname in sorted(avail_fixes):
print(" {} ({})".format(fixname, fixname.split(".fix_", 1)[1]))
else:
print(" (None)")
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this ever happen? It looks like at least the 2to3 part of avail_fixes is hardcoded, so it should never be an empty set.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Exclusions are applied after inclusions, so this does happen (at that point in the code) in certain edge cases (e.g., your "fix" list (explicit) is a subset of the "nofix" list, in which case the explicit fix is noted but the fixer won't be loaded because it's in the "nofix" list). I felt it was better to have this show something in this odd null case than nothing at all.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, but this is listing available fixers, if I understand correctly - it's before any of the selection has been applied.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I was looking at one of the other (None)s. It should be impossible but I added it as in the other areas for insurance against the unexpected (and future-proofing) out of habit. It could be deleted.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK. Let's either delete the branch, or put a comment mentioning that it should never happen, but it's just for insurance. Then I'm happy to merge this. :-)

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'll remove the guard around it - there should always be some initial fixers possible, even if they all get cancelled out later.

if tgt == fix or tgt.endswith(".fix_{}".format(fix)):
matched = tgt
unwanted_fixes.add(matched)
if matched is None:
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be done without using the matched variable, by using break and an else on the for loop (which is run if it never reached break). Personally I find this quite neat for this kind of case, but I know some people don't like the for/else construct, so it's up to you.

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 was a bit of insurance against multiple matches too, which is why I didn't put a break in to begin with (that I often forget about else when it comes to for loops in Python is, however, a sad testament to my C/C++ upbringing. 😃 )

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough.

@MartinFalatic
Copy link
Contributor Author

Do you prefer I pre-squash my next commit (--amend and --force push) or just add a new commit?

@takluyver
Copy link
Contributor

I'm happy either way. :-)

@MartinFalatic
Copy link
Contributor Author

And a second question: In explaining the (None) bit above I first discussed the explicit fixers. Part of why I display them at all is to show what is ultimately getting fed into Python's 2to3 RefactoringTool, but when I wrote it I wasn't sure if it would take an explicit fixer even if it was given no matching fixer in fixer_names.

For example: python ./modernize.py modernize.py -v --nofix import,raise --fix import gives:

 Loading the following fixers:
    (None)
 Applying the following explicit transformations:
    libmodernize.fixes.fix_import  (import)

And RefactoringTool loads no fixers, not even the explicit one. Looking at the Python 2to3 source that seems to be a general rule, but I'm also wondering if there are any obvious exceptions to that rule?

In short, is it fair to say that RefactoringTool will never apply an explicit fix if it's not in fixer_names to begin with? If so, I can filter the displayed list of explicit transformations given against what fixers are actually going to get passed, making that message more exact.

@takluyver
Copy link
Contributor

Looking at the code in lib2to3, it looks like that's the case - it iterates through fixers and checks them against explicit if necessary (if they're not a fixer that's applied by default, I guess). But it's been quite some time since I looked into this code in any detail, so you probably have a better idea of what it's doing right now than I do.

Also added some comments and cleaned up logic
@takluyver takluyver merged commit 53fa52f into PyCQA:master Apr 11, 2018
@takluyver
Copy link
Contributor

Thanks!

@MartinFalatic
Copy link
Contributor Author

Glad to contribute! I pushed the remaining updates as a new commit because I remembered that Travis doesn't seem to re-run correctly otherwise.

FYI, after all this I created Python Modernize Reporter as a wrapper to add features and functionality that were probably beyond the scope of modernize itself (e.g., production hooks and output tweaks for lint-like behavior). Very grateful for modernize as we migrate to Python 3.

Hope to see you at PyCon if you're going this year (or next time you're at a meetup in SF).

@MartinFalatic MartinFalatic deleted the mff-allow-shortnames branch April 11, 2018 20:11
@takluyver
Copy link
Contributor

Cool. :-) I think at least some of that (like TeamCity integration) is probably out of scope, but feel free to propose changes if it makes what you're doing easier or more robust. We could also consider bringing it under the python-modernize Github org if you like.

I moved away from the Bay Area in 2015, and I'm avoiding travel to the US for now due to the political situation, so it might be some time before you see me. I'm just starting a job in Germany at the moment.

@MartinFalatic
Copy link
Contributor Author

I'm sorry to hear that but I understand - it's an unpleasant time here to say the least. Wishing you well on the new job!

As for the reporter tool, it's a work in progress.... and it's not just a reporter anymore - it passes almost all of the options straight through to modernize. The file/folder exclusion functionality is really the biggest feature - it makes it a lot easier to manage files that don't take well to the automated changes.

@MartinFalatic
Copy link
Contributor Author

When you get a moment please check out #165 - if/when that's in a release I can finally stop using the (temporary) fork I'm using now. We can continue the discussion there...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Select fixes using short name
2 participants