Example #1
0
    def parseEnums(self, proto):
        enumValues = {}
        enumDocs = {}
        for em in proto.findNextSiblings('div', limit=1)[0].findAll('em'):
            enumKey = str(em.contents[-1])
            try:
                enumVal = getattr(self.apiClass, enumKey)
            except:
                _logger.warn("%s.%s of enum %s does not exist" %
                             (self.apiClassName, enumKey, self.currentMethod))
                enumVal = None
            enumValues[enumKey] = enumVal

            docItem = em.next.next.next.next.next

            if isinstance(docItem, NavigableString):
                enumDocs[enumKey] = str(docItem).strip()
            else:
                enumDocs[enumKey] = str(docItem.contents[0]).strip()

        pymelEnumList = self.getPymelEnums(enumValues)
        for val, pyval in zip(enumValues, pymelEnumList):
            enumDocs[pyval] = enumDocs[val]

        enumInfo = {
            'values': util.Enum(self.currentMethod, enumValues,
                                multiKeys=True),
            'valueDocs': enumDocs,

            #'doc' : methodDoc
        }
        return enumInfo, util.Enum(self.currentMethod,
                                   pymelEnumList,
                                   multiKeys=True)
    def _apiEnumToPymelEnum(self, apiEnum, apiToPymelNames=None):
        defaultsSet = self.PYMEL_ENUM_DEFAULTS.get(apiEnum.name, set())
        defaults = {}
        if apiToPymelNames is None:
            apiToPymelNames = self._apiEnumNamesToPymelEnumNames(apiEnum)
        pymelKeyDict = {}
        docs = dict(apiEnum._docs)
        for apiName, val in apiEnum._keys.iteritems():
            # want to include docs, so make dict (key, doc) => val
            pymelKeyDict[apiName] = val
            pymelName = apiToPymelNames[apiName]
            pymelKeyDict[pymelName] = val

            doc = apiEnum._docs.get(apiName)
            if doc:
                docs[pymelName] = doc

            # in the pymel dict, the pymel name should always be the default
            # key for a value... but the original dict may also have multiple
            # keys for a value... so:
            #   if there is an entry in PYMEL_ENUM_DEFAULTS for this
            #     class/pymelName, then use that as the default
            #   otherwise, use the pymel equivalent of whatever the original
            #     api default was
            if (pymelName in defaultsSet
                    # need to check val not in defaults, or else we can override
                    # a value set due to defaultsSet
                    or (val not in defaults and apiName == apiEnum.getKey(val))):
                defaults[val] = pymelName
        return util.Enum(apiEnum.name, pymelKeyDict, multiKeys=True,
                         defaultKeys=defaults)
    def parseEnums(self, proto):
        enumValues={}
        enumDocs={}
        for em in proto.findNextSiblings( 'div', limit=1)[0].findAll( 'em'):
            enumKey = str(em.contents[-1])
            try:
                enumVal = getattr(self.apiClass, enumKey)
            except:
                _logger.warn( "%s.%s of enum %s does not exist" % ( self.apiClassName, enumKey, self.currentMethod))
                enumVal = None
            enumValues[ enumKey ] = enumVal
            # TODO:
            # do we want to feed the docstrings to the Enum object itself
            # (which seems to have support for docstrings)? Currently, we're
            # not...
            docItem = em.next.next.next.next.next

            if isinstance( docItem, NavigableString ):
                enumDocs[enumKey] = str(docItem).strip()
            else:
                enumDocs[enumKey] = str(docItem.contents[0]).strip()

        apiEnum = util.Enum(self.currentMethod, enumValues, multiKeys=True)
        apiToPymelNames = self._apiEnumNamesToPymelEnumNames(apiEnum)
        pymelEnum = self._apiEnumToPymelEnum(apiEnum,
                                             apiToPymelNames=apiToPymelNames)
        for apiName, pymelName in apiToPymelNames.iteritems():
            apiDoc = enumDocs.get(apiName)
            if apiDoc is not None:
                enumDocs[pymelName] = apiDoc

        enumInfo = {'values' : apiEnum,
                    'valueDocs' : enumDocs,

                      #'doc' : methodDoc
                    }
        return enumInfo, pymelEnum
Example #4
0
    """
    a convenience function that allows any module to setup a logger by simply
    calling `getLogger(__name__)`.  If the module is a package, "__init__" will
    be stripped from the logger name
    """
    suffix = '.__init__'
    if name.endswith(suffix):
        name = name[:-len(suffix)]
    logger = logging.getLogger(name)
    environLogLevelOverride(logger)
    return logger


# keep as an enumerator so that we can keep the order
logLevels = util.Enum(
    'logLevels',
    dict([(getLevelName(n), n) for n in range(0, CRITICAL + 1, 10)]))


def nameToLevel(name):
    return logLevels.getIndex(name)


def levelToName(level):
    return logLevels.getKey(level)


environLogLevelOverride(pymelLogger)

#===============================================================================
# DECORATORS
Example #5
0
    def parse(self, apiClassName):

        self.enums = {}
        self.pymelEnums = {}
        self.methods = util.defaultdict(list)
        self.currentMethod = None
        self.badEnums = []

        self.apiClassName = apiClassName
        self.apiClass = getattr(self.apiModule, self.apiClassName)

        self.docfile = os.path.join(self.docloc, 'API',
                                    self.getClassFilename() + '.html')

        _logger.info("parsing file %s", self.docfile)

        f = open(self.docfile)

        soup = BeautifulSoup(f.read(), convertEntities='html')
        f.close()

        for proto in soup.body.findAll('div', **{'class': 'memproto'}):

            returnDoc = ''

            # NAME AND RETURN TYPE
            memb = proto.findAll('td', **{'class': 'memname'})[0]
            buf = [x.strip() for x in memb.findAll(text=True)]
            if len(buf) == 1:
                buf = [
                    x for x in buf[0].split()
                    if x not in ['const', 'unsigned']
                ]
                if len(buf) == 1:
                    returnType = None
                    methodName = buf[0]
                else:
                    returnType = ''.join(buf[:-1])
                    methodName = buf[-1]
            else:
                returnType = buf[0]
                methodName = buf[1]
                buf = [
                    x for x in buf[0].split()
                    if x not in ['const', 'unsigned']
                ]
                if len(buf) > 1:
                    returnType += buf[0]
                    methodName = buf[1]

            methodName = methodName.split('::')[-1]

            # convert operators to python special methods
            if methodName.startswith('operator'):
                methodName = self.getOperatorName(methodName)

                if methodName is None: continue

            # no MStatus in python
            if returnType in ['MStatus', 'void']:
                returnType = None
            else:
                returnType = self.handleEnums(returnType)

            # convert to unicode
            self.currentMethod = str(methodName)

            #constructors and destructors
            if self.currentMethod.startswith(
                    '~') or self.currentMethod == self.apiClassName:
                continue

            # ENUMS
            if returnType == 'enum':
                self.xprint("ENUM", returnType)
                #print returnType, methodName
                try:
                    enumValues = {}
                    enumDocs = {}
                    for em in proto.findNextSiblings('div',
                                                     limit=1)[0].findAll('em'):
                        enumKey = str(em.contents[-1])
                        try:
                            enumVal = getattr(self.apiClass, enumKey)
                        except:
                            _logger.warn("%s.%s of enum %s does not exist" %
                                         (self.apiClassName, enumKey,
                                          self.currentMethod))
                            enumVal = None
                        enumValues[enumKey] = enumVal

                        docItem = em.next.next.next.next.next

                        if isinstance(docItem, NavigableString):
                            enumDocs[enumKey] = str(docItem).strip()
                        else:
                            enumDocs[enumKey] = str(
                                docItem.contents[0]).strip()

                    pymelEnumList = self.getPymelEnums(enumValues)
                    for val, pyval in zip(enumValues, pymelEnumList):
                        enumDocs[pyval] = enumDocs[val]

                    enumInfo = {
                        'values':
                        util.Enum(self.currentMethod,
                                  enumValues,
                                  multiKeys=True),
                        'valueDocs':
                        enumDocs,

                        #'doc' : methodDoc
                    }

                    #print enumList

                    self.enums[self.currentMethod] = enumInfo
                    self.pymelEnums[self.currentMethod] = util.Enum(
                        self.currentMethod, pymelEnumList, multiKeys=True)

                except AttributeError, msg:
                    _logger.error("FAILED ENUM: %s", msg)

            # ARGUMENTS
            else:
                self.xprint("RETURN", returnType)

                argInfo = {}
                argList = []
                defaults = {}
                names = []
                directions = {}
                docs = {}
                inArgs = []
                outArgs = []
                types = {}
                typeQualifiers = {}
                methodDoc = ''
                deprecated = False

                # Static methods
                static = False
                try:
                    code = proto.findAll('code')[-1].string
                    if code and code.strip() == '[static]':
                        static = True
                except IndexError:
                    pass

                tmpTypes = []
                # TYPES
                for paramtype in proto.findAll('td', **{'class': 'paramtype'}):
                    buf = []
                    [
                        buf.extend(x.split())
                        for x in paramtype.findAll(text=True)
                    ]  #if x.strip() not in ['', '*', '&', 'const', 'unsigned'] ]
                    buf = [str(x.strip()) for x in buf if x.strip()]

                    i = 0
                    for i, each in enumerate(buf):
                        if each not in ['*', '&', 'const', 'unsigned']:
                            argtype = buf.pop(i)
                            break
                    else:
                        # We didn't find any arg type - therefore everything
                        # in buf is in the set('*', '&', 'const', 'unsigned')
                        # ... so it's implicitly an unsigned int
                        argtype = 'int'

                    if 'unsigned' in buf and argtype in ('char', 'int', 'int2',
                                                         'int3', 'int4'):
                        argtype = 'u' + argtype

                    argtype = self.handleEnums(argtype)

                    #print '\targtype', argtype, buf
                    tmpTypes.append((argtype, buf))

                # ARGUMENT NAMES
                i = 0
                for paramname in proto.findAll('td', **{'class': 'paramname'}):
                    buf = [
                        x.strip() for x in paramname.findAll(text=True)
                        if x.strip() not in ['', ',']
                    ]
                    if buf:
                        argname = buf[0]
                        data = buf[1:]

                        type, qualifiers = tmpTypes[i]
                        default = None
                        joined = ''.join(data).strip()

                        if joined:
                            joined = joined.encode('ascii', 'ignore')
                            # break apart into index and defaults :  '[3] = NULL'
                            brackets, default = re.search(
                                '([^=]*)(?:\s*=\s*(.*))?', joined).groups()

                            if brackets:
                                numbuf = re.split(r'\[|\]', brackets)
                                if len(numbuf) > 1:
                                    # Note that these two args need to be cast differently:
                                    #   int2 foo;
                                    #   int bar[2];
                                    # ... so, instead of storing the type of both as
                                    # 'int2', we make the second one 'int__array2'
                                    type = type + '__array' + numbuf[1]
                                else:
                                    print "this is not a bracketed number", repr(
                                        brackets), joined

                            if default is not None:
                                try:
                                    # Constants
                                    default = {
                                        'true': True,
                                        'false': False,
                                        'NULL': None
                                    }[default]
                                except KeyError:
                                    try:
                                        if type in [
                                                'int', 'uint', 'long', 'uchar'
                                        ]:
                                            default = int(default)
                                        elif type in ['float', 'double']:
                                            # '1.0 / 24.0'
                                            if '/' in default:
                                                default = eval(default)
                                            # '1.0e-5F'  --> '1.0e-5'
                                            elif default.endswith('F'):
                                                default = float(default[:-1])
                                            else:
                                                default = float(default)
                                        else:
                                            default = self.handleEnumDefaults(
                                                default, type)
                                    except ValueError:
                                        default = self.handleEnumDefaults(
                                            default, type)
                                # default must be set here, because 'NULL' may be set to back to None, but this is in fact the value we want
                                self.xprint('DEFAULT', default)
                                defaults[argname] = default

                        types[argname] = type
                        typeQualifiers[argname] = qualifiers
                        names.append(argname)
                        i += 1

                try:
                    # ARGUMENT DIRECTION AND DOCUMENTATION
                    addendum = proto.findNextSiblings('div', limit=1)[0]
                    #try: self.xprint( addendum.findAll(text=True ) )
                    #except: pass

                    #if addendum.findAll( text = re.compile( '(This method is obsolete.)|(Deprecated:)') ):

                    if addendum.findAll(text=lambda x: x in self.OBSOLETE_MSG):
                        self.xprint("OBSOLETE")
                        self.currentMethod = None
                        continue

                    #if self.currentMethod == 'createColorSet': raise NotImplementedError
                    if addendum.findAll(
                            text=lambda x: x in self.DEPRECATED_MSG):
                        self.xprint("DEPRECATED")
                        #print self.apiClassName + '.' + self.currentMethod + ':' + ' DEPRECATED'
                        deprecated = True

                    methodDoc = ' '.join(addendum.p.findAll(text=True))

                    tmpDirs = []
                    tmpNames = []
                    tmpDocs = []

                    #extraInfo = addendum.dl.table
                    extraInfos = addendum.findAll('table')

                    if extraInfos:
                        #print "NUMBER OF TABLES", len(extraInfos)
                        for extraInfo in extraInfos:
                            tmpDirs += extraInfo.findAll(
                                text=lambda text: text in ['[in]', '[out]'])

                            #tmpNames += [ em.findAll( text=True, limit=1 )[0] for em in extraInfo.findAll( 'em') ]
                            tmpNames = []
                            for tr in extraInfo.findAll('tr'):
                                assert tr, "could not find name tr"
                                em = tr.findNext('em')
                                assert tr, "could not find name em"
                                name = em.findAll(text=True, limit=1)[0]
                                tmpNames.append(name)

#                            for td in extraInfo.findAll( 'td' ):
#                                assert td, "could not find doc td"
#                                for doc in td.findAll( text=lambda text: text.strip(), recursive=False) :
#                                    if doc: tmpDocs.append( ''.join(doc) )

                            for doc in [
                                    td.findAll(text=lambda text: text.strip(),
                                               recursive=False)
                                    for td in extraInfo.findAll('td')
                            ]:
                                if doc: tmpDocs.append(''.join(doc))

                        assert len(tmpDirs) == len(tmpNames) == len(
                            tmpDocs
                        ), 'names and types lists are of unequal lengths: %s vs. %s vs. %s' % (
                            tmpDirs, tmpNames, tmpDocs)
                        assert sorted(tmpNames) == sorted(typeQualifiers.keys(
                        )), 'name list mismatch %s %s' % (
                            sorted(tmpNames), sorted(typeQualifiers.keys()))

                        #self.xprint(  sorted(tmpNames), sorted(typeQualifiers.keys()), sorted(typeQualifiers.keys()) )

                        for name, dir, doc in zip(tmpNames, tmpDirs, tmpDocs):
                            if dir == '[in]':
                                # attempt to correct bad in/out docs
                                if re.search(
                                        r'\b([fF]ill|[sS]tor(age)|(ing))|([rR]esult)',
                                        doc):
                                    _logger.warn(
                                        "%s.%s(%s): Correcting suspected output argument '%s' based on doc '%s'"
                                        %
                                        (self.apiClassName, self.currentMethod,
                                         ', '.join(names), name, doc))
                                    dir = 'out'
                                elif not re.match(
                                        'set[A-Z]', self.currentMethod
                                ) and '&' in typeQualifiers[name] and types[
                                        name] in [
                                            'int', 'double', 'float', 'uint',
                                            'uchar'
                                        ]:
                                    _logger.warn(
                                        "%s.%s(%s): Correcting suspected output argument '%s' based on reference type '%s &' ('%s')'"
                                        %
                                        (self.apiClassName, self.currentMethod,
                                         ', '.join(names), name, types[name],
                                         doc))
                                    dir = 'out'
                                else:
                                    dir = 'in'
                            elif dir == '[out]':
                                if types[name] == 'MAnimCurveChange':
                                    _logger.warn(
                                        "%s.%s(%s): Setting MAnimCurveChange argument '%s' to an input arg (instead of output)"
                                        %
                                        (self.apiClassName, self.currentMethod,
                                         ', '.join(names), name))
                                    dir = 'in'
                                else:
                                    dir = 'out'
                            else:
                                raise

                            assert name in names
                            directions[name] = dir
                            docs[name] = doc

                        # Documentation for Return Values
                        if returnType:
                            try:
                                returnDocBuf = addendum.findAll(
                                    'dl', limit=1,
                                    **{'class':
                                       'return'})[0].findAll(text=True)
                            except IndexError:
                                pass
                            else:
                                if returnDocBuf:
                                    returnDoc = ''.join(
                                        returnDocBuf[1:]).strip('\n')
                                self.xprint('RETURN_DOC', repr(returnDoc))

                except (AttributeError, AssertionError), msg:
                    self.xprint("FAILED", str(msg))
                    pass

#                print names
#                print types
#                print defaults
#                print directions
#                print docs

                for argname in names[:]:
                    type = types[argname]
                    direction = directions.get(argname, 'in')
                    doc = docs.get(argname, '')

                    if type == 'MStatus':
                        types.pop(argname)
                        defaults.pop(argname, None)
                        directions.pop(argname, None)
                        docs.pop(argname, None)
                        idx = names.index(argname)
                        names.pop(idx)
                    else:
                        if direction == 'in':
                            inArgs.append(argname)
                        else:
                            outArgs.append(argname)
                        argInfo[argname] = {'type': type, 'doc': doc}

                # correct bad outputs
                if self.isGetMethod() and not returnType and not outArgs:
                    for argname in names:
                        if '&' in typeQualifiers[argname]:
                            doc = docs.get(argname, '')
                            directions[argname] = 'out'
                            idx = inArgs.index(argname)
                            inArgs.pop(idx)
                            outArgs.append(argname)

                            _logger.warn(
                                "%s.%s(%s): Correcting suspected output argument '%s' because there are no outputs and the method is prefixed with 'get' ('%s')"
                                % (self.apiClassName, self.currentMethod,
                                   ', '.join(names), argname, doc))

                # now that the directions are correct, make the argList
                for argname in names:
                    type = types[argname]
                    direction = directions.get(argname, 'in')
                    data = (argname, type, direction)
                    if self.verbose: self.xprint("ARG", data)
                    argList.append(data)

                methodInfo = {
                    'argInfo': argInfo,
                    'returnInfo': {
                        'type': returnType,
                        'doc': returnDoc
                    },
                    'args': argList,
                    'returnType': returnType,
                    'inArgs': inArgs,
                    'outArgs': outArgs,
                    'doc': methodDoc,
                    'defaults': defaults,
                    #'directions' : directions,
                    'types': types,
                    'static': static,
                    'typeQualifiers': typeQualifiers,
                    'deprecated': deprecated
                }
                self.methods[self.currentMethod].append(methodInfo)

                # reset
                self.currentMethod = None