1
0
mirror of https://github.com/gryf/pythonhelper.git synced 2025-12-18 20:10:24 +01:00

Enforce PEP8 on the Python code

This commit is contained in:
2016-05-18 21:06:10 +02:00
parent d40dbfe22b
commit 9809859c73
2 changed files with 161 additions and 175 deletions

View File

@@ -1,7 +1,10 @@
"""
Simple analyzer for python source files. Collect and give info about file
structure: classes, its methods and functions.
"""
import re import re
import sys import sys
import time import time
import traceback
import vim import vim
@@ -13,45 +16,32 @@ BUFFERTICKS = {}
class PythonTag(object): class PythonTag(object):
"""A simple storage class representing a python tag.""" """A simple storage class representing a python tag."""
# possible tag types CLASS = "class"
TT_CLASS = 0 METHOD = "method"
TT_METHOD = 1 FUNCTION = "function"
TT_FUNCTION = 2
# tag type names def __init__(self, tag_type, name, full_name, line_number, indent_level):
TAG_TYPE_NAME = {TT_CLASS: "class", """Initializes instances of Python tags.
TT_METHOD: "method",
TT_FUNCTION: "function"}
def __init__(self, type, name, fullName, lineNumber, indentLevel): :param tag_type: Tag type as string
:param name: Short tag name
:param full_name: Full tag name (in dotted notation)
:param line_number: line number on which the tag starts
:param indent_level: indentation level of the tag (number)
""" """
Initializes instances of PythonTag(). self.tag_type = tag_type
Parameters
type -- tag type
name -- short tag name
fullName -- full tag name (in dotted notation)
lineNumber -- line number on which the tag starts
indentLevel -- indentation level of the tag
"""
self.type = type
self.name = name self.name = name
self.fullName = fullName self.full_name = full_name
self.lineNumber = lineNumber self.line_number = line_number
self.indentLevel = indentLevel self.indent_level = indent_level
def __str__(self): def __str__(self):
"""Returns a string representation of the tag.""" """Returns a string representation of the tag."""
return "%s (%s) [%s, %u, %u]" % (self.name, return "%s (%s) [%s, %u, %u]" % (self.name,
PythonTag.TAG_TYPE_NAME[self.type], self.tag_type,
self.fullName, self.full_name,
self.lineNumber, self.line_number,
self.indentLevel,) self.indent_level,)
__repr__ = __str__ __repr__ = __str__
@@ -71,308 +61,304 @@ class SimplePythonTagsParser(object):
""" """
Initializes instances of SimplePythonTagsParser(). Initializes instances of SimplePythonTagsParser().
Parameters :param source: source for which the tags will be generated. It must
be a generator.
source -- source for which the tags will be generated. It must
be a generator.
""" """
self.source = source self.source = source
def getTags(self): def get_tags(self):
""" """
Determines all the tags for the buffer. Returns a tuple in the format Determines all the tags for the buffer.
(tagLineNumbers, tags,).
:returns: tuple in the format (tag_line_numbers, tags,).
""" """
tagLineNumbers = [] tag_line_numbers = []
tags = {} tags = {}
tagsStack = [] tags_stack = []
import itertools import itertools
# go through all the lines in the source and localize all Python tags # go through all the lines in the source and localize all Python tags
# in it # in it
for (line, lineNumber) in zip(self.source, itertools.count(1)): for (line, line_number) in zip(self.source, itertools.count(1)):
# extract the line's indentation characters and its content # extract the line's indentation characters and its content
lineMatch = self.COMMENTS_INDENT_RE.match(line) line_match = self.COMMENTS_INDENT_RE.match(line)
lineContent = lineMatch.group(2) line_content = line_match.group(2)
# match for the class tag # match for the class tag
tagMatch = self.CLASS_RE.match(lineContent) tag_match = self.CLASS_RE.match(line_content)
# if the class tag has been found, store some information on it # if the class tag has been found, store some information on it
if (tagMatch): if tag_match:
currentTag = self.getPythonTag(tagsStack, lineNumber, current_tag = self.get_python_tag(tags_stack, line_number,
lineMatch.group(1), line_match.group(1),
tagMatch.group(1), tag_match.group(1),
self.tagClassTypeDecidingMethod) self.tag_class_type_deciding_method)
tagLineNumbers.append(lineNumber) tag_line_numbers.append(line_number)
tags[lineNumber] = currentTag tags[line_number] = current_tag
else: else:
# match for the method/function tag # match for the method/function tag
tagMatch = self.METHOD_RE.match(lineContent) tag_match = self.METHOD_RE.match(line_content)
# if the method/function tag has been found, store some # if the method/function tag has been found, store some
# information on it # information on it
if (tagMatch): if tag_match:
currentTag = self.getPythonTag(tagsStack, current_tag = self.get_python_tag(tags_stack,
lineNumber, line_number,
lineMatch.group(1), line_match.group(1),
tagMatch.group(1), tag_match.group(1),
self.tagFunctionTypeDecidingMethod) self.tag_function_type_deciding_method)
tagLineNumbers.append(lineNumber) tag_line_numbers.append(line_number)
tags[lineNumber] = currentTag tags[line_number] = current_tag
return (tagLineNumbers, tags,) return (tag_line_numbers, tags,)
def getParentTag(self, tagsStack): def get_parent_tag(self, tags_stack):
""" """
Given a tag, returns its parent tag (instance of PythonTag()) from the Given a tag, returns its parent tag (instance of PythonTag()) from the
specified tag list. If no such parent tag exists, returns None. specified tag list. If no such parent tag exists, returns None.
Parameters :param tags_stack: list (stack) of currently open PythonTag() instances
tagsStack -- list (stack) of currently open PythonTag() instances
""" """
if (len(tagsStack)): if len(tags_stack):
parentTag = tagsStack[-1] parent_tag = tags_stack[-1]
else: else:
parentTag = None parent_tag = None
return parentTag return parent_tag
def computeIndentationLevel(indentChars): def compute_indentation_level(indent_chars):
""" """
Computes the indentation level from the specified string. Computes the indentation level from the specified string.
Parameters indent_chars -- white space before any other character on line
indentChars -- white space before any other character on line
""" """
indentLevel = 0 indent_level = 0
# compute the indentation level (expand tabs) # compute the indentation level (expand tabs)
for char in indentChars: for char in indent_chars:
if (char == '\t'): if char == '\t':
indentLevel += SimplePythonTagsParser.TABSIZE indent_level += SimplePythonTagsParser.TABSIZE
else: else:
indentLevel += 1 indent_level += 1
return indentLevel return indent_level
computeIndentationLevel = staticmethod(computeIndentationLevel) compute_indentation_level = staticmethod(compute_indentation_level)
def getPythonTag(self, tagsStack, lineNumber, indentChars, tagName, tagTypeDecidingMethod): def get_python_tag(self, tags_stack, line_number, indent_chars, tag_name,
tag_type_deciding_method):
""" """
Returns instance of PythonTag() based on the specified data. Returns instance of PythonTag based on the specified data.
Parameters Parameters
tagsStack -- list (stack) of tags currently active. Note: Modified tags_stack -- list (stack) of tags currently active. Note: Modified
in this method! in this method!
lineNumber -- current line number line_number -- current line number
indentChars -- characters making up the indentation level of the indent_chars -- characters making up the indentation level of the
current tag current tag
tagName -- short name of the current tag tag_name -- short name of the current tag
tagTypeDecidingMethod -- reference to the method that is called to tag_type_deciding_method -- reference to the method that is called to
determine the type of the current tag determine the type of the current tag
""" """
indentLevel = self.computeIndentationLevel(indentChars) indent_level = self.compute_indentation_level(indent_chars)
parentTag = self.getParentTag(tagsStack) parent_tag = self.get_parent_tag(tags_stack)
# handle enclosed tag # handle enclosed tag
while (parentTag): while parent_tag:
# if the indent level of the parent tag is greater than of the # if the indent level of the parent tag is greater than of the
# current tag, use parent tag of the parent tag # current tag, use parent tag of the parent tag
if (parentTag.indentLevel >= indentLevel): if parent_tag.indent_level >= indent_level:
del tagsStack[-1] del tags_stack[-1]
# otherwise we have all information on the current tag and can # otherwise we have all information on the current tag and can
# return it # return it
else: else:
tag = PythonTag(tagTypeDecidingMethod(parentTag.type), tag = PythonTag(tag_type_deciding_method(parent_tag.tag_type),
tagName, "%s.%s" % (parentTag.fullName, tag_name, "%s.%s" % (parent_tag.full_name,
tagName,), tag_name,),
lineNumber, indentLevel) line_number, indent_level)
break break
# use the parent tag of the parent tag # use the parent tag of the parent tag
parentTag = self.getParentTag(tagsStack) parent_tag = self.get_parent_tag(tags_stack)
# handle a top-indent level tag # handle a top-indent level tag
else: else:
tag = PythonTag(tagTypeDecidingMethod(None), tagName, tagName, tag = PythonTag(tag_type_deciding_method(None), tag_name, tag_name,
lineNumber, indentLevel) line_number, indent_level)
# add the tag to the list of tags # add the tag to the list of tags
tagsStack.append(tag) tags_stack.append(tag)
return tag return tag
def tagClassTypeDecidingMethod(self, parentTagType): def tag_class_type_deciding_method(self, parent_tag_type):
""" """
Returns tag type of the current tag based on its previous tag (super Returns tag type of the current tag based on its previous tag (super
tag) for classes. tag) for classes.
Parameters Parameters
parentTagType -- type of the enclosing/parent tag parent_tag_type -- type of the enclosing/parent tag
""" """
return PythonTag.TT_CLASS return PythonTag.CLASS
def tagFunctionTypeDecidingMethod(self, parentTagType): def tag_function_type_deciding_method(self, parent_tag_type):
""" """
Returns tag type of the current tag based on its previous tag (super Returns tag type of the current tag based on its previous tag (super
tag) for functions/methods. tag) for functions/methods.
Parameters Parameters
parentTagType -- type of the enclosing/parent tag parent_tag_type -- type of the enclosing/parent tag
""" """
if (parentTagType == PythonTag.TT_CLASS): if parent_tag_type == PythonTag.CLASS:
return PythonTag.TT_METHOD return PythonTag.METHOD
else: else:
return PythonTag.TT_FUNCTION return PythonTag.FUNCTION
def vimBufferIterator(vimBuffer): def vim_buffer_iterator(vim_buffer):
for line in vimBuffer: for line in vim_buffer:
yield line + "\n" yield line + "\n"
def getNearestLineIndex(row, tagLineNumbers): def get_nearest_line_index(row, tag_line_numbers):
""" """
Returns the index of 'tagLineNumbers' that contains the line nearest to Returns the index of 'tag_line_numbers' that contains the line nearest to
the specified cursor row. the specified cursor row.
Parameters Parameters
row -- current cursor row row -- current cursor row
tagLineNumbers -- list of tags' line numbers (ie. their position) tag_line_numbers -- list of tags' line numbers (ie. their position)
""" """
nearestLineNumber = -1 nearest_line_number = -1
nearestLineIndex = -1 nearest_line_index = -1
# go through all tag line numbers and find the one nearest to the # go through all tag line numbers and find the one nearest to the
# specified row # specified row
for lineIndex, lineNumber in enumerate(tagLineNumbers): for line_index, line_number in enumerate(tag_line_numbers):
# if the current line is nearer the current cursor position, take it # if the current line is nearer the current cursor position, take it
if (nearestLineNumber < lineNumber <= row): if nearest_line_number < line_number <= row:
nearestLineNumber = lineNumber nearest_line_number = line_number
nearestLineIndex = lineIndex nearest_line_index = line_index
# if we've come past the current cursor position, end the search # if we've come past the current cursor position, end the search
if (lineNumber >= row): if line_number >= row:
break break
return nearestLineIndex return nearest_line_index
def getTags(bufferNumber, changedTick): def get_tags(buffer_number, changed_tick):
""" """
Reads the tags for the buffer specified by the number. Returns a tuple Reads the tags for the buffer specified by the number. Returns a tuple
of the format (taglinenumber[buffer], tags[buffer],). of the format (taglinenumber[buffer], tags[buffer],).
Parameters Parameters
bufferNumber -- number of the current buffer buffer_number -- number of the current buffer
changedTick -- always-increasing number used to indicate that the changed_tick -- always-increasing number used to indicate that the
buffer has been modified since the last time buffer has been modified since the last time
""" """
global TAGLINENUMBERS, TAGS, BUFFERTICKS global TAGLINENUMBERS, TAGS, BUFFERTICKS
# return immediately if there's no need to update the tags # return immediately if there's no need to update the tags
if (BUFFERTICKS.get(bufferNumber, None) == changedTick): if (BUFFERTICKS.get(buffer_number, None) == changed_tick):
return (TAGLINENUMBERS[bufferNumber], TAGS[bufferNumber]) return (TAGLINENUMBERS[buffer_number], TAGS[buffer_number])
# get the tags # get the tags
simpleTagsParser = SimplePythonTagsParser(vimBufferIterator(vim.current.buffer)) simple_tags_parser = SimplePythonTagsParser(vim_buffer_iterator(vim.current.buffer))
tagLineNumbers, tags = simpleTagsParser.getTags() tag_line_numbers, tags = simple_tags_parser.get_tags()
# update the global variables # update the global variables
TAGS[bufferNumber] = tags TAGS[buffer_number] = tags
TAGLINENUMBERS[bufferNumber] = tagLineNumbers TAGLINENUMBERS[buffer_number] = tag_line_numbers
BUFFERTICKS[bufferNumber] = changedTick BUFFERTICKS[buffer_number] = changed_tick
return (tagLineNumbers, tags) return (tag_line_numbers, tags)
def findTag(bufferNumber, changedTick): def find_tag(buffer_number, changed_tick):
""" """
Tries to find the best tag for the current cursor position. Tries to find the best tag for the current cursor position.
Parameters Parameters
bufferNumber -- number of the current buffer buffer_number -- number of the current buffer
changedTick -- always-increasing number used to indicate that the changed_tick -- always-increasing number used to indicate that the
buffer has been modified since the last time buffer has been modified since the last time
""" """
try: try:
# get the tag data for the current buffer # get the tag data for the current buffer
tagLineNumbers, tags = getTags(bufferNumber, changedTick) tag_line_numbers, tags = get_tags(buffer_number, changed_tick)
# link to Vim's internal data # link to Vim's internal data
currentBuffer = vim.current.buffer current_buffer = vim.current.buffer
currentWindow = vim.current.window current_window = vim.current.window
row, col = currentWindow.cursor row, col = current_window.cursor
# get the index of the nearest line # get the index of the nearest line
nearestLineIndex = getNearestLineIndex(row, tagLineNumbers) nearest_line_index = get_nearest_line_index(row, tag_line_numbers)
# if a line has been found, find out if the tag is correct {{{ # if a line has been found, find out if the tag is correct {{{
# E.g. the cursor might be below the last tag, but in code that has # E.g. the cursor might be below the last tag, but in code that has
# nothing to do with the tag, which we know because the line is # nothing to do with the tag, which we know because the line is
# indented differently. In such a case no applicable tag has been # indented differently. In such a case no applicable tag has been
# found. # found.
while (nearestLineIndex > -1): while nearest_line_index > -1:
# get the line number of the nearest tag # get the line number of the nearest tag
nearestLineNumber = tagLineNumbers[nearestLineIndex] nearest_line_number = tag_line_numbers[nearest_line_index]
# walk through all the lines in the range (nearestTagLine, # walk through all the lines in the range (nearestTagLine,
# cursorRow) # cursorRow)
for lineNumber in xrange(nearestLineNumber + 1, row): for line_number in xrange(nearest_line_number + 1, row):
# get the current line # get the current line
line = currentBuffer[lineNumber] line = current_buffer[line_number]
# count the indentation of the line, if it's lower than the # count the indentation of the line, if it's lower than the
# tag's, the tag is invalid # tag's, the tag is invalid
if (len(line)): if len(line):
# initialize local auxiliary variables # initialize local auxiliary variables
lineStart = 0 line_start = 0
i = 0 i = 0
# compute the indentation of the line # compute the indentation of the line
while ((i < len(line)) and (line[i].isspace())): while (i < len(line)) and (line[i].isspace()):
# move the start of the line code # move the start of the line code
if (line[i] == '\t'): if line[i] == '\t':
lineStart += SimplePythonTagsParser.TABSIZE line_start += SimplePythonTagsParser.TABSIZE
else: else:
lineStart += 1 line_start += 1
# go to the next character on the line # go to the next character on the line
i += 1 i += 1
# if the line contains only spaces, skip it # if the line contains only spaces, skip it
if (i == len(line)): if i == len(line):
continue continue
# if the next character is a '#' (python comment), skip # if the next character is a '#' (python comment), skip
# to the next line # to the next line
if (line[i] == '#'): if line[i] == '#':
continue continue
# if the line's indentation starts before or at the # if the line's indentation starts before or at the
# nearest tag's, the tag is invalid # nearest tag's, the tag is invalid
if (lineStart <= tags[nearestLineNumber].indentLevel): if line_start <= tags[nearest_line_number].indent_level:
nearestLineIndex -= 1 nearest_line_index -= 1
break break
# the tag is correct, so use it # the tag is correct, so use it
@@ -381,27 +367,27 @@ def findTag(bufferNumber, changedTick):
# no applicable tag has been found # no applicable tag has been found
else: else:
nearestLineNumber = -1 nearest_line_number = -1
# describe the cursor position (what tag the cursor is on) # describe the cursor position (what tag the cursor is on)
# reset the description # reset the description
tagDescription = "" tag_description = ""
tagDescriptionTag = "" tag_description_tag = ""
tagDescriptionType = "" tag_description_type = ""
# if an applicable tag has been found, set the description accordingly # if an applicable tag has been found, set the description accordingly
if (nearestLineNumber > -1): if nearest_line_number > -1:
tagInfo = tags[nearestLineNumber] tag_info = tags[nearest_line_number]
tagDescriptionTag = tagInfo.fullName tag_description_tag = tag_info.full_name
tagDescriptionType = PythonTag.TAG_TYPE_NAME[tagInfo.type] tag_description_type = tag_info.tag_type
tagDescription = "%s (%s)" % (tagDescriptionTag, tag_description = "%s (%s)" % (tag_description_tag,
tagDescriptionType) tag_description_type)
# update the variable for the status line so it get updated with the # update the variable for the status line so it get updated with the
# new description # new description
vim.command("let w:PHStatusLine=\"%s\"" % (tagDescription,)) vim.command("let w:PHStatusLine=\"%s\"" % (tag_description,))
vim.command("let w:PHStatusLineTag=\"%s\"" % (tagDescriptionTag,)) vim.command("let w:PHStatusLineTag=\"%s\"" % (tag_description_tag,))
vim.command("let w:PHStatusLineType=\"%s\"" % (tagDescriptionType,)) vim.command("let w:PHStatusLineType=\"%s\"" % (tag_description_type,))
# handle possible exceptions # handle possible exceptions
except Exception: except Exception:
@@ -414,8 +400,8 @@ def findTag(bufferNumber, changedTick):
# where you have encountered exceptions? # where you have encountered exceptions?
# bury into the traceback # bury into the traceback
ec, ei, tb = sys.exc_info() ec, ei, tb = sys.exc_info()
while (tb != None): while tb != None:
if (tb.tb_next == None): if tb.tb_next == None:
break break
tb = tb.tb_next tb = tb.tb_next
@@ -426,19 +412,19 @@ def findTag(bufferNumber, changedTick):
time.sleep(0.5) time.sleep(0.5)
def deleteTags(bufferNumber): def delete_tags(buffer_number):
""" """
Removes tag data for the specified buffer number. Removes tag data for the specified buffer number.
Parameters Parameters
bufferNumber -- number of the buffer buffer_number -- number of the buffer
""" """
global TAGS, TAGLINENUMBERS, BUFFERTICKS global TAGS, TAGLINENUMBERS, BUFFERTICKS
# try to delete the tags for the buffer # try to delete the tags for the buffer
for o in (TAGS, TAGLINENUMBERS, BUFFERTICKS): for o in (TAGS, TAGLINENUMBERS, BUFFERTICKS):
try: try:
del o[bufferNumber] del o[buffer_number]
except KeyError: except KeyError:
pass pass

View File

@@ -67,7 +67,7 @@ function! PHCursorHold()
" call Python function findTag() with the current buffer number and change " call Python function findTag() with the current buffer number and change
" status indicator " status indicator
execute g:pythonhelper_python . ' findTag(' . expand("<abuf>") . execute g:pythonhelper_python . ' find_tag(' . expand("<abuf>") .
\ ', ' . b:changedtick . ')' \ ', ' . b:changedtick . ')'
endfunction endfunction
@@ -80,7 +80,7 @@ function! PHBufferDelete()
" call Python function deleteTags() with the current buffer number and " call Python function deleteTags() with the current buffer number and
" change status indicator " change status indicator
execute g:pythonhelper_python . ' deleteTags(' . expand("<abuf>") . ')' execute g:pythonhelper_python . ' delete_tags(' . expand("<abuf>") . ')'
endfunction endfunction