Live Chat

We'll need to share your messages (and your email address if you're logged in) with our live chat provider, Drift. Here's their privacy policy.

If you don't want to do this, you can email us instead at contact@anvil.works.

Blog - Page 7

App Secrets
15th of January 2018

Encrypted Storage, Made Easy.

When your web app is handling sensitive information — be it API credentials, database passwords, or sensitive personal data — it’s important to keep it protected. It certainly shouldn’t be sitting in your source code, for anyone to see. It should be kept encrypted, until the time it’s needed. This is called encryption at rest.

With Anvil, you can store password or API keys encrypted in your app, with a name – for example, I could store my GitHub password in a secret called github_password. Then I can use it in my app, like this:

  username = "meredydd"
  password = anvil.secrets.get_secret("github_password")

  r = anvil.http.request("https://api.github.com/user/repos", json=True,
                         username=username, password=password)

I can also create and store an encryption key, and use it to encrypt and decrypt data. Encrypted-at-rest data storage is this simple:

  # This app has an encryption key named 'my_key':

  @anvil.server.callable
  def save_secret_data(new_text):
    encrypted_text = anvil.secrets.encrypt_with_key('my_key', new_text)

    app_tables.secret_data.add_row(text=encrypted_text)

Watch our video tutorial here, or read our API documentation :

Try it yourself

How it works

App Secrets are encrypted, and stored with the source code for your Anvil app. Secrets and keys are encrypted with a key unique to that application – making the encrypted value useless outside that app.

All encryption is performed with high quality, industry-standard encryption schemes that avoid the pitfalls of using low-level cryptographic libraries. For more details, see our documentation .

We believe security and privacy are important, and we are proud to empower our users to use cryptography easily and safely. We are grateful to the security and privacy researchers who have helped us develop and validate this feature, and we welcome contact from the security community at security@anvil.works.


Building a Python Autocompleter
8th of November, 2017

Auto-completing Python on the Web

Why do you need autocompletion, and how does it work? My talk at PyCon UK 2017 explains how – and why – we built an in-browser autocompleter for Anvil.

Watch it below, or scroll down for a transcript:

Anvil logo
For more information about Anvil, check out anvil.works.
Skulpt logo
For more information about Skulpt, check out skulpt.org.
Transcript:

I'd like to start by taking some time to thank the PyCon UK organising committee. This has been our first time sponsoring PyCon UK, and we've been made to feel very welcome.

We make Anvil – it's a tool for building full-stack web apps with nothing but Python. We did this because we think we can make a better developer experience using full Python than using the traditional tools. And autocompletion is part of that.

Having proper code completion gives you discoverability. It lets you explore the APIs, without having to tab to the documentation all the time. It gives you speed, because you go a lot faster when you can hit the Tab key every few characters. It gives you confidence that what you're doing is right, because there are whole classes of bugs you can fix without even hitting the Run button. And it just feels good to use.

We started out thinking we could make a good developer experience without autocomplete. I'm here to tell you that we were wrong.

If you don't use autocomplete yourself, you might not know what you're missing. I really encourage you to try it out. Even if you spend all your days in a terminal with vim or emacs, you can get Jedi as a plugin. Really, just try it for a week. See what happens. You'll be amazed.

Unfortunately, Jedi wasn't quite what we needed. Earlier I said Anvil was "full stack", and that means that it knows about everything from your database tables...

...to your server-side code...

...to your client-side code. And it's got to autocomplete all of them.

What's more, Anvil is web-based, and Jedi is expecting a filesystem. And when you're hitting the Tab key, there's just not enough time to go back to the server to fetch your completions.

So we had to write it ourselves, in Javascript. Which means that, yes, here I am, talking about my Javascript project at a Python conference. (Please save the rotten fruit until after the photos at the end.)

So, what do we do?

Conveniently, because Anvil runs all your client-side Python code in the browser, we have a Javascript parser for Python just lying around the place. (We use the open-source Skulpt project.)

So we can take your code, insert a random symbol at your cursor position, and then feed it to the Skulpt parser. The parser then produces an abstract syntax tree that represents your module.

We can then recursively walk this tree, and build up a representation of what's in scope at any given point in the code. We actually use Javascript's prototypical inheritance for this: Inside scopes (like inside a function) prototypically inherit all the names that are in scope from outside scopes (like the module globals).

And when we hit a Name node that contains the magic cursor symbol, we can just suggest all the things that are currently in scope.

That's not the only thing we want to autocomplete. For example, you can access Anvil's database rows like dictionaries, using square brackets (aka __getitem__). So we have to store which items are available on each type.

We also compute and store what you get if you iterate over an object, what its attributes are, what you get if you call it as a function, and so on.

Speaking of function calls, we also need to infer between modules. For example, in Anvil you write your server code in Python, and you call it from the client with a Python function call. And if you pass something into that server code, you want to be able to autocomplete it inside that server function.

So, as well as saving the top-level scope of every module (so it can be used in import statements from other modules), we also store every outbound call from a module – including calls to the server.

So when we parse this client code, we store this outbound call, along with the types of its arguments. And then when we're parsing the server code for autocomplete, we can just pull out the type information for those arguments, and stick it into the local scope, where it autocompletes.

We can store a lot of information about types. This leads to a rather philosophical discussion about what, exactly, a type is.

You might say, "that's easy, the type of an object is its Python class". But of course, in Python, you can dynamically add attributes to individual object instances. And, arguably, even two dicts aren't really the same type.

So what we actually do is mostly forget the Python class – our autocompleter is duck-typed. As far as we're concerned, these two dictionaries are two separate types, with separate item mappings, and should be treated as such.

There's so much more I could talk about, but this is a short talk. And so, if you remember only one thing, make it this:

Ladies and gentlemen of Pycon UK 2017, use autocomplete!

Thank you very much.




SMS Surveys in Pure Python
10th October 2017

Telephony integration, deployed in minutes

There are some great telephony services for developers out there these days. A simple HTTP call or webhook is all it takes to send and receive messages, but building and hosting a webapp to do this is still far harder than it should be.

Enter Anvil, a platform for full-stack web development in nothing but Python. An SMS survey is a great example of something that should be really simple to build, so I’ll show you how I did it in just a few minutes with Anvil.

First, head over to https://sms-poll.anvilapp.net to see the finished app. You can also take a look at the source code and try it out yourself.

Let’s take a quick tour through the code to see the highlights. There are two main parts:

  • The main form, displaying voting instructions and current live survey results.

  • The Webhook Server Module where you’ll find the HTTP endpoint that can be called for each incoming SMS. I used the excellent Nexmo API, which makes it really simple to receive SMS messages to a custom webhook.

Receiving SMS Messages

Take a look at the Webhook Server Module. You’ll see that it only took one function to handle incoming messages:

  @anvil.server.http_endpoint("/sms")
  @tables.in_transaction
  def incoming_sms(msisdn, text, **kw):
    ...

The incoming_sms function is decorated with @anvil.server.http_endpoint, which means external services can make HTTP requests that trigger the function call. After signing up with Nexmo, I simply set the incoming webhook address to the URL of my HTTP endpoint. In my case, that URL is “https://sms-poll.anvilapp.net/_/api/sms” – your copy of the app will have its own URL, which you can find displayed at the bottom of the Server Module.

HTTP form parameters are passed as keyword arguments to this function. We have explicitly captured the sender (msisdn) and contents (text) of the message, and used **kw to receive the other paramaters. In fact, form parameters, query string parameters and path parameters are all provided as keyword arguments – see the documentation for more information.

Inside the incoming_sms function we add a row to our data table with the details of the incoming message:

    app_tables.messages.add_row(sender=msisdn, time=datetime.now(), message=text)

Next we decide whether the incoming message contains a valid vote, and if so increment the appropriate total in the Results table.

    colours = {
      "A": "Red",
      "B": "Green",
      "C": "Blue",
      "D": "Yellow",
    }

    colour = colours.get(text.upper(), None)
    if colour is not None:
      app_tables.results.get(colour=colour)['votes'] += 1

Readers of a nervous disposition should note that the entire incoming_sms function is decorated with @tables.in_transaction, so operations like incrementing values in the database are perfectly safe. If two requests arrive simultaneously, and they try to edit the same table row, one of them will be automatically rolled back and retried. See the Data Tables documentation for more details, and the Data Tables tutorial for examples.

Displaying poll results

Laying out the UI for our web application takes no code at all – Anvil’s visual designer lets you drag and drop components into place until your page looks the way you want it to look. For this app, we’ll add a few containers and labels, and a Plot component for displaying the chart.

Once we’ve created the Plot component, we have the full Plotly API at our fingertips. Drawing a bar chart couldn’t be easier.

Back in the main form, we query for the latest results:

  results = list(app_tables.results.search())

Then it’s just a matter of creating a Plotly Bar Chart object, and populating it with our results. Notice that we use list comprehensions to assemble the necessary data for each axis:

  # Create a Plotly Bar chart with colours along the x-axis
  # and number of votes on the y-axis.
  self.results_plot.data = go.Bar(
    x=[v['colour'] for v in results],
    y=[v['votes'] for v in results],
    width=0.5,
    opacity=0.6,
    marker=go.Marker(
      # Set the colour of the bar to the colour being voted for
      color=[v['colour'].lower() for v in results],
      line=go.Line(
        color="#888",
        width=1
      )
    )
  )
  
  # Set the axis and plot labels
  self.results_plot.layout = go.Layout(
    yaxis=go.YAxis(
      title="Votes",
      # Start y-axis ticks at 0 and increment by 1.
      tick0=0,
      dtick=1
    ),
    xaxis=go.YAxis(
      title="Colour"
    ),
    title="Live Poll Results (%s votes)" % sum([v['votes'] for v in results])
  )

The final piece of the puzzle is to make the chart update live. For this we use a Timer component on the form, and set its interval to 1 second. In the tick event handler, we simply call the method to redraw the plot.

Ready to go

And there you have it. A complete SMS polling app, built and deployed in no time. Anvil lets you build great apps on top of great APIs (like Nexmo) without any of the hassle traditionally required for web development.

See the code

New: Media in data tables
5th of October 2017

Store files in Data Tables. Why not?

We’re used to storing text and numbers in databases, so why not binary media? Whether it’s images, PDF files or whole spreadsheets, now you can store your files directly in Anvil Data Tables like any other data type.

Just create a Media column in your table:

Creating a media column

Create rows in your table by uploading files:

Uploading table media

And view or download the media objects directly from the database:

Downloading table media

A Media object from a table row behaves just like any other Media object in Anvil - you can assign it to the source property of an Image component, get its URL, or even get its contents as a binary string with the get_bytes() method. See the reference documentation   for more details.

Try it yourself

New: Data Bindings
21st of September 2017

Design your data right onto the page

Let’s say you’re writing a web app, and you need to display and edit some data. Wouldn’t it be great if you could drag and drop to design how your page looks, and where the data goes? Especially if we kept the power and flexibility of writing real code?

We thought so too, and today we’re proud to announce Anvil Data Bindings:



Loading video...
Watch the tutorial

Data bindings extend Anvil’s visual designer. As well as positioning your components on the page with drag and drop, you can now set component properties to any Python expression. You can even assign updated values to this expression when you change your component.

Finally, today we are also introducing the RepeatingPanel. This makes it easy to repeat components for every element in a list - now, displaying items in a table or list is a snap!

You can find out more about data bindings with the tutorial . You can also read the reference documentation .

Learn More

Get the best out of Anvil with our free email course.