跳转到主要内容

调用图地址库。

项目描述

Ptera

Ptera是一种强大的方式,可用于为日志记录、调试和测试目的对代码进行测量。通过简单的调用ptera.probing(),您可以

📖 阅读文档

安装

pip install ptera

示例

您可以使用Ptera观察程序中任何变量的赋值

from ptera import probing

def f(x):
    y = 10
    for i in range(1, x + 1):
        y = y + i
    return y

with probing("f > y").values() as values:
    f(3)

# These are all the values taken by the y variable in f.
assert values == [
    {"y": 10},
    {"y": 11},
    {"y": 13},
    {"y": 16},
]

在上面的示例中,

  1. 我们使用选择器f > y 选择 函数f的变量y
  2. 我们使用values()方法获取一个列表,其中将逐步累积y的值。
  3. 当在probing块内部调用f时,将拦截对y的赋值并将其追加到列表中。
  4. probing块完成后,将移除测量并使f恢复其正常行为。

创建测量

使用探测

Ptera的探测接口受到函数式响应式编程的启发,与giving(本身基于rx)的接口相同。请在此处查看操作符的完整列表。

如果您想保持简单,并仅获取值列表,则始终可以使用with probing(...).values(),如上例所示。您还可以使用with probing(...).display()来打印值。

除此之外,您还可以定义复杂的数据处理管道。例如

with probing("f > x") as probe:
    probe["x"].map(abs).max().print()
    f(1234)

上面的代码定义了一个管道,该管道提取x的值,对每个元素应用abs函数,取这些绝对值的最大值,然后打印出来。请注意,这个语句在执行时并没有真正任何事情,它只声明了一个管道,该管道将在之后的任何时候激活,当探测到的变量被设置时。这就是为什么在之后而不是之前调用f的原因。

更多示例

Ptera致力于提供新的方法来检查程序正在做什么,所以所有示例都将基于这个简单的二分搜索函数

from ptera import global_probe, probing

def f(arr, key):
    lo = -1
    hi = len(arr)
    while lo < hi - 1:
        mid = lo + (hi - lo) // 2
        if (elem := arr[mid]) > key:
            hi = mid
        else:
            lo = mid
    return lo + 1

##############################
# THE PROBING CODE GOES HERE #
##############################

f(list(range(1, 350, 7)), 136)

为了获取下表中右侧列的输出,需要在左侧列的代码插入到调用f之前的大注释处。大多数global_probe上的方法都定义了探测值将通过的管道(接口受到函数式响应式编程的启发),因此,在调用仪器化函数之前定义它们非常重要。

代码 输出

display方法提供了一种简单的日志记录值的方法。

global_probe("f > mid").display()
mid: 24
mid: 11
mid: 17
mid: 20
mid: 18
mid: 19

print方法允许您指定一个格式字符串。

global_probe("f(mid) > elem").print("arr[{mid}] == {elem}")
arr[24] == 169
arr[11] == 78
arr[17] == 120
arr[20] == 141
arr[18] == 127
arr[19] == 134

减少操作很简单:提取键,并使用minmax等。

global_probe("f > lo")["lo"].max().print("max(lo) = {}")
global_probe("f > hi")["hi"].min().print("min(hi) = {}")
max(lo) = 19
min(hi) = 20

使用fail()定义断言(对于调试,还可以尝试.breakpoint()!)

def unordered(xs):
    return any(x > y for x, y in zip(xs[:-1], xs[1:]))

probe = global_probe("f > arr")["arr"]
probe.filter(unordered).fail("List is unordered: {}")

f([1, 6, 30, 7], 18)
Traceback (most recent call last):
  ...
  File "test.py", line 30, in <module>
    f([1, 6, 30, 7], 18)
  File "<string>", line 3, in f__ptera_redirect
  File "test.py", line 3, in f
    def f(arr, key):
giving.gvn.Failure: List is unordered: [1, 6, 30, 7]

累积到列表中

results = global_probe("f > mid")["mid"].accum()
f(list(range(1, 350, 7)), 136)
print(results)

with probing("f > mid")["mid"].values() as results:
    f(list(range(1, 350, 7)), 136)

print(results)
[24, 11, 17, 20, 18, 19]

探测

用法:with ptera.probing(selector) as probe: ...

选择器是我们想要通过探测器流经哪些函数中哪些变量的规范。其中一个变量必须是选择器的焦点,这意味着当那个变量被设置时,探测器将被触发。焦点可以表示为f(!x)f > x(在两种情况下焦点都是x)。

探测rx.Observable的包装器,支持大量的操作符,如mapfilterminaveragethrottle等(接口与giving相同)。

示例1:中间变量

Ptera能够捕获函数中的任何变量,而不仅仅是输入和返回值

def fact(n):
    curr = 1
    for i in range(n):
        curr = curr * (i + 1)
    return curr

with probing("fact(i, !curr)").print():
    fact(3)
    # {'curr': 1}
    # {'curr': 1, 'i': 0}
    # {'curr': 2, 'i': 1}
    # {'curr': 6, 'i': 2}

选择器上方的"!"表示焦点是curr。这意味着它在curr被设置时被触发。这就是为什么第一个结果没有i的值。您可以使用选择器fact(!i, curr)来关注i

with probing("fact(!i, curr)").print():
    fact(3)
    # {'i': 0, 'curr': 1}
    # {'i': 1, 'curr': 1}
    # {'i': 2, 'curr': 2}

您可以看到关联是不同的(当i为2时,curr为2,而当另一个选择器时为6),但这仅仅是因为现在它们是在i被设置时触发的。

示例2:多个作用域

选择器可以在调用图中作用多个嵌套作用域。例如,选择器 f(x) > g(y) > h > z 会从三个不同函数的作用域中捕获变量 xyz,但仅当 f 调用 gg 调用 h 时(无论是直接还是间接)。

def f(x):
    return g(x + 1) * g(-x - 1)

def g(x):
    return x * 2

# Use "as" to rename a variable if there is a name conflict
with probing("f(x) > g > x as gx").print():
    f(5)
    # {'gx': 6, 'x': 5}
    # {'gx': -6, 'x': 5}
    g(10)
    # Prints nothing

示例 3:覆盖变量

还可以使用 override(或 koverride)方法来覆盖变量的值。

def add_ct(x):
    ct = 1
    return x + ct

with probing("add_ct(x) > ct", overridable=True) as probe:
    # The value of other variables can be used to compute the new value of ct
    probe.override(lambda data: data["x"])

    # You can also use koverride, which calls func(**data)
    # probe.koverride(lambda x: x)

    print(add_ct(3))   # sets ct = x = 3; prints 6
    print(add_ct(10))  # sets ct = x = 20; prints 20

重要: override() 只会覆盖 焦点变量。如前所述,焦点变量是位于 > 右侧的变量,或者以 ! 为前缀的变量。Ptera 选择器只有在焦点变量被设置时才会触发,因此实际上它是有意义覆盖的唯一变量。

这一点值得记住,因为否则有时并不明显了解覆盖做了什么。例如

with probing("add_ct(x) > ct", overridable=True) as probe:
    # The focus is ct, so override will always set ct
    # Therefore, this sets ct = 10 when x == 3:
    probe.where(x=3).override(10)

    print(add_ct(3))   # sets ct = 10; prints 13
    print(add_ct(10))  # does not override anything; prints 11

项目详情


下载文件

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

源分发

ptera-1.4.1.tar.gz (40.0 kB 查看散列)

上传时间

构建分发

ptera-1.4.1-py3-none-any.whl (39.3 kB 查看散列)

上传时间 Python 3

支持者

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