内存文件/流中的pylint

伊比扎曼

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

因此,存在两个问题:首先,如何在不将输入的文本写入临时文件并将其提供给pylint的情况下执行此操作?我想在某个时候pylint(或astroid)处理流而不是文件了。

而且,更重要的是,这是一个好主意吗?会导致进口或其他物品出现问题吗?直觉上我不会说,因为它似乎产生了一个新的进程(带有epylint),但是我不是python专家,所以我真的不确定。如果我用这个来发射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),所以……猴子补丁!

我们将ill-defined属性替换为一个属性,该属性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字段。因此,我们现在在以下代码中包含以下代码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)

可以说没有多少毛刺就能创造出真正好的代码。不幸的是,pylint通过在文件上调用它的特殊性将巨大的复杂性结合在一起。真正好的代码具有很好的本机API,并使用CLI接口进行包装。不要问我,如果在内部存在file_stream,那么模块是从构建的,但是却忘记了源代码。

PS:我必须在代码中进行其他更改:load_default_plugins必须先于其他内容(也许prepare_checkers,也许其他)

PPS:我建议将BaseReporter子类化,并使用它代替您的 InMemoryMessagesBuffer

PPPS:这刚刚被撤消(2014年3月3日),并将解决此问题:https ://bitbucket.org/logilab/astroid/pull-request/15/astroidbuilderstring_build-was/diff

4PS:现在是正式版本,因此不需要修补猴子:astroid.scoped_nodes.Module现在具有file_bytes属性(不带下划线)。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

上传前如何操作内存中的文件流

来自分类Dev

如何在python的内存中从Google Drive读取(流)文件?

来自分类Dev

Azure下载Blob文件流/内存流

来自分类Dev

Azure下载Blob文件流/内存流

来自分类Dev

如何使用内存流而不是文件流

来自分类Dev

内存消耗Java文件流

来自分类Dev

如何从内存中删除流?

来自分类Dev

如何从内存中删除流?

来自分类Dev

将流写入文件和内存

来自分类Dev

我可以使用ffmpeg将JPEG输出到内存流而不是文件中吗?

来自分类Dev

从文件或流中读取

来自分类Dev

从文件或流中采样?

来自分类Dev

从内存流中读取多个位图

来自分类Dev

从内存流中读取已存储的PDF

来自分类Dev

从内存流中查找图像类型

来自分类Dev

打开内存中的文件

来自分类Dev

处理内存中的文件

来自分类Dev

打开内存中的文件

来自分类Dev

如何在内存流中获取word文件,将其存储在缓冲区中并以PDF格式返回?

来自分类Dev

让cv2.imread从文件对象或类似内存流的数据(此处为未提取的tar)中读取图像

来自分类Dev

C#内存流/ OpenXML有时会在文件名中添加额外的字符

来自分类Dev

上传到内存流时无法访问关闭的文件

来自分类Dev

从内存流复制到文件流失败

来自分类Dev

C#从OpenXML返回内存流导致损坏的Word文件

来自分类Dev

C#从OpenXML返回内存流导致损坏的Word文件

来自分类Dev

如何寻找内存映射文件的流指针(使用boost)?

来自分类Dev

将压缩的内存流保存到文件流,最终文件已损坏

来自分类Dev

文件中的可重用流

来自分类Dev

在Java中读取流文件