gpt4 book ai didi

python - 内存文件/流上的 pylint

转载 作者:太空宇宙 更新时间:2023-11-04 03:54:37 24 4
gpt4 key购买 nike

我想在程序中嵌入 pylint。用户输入 python 程序(在 Qt 中,在 QTextEdit 中,尽管不相关),然后在后台我调用 pylint 来检查他输入的文本。最后,我在消息框中打印错误。

因此有两个问题:首先,如果不将输入的文本写入临时文件并将其提供给 pylint,我该如何做到这一点?我想在某些时候 pylint(或 astroid)处理的是流而不是文件。

而且,更重要的是,这是个好主意吗?它会导致进口或其他东西出现问题吗?直觉上我会说不,因为它似乎产生了一个新进程(使用 epylint),但我不是 python 专家所以我真的不确定。如果我使用 this启动 pylint,也可以吗?

编辑:我尝试修补 pylint 的内部结构,用它来处理事件,但最终卡在了某个点。

这是目前的代码:

from astroid.builder import AstroidBuilder
from astroid.exceptions import AstroidBuildingException
from logilab.common.interface import implements
from pylint.interfaces import IRawChecker, ITokenChecker, IAstroidChecker
from pylint.lint import PyLinter
from pylint.reporters.text import TextReporter
from pylint.utils import PyLintASTWalker

class Validator():
def __init__(self):
self._messagesBuffer = InMemoryMessagesBuffer()
self._validator = None
self.initValidator()

def initValidator(self):
self._validator = StringPyLinter(reporter=TextReporter(output=self._messagesBuffer))
self._validator.load_default_plugins()
self._validator.disable('W0704')
self._validator.disable('I0020')
self._validator.disable('I0021')
self._validator.prepare_import_path([])

def destroyValidator(self):
self._validator.cleanup_import_path()

def check(self, string):
return self._validator.check(string)


class InMemoryMessagesBuffer():
def __init__(self):
self.content = []
def write(self, st):
self.content.append(st)
def messages(self):
return self.content
def reset(self):
self.content = []

class StringPyLinter(PyLinter):
"""Does what PyLinter does but sets checkers once
and redefines get_astroid to call build_string"""
def __init__(self, options=(), reporter=None, option_groups=(), pylintrc=None):
super(StringPyLinter, self).__init__(options, reporter, option_groups, pylintrc)
self._walker = None
self._used_checkers = None
self._tokencheckers = None
self._rawcheckers = None
self.initCheckers()

def __del__(self):
self.destroyCheckers()

def initCheckers(self):
self._walker = PyLintASTWalker(self)
self._used_checkers = self.prepare_checkers()
self._tokencheckers = [c for c in self._used_checkers if implements(c, ITokenChecker)
and c is not self]
self._rawcheckers = [c for c in self._used_checkers if implements(c, IRawChecker)]
# notify global begin
for checker in self._used_checkers:
checker.open()
if implements(checker, IAstroidChecker):
self._walker.add_checker(checker)

def destroyCheckers(self):
self._used_checkers.reverse()
for checker in self._used_checkers:
checker.close()

def check(self, string):
modname = "in_memory"
self.set_current_module(modname)

astroid = self.get_astroid(string, modname)
self.check_astroid_module(astroid, self._walker, self._rawcheckers, self._tokencheckers)

self._add_suppression_messages()
self.set_current_module('')
self.stats['statement'] = self._walker.nbstatements

def get_astroid(self, string, modname):
"""return an astroid representation for a module"""
try:
return AstroidBuilder().string_build(string, modname)
except SyntaxError as ex:
self.add_message('E0001', line=ex.lineno, args=ex.msg)
except AstroidBuildingException as ex:
self.add_message('F0010', args=ex)
except Exception as ex:
import traceback
traceback.print_exc()
self.add_message('F0002', args=(ex.__class__, ex))


if __name__ == '__main__':
code = """
a = 1
print(a)
"""

validator = Validator()
print(validator.check(code))

回溯如下:

Traceback (most recent call last):
File "validator.py", line 16, in <module>
main()
File "validator.py", line 13, in main
print(validator.check(code))
File "validator.py", line 30, in check
self._validator.check(string)
File "validator.py", line 79, in check
self.check_astroid_module(astroid, self._walker, self._rawcheckers, self._tokencheckers)
File "c:\Python33\lib\site-packages\pylint\lint.py", line 659, in check_astroid_module
tokens = tokenize_module(astroid)
File "c:\Python33\lib\site-packages\pylint\utils.py", line 103, in tokenize_module
print(module.file_stream)
AttributeError: 'NoneType' object has no attribute 'file_stream'
# And sometimes this is added :
File "c:\Python33\lib\site-packages\astroid\scoped_nodes.py", line 251, in file_stream
return open(self.file, 'rb')
OSError: [Errno 22] Invalid argument: '<?>'

我明天继续挖掘。 :)

最佳答案

我让它运行了。

第一个(NoneType …)真的很简单,而且你的代码中有一个错误:

遇到异常可以使get_astroid “失败”,即发送一条语法错误消息并返回!

但是对于第二个……pylint/logilab 的 API 中的废话……让我解释一下:你的 astroid这里的对象类型为 astroid.scoped_nodes.Module .

它也是由工厂创建的,AstroidBuilder , 它设置了 astroid.file = '<?>' .

不幸的是,Module类具有以下属性:

@property
def file_stream(self):
if self.file is not None:
return open(self.file, 'rb')
return None

除了子类化(这会使我们无法使用 AstroidBuilder 中的魔法),没有办法跳过它,所以……猴子修补!

在进行上述默认行为之前,我们将定义错误的属性替换为检查实例是否引用我们的代码字节的属性(例如 astroid._file_bytes)。

def _monkeypatch_module(module_class):
if module_class.file_stream.fget.__name__ == 'file_stream_patched':
return # only patch if patch isn’t already applied

old_file_stream_fget = module_class.file_stream.fget
def file_stream_patched(self):
if hasattr(self, '_file_bytes'):
return BytesIO(self._file_bytes)
return old_file_stream_fget(self)

module_class.file_stream = property(file_stream_patched)

monkeypatching 可以在调用 check_astroid_module 之前调用.但是还必须做一件事。看,还有更多隐式行为:一些检查器期望并使用 astroidfile_encoding field 。所以我们现在在 check 中间有这段代码:

astroid = self.get_astroid(string, modname)
if astroid is not None:
_monkeypatch_module(astroid.__class__)
astroid._file_bytes = string.encode('utf-8')
astroid.file_encoding = 'utf-8'

self.check_astroid_module(astroid, self._walker, self._rawcheckers, self._tokencheckers)

可以说,再多的 linting 也无法创建真正好的代码。不幸的是,pylint 将巨大的复杂性与在文件上调用它的特化结合在一起。真正好的代码有一个很好的 native API 并用 CLI 接口(interface)包装它。不要问我为什么 file_stream 在内部存在,Module 是从中构建的但忘记了源代码。

PS:我不得不在您的代码中更改其他内容:load_default_plugins必须在其他一些东西之前出现(也许是 prepare_checkers ,也许……其他)

PPS:我建议子类化 BaseReporter 并使用它来代替你的 InMemoryMessagesBuffer

PPPS:这刚刚被取消(3.2014),并将解决这个问题:https://bitbucket.org/logilab/astroid/pull-request/15/astroidbuilderstring_build-was/diff

4PS:现在是正式版,所以不需要猴子补丁:astroid.scoped_nodes.Module现在有一个 file_bytes属性(不带前导下划线)。

关于python - 内存文件/流上的 pylint,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19542329/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com