Add pylintrc and fix style for many scripts.

Fix a bug in scm.SVN.DiffItem()

Reduce variable aliasing, fix alignments, etc.

Review URL: http://codereview.chromium.org/2673002

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@48943 0039d316-1c4b-4281-b951-d872f2087c98
experimental/szager/collated-output
maruel@chromium.org 15 years ago
parent 524a6eda43
commit 6e29d572b5

@ -29,7 +29,7 @@ Example: %(app)s --merge 12345 --branch 187
Example: %(app)s --merge 12345 --local Example: %(app)s --merge 12345 --local
[Merge from branch to branch] [Merge from branch to branch]
--merge <revision> --sbranch <branch_num> --branch <branch_num> --merge <revision> --sbranch <branch_num> --branch <branch_num>
Example: %(app)s --merge 12345 --sbranch 248 --branch 249 Example: %(app)s --merge 12345 --sbranch 248 --branch 249
[Revert from trunk] [Revert from trunk]
@ -62,15 +62,15 @@ def deltree(root):
os.unlink(path) os.unlink(path)
os.rmdir(root) os.rmdir(root)
def clobberDir(dir): def clobberDir(dirname):
"""Removes a given directory""" """Removes a given directory"""
if (os.path.exists(dir)): if (os.path.exists(dirname)):
print dir + " directory found, deleting" print dir + " directory found, deleting"
# The following line was removed due to access controls in Windows # The following line was removed due to access controls in Windows
# which make os.unlink(path) calls impossible. # which make os.unlink(path) calls impossible.
#TODO(laforge) : Is this correct? #TODO(laforge) : Is this correct?
deltree(dir) deltree(dirname)
def runGcl(subcommand): def runGcl(subcommand):
gcl_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gcl") gcl_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gcl")
@ -96,7 +96,7 @@ def getSVNInfo(url, revision):
for line in svn_info: for line in svn_info:
match = re.search(r"(.*?):(.*)", line) match = re.search(r"(.*?):(.*)", line)
if match: if match:
info[match.group(1).strip()]=match.group(2).strip() info[match.group(1).strip()]=match.group(2).strip()
return info return info
@ -137,7 +137,7 @@ def inCheckoutRoot(path):
info = getSVNInfo(path, "HEAD") info = getSVNInfo(path, "HEAD")
if (not info.has_key("Repository Root")): if (not info.has_key("Repository Root")):
return False return False
repo_root = info["Repository Root"]; repo_root = info["Repository Root"]
info = getSVNInfo(os.path.dirname(os.path.abspath(path)), "HEAD") info = getSVNInfo(os.path.dirname(os.path.abspath(path)), "HEAD")
if (info.get("Repository Root", None) != repo_root): if (info.get("Repository Root", None) != repo_root):
return True return True
@ -146,9 +146,9 @@ def inCheckoutRoot(path):
def getRevisionLog(url, revision): def getRevisionLog(url, revision):
"""Takes an svn url and gets the associated revision.""" """Takes an svn url and gets the associated revision."""
command = 'svn log ' + url + " -r"+str(revision) command = 'svn log ' + url + " -r"+str(revision)
svn_log = subprocess.Popen(command, svn_log = subprocess.Popen(command,
shell=True, shell=True,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE).stdout.readlines() stderr=subprocess.PIPE).stdout.readlines()
# Don't include the header lines and the trailing "---..." line and eliminate # Don't include the header lines and the trailing "---..." line and eliminate
# any '\r's. # any '\r's.
@ -165,12 +165,12 @@ def getSVNVersionInfo():
for line in svn_info: for line in svn_info:
match = re.search(r"svn, version ((\d+)\.(\d+)\.(\d+)) \(r(\d+)\)", line) match = re.search(r"svn, version ((\d+)\.(\d+)\.(\d+)) \(r(\d+)\)", line)
if match: if match:
info['version']=match.group(1) info['version'] = match.group(1)
info['major']=int(match.group(2)) info['major'] = int(match.group(2))
info['minor']=int(match.group(3)) info['minor'] = int(match.group(3))
info['patch']=int(match.group(4)) info['patch'] = int(match.group(4))
info['revision']=match.group(5) info['revision'] = match.group(5)
return info return info
return None return None
@ -217,32 +217,32 @@ def checkoutRevision(url, revision, branch_url, revert=False):
# directories get clobbered and the merge step fails. # directories get clobbered and the merge step fails.
paths.sort() paths.sort()
# Checkout the directories that already exist # Checkout the directories that already exist
for path in paths: for path in paths:
if (export_map.has_key(path) and not revert): if (export_map.has_key(path) and not revert):
print "Exclude new directory " + path print "Exclude new directory " + path
continue continue
subpaths = path.split('/') subpaths = path.split('/')
subpaths.pop(0) subpaths.pop(0)
base = '' base = ''
for subpath in subpaths: for subpath in subpaths:
base += '/' + subpath base += '/' + subpath
# This logic ensures that you don't empty out any directories # This logic ensures that you don't empty out any directories
if not os.path.exists("." + base): if not os.path.exists("." + base):
command = ('svn update --depth empty ' + "." + base) command = ('svn update --depth empty ' + "." + base)
print command print command
os.system(command) os.system(command)
if (revert): if (revert):
files = getAllFilesInRevision(files_info) files = getAllFilesInRevision(files_info)
else: else:
files = getExistingFilesInRevision(files_info) files = getExistingFilesInRevision(files_info)
for file in files: for f in files:
# Prevent the tool from clobbering the src directory # Prevent the tool from clobbering the src directory
if (file == ""): if (f == ""):
continue continue
command = ('svn up ".' + file + '"') command = ('svn up ".' + f + '"')
print command print command
os.system(command) os.system(command)
@ -272,7 +272,7 @@ def exportRevision(url, revision):
os.system(command) os.system(command)
command = 'svn add .' + path command = 'svn add .' + path
print command print command
os.system(command) os.system(command)
def deleteRevision(url, revision): def deleteRevision(url, revision):
@ -298,7 +298,7 @@ def revertExportRevision(url, revision):
def revertRevision(url, revision): def revertRevision(url, revision):
paths = getBestMergePaths(url, revision) paths = getBestMergePaths(url, revision)
for path in paths: for path in paths:
command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) + command = ('svn merge -N -r ' + str(revision) + ":" + str(revision-1) +
" " + url + path + " ." + path) " " + url + path + " ." + path)
print command print command
os.system(command) os.system(command)
@ -336,12 +336,7 @@ def getBestMergePaths(url, revision):
def getBestMergePaths2(files_info, revision): def getBestMergePaths2(files_info, revision):
"""Takes an svn url and gets the associated revision.""" """Takes an svn url and gets the associated revision."""
return list(set([f[2] for f in files_info]))
map = dict()
for file_info in files_info:
map[file_info[2]] = file_info[2]
return map.keys()
def getBestExportPathsMap(url, revision): def getBestExportPathsMap(url, revision):
return getBestExportPathsMap2(getFileInfo(url, revision), revision) return getBestExportPathsMap2(getFileInfo(url, revision), revision)
@ -353,15 +348,15 @@ def getBestExportPathsMap2(files_info, revision):
if export_map_: if export_map_:
return export_map_ return export_map_
map = dict() result = {}
for file_info in files_info: for file_info in files_info:
if (file_info[0] == "A"): if (file_info[0] == "A"):
if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1], if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1],
revision)): revision)):
map[file_info[2] + "/" + file_info[3]] = "" result[file_info[2] + "/" + file_info[3]] = ""
export_map_ = map export_map_ = result
return map return result
def getBestDeletePathsMap(url, revision): def getBestDeletePathsMap(url, revision):
return getBestDeletePathsMap2(getFileInfo(url, revision), revision) return getBestDeletePathsMap2(getFileInfo(url, revision), revision)
@ -373,28 +368,25 @@ def getBestDeletePathsMap2(files_info, revision):
if delete_map_: if delete_map_:
return delete_map_ return delete_map_
map = dict() result = {}
for file_info in files_info: for file_info in files_info:
if (file_info[0] == "D"): if (file_info[0] == "D"):
if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1], if(isSVNDirectory("svn://svn.chromium.org/chrome/" + file_info[1],
revision)): revision)):
map[file_info[2] + "/" + file_info[3]] = "" result[file_info[2] + "/" + file_info[3]] = ""
delete_map_ = result
return result
delete_map_ = map
return map
def getExistingFilesInRevision(files_info): def getExistingFilesInRevision(files_info):
"""Checks for existing files in the revision. """Checks for existing files in the revision.
Anything that's A will require special treatment (either a merge or an Anything that's A will require special treatment (either a merge or an
export + add) export + add)
""" """
map = [] return ['%s/%s' % (f[2], f[3]) for f in files_info if f[0] != 'A']
for file_info in files_info:
if file_info[0] != "A":
map.append(file_info[2] + "/" + file_info[3])
return map
def getAllFilesInRevision(files_info): def getAllFilesInRevision(files_info):
"""Checks for existing files in the revision. """Checks for existing files in the revision.
@ -402,23 +394,17 @@ def getAllFilesInRevision(files_info):
Anything that's A will require special treatment (either a merge or an Anything that's A will require special treatment (either a merge or an
export + add) export + add)
""" """
map = [] return ['%s/%s' % (f[2], f[3]) for f in files_info]
for file_info in files_info:
map.append(file_info[2] + "/" + file_info[3])
return map
def prompt(question): def prompt(question):
answer = None while True:
while not answer:
print question + " [y|n]:", print question + " [y|n]:",
answer = sys.stdin.readline() answer = sys.stdin.readline()
if answer.lower().startswith('n'): if answer.lower().startswith('n'):
return False return False
elif answer.lower().startswith('y'): elif answer.lower().startswith('y'):
return True return True
else:
answer = None
def text_prompt(question, default): def text_prompt(question, default):
print question + " [" + default + "]:" print question + " [" + default + "]:"
@ -427,7 +413,8 @@ def text_prompt(question, default):
return default return default
return answer return answer
def main(options, args):
def drover(options, args):
revision = options.revert or options.merge revision = options.revert or options.merge
# Initialize some variables used below. They can be overwritten by # Initialize some variables used below. They can be overwritten by
@ -442,18 +429,18 @@ def main(options, args):
if options.branch: if options.branch:
DEFAULT_WORKING += ("_" + options.branch) DEFAULT_WORKING += ("_" + options.branch)
if not isMinimumSVNVersion(1,5): if not isMinimumSVNVersion(1, 5):
print "You need to use at least SVN version 1.5.x" print "You need to use at least SVN version 1.5.x"
sys.exit(1) return 1
# Override the default properties if there is a drover.properties file. # Override the default properties if there is a drover.properties file.
global file_pattern_ global file_pattern_
if os.path.exists("drover.properties"): if os.path.exists("drover.properties"):
file = open("drover.properties") f = open("drover.properties")
exec(file) exec(f)
file.close() f.close()
if FILE_PATTERN: if FILE_PATTERN:
file_pattern_ = FILE_PATTERN file_pattern_ = FILE_PATTERN
if options.revert and options.branch: if options.revert and options.branch:
url = BRANCH_URL.replace("$branch", options.branch) url = BRANCH_URL.replace("$branch", options.branch)
@ -468,21 +455,21 @@ def main(options, args):
working = os.getcwd() working = os.getcwd()
if not inCheckoutRoot(working): if not inCheckoutRoot(working):
print "'%s' appears not to be the root of a working copy" % working print "'%s' appears not to be the root of a working copy" % working
sys.exit(1) return 1
if isSVNDirty(): if isSVNDirty():
print "Working copy contains uncommitted files" print "Working copy contains uncommitted files"
sys.exit(1) return 1
command = 'svn log ' + url + " -r "+str(revision) + " -v" command = 'svn log ' + url + " -r "+str(revision) + " -v"
os.system(command) os.system(command)
if not (options.revertbot or prompt("Is this the correct revision?")): if not (options.revertbot or prompt("Is this the correct revision?")):
sys.exit(0) return 0
if (os.path.exists(working)) and not options.local: if (os.path.exists(working)) and not options.local:
if not (options.revertbot or SKIP_CHECK_WORKING or if not (options.revertbot or SKIP_CHECK_WORKING or
prompt("Working directory: '%s' already exists, clobber?" % working)): prompt("Working directory: '%s' already exists, clobber?" % working)):
sys.exit(0) return 0
deltree(working) deltree(working)
if not options.local: if not options.local:
@ -533,7 +520,7 @@ def main(options, args):
os.unlink(filename) os.unlink(filename)
if options.local: if options.local:
sys.exit(0) return 0
print author print author
print revision print revision
@ -552,18 +539,19 @@ def main(options, args):
print "Deleting the changelist." print "Deleting the changelist."
print "gcl delete " + str(revision) print "gcl delete " + str(revision)
runGcl("delete " + str(revision)) runGcl("delete " + str(revision))
sys.exit(0) return 0
# We commit if the reverbot is set to commit automatically, or if this is # We commit if the reverbot is set to commit automatically, or if this is
# not the revertbot and the user agrees. # not the revertbot and the user agrees.
if options.revertbot_commit or (not options.revertbot and if options.revertbot_commit or (not options.revertbot and
prompt("Would you like to commit?")): prompt("Would you like to commit?")):
print "gcl commit " + str(revision) + " --no_presubmit --force" print "gcl commit " + str(revision) + " --no_presubmit --force"
runGcl("commit " + str(revision) + " --no_presubmit --force") return runGcl("commit " + str(revision) + " --no_presubmit --force")
else: else:
sys.exit(0) return 0
if __name__ == "__main__":
def main():
option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]}) option_parser = optparse.OptionParser(usage=USAGE % {"app": sys.argv[0]})
option_parser.add_option('-m', '--merge', type="int", option_parser.add_option('-m', '--merge', type="int",
help='Revision to merge from trunk to branch') help='Revision to merge from trunk to branch')
@ -578,7 +566,7 @@ if __name__ == "__main__":
option_parser.add_option('-w', '--workdir', option_parser.add_option('-w', '--workdir',
help='subdir to use for the revert') help='subdir to use for the revert')
option_parser.add_option('-a', '--auditor', option_parser.add_option('-a', '--auditor',
help='overrides the author for reviewer') help='overrides the author for reviewer')
option_parser.add_option('', '--revertbot', action='store_true', option_parser.add_option('', '--revertbot', action='store_true',
default=False) default=False)
option_parser.add_option('', '--revertbot-commit', action='store_true', option_parser.add_option('', '--revertbot-commit', action='store_true',
@ -588,14 +576,18 @@ if __name__ == "__main__":
if not options.merge and not options.revert: if not options.merge and not options.revert:
option_parser.error("You need at least --merge or --revert") option_parser.error("You need at least --merge or --revert")
sys.exit(1) return 1
if options.merge and not options.branch and not options.local: if options.merge and not options.branch and not options.local:
option_parser.error("--merge requires either --branch or --local") option_parser.error("--merge requires either --branch or --local")
sys.exit(1) return 1
if options.local and (options.revert or options.branch): if options.local and (options.revert or options.branch):
option_parser.error("--local cannot be used with --revert or --branch") option_parser.error("--local cannot be used with --revert or --branch")
sys.exit(1) return 1
return drover(options, args)
sys.exit(main(options, args))
if __name__ == "__main__":
sys.exit(main())

@ -12,7 +12,6 @@ import getpass
import os import os
import random import random
import re import re
import shutil
import string import string
import subprocess import subprocess
import sys import sys
@ -127,7 +126,6 @@ def GetCachedFile(filename, max_age=60*60*24*3, use_root=False):
Note: The cache will be inconsistent if the same file is retrieved with both Note: The cache will be inconsistent if the same file is retrieved with both
use_root=True and use_root=False. Don't be stupid. use_root=True and use_root=False. Don't be stupid.
""" """
global FILES_CACHE
if filename not in FILES_CACHE: if filename not in FILES_CACHE:
# Don't try to look up twice. # Don't try to look up twice.
FILES_CACHE[filename] = None FILES_CACHE[filename] = None
@ -173,7 +171,6 @@ def GetCodeReviewSetting(key):
"""Returns a value for the given key for this repository.""" """Returns a value for the given key for this repository."""
# Use '__just_initialized' as a flag to determine if the settings were # Use '__just_initialized' as a flag to determine if the settings were
# already initialized. # already initialized.
global CODEREVIEW_SETTINGS
if '__just_initialized' not in CODEREVIEW_SETTINGS: if '__just_initialized' not in CODEREVIEW_SETTINGS:
settings_file = GetCachedFile(CODEREVIEW_SETTINGS_FILE) settings_file = GetCachedFile(CODEREVIEW_SETTINGS_FILE)
if settings_file: if settings_file:
@ -190,10 +187,10 @@ def Warn(msg):
ErrorExit(msg, exit=False) ErrorExit(msg, exit=False)
def ErrorExit(msg, exit=True): def ErrorExit(msg, do_exit=True):
"""Print an error message to stderr and optionally exit.""" """Print an error message to stderr and optionally exit."""
print >>sys.stderr, msg print >> sys.stderr, msg
if exit: if do_exit:
sys.exit(1) sys.exit(1)
@ -776,8 +773,8 @@ def CMDupload(change_info, args):
cc_list = GetCodeReviewSetting("CC_LIST") cc_list = GetCodeReviewSetting("CC_LIST")
if not no_watchlists and watchers: if not no_watchlists and watchers:
# Filter out all empty elements and join by ',' # Filter out all empty elements and join by ','
cc_list = ','.join(filter(None, [cc_list] + watchers)) cc_list = ','.join(filter(None, [cc_list] + watchers))
if cc_list: if cc_list:
upload_arg.append("--cc=" + cc_list) upload_arg.append("--cc=" + cc_list)
upload_arg.append("--description_file=" + desc_file + "") upload_arg.append("--description_file=" + desc_file + "")
@ -1178,7 +1175,7 @@ def CMDdiff(args):
def CMDsettings(): def CMDsettings():
"""Prints code review settings for this checkout.""" """Prints code review settings for this checkout."""
# Force load settings # Force load settings
GetCodeReviewSetting("UNKNOWN"); GetCodeReviewSetting("UNKNOWN")
del CODEREVIEW_SETTINGS['__just_initialized'] del CODEREVIEW_SETTINGS['__just_initialized']
print '\n'.join(("%s: %s" % (str(k), str(v)) print '\n'.join(("%s: %s" % (str(k), str(v))
for (k,v) in CODEREVIEW_SETTINGS.iteritems())) for (k,v) in CODEREVIEW_SETTINGS.iteritems()))

@ -1063,7 +1063,7 @@ def Command(name):
def CMDhelp(parser, args): def CMDhelp(parser, args):
"""Prints list of commands or help for a specific command.""" """Prints list of commands or help for a specific command."""
(options, args) = parser.parse_args(args) (_, args) = parser.parse_args(args)
if len(args) == 1: if len(args) == 1:
return Main(args + ['--help']) return Main(args + ['--help'])
parser.print_help() parser.print_help()
@ -1085,51 +1085,52 @@ def GenUsage(parser, command):
def Main(argv): def Main(argv):
"""Doesn't parse the arguments here, just find the right subcommand to """Doesn't parse the arguments here, just find the right subcommand to
execute.""" execute."""
# Do it late so all commands are listed.
CMDhelp.usage = ('\n\nCommands are:\n' + '\n'.join([
' %-10s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip())
for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')]))
parser = optparse.OptionParser(version='%prog ' + __version__)
parser.add_option("-v", "--verbose", action="count", default=0,
help="Produces additional output for diagnostics. Can be "
"used up to three times for more logging info.")
parser.add_option("--gclientfile", metavar="FILENAME", dest="config_filename",
default=os.environ.get("GCLIENT_FILE", ".gclient"),
help="Specify an alternate .gclient file")
# Integrate standard options processing.
old_parser = parser.parse_args
def Parse(args):
(options, args) = old_parser(args)
if options.verbose == 2:
logging.basicConfig(level=logging.INFO)
elif options.verbose > 2:
logging.basicConfig(level=logging.DEBUG)
options.entries_filename = options.config_filename + "_entries"
if not hasattr(options, 'revisions'):
# GClient.RunOnDeps expects it even if not applicable.
options.revisions = []
if not hasattr(options, 'head'):
options.head = None
return (options, args)
parser.parse_args = Parse
# We don't want wordwrapping in epilog (usually examples)
parser.format_epilog = lambda _: parser.epilog or ''
if argv:
command = Command(argv[0])
if command:
# "fix" the usage and the description now that we know the subcommand.
GenUsage(parser, argv[0])
return command(parser, argv[1:])
# Not a known command. Default to help.
GenUsage(parser, 'help')
return CMDhelp(parser, argv)
if "__main__" == __name__:
try: try:
sys.exit(Main(sys.argv[1:])) # Do it late so all commands are listed.
CMDhelp.usage = ('\n\nCommands are:\n' + '\n'.join([
' %-10s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip())
for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')]))
parser = optparse.OptionParser(version='%prog ' + __version__)
parser.add_option("-v", "--verbose", action="count", default=0,
help="Produces additional output for diagnostics. Can be "
"used up to three times for more logging info.")
parser.add_option("--gclientfile", metavar="FILENAME",
dest="config_filename",
default=os.environ.get("GCLIENT_FILE", ".gclient"),
help="Specify an alternate .gclient file")
# Integrate standard options processing.
old_parser = parser.parse_args
def Parse(args):
(options, args) = old_parser(args)
if options.verbose == 2:
logging.basicConfig(level=logging.INFO)
elif options.verbose > 2:
logging.basicConfig(level=logging.DEBUG)
options.entries_filename = options.config_filename + "_entries"
if not hasattr(options, 'revisions'):
# GClient.RunOnDeps expects it even if not applicable.
options.revisions = []
if not hasattr(options, 'head'):
options.head = None
return (options, args)
parser.parse_args = Parse
# We don't want wordwrapping in epilog (usually examples)
parser.format_epilog = lambda _: parser.epilog or ''
if argv:
command = Command(argv[0])
if command:
# "fix" the usage and the description now that we know the subcommand.
GenUsage(parser, argv[0])
return command(parser, argv[1:])
# Not a known command. Default to help.
GenUsage(parser, 'help')
return CMDhelp(parser, argv)
except gclient_utils.Error, e: except gclient_utils.Error, e:
print >> sys.stderr, "Error: %s" % str(e) print >> sys.stderr, "Error: %s" % str(e)
sys.exit(1) return 1
if "__main__" == __name__:
sys.exit(Main(sys.argv[1:]))
# vim: ts=2:sw=2:tw=80:et: # vim: ts=2:sw=2:tw=80:et:

@ -30,11 +30,11 @@ class DiffFilterer(object):
self._current_file = "" self._current_file = ""
self._replacement_file = "" self._replacement_file = ""
def SetCurrentFile(self, file): def SetCurrentFile(self, current_file):
self._current_file = file self._current_file = current_file
# Note that we always use '/' as the path separator to be # Note that we always use '/' as the path separator to be
# consistent with svn's cygwin-style output on Windows # consistent with svn's cygwin-style output on Windows
self._replacement_file = posixpath.join(self._relpath, file) self._replacement_file = posixpath.join(self._relpath, current_file)
def ReplaceAndPrint(self, line): def ReplaceAndPrint(self, line):
print(line.replace(self._current_file, self._replacement_file)) print(line.replace(self._current_file, self._replacement_file))
@ -114,15 +114,14 @@ class SCMWrapper(object):
class GitWrapper(SCMWrapper): class GitWrapper(SCMWrapper):
"""Wrapper for Git""" """Wrapper for Git"""
def cleanup(self, options, args, file_list): @staticmethod
def cleanup(options, args, file_list):
"""'Cleanup' the repo. """'Cleanup' the repo.
There's no real git equivalent for the svn cleanup command, do a no-op. There's no real git equivalent for the svn cleanup command, do a no-op.
""" """
__pychecker__ = 'unusednames=options,args,file_list'
def diff(self, options, args, file_list): def diff(self, options, args, file_list):
__pychecker__ = 'unusednames=options,args,file_list'
merge_base = self._Run(['merge-base', 'HEAD', 'origin']) merge_base = self._Run(['merge-base', 'HEAD', 'origin'])
self._Run(['diff', merge_base], redirect_stdout=False) self._Run(['diff', merge_base], redirect_stdout=False)
@ -132,7 +131,6 @@ class GitWrapper(SCMWrapper):
Exports into the specified directory, creating the path if it does Exports into the specified directory, creating the path if it does
already exist. already exist.
""" """
__pychecker__ = 'unusednames=options,file_list'
assert len(args) == 1 assert len(args) == 1
export_path = os.path.abspath(os.path.join(args[0], self.relpath)) export_path = os.path.abspath(os.path.join(args[0], self.relpath))
if not os.path.exists(export_path): if not os.path.exists(export_path):
@ -147,7 +145,6 @@ class GitWrapper(SCMWrapper):
The patch file is generated from a diff of the merge base of HEAD and The patch file is generated from a diff of the merge base of HEAD and
its upstream branch. its upstream branch.
""" """
__pychecker__ = 'unusednames=options,args,file_list'
path = os.path.join(self._root_dir, self.relpath) path = os.path.join(self._root_dir, self.relpath)
merge_base = self._Run(['merge-base', 'HEAD', 'origin']) merge_base = self._Run(['merge-base', 'HEAD', 'origin'])
command = ['diff', merge_base] command = ['diff', merge_base]
@ -403,7 +400,6 @@ class GitWrapper(SCMWrapper):
All reverted files will be appended to file_list. All reverted files will be appended to file_list.
""" """
__pychecker__ = 'unusednames=args'
path = os.path.join(self._root_dir, self.relpath) path = os.path.join(self._root_dir, self.relpath)
if not os.path.isdir(path): if not os.path.isdir(path):
# revert won't work if the directory doesn't exist. It needs to # revert won't work if the directory doesn't exist. It needs to
@ -413,7 +409,7 @@ class GitWrapper(SCMWrapper):
return self.update(options, [], file_list) return self.update(options, [], file_list)
default_rev = "refs/heads/master" default_rev = "refs/heads/master"
url, deps_revision = gclient_utils.SplitUrlRevision(self.url) _, deps_revision = gclient_utils.SplitUrlRevision(self.url)
if not deps_revision: if not deps_revision:
deps_revision = default_rev deps_revision = default_rev
if deps_revision.startswith('refs/heads/'): if deps_revision.startswith('refs/heads/'):
@ -425,7 +421,6 @@ class GitWrapper(SCMWrapper):
def revinfo(self, options, args, file_list): def revinfo(self, options, args, file_list):
"""Display revision""" """Display revision"""
__pychecker__ = 'unusednames=options,args,file_list'
return self._Run(['rev-parse', 'HEAD']) return self._Run(['rev-parse', 'HEAD'])
def runhooks(self, options, args, file_list): def runhooks(self, options, args, file_list):
@ -433,7 +428,6 @@ class GitWrapper(SCMWrapper):
def status(self, options, args, file_list): def status(self, options, args, file_list):
"""Display status information.""" """Display status information."""
__pychecker__ = 'unusednames=options,args'
if not os.path.isdir(self.checkout_path): if not os.path.isdir(self.checkout_path):
print('\n________ couldn\'t run status in %s:\nThe directory ' print('\n________ couldn\'t run status in %s:\nThe directory '
'does not exist.' % self.checkout_path) 'does not exist.' % self.checkout_path)
@ -574,7 +568,8 @@ class GitWrapper(SCMWrapper):
# whitespace between projects when syncing. # whitespace between projects when syncing.
print "" print ""
def _CheckMinVersion(self, min_version): @staticmethod
def _CheckMinVersion(min_version):
(ok, current_version) = scm.GIT.AssertVersion(min_version) (ok, current_version) = scm.GIT.AssertVersion(min_version)
if not ok: if not ok:
raise gclient_utils.Error('git version %s < minimum required %s' % raise gclient_utils.Error('git version %s < minimum required %s' %
@ -594,31 +589,31 @@ class GitWrapper(SCMWrapper):
try: try:
scm.GIT.Capture(['update-index', '--ignore-submodules', '--refresh'], scm.GIT.Capture(['update-index', '--ignore-submodules', '--refresh'],
self.checkout_path, print_error=False) self.checkout_path, print_error=False)
except gclient_utils.CheckCallError, e: except gclient_utils.CheckCallError:
raise gclient_utils.Error('\n____ %s%s\n' raise gclient_utils.Error('\n____ %s%s\n'
'\tYou have unstaged changes.\n' '\tYou have unstaged changes.\n'
'\tPlease commit, stash, or reset.\n' '\tPlease commit, stash, or reset.\n'
% (self.relpath, rev_str)) % (self.relpath, rev_str))
try: try:
scm.GIT.Capture(['diff-index', '--cached', '--name-status', '-r', scm.GIT.Capture(['diff-index', '--cached', '--name-status', '-r',
'--ignore-submodules', 'HEAD', '--'], self.checkout_path, '--ignore-submodules', 'HEAD', '--'], self.checkout_path,
print_error=False) print_error=False)
except gclient_utils.CheckCallError, e: except gclient_utils.CheckCallError:
raise gclient_utils.Error('\n____ %s%s\n' raise gclient_utils.Error('\n____ %s%s\n'
'\tYour index contains uncommitted changes\n' '\tYour index contains uncommitted changes\n'
'\tPlease commit, stash, or reset.\n' '\tPlease commit, stash, or reset.\n'
% (self.relpath, rev_str)) % (self.relpath, rev_str))
def _CheckDetachedHead(self, rev_str): def _CheckDetachedHead(self, rev_str):
# HEAD is detached. Make sure it is safe to move away from (i.e., it is # HEAD is detached. Make sure it is safe to move away from (i.e., it is
# reference by a commit). If not, error out -- most likely a rebase is # reference by a commit). If not, error out -- most likely a rebase is
# in progress, try to detect so we can give a better error. # in progress, try to detect so we can give a better error.
try: try:
out, err = scm.GIT.Capture( _, _ = scm.GIT.Capture(
['name-rev', '--no-undefined', 'HEAD'], ['name-rev', '--no-undefined', 'HEAD'],
self.checkout_path, self.checkout_path,
print_error=False) print_error=False)
except gclient_utils.CheckCallError, e: except gclient_utils.CheckCallError:
# Commit is not contained by any rev. See if the user is rebasing: # Commit is not contained by any rev. See if the user is rebasing:
if self._IsRebasing(): if self._IsRebasing():
# Punt to the user # Punt to the user
@ -670,21 +665,18 @@ class SVNWrapper(SCMWrapper):
def cleanup(self, options, args, file_list): def cleanup(self, options, args, file_list):
"""Cleanup working copy.""" """Cleanup working copy."""
__pychecker__ = 'unusednames=file_list,options'
command = ['cleanup'] command = ['cleanup']
command.extend(args) command.extend(args)
scm.SVN.Run(command, os.path.join(self._root_dir, self.relpath)) scm.SVN.Run(command, os.path.join(self._root_dir, self.relpath))
def diff(self, options, args, file_list): def diff(self, options, args, file_list):
# NOTE: This function does not currently modify file_list. # NOTE: This function does not currently modify file_list.
__pychecker__ = 'unusednames=file_list,options'
command = ['diff'] command = ['diff']
command.extend(args) command.extend(args)
scm.SVN.Run(command, os.path.join(self._root_dir, self.relpath)) scm.SVN.Run(command, os.path.join(self._root_dir, self.relpath))
def export(self, options, args, file_list): def export(self, options, args, file_list):
"""Export a clean directory tree into the given path.""" """Export a clean directory tree into the given path."""
__pychecker__ = 'unusednames=file_list,options'
assert len(args) == 1 assert len(args) == 1
export_path = os.path.abspath(os.path.join(args[0], self.relpath)) export_path = os.path.abspath(os.path.join(args[0], self.relpath))
try: try:
@ -699,7 +691,6 @@ class SVNWrapper(SCMWrapper):
def pack(self, options, args, file_list): def pack(self, options, args, file_list):
"""Generates a patch file which can be applied to the root of the """Generates a patch file which can be applied to the root of the
repository.""" repository."""
__pychecker__ = 'unusednames=file_list,options'
path = os.path.join(self._root_dir, self.relpath) path = os.path.join(self._root_dir, self.relpath)
command = ['diff'] command = ['diff']
command.extend(args) command.extend(args)
@ -847,7 +838,6 @@ class SVNWrapper(SCMWrapper):
All reverted files will be appended to file_list, even if Subversion All reverted files will be appended to file_list, even if Subversion
doesn't know about them. doesn't know about them.
""" """
__pychecker__ = 'unusednames=args'
path = os.path.join(self._root_dir, self.relpath) path = os.path.join(self._root_dir, self.relpath)
if not os.path.isdir(path): if not os.path.isdir(path):
# svn revert won't work if the directory doesn't exist. It needs to # svn revert won't work if the directory doesn't exist. It needs to
@ -900,7 +890,6 @@ class SVNWrapper(SCMWrapper):
def revinfo(self, options, args, file_list): def revinfo(self, options, args, file_list):
"""Display revision""" """Display revision"""
__pychecker__ = 'unusednames=args,file_list,options'
return scm.SVN.CaptureBaseRevision(self.checkout_path) return scm.SVN.CaptureBaseRevision(self.checkout_path)
def runhooks(self, options, args, file_list): def runhooks(self, options, args, file_list):
@ -924,7 +913,8 @@ class SVNWrapper(SCMWrapper):
# Find the forth '/' and strip from there. A bit hackish. # Find the forth '/' and strip from there. A bit hackish.
return '/'.join(self.url.split('/')[:4]) + url return '/'.join(self.url.split('/')[:4]) + url
def AddAdditionalFlags(self, command, options, revision): @staticmethod
def AddAdditionalFlags(command, options, revision):
"""Add additional flags to command depending on what options are set. """Add additional flags to command depending on what options are set.
command should be a list of strings that represents an svn command. command should be a list of strings that represents an svn command.

@ -228,7 +228,7 @@ def SubprocessCallAndFilter(command,
in_directory, in_directory,
print_messages, print_messages,
print_stdout, print_stdout,
fail_status=None, filter=None): fail_status=None, filter_fn=None):
"""Runs command, a list, in directory in_directory. """Runs command, a list, in directory in_directory.
If print_messages is true, a message indicating what is being done If print_messages is true, a message indicating what is being done
@ -240,7 +240,7 @@ def SubprocessCallAndFilter(command,
Also, if print_stdout is true, the command's stdout is also forwarded Also, if print_stdout is true, the command's stdout is also forwarded
to stdout. to stdout.
If a filter function is specified, it is expected to take a single If a filter_fn function is specified, it is expected to take a single
string argument, and it will be called with each line of the string argument, and it will be called with each line of the
subprocess's output. Each line has had the trailing newline character subprocess's output. Each line has had the trailing newline character
trimmed. trimmed.
@ -277,8 +277,8 @@ def SubprocessCallAndFilter(command,
sys.stdout.write(in_byte) sys.stdout.write(in_byte)
if in_byte != "\n": if in_byte != "\n":
in_line += in_byte in_line += in_byte
if in_byte == "\n" and filter: if in_byte == "\n" and filter_fn:
filter(in_line) filter_fn(in_line)
in_line = "" in_line = ""
in_byte = kid.stdout.read(1) in_byte = kid.stdout.read(1)
rv = kid.wait() rv = kid.wait()

@ -0,0 +1,253 @@
[MASTER]
# Specify a configuration file.
#rcfile=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Profiled execution.
profile=no
# Add <file or directory> to the black list. It should be a base name, not a
# path. You may set this option multiple times.
ignore=CVS
# Pickle collected data for later comparisons.
persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
[MESSAGES CONTROL]
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifier separated by comma (,) or put this option
# multiple time.
# C0103: Invalid name ""
# C0111: Missing docstring
# C0302: Too many lines in module (N)
# R0902: Too many instance attributes (N/7)
# R0903: Too few public methods (N/2)
# R0911: Too many return statements (N/6)
# R0912: Too many branches (N/12)
# R0913: Too many arguments (N/5)
# R0914: Too many local variables (N/15)
# R0915: Too many statements (N/50)
# W0122: Use of the exec statement
# W0141: Used builtin function ''
# W0603: Using the global statement
# W0613: Unused argument ''
# W6501: Specify string format arguments as logging function parameters
disable=C0103,C0111,C0302,R0902,R0903,R0911,R0912,R0913,R0914,R0915,W0122,W0141,W0603,W0613,W6501
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html
output-format=text
# Include message's id in output
include-ids=yes
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
# CHANGE: No report.
reports=no
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (R0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Add a comment according to your evaluation note. This is used by the global
# evaluation report (R0004).
comment=no
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=80
# Maximum number of lines in a module
max-module-lines=1000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
# CHANGE: Use " " instead.
indent-string=' '
[TYPECHECK]
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set).
ignored-classes=SQLObject
# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
zope=no
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed.
generated-members=REQUEST,acl_users,aq_parent
[BASIC]
# Required attributes for module, separated by a comma
required-attributes=
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,apply,input
# Regular expression which should only match correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression which should only match correct module level names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression which should only match correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Regular expression which should only match functions or classes name which do
# not require a docstring
no-docstring-rgx=__.*__
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# A regular expression matching names used for dummy variables (i.e. not used).
dummy-variables-rgx=_|dummy
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=4
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
[CLASSES]
# List of interface methods to ignore, separated by a comma. This is used for
# instance to not check methods defines in Zope's Interface base class.
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of branch for function / method body
max-branchs=12
# Maximum number of statements in function / method body
max-statements=50
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report R0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report R0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report R0402 must
# not be disabled)
int-import-graph=

@ -18,8 +18,8 @@ import xml.dom.minidom
import gclient_utils import gclient_utils
def ValidateEmail(email): def ValidateEmail(email):
return (re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email) return (re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email)
is not None) is not None)
def GetCasedPath(path): def GetCasedPath(path):
@ -121,10 +121,10 @@ class GIT(object):
in_directory, in_directory,
print_messages, print_messages,
print_stdout, print_stdout,
filter): filter_fn):
"""Runs a command, optionally outputting to stdout. """Runs a command, optionally outputting to stdout.
stdout is passed line-by-line to the given filter function. If stdout is passed line-by-line to the given filter_fn function. If
print_stdout is true, it is also printed to sys.stdout as in Run. print_stdout is true, it is also printed to sys.stdout as in Run.
Args: Args:
@ -133,7 +133,7 @@ class GIT(object):
print_messages: Whether to print status messages to stdout about print_messages: Whether to print status messages to stdout about
which commands are being run. which commands are being run.
print_stdout: Whether to forward program's output to stdout. print_stdout: Whether to forward program's output to stdout.
filter: A function taking one argument (a string) which will be filter_fn: A function taking one argument (a string) which will be
passed each line (with the ending newline character removed) of passed each line (with the ending newline character removed) of
program's output for filtering. program's output for filtering.
@ -146,7 +146,7 @@ class GIT(object):
in_directory, in_directory,
print_messages, print_messages,
print_stdout, print_stdout,
filter=filter) filter_fn=filter_fn)
@staticmethod @staticmethod
def GetEmail(repo_root): def GetEmail(repo_root):
@ -464,10 +464,10 @@ class SVN(object):
in_directory, in_directory,
print_messages, print_messages,
print_stdout, print_stdout,
filter): filter_fn):
"""Runs a command, optionally outputting to stdout. """Runs a command, optionally outputting to stdout.
stdout is passed line-by-line to the given filter function. If stdout is passed line-by-line to the given filter_fn function. If
print_stdout is true, it is also printed to sys.stdout as in Run. print_stdout is true, it is also printed to sys.stdout as in Run.
Args: Args:
@ -476,7 +476,7 @@ class SVN(object):
print_messages: Whether to print status messages to stdout about print_messages: Whether to print status messages to stdout about
which commands are being run. which commands are being run.
print_stdout: Whether to forward program's output to stdout. print_stdout: Whether to forward program's output to stdout.
filter: A function taking one argument (a string) which will be filter_fn: A function taking one argument (a string) which will be
passed each line (with the ending newline character removed) of passed each line (with the ending newline character removed) of
program's output for filtering. program's output for filtering.
@ -489,7 +489,7 @@ class SVN(object):
in_directory, in_directory,
print_messages, print_messages,
print_stdout, print_stdout,
filter=filter) filter_fn=filter_fn)
@staticmethod @staticmethod
def CaptureInfo(relpath, in_directory=None, print_error=True): def CaptureInfo(relpath, in_directory=None, print_error=True):
@ -507,7 +507,8 @@ class SVN(object):
GetNamedNodeText = gclient_utils.GetNamedNodeText GetNamedNodeText = gclient_utils.GetNamedNodeText
GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText GetNodeNamedAttributeText = gclient_utils.GetNodeNamedAttributeText
def C(item, f): def C(item, f):
if item is not None: return f(item) if item is not None:
return f(item)
# /info/entry/ # /info/entry/
# url # url
# reposityory/(root|uuid) # reposityory/(root|uuid)
@ -516,7 +517,6 @@ class SVN(object):
# str() the results because they may be returned as Unicode, which # str() the results because they may be returned as Unicode, which
# interferes with the higher layers matching up things in the deps # interferes with the higher layers matching up things in the deps
# dictionary. # dictionary.
# TODO(maruel): Fix at higher level instead (!)
result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str) result['Repository Root'] = C(GetNamedNodeText(dom, 'root'), str)
result['URL'] = C(GetNamedNodeText(dom, 'url'), str) result['URL'] = C(GetNamedNodeText(dom, 'url'), str)
result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str) result['UUID'] = C(GetNamedNodeText(dom, 'uuid'), str)
@ -647,11 +647,11 @@ class SVN(object):
info.get('Schedule') == 'add') info.get('Schedule') == 'add')
@staticmethod @staticmethod
def GetFileProperty(file, property_name): def GetFileProperty(filename, property_name):
"""Returns the value of an SVN property for the given file. """Returns the value of an SVN property for the given file.
Args: Args:
file: The file to check filename: The file to check
property_name: The name of the SVN property, e.g. "svn:mime-type" property_name: The name of the SVN property, e.g. "svn:mime-type"
Returns: Returns:
@ -659,7 +659,7 @@ class SVN(object):
is not set on the file. If the file is not under version control, the is not set on the file. If the file is not under version control, the
empty string is also returned. empty string is also returned.
""" """
output = SVN.Capture(["propget", property_name, file]) output = SVN.Capture(["propget", property_name, filename])
if (output.startswith("svn: ") and if (output.startswith("svn: ") and
output.endswith("is not under version control")): output.endswith("is not under version control")):
return "" return ""
@ -685,7 +685,8 @@ class SVN(object):
try: try:
# Use "svn info" output instead of os.path.isdir because the latter fails # Use "svn info" output instead of os.path.isdir because the latter fails
# when the file is deleted. # when the file is deleted.
return SVN._DiffItemInternal(SVN.CaptureInfo(filename), return SVN._DiffItemInternal(filename, SVN.CaptureInfo(filename),
bogus_dir,
full_move=full_move, revision=revision) full_move=full_move, revision=revision)
finally: finally:
shutil.rmtree(bogus_dir) shutil.rmtree(bogus_dir)
@ -835,8 +836,8 @@ class SVN(object):
def ReadSimpleAuth(filename): def ReadSimpleAuth(filename):
f = open(filename, 'r') f = open(filename, 'r')
values = {} values = {}
def ReadOneItem(type): def ReadOneItem(item_type):
m = re.match(r'%s (\d+)' % type, f.readline()) m = re.match(r'%s (\d+)' % item_type, f.readline())
if not m: if not m:
return None return None
data = f.read(int(m.group(1))) data = f.read(int(m.group(1)))

Loading…
Cancel
Save