view contrib/hgfixes/fix_bytes.py @ 20894:04e1596d5dbd

revset: improve _descendants performance Previously revset._descendants would iterate over the entire subset (which is often the entire repo) and test if each rev was in the descendants list. This is really slow on large repos (3+ seconds). Now we iterate over the descendants and test if they're in the subset. This affects advancing and retracting the phase boundary (3.5 seconds down to 0.8 seconds, which is even faster than it was in 2.9). Also affects commands that move the phase boundary (commit and rebase, presumably). The new revsetbenchmark indicates an improvement from 0.2 to 0.12 seconds. So future revset changes should be able to notice regressions. I removed a bad test. It was recently added and tested '1:: and reverse(all())', which has an amibiguous output direction. Previously it printed in reverse order, because we iterated over the subset (the reverse part). Now it prints in normal order because we iterate over the 1:: . Since the revset itself doesn't imply an order, I removed the test.
author Durham Goode <durham@fb.com>
date Tue, 25 Mar 2014 14:10:01 -0700
parents e51d4aedace9
children 48ef68004ec9
line wrap: on
line source

"""Fixer that changes plain strings to bytes strings."""

import re

from lib2to3 import fixer_base
from lib2to3.pgen2 import token
from lib2to3.fixer_util import Name
from lib2to3.pygram import python_symbols as syms

_re = re.compile(r'[rR]?[\'\"]')

# XXX: Implementing a blacklist in 2to3 turned out to be more troublesome than
# blacklisting some modules inside the fixers. So, this is what I came with.

blacklist = ['mercurial/demandimport.py',
             'mercurial/py3kcompat.py', # valid python 3 already
             'mercurial/i18n.py',
            ]

def isdocstring(node):
    def isclassorfunction(ancestor):
        symbols = (syms.funcdef, syms.classdef)
        # if the current node is a child of a function definition, a class
        # definition or a file, then it is a docstring
        if ancestor.type == syms.simple_stmt:
            try:
                while True:
                    if ancestor.type in symbols:
                        return True
                    ancestor = ancestor.parent
            except AttributeError:
                return False
        return False

    def ismodule(ancestor):
        # Our child is a docstring if we are a simple statement, and our
        # ancestor is file_input. In other words, our child is a lone string in
        # the source file.
        try:
            if (ancestor.type == syms.simple_stmt and
                ancestor.parent.type == syms.file_input):
                    return True
        except AttributeError:
            return False

    def isdocassignment(ancestor):
        # Assigning to __doc__, definitely a string
        try:
            while True:
                if (ancestor.type == syms.expr_stmt and
                    Name('__doc__') in ancestor.children):
                        return True
                ancestor = ancestor.parent
        except AttributeError:
            return False

    if ismodule(node.parent) or \
       isdocassignment(node.parent) or \
       isclassorfunction(node.parent):
        return True
    return False

def shouldtransform(node):
    specialnames = ['__main__']

    if node.value in specialnames:
        return False

    ggparent = node.parent.parent.parent
    sggparent = str(ggparent)

    if 'getattr' in sggparent or \
       'hasattr' in sggparent or \
       'setattr' in sggparent or \
       'encode' in sggparent or \
       'decode' in sggparent:
        return False

    return True

class FixBytes(fixer_base.BaseFix):

    PATTERN = 'STRING'

    def transform(self, node, results):
        if self.filename in blacklist:
            return
        if node.type == token.STRING:
            if _re.match(node.value):
                if isdocstring(node):
                    return
                if not shouldtransform(node):
                    return
                new = node.clone()
                new.value = 'b' + new.value
                return new