How to Contribute

原文

报bug

如果你发现了angr无法解决的问题,请告诉我们

1.创建一个 angr/binaries和angr/angr的分支
2.给我们一个带有angr/binaries的pr,带有有问题的binaries
3.给我们一个angr/angr的pr,用测试用例来触发二进制文件

angr/tests/broken_x.py
或者angr/tests/broken_y.py之类的

请尝试遵循我们拥有的测试用例格式(所以代码在test_xxx函数中),这样我们就可以很容易地合并,并使脚本运行。
一个例子是:

def test_some_broken_feature():
    p = angr.Project("some_binary")
    result = p.analyses.SomethingThatDoesNotWork()
    assert result == "what it should *actually* be if it worked"

if __name__ == '__main__':
    test_some_broken_feature()

这将极大地帮助我们重现错误并且更快地修复它。

理想情况是,当错误修复后,你的测试用例将pass。

然后我们会修复bug,并将broken_x.py重命名为test_x.py,测试用例将在每次push时运行在我们的内部CI中,确保我们不会再破坏这个特性。

开发angr

这是一些指导方针,这样我们就可以保持代码库的良好状态!

编码风格

我们试图在不显得愚蠢的情况下尽可能地接近PEP8代码约定。如果你使用Vim,python-mode插件会完成所有你需要的工作。你还可以手动配置Vim以符合这个行为。

最重要的是,在编写angr时,请考虑以下几点:

  • 尽可能使用属性访问参数(参见@property装饰器),而不是getter和setter。这不是java,在iPython中属性支持tab补全。
  • 使用angr-dev repo 中的.pylintrc,这是相当宽松的,但是如果pylint在这些设置下抱怨,我们的CI服务器会让你的构建失败
  • 在任何情况下,都不要抛出Exception或者断言(assert false)。使用正确的异常类型。如果没有一个正确的一场类型,子类化你正在工作的模块的核心异常(例如,angr中的AngrError,SimuVEX中的SimError,等等)并引发它。我们在正确的位置捕捉并正确处理正确类型的错误,但是断言和Exception在任何地方都不会被处理,并强制终止分析。
  • 不要使用tab,用空格缩进。标准是4个空格
  • 避免很长的行,长一点的行是可以的,但是很难阅读,以120个字符为界吧
  • 避免使用非常长的函数,通常最好将它们分解成更小的函数
  • 总是使用而不是_描述private的成员(这样我们可以在调试时访问它们)。你可能认为没有人需要调用给定的函数,但是相信我们,你错了。

文档

给你的代码添加文档。每个类定义的公共函数都应该有一下描述

  • 它做什么的
  • 参数类型和含义是什么
  • 它的返回是什么

类文档字符串将由我们的linter强制执行。在任何情况下都不要便携文档字符串,它只提供类名以外的信息。你应该尝试编写的是累应该使用的环境的描述。如果不应该由最终用户实例化类,则写一份描述,说明将在哪里生成它,以及如何获取实例。如果类应该最终由用户实例化,请解释它的核心代表什么类型的对象,对其参数的期望是什么,以及如何安全地管理其对象类型。

我们使用Sphinx生成的API文档.
Sphinx支持用ReStructured Text编写的文档字符串,使用特殊的关键字给函数和类参数、返回值、返回类型、成员等添加文档。

下面是函数文档的一个例子
理想情况下,参数描述应该垂直对齐,以使文档字符串尽可能具有可读性

def prune(self, filter_func=None, from_stash=None, to_stash=None):
    """
    Prune unsatisfiable paths from a stash.

    :param filter_func: Only prune paths that match this filter.
    :param from_stash:  Prune paths from this stash. (default: 'active')
    :param to_stash:    Put pruned paths in this stash. (default: 'pruned')
    :returns:           The resulting PathGroup.
    :rtype:             PathGroup
    """

这种格式的优点在于生成的文档中可以清楚地识别函数参数。但是。它会使文档重复,在某些情况下,文本描述更具有可读性。选择你认为更适合要编写文档的函数或类的格式。

 def read_bytes(self, addr, n):
    """
    Read `n` bytes at address `addr` in memory and return an array of bytes.
    """

单元测试

如果你正在推动一个新功能,而它并没有伴随着一个测试用例,那么它将很快出现问题。请为你的东西编写测试用例。

我们有一个内部CI服务器来运行测试,以检查每个提交的功能和回归。为了让我们的服务器运行您的测试,请在相应存储库的tests文件夹中匹配test_*.py的文件中,以nosetests可接受的格式编写测试。一个测试文件可以包含任何数量的def test_*():形式的函数或形式的class test * (unittest.TestCase):。它们中的每一个都将作为测试运行,如果它们引发任何异常或断言,则测试失败。不要用nose.tools.assert_ *函数,因为我们目前试图迁移到nose2。对描述性消息使用assert语句

查看现有的测试作为示例。它们中的许多使用了另一种格式,其中test_*函数实际上是一个生成器,它生成要调用的函数元组及其参数,以便对测试进行简单的参数化。
最后,不要在测试函数中添加文档字符串。

你可能感兴趣的:(How to Contribute)