Source code for taurus.test.moduleexplorer

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#############################################################################
##
## This file is part of Taurus
##
## http://taurus-scada.org
##
## Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
##
## Taurus is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Taurus is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
##
###########################################################################

'''Utility code for returning info about a module'''

import sys
import os
import inspect
import glob
import re


[docs]class ModuleExplorer(object): def __init__(self, exclude_patterns=(), verbose=True): ''' :param exclude_patterns: (seq<str>) sequence of strings containing regexp patterns. Each candidate to be explored will be matched against these patterns and will be excluded if it matches any of them. :param verbose: (bool) If True (default) status messages will be printed to stdout ''' self.exclude_patterns = [re.compile(p) for p in exclude_patterns] self.verbose = verbose def _matchesAnyPattern(self, name, paterns): for p in paterns: if re.match(p, name) is not None: if self.verbose: print 'excluding "%s"' % name return True return False def _getlocalmembernames(self, module, predicate=None): ret = [] modulepath, tail = os.path.split(inspect.getabsfile(module)) for n, v in inspect.getmembers(module, predicate): if inspect.isbuiltin(v): continue # ignore builtin functions try: memberpath, tail = os.path.split(inspect.getabsfile(v)) except TypeError: continue # ignore builtin modules if memberpath == modulepath: ret.append(n) return ret def _getSubmodulesFromPath(self, modulepath): g = glob.glob(os.path.join(modulepath, '*', '__init__.py')) ret = [re.findall(r".+\/(.*)\/__init__.py", s)[0] for s in g] return ret def _isclass_with_init(self, obj): return inspect.isclass(obj) and hasattr(obj, '__init__') def _isenumeration(self, obj): # return isinstance(obj, taurus.core.util) return False # @todo
[docs] def exploreModule(self, modulename): '''Recursive function that gathers info on a module and all its submodules. :param modulename: the name of the module to explore :return: (dict<str,object>) a dictionary containing submodulenames, localclassnames, localfunctionnames, localenumerationnames, externalmembernames, submodules, warnings ''' if self.verbose: print "Exploring %s..." % modulename warnings = [] try: module = __import__(modulename, fromlist=['']) except Exception as e: msg = 'exploreModule: WARNING: Cannot import %s. Reason: %s' % ( modulename, repr(e)) warnings.append(msg) if self.verbose: print msg return dict(modulename=modulename, basemodulename=modulename.split('.')[-1], modulepath=None, submodulenames=[], localclassnames=[], localfunctionnames=[], localenumerationnames=[], externalmembernames=[], submodules={}, warnings=warnings) modulepath, tail = os.path.split(inspect.getabsfile(module)) submodulenames = sorted(self._getSubmodulesFromPath(modulepath)) localclassnames = sorted( self._getlocalmembernames(module, self._isclass_with_init)) localfunctionnames = sorted( self._getlocalmembernames(module, inspect.isfunction)) localenumerationnames = sorted([]) # @todo externalmembernames = sorted([]) # @todo # localmembers = list(submodules) + localfunctionnames + localclassnames + localenumerationnames # externalmembers = [n for n,v in inspect.getmembers(object) if (n not in # localmembers and not n.startswith('_'))] #filter out excluded members submodulenames = [n for n in submodulenames if not self._matchesAnyPattern( os.path.join(modulename, n), self.exclude_patterns)] localclassnames = [n for n in localclassnames if not self._matchesAnyPattern( os.path.join(modulename, n), self.exclude_patterns)] localfunctionnames = [n for n in localfunctionnames if not self._matchesAnyPattern( os.path.join(modulename, n), self.exclude_patterns)] localenumerationnames = [n for n in localenumerationnames if not self._matchesAnyPattern( os.path.join(modulename, n), self.exclude_patterns)] externalmembernames = [n for n in externalmembernames if not self._matchesAnyPattern( os.path.join(modulename, n), self.exclude_patterns)] #recurse submodules = {} for n in submodulenames: sm_name = '.'.join((modulename, n)) submodules[n] = self.exploreModule(sm_name) return dict(modulename=modulename, basemodulename=modulename.split('.')[-1], modulepath=modulepath, submodulenames=submodulenames, localclassnames=localclassnames, localfunctionnames=localfunctionnames, localenumerationnames=localenumerationnames, externalmembernames=externalmembernames, submodules=submodules, warnings=warnings)
@staticmethod
[docs] def getAll(info, key): ''' append all values for a given key in a nested "moduleinfo" dictionary :param info: (dict) a moduleinfo dictionary like the one returned by :meth:`exploreModule` :param key: (str) a key of a moduleinfo dictionary :return: (list<tuple>) a list that concatenates tuples where the first element is the (sub)module name and the second element is the value for the given key. If for a certain submodule, the value is empty, it is not included in the list at all. ''' mname = info['modulename'] try: if not isinstance(info[key], list): ret = [(mname, info[key])] else: ret = [(mname, el) for el in info[key]] except KeyError: return [] for sminfo in info['submodules'].itervalues(): ret += ModuleExplorer.getAll(sminfo, key) return ret
@staticmethod
[docs] def explore(modulename, exclude_patterns=(), verbose=True): '''convenience to explore a module :param modulename: the name of the module to explore :param exclude_patterns: (seq<str>) sequence of strings containing regexp patterns. Each candidate to be explored will be matched against these patterns and will be excluded if it matches any of them. :param verbose: (bool) If True (default) status messages will be printed to stdout :return: (dict<str,object>, allwarnings) a tuple whose first member is a dictionary containing submodulenames, localclassnames, localfunctionnames, localenumerationnames, externalmembernames, submodules, warnings. The second member of the tuple is a list containing all the warnings accummulated. ''' explorer = ModuleExplorer( exclude_patterns=exclude_patterns, verbose=verbose) minfo = explorer.exploreModule(modulename) return minfo, ModuleExplorer.getAll(minfo, 'warnings')
def main(modulename='taurus', exclude_patterns=('.*/ui')): moduleinfo, allw = ModuleExplorer.explore( modulename, exclude_patterns=exclude_patterns, verbose=True) print '\n\n' + '*' * 50 print "Exploration finished with %i warnings:" % (len(allw)) for m, w in allw: print w print '*' * 50 + '\n' print assert len(allw) == 0 if __name__ == "__main__": main()