Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/fixers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ version of ``six`` is installed.
.. seealso::
:func:`six.with_metaclass`

.. 2to3fixer:: nonzero

Changes all definitions of :func:`__nonzero__ <python2:__nonzero__>` to
:func:`__bool__ <python3:__bool__>`, adding an alias that maintains PY2
compatibility.

.. 2to3fixer:: raise_six

Changes ``raise E, V, T`` to ``six.reraise(E, V, T)``.
Expand Down
1 change: 1 addition & 0 deletions libmodernize/fixes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
'libmodernize.fixes.fix_int_long_tuple',
'libmodernize.fixes.fix_map',
'libmodernize.fixes.fix_metaclass',
'libmodernize.fixes.fix_nonzero',
'libmodernize.fixes.fix_raise_six',
'libmodernize.fixes.fix_unicode',
'libmodernize.fixes.fix_unicode_type',
Expand Down
72 changes: 72 additions & 0 deletions libmodernize/fixes/fix_nonzero.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""Fixer for `__nonzero__` -> `__bool__`, adding an alias for PY2 compatibility.

Based on Lib/lib2to3/fixes/fix_nonzero.py.

"""
# This is a derived work of Lib/lib2to3/fixes/fix_nonzero.py. That file
# is under the copyright of the Python Software Foundation and licensed
# under the Python Software Foundation License 2.
#
# Copyright notice:
#
# 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020
# Python Software Foundation; All Rights Reserved.

# Author: Collin Winter, James Bowman

from __future__ import absolute_import
from lib2to3 import fixer_base
from lib2to3.fixer_util import Assign, Dot, find_indentation, Leaf, Name, Newline, Node, syms
from lib2to3.pygram import token
import libmodernize


class FixNonzero(fixer_base.BaseFix):
BM_compatible = True

PATTERN = """
classdef< 'class' any+ ':'
suite< any*
funcdef< 'def' name='__nonzero__'
parameters< '(' NAME ')' > any+ >
any* > >
"""

def transform(self, node, results):
# Start by making the same transformation as lib2to3.fix_nonzero, purely
# renaming the method:

name = results["name"]
bool_funcdef = name.parent
new_name = Name("__bool__", prefix=name.prefix)
name.replace(new_name)

# Then, import six and add a conditional PY2-enabled function alias:

libmodernize.touch_import(None, u'six', node)

suite = bool_funcdef.parent
bool_funcdef_suite = bool_funcdef.children[-1]
old_dedent = bool_funcdef_suite.children[-1]
new_dedent = Leaf(token.DEDENT, '', prefix='\n' + find_indentation(bool_funcdef))
bool_funcdef_suite.children[-1].replace(new_dedent)

reassignment = Node(syms.suite, [
Newline(),
Leaf(token.INDENT,
find_indentation(bool_funcdef.children[-1].children[1])),
Copy link
Author

Choose a reason for hiding this comment

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

Replace bool_funcdef.children[-1] with bool_funcdef_suite.

Node(syms.simple_stmt,
[Assign(Name('__nonzero__'), Name('__bool__')),
Newline()]), old_dedent
])

if_stmt = Node(syms.if_stmt, [
Name('if'),
Node(syms.power,
[Name(' six'),
Node(syms.trailer, [Dot(), Name('PY2')])]),
Leaf(token.COLON, ':'), reassignment
])

suite.insert_child(suite.children.index(bool_funcdef) + 1, if_stmt)
111 changes: 111 additions & 0 deletions tests/test_fix_nonzero.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from __future__ import absolute_import

from utils import check_on_input


NONZERO_DEF = ("""\
class Foo:
def other(self):
return false

def __nonzero__(self):
return true

def another(self):
return false
""", """\
from __future__ import absolute_import
import six
class Foo:
def other(self):
return false

def __bool__(self):
return true

if six.PY2:
__nonzero__ = __bool__

def another(self):
return false
""")

NONZERO_DEF_2_SPACE_INDENT = ("""\
class Foo:
def other(self):
return false

def __nonzero__(self):
return true

def another(self):
return false
""", """\
from __future__ import absolute_import
import six
class Foo:
def other(self):
return false

def __bool__(self):
return true

if six.PY2:
__nonzero__ = __bool__

def another(self):
return false
""")

MULTI_CLASS_NONZERO_DEF = ("""\
class Foo:
def __nonzero__(self):
return true


class Bar:
pass
""", """\
from __future__ import absolute_import
import six
class Foo:
def __bool__(self):
return true

if six.PY2:
__nonzero__ = __bool__


class Bar:
pass
""")

INNER_CLASS_NONZERO_DEF = ("""\
class Baz:
class Foo:
def __nonzero__(self):
return true
""", """\
from __future__ import absolute_import
import six
class Baz:
class Foo:
def __bool__(self):
return true

if six.PY2:
__nonzero__ = __bool__
""")


def test_nonzero_def():
check_on_input(*NONZERO_DEF)

def test_nonzero_def_2_space_indent():
check_on_input(*NONZERO_DEF_2_SPACE_INDENT)

def test_multi_class_nonzero_def():
check_on_input(*MULTI_CLASS_NONZERO_DEF)

def test_inner_class_nonzero_def():
check_on_input(*INNER_CLASS_NONZERO_DEF)