In [2]:
import ast
import os

In [3]:
def setuppy_to_ast_tree(path_to_setuppy):
    with open(path_to_setuppy, 'r') as f:
        code = f.read()
        print(code)
    tree = ast.parse(code)
    return tree

In [4]:
setuppy_path = os.path.join(os.path.expanduser('~/dev/python/scikit-xray/setup.py'))

In [5]:
tree = setuppy_to_ast_tree(setuppy_path)


#!/usr/bin/env python

import setuptools
from distutils.core import setup, Extension
from setupext import ext_modules
import versioneer
import numpy as np
import os

# Utility function to read the README file.
# Used for the long_description.  It's nice, because now 1) we have a top level
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...
def read(fname):
    return open(os.path.join(os.path.dirname(__file__), fname)).read()

setup(
    name='scikit-xray',
    version=versioneer.get_version(),
    cmdclass=versioneer.get_cmdclass(),
    author='Brookhaven National Lab',
    description="Data analysis tools for X-ray science",
    packages=setuptools.find_packages(exclude=['doc']),
    include_dirs=[np.get_include()],
    package_data={'skxray.core.constants': ['data/*.dat']},
    install_requires=['six', 'numpy'],  # essential deps only
    ext_modules=ext_modules,
    url='http://github.com/scikit-xray/scikit-xray',
    keywords='Xray Analysis',
    license='BSD',
    classifiers=['Development Status :: 3 - Alpha',
                 "License :: OSI Approved :: BSD License",
                 "Programming Language :: Python :: 2.7",
                 "Programming Language :: Python :: 3.4",
                 "Topic :: Scientific/Engineering :: Physics",
                 "Topic :: Scientific/Engineering :: Chemistry",
                 "Topic :: Software Development :: Libraries",
                 "Intended Audience :: Science/Research",
                 "Intended Audience :: Developers",
                 ], requires=['numpy']
    )


In [6]:
for t in tree.body:
    print(t)


<_ast.Import object at 0x7fd3d4084be0>
<_ast.ImportFrom object at 0x7fd3d4084c50>
<_ast.ImportFrom object at 0x7fd3d4084cf8>
<_ast.Import object at 0x7fd3d4084d68>
<_ast.Import object at 0x7fd3d4084dd8>
<_ast.Import object at 0x7fd3d4084e48>
<_ast.FunctionDef object at 0x7fd3d4084eb8>
<_ast.Expr object at 0x7fd3d408e2e8>

In [20]:
for kw in tree.body[-1].value.keywords:
    print(vars(kw))


{'arg': 'name', 'value': <_ast.Str object at 0x7fd3d408e3c8>}
{'arg': 'version', 'value': <_ast.Call object at 0x7fd3d408e438>}
{'arg': 'cmdclass', 'value': <_ast.Call object at 0x7fd3d408e518>}
{'arg': 'author', 'value': <_ast.Str object at 0x7fd3d408e5f8>}
{'arg': 'description', 'value': <_ast.Str object at 0x7fd3d408e668>}
{'arg': 'packages', 'value': <_ast.Call object at 0x7fd3d408e6d8>}
{'arg': 'include_dirs', 'value': <_ast.List object at 0x7fd3d408e860>}
{'arg': 'package_data', 'value': <_ast.Dict object at 0x7fd3d408e978>}
{'arg': 'install_requires', 'value': <_ast.List object at 0x7fd3d408ea90>}
{'arg': 'ext_modules', 'value': <_ast.Name object at 0x7fd3d408eb70>}
{'arg': 'url', 'value': <_ast.Str object at 0x7fd3d408ebe0>}
{'arg': 'keywords', 'value': <_ast.Str object at 0x7fd3d408ec50>}
{'arg': 'license', 'value': <_ast.Str object at 0x7fd3d408ecc0>}
{'arg': 'classifiers', 'value': <_ast.List object at 0x7fd3d408ed30>}
{'arg': 'requires', 'value': <_ast.List object at 0x7fd3d408ef98>}

In [115]:
class SetupScraper(ast.NodeVisitor):
    def __init__(self):
        self.in_setup = False
        self.setup_info = []
    
    def visit_keyword(self, node):
        if self.in_setup:
            self.setup_info.append([node.arg])
            print(vars(node))
        self.visit(node.value)
        
    def visit_Call(self, node):
        if self.in_setup:
            self.setup_info[-1].append(node)
        if isinstance(node.func, ast.Name) and node.func.id == 'setup':
            self.in_setup = True
        self.generic_visit(node)
        if isinstance(node.func, ast.Name) and node.func.id == 'setup':
            self.in_setup = False
    def visit_Name(self, node):
        if node.id == 'setup':
            return
        if self.in_setup:
            self.setup_info[-1].append(node.id)
            print(vars(node))
    def visit_Str(self, node):
        if self.in_setup:
            self.setup_info[-1].append(node.s)
            print(node.s)
            
    def generic_visit(self, node):
        """Called if no explicit visitor function exists for a node.

        Overridden from the ast.NodeVisitor base class so that I can add some
        local state to keep track of whether or not my node visitor is inside
        a try/except block.  When a try block is encountered, the node is added
        to the `trys` instance attribute and then the try block is recursed in
        to.  Once the recursion has exited, the node is removed from the `trys`
        instance attribute
        """
        for field, value in ast.iter_fields(node):
            if isinstance(value, list):
                for item in value:
                    if isinstance(item, ast.AST):
                        self.visit(item)
            elif isinstance(value, ast.AST):
                self.visit(value)

In [116]:
scraper = SetupScraper()

In [117]:
scraper.visit(tree)


{'arg': 'name', 'value': <_ast.Str object at 0x7f43f80c3470>}
replay
{'arg': 'version', 'value': <_ast.Name object at 0x7f43f80c34e0>}
{'ctx': <_ast.Load object at 0x7f4401f95908>, 'lineno': 62, 'id': 'FULLVERSION', 'col_offset': 12}
{'arg': 'author', 'value': <_ast.Str object at 0x7f43f80c3550>}
Brookhaven National Laboratory
{'arg': 'packages', 'value': <_ast.List object at 0x7f43f80c35c0>}
replay
replay.muxer
replay.scalar
replay.search
replay.tests
{'arg': 'entry_points', 'value': <_ast.Dict object at 0x7f43f80c3748>}
console_scripts
replay = replay.replay:main
{'arg': 'package_data', 'value': <_ast.Dict object at 0x7f43f80c3860>}
replay.gui
*.enaml

In [118]:
scraper.setup_info


Out[118]:
[['name', 'replay'],
 ['version', 'FULLVERSION'],
 ['author', 'Brookhaven National Laboratory'],
 ['packages',
  'replay',
  'replay.muxer',
  'replay.scalar',
  'replay.search',
  'replay.tests'],
 ['entry_points', 'console_scripts', 'replay = replay.replay:main'],
 ['package_data', 'replay.gui', '*.enaml']]

In [111]:
vars(setup_call.value)


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-111-4e51a5dd927d> in <module>()
----> 1 vars(setup_call.value)

AttributeError: 'If' object has no attribute 'value'

In [79]:
for kw in setup_call.value.keywords:
    value = kw.value
    stringed = ''
#     print(value)
    if type(value) == ast.Str:
        stringed = value.s
    elif type(value) == ast.Call:
        stringed = value.func.attr + '()'
    elif type(value) == ast.List:
        stringed = [vars(v) for v in value.elts]
    if stringed:
        print('%s=%s' % (kw.arg, stringed))
    else:
        print('%s --> %s --> %s' % (kw.arg, type(value), vars(value)))


name=scikit-xray
version=get_version()
cmdclass=get_cmdclass()
author=Brookhaven National Lab
description=Data analysis tools for X-ray science
packages=find_packages()
include_dirs=[{'col_offset': 18, 'starargs': None, 'func': <_ast.Attribute object at 0x7f7eec2b4320>, 'kwargs': None, 'args': [], 'keywords': [], 'lineno': 24}]
package_data --> <class '_ast.Dict'> --> {'keys': [<_ast.Str object at 0x7f7eec2b4400>], 'values': [<_ast.List object at 0x7f7eec2b4438>], 'col_offset': 17, 'lineno': 25}
install_requires=[{'s': 'six', 'col_offset': 22, 'lineno': 26}, {'s': 'numpy', 'col_offset': 29, 'lineno': 26}]
ext_modules --> <class '_ast.Name'> --> {'col_offset': 16, 'ctx': <_ast.Load object at 0x7f7ef5438908>, 'id': 'ext_modules', 'lineno': 27}
url=http://github.com/scikit-xray/scikit-xray
keywords=Xray Analysis
license=BSD
classifiers=[{'s': 'Development Status :: 3 - Alpha', 'col_offset': 17, 'lineno': 31}, {'s': 'License :: OSI Approved :: BSD License', 'col_offset': 17, 'lineno': 32}, {'s': 'Programming Language :: Python :: 2.7', 'col_offset': 17, 'lineno': 33}, {'s': 'Programming Language :: Python :: 3.4', 'col_offset': 17, 'lineno': 34}, {'s': 'Topic :: Scientific/Engineering :: Physics', 'col_offset': 17, 'lineno': 35}, {'s': 'Topic :: Scientific/Engineering :: Chemistry', 'col_offset': 17, 'lineno': 36}, {'s': 'Topic :: Software Development :: Libraries', 'col_offset': 17, 'lineno': 37}, {'s': 'Intended Audience :: Science/Research', 'col_offset': 17, 'lineno': 38}, {'s': 'Intended Audience :: Developers', 'col_offset': 17, 'lineno': 39}]
requires=[{'s': 'numpy', 'col_offset': 30, 'lineno': 40}]

In [ ]: