Joyful Python Web App
development with Appier
João Magalhães
Slide 2
Slide 2 text
A bit of history
● Born out of Hive Solutions’ necessity
● Built in 2013 out of Flask Quorum set of Flask extensions
● Simple design influenced by Flask
● “Some batteries included” philosophy
Slide 3
Slide 3 text
“Some batteries included”
● Django, batteries included (too bloated)
● Flask (minimalist, some major things are missing)
● Appier in between both
Slide 4
Slide 4 text
Design decisions
● Object oriented in nature
● Data model layer out of the box
● Built for easy REST API building
● Extensible by design
● Scalable and performant
● Asynchronous (coroutines and futures)
… and JSON
class HelloApp(appier.App):
@appier.route("/", "GET", json=True)
def hello_json(self):
return {"Hello" : "world"}
HelloApp().serve()
Slide 7
Slide 7 text
Async support design
● Scalable (solves the C10K problem)
● Compatible (supports both Python 2.6+ and 3.3+)
● Simple (code looks as similar as possible to “normal”
blocking Appier code)
● Uses (semi-)coroutines together with futures
● Works great with the asyncio module (from standard lib)
Slide 8
Slide 8 text
What’s asynchronous on Appier
● Async from top to bottom & out-of-the box
● Lots of challenges implementing it
● Template rendering
● Data layer
● Filesystem IO, HTTP clients, etc.
Slide 9
Slide 9 text
Not Async example
class NotAsyncHTTPApp(appier.App):
@appier.route("/", "GET")
def example(self):
return appier.get("https://appier.hive.pt/")
NotAsyncHTTPApp().serve()
Slide 10
Slide 10 text
Async example
class AsyncHTTPApp(appier.App):
@appier.route("/", "GET")
async def example(self):
return await appier.get_w("https://appier.hive.pt/")
AsyncHTTPApp().serve()
Slide 11
Slide 11 text
Another async example
class AsyncHTTPApp(appier.App):
@appier.route("/", "GET")
async def example(self):
url = "https://appier.hive.pt/”
await asyncio.sleep(3)
response = await aiohttp.request("GET", url)
return await response.read()
AsyncHTTPApp().serve()
Slide 12
Slide 12 text
Async data layer
class AsyncModelsApp(appier.App):
@appier.route("/", "GET")
async def example(self):
person = models.Person(name="Tobias")
await person.save()
print(person.id)
person = await models.Person.get(name="Tobias")
return person.map()
HelloApp().serve()
Slide 13
Slide 13 text
Templates
● Modular template support
● Jinja2 is first class citizen
● Async rendering support for Jinja2 out-of-the-box
Slide 14
Slide 14 text
Models
● Simple model design approach
● Multiple back-end support
● Validations, pre_*, post_* and on_* hooks
● Custom type support (implement loads() and dumps())
● Async support (still under construction)
Slide 15
Slide 15 text
Models - Example
class Person(appier.Model):
identifier = appier.field(
type = int,
index = True,
increment = True
)
name = appier.field()
age = appier.field(
type = int
)
Slide 16
Slide 16 text
Models - Custom Types
class Person(appier.Model):
thumbnail = appier.field(
type = appier.image(
width = 400,
height = 400,
format = "png"
)
)
● Admin interface
● Access control
● Auto-snapshotting of models
● Encrypted model fields
● App configuration (JSON, env variables, DB, other)
Other cool features
What’s joy ?
● Having a minimal set of dependencies
● Owning the code by understanding it
● Having code that scales properly
● Using simple tools to build complex things
Slide 22
Slide 22 text
Q & A
Best question gets a free PyCharm subscription