本文介绍 Python 运行环境的相关内容,包括打印日志、错误处理、内置模块、自定义模块以及调用外部命令。

背景信息

函数计算目前支持以下 Python 运行环境:

  • Python 2.7 ( runtime = python2.7 )
  • Python 3.6 ( runtime = python3 )

打印日志

函数向 stdout 打印的内容会被收集到创建服务时指定的 Logstore 中,在这里有两种打印日志的方法:

  • 使用 logging 模块,这样在每条日志中都包含时间、requestId、日志级别等信息。
    import logging
    
    def my_handler(event, context):     
        logger = logging.getLogger()     
        logger.info('hello world')     
        return 'done'

    执行上面的代码输出的日志内容如下所示。

    message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO]   hello world
    说明 在这里推荐使用 logging 模块打印日志,因为这样可以自动包含 requestId 信息,方便在出错的时候定位问题日志。
  • 直接使用 print,把内容原样地输出到日志中。
    def my_handler(event, context):      
        print 'hello world'      
        return 'done'

    执行上面的代码输出的日志内容如下所示。

    message:hello world

错误处理

函数在执行过程如果抛出异常,那么函数计算会把异常捕获并将异常信息返回,示例如下所示。

def my_handler(event, context):   
    raise Exception('something is wrong')

调用函数时收到的响应如下所示。

{    
    "errorMessage": "something is wrong",    
    "errorType": "Exception",    
    "stackTrace": [       
        [            
            "File \"/code/index.py\"",           
            "line 2",            
            "in my_handler",            
            "raise Exception('something is wrong')"        
        ]    
    ]
}

发生异常时,函数调用的响应的 HTTP header 中会包含 X-Fc-Error-Type: UnhandledInvocationError。函数计算的错误类型的更多信息,请参见错误类型

使用内置模块

除了 Python 的标准模块,函数计算的 Python 运行环境中还包含了一些常用模块,您可以直接引用,目前包含的模块如下所示。

模块名称 模块介绍 相关链接
oss2 2.6.0 OSS SDK https://github.com/aliyun/aliyun-oss-python-sdk
tablestore 4.6.0 表格存储 SDK https://github.com/aliyun/aliyun-tablestore-python-sdk
aliyun-fc2 2.1.0 函数计算 SDK https://github.com/aliyun/fc-python-sdk
aliyun-python-sdk-ecs 4.10.1 云服务器 SDK https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-ecs
aliyun-python-sdk-vpc 3.0.2 专用网络 SDK https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-vpc
aliyun-python-sdk-rds 2.1.4 云数据库 SDK https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-rds
aliyun-python-sdk-kms 2.5.0 密钥管理服务 SDK https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-kms
pydatahub 2.11.2 DataHub SDK https://github.com/aliyun/aliyun-datahub-sdk-python
aliyun-mns 1.1.5 消息服务 https://github.com/gikoluo/aliyun-mns
aliyun-python-sdk-cdn 2.6.2 CDN 服务 https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-cdn
aliyun-python-sdk-ram 3.0.0 访问控制 RAM https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-ram
aliyun-python-sdk-sts 3.0.0 访问控制 STS https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-sts
aliyun-python-sdk-iot 7.8.0 物联网平台IOT https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-iot
aliyun-log-python-sdk 0.6.38 日志服务 SLS https://github.com/aliyun/aliyun-log-python-sdk
wand 0.4.4 图片处理库 http://docs.wand-py.org/en/0.4.4/
opencv 3.3.0.10 计算机视觉库 http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_setup/py_intro/py_intro.html
numpy 1.13.3 科学计算库 http://www.numpy.org/
scipy 1.0.0 科学计算库 https://www.scipy.org/
matplotlib 2.0.2 绘图库 https://matplotlib.org/
scrapy 1.4.0 数据抓取库 https://scrapy.org/

访问 OSS 的示例代码如下所示。

import json
import oss2
def my_handler(event, context):
    evt = json.loads(event)
    creds = context.credentials
    # do not forget security_token
    auth = oss2.StsAuth(creds.access_key_id, creds.access_key_secret, creds.security_token)
    bucket = oss2.Bucket(auth, evt['endpoint'], evt['bucket'])
    bucket.put_object(evt['objectName'], evt['message'])
    return 'success'
注意 在上面的代码示例中,演示了如何使用临时密钥向 OSS 中上传一个文件。其他使用 Python 的第三方库的示例请参见 fc-python-demo

使用自定义模块

如果您需要使用自定义的模块,则需要将自定义模块与代码一起打包上传。您可以通过 pip 包管理器进行依赖管理,如果您使用 Funcraft 部署应用,可以使用 fun install 命令来安装依赖。

  • 使用 pip 包管理器进行依赖管理

    使用方法:

    • 通过 pip install -t . 命令安装依赖库至函数根目录下。
    • 上传代码库时将依赖库一同打包上传。

    下文以安装 pymysql 库为例进行详细介绍。

    1. 建立一个目录,用于存放代码和依赖模块。
      mkdir /tmp/code
    2. /tmp/code 目录下安装依赖。
      cd /tmp/code
      pip install -t . PyMySQL
    3. 新建代码文件,例如 /tmp/code/index.py,在代码中使用 pymysql
      import pymysql.cursors
      # Connect to the database
      connection = pymysql.connect(host='localhost',
                                   user='user',
                                   password='passwd',
                                   db='db',
                                   charset='utf8mb4',
                                   cursorclass=pymysql.cursors.DictCursor)
      def handler(event, context):
       with connection.cursor() as cursor:
           # Read a single record
           sql = "SELECT count(*) FROM `users`"
           cursor.execute(sql)
           result = cursor.fetchone()
           print(result)
           return result
    4. 打包上传。

      打包时,需要针对文件进行打包,而不是针对代码整体目录进行打包;打包完成后,入口函数文件需要位于包内的根目录。

      • 在 Windows 下打包时,可以进入函数代码目录,全选所有文件以后,单击鼠标右键,选择 压缩为 zip 包,生成代码包。
      • 在 Linux 下打包时,通过调用 zip 命令时,将源文件指定为代码目录下的所有文件,实现生成部署代码包,例如 zip code.zip /home/code/*

      打包后,您通过函数计算控制台代码执行页面,您可以选择OSS 上传代码包上传方式上传代码包。

  • 使用 fun install 安装依赖

    如果您使用 Funcraft 部署应用,可以使用 fun install 命令来安装依赖。关于使用 Funcraft 的部署应用请参见 Fun,关于 fun install 的详细信息请参见 fun install 介绍

    下文以安装 pymysql 库为例进行详细介绍。

    1. 在函数根目录下初始化 Funfile 文件。
      $ fun install init
      ? Select a runtime   
        nodejs8   
        nodejs10   
        python2.7 
      ❯ python3   
        java8   
        php7.2   
        dotnetcore2.1 
       (Move up and down to reveal more choices)
    2. 选择 python3 后,会在当前目录生成一个名为 Funfile 的文件,编辑文件内容。
      RUNTIME python3RUN 
      fun-install pip install PyMySQL
    3. 新建 template.yml 文件,例如 /tmp/code/template.yml,详情请参见template.yml,内容如下。
      ROSTemplateFormatVersion: '2015-09-01'
      Transform: 'Aliyun::Serverless-2018-04-03'
      Resources:
      FunDemo:  
       Type: 'Aliyun::Serverless::Service' 
       pythondemo:    
         Type: 'Aliyun::Serverless::Function'   
         Properties:     
           Handler: index.handler     
           Runtime: python3     
           CodeUri: './'
      这个 template.yml 的含义如下:声明名为 FunDemo 的服务,在这个服务下,声明一个名为 pythondemo 的函数,配置函数入口为 index.handler ,函数的 runtime 为 python3,并指定 CodeUri 为当前目录。在部署时,Fun 会将 CodeUri 指定的目录打包上传。
    4. 在函数根目录下执行 fun install 命令安装依赖。
      $ fun install
      using template: template.yml
      start installing function dependencies without docker
      
      building FunDemo/pythondemoFunfile exist, 
      Fun will use container to build forcely
      Step 1/2 : FROM registry.cn-beijing.aliyuncs.com/aliyunfc/runtime-python3.6:build-1.9.4
      ---> 702c91653452
      Step 2/2 : RUN fun-install pip install PyMySQL
      ---> Using cache
      ---> 8f59062f15bb
      sha256:8f59062f15bb7a86bd59b85e3b61bd0e4ed711c536fe0cd10fdefebc78eae152
      Successfully built 8f59062f15bb
      Successfully tagged fun-cache-a6f4221c-33c8-4050-93b8-015e42396475:latest
      copying function artifact to /Users/txd123/Desktop/express
      
      Install Success
      
      
      Tips for next step
      ======================
      * Invoke Event Function: fun local invoke
      * Invoke Http Function: fun local start
      * Build Http Function: fun build
      * Deploy Resources: fun deploy
    5. 使用 Funcraft 部署,具体内容请参见功能概览
      fun deploy -y

      执行成功时,会看到相关日志。

      $ fun deploy -y
      using template: template.yml
      using region: cn-hangzhou
      using accountId: ***********3743using accessKeyId: ***********Ptgk
      using timeout: 60
      
      Collecting your services information, in order to caculate devlopment changes...
      
      Resources Changes(Beta version! Only FC resources changes will be displayed):
      
      ┌────────────┬──────────────────────────────┬────────┬──────────┐
      │ Resource   │ ResourceType                 │ Action │ Property │
      ├────────────┼──────────────────────────────┼────────┼──────────┤
      │            │                              │        │ Handler  │
      │            │                              │        ├──────────┤
      │ pythondemo │ Aliyun::Serverless::Function │ Add    │ Runtime  │
      │            │                              │        ├──────────┤
      │            │                              │        │ CodeUri  │
      └────────────┴──────────────────────────────┴────────┴──────────┘
      
      Waiting for service FunDemo to be deployed...      
            Waiting for function pythondemo to be deployed...              
                    Waiting for packaging function pythondemo code...              
                    The function pythondemo has been packaged. A total of 51 files were compressed and the final size was 114.35 KB
            function pythondemo deploy success
      service FunDemo deploy success

    登录函数计算控制台,即可看到相关的服务、函数被创建成功,且触发执行可以返回正确的结果。

调用外部命令

您的函数可能会用到一些工具,而这些工具并不是用 Python 写的(例如 Shell、C++ 、Go 编译出来的可执行文件)。您仍然可以将这些工具与代码一起打包,然后在函数中通过运行外部命令的方法来使用这些工具。

注意 使用 C 、 C++ 、Go 编译出来的可执行文件,需要与函数计算的运行环境兼容。函数计算的 Node.js 运行环境如下所示。
  • Linux 内核版本:Linux 4.4.24-2.al7.x86_64。
  • Docker 基础镜像:docker pull python:2.7 ; docker pull python:3.6。

您可以使用 Funcraft 工具安装依赖,具体内容请参见 Fun。下文以 runtime 为 python2.7,安装 mysql-python(含有 .so 文件)为例。

可以直接在代码目录执行 fun install --runtime python2.7 --package-type pip mysql-python 命令 ,即可安装好依赖。

注意
  • 第一次执行这个命令由于要拉取镜像,可能要耗费不少时间,请耐心等待。
  • 依赖(含有 mysql.so)会被装在 .fun 目录下,只需要在 template.yml 中通过 CodeUri 属性配置将 .fun 以及代码包含进来就可以了。

下面的代码演示了如何运行一个 Shell 脚本。

import os
import subprocess

def my_handler(event, context):    
    script_path = os.environ.get('FC_FUNC_CODE_PATH') + '/script.sh'    
    ret = subprocess.check_output(['bash', script_path])    
    return ret