diff --git a/README.rst b/README.rst index 5c2981a..e525360 100644 --- a/README.rst +++ b/README.rst @@ -19,37 +19,36 @@ Usage :: - >>> import json >>> from ast import parse >>> from ast2json import ast2json - >>> ast = ast2json(parse(open('some_python_source_file.py').read())) - >>> print json.dumps(ast, indent=4) + >>> print(ast2json(parse(open('some_python_source_file.py').read(), indent=4)) -If you are lazy, "str2json" will apply the "parse" method of ast on a string for you, so you'll be able to write: +If you want just the dict use: :: - >>> str2json(open('some_python_source_file.py').read()) + >>> from ast2json import ast2dict + >>> str2dict(open('some_python_source_file.py').read()) Example ======= -This is the result of converting 'print "Hello World!"' (and applying json.dumps on the result). +This is the result of converting 'print("Hello World!")'. :: { "body": [ { - "_type": "Print", + "node_type": "Print", "nl": true, "col_offset": 0, "dest": null, "values": [ { "s": "Hello World!", - "_type": "Str", + "node_type": "Str", "lineno": 1, "col_offset": 6 } @@ -57,7 +56,7 @@ This is the result of converting 'print "Hello World!"' (and applying json.dumps "lineno": 1 } ], - "_type": "Module" + "node_type": "Module" } diff --git a/ast2json.py b/ast2json.py index 960ea97..2a21ca2 100644 --- a/ast2json.py +++ b/ast2json.py @@ -1,4 +1,5 @@ # Copyright (c) 2013, Laurent Peuch +# Copyright (c) 2015, Eddy Ernesto del Valle Pino # # All rights reserved. # Redistribution and use in source and binary forms, with or without @@ -24,39 +25,76 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import json + from _ast import AST from ast import parse -def ast2json(node): - assert isinstance(node, AST) - to_return = {} - to_return['_type'] = node.__class__.__name__ +class AstJsonEncoder(json.JSONEncoder): + + def default(self, obj): + if isinstance(obj, AST): + value = { + attr: getattr(obj, attr) + for attr in dir(obj) + if not attr.startswith('_') + } + value['node_type'] = obj.__class__.__name__ + return value + return super(AstJsonEncoder, self) .default(obj) + + +def ast2json(node, *args, **kwargs): + return AstJsonEncoder(*args, **kwargs).encode(node) + + +def ast2dict(node): + result = {} + result['node_type'] = node.__class__.__name__.lower() for attr in dir(node): - if attr.startswith("_"): - continue - to_return[attr] = get_value(getattr(node, attr)) + if not attr.startswith("_") and attr not in ('lineno', 'col_offset'): + value = getattr(node, attr) + if isinstance(value, AST): + value = ast2dict(value) + elif isinstance(value, list): + value = [ast2dict(n) for n in value] + result[attr] = value + return result + - return to_return +class Tag: + def __init__(self, name=None, attrs=None, children=None): + self.name = (name or attrs.pop('node_type', '')).lower() + self.attrs = {} + self.children = [Tag(attrs=child) for child in children or []] + for attr, value in (attrs or {}).items(): + if isinstance(value, list): + self.children.append(Tag(name=attr, children=value)) + elif isinstance(value, dict): + self.children.append(Tag(name=attr, children=[value])) + else: + self.attrs[attr] = value -def str2json(string): - return ast2json(parse(string)) + def __str__(self): + attrs = [] + for attr, value in self.attrs.items(): + attrs.append('%(attr)s="%(value)s"' % locals()) + attrs = ' '.join(attrs) + head = '%(name)s %(attrs)s' % {'name': self.name, 'attrs': attrs} + return '<%(head)s>\n%(children)s' % { + 'name': self.name, + 'head': head.strip(), + 'children': '\n'.join(str(node) for node in self.children) + '\n' + } -def get_value(attr_value): - if attr_value is None: - return attr_value - if isinstance(attr_value, (int, basestring, float, long, complex, bool)): - return attr_value - if isinstance(attr_value, list): - return [get_value(x) for x in attr_value] - if isinstance(attr_value, AST): - return ast2json(attr_value) - else: - raise Exception("unknow case for '%s' of type '%s'" % (attr_value, type(attr_value))) +def ast2xml(node): + return Tag(attrs=ast2dict(node)) if __name__ == '__main__': - import json - print json.dumps(ast2json(parse(open(__file__, "r").read())), indent=4) + import yaml + import sys + print(yaml.dump(ast2dict(parse(open(sys.argv[1]).read()))))