ES2015 and React in pure Python environment

Alessandro Molina
6 min readSep 26, 2016

--

ES2015 is the last approved Javascript standard which includes a lot of improvements and features which Python developers are used to. Features like arrow functions, template strings, classes, destructuring, iterators and generators are all features that as Python developers we are used to and that greatly improve your life as a developer (if you don’t know what ES2015 brings, a quick overview of the new features is available at http://es6-features.org)

While ES2015 is now quite widespread, its support in browser is still lacking, which forces developers to use a transpiler to convert ES2015 code to current Javascript that can run in browsers. The problem is that so far using a transpiler was only available to NodeJS environment as most of the required tools only run on Node.

To make things even more complex, modern JS frameworks are now starting to embrace the same approach to allow simpler code bases. Both Angular2 and React introduced a preprocessing step to convert a simpler and easier to read code to the longer version that will actually run on the browser. So if you want to embrace both ES2015 and React you will need two transpiling phases. One for JSX (an HTML like language for JavaScript to generate the React DOM nodes) and one for ES2015.

Last but not least as ES2015 introduced modules support, and the lack of them for years lead to hundreds of different ways to implement modules in Javascript, you will also need a module loader or bundles packager.

This usually forces people to use tools like Webpack and BabelJS to solve the three previously mentioned tasks. Again both of them only work on NodeJS.

As Python developers this force us to always keep around two environments: Node and Python. Have two package manager: Npm and PIP and manage two list of requirements.

Clearly this was far from being perfect and lead many people to look for alternatives to manage everything from Python.

Good news is that it is actually possible.

To package modules WebAssets can be used instead WebPack and BabelJS can be run on DukPy (it actually comes bundled with DukPy).

To show how to create a simple HelloWorld application that uses React, DukPy and WebAssets I’ll use TurboGears2 and the Kajiki template engine, but any WSGI web framework and template engine will do.

First we need to have a working webpage where to inject the React component, so let’s install all we need and get ready:

$ pip install TurboGears2
$ pip install kajiki

Now that the needed pieces are there we can prepare page content

import kajikiCONTENT = kajiki.XMLTemplate(u'''
<html>
<head></head>
<body>
<div id="component">Hello!</div>
</body>
</html>
'''
, mode='html5')

The page itself is pretty simple as our purpose is just to have a target for our React component, even the template itself is not very useful as there are no substitutions undergoing, but we will need it to integrate WebAssets so it’s good to have it there.

Then we need a controller that actually serves the page

from tg import TGController
from tg import expose
class RootController(TGController):
@expose()
def index(self):
return CONTENT().render()

and an application that uses our controller

from tg import AppConfig

config = AppConfig(minimal=True, root_controller=RootController())
application = config.make_wsgi_app()

Now that the application is ready we can start a simple WSGI server to serve it and see the result

from wsgiref.simple_server import make_serverprint("Serving on port 8080...")
httpd = make_server('', 8080, application)
httpd.serve_forever()

Running our Python code and pointing the browser to http://localhost:8080/ should now greet us with an Hello! which we are going to replace with our React component soon.

As we need to serve JavaScript assets, to serve our React component, we will need to tell TurboGears to serve them for us, and we are going to serve them all from a public directory inside our project.

Make sure you create one:

mkdir statics

And then tell TurboGears to serve assets from the newly created directory by adding the relevant options to our application configuration (make sure you add them before calling make_wsgi_app):

config.serve_static = True
config.paths['static_files'] = 'statics'

Now every file we put into the statics directory will be available on http://localhost:8080/filename

Everything is now in place to actually allow for serving our React component, we might just throw react into the statics directory and use it, but our purpose is actually to show how to create a resources bundle, so instead of serving react from the statics folder directly, we are going to serve it through WebAssets.

First of all let’s download react and react-dom into our statics directory from cdnjs.com:

$ cd statics
$ curl -O 'https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.js'
$ curl -O 'https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js'

Now we need to install WebAssets for TurboGears through the tgext.webassets package and register our resources for the bundle:

$ pip install tgext.webassets

Once tgext.webassets is installed we can plug it into our application and create a bundle that includes both react and react-dom and minifies them through rjsmin (make sure again you add following code before calling make_wsgi_app):

import tgext.webassets as wawa.plugme(
config,
bundles={
'bundle.js': wa.Bundle(
'react.js',
'react-dom.js',
filters='rjsmin',
output='assets/bundle.js'
)
}
)

By default tgext.webassets looks for bundle members into the static_files path, so both react.js and react-dom.js should be found if you downloaded them into the statics directory.

Now that our bundle is created we should use it into our page and render a small react element to ensure things are properly set up, change our CONTENT to include the bundle and render the element:

CONTENT = kajiki.XMLTemplate(u'''<html>
<head>
</head>
<body>
<div id="component">Hello!</div>
<script py:for="m in bundle_js.urls()" src="$m"></script>
<script>
ReactDOM.render(
React.createElement('div', {}, 'Hello from React'),
document.getElementById('component')
);
</script>
</body>
</html>
'''
, mode='html5')

As you might have noticed we are using the bundle_js, where does it came from? Well our controller need to make it available to the template, so let’s get our bundle from tgext.webassets and pass it to the template:

from tg import app_globalsclass RootController(TGController):
@expose()
def index(self):
return CONTENT(dict(
bundle_js=app_globals.webassets['bundle.js']
)).render()

If everything worked as expected you should see “Hello from React” when restarting the application and heading to localhost:8080

Now that we have a packaged bundle and react works as expected let’s add the last step: ES2015 and JSX support.

To do so let’s put an HelloWorld.jsx file into the statics directory with our HelloWorld component:

export class HelloWorld extends React.Component {
render() {
return (
<div className="helloworld">
Hello {this.props.name}
</div>
);
}
}

This is no more than a simple “Hello YourName” component, but it’s already enough to showcase how ES2015 classes and inheritance can be used together with JSX (noticed the return <div> thing? That’s JSX)

What we want to do is adding HelloWorld.jsx to our bundle, but as it cannot be executed as it is, we need to specify a different filter, the babeljsx filter which will convert the code to something our browser can understand and add the babel_modules_loader option to tell babel we want our modules managed by the Universal Module Definition (as we are not going to load them through any loader):

wa.plugme(
config,
options={
'babel_modules_loader': 'umd'
},
bundles={
'bundle.js': wa.Bundle(
'react.js',
'react-dom.js',
wa.Bundle(
'HelloWorld.jsx',
filters='babeljsx',
),
filters='rjsmin',
output='assets/bundle.js'
)
}
)

Where does the babeljsx filter come from? We actually need to get it from DukPy and register it into webassets to ensure it’s available (make sure you add this before the plugme call or it will fail due to the missing filter):

from webassets.filter import register_filter
from dukpy.webassets import BabelJSX
register_filter(BabelJSX)

As we have not yet installed dukpy trying to run the application will just fail, so before restarting our app make sure you installed dukpy:

$ pip install dukpy

Pointing our browser to the usual URL won’t show much different, as we are not using our component in any place, we will just see the previous “Hello from React” text.

To see our component in action we need to replace the React.createElement call into our page CONTENT with one that creates our own component:

CONTENT = kajiki.XMLTemplate(u'''<html>
<head>
</head>
<body>
<div id="component">Hello!</div>
<script py:for="m in bundle_js.urls()" src="$m"></script>
<script>
ReactDOM.render(
React.createElement(HelloWorld.HelloWorld,
{'name': 'YourName :)'},
null),
document.getElementById('component')
);
</script>
</body>
</html>
'''
, mode='html5')

Now restarting our application and heading our browser to http://localhost:8080 will now serve our HelloWorld component greeting us with the Hello YourName ;) text.

Congratulations! Your wrote your ES2015 React application only by using Python :D

--

--

Alessandro Molina

Passionate developer and director of engineering. TurboGears2 , Apache Arrow and more. Author of http://pythontdd.com and http://pythonstandardlibrarybook.com