enaml hacking: "coded widget" that compiles and includes widgets from a source code attribute

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

enaml hacking: "coded widget" that compiles and includes widgets from a source code attribute

Matthew Scott
Enaml enthusiasts might enjoy this little hack:

To try it out:
$ git clone https://github.com/11craft/kilometer-browser
$ enaml-run kilometer-browser/kmb/components/codedwidget.enaml

Renaming suggestions happily accepted!  :)  I wasn't sure what else to call it for now.

I found the exploration toward this particular solution interesting.  There is definitely more than one way to do things to achieve the same goal.

For example, I originally used something like this (extra code excluded, so this won't work directly):

    enamldef CodedWidget(Container):
        attr main_component
        attr module
        main_component << include.components[0] if include.components else None
        Include:
            id: include
            components << [module.Main()] if hasattr(module, 'Main') else []       

This worked great... the only reason I changed it to what it is now is because I wanted to capture compile-time and run-time exceptions into their own attributes.  So it transformed to this:

    enamldef CodedWidget(Container):
        attr main_component = None
        attr module
        module ::
            self.main_component = module.Main()
        Include:
            components << [main_component] if main_component else []

For discussion here, the source for just the CodedWidget component itself:



enamldef CodedWidget(Container):

    # just contain the widget, don't pad it
    padding = (0, 0, 0, 0)

    # enaml source code to compile (UTF-8 encoded)
    attr source_code: str = ''
    # compile the source code on change, and store the resulting module
    source_code ::
        try:
            enaml_file = '<string>'
            ast = parse(source_code, filename=enaml_file)
            code = EnamlCompiler.compile(ast, enaml_file)
            module = types.ModuleType('__main__')
            module.__file__ = enaml_file
            ns = module.__dict__
            with imports():
                exec code in ns
            self.module = module
        except Exception, e:
            # Something happened, so don't trust the module; empty instead.
            self.module = None
            self.compile_exception = e
            raise
        else:
            self.compile_exception = None

    # module that results from compiling source_code
    attr module = None
    module ::
        if hasattr(module, 'Main'):
            try:
                main = module.Main()
            except Exception, e:
                self.runtime_exception = e
                raise
            else:
                self.runtime_exception = None
                self.main_component = main
        else:
            self.main_component = None

    # instance of the Main component from the module
    attr main_component = None

    # exception that occurred during compilation
    attr compile_exception = None
    # exception that occurred when calling Main()
    attr runtime_exception = None

    Include:
        components << [main_component] if main_component is not None else []



-- 
Matthew Scott
ElevenCraft Inc.


_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: enaml hacking: "coded widget" that compiles and includes widgets from a source code attribute

Chris Colbert


On Thu, Jun 28, 2012 at 10:31 PM, Matthew Scott <[hidden email]> wrote:
Enaml enthusiasts might enjoy this little hack:

To try it out:
$ enaml-run kilometer-browser/kmb/components/codedwidget.enaml

Renaming suggestions happily accepted!  :)  I wasn't sure what else to call it for now.


This is pretty clever :-)
 
I found the exploration toward this particular solution interesting.  There is definitely more than one way to do things to achieve the same goal.

For example, I originally used something like this (extra code excluded, so this won't work directly):

    enamldef CodedWidget(Container):
        attr main_component
        attr module
        main_component << include.components[0] if include.components else None
        Include:
            id: include
            components << [module.Main()] if hasattr(module, 'Main') else []       


As an FYI, attribute declarations can have a default binding.

So instead of:

attr main_component
main_component << include.components[0] if include.components else None

You could do this (if you wanted):

attr main_component << include.components[0] if include.components else None
 

This worked great... the only reason I changed it to what it is now is because I wanted to capture compile-time and run-time exceptions into their own attributes.  So it transformed to this:

    enamldef CodedWidget(Container):
        attr main_component = None
        attr module
        module ::
            self.main_component = module.Main()
        Include:
            components << [main_component] if main_component else []

For discussion here, the source for just the CodedWidget component itself:



enamldef CodedWidget(Container):

    # just contain the widget, don't pad it
    padding = (0, 0, 0, 0)

    # enaml source code to compile (UTF-8 encoded)
    attr source_code: str = ''
    # compile the source code on change, and store the resulting module
    source_code ::
        try:
            enaml_file = '<string>'
            ast = parse(source_code, filename=enaml_file)
            code = EnamlCompiler.compile(ast, enaml_file)
            module = types.ModuleType('__main__')
            module.__file__ = enaml_file
            ns = module.__dict__
            with imports():
                exec code in ns
            self.module = module
        except Exception, e:
            # Something happened, so don't trust the module; empty instead.
            self.module = None
            self.compile_exception = e
            raise
        else:
            self.compile_exception = None

    # module that results from compiling source_code
    attr module = None
    module ::
        if hasattr(module, 'Main'):
            try:
                main = module.Main()
            except Exception, e:
                self.runtime_exception = e
                raise
            else:
                self.runtime_exception = None
                self.main_component = main
        else:
            self.main_component = None

    # instance of the Main component from the module
    attr main_component = None

    # exception that occurred during compilation
    attr compile_exception = None
    # exception that occurred when calling Main()
    attr runtime_exception = None

    Include:
        components << [main_component] if main_component is not None else []



-- 
Matthew Scott
ElevenCraft Inc.


_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev



_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev