Package: parfait
Chalice is a microframework for writing serverless apps in python. It allows
you to quickly create and deploy applications that use AWS Lambda.  It provides:A command line tool for creating, deploying, and managing your appA decorator based API for integrating with Amazon API Gateway, Amazon S3,
Amazon SNS, Amazon SQS, and other AWS services.Automatic IAM policy generationYou can create Rest APIs:fromchaliceimportChaliceapp=Chalice(app_name="helloworld")@app.route("/")defindex():return{"hello":"world"}Tasks that run on a periodic basis:fromchaliceimportChalice,Rateapp=Chalice(app_name="helloworld")# Automatically runs every 5 minutes@app.schedule(Rate(5,unit=Rate.MINUTES))defperiodic_task(event):return{"hello":"world"}You can connect a lambda function to an S3 event:fromchaliceimportChaliceapp=Chalice(app_name="helloworld")# Whenever an object is uploaded to 'mybucket'# this lambda function will be invoked.@app.on_s3_event(bucket='mybucket')defhandler(event):print("Object uploaded for bucket:%s, key:%s"%(event.bucket,event.key))As well as an SQS queue:fromchaliceimportChaliceapp=Chalice(app_name="helloworld")# Invoke this lambda function whenever a message# is sent to the ``my-queue-name`` SQS queue.@app.on_sqs_message(queue='my-queue-name')defhandler(event):forrecordinevent:print("Message body:%s"%record.body)And several other AWS resources.Once you’ve written your code, you just runchalice deployand Chalice takes care of deploying your app.$ chalice deploy
...
https://endpoint/dev

$ curl https://endpoint/api
{"hello": "world"}Up and running in less than 30 seconds.
Give this project a try and share your feedback with us here on Github.The documentation is availableon readthedocs.QuickstartIn this tutorial, you’ll use thechalicecommand line utility
to create and deploy a basic REST API.
First, you’ll need to installchalice.  Using a virtualenv
is recommended:$ pip install virtualenv
$ virtualenv ~/.virtualenvs/chalice-demo
$ source ~/.virtualenvs/chalice-demo/bin/activateNote:make sure you are using python2.7, python3.6, or python3.7.
These are the only python versions currently supported by AWS Lambda so they
are also the only versions supported by thechaliceCLI andchalicepython package. You can check the version of python in your virtualenv by
running:# Double check you have a supported python version in your virtualenv
$ python -VNext, in your virtualenv, installchalice:$ pip install chaliceYou can verify you have chalice installed by running:$ chalice --help
Usage: chalice [OPTIONS] COMMAND [ARGS]...
...CredentialsBefore you can deploy an application, be sure you have
credentials configured.  If you have previously configured your
machine to run boto3 (the AWS SDK for Python) or the AWS CLI then
you can skip this section.If this is your first time configuring credentials for AWS you
can follow these steps to quickly get started:$ mkdir ~/.aws
$ cat >> ~/.aws/config
[default]
aws_access_key_id=YOUR_ACCESS_KEY_HERE
aws_secret_access_key=YOUR_SECRET_ACCESS_KEY
region=YOUR_REGION (such as us-west-2, us-west-1, etc)If you want more information on all the supported methods for
configuring credentials, see theboto3 docs.Creating Your ProjectThe next thing we’ll do is use thechalicecommand to create a new
project:$ chalice new-project helloworldThis will create ahelloworlddirectory.  Cd into this
directory.  You’ll see several files have been created for you:$ cd helloworld
$ ls -la
drwxr-xr-x   .chalice
-rw-r--r--   app.py
-rw-r--r--   requirements.txtYou can ignore the.chalicedirectory for now, the two main files
we’ll focus on isapp.pyandrequirements.txt.Let’s take a look at theapp.pyfile:fromchaliceimportChaliceapp=Chalice(app_name='helloworld')@app.route('/')defindex():return{'hello':'world'}Thenew-projectcommand created a sample app that defines a
single view,/, that when called will return the JSON body{"hello": "world"}.DeployingLet’s deploy this app.  Make sure you’re in thehelloworlddirectory and runchalice deploy:$ chalice deploy
...
Initiating first time deployment...
https://qxea58oupc.execute-api.us-west-2.amazonaws.com/api/You now have an API up and running using API Gateway and Lambda:$ curl https://qxea58oupc.execute-api.us-west-2.amazonaws.com/api/
{"hello": "world"}Try making a change to the returned dictionary from theindex()function.  You can then redeploy your changes by runningchalice deploy.For the rest of these tutorials, we’ll be usinghttpieinstead ofcurl(https://github.com/jakubroztocil/httpie) to test our API.  You can installhttpieusingpip install httpie, or if you’re on Mac, you can runbrew install httpie.  The Github link has more information on installation
instructions.  Here’s an example of usinghttpieto request the root
resource of the API we just created.  Note that the command name ishttp:$ http https://qxea58oupc.execute-api.us-west-2.amazonaws.com/api/
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 18
Content-Type: application/json
Date: Mon, 30 May 2016 17:55:50 GMT
X-Cache: Miss from cloudfront

{
    "hello": "world"
}Additionally, the API Gateway endpoints will be shortened tohttps://endpoint/api/for brevity.  Be sure to substitutehttps://endpoint/api/for the actual endpoint that thechaliceCLI displays when you deploy your API (it will look something likehttps://abcdefg.execute-api.us-west-2.amazonaws.com/api/.Next StepsYou’ve now created your first app usingchalice.The next few sections will build on this quickstart section and introduce
you to additional features including: URL parameter capturing,
error handling, advanced routing, current request metadata, and automatic
policy generation.Tutorial: URL ParametersNow we’re going to make a few changes to ourapp.pyfile that
demonstrate additional capabilities provided by the python serverless
microframework for AWS.Our application so far has a single view that allows you to make
an HTTP GET request to/.  Now let’s suppose we want to capture
parts of the URI:fromchaliceimportChaliceapp=Chalice(app_name='helloworld')CITIES_TO_STATE={'seattle':'WA','portland':'OR',}@app.route('/')defindex():return{'hello':'world'}@app.route('/cities/{city}')defstate_of_city(city):return{'state':CITIES_TO_STATE[city]}In the example above, we’ve now added astate_of_cityview that allows
a user to specify a city name.  The view function takes the city
name and returns name of the state the city is in.  Notice that the@app.routedecorator has a URL pattern of/cities/{city}.  This
means that the value of{city}is captured and passed to the view
function.  You can also see that thestate_of_citytakes a single
argument.  This argument is the name of the city provided by the user.
For example:GET /cities/seattle   --> state_of_city('seattle')
GET /cities/portland  --> state_of_city('portland')Now that we’ve updated ourapp.pyfile with this new view function,
let’s redeploy our application.  You can runchalice deployfrom
thehelloworlddirectory and it will deploy your application:$ chalice deployLet’s try it out.  Note the examples below use thehttpcommand
from thehttpiepackage.  You can install this usingpip install httpie:$ http https://endpoint/api/cities/seattle
HTTP/1.1 200 OK

{
    "state": "WA"
}

$ http https://endpoint/api/cities/portland
HTTP/1.1 200 OK

{
    "state": "OR"
}Notice what happens if we try to request a city that’s not in ourCITIES_TO_STATEmap:$ http https://endpoint/api/cities/vancouver
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
X-Cache: Error from cloudfront

{
    "Code": "ChaliceViewError",
    "Message": "ChaliceViewError: An internal server error occurred."
}In the next section, we’ll see how to fix this and provide better
error messages.Tutorial: Error MessagesIn the example above, you’ll notice that when our app raised
an uncaught exception, a 500 internal server error was returned.In this section, we’re going to show how you can debug and improve
these error messages.The first thing we’re going to look at is how we can debug this
issue.  By default, debugging is turned off, but you can
enable debugging to get more information:fromchaliceimportChaliceapp=Chalice(app_name='helloworld')app.debug=TrueTheapp.debug = Trueenables debugging for your app.
Save this file and redeploy your changes:$ chalice deploy
...
https://endpoint/api/Now, when you request the same URL that returned an internal
server error, you’ll get back the original stack trace:$ http https://endpoint/api/cities/vancouver
Traceback (most recent call last):
  File "/var/task/chalice/app.py", line 304, in _get_view_function_response
    response = view_function(*function_args)
  File "/var/task/app.py", line 18, in state_of_city
    return {'state': CITIES_TO_STATE[city]}
KeyError: u'vancouver'We can see that the error is caused from an uncaughtKeyErrorresulting
from trying to access thevancouverkey.Now that we know the error, we can fix our code.  What we’d like to do is
catch this exception and instead return a more helpful error message
to the user.  Here’s the updated code:fromchaliceimportBadRequestError@app.route('/cities/{city}')defstate_of_city(city):try:return{'state':CITIES_TO_STATE[city]}exceptKeyError:raiseBadRequestError("Unknown city '%s', valid choices are:%s"%(city,', '.join(CITIES_TO_STATE.keys())))Save and deploy these changes:$ chalice deploy
$ http https://endpoint/api/cities/vancouver
HTTP/1.1 400 Bad Request

{
    "Code": "BadRequestError",
    "Message": "Unknown city 'vancouver', valid choices are: portland, seattle"
}We can see now that we have received aCodeandMessagekey, with the message
being the value we passed toBadRequestError.  Whenever you raise
aBadRequestErrorfrom your view function, the framework will return an
HTTP status code of 400 along with a JSON body with aCodeandMessage.
There are a few additional exceptions you can raise from your python code:* BadRequestError - return a status code of 400
* UnauthorizedError - return a status code of 401
* ForbiddenError - return a status code of 403
* NotFoundError - return a status code of 404
* ConflictError - return a status code of 409
* UnprocessableEntityError - return a status code of 422
* TooManyRequestsError - return a status code of 429
* ChaliceViewError - return a status code of 500You can import these directly from thechalicepackage:fromchaliceimportUnauthorizedErrorTutorial: Additional RoutingSo far, our examples have only allowed GET requests.
It’s actually possible to support additional HTTP methods.
Here’s an example of a view function that supports PUT:@app.route('/resource/{value}',methods=['PUT'])defput_test(value):return{"value":value}We can test this method using thehttpcommand:$ http PUT https://endpoint/api/resource/foo
HTTP/1.1 200 OK

{
    "value": "foo"
}Note that themethodskwarg accepts a list of methods.  Your view function
will be called when any of the HTTP methods you specify are used for the
specified resource.  For example:@app.route('/myview',methods=['POST','PUT'])defmyview():passThe above view function will be called when either an HTTP POST or
PUT is sent to/myview.Alternatively if you do not want to share the same view function across
multiple HTTP methods for the same route url, you may define separate view
functions to the same route url but have the view functions differ by
HTTP method. For example:@app.route('/myview',methods=['POST'])defmyview_post():pass@app.route('/myview',methods=['PUT'])defmyview_put():passThis setup will route all HTTP POST’s to/myviewto themyview_post()view function and route all HTTP PUT’s to/myviewto themyview_put()view function. It is also important to note that the view functionsmusthave unique names. For example, both view functions cannot be
namedmyview().In the next section we’ll go over how you can introspect the given request
in order to differentiate between various HTTP methods.Tutorial: Request MetadataIn the examples above, you saw how to create a view function that supports
an HTTP PUT request as well as a view function that supports both POST and
PUT via the same view function.  However, there’s more information we
might need about a given request:In a PUT/POST, you frequently send a request body.  We need some
way of accessing the contents of the request body.For view functions that support multiple HTTP methods, we’d like
to detect which HTTP method was used so we can have different
code paths for PUTs vs. POSTs.All of this and more is handled by the current request object that thechalicelibrary makes available to each view function when it’s called.Let’s see an example of this.  Suppose we want to create a view function
that allowed you to PUT data to an object and retrieve that data
via a corresponding GET.  We could accomplish that with the
following view function:fromchaliceimportNotFoundErrorOBJECTS={}@app.route('/objects/{key}',methods=['GET','PUT'])defmyobject(key):request=app.current_requestifrequest.method=='PUT':OBJECTS[key]=request.json_bodyelifrequest.method=='GET':try:return{key:OBJECTS[key]}exceptKeyError:raiseNotFoundError(key)Save this in yourapp.pyfile and rerunchalice deploy.
Now, you can make a PUT request to/objects/your-keywith a request
body, and retrieve the value of that body by making a subsequentGETrequest to the same resource.  Here’s an example of its usage:# First, trying to retrieve the key will return a 404.
$ http GET https://endpoint/api/objects/mykey
HTTP/1.1 404 Not Found

{
    "Code": "NotFoundError",
    "Message": "mykey"
}

# Next, we'll create that key by sending a PUT request.
$ echo '{"foo": "bar"}' | http PUT https://endpoint/api/objects/mykey
HTTP/1.1 200 OK

null

# And now we no longer get a 404, we instead get the value we previously
# put.
$ http GET https://endpoint/api/objects/mykey
HTTP/1.1 200 OK

{
    "mykey": {
        "foo": "bar"
    }
}You might see a problem with storing the objects in a module levelOBJECTSvariable.  We address this in the next section.Theapp.current_requestobject also has the following properties.current_request.query_params- A dict of the query params for the request.current_request.headers- A dict of the request headers.current_request.uri_params- A dict of the captured URI params.current_request.method-  The HTTP method (as a string).current_request.json_body- The parsed JSON body (json.loads(raw_body))current_request.raw_body- The raw HTTP body as bytes.current_request.context- A dict of additional context informationcurrent_request.stage_vars- Configuration for the API Gateway stageDon’t worry about thecontextandstage_varsfor now.  We haven’t
discussed those concepts yet.  Thecurrent_requestobject also
has ato_dictmethod, which returns all the information about the
current request as a dictionary.  Let’s use this method to write a view
function that returns everything it knows about the request:@app.route('/introspect')defintrospect():returnapp.current_request.to_dict()Save this to yourapp.pyfile and redeploy withchalice deploy.
Here’s an example of hitting the/introspectURL.  Note how we’re
sending a query string as well as a customX-TestHeaderheader:$ http 'https://endpoint/api/introspect?query1=value1&query2=value2' 'X-TestHeader: Foo'
HTTP/1.1 200 OK

{
    "context": {
        "apiId": "apiId",
        "httpMethod": "GET",
        "identity": {
            "accessKey": null,
            "accountId": null,
            "apiKey": null,
            "caller": null,
            "cognitoAuthenticationProvider": null,
            "cognitoAuthenticationType": null,
            "cognitoIdentityId": null,
            "cognitoIdentityPoolId": null,
            "sourceIp": "1.1.1.1",
            "userAgent": "HTTPie/0.9.3",
            "userArn": null
        },
        "requestId": "request-id",
        "resourceId": "resourceId",
        "resourcePath": "/introspect",
        "stage": "dev"
    },
    "headers": {
        "accept": "*/*",
        ...
        "x-testheader": "Foo"
    },
    "method": "GET",
    "query_params": {
        "query1": "value1",
        "query2": "value2"
    },
    "raw_body": null,
    "stage_vars": null,
    "uri_params": null
}Tutorial: Request Content TypesThe default behavior of a view function supports
a request body ofapplication/json.  When a request is
made with aContent-Typeofapplication/json, theapp.current_request.json_bodyattribute is automatically
set for you.  This value is the parsed JSON body.You can also configure a view function to support other
content types.  You can do this by specifying thecontent_typesparameter value to yourapp.routefunction.  This parameter is a list of acceptable content
types.  Here’s an example of this feature:importsysfromchaliceimportChaliceifsys.version_info[0]==3:# Python 3 imports.fromurllib.parseimporturlparse,parse_qselse:# Python 2 imports.fromurlparseimporturlparse,parse_qsapp=Chalice(app_name='helloworld')@app.route('/',methods=['POST'],content_types=['application/x-www-form-urlencoded'])defindex():parsed=parse_qs(app.current_request.raw_body.decode())return{'states':parsed.get('states',[])}There’s a few things worth noting in this view function.
First, we’ve specified that we only accept theapplication/x-www-form-urlencodedcontent type.  If we
try to send a request withapplication/json, we’ll now
get a415 Unsupported Media Typeresponse:$ http POST https://endpoint/api/ states=WA states=CA --debug
...
>>> requests.request(**{'allow_redirects': False,
 'headers': {'Accept': 'application/json',
             'Content-Type': 'application/json',
...


HTTP/1.1 415 Unsupported Media Type

{
    "message": "Unsupported Media Type"
}If we use the--formargument, we can see the
expected behavior of this view function becausehttpiesets theContent-Typeheader toapplication/x-www-form-urlencoded:$ http --form POST https://endpoint/api/formtest states=WA states=CA --debug
...
>>> requests.request(**{'allow_redirects': False,
 'headers': {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
...

HTTP/1.1 200 OK
{
    "states": [
        "WA",
        "CA"
    ]
}The second thing worth noting is thatapp.current_request.json_bodyis only available for the application/json content type.In our example above, we usedapp.current_request.raw_bodyto access
the raw body bytes:parsed=parse_qs(app.current_request.raw_body)app.current_request.json_bodyis set toNonewhenever theContent-Typeis notapplication/json.  This means that
you will need to useapp.current_request.raw_bodyand parse
the request body as needed.Tutorial: Customizing the HTTP ResponseThe return value from a chalice view function is serialized as JSON as the
response body returned back to the caller.  This makes it easy to create
rest APIs that return JSON response bodies.Chalice allows you to control this behavior by returning an instance of
a chalice specificResponseclass.  This behavior allows you to:Specify the status code to returnSpecify custom headers to add to the responseSpecify response bodies that are notapplication/jsonHere’s an example of this:fromchaliceimportChalice,Responseapp=Chalice(app_name='custom-response')@app.route('/')defindex():returnResponse(body='hello world!',status_code=200,headers={'Content-Type':'text/plain'})This will result in a plain text response body:$ http https://endpoint/api/
HTTP/1.1 200 OK
Content-Length: 12
Content-Type: text/plain

hello world!Tutorial: GZIP compression for jsonThe return value from a chalice view function is serialized as JSON as the
response body returned back to the caller.  This makes it easy to create
rest APIs that return JSON response bodies.Chalice allows you to control this behavior by returning an instance of
a chalice specificResponseclass.  This behavior allows you to:Addapplication/jsonto binary_typesSpecify the status code to returnSpecify custom headerContent-Type:application/jsonSpecify custom headerContent-Encoding:gzipHere’s an example of this:importjsonimportgzipfromchaliceimportChalice,Responseapp=Chalice(app_name='compress-response')app.api.binary_types.append('application/json')@app.route('/')defindex():blob=json.dumps({'hello':'world'}).encode('utf-8')payload=gzip.compress(blob)custom_headers={'Content-Type':'application/json','Content-Encoding':'gzip'}returnResponse(body=payload,status_code=200,headers=custom_headers)Tutorial: CORS SupportYou can specify whether a view supports CORS by adding thecors=Trueparameter to your@app.route()call.  By
default this value is false:@app.route('/supports-cors',methods=['PUT'],cors=True)defsupports_cors():return{}Settingscors=Truehas similar behavior to enabling CORS
using the AWS Console.  This includes:Injecting theAccess-Control-Allow-Origin:*header to your
responses, including all error responses you can return.Automatically adding anOPTIONSmethod to support preflighting
requests.The preflight request will return a response that includes:Access-Control-Allow-Origin:*TheAccess-Control-Allow-Methodsheader will return a list of all HTTP
methods you’ve called out in your view function.  In the example above,
this will bePUT,OPTIONS.Access-Control-Allow-Headers:Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token.If more fine grained control of the CORS headers is desired, set thecorsparameter to an instance ofCORSConfiginstead ofTrue. TheCORSConfigobject can be imported from from thechalicepackage it’s
constructor takes the following keyword arguments that map to CORS headers:ArgumentTypeHeaderallow_originstrAccess-Control-Allow-Originallow_headerslistAccess-Control-Allow-Headersexpose_headerslistAccess-Control-Expose-Headersmax_ageintAccess-Control-Max-Ageallow_credentialsboolAccess-Control-Allow-CredentialsCode sample defining more CORS headers:fromchaliceimportCORSConfigcors_config=CORSConfig(allow_origin='https://foo.example.com',allow_headers=['X-Special-Header'],max_age=600,expose_headers=['X-Special-Header'],allow_credentials=True)@app.route('/custom_cors',methods=['GET'],cors=cors_config)defsupports_custom_cors():return{'cors':True}There’s a couple of things to keep in mind when enabling cors for a view:AnOPTIONSmethod for preflighting is always injected.  Ensure that
you don’t haveOPTIONSin themethods=[...]list of your
view function.Even though theAccess-Control-Allow-Originheader can be set to a
string that is a space separated list of origins, this behavior does not
work on all clients that implement CORS. You should only supply a single
origin to theCORSConfigobject. If you need to supply multiple origins
you will need to define a custom handler for it that acceptsOPTIONSrequests and matches theOriginheader against a whitelist of origins.
If the match is successful then return just theirOriginback to them
in theAccess-Control-Allow-Originheader.Example:fromchaliceimportChalice,Responseapp=Chalice(app_name='multipleorigincors')_ALLOWED_ORIGINS=set(['http://allowed1.example.com','http://allowed2.example.com',])@app.route('/cors_multiple_origins',methods=['GET','OPTIONS'])defsupports_cors_multiple_origins():method=app.current_request.methodifmethod=='OPTIONS':headers={'Access-Control-Allow-Method':'GET,OPTIONS','Access-Control-Allow-Origin':','.join(_ALLOWED_ORIGINS),'Access-Control-Allow-Headers':'X-Some-Header',}origin=app.current_request.headers.get('origin','')iforiginin_ALLOWED_ORIGINS:headers.update({'Access-Control-Allow-Origin':origin})returnResponse(body=None,headers=headers,)elifmethod=='GET':return'Foo'Every view function must explicitly enable CORS support.The last point will change in the future.  Seethis issuefor more information.Tutorial: Policy GenerationIn the previous section we created a basic rest API that
allowed you to store JSON objects by sending the JSON
in the body of an HTTP PUT request to/objects/{name}.
You could then retrieve objects by sending a GET request to/objects/{name}.However, there’s a problem with the code we wrote:OBJECTS={}@app.route('/objects/{key}',methods=['GET','PUT'])defmyobject(key):request=app.current_requestifrequest.method=='PUT':OBJECTS[key]=request.json_bodyelifrequest.method=='GET':try:return{key:OBJECTS[key]}exceptKeyError:raiseNotFoundError(key)We’re storing the key value pairs in a module levelOBJECTSvariable.  We can’t rely on local storage like this persisting
across requests.A better solution would be to store this information in Amazon S3.
To do this, we’re going to use boto3, the AWS SDK for Python.
First, install boto3:$ pip install boto3Next, addboto3to your requirements.txt file:$ echo 'boto3==1.3.1' >> requirements.txtThe requirements.txt file should be in the same directory that contains
yourapp.pyfile.  Next, let’s update our view code to use boto3:importjsonimportboto3frombotocore.exceptionsimportClientErrorfromchaliceimportNotFoundErrorS3=boto3.client('s3',region_name='us-west-2')BUCKET='your-bucket-name'@app.route('/objects/{key}',methods=['GET','PUT'])defs3objects(key):request=app.current_requestifrequest.method=='PUT':S3.put_object(Bucket=BUCKET,Key=key,Body=json.dumps(request.json_body))elifrequest.method=='GET':try:response=S3.get_object(Bucket=BUCKET,Key=key)returnjson.loads(response['Body'].read())exceptClientErrorase:raiseNotFoundError(key)Make sure to changeBUCKETwith the name of an S3 bucket
you own.  Redeploy your changes withchalice deploy.
Now, whenever we make aPUTrequest to/objects/keyname, the
data send will be stored in S3.  Any subsequentGETrequests will
retrieve this data from S3.Manually Providing PoliciesIAM permissions can be auto generated, provided manually or can be
pre-created and explicitly configured. To use a
pre-configured IAM role ARN for chalice, add these two keys to your
chalice configuration. Setting manage_iam_role to false tells
Chalice to not attempt to generate policies and create IAM role."manage_iam_role":false
"iam_role_arn":"arn:aws:iam::<account-id>:role/<role-name>"Whenever your application is deployed usingchalice, the
auto generated policy is written to disk at<projectdir>/.chalice/policy.json.  When you run thechalice deploycommand, you can also specify the--no-autogen-policyoption.  Doing so will result in thechaliceCLI loading the<projectdir>/.chalice/policy.jsonfile and using that file as the policy for the IAM role.
You can manually edit this file and specify--no-autogen-policyif you’d like to have full control over what IAM policy to associate
with the IAM role.You can also run thechalicegen-policycommand from your project
directory to print the auto generated policy to stdout.  You can
then use this as a starting point for your policy.$ chalice gen-policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:ListAllMyBuckets"
      ],
      "Resource": [
        "*"
      ],
      "Effect": "Allow",
      "Sid": "9155de6ad1d74e4c8b1448255770e60c"
    }
  ]
}Experimental StatusThe automatic policy generation is still in the early stages, it should
be considered experimental.  You can always disable policy
generation with--no-autogen-policyfor complete control.Additionally, you will be prompted for confirmation whenever the
auto policy generator detects actions that it would like to add or remove:$ chalice deploy
Updating IAM policy.

The following action will be added to the execution policy:

s3:ListBucket

Would you like to continue?  [Y/n]:Tutorial: Using Custom AuthenticationAWS API Gateway routes can be authenticated in multiple ways:API KeyAWS IAMCognito User PoolsCustom Auth HandlerAPI Key@app.route('/authenticated',methods=['GET'],api_key_required=True)defauthenticated():return{"secure":True}Only requests sent with a validX-Api-Keyheader will be accepted.Using AWS IAMauthorizer=IAMAuthorizer()@app.route('/iam-role',methods=['GET'],authorizer=authorizer)defauthenticated():return{"secure":True}Using Amazon Cognito User PoolsTo integrate with cognito user pools, you can use theCognitoUserPoolAuthorizerobject:authorizer=CognitoUserPoolAuthorizer('MyPool',header='Authorization',provider_arns=['arn:aws:cognito:...:userpool/name'])@app.route('/user-pools',methods=['GET'],authorizer=authorizer)defauthenticated():return{"secure":True}Note, earlier versions of chalice also have anapp.define_authorizermethod as well as anauthorizer_nameargument on the@app.route(...)method.  This approach is deprecated in favor ofCognitoUserPoolAuthorizerand theauthorizerargument in the@app.route(...)method.app.define_authorizerwill be removed in future versions of chalice.Using Custom AuthorizersTo integrate with custom authorizers, you can use theCustomAuthorizermethod
on theappobject.  You’ll need to set theauthorizer_urito the URI of your lambda function.authorizer=CustomAuthorizer('MyCustomAuth',header='Authorization',authorizer_uri=('arn:aws:apigateway:region:lambda:path/2015-03-31''/functions/arn:aws:lambda:region:account-id:''function:FunctionName/invocations'))@app.route('/custom-auth',methods=['GET'],authorizer=authorizer)defauthenticated():return{"secure":True}Tutorial: Local ModeAs you develop your application, you may want to experiment locally  before
deploying your changes.  You can usechalice localto spin up a local
HTTP server you can use for testing.For example, if we have the followingapp.pyfile:fromchaliceimportChaliceapp=Chalice(app_name='helloworld')@app.route('/')defindex():return{'hello':'world'}We can runchalice localto test this API locally:$ chalice local
Serving on localhost:8000We can override the port using:$ chalice local –port=8080We can now test our API usinglocalhost:8000:$ http localhost:8000/
HTTP/1.0 200 OK
Content-Length: 18
Content-Type: application/json
Date: Thu, 27 Oct 2016 20:08:43 GMT
Server: BaseHTTP/0.3 Python/2.7.11

{
    "hello": "world"
}Thechalice localcommanddoes notassume the
role associated with your lambda function, so you’ll
need to use anAWS_PROFILEthat has sufficient permissions
to your AWS resources used in yourapp.py.Deleting Your AppYou can use thechalice deletecommand to delete your app.
Similar to thechalice deploycommand, you can specify which
chalice stage to delete.  By default it will delete thedevstage:$ chalice delete --stage dev
Deleting Rest API: duvw4kwyl3
Deleting function aws:arn:lambda:region:123456789:helloworld-dev
Deleting IAM Role helloworld-devFeedbackWe’also love to hear from you.  Please create any Github issues for
additional features you’d like to see over athttps://github.com/aws/chalice/issues.  You can also chat with us
on gitter:https://gitter.im/awslabs/chaliceFAQQ: How does the Python Serverless Microframework for AWS compare to other
similar frameworks?The biggest difference between this framework and others is that the Python
Serverless Microframework for AWS is singularly focused on using a familiar,
decorator-based API to write python applications that run on Amazon API Gateway
and AWS Lambda.  You can think of it asFlask/Bottlefor serverless APIs.  Its goal is to make writing and deploying these types of
applications as simple as possible specifically for Python developers.To achieve this goal, it has to make certain tradeoffs.  Python will always
remain the only supported language in this framework.  Not every feature of API
Gateway and Lambda is exposed in the framework.  It makes assumptions about how
applications will be deployed, and it has restrictions on how an application
can be structured.  It does not address the creation and lifecycle of other AWS
resources your application may need (Amazon S3 buckets, Amazon DynamoDB tables,
etc.).  The feature set is purposefully small.Other full-stack frameworks offer a lot more features and configurability than
what this framework has and likely will ever have.  Those frameworks are
excellent choices for applications that need more than what is offered by this
microframework.  If all you need is to create a simple rest API in Python that
runs on Amazon API Gateway and AWS Lambda, consider giving the Python
Serverless Microframework for AWS a try.Related Projectsserverless- Build applications
comprised of microservices that run in response to events, auto-scale for
you, and only charge you when they run.Zappa- Deploy python WSGI applications
on AWS Lambda and API Gateway.claudia- Deploy node.js projects
to AWS Lambda and API Gateway.Domovoi- An extension to Chalice that
handles a variety of AWS Lambda event sources such as SNS push notifications,
S3 events, and Step Functions state machines.