def declare()

in src/composer/composer.py [0:0]


def declare(combinators, prefix=None):
    '''
        derive combinator methods from combinator table
        check argument count and map argument positions to argument names
        delegate to Composition constructor for the rest of the validation
    '''
    if not isinstance(combinators, dict):
        raise ComposerError('Invalid argument "combinators" in "declare"', combinators)

    if prefix is not None and not isinstance(prefix, str):
        raise ComposerError('Invalid argument "prefix" in "declare"', prefix)

    composer = types.SimpleNamespace()
    for key in combinators:
        type_ = prefix + '.' + key if prefix is not None else key
        combinator = combinators[key]

        if not isinstance(combinator, dict) or ('args' in combinator and not isinstance(combinator['args'], list)):
            raise ComposerError('Invalid "'+type_+'" combinator specification in "declare"', combinator)

        if 'args' in combinator:
            for arg in combinator['args']:
                if not isinstance(arg['name'], str):
                    raise ComposerError('Invalid "'+type_+'" combinator specification in "declare"', combinator)

        # Javascript capturing rules differ from python3 ones.
        def capture(combinator=combinator, type_=type_):
            def combine(*arguments):
                composition = { 'type': type_, '.combinator': lambda : combinator }
                skip = len(combinator.get('args', []))
                if 'components' not in combinator and len(arguments) > skip:
                    raise ComposerError('Too many arguments in "'+type_+'" combinator')

                for i in range(skip):
                    composition[combinator['args'][i]['name']] = arguments[i]

                if 'components' in combinator:
                    composition['components'] = arguments[skip:]

                return Composition(composition)
            return combine

        setattr(composer, key, capture())

    return composer