This Style Guide provides the guidance for how Python code used in this repo should be formatted and laid out. Please use these guidelines (unless you know better ones -- if you do, open an issue/PR). When in doubt, please refer to the official Python PEP 8 guide.
- Code Layout
- General
- Constants, Variables, & Attributes
- Handlers & Interceptors
- Boilerplate Code
- Functions
- Testing
- Internationalization (I18N)
Ensure that the code in lambda_function.py follows this order:
- License header (if applicable)
- Constants (not including language strings if multiple languages in the same file)
- Handlers
- Functions (if not in separate file)
- Lambda setup, e.g.,
lambda_handler = ...
- Language strings (if including multiple languages in the same file)
- Code needs to be internationalized (use language strings, etc.) but does not need to be translated. Minimum support of en-US required.
- The
.speak
method should takespeech
as its parameter. - The
.reprompt
method should takereprompt
,prompt
orspeech
as its parameter. - Comments usage:
-
Indicate what is required/optional action is required.
-
Mark required actions in comments with
**TODO**
. -
Describe key or unusual code.
-
Describing classes. These should use docstring formatting, and can keep the closing
"""
on the same line for short descriptions, e.g.class CancelOrStopIntentHandler(AbstractRequestHandler): """Handler for Cancel and Stop Intents."""
-
- To make code more readable, indent and line break statements with multiple conditionals. For example,
Do
Don't This line is too long. Limit all lines to a maximum of 115 characters. Use a line break.
return (is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input))
Don't This line is not indented properly. Four spaces are the preferred indentation method.return (is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input))
return (is_intent_name("AMAZON.CancelIntent")(handler_input) or is_intent_name("AMAZON.StopIntent")(handler_input))
- Constants, session attributes, and variables should be named in snake case (e.g.,
lower_case_with_underscores
). - Class names should use the CapWords convention (e.g.,
SkillBuilder
) - Rename boolean variables to begin with "is", "has", "can" or comparable prefix to indicate boolean (e.g. "is_correct", instead of just "correct").
-
If a handler handles exactly one intent/request type, name the handler like Handler (without the AMAZON prefix for built-in intents):
- LaunchRequestHandler
- SomeRandomIntentHandler
- RepeatIntentHandler
- SessionEndedRequestHandler
-
The preferred order for the Handlers when constructing the SkillBuilder:
- LaunchRequestHandler
- ...skill specific intent handlers...
- RepeatIntentHandler (if applicable)
- HelpIntentHandler
- CancelOrStopIntentHandler
- FallbackIntentHandler
- SessionEndedRequestHandler
- IntentReflectorHandler (for debugging use)
- CatchAllExceptionHandler (if applicable)
-
Request and response interceptors should use the suffix Interceptor and should include Request or Response (if specific to that type).
-
The Handlers & Interceptors implementation code should be in the same order as they are added to the SkillBuilder.
-
If a handler is not the only handler for a given intent, include a comment to that effect immediately preceding the
can_handle(...)
, e.g.,class LaunchRequestHandler(AbstractRequestHandler): """This LaunchRequestHandler catches LaunchRequests which are not handled by more specific handlers""" def can_handle(self, handler_input) ...
The code in these sections should be used for their respective handlers. If the functionality of the code requires changes, use this as a baseline.
class SessionEndedRequestHandler(AbstractRequestHandler):
"""Handler for SessionEndedRequest."""
def can_handle(self, handler_input):
return is_request_type("SessionEndedRequest")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
logger.info("In SessionEndedRequestHandler")
logger.info("Session ended with reason: {}".format(
handler_input.request_envelope.request.reason))
return handler_input.response_builder.response
class FallbackIntentHandler(AbstractRequestHandler):
"""Handler for Fallback Intent.
AMAZON.FallbackIntent is only available in en-US locale.
This handler will not be triggered except in that locale,
so it is safe to deploy on any locale.
"""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return is_intent_name("AMAZON.FallbackIntent")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
logger.info("In FallbackIntentHandler")
# get localization data
data = handler_input.attributes_manager.request_attributes["_"]
speech = data[prompts.FALLBACK_MESSAGE]
reprompt = data[prompts.FALLBACK_REPROMPT]
handler_input.response_builder.speak(speech).ask(reprompt)
return handler_input.response_builder.response
class CatchAllExceptionHandler(AbstractExceptionHandler):
"""Catch All Exception handler.
This handler catches all kinds of exceptions and prints
the stack trace on AWS Cloudwatch with the request envelope."""
def can_handle(self, handler_input, exception):
# type: (HandlerInput, Exception) -> bool
return True
def handle(self, handler_input, exception):
# type: (HandlerInput, Exception) -> Response
logger.error(exception, exc_info=True)
speech = "Sorry, I can't understand the command. Please say again."
handler_input.response_builder.speak(speech).ask(speech)
return handler_input.response_builder.response
class RequestLogger(AbstractRequestInterceptor):
"""Log the request envelope."""
def process(self, handler_input):
# type: (HandlerInput) -> None
logger.info("Request Envelope: {}".format(
handler_input.request_envelope))
class ResponseLogger(AbstractResponseInterceptor):
"""Log the response envelope."""
def process(self, handler_input, response):
# type: (HandlerInput, Response) -> None
logger.info("Response: {}".format(response))
- All (non-handler) functions Function names should be lowercase, with words separated by underscores as necessary to improve readability.
- All functions names should begin with an action verb, e.g.,
get_final_score
,format_casing
,supports_display
, etc. - If a utility function is available in the SDK, the use of it is preferred over using a locally maintain functioned with comparable functionality.
- No specific test methodology is currently required, but do test your code. Use unit testing (no voice interaction) in addition to voice testing (full stack).
- Where applicable, include certification testing instructions.
I18N is handled through the multilingual internationalization module gettext
. Follow our localization and internationalization guide at https://github.com/alexa/skill-sample-python-howto/blob/master/instructions/localization.md.