{ "info": { "author": "Rob deCarvalho", "author_email": "not_listed@nothing.net", "bugtrack_url": null, "classifiers": [ "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5" ], "description": "Behold: A debugging tool for large Python projects\n===\nBehold is a package that makes it easier to debug large Python projects. It\nenables you to perform [contextual debugging](#contextual-debugging-explained)\nover your entire code base. This means that you can use the state inside one\nmodule to control either printing or step-debugging in a completely different\nmodule. Given the stateful nature of many large, multi-file applications (I'm\nlooking at you, Django), this capability provides valuable control over your\ndebugging work flow.\n\nBehold is written in pure Python with no dependencies. It is compatible with\nboth Python2 and Python3.\n\nThis page shows several examples to get you started. The\nAPI documentation can be found here.\n\n\nInstallation\n---\n```bash\npip install behold\n```\n\nTable of Contents\n---\n\n* [API Documentation](http://behold.readthedocs.io/en/latest/ref/behold.html)\n* [Simple print-style debugging](#simple-print-style-debugging)\n* [Conditional printing](#conditional-printing)\n* [Tagged printing](#tagged-printing)\n* [Contextual debugging](#contextual-debugging-explained)\n* [Printing object attributes](#printing-object-attributes)\n* [Printing global variables and nested attributes](#printing-global-variables-and-nested-attributes)\n* [Stashing results](#stashing-results)\n* [Custom attribute extraction](#custom-attribute-extraction)\n\n\nSimple Print-Style Debugging\n---\nBehold provides a uniform look to your print-style debugging statements.\n```python\nfrom behold import Behold\n\nletters = ['a', 'b', 'c', 'd', 'A', 'B', 'C', 'D']\n\nfor index, letter in enumerate(letters):\n # The following line is equivalent to\n # print('index: {}, letter: {}'.format(index, letter))\n Behold().show('index', 'letter')\n```\nOutput:\n```\nindex: 0, letter: a\nindex: 1, letter: b\nindex: 2, letter: c\nindex: 3, letter: d\nindex: 4, letter: A\nindex: 5, letter: B\nindex: 6, letter: C\nindex: 7, letter: D\n```\n\nConditional Printing\n---\nYou can filter your debugging statements based on scope variables.\n```python\nfrom behold import Behold\n\nletters = ['a', 'b', 'c', 'd', 'A', 'B', 'C', 'D']\n\nfor index, letter in enumerate(letters):\n # The following line is equivalent to\n # if letter.upper() == letter and index % 2 == 0:\n # print('index: {}'.format(index))\n Behold().when(letter.upper() == letter and index % 2 == 0).show('index')\n\n # If you don't like typing, the Behold class is aliased to B\n # from behold import B # this also works\n```\nOutput:\n```\nindex: 4\nindex: 6\n```\n\nTagged Printing\n---\nEach instance of a behold object can be tagged to produce distinguishable\noutput. This makes it easy to grep for specific output you want to see.\n```python\nfrom behold import Behold\n\nletters = ['a', 'b', 'c', 'd', 'A', 'B', 'C', 'D']\n\nfor index, letter in enumerate(letters):\n # The following two lines of code are equivalent to\n # if letter.upper() == letter and index % 2 == 0:\n # print('index: {}, letter:, {}, even_uppercase'.format(index, letter))\n # if letter.upper() != letter and index % 2 != 0:\n # print('index: {}, letter: {} odd_lowercase'.format(index, letter))\n Behold(tag='even_uppercase').when(letter.upper() == letter and index % 2 == 0).show('index', 'letter')\n Behold(tag='odd_lowercase').when(letter.lower() == letter and index % 2 != 0).show('index', 'letter')\n\n```\nOutput:\n```\nindex: 1, letter: b, odd_lowercase\nindex: 3, letter: d, odd_lowercase\nindex: 4, letter: A, even_uppercase\nindex: 6, letter: C, even_uppercase\n```\n\nContextual Debugging Explained\n---\nLet's say you have a complicated code base consisting of many files spread over\nmany directories. In the course of chasing down bugs, you may want to print out\nwhat is going on inside a particular function. But you only want the printing\nto happen when that function is called from some other function defined in a\ncompletely different file. Situations like this frequently arise in Django web\nprojects where the code can be spread across multiple apps. This is the use\ncase where Behold really shines. Here is a simple example.\n\nSay you want to debug a reusable function somewhere in one of your modules.\n```python\nfrom behold import Behold\n\n# Some function that is used everywhere in your code base\ndef my_function():\n x = 'hello' # your complicated logic goes here\n\n # This will print the value of x, but only when in 'testing' context\n Behold().when_context(what='testing').show('x')\n\n # This will drop into a step debugger only when in 'debugging' context\n if Behold().when_context(what='debugging').is_true():\n import pdb; pdb.set_trace()\n```\n\nNow, from a completely different module somewhere else in your project, you can\ncontrol how your function gets debugged.\n```python\nfrom behold import in_context\n\n# Decorate your testing function to execute in a 'testing' context\n@in_context(what='testing')\ndef test_x():\n my_function()\ntest_x() # This will print 'x: hello' to your console\n\n# Use a context manager to set a debugging context\nwith in_context(what='debugging'):\n my_function() # This will drop you into the pdb debugger.\n\n```\n\n\nPrinting Object Attributes\n---\nUp to this point, we have only called the `.show()` method with string arguments\nholding names of local variables. What if we wanted to show attributes of some\nobject in our code? The example below uses an instance of the\n\nItem class\n\n\n```python\nfrom behold import Behold, Item\n\n# Define an item with three attributes.\nitem = Item(a=1, b=2, c=3)\n\n# The show() method will accept up to one non-string argument. If it detects that\n# that a non-string argument has been passed, it will call getattr() on the\n# non-string variable to display the str representation of the attributes listed\n# in the string arguments.\nBehold(tag='with_args').show(item, 'a', 'b')\n\n# Calling show with an object and no string arguments defaults to printing all\n# attributes in the object's __dict__.\nBehold(tag='no_args').show(item)\n```\nOutput:\n```\na: 1, b: 2, with_args\na: 1, b: 2, c: 3, no_args\n```\n\nPrinting Global Variables and Nested Attributes\n---\nWhen providing string arguments to the `.show()` method, the default behavior is\nto examine the local variables for names matching the strings. Global variables\ncan not be accessed in this way. Furthermore, if you have classes with nested\nattributes, those will also not be accessible with simple string arguments.\nThis example illustrates how to use `.show()` to access these types of\nvariables.\n\n```python\nfrom __future__ import print_function\nfrom behold import Behold, Item\n\n# define a global variable\ng = 'global_content'\n\n# Now set up nested a nested function to create a closure variable\ndef example_func():\n employee = Item(name='Toby')\n boss = Item(employee=employee, name='Michael')\n\n print('# Can\\'t see global variable')\n Behold().show('boss', 'employee', 'g')\n\n print('\\n# I can see the the boss\\'s name, but not employee name')\n Behold('no_employee_name').show(boss)\n\n print('\\n# Here is how to show global variables')\n Behold().show(global_g=g, boss=boss)\n\n # Or if you don't like the ordering the dict keys give you,\n # you can enforce it with the order of some string arguments\n print('\\n# You can force variable ordering by supplying string arguments')\n Behold().show('global_g', 'boss', global_g=g, boss=boss)\n\n print('\\n# And a similar strategy for nested attributes')\n Behold().show(employee_name=boss.employee.name)\n\nexample_func()\n```\nOutput:\n```bash\n# Can't see global variable\nboss: Item('employee', 'name'), employee: Item('name'), g: None\n\n# I can see the the boss's name, but not employee name\nemployee: Item('name'), name: Michael, no_employee_name\n\n# Here is how to show global variables\nboss: Item('employee', 'name'), global_g: global_content\n\n# You can force variable ordering by supplying string arguments\nglobal_g: global_content, boss: Item('employee', 'name')\n\n# And a similar strategy for nested attributes\nemployee_name: Toby\n```\n\nStashing Results\n---\nBehold provides a global stash space where you can store observed values for\nlater use in a top-level summary. The stash space is global, so you need to\ncarefully manage it in order not to confuse yourself. Here is an example of\nusing the stash feature to print summary info. The list of dicts returned by the\n`.get_stash()` function was specifically designed to be passed directly to a Pandas Dataframe constructor to help\nsimplify further analysis. \n\n```python\nfrom __future__ import print_function\nfrom pprint import pprint\nfrom behold import Behold, in_context, get_stash, clear_stash\n\ndef my_function():\n out = []\n for nn in range(5):\n x, y, z = nn, 2 * nn, 3 * nn\n out.append((x, y, z))\n\n # You must define tags if you want to stash variables. The tag\n # names become the keys in the global stash space\n\n # this will only populate when testing x\n Behold(tag='test_x').when_context(what='test_x').stash('y', 'z')\n\n # this will only populate when testing y\n Behold(tag='test_y').when_context(what='test_y').stash('x', 'z')\n\n # this will only populate when testing z\n Behold(tag='test_z').when_context(what='test_z').stash('x', 'y')\n return out\n\n\n@in_context(what='test_x')\ndef test_x():\n assert(sum([t[0] for t in my_function()]) == 10)\n\n@in_context(what='test_y')\ndef test_y():\n assert(sum([t[1] for t in my_function()]) == 20)\n\n@in_context(what='test_z')\ndef test_z():\n assert(sum([t[2] for t in my_function()]) == 30)\n\ntest_x()\ntest_y()\ntest_z()\n\n\nprint('\\n# contents of test_x stash. Notice only y and z as expected')\npprint(get_stash('test_x'))\n\nprint('\\n# contents of test_y stash. Notice only x and z as expected')\npprint(get_stash('test_y'))\n\nprint('\\n# contents of test_z stash. Notice only x and y as expected')\npprint(get_stash('test_z'))\n\n# With no arguments, clear_stash will delete all stashes. You can\n# select a specific set of stashes to clear by supplying their names.\nclear_stash()\n```\nOutput:\n```\n\n# contents of test_x stash. Notice only y and z as expected\n[{'y': 0, 'z': 0},\n{'y': 2, 'z': 3},\n{'y': 4, 'z': 6},\n{'y': 6, 'z': 9},\n{'y': 8, 'z': 12}]\n\n# contents of test_y stash. Notice only x and z as expected\n[{'x': 0, 'z': 0},\n{'x': 1, 'z': 3},\n{'x': 2, 'z': 6},\n{'x': 3, 'z': 9},\n{'x': 4, 'z': 12}]\n\n# contents of test_z stash. Notice only x and y as expected\n[{'x': 0, 'y': 0},\n{'x': 1, 'y': 2},\n{'x': 2, 'y': 4},\n{'x': 3, 'y': 6},\n{'x': 4, 'y': 8}]\n```\n\nCustom Attribute Extraction\n---\nWhen working with database applications, you frequently encounter objects that\nare referenced by id numbers. These ids serve as record keys from which you can\nextract human-readable information. When you are debugging, it can often get\nconfusing if your screen dump involves just a bunch of id numbers. What you\nwould actually like to see is some meaningful name corresponding to that id. By\nsimply overriding one method of the Behold class, this behavior is quite easy to\nimplement. This example shows how.\n```python\nfrom __future__ import print_function\nfrom behold import Behold, Item\n\n\n# Subclass Behold to enable custom attribute extraction\nclass CustomBehold(Behold):\n @classmethod\n def load_state(cls):\n # Notice this is a class method so that the loaded state will be\n # available to all instances of CustomBehold. A common use case would\n # be to load state like this once from a database and then be able to\n # reuse it at will without invoking continual database activity. In\n # this example, imagine the numbers are database ids and you have \n # constructed a mapping from id to some human-readable description.\n cls.name_lookup = {\n 1: 'John',\n 2: 'Paul',\n 3: 'George',\n 4: 'Ringo'\n }\n cls.instrument_lookup = {\n 1: 'Rhythm Guitar',\n 2: 'Bass Guitar',\n 3: 'Lead Guitar',\n 4: 'Drums'\n }\n\n def extract(self, item, name):\n \"\"\"\n I am overriding the extract() method of the behold class. This method\n is responsible for taking an object and turning it into a string. The\n default behavior is to simply call str() on the object.\n \"\"\"\n # if the lookup state hasn't been loaded, do so now.\n if not hasattr(self.__class__, 'name_lookup'):\n self.__class__.load_state()\n\n # extract the value from the behold item\n val = getattr(item, name)\n\n # If this is a Item object, enable name translation\n if isinstance(item, Item) and name == 'name':\n return self.__class__.name_lookup.get(val, None)\n\n # If this is a Item object, enable instrument translation\n elif isinstance(item, Item) and name == 'instrument':\n return self.__class__.instrument_lookup.get(val, None)\n\n # otherwise, just call the default extractor\n else:\n return super(CustomBehold, self).extract(item, name)\n\n\n# define a list of items where names and instruments are given by id numbers\nitems = [Item(name=nn, instrument=nn) for nn in range(1, 5)]\n\nprint('\\n# Show items using standard Behold class')\nfor item in items:\n Behold().show(item)\n\n\nprint('\\n# Show items using CustomBehold class with specialized extractor')\nfor item in items:\n CustomBehold().show(item, 'name', 'instrument')\n```\nOutput:\n```bash\n# Show items using standard Behold class\ninstrument: 1, name: 1\ninstrument: 2, name: 2\ninstrument: 3, name: 3\ninstrument: 4, name: 4\n\n# Show items using CustomBehold class with specialized extractor\nname: John, instrument: Rhythm Guitar\nname: Paul, instrument: Bass Guitar\nname: George, instrument: Lead Guitar\nname: Ringo, instrument: Drums\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/robdmc/behold", "keywords": "", "license": "MIT", "maintainer": null, "maintainer_email": null, "name": "behold", "package_url": "https://pypi.org/project/behold/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/behold/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/robdmc/behold" }, "release_url": "https://pypi.org/project/behold/0.2.0/", "requires_dist": null, "requires_python": null, "summary": "UNKNOWN", "version": "0.2.0" }, "last_serial": 2537184, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "af89e73972176464cfdde9b35be631ef", "sha256": "6121c259468a0e265523b73f1af5e06be2722bbf7efdabe05c07ad18370408c4" }, "downloads": -1, "filename": "behold-0.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "af89e73972176464cfdde9b35be631ef", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 3245, "upload_time": "2016-12-08T01:46:18", "url": "https://files.pythonhosted.org/packages/32/be/e19549713affb3c297bb171b1d93bf72dc138295d4221d99890d43cb46db/behold-0.0.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "f752ebcee97ab561111713b3256b26c6", "sha256": "28adc7aec05b921e52c3c10b2c31dc211e5fa7e57b2bcccdbfc9b4b62eeb4397" }, "downloads": -1, "filename": "behold-0.0.1.tar.gz", "has_sig": false, "md5_digest": "f752ebcee97ab561111713b3256b26c6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 2848, "upload_time": "2016-12-08T01:46:16", "url": "https://files.pythonhosted.org/packages/a2/a5/d2a66108fc4890e7f340e12c924341b1e09979ba1854b262fa0ab95de1d3/behold-0.0.1.tar.gz" } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "9dbb575f142835e8e012794dbcb466a3", "sha256": "874b1e4b9d9eb63f4dbfae14b033a3b3582fc16ec6043a072db90e940b70febf" }, "downloads": -1, "filename": "behold-0.0.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "9dbb575f142835e8e012794dbcb466a3", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 9493, "upload_time": "2016-12-12T03:14:52", "url": "https://files.pythonhosted.org/packages/0f/52/1426fa9b591cf135017fab3d9778a55115da266058feaab52e3918ccfced/behold-0.0.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e504d222d6fcc6f33f40645537b2a657", "sha256": "fc9149b2ce5aea10dedfe84c98d27c1f78adbdb00ee52ca7433df17c24b59d8c" }, "downloads": -1, "filename": "behold-0.0.2.tar.gz", "has_sig": false, "md5_digest": "e504d222d6fcc6f33f40645537b2a657", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6314, "upload_time": "2016-12-12T03:14:50", "url": "https://files.pythonhosted.org/packages/a9/eb/05dd9b8a4a0db8b983031f950bb901f9f4ea7cad381b587e6fc9a36e602c/behold-0.0.2.tar.gz" } ], "0.1.0": [ { "comment_text": "", "digests": { "md5": "6a7ef0876fcf707b7d03afae6016ea9f", "sha256": "9821c9d666a71fee5262c00177ae2652b1274995ceac82eb8be124f15bbe5e84" }, "downloads": -1, "filename": "behold-0.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "6a7ef0876fcf707b7d03afae6016ea9f", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 18141, "upload_time": "2016-12-13T16:50:23", "url": "https://files.pythonhosted.org/packages/5c/c5/8dab74c9a6fe3622e2869360f0a827cb544b0b147089ff0a3388bd382fd6/behold-0.1.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0af2b062f9824f8715daf16b891c148e", "sha256": "bba837d8ff4a5552643d833b1652ab1015fd6f931da9e0ccd861345e9e0f7721" }, "downloads": -1, "filename": "behold-0.1.0.tar.gz", "has_sig": false, "md5_digest": "0af2b062f9824f8715daf16b891c148e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10879, "upload_time": "2016-12-13T16:50:21", "url": "https://files.pythonhosted.org/packages/e2/ae/dc7edd2ca4e199f2c54be6c786d73e5f79e12b1cdbf7995cd5ad65e032a1/behold-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "338689133807566d7bc9ff0dfc8534ad", "sha256": "66a7eba52b48a52cb093ea15a68109056ed7fe9103bdea35292b36c0d6c3fa46" }, "downloads": -1, "filename": "behold-0.1.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "338689133807566d7bc9ff0dfc8534ad", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 18224, "upload_time": "2016-12-13T18:47:54", "url": "https://files.pythonhosted.org/packages/2d/32/6e5f5f91d014edf3066583233cbd285579dc7f88262d16b97bd8e163a25f/behold-0.1.1-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "1b22aeb65c6dcbd19daffff1bdf9f2ec", "sha256": "b5e198feead34cc2e5bb669400d186e3ee7d63181ec4183dc47cd4b2ac585c6e" }, "downloads": -1, "filename": "behold-0.1.1.tar.gz", "has_sig": false, "md5_digest": "1b22aeb65c6dcbd19daffff1bdf9f2ec", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10916, "upload_time": "2016-12-13T18:47:53", "url": "https://files.pythonhosted.org/packages/59/46/e632a8504b4f42080b1c59fca7a572a42399c1c193efbce8d6fb6d31ff53/behold-0.1.1.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "3c915c0c920dbdb56f85df626da5e6e7", "sha256": "934c3744b6f9e1d5a35b2166da30566c9be2fea887840664d3514fd314a7106b" }, "downloads": -1, "filename": "behold-0.2.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "3c915c0c920dbdb56f85df626da5e6e7", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 23430, "upload_time": "2016-12-23T19:35:57", "url": "https://files.pythonhosted.org/packages/28/ef/02c08725b425e2217f89a767ed8237d0550edb3aca7f18a435808e4efdef/behold-0.2.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3ec522a931f7827452e7f2f7645c124a", "sha256": "333462dfc825557532800d4edd4949e6228de8c6eb93d5003716cb258fd563a7" }, "downloads": -1, "filename": "behold-0.2.0.tar.gz", "has_sig": false, "md5_digest": "3ec522a931f7827452e7f2f7645c124a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14912, "upload_time": "2016-12-23T19:35:55", "url": "https://files.pythonhosted.org/packages/9b/c3/6142c4cfed91d9eefa155e1d69241582f87ff3290fe163cf8460b7905d51/behold-0.2.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "3c915c0c920dbdb56f85df626da5e6e7", "sha256": "934c3744b6f9e1d5a35b2166da30566c9be2fea887840664d3514fd314a7106b" }, "downloads": -1, "filename": "behold-0.2.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "3c915c0c920dbdb56f85df626da5e6e7", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 23430, "upload_time": "2016-12-23T19:35:57", "url": "https://files.pythonhosted.org/packages/28/ef/02c08725b425e2217f89a767ed8237d0550edb3aca7f18a435808e4efdef/behold-0.2.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3ec522a931f7827452e7f2f7645c124a", "sha256": "333462dfc825557532800d4edd4949e6228de8c6eb93d5003716cb258fd563a7" }, "downloads": -1, "filename": "behold-0.2.0.tar.gz", "has_sig": false, "md5_digest": "3ec522a931f7827452e7f2f7645c124a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14912, "upload_time": "2016-12-23T19:35:55", "url": "https://files.pythonhosted.org/packages/9b/c3/6142c4cfed91d9eefa155e1d69241582f87ff3290fe163cf8460b7905d51/behold-0.2.0.tar.gz" } ] }