跳转到主要内容

自行车修理工 - 重写Python源代码

项目描述

自行车修理工

BRM是一个Python源代码修改库,可以保证无损失修改的完全往返能力。它通常用于非结构化源代码部分,可以直接在标记上执行修改。

一个简单的例子是TokenTransformer,我们将每个+(加号)运算符更改为-(减号)运算符。

class DestoryAllOfThem(TokenTransformer):
    
    # Replace each PLUS token with a MINUS
    def visit_plus(self, token):
        return token._replace(string="-")

transformer = DestoryAllOfThem()
assert transformer.transform("(2p) + 2 # with my precious comment") == "(2p) - 2 # with my precious comment"

与任何形式的结构化树表示相比,基于标记的重构的一个优点是,你可以更加自由地进行操作。例如,你想原型化一个新的语法想法,比如一个运算符;这里就是了

class SquareRoot(TokenTransformer):

    # Register a new token called `squareroot`
    def register_squareroot(self):
        return "√"

    # Match a squareroot followed by a number
    @pattern("squareroot", "number")
    def remove_varprefix(self, operator, token):
        return self.quick_tokenize(f"int({token.string} ** 0.5)")

sqr = SquareRoot()
assert eval(sqr.transform("√9")) == 3

为什么选择BRM

  • BRM是一个极其简单、无依赖、纯Python工具,只有500行代码,你可以轻松地将其作为产品使用。
  • BRM支持所有新的Python语法,无需等待我们的上游更改。
  • BRM支持不完整的文件(以及包含无效Python语法的文件)。
  • BRM支持引入新语法并将其永久化用于原型。

如果你需要这些功能之一,BRM可能非常适合。但我必须警告,不要用它来进行复杂的重构任务,因为我们不打算解决这个问题。如果你需要这样的工具,请查看refactorparso

永久性

如果你喜欢转换器的概念并在实际代码中使用它们,BRM提供了一个自定义编码,当指定时将自动运行你的转换器。

  • 编写转换器
  • 将其复制到~/.brm文件夹,或者简单地使用cp <file>.py $(python -m brm)
  • 在每个文件上指定# coding: brm

示例

from brm import TokenTransformer, pattern

class AlwaysTrue(TokenTransformer):

    STRICT = False

    # Make every if/elif statement `True`
    @pattern("name", "*any", "colon")
    def always_true_if(self, *tokens):
        statement, *_, colon = tokens
        if statement.string not in {"if", "elif"}:
            return
        true, = self.quick_tokenize("True")
        return (statement, true, colon)

让我们把我们的变压器放到BRM的变压器文件夹中,然后运行我们的示例。

(.venv) [  9:12ÖS ]  [ isidentical@x200:~ ]
 $ cat -n r.py
     1  # coding: brm
     2
     3  a = 2
     4  if a > 2:
     5      print("LOL")
(.venv) [  9:12ÖS ]  [ isidentical@x200:~ ]
 $ cp test.py $(python -m brm)
(.venv) [  9:12ÖS ]  [ isidentical@x200:~ ]
 $ python r.py
LOL

嗖!

BRM模式语法

对于BRM,Python源代码只是一系列标记。它不会在这些标记之间创建任何关系,甚至不会验证文件在语法上是正确的。例如,看一下下面的文件

if a == x:
    2 + 2 # lol

对于BRM,从抽象的角度来看,文件只是以下文本

NAME NAME EQEQUAL NAME COLON NEWLINE INDENT NUMBER PLUS NUMBER COMMENT NEWLINE DEDENT ENDMARKER

并且在内部是这样处理的

brm pattern show gif

如果您想在这里匹配二进制加法运算(2 + 2),您可以创建带有number, plus, name的模式。

注意:如果您想可视化您的模式并查看它们匹配的内容,请尝试examples/visualize.py

附加信息

如果您正在使用TokenTransformer,有一些实用的函数您可以查看

函数 返回值 描述
quick_tokenize(source: str, *, strip: bool = True) List[TokenInfo] 将给定的source文本拆分为标记列表。如果stripTrue,则最后两个标记(NEWLINEEOF)将被省略。
quick_untokenize(tokens: List[TokenInfo]) str 将给定的标记序列转换回表示形式,在标记化时将产生相同的标记(有损转换)。如果您想要完整的往返/无损转换,请使用tokenize.untokenize
directional_length(tokens: List[TokenInfo]) int 计算序列中第一个和最后一个标记之间的线性距离。
shift_all(tokens: List[TokenInfo], x_offset: int, y_offset: int) List[TokenInfo] 将给定序列中的每个标记沿列偏移量x_offset移动,并沿行号移动y_offset。返回新的标记列表。
until(toktype: int, stream: List[TokenInfo]) Iterator[TokenInfo] 在看到类型为toktype的标记之前产生所有标记。如果没有看到这样的标记,它将引发一个ValueError
_get_type(token: TokenInfo) int 返回给定标记的类型。与until()一起使用时很有用。(内部使用)

项目详情


下载文件

下载您平台上的文件。如果您不确定要选择哪个,请了解更多关于安装包的信息。

源分发

brm-0.3.0.tar.gz (9.7 kB 查看哈希值)

上传时间:

构建分发

brm-0.3.0-py2.py3-none-any.whl (9.0 kB 查看哈希值)

上传时间: Python 2 Python 3

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面