{ "info": { "author": "runsun", "author_email": "runsun@gmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "# pyTreeLog: tree-like logging tool for python\n\nThis tool allows python users to log entries in **layers of blocks** that are displayed in a **tree-like** structure:\n\n```\n-start \n.-----------------------\n|* f(x=\"test\")\n| -x = test\n| .-----------------------\n| :* Start loop:\n| : -i=0\n| : -i=1\n| : -i=2\n| :* end loop\n| '-----------------------\n'-----------------------\n-done\n``` \n\nFeatures:\n\n* Displayed python logging entries in **tree-like** structure;\n* Easily log the entry/exit of a function;\n* Output can be displayed in console or saved to a variable or a file\n\nNote: this `README.md` file is both a doc and the source of doctest (see section **Test** for details).\n\n\n## Installation \n\nSimply \n\n`pip install pytreelog`\n\nOr for likely the most updated one:\n\n`pip install git+https://gitlab.com/runsun/pytreelog.git`\n\nOr, if you don't want to have this doc and don't want to do tests, you can simmply \ncopy the file `pytreelog/pytreelog.py` to wherever you desire. \n\n\n## Quick Preview\n\n```python\nfrom pytreelog.pytreelog import TreeLog \ntl = TreeLog()\ntl(x) # log x\n\n2 block types: *user block* and *func block* (See `Decorator Approach` below). \n\ntl.b(x) # Beging a new *user block*. 'x' is optional. If not given, \n # x will be assigned the function call arg values. \n\ntl.e(x) # End the current block. 'x' is optional. If x not given\n # for a function that return something, it will show the\n # returned value\n\ntl.data # list of lines of the logged data\ntl.text() # returns the data as a string\ntl.reset() # reset the data and block info to start anew \n```\n\n## Turn on/off\n\nYou can turn logging on/off with either :\n\n```python\ntl.on()\ntl.off()\n```\n\nor \n\n```python\ntl._on = True\ntl._on = False\n```\n\ndefault: on\n\n## Basic usage \n\n```\n>>> from pytreelog.pytreelog import TreeLog\n>>> tl = TreeLog()\n>>> def f(x):\n... tl.b('BEG') ## <== New block\n... tl(\"test line \"+str(x)) ## <== Any string\n... tl.e('END') ## <== End of a block\n>>> f(1) \n\n>>> print(tl.text()) # doctest: +NORMALIZE_WHITESPACE \n=============================================\nTreeLog output\n=============================================\n.-----------------------\n|* BEG\n| -test line 1\n|* END\n'-----------------------\n\n``` \n\n#### Beginning/End of a block : `tl.b()`, `tl.e()`\n\n```\n>>> def f(x):\n... tl.b()\n... tl(\"test line \"+str(x))\n... tl.e()\n>>> f(2) \n\n>>> print(tl.text()) # doctest: +NORMALIZE_WHITESPACE \n=============================================\nTreeLog output\n=============================================\n.-----------------------\n|* BEG\n| -test line 1\n|* END\n'-----------------------\n.-----------------------\n|* f(x=2)\n| -test line 2\n'----------------------- \n\n```\n\nNote: `tl.b()` logs function name and input value, `tl.e()` logs nothing. Also note that the output is appended to previous result. \n\nAnother example:\n\n```\n>>> tl.reset() \n>>> tl('start test') ## log a single line \n>>> def f(x):\n... a=0\n... tl.b() ## Begin a block, logs func name and input value(s)\n... tl('x = '+str(x)) ## Log a line\n... tl.b('Starting loop:') ## Begin of another block\n...\n... for i in range(3):\n... tl('i='+str(i)) ## Log i\n...\n... tl.e('end loop') ## End of block\n... tl.e() ## End of block\n>>> f('test')\n>>> tl('done')\n\n>>> print(tl.text()) # doctest: +NORMALIZE_WHITESPACE\n=============================================\nTreeLog output\n=============================================\n-start test\n.-----------------------\n|* f(x=\"test\")\n| -x = test\n| .-----------------------\n| :* Starting loop:\n| : -i=0\n| : -i=1\n| : -i=2\n| :* end loop\n| '-----------------------\n'-----------------------\n-done\n\n``` \n\n\n## Decorator Approach\n\nThe logging approach below to indicate a function block:\n\n```python\ndef ff(): \n tl.b()\t \n ...\n tl.e() \n```\n\ncan be done in a *Decorator Aproach* by using the TreeLog instance, `tl`, as the decorator to track the entry/exit of the function `ff` below: \n\n```python\n@tl\ndef ff(): \n ...\n```\n\nThis allows:\n\n* Automatically log the entry and exit point of a function;\n* Log the return value of a function: \n\n#### Log function return \n\nThe *Decorator Approach* automatically logs the return of a function: \n\n```\n>>> tl.reset()\n>>> @tl\n... def test(a,b=0):\n... tl.b('User-block beg')\n... tl('Inside test()')\n... tl.e('User-block end')\n... return a+b\n>>> test(3,4)\n7\n\n>>> print(tl.text()) # doctest: +NORMALIZE_WHITESPACE\n=============================================\nTreeLog output\n=============================================\n.-----------------------\n|> test(a=3, b=4)\n| .-----------------------\n| :* User-block beg\n| : -Inside test()\n| :* User-block end\n| '-----------------------\n|< 7\n'-----------------------\n\n```\n\nNote that, with *Decorator Arrpoach*, the symbols of *function block head/tail* (i.e., `>` and `<`) are different from those logged with `tl.b()` and `tl.e()` (i.e., `*`). This allows the log to tell *function block* and *user-defined block* apart:\n\n* *function block*: `>` and `<`\n* *user-defined block*: `*` and `*`\n\nBoth of them can be customized (See **Customizable API** below).\n\n\n#### Decorator Approach for one-time use\n\nIf just want to track nothing but entry/exit of a function, use an unbound instance for one-time use: \n\n```python\n>>> @TreeLog()\n... def test3():\n... ... \n\n``` \n\nMore example:\n\n``` \n>>> tl.reset()\n>>> @tl\n... def test(a,b=0):\n... tl.l('Inside test()')\n... @tl\n... def inside(a,b):\n... return a+b\n... return inside(a,b)*2\n>>> test(3,4)\n14\n\n>>> print(tl.text()) # doctest: +NORMALIZE_WHITESPACE\n=============================================\nTreeLog output\n=============================================\n.-----------------------\n|> test(a=3, b=4)\n| -Inside test()\n| .-----------------------\n| :> a.inside(b=4)\n| :< 7\n| '-----------------------\n|< 14\n'-----------------------\n\n```\n#### Use decorator approach in classes\n\nExample of using `TreeLog` in classes: \n\n```\n>>> log= TreeLog()\n>>> class Cls(object):\n... @log\n... def __init__(self): \n... self.clsname = 'CLS'\n... self.setname( prefix='I am ') ## run inside __init__\n... @log\n... def setname(self, prefix): \n... self.name= prefix + self.clsname\n... @log \n... def getname(self): \n... return self.name\n\n>>> cc = Cls() ## run __init__, logging __init__ and setname. \n>>> cc.getname()\n'I am CLS'\n\n>>> cc.setname('This is ') ## run outside __init__. \n ## Note the user arg ('This is ') is logged.\n>>> cc.getname()\n'This is CLS'\n\n>>> print(BR.join( log.data )) # doctest: +NORMALIZE_WHITESPACE\n=============================================\nTreeLog output\n=============================================\n.-----------------------\n|> self.__init__()\n| .-----------------------\n| :> self.setname(prefix=\"I am \")\n| '-----------------------\n'-----------------------\n.-----------------------\n|> cc.getname()\n|< \"I am CLS\"\n'-----------------------\n.-----------------------\n|> cc.setname(prefix=\"This is \")\n'-----------------------\n.-----------------------\n|> cc.getname()\n|< \"This is CLS\"\n'-----------------------\n\n```\n\nNote that:\n\n1. instance name, cc, is logged;\n2. setname is logged twice, one bound to self and the other to cc. \n3. getname has a return, which is logged, too. \n\n\n\n## Output\n\n`tl.data` is a list containing the logged strings. `tl.text()` returns a single string. \nYou can also log to external variable/file :\n\n#### Log to an external variable \n\nUse the `external` argument for `TreeLog` to save output to a variable, which\nis a dict contains a `data` key, `{'data':[]}`:\n\n```\n>>> external={'data':[]} \n>>> p = TreeLog(external=external)\n>>> p.b('start') \n>>> @p\n... def g(name):\n... p.l('test')\n>>> g('g')\n>>> p.e('stop')\n\n>>> for x in external['data']: print(x) # doctest: +NORMALIZE_WHITESPACE\n=============================================\nTreeLog output\n=============================================\n.-----------------------\n|* start\n| .-----------------------\n| :> g(name=\"g\")\n| : -test\n| '-----------------------\n|* stop\n'-----------------------\n\n```\n\n#### Log to a file \n\nYou can log to a file: \n\n```python\npp= TreeLog(logfile='./pytreelog.log')\n``` \n\n\n## Customizable API\n\nDefaults of the `TreeLog()` module:\n\n```python\n TreeLog( header = '='*45+BR+'TreeLog output'+ BR+'='*45\n , _on = True\n , block_beg = '* '\n , block_end = '* '\n , func_beg = '> '\n , func_end = '< '\n , indent = 1\n , blockline = '-----------------------'\n , blocksymbols= '|:' \n , external = None # External var ( {'data':[]} ) to which the data is logged\n , logfile = ''\n )\n```\n\n`blocksymbols`: a str containing symbols of the left-border of all layers. The default is `|:`, means the symbols of left-border will be `|` and `:`, alternatively. \n\nThe following example shows alternative symbols of 3 (i.e., `[=!`):\n\n```\n>>> tl= TreeLog(blocksymbols= '[=!')\n>>> @tl\n... def f():\n... tl.b('1st layer')\n... for i in (1,2):\n... tl.b('2nd layer')\n... for j in (3,4):\n... tl.b('3rd layer') \n... for k in (5,6):\n... tl( str( (i,j,k) ) )\n... tl.e()\n... tl.e()\n... tl.e()\n>>> f()\n\n>>> print(BR.join( tl.data )) # doctest: +NORMALIZE_WHITESPACE\n=============================================\nTreeLog output\n=============================================\n.-----------------------\n[> f()\n[ .-----------------------\n[ =* 1st layer\n[ = .-----------------------\n[ = !* 2nd layer\n[ = ! .-----------------------\n[ = ! [* 3rd layer\n[ = ! [ -(1, 3, 5)\n[ = ! [ -(1, 3, 6)\n[ = ! '-----------------------\n[ = ! .-----------------------\n[ = ! [* 3rd layer\n[ = ! [ -(1, 4, 5)\n[ = ! [ -(1, 4, 6)\n[ = ! '-----------------------\n[ = '-----------------------\n[ = .-----------------------\n[ = !* 2nd layer\n[ = ! .-----------------------\n[ = ! [* 3rd layer\n[ = ! [ -(2, 3, 5)\n[ = ! [ -(2, 3, 6)\n[ = ! '-----------------------\n[ = ! .-----------------------\n[ = ! [* 3rd layer\n[ = ! [ -(2, 4, 5)\n[ = ! [ -(2, 4, 6)\n[ = ! '-----------------------\n[ = '-----------------------\n[ '-----------------------\n'-----------------------\n\n```\n\n\n## Test\n\nAll tests are written in this `README.md` (which means, this `README.md` is both a doc and tests). The content of this file is loaded by `pytreelog.test()` and assigned to `TreeLog.__doc__` then tests are carried out by `doctest.testmod()` (See the code in `pytreelog.test()` for details). \n\n```python\n\\>>> from pytreelog import pytreelog\n\\>>> pytreelog.test()\n--- Loading \"C:\\python37\\lib\\site-packages\\pytreelog\\README.md\" for doctest:\n--- Tests done.\n```\n\n## License\n\n[MIT](https://opensource.org/licenses/MIT)\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://gitlab.com/runsun/pytreelog", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "pytreelog", "package_url": "https://pypi.org/project/pytreelog/", "platform": "", "project_url": "https://pypi.org/project/pytreelog/", "project_urls": { "Homepage": "https://gitlab.com/runsun/pytreelog" }, "release_url": "https://pypi.org/project/pytreelog/194rc0.post1/", "requires_dist": null, "requires_python": "", "summary": "Tree-like logging util for python", "version": "194rc0.post1" }, "last_serial": 5135746, "releases": { "194rc0.post1": [ { "comment_text": "", "digests": { "md5": "a58abe0b8ffb738a8ce34cfde6855988", "sha256": "9d53da8e1eabe537c251ba3ac55d8fb4d1710f870d18caadb5f2c9dbdaa78f09" }, "downloads": -1, "filename": "pytreelog-194rc0.post1-py3-none-any.whl", "has_sig": false, "md5_digest": "a58abe0b8ffb738a8ce34cfde6855988", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17538, "upload_time": "2019-04-12T21:29:19", "url": "https://files.pythonhosted.org/packages/32/e9/73d83a7e26e99fd668fb52dba334f065e8346b513a57fd9f830568e6f209/pytreelog-194rc0.post1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ea9fd89270d293c3ef3693e43f2ba4d8", "sha256": "6c82d160a60804c749c696fa4ebca94e0f76aba069adf8dd953e40dc9d58decc" }, "downloads": -1, "filename": "pytreelog-194rc0.post1.tar.gz", "has_sig": false, "md5_digest": "ea9fd89270d293c3ef3693e43f2ba4d8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18208, "upload_time": "2019-04-12T21:29:21", "url": "https://files.pythonhosted.org/packages/e5/e5/764390ab18b0c33399a42dc6bf3fe4f4ebf411774a68e8be10c93149dc47/pytreelog-194rc0.post1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "a58abe0b8ffb738a8ce34cfde6855988", "sha256": "9d53da8e1eabe537c251ba3ac55d8fb4d1710f870d18caadb5f2c9dbdaa78f09" }, "downloads": -1, "filename": "pytreelog-194rc0.post1-py3-none-any.whl", "has_sig": false, "md5_digest": "a58abe0b8ffb738a8ce34cfde6855988", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 17538, "upload_time": "2019-04-12T21:29:19", "url": "https://files.pythonhosted.org/packages/32/e9/73d83a7e26e99fd668fb52dba334f065e8346b513a57fd9f830568e6f209/pytreelog-194rc0.post1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ea9fd89270d293c3ef3693e43f2ba4d8", "sha256": "6c82d160a60804c749c696fa4ebca94e0f76aba069adf8dd953e40dc9d58decc" }, "downloads": -1, "filename": "pytreelog-194rc0.post1.tar.gz", "has_sig": false, "md5_digest": "ea9fd89270d293c3ef3693e43f2ba4d8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 18208, "upload_time": "2019-04-12T21:29:21", "url": "https://files.pythonhosted.org/packages/e5/e5/764390ab18b0c33399a42dc6bf3fe4f4ebf411774a68e8be10c93149dc47/pytreelog-194rc0.post1.tar.gz" } ] }