| #!/usr/bin/env python |
| |
| # Copyright (c) 2001-2002, MetaSlash Inc. All rights reserved. |
| |
| """ |
| Object to hold information about functions. |
| Also contain a pseudo Python function object |
| """ |
| |
| import string |
| |
| _ARGS_ARGS_FLAG = 4 |
| _KW_ARGS_FLAG = 8 |
| _CO_FLAGS_MASK = _ARGS_ARGS_FLAG + _KW_ARGS_FLAG |
| |
| class _ReturnValues: |
| def __init__(self): |
| self.returnValues = None |
| |
| def returnsNoValue(self): |
| returnValues = self.returnValues |
| # if unset, we don't know |
| if returnValues is None: |
| return 0 |
| # it's an empty list, that means no values |
| if not returnValues: |
| return 1 |
| # make sure each value is not None |
| for rv in returnValues: |
| if not rv[1].isNone(): |
| return 0 |
| return returnValues[-1][1].isImplicitNone() |
| |
| class FakeCode : |
| "This is a holder class for code objects (so we can modify them)" |
| def __init__(self, code, varnames = None) : |
| for attr in dir(code): |
| try: |
| setattr(self, attr, getattr(code, attr)) |
| except: |
| pass |
| if varnames is not None: |
| self.co_varnames = varnames |
| |
| class FakeFunction(_ReturnValues): |
| "This is a holder class for turning code at module level into a function" |
| |
| def __init__(self, name, code, func_globals = {}, varnames = None) : |
| _ReturnValues.__init__(self) |
| self.func_name = self.__name__ = name |
| self.func_doc = self.__doc__ = "ignore" |
| |
| self.func_code = FakeCode(code, varnames) |
| self.func_defaults = None |
| self.func_globals = func_globals |
| |
| def __str__(self): |
| return self.func_name |
| |
| def __repr__(self): |
| return '%s from %r' % (self.func_name, self.func_code.co_filename) |
| |
| class Function(_ReturnValues): |
| "Class to hold all information about a function" |
| |
| def __init__(self, function, isMethod=0): |
| _ReturnValues.__init__(self) |
| self.function = function |
| self.isMethod = isMethod |
| self.minArgs = self.maxArgs = function.func_code.co_argcount |
| if function.func_defaults is not None : |
| self.minArgs = self.minArgs - len(function.func_defaults) |
| # if function uses *args, there is no max # args |
| try: |
| if function.func_code.co_flags & _ARGS_ARGS_FLAG != 0 : |
| self.maxArgs = None |
| self.supportsKW = function.func_code.co_flags & _KW_ARGS_FLAG |
| except AttributeError: |
| # this happens w/Zope |
| self.supportsKW = 0 |
| |
| def __str__(self): |
| return self.function.func_name |
| |
| def __repr__(self): |
| return '%s from %r:%d' % (self.function.func_name, |
| self.function.func_code.co_filename, |
| self.function.func_code.co_firstlineno) |
| |
| def arguments(self) : |
| numArgs = self.function.func_code.co_argcount |
| if self.maxArgs is None : |
| numArgs = numArgs + 1 |
| if self.supportsKW : |
| numArgs = numArgs + 1 |
| return self.function.func_code.co_varnames[:numArgs] |
| |
| def isParam(self, name) : |
| return name in self.arguments() |
| |
| def isStaticMethod(self): |
| return self.isMethod and isinstance(self.function, type(create_fake)) |
| |
| def isClassMethod(self): |
| try: |
| return self.isMethod and self.function.im_self is not None |
| except AttributeError: |
| return 0 |
| |
| def defaultValue(self, name) : |
| func_code = self.function.func_code |
| arg_names = list(func_code.co_varnames[:func_code.co_argcount]) |
| i = arg_names.index(name) |
| if i < self.minArgs : |
| raise ValueError |
| return self.function.func_defaults[i - self.minArgs] |
| |
| def varArgName(self) : |
| if self.maxArgs is not None : |
| return None |
| func_code = self.function.func_code |
| return func_code.co_varnames[func_code.co_argcount] |
| |
| def create_fake(name, code, func_globals = {}, varnames = None) : |
| return Function(FakeFunction(name, code, func_globals, varnames)) |
| |
| def create_from_file(file, filename, module) : |
| if file is None: |
| return create_fake(filename, compile('', filename, 'exec')) |
| |
| # Make sure the file is at the beginning |
| # if python compiled the file, it will be at the end |
| file.seek(0) |
| |
| # Read in the source file, see py_compile.compile() for games w/src str |
| codestr = file.read() |
| codestr = string.replace(codestr, "\r\n", "\n") |
| codestr = string.replace(codestr, "\r", "\n") |
| if codestr and codestr[-1] != '\n' : |
| codestr = codestr + '\n' |
| code = compile(codestr, filename, 'exec') |
| return Function(FakeFunction('__main__', code, module.__dict__)) |
| |
| def _co_flags_equal(o1, o2) : |
| return (o1.co_flags & _CO_FLAGS_MASK) == (o2.co_flags & _CO_FLAGS_MASK) |
| |
| def same_signature(func, object) : |
| '''Return a boolean value if the <func> has the same signature as |
| a function with the same name in <object> (ie, an overriden method)''' |
| |
| try : |
| baseMethod = getattr(object, func.func_name) |
| base_func_code = baseMethod.im_func.func_code |
| except AttributeError : |
| return 1 |
| |
| return _co_flags_equal(base_func_code, func.func_code) and \ |
| base_func_code.co_argcount == func.func_code.co_argcount |
| |