Skip to content
This repository has been archived by the owner on Dec 21, 2023. It is now read-only.

Commit

Permalink
Merge pull request #75 from matthew-brett/master
Browse files Browse the repository at this point in the history
MRG: Python 3 fixes, and some refactoring
  • Loading branch information
kevinw committed Sep 1, 2017
2 parents dc3d90f + 6cc5ecd commit 78deba6
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 93 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -1 +1,3 @@
pyflakes-vim.zip
*.pyc
.cache/
2 changes: 1 addition & 1 deletion .gitmodules
@@ -1,3 +1,3 @@
[submodule "ftplugin/python/pyflakes"]
path = ftplugin/python/pyflakes
url = https://github.com/pyflakes/pyflakes.git
url = https://github.com/PyCQA/pyflakes.git
8 changes: 8 additions & 0 deletions README.rst
Expand Up @@ -43,6 +43,14 @@ Quick Installation
``~/.vim/ftplugin/python`` (or somewhere similar on your
`runtime path`_ that will be sourced for Python files).

Test
----

Some tiny tests with::

pip install -r test-requirements.txt
py.test ftplugin/python/test_flaker.py

.. _release: http://www.vim.org/scripts/script.php?script_id=2441
.. _pathogen: http://www.vim.org/scripts/script.php?script_id=2332
.. _runtime path: http://vimdoc.sourceforge.net/htmldoc/options.html#'runtimepath'
Expand Down
101 changes: 101 additions & 0 deletions ftplugin/python/flaker.py
@@ -0,0 +1,101 @@
""" Code for running pyflakes checks in Vim buffer
The main function is ``check``, which runs the pyflakes check on a buffer.
"""

import sys
import ast
from operator import attrgetter
import re

from pyflakes import checker, messages

try:
# Vim module available within vim
import vim
except ImportError:
# Otherwise, mock it up for tests
from mock import Mock
vim = Mock()


class loc(object):

def __init__(self, lineno, col=None):
self.lineno = lineno
self.col_offset = col


class SyntaxError(messages.Message):

message = 'could not compile: %s'

def __init__(self, filename, lineno, col, message):
messages.Message.__init__(self, filename, loc(lineno, col))
self.message_args = (message,)
self.lineno = lineno


class blackhole(object):
write = flush = lambda *a, **k: None


def check(buffer):
filename = buffer.name
contents = buffer[:]

# shebang usually found at the top of the file, followed by source code encoding marker.
# assume everything else that follows is encoded in the encoding.
for n, line in enumerate(contents):
if n >= 2:
break
elif re.match(r'#.*coding[:=]\s*([-\w.]+)', line):
contents = ['']*(n+1) + contents[n+1:]
break

contents = '\n'.join(contents) + '\n'

vimenc = vim.eval('&encoding')
if vimenc and hasattr(contents, 'decode'):
contents = contents.decode(vimenc)

builtins = set(['__file__'])
try:
builtins.update(set(eval(vim.eval('string(g:pyflakes_builtins)'))))
except Exception:
pass

try:
# TODO: use warnings filters instead of ignoring stderr
old_stderr, sys.stderr = sys.stderr, blackhole()
try:
tree = ast.parse(contents, filename or '<unknown>')
finally:
sys.stderr = old_stderr
except:
exc_value = sys.exc_info()[1]
try:
lineno, offset, line = exc_value.args[1][1:]
except IndexError:
lineno, offset, line = 1, 0, ''
if line and line.endswith("\n"):
line = line[:-1]

return [SyntaxError(filename, lineno, offset, str(exc_value))]
else:
# pyflakes looks to _MAGIC_GLOBALS in checker.py to see which
# UndefinedNames to ignore
old_globals = getattr(checker,' _MAGIC_GLOBALS', [])
checker._MAGIC_GLOBALS = set(old_globals) | builtins

filename = '(none)' if filename is None else filename
w = checker.Checker(tree, filename)

checker._MAGIC_GLOBALS = old_globals

w.messages.sort(key = attrgetter('lineno'))
return w.messages


def vim_quote(s):
return s.replace("'", "''")
108 changes: 17 additions & 91 deletions ftplugin/python/pyflakes.vim
Expand Up @@ -24,8 +24,12 @@ endif
if !exists("b:did_python_init")
let b:did_python_init = 0

if !has('python')
" the pyflakes.vim plugin requires Vim to be compiled with +python
if has('python')
command! -nargs=1 Python python <args>
elseif has('python3')
command! -nargs=1 Python python3 <args>
else
echo "Error: Requires Vim compiled with +python or +python3"
finish
endif

Expand All @@ -34,100 +38,22 @@ if !exists('g:pyflakes_use_quickfix')
endif


python << EOF
Python << EOF
import vim
import os.path
from os.path import dirname, join as pjoin
import sys

if sys.version_info[:2] < (2, 5):
raise AssertionError('Vim must be compiled with Python 2.5 or higher; you have ' + sys.version)

# get the directory this script is in: the pyflakes python module should be installed there.
scriptdir = os.path.join(os.path.dirname(vim.eval('expand("<sfile>")')), 'pyflakes')
if scriptdir not in sys.path:
sys.path.insert(0, scriptdir)

import ast
from pyflakes import checker, messages
from operator import attrgetter
import re

class loc(object):
def __init__(self, lineno, col=None):
self.lineno = lineno
self.col_offset = col

class SyntaxError(messages.Message):
message = 'could not compile: %s'
def __init__(self, filename, lineno, col, message):
messages.Message.__init__(self, filename, loc(lineno, col))
self.message_args = (message,)
# fix 某些情况缺少lineno导致异常
self.lineno = lineno

class blackhole(object):
write = flush = lambda *a, **k: None

def check(buffer):
filename = buffer.name
contents = buffer[:]

# shebang usually found at the top of the file, followed by source code encoding marker.
# assume everything else that follows is encoded in the encoding.
encoding_found = False
for n, line in enumerate(contents):
if n >= 2:
break
elif re.match(r'#.*coding[:=]\s*([-\w.]+)', line):
contents = ['']*(n+1) + contents[n+1:]
break

contents = '\n'.join(contents) + '\n'

vimenc = vim.eval('&encoding')
if vimenc:
contents = contents.decode(vimenc)

builtins = set(['__file__'])
try:
builtins.update(set(eval(vim.eval('string(g:pyflakes_builtins)'))))
except Exception:
pass

try:
# TODO: use warnings filters instead of ignoring stderr
old_stderr, sys.stderr = sys.stderr, blackhole()
try:
tree = ast.parse(contents, filename or '<unknown>')
finally:
sys.stderr = old_stderr
except:
try:
value = sys.exc_info()[1]
lineno, offset, line = value[1][1:]
except IndexError:
lineno, offset, line = 1, 0, ''
if line and line.endswith("\n"):
line = line[:-1]

return [SyntaxError(filename, lineno, offset, str(value))]
else:
# pyflakes looks to _MAGIC_GLOBALS in checker.py to see which
# UndefinedNames to ignore
old_globals = getattr(checker,' _MAGIC_GLOBALS', [])
checker._MAGIC_GLOBALS = set(old_globals) | builtins

filename = '(none)' if filename is None else filename
w = checker.Checker(tree, filename)

checker._MAGIC_GLOBALS = old_globals

w.messages.sort(key = attrgetter('lineno'))
return w.messages
script_dir = dirname(vim.eval('expand("<sfile>")'))
flakes_dir = pjoin(script_dir, 'pyflakes')
for path in (script_dir, flakes_dir):
if path not in sys.path:
sys.path.insert(0, path)


def vim_quote(s):
return s.replace("'", "''")
from flaker import check, vim_quote
EOF
let b:did_python_init = 1
endif
Expand Down Expand Up @@ -230,14 +156,14 @@ if !exists("*s:RunPyflakes")
else
let b:cleared = 1
endif

let b:matched = []
let b:matchedlines = {}

let b:qf_list = []
let b:qf_window_count = -1
python << EOF

Python << EOF
for w in check(vim.current.buffer):
if not isinstance(w.lineno, int):
lineno = str(w.lineno.lineno)
Expand Down
37 changes: 37 additions & 0 deletions ftplugin/python/test_flaker.py
@@ -0,0 +1,37 @@
""" Test flaker module
The flaker module contains Python functions used by the Vim code.
Run with::
pip install nose pytest
py.test test_flaker.py
"""

from os.path import dirname, join as pjoin
import sys

# Put our copy of pyflakes on the PATH
sys.path.insert(0, pjoin(dirname(__file__), 'pyflakes'))

import flaker


class Buffer(object):

def __init__(self, **kwargs):
self.__dict__.update(kwargs)

def __getitem__(self, slicer):
return self.contents[slicer]


def test_check():
# Try a syntax error
buffer = Buffer(name='foo', contents = ["First line\n"])
ret = flaker.check(buffer)
assert len(ret) == 1
assert isinstance(ret[0], flaker.SyntaxError)
# Code OK, empty list returned
buffer = Buffer(name='foo', contents = ["a = 1\n"])
assert flaker.check(buffer) == []
2 changes: 2 additions & 0 deletions test-requirements.txt
@@ -0,0 +1,2 @@
pytest
mock

0 comments on commit 78deba6

Please sign in to comment.