├── .github └── workflows │ └── run-tests.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── var_dump_test.py └── var_export_test.py └── var_dump ├── __init__.py └── _var_dump.py /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ '*' ] 4 | paths: 5 | - 'tests/**' 6 | - 'var_dump/**' 7 | - '.github/workflows/run-tests.yml' 8 | pull_request: 9 | branches: [ master ] 10 | 11 | 12 | jobs: 13 | run_tests: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | strategy: 18 | matrix: 19 | python-version: [3.7, 3.11] 20 | name: Run unit tests 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | - name: Setup python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v2 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | - name: Run tests 29 | run: | 30 | python -m unittest tests 31 | 32 | run_tests_older_python: 33 | runs-on: ubuntu-20.04 34 | permissions: 35 | contents: read 36 | strategy: 37 | matrix: 38 | python-version: [ 3.5 ] 39 | name: Run unit tests 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v3 43 | - name: Setup python ${{ matrix.python-version }} 44 | uses: actions/setup-python@v2 45 | with: 46 | python-version: ${{ matrix.python-version }} 47 | - name: Run tests 48 | run: | 49 | python -m unittest tests -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.db 3 | .idea/ 4 | *.iml 5 | test.py 6 | MANIFEST 7 | dist/ 8 | /build/ 9 | /var_dump.egg-info/ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Shamim Hasnath 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 1. Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 3. All advertising materials mentioning features or use of this software 12 | must display the following acknowledgement: 13 | This product includes software developed by the . 14 | 4. Neither the name of the copyright holder nor the 15 | names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY ABOVE COPYRIGHT HOLDER ''AS IS'' AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include var_dump/_var_dump.py 2 | include var_dump/__init__.py -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # var_dump # 2 | **for python** 3 | 4 | 5 | --- 6 | 7 | 8 | 9 | **var\_dump** is a PHP's `var_dump()` equivalent function for python. It displays structured information such as type, value etc of a python `object`, `list`, `tuple`, `dict` & other types. 10 | 11 | ### Installation ### 12 | 13 | ---------- 14 | using `pip` 15 | 16 | pip install var_dump 17 | 18 | Or 19 | 20 | clone the project & cd into the `python-var-dump` directory 21 | then run: 22 | 23 | python setup.py install 24 | ### Examples ### 25 | ---- 26 | #### Example #1: #### 27 | from var_dump import var_dump 28 | 29 | var_dump(123) # output: #0 int(123) 30 | var_dump(123.44) # output: #0 float(123.44) 31 | var_dump("this is a string") # output: #0 str(16) "this is a string" 32 | var_dump(None) # output: # output: #0 NoneType(None) 33 | var_dump(True) # output # output: #0 bool(True) 34 | 35 | #### Example #2: #### 36 | you can pass more than one argument: 37 | 38 | from var_dump import var_dump 39 | 40 | var_dump(123, 123.44, None, False) 41 | 42 | #### Output: #### 43 | #0 int(123) 44 | #1 float(123.44) 45 | #2 NoneType(None) 46 | #3 bool(False) 47 | 48 | #### Example #3: #### 49 | 50 | from var_dump import var_dump 51 | 52 | class Base(object): 53 | 54 | def __init__(self): 55 | self.baseProp = (33, 44) 56 | self.fl = 44.33 57 | 58 | class Bar(object): 59 | 60 | def __init__(self): 61 | self.barProp = "I'm from Bar class" 62 | self.boo = True 63 | 64 | 65 | class Foo(Base): 66 | 67 | def __init__(self): 68 | super(Foo, self).__init__() 69 | self.someList = ['foo', 'goo'] 70 | self.someTuple = (33, (23, 44), 55) 71 | self.anOb = Bar() 72 | self.no = None 73 | 74 | foo = Foo() 75 | var_dump(foo) 76 | 77 | #### Output #### 78 | #0 object(Foo) (6) 79 | baseProp => tuple(2) 80 | [0] => int(33) 81 | [1] => int(44) 82 | someTuple => tuple(3) 83 | [0] => int(33) 84 | [1] => tuple(2) 85 | [0] => int(23) 86 | [1] => int(44) 87 | [2] => int(55) 88 | no => NoneType(None) 89 | someList => list(2) 90 | [0] => str(3) "foo" 91 | [1] => str(3) "goo" 92 | fl => float(44.33) 93 | anOb => object(Bar) (2) 94 | boo => bool(True) 95 | barProp => str(18) "I'm from Bar class" 96 | 97 | 98 | ### Unit tests ### 99 | ---- 100 | 101 | Tests are written using [`unittest`](https://docs.python.org/3/library/unittest.html). 102 | 103 | Run them with `python -m unittest tests`. 104 | 105 | --- 106 | 107 | **License: BSD License** 108 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='var_dump', 5 | version='1.2', 6 | packages=['var_dump'], 7 | url='http://github.com/sha256/python-var-dump', 8 | license='BSD', 9 | author='Shamim Hasnath', 10 | author_email='shamim@hasnath.net', 11 | description='var_dump for python', 12 | download_url='http://github.com/sha256/python-var-dump/tarball', 13 | classifiers=[ 14 | 'Development Status :: 5 - Production/Stable', 15 | 'License :: OSI Approved :: BSD License', 16 | 'Operating System :: OS Independent', 17 | 'Programming Language :: Python', 18 | 'Programming Language :: Python :: 2.6', 19 | 'Programming Language :: Python :: 2.7', 20 | 'Programming Language :: Python :: 3.2', 21 | 'Topic :: Software Development :: Libraries :: Python Modules', 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | from .var_dump_test import VarDumpTestCase 2 | from .var_export_test import VarExportTestCase 3 | -------------------------------------------------------------------------------- /tests/var_dump_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from var_dump import var_dump 4 | from unittest.mock import MagicMock, call, patch 5 | 6 | 7 | class VarDumpTestCase(unittest.TestCase): 8 | @patch('builtins.print') 9 | def test_var_dump(self, print_mock: MagicMock): 10 | var_dump('abc') 11 | 12 | print_mock.assert_called_with('#0 str(3) "abc"') 13 | self.assertEqual(1, print_mock.call_count) 14 | 15 | @patch('builtins.print') 16 | def test_var_dump_multiple_values_at_once(self, print_mock: MagicMock): 17 | var_dump('foo', 55, False) 18 | 19 | print_mock.assert_has_calls([ 20 | call('#0 str(3) "foo"'), 21 | call('#1 int(55) '), 22 | call('#2 bool(False) '), 23 | ]) 24 | self.assertEqual(3, print_mock.call_count) 25 | 26 | 27 | if __name__ == '__main__': 28 | unittest.main() 29 | -------------------------------------------------------------------------------- /tests/var_export_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from enum import Enum 4 | from var_dump import var_export 5 | 6 | 7 | class Foo: 8 | def __init__(self): 9 | self.x = 5 10 | self.y = 'abc' 11 | self.z = True 12 | 13 | 14 | class Bar: 15 | def __init__(self): 16 | self.value = False 17 | self.foo = Foo() 18 | 19 | 20 | class Color(Enum): 21 | RED = 1 22 | GREEN = 2 23 | 24 | 25 | class ObjectWithoutDict: 26 | """ 27 | https://stackoverflow.com/questions/25221270/how-to-create-class-object-without-dict 28 | """ 29 | __slots__ = () 30 | 31 | 32 | class ObjectWithCircularReference: 33 | def __init__(self): 34 | self.r = self 35 | 36 | 37 | class DeepCircularReferenceParent: 38 | def __init__(self): 39 | self.child = DeepCircularReferenceChild(self) 40 | 41 | 42 | class DeepCircularReferenceChild: 43 | def __init__(self, parent: DeepCircularReferenceParent): 44 | self.parent = parent 45 | 46 | 47 | class ParentObjectThatIsEqualToOthers: 48 | def __init__(self): 49 | self.foo = Foo() 50 | 51 | def __eq__(self, other): 52 | return True 53 | 54 | 55 | class VarExportTestCase(unittest.TestCase): 56 | def test_var_export(self): 57 | data = [ 58 | # todo: why is there a space at the end? 59 | [None, '#0 NoneType(None) '], 60 | 61 | # booleans 62 | [True, '#0 bool(True) '], 63 | [False, '#0 bool(False) '], 64 | 65 | # strings 66 | ['', '#0 str(0) ""'], 67 | ['a', '#0 str(1) "a"'], 68 | ['abc', '#0 str(3) "abc"'], 69 | 70 | # numbers 71 | [0, '#0 int(0) '], 72 | [12, '#0 int(12) '], 73 | [-13, '#0 int(-13) '], 74 | [21.37, '#0 float(21.37) '], 75 | 76 | # enums 77 | [Color.RED, '#0 Enum(Color.RED)'], 78 | [Color(2), '#0 Enum(Color.GREEN)'], 79 | 80 | # dicts 81 | [ 82 | {'foo': 12, 'bar': False}, 83 | "#0 dict(2) " 84 | " ['foo'] => int(12) " 85 | " ['bar'] => bool(False) ", 86 | ], 87 | 88 | # objects 89 | [Foo(), '#0 object(Foo) (3)' 90 | ' x => int(5) ' 91 | ' y => str(3) "abc"' 92 | ' z => bool(True) '], 93 | 94 | # nested objects 95 | [Bar(), '#0 object(Bar) (2)' 96 | ' value => bool(False) ' 97 | ' foo => object(Foo) (3)' 98 | ' x => int(5) ' 99 | ' y => str(3) "abc"' 100 | ' z => bool(True) '], 101 | ] 102 | 103 | for given, expected in data: 104 | with self.subTest([given, expected]): 105 | self.assertEqual( 106 | var_export(given), 107 | expected 108 | ) 109 | 110 | def test_var_export_multiple_values_at_once(self): 111 | self.assertEqual( 112 | var_export('foo', 55, Bar(), False), 113 | '#0 str(3) "foo"' 114 | '#1 int(55) ' 115 | '#2 object(Bar) (2)' 116 | ' value => bool(False) ' 117 | ' foo => object(Foo) (3)' 118 | ' x => int(5) ' 119 | ' y => str(3) "abc"' 120 | ' z => bool(True) ' 121 | '#3 bool(False) ' 122 | ) 123 | 124 | def test_var_export_object_without_dict(self): 125 | self.assertRegex( 126 | var_export(ObjectWithoutDict()), 127 | '#0 object\(ObjectWithoutDict\) ' 128 | '\(\)' 129 | ) 130 | 131 | def test_var_export_circular_reference(self): 132 | self.assertEqual( 133 | var_export(ObjectWithCircularReference()), 134 | '#0 object(ObjectWithCircularReference) (1)' 135 | ' r => object(ObjectWithCircularReference) (1) …circular reference…' 136 | ) 137 | 138 | def test_var_export_compares_parents_by_reference_not_value(self): 139 | self.assertEqual( 140 | var_export(ParentObjectThatIsEqualToOthers()), 141 | '#0 object(ParentObjectThatIsEqualToOthers) (1)' 142 | ' foo => object(Foo) (3)' 143 | ' x => int(5) ' 144 | ' y => str(3) "abc"' 145 | ' z => bool(True) ' 146 | ) 147 | 148 | def test_var_export_deep_circular_reference(self): 149 | self.assertEqual( 150 | var_export(DeepCircularReferenceParent()), 151 | '#0 object(DeepCircularReferenceParent) (1)' 152 | ' child => object(DeepCircularReferenceChild) (1)' 153 | ' parent => object(DeepCircularReferenceParent) (1) …circular reference…' 154 | ) 155 | 156 | 157 | if __name__ == '__main__': 158 | unittest.main() 159 | -------------------------------------------------------------------------------- /var_dump/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'sha256' 2 | from ._var_dump import var_dump as var_dump 3 | from ._var_dump import var_export as var_export 4 | 5 | __all__ = ['var_dump','var_export',] 6 | -------------------------------------------------------------------------------- /var_dump/_var_dump.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys 3 | 4 | try: 5 | from enum import Enum 6 | except ImportError: 7 | Enum = type(str) 8 | 9 | try: 10 | from types import NoneType 11 | except ImportError: 12 | NoneType = type(None) 13 | 14 | if sys.version_info > (3,): 15 | long = int 16 | unicode = str 17 | 18 | __author__ = "Shamim Hasnath" 19 | __copyright__ = "Copyright 2013, Shamim Hasnath" 20 | __license__ = "BSD License" 21 | __version__ = "1.2.0" 22 | 23 | TAB_SIZE = 4 24 | 25 | 26 | def display(o, space, num, key, typ, proret): 27 | st = "" 28 | l = [] 29 | if key: 30 | if typ is dict: 31 | st += " " * space + "['%s'] => " 32 | else: 33 | st += " " * space + "%s => " 34 | l.append(key) 35 | elif space > 0: 36 | st += " " * space + "[%d] => " 37 | l.append(num) 38 | else: # at the very start 39 | st += "#%d " 40 | l.append(num) 41 | 42 | if type(o) in (tuple, list, dict, int, str, float, long, bool, NoneType, unicode): 43 | st += "%s(%s) " 44 | l.append(type(o).__name__) 45 | 46 | if type(o) in (int, float, long, bool, NoneType): 47 | l.append(o) 48 | else: 49 | l.append(len(o)) 50 | 51 | if type(o) in (str, unicode): 52 | st += '"%s"' 53 | l.append(o) 54 | 55 | elif isinstance(o, Enum): 56 | st += "Enum(%s)" 57 | l.append(str(o)) 58 | 59 | elif isinstance(o, object): 60 | st += "object(%s) (%s)" 61 | l.append(o.__class__.__name__) 62 | try: 63 | l.append(len(o.__dict__)) 64 | except AttributeError: 65 | l.append(str(o)) 66 | 67 | if proret: 68 | print(st % tuple(l)) 69 | 70 | return st % tuple(l) 71 | 72 | 73 | def dump(o, space, num, key, typ, proret, parents=None): 74 | if type(o) in (str, int, float, long, bool, NoneType, unicode, Enum) or isinstance(o, Enum): 75 | return display(o, space, num, key, typ, proret) 76 | 77 | if not isinstance(o, object): 78 | return '' 79 | 80 | r = display(o, space, num, key, typ, proret) 81 | 82 | if parents is None: 83 | parents = [] 84 | elif any(o is parent for parent in parents): 85 | return r + ' …circular reference…' 86 | 87 | parents.append(o) 88 | num = 0 89 | 90 | if type(o) in (tuple, list, dict): 91 | typ = type(o) # type of the container of str, int, long, float etc 92 | elif isinstance(o, object): 93 | try: 94 | o = o.__dict__ 95 | except AttributeError: 96 | return r 97 | typ = object 98 | for i in o: 99 | space += TAB_SIZE 100 | if type(o) is dict: 101 | r += dump(o[i], space, num, i, typ, proret, parents) 102 | else: 103 | r += dump(i, space, num, '', typ, proret) 104 | num += 1 105 | space -= TAB_SIZE 106 | return r 107 | 108 | 109 | def var_dump(*obs): 110 | """ 111 | shows structured information of an object, list, tuple, etc. 112 | """ 113 | i = 0 114 | for x in obs: 115 | dump(x, 0, i, '', object, True) 116 | i += 1 117 | 118 | 119 | def var_export(*obs): 120 | """ 121 | returns output as string 122 | """ 123 | r = '' 124 | i = 0 125 | for x in obs: 126 | r += dump(x, 0, i, '', object, False) 127 | i += 1 128 | return r 129 | --------------------------------------------------------------------------------