运行时验证框架
项目描述
# pythonrv
pythonrv 是python的运行时验证框架。它是
当前正在编写的硕士论文的实现。请参阅
[tgwizard.github.com/thesis](http://tgwizard.github.com/thesis) 了解更多
信息。
[](http://travis-ci.org/tgwizard/pythonrv)
## 安装
pythonrv 可以作为 [ pip 包](http://pypi.python.org/pypi/pythonrv),
带有 setup.py 和一切。使用
[pip](http://www.pip-installer.org/en/latest/index.html) 像
这样安装它:
pip install pythonrv
pythonrv 已经用 Python 2.7 进行了测试。它还不适用于 Python 3。
## 编码
克隆存储库:
git clone git@github.com:tgwizard/pythonrv.git
cd pythonrv
初始化环境(最好通过
[virtualenv](http://pypi.python.org/pypi/virtualenv),也可能通过
[virtualenvwrapper]( http://www.doughellmann.com/docs/virtualenvwrapper/)):
pip install -r requirements.txt
运行测试:
./runtests.sh
## 运行时验证运行时验证是在运行期间
验证程序正确性的想法
它的执行。通过检查程序是否符合
其规范来进行验证。
运行时验证有多种方法,pythonrv 就是其中之一。它
基于这样的想法,规范应该用目标
编程语言编写,以类似于单元测试的方式,因此使
它们更具表现力和更容易学习。
用 pythonrv 编写的规范被“烘焙”到目标的源
代码中,因此每当调用函数时,规范也会被执行
。
## 示例
下面是一些显示 pythonrv API 的简单示例。更真实
的示例,请参见
[examples](https://github.com/tgwizard/pythonrv/tree/master/examples) 文件夹。
[单元
测试](https://github.com/tgwizard/pythonrv/tree/master/pythonrv/test)
也可能有助于查看哪些有效。
首先,假设我们有一个要验证正确性的函数:
~~~ python
# factorial.py
def factorial(n):
res = 1
for i in range(2, n)
res *= i
return res
~~~
我们现在可以为其编写规范,最好在另一个文件中:
~~~ python
# rvspecs.py
from pythonrv import rv
import factorial
@rv.monitor(fact=factorial.factorial)
def input_only_spec(event):
assert event.fn.fact .inputs[0] >= 0
@rv.monitor(fact=factorial.factorial)
@rv.spec(when=rv.POST)
def simple_specification(event):
断言 event.fn.fact.result >= event.fn。 fact.inputs[0]
@rv.monitor(fact=factorial.factorial)
@rv.spec(when=rv.POST, history_size=rv.INFINITE_HISTORY_SIZE)
def simple_specification(event):
in_out = (event.fn.fact.inputs[0], event. fn.fact.result)
old_in_out = [(x.inputs[0], x.result) for x in event.fn.fact.history]
for a in old_in_out:
if in_out[0] > old_in_out[0]:
断言 in_out [1] >= old_in_out[1]
~~~
第一个规范检查所有输入是否大于或
等于零。
第二个规范检查所有输出是否至少与
输入一样大。第三个规范
根据函数的历史数据验证输入/输出;给定更大的输入,输出必须是
比以前更大或相等。请注意,最后两个规范是在
调用阶乘之后执行的。这使他们可以访问返回值和
输出参数。
向规范发送一个事件,其中包含被调用函数的信息、其
历史记录以及规范监视的所有函数的历史记录。
~~~ python
from pythonrv import rv
import mymodule
@rv.monitor(foo=mymodule.foo, bar=mymodule.bar)
@rv.spec(when=rv.POST, history_size=20)
def more_specifications(event):
# 这里是否所有函数都被监控
event.fn
# 当前调用的函数可以像这样访问
event.called_function
# 如果调用了 mymodule.foo,则与
event.fn.foo相同
# 我们还可以检查是否调用了函数
assert event.fn.foo.call
# 输入、输出和结果可以像这个
事件一样被访问.fn.foo.inputs # 输入参数元组
的副本 event.fn.foo.input_kwargs # 输入关键字参数的副本 dict
event.fn.foo.outputs
event.fn.foo.output_kwargs
event.fn。 foo.result
# 我们可以访问前一个事件,并且前一个函数调用
event.prev
event.fn.foo.prev
# 如果对 mymodule.foo 的两次调用连续
发生 assert event.prev.fn.foo == event。 fn.foo.prev
# 但如果一个规范监控多个函数,则不需要这样做 #
我们可以访问 event.history 中 old_event 的“整个” 历史
记录 :
通过 # 这显然是一个很大的内存消耗,所以默认情况下只有两个事件 # 存储在历史记录中(这个和上一个)。这可以改变, # 就像我们在这里使用@rv.spec(history_size=20) # 我们也可以说下次调用某个受监控的函数时, # 应该发生一些事情 event.next(call_next_time) # 或者只是一个具体监控函数 event.fn.foo.next(call_next_time)
# 我们还可以指定下一个被监控的函数
# 调用:
event.next_call_should_be(event.fn.bar)
# 有时一个规范可以“完成”——它不需要再次验证
event.success("可选消息告诉一切正常")
# 或
event.failure("我们失败了,没有必要继续验证")
def call_next_time(event):
# 在这里我们可以访问与规范
通过中相同的所有数据
~~ ~
## When to Execute the Specification
在调用传递给活动被
监控者之前执行规范。这可以自定义:
~~~ python
@rv.monitor(f=func)
@rv.spec(when=rv.POST)
def spec_after(event):
pass
# 这是默认的,不需要显式指定PRE
@rv.monitor(f=func)
@rv.spec(when=rv. PRE)
def spec_before(event):
pass
~~~
## Dealing with Errors
Specifications 通过引发 `AssertionError`
异常(`assert` 语句执行)来表示验证错误。发生这种情况时,pythonrv
默认情况下会传播此错误,如果未捕获,程序将停止。
如果这不是所需的行为,则可以对其进行更改。对于日志错误
处理程序,请执行
~~~ python
from pythonrv import rv
rv.configure(error_handler=rv.LoggingErrorHandler())
~~~
然后通过 [python 日志
模块](http://docs.python.org/library/logging.html) 以正常方式配置日志记录。
规范可以用错误级别标记:
~~~ python
@rv.monitor(f=func)
@rv.spec(level=rv.DEBUG)
def spec(event):
pass
~~~
可用的错误级别是DEBUG, INFO、WARNING、ERROR 和 CRITICAL(就像
在日志记录模块中一样)。
有关错误处理的更多信息,请参阅 [源
代码](https://github.com/tgwizard/pythonrv/blob/master/pythonrv/rv.py) 和
[单元
测试](https://github.com /tgwizard/pythonrv/blob/master/pythonrv/test/rv_configuration_test.py)。
## 技术问题
### 导入
建议您
在程序中首先执行的操作中导入您的 rv 规范。原因将在下文详述。
在编写规范时,这是**错误**的方式:
~~~ python
# this doesn't work
from mymodule import myfunc
@rv.monitor(f=myfunc)
def spec(event):
pass
~~~
This is the正确方法:
~~~ python
# this works
import mymodule
@rv.monitor(f=mymodule.myfunc)
def spec(event):
pass
~~~
第一个例子创建一个对myfunc的引用并将它插入到
当前模块(定义规范的模块)。监控功能
意味着在它周围添加一个包装器,在这种情况下,我们只为
当前模块中的 myfunc 引用添加一个包装器。我们不修改 mymodule,
所有其他代码都将使用它。第二个例子解决了这个问题。
上述原因也解释了为什么应该
在执行的最开始导入/定义规范:其他模块可能
使用 from x import y 样式,如果它们在 rv 规范
有机会监视/检测函数,它们将获得
对它们的未监控/未检测的引用。
### 复制参数
当拦截函数调用时,pythonrv 复制参数以确保
它存储的历史不会在之后和/或从
外部。这也使得输入参数确实是输入
参数,而不是由函数本身修改。
这有时可能被认为是不必要的,或不必要的昂贵。它
有时甚至可能不起作用,例如当
涉及 [`cStringIO`](http://docs.python.org/library/stringio.html) 时。v1.4.x
中的 [Django](https://www.djangoproject.com/) 请求就是这种情况
(但不是在主服务器上)。
所有规范都可以关闭复制:
~~~ python
from pythonrv import rv
rv.configure(enable_copy_args=False)
~~~
或者针对特定规范:
~~~ python
from pythonrv import rv
@rv.monitor(func=somemodule .somefunc)
@rv.spec(enable_copy_args=False)
def spec(event):
pass
~~~
注意:禁用一个规范的
参数复制实际上会禁用该规范的所有受监视函数的参数复制。监视相同功能的其他
规范也不会复制参数
。这是“一个特点”。
## 许可证
pythonrv 在 [MIT
许可证](http://opensource.org/licenses/mit-license.php) 下发布。
pythonrv 是python的运行时验证框架。它是
当前正在编写的硕士论文的实现。请参阅
[tgwizard.github.com/thesis](http://tgwizard.github.com/thesis) 了解更多
信息。
[](http://travis-ci.org/tgwizard/pythonrv)
## 安装
pythonrv 可以作为 [ pip 包](http://pypi.python.org/pypi/pythonrv),
带有 setup.py 和一切。使用
[pip](http://www.pip-installer.org/en/latest/index.html) 像
这样安装它:
pip install pythonrv
pythonrv 已经用 Python 2.7 进行了测试。它还不适用于 Python 3。
## 编码
克隆存储库:
git clone git@github.com:tgwizard/pythonrv.git
cd pythonrv
初始化环境(最好通过
[virtualenv](http://pypi.python.org/pypi/virtualenv),也可能通过
[virtualenvwrapper]( http://www.doughellmann.com/docs/virtualenvwrapper/)):
pip install -r requirements.txt
运行测试:
./runtests.sh
## 运行时验证运行时验证是在运行期间
验证程序正确性的想法
它的执行。通过检查程序是否符合
其规范来进行验证。
运行时验证有多种方法,pythonrv 就是其中之一。它
基于这样的想法,规范应该用目标
编程语言编写,以类似于单元测试的方式,因此使
它们更具表现力和更容易学习。
用 pythonrv 编写的规范被“烘焙”到目标的源
代码中,因此每当调用函数时,规范也会被执行
。
## 示例
下面是一些显示 pythonrv API 的简单示例。更真实
的示例,请参见
[examples](https://github.com/tgwizard/pythonrv/tree/master/examples) 文件夹。
[单元
测试](https://github.com/tgwizard/pythonrv/tree/master/pythonrv/test)
也可能有助于查看哪些有效。
首先,假设我们有一个要验证正确性的函数:
~~~ python
# factorial.py
def factorial(n):
res = 1
for i in range(2, n)
res *= i
return res
~~~
我们现在可以为其编写规范,最好在另一个文件中:
~~~ python
# rvspecs.py
from pythonrv import rv
import factorial
@rv.monitor(fact=factorial.factorial)
def input_only_spec(event):
assert event.fn.fact .inputs[0] >= 0
@rv.monitor(fact=factorial.factorial)
@rv.spec(when=rv.POST)
def simple_specification(event):
断言 event.fn.fact.result >= event.fn。 fact.inputs[0]
@rv.monitor(fact=factorial.factorial)
@rv.spec(when=rv.POST, history_size=rv.INFINITE_HISTORY_SIZE)
def simple_specification(event):
in_out = (event.fn.fact.inputs[0], event. fn.fact.result)
old_in_out = [(x.inputs[0], x.result) for x in event.fn.fact.history]
for a in old_in_out:
if in_out[0] > old_in_out[0]:
断言 in_out [1] >= old_in_out[1]
~~~
第一个规范检查所有输入是否大于或
等于零。
第二个规范检查所有输出是否至少与
输入一样大。第三个规范
根据函数的历史数据验证输入/输出;给定更大的输入,输出必须是
比以前更大或相等。请注意,最后两个规范是在
调用阶乘之后执行的。这使他们可以访问返回值和
输出参数。
向规范发送一个事件,其中包含被调用函数的信息、其
历史记录以及规范监视的所有函数的历史记录。
~~~ python
from pythonrv import rv
import mymodule
@rv.monitor(foo=mymodule.foo, bar=mymodule.bar)
@rv.spec(when=rv.POST, history_size=20)
def more_specifications(event):
# 这里是否所有函数都被监控
event.fn
# 当前调用的函数可以像这样访问
event.called_function
# 如果调用了 mymodule.foo,则与
event.fn.foo相同
# 我们还可以检查是否调用了函数
assert event.fn.foo.call
# 输入、输出和结果可以像这个
事件一样被访问.fn.foo.inputs # 输入参数元组
的副本 event.fn.foo.input_kwargs # 输入关键字参数的副本 dict
event.fn.foo.outputs
event.fn.foo.output_kwargs
event.fn。 foo.result
# 我们可以访问前一个事件,并且前一个函数调用
event.prev
event.fn.foo.prev
# 如果对 mymodule.foo 的两次调用连续
发生 assert event.prev.fn.foo == event。 fn.foo.prev
# 但如果一个规范监控多个函数,则不需要这样做 #
我们可以访问 event.history 中 old_event 的“整个” 历史
记录 :
通过 # 这显然是一个很大的内存消耗,所以默认情况下只有两个事件 # 存储在历史记录中(这个和上一个)。这可以改变, # 就像我们在这里使用@rv.spec(history_size=20) # 我们也可以说下次调用某个受监控的函数时, # 应该发生一些事情 event.next(call_next_time) # 或者只是一个具体监控函数 event.fn.foo.next(call_next_time)
# 我们还可以指定下一个被监控的函数
# 调用:
event.next_call_should_be(event.fn.bar)
# 有时一个规范可以“完成”——它不需要再次验证
event.success("可选消息告诉一切正常")
# 或
event.failure("我们失败了,没有必要继续验证")
def call_next_time(event):
# 在这里我们可以访问与规范
通过中相同的所有数据
~~ ~
## When to Execute the Specification
在调用传递给活动被
监控者之前执行规范。这可以自定义:
~~~ python
@rv.monitor(f=func)
@rv.spec(when=rv.POST)
def spec_after(event):
pass
# 这是默认的,不需要显式指定PRE
@rv.monitor(f=func)
@rv.spec(when=rv. PRE)
def spec_before(event):
pass
~~~
## Dealing with Errors
Specifications 通过引发 `AssertionError`
异常(`assert` 语句执行)来表示验证错误。发生这种情况时,pythonrv
默认情况下会传播此错误,如果未捕获,程序将停止。
如果这不是所需的行为,则可以对其进行更改。对于日志错误
处理程序,请执行
~~~ python
from pythonrv import rv
rv.configure(error_handler=rv.LoggingErrorHandler())
~~~
然后通过 [python 日志
模块](http://docs.python.org/library/logging.html) 以正常方式配置日志记录。
规范可以用错误级别标记:
~~~ python
@rv.monitor(f=func)
@rv.spec(level=rv.DEBUG)
def spec(event):
pass
~~~
可用的错误级别是DEBUG, INFO、WARNING、ERROR 和 CRITICAL(就像
在日志记录模块中一样)。
有关错误处理的更多信息,请参阅 [源
代码](https://github.com/tgwizard/pythonrv/blob/master/pythonrv/rv.py) 和
[单元
测试](https://github.com /tgwizard/pythonrv/blob/master/pythonrv/test/rv_configuration_test.py)。
## 技术问题
### 导入
建议您
在程序中首先执行的操作中导入您的 rv 规范。原因将在下文详述。
在编写规范时,这是**错误**的方式:
~~~ python
# this doesn't work
from mymodule import myfunc
@rv.monitor(f=myfunc)
def spec(event):
pass
~~~
This is the正确方法:
~~~ python
# this works
import mymodule
@rv.monitor(f=mymodule.myfunc)
def spec(event):
pass
~~~
第一个例子创建一个对myfunc的引用并将它插入到
当前模块(定义规范的模块)。监控功能
意味着在它周围添加一个包装器,在这种情况下,我们只为
当前模块中的 myfunc 引用添加一个包装器。我们不修改 mymodule,
所有其他代码都将使用它。第二个例子解决了这个问题。
上述原因也解释了为什么应该
在执行的最开始导入/定义规范:其他模块可能
使用 from x import y 样式,如果它们在 rv 规范
有机会监视/检测函数,它们将获得
对它们的未监控/未检测的引用。
### 复制参数
当拦截函数调用时,pythonrv 复制参数以确保
它存储的历史不会在之后和/或从
外部。这也使得输入参数确实是输入
参数,而不是由函数本身修改。
这有时可能被认为是不必要的,或不必要的昂贵。它
有时甚至可能不起作用,例如当
涉及 [`cStringIO`](http://docs.python.org/library/stringio.html) 时。v1.4.x
中的 [Django](https://www.djangoproject.com/) 请求就是这种情况
(但不是在主服务器上)。
所有规范都可以关闭复制:
~~~ python
from pythonrv import rv
rv.configure(enable_copy_args=False)
~~~
或者针对特定规范:
~~~ python
from pythonrv import rv
@rv.monitor(func=somemodule .somefunc)
@rv.spec(enable_copy_args=False)
def spec(event):
pass
~~~
注意:禁用一个规范的
参数复制实际上会禁用该规范的所有受监视函数的参数复制。监视相同功能的其他
规范也不会复制参数
。这是“一个特点”。
## 许可证
pythonrv 在 [MIT
许可证](http://opensource.org/licenses/mit-license.php) 下发布。