{ "info": { "author": "Adam Renberg", "author_email": "tgwizard@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Testing", "Topic :: System :: Logging", "Topic :: System :: Monitoring" ], "description": "# pythonrv\n\npythonrv is a runtime verification framework for python. It is the\nimplementation of a master thesis currently being written. Please see\n[tgwizard.github.com/thesis](http://tgwizard.github.com/thesis) for more\ninformation.\n\n[![Build Status](https://secure.travis-ci.org/tgwizard/pythonrv.png)](http://travis-ci.org/tgwizard/pythonrv)\n\n## Installing\n\npythonrv is available as a [pip package](http://pypi.python.org/pypi/pythonrv),\nwith setup.py and everything. Use\n[pip](http://www.pip-installer.org/en/latest/index.html) to install it like\nthis:\n\n pip install pythonrv\n\npythonrv has been tested with Python 2.7. It does not yet work with Python 3.\n\n## Coding\n\nClone the repository:\n\n git clone git@github.com:tgwizard/pythonrv.git\n cd pythonrv\n\nInitialize the environment (preferrably through\n[virtualenv](http://pypi.python.org/pypi/virtualenv), and possibly\n[virtualenvwrapper](http://www.doughellmann.com/docs/virtualenvwrapper/)):\n\n pip install -r requirements.txt\n\nRun the tests:\n\n ./runtests.sh\n\n\n## Runtime Verification\n\nRuntime verification is the idea of verifying the correctness of a program\nduring its execution. Verification is done by checking if the program conforms\nto its specifications.\n\nThere are several approaches to runtime verification, where pythonrv is one. It\nis based on the idea that the specifications should be written in the target\nprogramming language, in a similar manner to unit tests, and therefore make\nthem both more expressive and easier to learn.\n\nThe specifications written in pythonrv get \"baked\" into the target's source\ncode, so that whenever a function is called the specifications are executed as\nwell.\n\n## Examples\n\nHere are some simple examples showing the pythonrv API. For more realistic\nexamples, see the\n[examples](https://github.com/tgwizard/pythonrv/tree/master/examples) folder.\nThe [unit\ntests](https://github.com/tgwizard/pythonrv/tree/master/pythonrv/test) might\nalso be useful to see what works.\n\nFirst, lets say we have a function which correctness we want to verify:\n\n~~~ python\n# factorial.py\ndef factorial(n):\n res = 1\n for i in range(2, n)\n res *= i\n return res\n~~~\n\nWe can now write specifications for it, preferably in another file:\n\n~~~ python\n# rvspecs.py\nfrom pythonrv import rv\nimport factorial\n\n@rv.monitor(fact=factorial.factorial)\ndef input_only_spec(event):\n assert event.fn.fact.inputs[0] >= 0\n\n@rv.monitor(fact=factorial.factorial)\n@rv.spec(when=rv.POST)\ndef simple_specification(event):\n assert event.fn.fact.result >= event.fn.fact.inputs[0]\n\n@rv.monitor(fact=factorial.factorial)\n@rv.spec(when=rv.POST, history_size=rv.INFINITE_HISTORY_SIZE)\ndef simple_specification(event):\n in_out = (event.fn.fact.inputs[0], event.fn.fact.result)\n old_in_out = [(x.inputs[0], x.result) for x in event.fn.fact.history]\n\n for a in old_in_out:\n if in_out[0] > old_in_out[0]:\n assert in_out[1] >= old_in_out[1]\n~~~\n\nThe first specification checks that all inputs are greater-than-or-equal-to\nzero.\nThe second specification checks that all outputs are at least as big as the\ninputs. The third specification verifies the input/output against the\nhistorical data for the function; given a larger input, the output must be\nlarger-or-equal than before. Note that the last two specs are executed after\nthe factorial has been called. This gives them access to return values and\noutput arguments.\n\nA specification is sent an event with information of the called function, its\nhistory, and the history of all functions monitored by the specification.\n\n~~~ python\nfrom pythonrv import rv\nimport mymodule\n\n@rv.monitor(foo=mymodule.foo, bar=mymodule.bar)\n@rv.spec(when=rv.POST, history_size=20)\ndef more_specifications(event):\n # here are all functions monitored\n event.fn\n # the currently called function can be accessed like this\n event.called_function\n # which, if mymodule.foo was called, is the same as\n event.fn.foo\n\n # we can also check if a function was called\n assert event.fn.foo.called\n\n # the inputs, outputs and result can be accessed like this\n event.fn.foo.inputs # a copy of the input argument tuple\n event.fn.foo.input_kwargs # a copy of the input key-word argument dict\n event.fn.foo.outputs\n event.fn.foo.output_kwargs\n event.fn.foo.result\n\n # we can gain access to the previous event, and the previous function call\n event.prev\n event.fn.foo.prev\n\n # if two calls to mymodule.foo occurr consecutively\n assert event.prev.fn.foo == event.fn.foo.prev\n # but they needn't be if more than one function is monitored by a spec\n\n # we can gain access to the \"entire\" history\n for old_event in event.history:\n pass\n # and\n for old_foo_call in event.fn.foo.history:\n pass\n # this is obviously a big drain on the memory, so by default only two events\n # are stored in the history (this and the previous). this can be changed,\n # like we do here with @rv.spec(history_size=20)\n\n # we can also say that the next time some monitored function is called,\n # something should happen\n event.next(call_next_time)\n\n # or for just a specific monitored function\n event.fn.foo.next(call_next_time)\n\n # we can also specify which monitored function should be the next to be\n # called:\n event.next_called_should_be(event.fn.bar)\n\n # sometimes a specification can \"finish\" - it need not be verified again\n event.success(\"optional message telling that everything was ok\")\n # or\n event.failure(\"we've failed, and there's no point continuing this verification\")\n\ndef call_next_time(event):\n # here we gain access to all the same data as in the spec\n pass\n~~~\n\n## When to Execute the Specification\n\nThe specification is executed before the call is passed on to the active\nmonitoree. This can be customized:\n\n~~~ python\n@rv.monitor(f=func)\n@rv.spec(when=rv.POST)\ndef spec_after(event):\n pass\n\n# this is the default, it is not needed to explicitly specify PRE\n@rv.monitor(f=func)\n@rv.spec(when=rv.PRE)\ndef spec_before(event):\n pass\n~~~\n\n## Dealing with Errors\n\nSpecifications signal verifications errors by raising the `AssertionError`\nexception (which the `assert` statement does). When this happens, pythonrv, by\ndefault, lets this error propagate, and, if uncaught, the program stops.\n\nIf this is not the desired behaviour, it can be changed. For a logging error\nhandler, do\n\n~~~ python\nfrom pythonrv import rv\nrv.configure(error_handler=rv.LoggingErrorHandler())\n~~~\n\nand then configure logging the normal way, through the [python logging\nmodule](http://docs.python.org/library/logging.html).\n\nSpecifications can be marked with an error level:\n\n~~~ python\n@rv.monitor(f=func)\n@rv.spec(level=rv.DEBUG)\ndef spec(event):\n pass\n~~~\n\nThe available error levels are DEBUG, INFO, WARNING, ERROR and CRITICAL (just\nas in the logging module).\n\nFor more on error handling, see the [source\ncode](https://github.com/tgwizard/pythonrv/blob/master/pythonrv/rv.py), and the\n[unit\ntests](https://github.com/tgwizard/pythonrv/blob/master/pythonrv/test/rv_configuration_test.py).\n\n## Technical Issues\n\n### Importing\nIt is recommended that you import your rv specifications among the first things\nyou do in your program. The reasons will be detailed below.\n\nWhen writing specs, this is the **wrong** way:\n\n~~~ python\n# this doesn't work\nfrom mymodule import myfunc\n@rv.monitor(f=myfunc)\ndef spec(event):\n pass\n~~~\n\nThis is the correct way:\n\n~~~ python\n# this works\nimport mymodule\n@rv.monitor(f=mymodule.myfunc)\ndef spec(event):\n pass\n~~~\n\nThe first example creates a reference to myfunc and inserts it into the\ncurrent module (the module defining the specification). Monitoring a function\nmeans adding a wrapper around it, and in this case we only add a wrapper for\nthe myfunc reference in the current module. We do not modify mymodule, which\nall other code will use. The second example fixes this.\n\nThe above reason also explains why the specifications should be\nimported/defined at the very beginning of the execution: Other modules might\nuse the from x import y style, and if they do so before the rv specifications\nhave had a chance to monitor/instrument the functions, they will get\nunmonitored/uninstrumented references to them.\n\n### Copying arguments\n\nWhen intercepting function calls, pythonrv copies the arguments to make sure\nthat the history it stores doesn't get altered afterwards and/or from the\noutside. This also makes it so that the input arguments really are input\narguments, and not modified by the function itself.\n\nThis might sometimes be deemed unnecessary, or needlessly expensive. It might\nsometimes not even work, for instance when\n[`cStringIO`](http://docs.python.org/library/stringio.html) is involved. This\nis the case for [Django](https://www.djangoproject.com/) requests in v1.4.x\n(but not on master).\n\nCopying can be turned off for all specifications:\n\n~~~ python\nfrom pythonrv import rv\nrv.configure(enable_copy_args=False)\n~~~\n\nOr for a specific specification:\n\n~~~ python\nfrom pythonrv import rv\n@rv.monitor(func=somemodule.somefunc)\n@rv.spec(enable_copy_args=False)\ndef spec(event):\n pass\n~~~\n\nNote: Disabling argument copying for one specification actually disables\nargument copying for all monitored functions for that specification. Other\nspecifications that monitor the same functions won't get argument copying\neither. This is \"a feature\".\n\n## License\n\npythonrv is released under the [MIT\nlicense](http://opensource.org/licenses/mit-license.php).\n", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/tgwizard/pythonrv", "keywords": "runtime verification,rv,verification,testing,aspect-oriented programming", "license": "UNKNOWN", "maintainer": null, "maintainer_email": null, "name": "pythonrv", "package_url": "https://pypi.org/project/pythonrv/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/pythonrv/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/tgwizard/pythonrv" }, "release_url": "https://pypi.org/project/pythonrv/0.1.3/", "requires_dist": null, "requires_python": null, "summary": "A runtime verification framework", "version": "0.1.3" }, "last_serial": 798159, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "18f2c5eff9602536ace2cef0460a2fdc", "sha256": "f9ebf439d4b155b21f42d65c29c3860f58ee0bc62f2a8b0e46f26a769c6206a6" }, "downloads": -1, "filename": "pythonrv-0.1.tar.gz", "has_sig": false, "md5_digest": "18f2c5eff9602536ace2cef0460a2fdc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19692, "upload_time": "2012-09-20T12:29:22", "url": "https://files.pythonhosted.org/packages/21/95/eb014f026719b256ef8c3d1627e1f6e2ed4f075e16602182bfe29b3cc20e/pythonrv-0.1.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "2b76033fed902877b07444d81e5009a9", "sha256": "29f07aa833a89a6e5d408f5994f3d46145e06fbe864c9be507b72d8b49142560" }, "downloads": -1, "filename": "pythonrv-0.1.1.tar.gz", "has_sig": false, "md5_digest": "2b76033fed902877b07444d81e5009a9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20812, "upload_time": "2012-09-25T08:24:29", "url": "https://files.pythonhosted.org/packages/9b/74/e30670e0a4648f248dabf98d88a4ca0f0841b0bd071bcbe483201b3e4a00/pythonrv-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "605030174bcb2c3b1303dbbfaaa94b1b", "sha256": "f083de19692bba8aa9252efb5ce97b8869b4009fa33073e4acdc60e6df44a70f" }, "downloads": -1, "filename": "pythonrv-0.1.2.tar.gz", "has_sig": false, "md5_digest": "605030174bcb2c3b1303dbbfaaa94b1b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23595, "upload_time": "2012-11-20T08:02:46", "url": "https://files.pythonhosted.org/packages/51/da/4ae5d4ca2f41dc6bbc017efbc5198e13af53405581e34647f235f5df786c/pythonrv-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "7f0051bb405ca665a1b46592f42e42e3", "sha256": "0f7d5560ab5e6b88810bb03ffd145a20744397a5983d724f96a0f753cc616bf6" }, "downloads": -1, "filename": "pythonrv-0.1.3.tar.gz", "has_sig": false, "md5_digest": "7f0051bb405ca665a1b46592f42e42e3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23597, "upload_time": "2012-11-20T08:06:52", "url": "https://files.pythonhosted.org/packages/3e/b0/8fc644512461ccd40b8ccb767fe00cd0933bf634067249c9c1dd173d743a/pythonrv-0.1.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "7f0051bb405ca665a1b46592f42e42e3", "sha256": "0f7d5560ab5e6b88810bb03ffd145a20744397a5983d724f96a0f753cc616bf6" }, "downloads": -1, "filename": "pythonrv-0.1.3.tar.gz", "has_sig": false, "md5_digest": "7f0051bb405ca665a1b46592f42e42e3", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 23597, "upload_time": "2012-11-20T08:06:52", "url": "https://files.pythonhosted.org/packages/3e/b0/8fc644512461ccd40b8ccb767fe00cd0933bf634067249c9c1dd173d743a/pythonrv-0.1.3.tar.gz" } ] }