Welcome to Archer

Archer: develop Thrift RPC service the Flask way

Welcome to Archer’s documentation. This documentation is divided into different parts. I recommend that you get started with Installation and then head over to the Quickstart. Besides the quickstart, there is also a more detailed tutorial that shows how to create a complete (albeit small) application with Archer. If you’d rather dive into the internals of Archer, check out the API documentation.

Archer depends on two external libraries: the Thriftpy interpreter engine and the Click cli parser. These libraries are not documented here. If you want to dive into their documentation, check out the following links:

User’s Guide

This part of the documentation, which is mostly prose, begins with some background information about Archer, then focuses on step-by-step instructions for web development with Archer.

foreword

Why Thrift

The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages.

You just wrie a thrift file:

thrift --gen <language> <Thrift filename>

after compiling for a given language, the corresponding SDK files are generated.

Why Thriftpy

Thriftpy is a Python implementation of Thrift which generates SDK modules dynamically when some thrift file is loaded, No SDK files any more, making development procedure more fluently.

Installation

Archer depends on some external libraries, like Thriftpy and Click. Thriftpy is an Thrift interface definition language interpreter written in Python which would load a thrift file and generate SDK module on the fly, saving you the time for compiling the thrift file by hand.

Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary.

So how do you get all that on your computer quickly? There are many ways you could do that, but the most kick-ass method is virtualenv, so let’s have a look at that first.

You will need Python 2.6 or newer to get started, so be sure to have an up-to-date Python 2.x installation. Python 3.x would also be OK.

virtualenv

Virtualenv is probably what you want to use during development, and if you have shell access to your production machines, you’ll probably want to use it there, too.

What problem does virtualenv solve? If you like Python as much as I do, chances are you want to use it for other projects besides Archer-based RPC applications. But the more projects you have, the more likely it is that you will be working with different versions of Python itself, or at least different versions of Python libraries. Let’s face it: quite often libraries break backwards compatibility, and it’s unlikely that any serious application will have zero dependencies. So what do you do if two or more of your projects have conflicting dependencies?

Virtualenv to the rescue! Virtualenv enables multiple side-by-side installations of Python, one for each project. It doesn’t actually install separate copies of Python, but it does provide a clever way to keep different project environments isolated. Let’s see how virtualenv works.

If you are on Mac OS X or Linux, chances are that one of the following two commands will work for you:

$ sudo easy_install virtualenv

or even better:

$ sudo pip install virtualenv

One of these will probably install virtualenv on your system. Maybe it’s even in your package manager. If you use Ubuntu, try:

$ sudo apt-get install python-virtualenv

Once you have virtualenv installed, just fire up a shell and create your own environment. I usually create a project folder and a venv folder within:

$ mkdir myproject
$ cd myproject
$ virtualenv venv
New python executable in venv/bin/python
Installing setuptools, pip............done.

Now, whenever you want to work on a project, you only have to activate the corresponding environment. On OS X and Linux, do the following:

$ . venv/bin/activate

If you are a Windows user, the following command is for you:

$ venv\scripts\activate

Either way, you should now be using your virtualenv (notice how the prompt of your shell has changed to show the active environment).

And if you want to go back to the real world, use the following command:

$ deactivate

After doing this, the prompt of your shell should be as familiar as before.

Now, let’s move on. Enter the following command to get Archer activated in your virtualenv:

$ pip install Archer

A few seconds later and you are good to go.

System-Wide Installation

This is possible as well, though I do not recommend it. Just run pip with root privileges:

$ sudo pip install Archer

(On Windows systems, run it in a command-prompt window with administrator privileges, and leave out sudo.)

Living on the Edge

If you want to work with the latest version of Archer, there are two ways: you can either let pip pull in the development version, or you can tell it to operate on a git checkout. Either way, virtualenv is recommended.

Get the git checkout in a new virtualenv and run in development mode:

$ git clone http://github.com/eleme/archer.git
Initialized empty Git repository in ~/dev/archer/.git/
$ cd archer
$ virtualenv venv
New python executable in venv/bin/python
Installing setuptools, pip............done.
$ . venv/bin/activate
$ python setup.py develop
...
Finished processing dependencies for Archer

This will pull in the dependencies and activate the git head as the current version inside the virtualenv. Then all you have to do is run git pull origin to update to the latest version.

Quickstart

This page gives a good introduction to Archer. It assumes you already have Archer installed. If you do not, head over to the Installation section.

A Minimal Application

A minimal Archer application looks something like this:

from archer import Archer
app = Archer('PingPong')

@app.api('ping')
def ping():
    return 'pong'

provided a hello.thrift file under your working directory, define a service in the file:

service PingPong {
    string ping(),
}

Note

Archer accepts one parameter, that is the name of the service defined in the thrift file.

So what did that code do?

  1. First we imported the Archer class. An instance of this class will be our Thrift RPC server_side application.
  2. Next we create an instance of this class. The first argument is the name of the application
  3. We then use the api() decorator to tell Archer what name defined in thrift file should trigger our function.
  4. The function returns the message for ping RPC call, which is a string pong here
  5. the service PingPong is defined in the hello.thrift file

Just save the python code as hello.py (or something similar) and the service definition in hello.thrift, run it with your Python interpreter. Make sure to not call your application archer.py because this would conflict with Archer itself.

To run the application you can use the archer command:

$ archer run
* Running on 127.0.0.1:6000 in DEBUG mode

This launches a very simple built_in server, which is good enough for testing but probably not what you want to use in production. For deployment options see deployment.

Now run call the remote function you can also use the archer command:

$ archer call ping
* pong

You should see that the string pong is returned

Externally Visible Server

you can make the server publicly available simply by adding --host=0.0.0.0 to the command line:

archer run --host=0.0.0.0

This tells your operating system to listen on all public IPs.

tutorial

tutorial

A minimal Archer application looks something like this:

from archer import Archer
app = Archer('PingPong')

@app.api('ping')
def ping():
    return 'pong'

provided a hello.thrift file under your working directory, define a service in the file:

service PingPong {
    string ping(),
}

So what did that code do?

  1. First we imported the Archer class. An instance of this class will be our Thrift RPC server_side application.
  2. Next we create an instance of this class. The first argument is the name of the application
  3. We then use the api() decorator to tell Archer what name defined in thrift file should trigger our function.
  4. The function returns the message for ping RPC call, which is a string pong here
  5. the service PingPong is defined in the hello.thrift file

Just save the python code as hello.py (or something similar) and the service definition in hello.thrift, run it with your Python interpreter. Make sure to not call your application archer.py because this would conflict with Archer itself.

To run the application you can use the archer command:

$ archer run
* Running on 127.0.0.1:6000 in DEBUG mode

This launches a very simple built_in server, which is good enough for testing but probably not what you want to use in production. For deployment options see deployment.

Now run call the remote function you can also use the archer command:

$ archer call ping
* pong

You should see that the string pong is returned

You can also run a client shell by:

$ archer client
>>> client.ping()

Externally Visible Server

you can make the server publicly available simply by adding --host=0.0.0.0 to the command line:

archer run --host=0.0.0.0

This tells your operating system to listen on all public IPs.

testing

test client

A test client could be initialized by calling test_client()

fake client

A test client could be initialized by calling fake_client()

errorhandling

register error handler

Exceptions occurs during an API calling would be caught by the error handlers registered. if no one is provided, the default handler would catch it. In Archer,an error handler is registed like:

app.register_error_handler(error, handler)

In which,e is the Exception,f is the error handler,for example:

class BasicException(Exception):
    pass

def BasicErrorHandler(meta, result):
    return 'BasicException'

app.register_error_handler(BasicException, BasicErrorHandler)

The two arguments meta and result refers to ApiMeta and ApiResultMeta.

Whenever a BasicException occurs,Archer will catch it and call BasicErrorHandler to handle it.

event

event

Event is used to add customized hook functions which would be called before or after An api calling.

Archer provides 3 events before_api_call, after_api_call, tear_down_api_call

for after_api_call,it would take one argument,
which is an instance of ApiMeta
it receives two arguments, first is instance of
ApiMeta and the second is instance of ApiResultMeta.

Command line tools

It’s quite easy to fire up a development server by using the archer
command line utility with Archer.run() method.

Command Line

The archer command line script (Command line tools) is strongly recommended for development because it provides a superior reload experience due to how it loads the application. The basic usage is like this:

$ archer --app my_application  run

This will enable the reloader and then start the server on http://localhost:6000/.

If you put your app instance in a python file or a __init__.py file in some directory under the root_path, archer will find the app for you automatically. In such cases, just:

$ archer run

And the server would start the same way. Super easy!

In Code

The alternative way to start the application is through the Archer.run() method. This will immediately launch a local server exactly the same way the archer script does.

Example:

if __name__ == '__main__':
    app.run()

client

Archer also provide the archer call to easy test a api without any coding:

$ archer --app my_application  call api_name param1,param2....

if you’d like archer find the app for you, just:

$ archer call api_name param1, param2...

And if everything is ok, the terminal would echo the return value of this api, or just the string OK if nothing is returned.

parameters are just separated by comma or whitespace, so a b c d and a,b,c,d are both ok.

Archer would handle the parameter type, so 123 would convert to int type. You can specify the type using : after a parameter, like 123:string, so that Archer would known that you want 123 to be a string instead of int.

Non built_in type

Customized types are not supported, as call command is just for quickly getting some feedback of an api, You need more complicated test cases to ensure your api work correctly, so don’t rely heavily on this command.

Working with the Shell

One of the reasons everybody loves Python is the interactive shell. It basically allows you to execute Python commands in real time and immediately get results back. Archer itself does not come with an interactive shell, because it does not require any specific setup upfront, just import your application and start playing around.

There are however some handy helpers to make playing around in the shell a more pleasant experience. The main issue with interactive console sessions is that you’re not really triggering a real rpc call from a client.

This is where some helper functions come in handy. Keep in mind however that these functions are not only there for interactive shell usage, but also for unit testing and other situations that require a faked request context.

Command Line Interface

Thee recommended way to work with the shell is the archer shell command which does a lot of this automatically for you. For instance the shell is automatically initialized with a loaded application context. with globals app, fake_client, test_client already set at your hand:

>>> thrift_file = app.thrift_file
>>> test_client.ping()
>>> fake_client.ping()

You may want to add some other variables to the global scope of the shell by using shell_context_processor() method.

For more information see Command line tools.

deployment

deployment

The development Server is not suitable for production use, we did not handle the thrift Protocol and Transport details instead of providing a default one which is suitable for development.

gunicorn_thrift is highly recommended if you’d like to deploy an Archer application, as Archer is designed to work with gunicorn_thrift.

API Reference

If you are looking for information on a specific function, class or method, this part of the documentation is for you.

API

This part of the documentation covers all the interfaces of Archer. For parts where Archer depends on external libraries, we document the most important right here and provide links to the canonical documentation.

Application Object

ApiMeta Object

ApiResultMeta

Test Client

Fake Client

Command Line Interface

archer.cli.call
archer.cli.shell
archer.cli.run

Additional Notes

Design notes, legal information and changelog are here for the interested.

Design Decisions in Archer

If you are curious why Archer does certain things the way it does and not differently, this section is for you. This should give you an idea about some of the design decisions that may appear arbitrary and surprising at first, especially in direct comparison with other frameworks.

The Explicit Application Object

A thrift application based on gunicorn_thrift has to have one central object that implements the actual application. In Archer this is an instance of the Archer class. Each Archer application has to create an instance of this class itself.

That instance is your gunicorn_thrift application, you don’t have to remember anything else. If you want to apply a gunicorn_thrift middleware, just wrap it and you’re done (though there are better ways to do that so that you do not lose the reference to the application object processor()).

Furthermore this design makes it possible to use a factory function to create the application which is very helpful for unittesting and similar things

Thread Locals

Unlike Flask, Archer doesn’t use thread local objects, no magic globals like current_app, request in Flask. We believe that things you can do with thread locals would exist a better way to do without it, and decouple your code with these globals means it would be easier to test and analyse, passing globals around everywhere seems not a good idea. Try to fire up a python shell and type import this, one thing you can find is:

"Explicit is better than implicit." --zen of Python

Say if we have an asynchronous server instead, using thread locals as globals would make no sense and break our application. Any way, no thread locals leaves the door open. We throw the ball to the end user to decide whether thread local would be used in an archer app, for more information , refer to the article GlobalState.

What Archer is, What Archer is Not

Archer will never have a database layer. It will not have a form library or anything else in that direction. Archer itself just bridges to gunicorn_thrift to implement a proper thrift application. It also binds to a few common standard library packages such as logging. Everything else is up for extensions.

Archer almost does nothing on the client side , as the client language is depend on what you prefer, and how to use the connection is also not predictable. So just implement the client side code the way you like or just whatever to satisfy your need.

Why is this the case? Because people have different preferences and requirements and Archer could not meet those if it would force any of this into the core.

The idea of Archer is to build a good foundation for all thrift applications. Everything else is up to you.

changes

changes

Archer Changelog

Here you can see the full list of changes between each Archer release.

Version 0.5

  • Allow archer to pass module_name to thriftpy #8

Version 0.1

First public preview release.

Version 0.2

Add client tools

licence

licence

This is the MIT license: http://www.opensource.org/licenses/mit-license.php

Copyright (c) 2014-2014 the Archer authors and contributors .

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.