Skip to content

add support of 'six.with_metaclass' #841

Merged
PCManticore merged 4 commits intopylint-dev:masterfrom
fmigneault:fix-six-metaclass
Feb 7, 2021
Merged

add support of 'six.with_metaclass' #841
PCManticore merged 4 commits intopylint-dev:masterfrom
fmigneault:fix-six-metaclass

Conversation

@fmigneault
Copy link
Copy Markdown
Contributor

Steps

  • For new features or bug fixes, add a ChangeLog entry describing what your PR does.
  • Write a good description on what the PR does.

Description

Fixes issue #713 by providing corresponding transform and unit tests.

Type of Changes

Type
🐛 Bug fix
✨ New feature

Related Issue

Closes #713

@fmigneault fmigneault changed the title add support to 'six.with_metaclass' add support of 'six.with_metaclass' Sep 12, 2020
@fmigneault
Copy link
Copy Markdown
Contributor Author

fmigneault commented Sep 12, 2020

flagged pylint issues are related to very recent merge of pylint-dev/pylint#3782 with fix of issue pylint-dev/pylint#3468

@tkukushkin
Copy link
Copy Markdown

@fmigneault please pay attention to #713 (comment). This code

from enum import Enum, EnumMeta

import six


class FooMeta(EnumMeta):
    pass


class Foo(six.with_metaclass(FooMeta, Enum)):
    bar = 1

produces error with this fix:

foo.py:3: [W0611 unused-import] Unused import six

@fmigneault
Copy link
Copy Markdown
Contributor Author

@tkukushkin
I don't know enough the internals of astroid to address that specific issue of #841 (comment). Maybe @PCManticore (or other dev from astroid) can help?

The problem in is that after six.with_metaclass(FooMeta, Enum) is processed, the ClassDef effectively becomes:

ClassDef.FooMeta(name='FooMeta',
                 doc=None,
                 decorators=None,
                 bases=[<Name.EnumMeta l.6 at 0x7ff0d24e1a10>],
                 body=[<Pass l.7 at 0x7ff0d24e1a90>])

Therefore there is no more reference to six as seen in the source code. At runtime, the class definition is "converted" and doesn't need six anymore. Is there some way to maintain that reference somewhere in the ClassDef for the check to find it?

@tkukushkin
Copy link
Copy Markdown

@fmigneault I completely understand the cause of this problem and I don't know not hacky solutions and PCManticore did not respond to my comment. That's why I didn't make pull request with this transform.

Copy link
Copy Markdown
Contributor

@PCManticore PCManticore left a comment

Choose a reason for hiding this comment

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

Overall the change looks good, but we need to drop the inference check from the predicate function as it can result in weird inference bugs.

if not isinstance(base, nodes.Call):
return False
try:
func = next(base.func.infer())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unfortunately we cannot use inference in predicate functions. Here you would need o unpack the arguments for checking if they refer to six.with_metaclass or not (by unpacking I mean either checking explicitly for Name or Attribute instances that match against six.with_metaclass)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should be good now. Let me know if it is still not what like you meant.

@PCManticore PCManticore merged commit 4629b93 into pylint-dev:master Feb 7, 2021
@PCManticore
Copy link
Copy Markdown
Contributor

Thanks for the PR @fmigneault !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect inference of ancestors of Enum class with six.with_metaclass

3 participants