Esempio n. 1
0
def create_js_component_class(cls, cls_name, base_class='Component.prototype'):
    """ Create the JS equivalent of a subclass of the Component class.
    
    Given a Python class with actions, properties, emitters and reactions,
    this creates the code for the JS version of the class. It also supports
    class constants that are int/float/str, or a tuple/list thereof.
    The given class does not have to be a subclass of Component.
    
    This more or less does what ComponentMeta does, but for JS.
    """

    assert cls_name != 'Component'  # we need this special class above instead

    # Collect meta information of all code pieces that we collect
    mc = MetaCollector(cls)
    mc.meta['std_functions'].add(
        'op_instantiate')  # b/c we use get_class_definition

    total_code = []
    funcs_code = []  # functions and emitters go below class constants
    const_code = []
    err = ('Objects on JS Component classes can only be int, float, str, '
           'or a list/tuple thereof. Not %s -> %r.')

    total_code.append('\n'.join(get_class_definition(cls_name,
                                                     base_class)).rstrip())
    prefix = '' if cls_name.count('.') else 'var '
    total_code[0] = prefix + total_code[0]
    prototype_prefix = '$' + cls_name.split('.')[-1] + '.'
    total_code.append('var %s = %s.prototype;' %
                      (prototype_prefix[:-1], cls_name))

    # Process class items in original order or sorted by name if we cant
    class_items = cls.__dict__.items()
    if sys.version_info < (3, 6):  # pragma: no cover
        class_items = sorted(class_items)

    for name, val in class_items:
        if isinstance(val, ActionDescriptor):
            # Set underlying function as class attribute. This is overwritten
            # by the instance, but this way super() works.
            funcname = name
            # Add function def
            code = mc.py2js(val._func, prototype_prefix + funcname)
            code = code.replace('super()', base_class)  # fix super
            # Tweak if this was an autogenerated action
            # we use flx_ prefixes to indicate autogenerated functions
            if val._func.__name__.startswith('flx_'):
                subname = name
                if name.startswith('set_') or name.startswith('_set_'):
                    subname = name[4:]
                code = code.replace("flx_name", "'%s'" % subname)
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            funcs_code.append(prototype_prefix + funcname + '.nobind = true;')
            funcs_code.append('')
        elif isinstance(val, ReactionDescriptor):
            funcname = name  # funcname is simply name, so that super() works
            # Add function def
            code = mc.py2js(val._func, prototype_prefix + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            funcs_code.append(prototype_prefix + funcname + '.nobind = true;')
            # Add connection strings, but not for implicit reactions
            if val._connection_strings:
                funcs_code.append(prototype_prefix + funcname +
                                  '._connection_strings = ' +
                                  reprs(val._connection_strings))
            funcs_code.append('')
        elif isinstance(val, EmitterDescriptor):
            funcname = name
            # Add function def
            code = mc.py2js(val._func, prototype_prefix + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            funcs_code.append(prototype_prefix + funcname + '.nobind = true;')
            funcs_code.append('')
        elif isinstance(val, Attribute):
            pass
        elif isinstance(val, Property):
            # Mutator and validator functions are picked up as normal functions.
            # Set default value on class.
            default_val = json.dumps(val._default)
            t = '%s_%s_value = %s;'
            const_code.append(t % (prototype_prefix, name, default_val))
        elif isinstance(val, classmethod):
            pass  # ignore, like magics
        elif (name.startswith('__') and name not in OK_MAGICS
              and not name.endswith('_validate')):
            # These are only magics, since class attributes with double-underscores
            # have already been mangled. Note that we need to exclude validator
            # funcs of private properties though.
            pass
        elif (name.endswith('_validate') and hasattr(val, '__self__')
              and isinstance(val.__self__, Property)):
            # Proxy the validator functions (not inline).
            prop_class = val.__self__.__class__
            mod_name_parts = prop_class.__module__.split('.')
            module_ns = sys.modules[cls.__module__].__dict__
            # Get canonical class name, included part of the module name, as
            # needed, depending on what names exist in the component module.
            prop_class_name = prop_class.__name__
            if prop_class_name not in module_ns:
                for ip in reversed(range(0, len(mod_name_parts))):
                    if mod_name_parts[ip] in module_ns:
                        prop_class_name = mod_name_parts[
                            ip] + '.' + prop_class_name
                        break
            # Create function that calls the _validate function
            t = ' = function (value) { return %s(value, %s, %s); }\n'
            code = prototype_prefix + name + t % (
                prop_class_name + '.prototype._validate', json.dumps(
                    name[1:-9]), json.dumps(val.__self__._data))
            funcs_code.append(code)
            mc.meta['vars_unknown'].add(prop_class_name)
        elif callable(val):
            # Functions, including methods attached by the meta class
            code = mc.py2js(val, prototype_prefix + name)
            code = code.replace('super()', base_class)  # fix super
            if val.__name__.startswith('flx_'):
                subname = name[8:] if name.startswith('_mutate_') else name
                code = code.replace("flx_name", "'%s'" % subname)
            funcs_code.append(code.rstrip())
            funcs_code.append('')
        else:
            # Static simple (json serializable) attributes, e.g. __actions__ etc.
            try:
                serialized = json.dumps(val)
            except Exception as err:  # pragma: no cover
                raise ValueError('Attributes on JS Component class must be '
                                 'JSON compatible.\n%s' % str(err))
            const_code.append(prototype_prefix + name + ' = ' + serialized)

    if const_code:
        total_code.append('')
        total_code.extend(const_code)
    if funcs_code:
        total_code.append('')
        total_code.extend(funcs_code)
    total_code.append('')

    # Return string with meta info (similar to what py2js returns)
    mc.meta['vars_unknown'].discard('flx_name')
    return mc.attach_meta('\n'.join(total_code))
Esempio n. 2
0
def create_js_hasevents_class(cls, cls_name, base_class='HasEvents.prototype'):
    """ Create the JS equivalent of a subclass of the HasEvents class.
    
    Given a Python class with handlers, properties and emitters, this
    creates the code for the JS version of this class. It also supports
    class constants that are int/float/str, or a tuple/list thereof.
    The given class does not have to be a subclass of HasEvents.
    
    This more or less does what HasEventsMeta does, but for JS.
    """
    
    assert cls_name != 'HasEvents'  # we need this special class above instead
    
    handlers = []
    emitters = []
    properties = []
    total_code = []
    funcs_code = []  # functions and emitters go below class constants
    const_code = []
    err = ('Objects on JS HasEvents classes can only be int, float, str, '
           'or a list/tuple thereof. Not %s -> %r.')
    
    total_code.append('\n'.join(get_class_definition(cls_name, base_class)).rstrip())
    prefix = '' if cls_name.count('.') else 'var '
    total_code[0] = prefix + total_code[0]
    
    # Functions to ignore
    special_funcs = ['_%s_func' % name for name in 
                     (cls.__handlers__ + cls.__emitters__ + cls.__properties__)]
    OK_MAGICS = ('__properties__', '__emitters__', '__handlers__',
                 '__proxy_properties__', '__emitter_flags__')
    
    for name, val in sorted(cls.__dict__.items()):
        name = name.replace('_JS__', '_%s__' % cls_name.split('.')[-1])  # fix mangling
        funcname = '_' + name + '_func'
        if name in special_funcs:
            pass
        elif isinstance(val, BaseEmitter):
            if isinstance(val, Property):
                properties.append(name)
            else:
                emitters.append(name)
            # Add function def
            code = py2js(val._func, cls_name + '.prototype.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.prototype.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Has default val?
            if isinstance(val, Property) and val._defaults:
                default_val = json.dumps(val._defaults[0])
                t = '%s.prototype.%s.default = %s;'
                funcs_code.append(t % (cls_name, funcname, default_val))
            # Add type of emitter
            t = '%s.prototype.%s.emitter_type = %s;'
            emitter_type = val.__class__.__name__
            funcs_code.append(t % (cls_name, funcname, reprs(emitter_type)))
            funcs_code.append('')
        elif isinstance(val, HandlerDescriptor):
            handlers.append(name)
            # Add function def
            code = py2js(val._func, cls_name + '.prototype.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.prototype.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Add connection strings to the function object
            t = '%s.prototype.%s._connection_strings = %s;'
            funcs_code.append(t % (cls_name, funcname, reprs(val._connection_strings)))
            funcs_code.append('')
        elif callable(val):
            code = py2js(val, cls_name + '.prototype.' + name)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            funcs_code.append('')
        elif name in OK_MAGICS:
            t = '%s.prototype.%s = %s;'
            const_code.append(t % (cls_name, name, reprs(val)))
        elif name.startswith('__'):
            pass  # we create our own __emitters__, etc.
        else:
            try:
                serialized = json.dumps(val)
            except Exception as err:  # pragma: no cover
                raise ValueError('Attributes on JS HasEvents class must be '
                                 'JSON compatible.\n%s' % str(err))
            #const_code.append('%s.prototype.%s = JSON.parse(%s)' %
            #                  (cls_name, name, reprs(serialized)))
            const_code.append('%s.prototype.%s = %s;' % (cls_name, name, serialized))
    
    if const_code:
        total_code.append('')
        total_code.extend(const_code)
    if funcs_code:
        total_code.append('')
        total_code.extend(funcs_code)
    total_code.append('')
    return '\n'.join(total_code)
Esempio n. 3
0
def create_js_hasevents_class(cls, cls_name, base_class='HasEvents.prototype'):
    """ Create the JS equivalent of a subclass of the HasEvents class.
    
    Given a Python class with handlers, properties and emitters, this
    creates the code for the JS version of this class. It also supports
    class constants that are int/float/str, or a tuple/list thereof.
    The given class does not have to be a subclass of HasEvents.
    
    This more or less does what HasEventsMeta does, but for JS.
    """

    assert cls_name != 'HasEvents'  # we need this special class above instead

    handlers = []
    emitters = []
    properties = []
    total_code = []
    funcs_code = []  # functions and emitters go below class constants
    const_code = []
    err = ('Objects on JS HasEvents classes can only be int, float, str, '
           'or a list/tuple thereof. Not %s -> %r.')

    total_code.append('\n'.join(get_class_definition(cls_name,
                                                     base_class)).rstrip())
    prefix = '' if cls_name.count('.') else 'var '
    total_code[0] = prefix + total_code[0]

    # Functions to ignore
    special_funcs = [
        '_%s_func' % name
        for name in (cls.__handlers__ + cls.__emitters__ + cls.__properties__)
    ]
    OK_MAGICS = ('__properties__', '__emitters__', '__handlers__',
                 '__proxy_properties__', '__emitter_flags__')

    for name, val in sorted(cls.__dict__.items()):
        name = name.replace('_JS__',
                            '_%s__' % cls_name.split('.')[-1])  # fix mangling
        funcname = '_' + name + '_func'
        if name in special_funcs:
            pass
        elif isinstance(val, BaseEmitter):
            if isinstance(val, Property):
                properties.append(name)
            else:
                emitters.append(name)
            # Add function def
            code = py2js(val._func, cls_name + '.prototype.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.prototype.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Has default val?
            if isinstance(val, Property) and val._defaults:
                default_val = json.dumps(val._defaults[0])
                t = '%s.prototype.%s.default = %s;'
                funcs_code.append(t % (cls_name, funcname, default_val))
            # Add type of emitter
            t = '%s.prototype.%s.emitter_type = %s;'
            emitter_type = val.__class__.__name__
            funcs_code.append(t % (cls_name, funcname, reprs(emitter_type)))
            funcs_code.append('')
        elif isinstance(val, HandlerDescriptor):
            handlers.append(name)
            # Add function def
            code = py2js(val._func, cls_name + '.prototype.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.prototype.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Add connection strings to the function object
            t = '%s.prototype.%s._connection_strings = %s;'
            funcs_code.append(
                t % (cls_name, funcname, reprs(val._connection_strings)))
            funcs_code.append('')
        elif callable(val):
            code = py2js(val, cls_name + '.prototype.' + name)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            funcs_code.append('')
        elif name in OK_MAGICS:
            t = '%s.prototype.%s = %s;'
            const_code.append(t % (cls_name, name, reprs(val)))
        elif name.startswith('__'):
            pass  # we create our own __emitters__, etc.
        else:
            try:
                serialized = json.dumps(val)
            except Exception as err:  # pragma: no cover
                raise ValueError('Attributes on JS HasEvents class must be '
                                 'JSON compatible.\n%s' % str(err))
            #const_code.append('%s.prototype.%s = JSON.parse(%s)' %
            #                  (cls_name, name, reprs(serialized)))
            const_code.append('%s.prototype.%s = %s;' %
                              (cls_name, name, serialized))

    if const_code:
        total_code.append('')
        total_code.extend(const_code)
    if funcs_code:
        total_code.append('')
        total_code.extend(funcs_code)
    total_code.append('')
    return '\n'.join(total_code)
Esempio n. 4
0
File: _js.py Progetto: pigay/flexx
def create_js_hasevents_class(cls, cls_name, base_class='HasEvents.Ƥ'):
    """ Create the JS equivalent of a subclass of the HasEvents class.
    
    Given a Python class with handlers, properties and emitters, this
    creates the code for the JS version of this class. It also supports
    class constants that are int/float/str, or a tuple/list thereof.
    The given class does not have to be a subclass of HasEvents.
    
    This more or less does what HasEventsMeta does, but for JS.
    """

    assert cls_name != 'HasEvents'  # we need this special class above instead

    # Collect meta information of all code pieces that we collect
    meta = {
        'vars_unknown': set(),
        'vars_global': set(),
        'std_functions': set(),
        'std_methods': set(),
        'linenr': 1e9
    }

    def py2js_local(*args, **kwargs):
        code = py2js(*args, **kwargs)
        for key in meta:
            if key == 'linenr':
                meta[key] = min(meta[key], code.meta[key])
            else:
                meta[key].update(code.meta[key])
        return code

    handlers = []
    emitters = []
    properties = []
    total_code = []
    funcs_code = []  # functions and emitters go below class constants
    const_code = []
    err = ('Objects on JS HasEvents classes can only be int, float, str, '
           'or a list/tuple thereof. Not %s -> %r.')

    total_code.append('\n'.join(get_class_definition(cls_name,
                                                     base_class)).rstrip())
    prefix = '' if cls_name.count('.') else 'var '
    total_code[0] = prefix + total_code[0]

    # Functions to ignore
    OK_MAGICS = ('__properties__', '__emitters__', '__handlers__',
                 '__local_properties__')

    for name, val in sorted(cls.__dict__.items()):
        name = name.replace('_JS__',
                            '_%s__' % cls_name.split('.')[-1])  # fix mangling
        if isinstance(val, BaseEmitter):
            funcname = name
            if isinstance(val, Property):
                properties.append(name)
            else:
                emitters.append(name)
            # Add function def
            code = py2js_local(val._func, cls_name + '.Ƥ.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.Ƥ.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Has default val?
            if isinstance(val, Property) and val._defaults:
                default_val = json.dumps(val._defaults[0])
                t = '%s.Ƥ.%s.default = %s;'
                funcs_code.append(t % (cls_name, funcname, default_val))
            # Add type of emitter
            t = '%s.Ƥ.%s.emitter_type = %s;'
            emitter_type = val.__class__.__name__
            funcs_code.append(t % (cls_name, funcname, reprs(emitter_type)))
            funcs_code.append('')
        elif isinstance(val, HandlerDescriptor):
            funcname = name  # funcname is simply name, so that super() works
            handlers.append(name)
            # Add function def
            code = py2js_local(val._func, cls_name + '.Ƥ.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.Ƥ.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Add connection strings to the function object
            t = '%s.Ƥ.%s._connection_strings = %s;'
            funcs_code.append(
                t % (cls_name, funcname, reprs(val._connection_strings)))
            funcs_code.append('')
        elif callable(val):
            code = py2js_local(val, cls_name + '.Ƥ.' + name)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            funcs_code.append('')
        elif name in OK_MAGICS:
            t = '%s.Ƥ.%s = %s;'
            const_code.append(t % (cls_name, name, reprs(val)))
        elif name.startswith('__'):
            pass  # we create our own __emitters__, etc.
        else:
            try:
                serialized = json.dumps(val)
            except Exception as err:  # pragma: no cover
                raise ValueError('Attributes on JS HasEvents class must be '
                                 'JSON compatible.\n%s' % str(err))
            #const_code.append('%s.Ƥ.%s = JSON.parse(%s)' %
            #                  (cls_name, name, reprs(serialized)))
            const_code.append('%s.Ƥ.%s = %s;' % (cls_name, name, serialized))

    if const_code:
        total_code.append('')
        total_code.extend(const_code)
    if funcs_code:
        total_code.append('')
        total_code.extend(funcs_code)
    total_code.append('')

    # Return string with meta info (similar to what py2js returns)
    js = JSString('\n'.join(total_code))
    js.meta = meta
    return js
Esempio n. 5
0
def create_js_hasevents_class(cls, cls_name, base_class='HasEvents.prototype'):
    """ Create the JS equivalent of a subclass of the HasEvents class.
    
    Given a Python class with handlers, properties and emitters, this
    creates the code for the JS version of this class. It also supports
    class constants that are int/float/str, or a tuple/list thereof.
    The given class does not have to be a subclass of HasEvents.
    
    This more or less does what HasEventsMeta does, but for JS.
    """
    
    assert cls_name != 'HasEvents'  # we need this special class above instead
    
    # Collect meta information of all code pieces that we collect
    meta = {'vars_unknown': set(), 'vars_global': set(), 'std_functions': set(),
            'std_methods': set(), 'linenr': 1e9}
    def py2js_local(*args, **kwargs):
        code = py2js(*args, **kwargs)
        for key in meta:
            if key == 'linenr':
                meta[key] = min(meta[key], code.meta[key])
            else:
                meta[key].update(code.meta[key])
        return code
    
    handlers = []
    emitters = []
    properties = []
    total_code = []
    funcs_code = []  # functions and emitters go below class constants
    const_code = []
    err = ('Objects on JS HasEvents classes can only be int, float, str, '
           'or a list/tuple thereof. Not %s -> %r.')
    
    total_code.append('\n'.join(get_class_definition(cls_name, base_class)).rstrip())
    prefix = '' if cls_name.count('.') else 'var '
    total_code[0] = prefix + total_code[0]
    
    # Functions to ignore
    OK_MAGICS = ('__properties__', '__emitters__', '__handlers__',
                 '__local_properties__')
    
    # Process class items in original order or sorted by name if we cant
    class_items = cls.__dict__.items()
    if sys.version_info < (3, 6):
        class_items = sorted(class_items)
    
    for name, val in class_items:
        name = name.replace('_JS__', '_%s__' % cls_name.split('.')[-1])  # fix mangling
        if isinstance(val, BaseEmitter):
            funcname = name
            if isinstance(val, Property):
                properties.append(name)
            else:
                emitters.append(name)
            # Add function def
            code = py2js_local(val._func, cls_name + '.prototype.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.prototype.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Has default val?
            if isinstance(val, Property) and val._defaults:
                default_val = json.dumps(val._defaults[0])
                t = '%s.prototype.%s.default = %s;'
                funcs_code.append(t % (cls_name, funcname, default_val))
            # Add type of emitter
            t = '%s.prototype.%s.emitter_type = %s;'
            emitter_type = val.__class__.__name__
            funcs_code.append(t % (cls_name, funcname, reprs(emitter_type)))
            funcs_code.append('')
        elif isinstance(val, HandlerDescriptor):
            funcname = name  # funcname is simply name, so that super() works
            handlers.append(name)
            # Add function def
            code = py2js_local(val._func, cls_name + '.prototype.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.prototype.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Add connection strings to the function object
            t = '%s.prototype.%s._connection_strings = %s;'
            funcs_code.append(t % (cls_name, funcname, reprs(val._connection_strings)))
            funcs_code.append('')
        elif callable(val):
            code = py2js_local(val, cls_name + '.prototype.' + name)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            funcs_code.append('')
        elif name in OK_MAGICS:
            t = '%s.prototype.%s = %s;'
            const_code.append(t % (cls_name, name, reprs(val)))
        elif name.startswith('__'):
            pass  # we create our own __emitters__, etc.
        else:
            try:
                serialized = json.dumps(val)
            except Exception as err:  # pragma: no cover
                raise ValueError('Attributes on JS HasEvents class must be '
                                 'JSON compatible.\n%s' % str(err))
            #const_code.append('%s.prototype.%s = JSON.parse(%s)' %
            #                  (cls_name, name, reprs(serialized)))
            const_code.append('%s.prototype.%s = %s;' % (cls_name, name, serialized))
    
    if const_code:
        total_code.append('')
        total_code.extend(const_code)
    if funcs_code:
        total_code.append('')
        total_code.extend(funcs_code)
    total_code.append('')
    
    # Return string with meta info (similar to what py2js returns)
    js = JSString('\n'.join(total_code))
    js.meta = meta
    return js
Esempio n. 6
0
def create_js_hasevents_class(cls, cls_name, base_class='HasEvents.prototype'):
    """ Create the JS equivalent of a subclass of the HasEvents class.
    
    Given a Python class with handlers, properties and emitters, this
    creates the code for the JS version of this class. It also supports
    class constants that are int/float/str, or a tuple/list thereof.
    """

    assert cls_name != 'HasEvents'  # we need this special class above instead

    handlers = []
    emitters = []
    properties = []
    total_code = []
    funcs_code = []  # functions and emitters go below class constants
    const_code = []
    err = ('Objects on JS HasEvents classes can only be int, float, str, '
           'or a list/tuple thereof. Not %s -> %r.')

    total_code.extend(get_class_definition(cls_name, base_class))
    prefix = '' if cls_name.count('.') else 'var '
    total_code[0] = prefix + total_code[0]

    for name, val in sorted(cls.__dict__.items()):
        name = name.replace('_JS__',
                            '_%s__' % cls_name.split('.')[-1])  # fix mangling
        funcname = '_' + name + '_func'
        if isinstance(val, BaseEmitter):
            if isinstance(val, Property):
                properties.append(name)
            else:
                emitters.append(name)
            # Add function def
            code = py2js(val._func, cls_name + '.prototype.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.prototype.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Add type of emitter
            t = '%s.prototype.%s._emitter_type = %s;'
            emitter_type = val.__class__.__name__
            funcs_code.append(t % (cls_name, funcname, reprs(emitter_type)))
            funcs_code.append('')
        elif isinstance(val, HandlerDescriptor):
            handlers.append(name)
            # Add function def
            code = py2js(val._func, cls_name + '.prototype.' + funcname)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            # Mark to not bind the func
            t = '%s.prototype.%s.nobind = true;'
            funcs_code.append(t % (cls_name, funcname))
            # Add connection strings to the function object
            t = '%s.prototype.%s._connection_strings = %s;'
            funcs_code.append(
                t % (cls_name, funcname, reprs(val._connection_strings)))
            funcs_code.append('')
        elif callable(val):
            code = py2js(val, cls_name + '.prototype.' + name)
            code = code.replace('super()', base_class)  # fix super
            funcs_code.append(code.rstrip())
            funcs_code.append('')
        elif name.startswith('__'):
            pass  # we create our own __emitters__ list
        else:
            try:
                serialized = json.dumps(val)
            except Exception as err:  # pragma: no cover
                raise ValueError('Attributes on JS HasEvents class must be '
                                 'JSON compatible.\n%s' % str(err))
            const_code.append('%s.prototype.%s = JSON.parse(%s)' %
                              (cls_name, name, reprs(serialized)))

    # Store handlers, properties and emitters that we found
    if base_class in ('Object', 'HasEvents.prototype'):
        t = '%s.prototype.__emitters__ = %s;'
        total_code.append(t % (cls_name, reprs(list(sorted(emitters)))))
        t = '%s.prototype.__properties__ = %s;'
        total_code.append(t % (cls_name, reprs(list(sorted(properties)))))
        t = '%s.prototype.__handlers__ = %s;'
        total_code.append(t % (cls_name, reprs(list(sorted(handlers)))))
    else:
        t = '%s.prototype.__emitters__ = %s.__emitters__.concat(%s).sort();'
        total_code.append(t % (cls_name, base_class, reprs(emitters)))
        t = '%s.prototype.__properties__ = %s.__properties__.concat(%s).sort();'
        total_code.append(t % (cls_name, base_class, reprs(properties)))
        t = '%s.prototype.__handlers__ = %s.__handlers__.concat(%s).sort();'
        total_code.append(t % (cls_name, base_class, reprs(handlers)))

    total_code.append('')
    total_code.extend(const_code)
    total_code.append('')
    total_code.extend(funcs_code)
    return '\n'.join(total_code)