【发布时间】:2021-11-09 17:37:52
【问题描述】:
编辑 3:
所以问题可能出在我的 Lambda 层依赖项的设置和配置中。我有一个包含 3 个文件的 /bin 目录:
- lambdazip.sh
- pdftk
- libgcj.so.10
pdftk 是一个 pdf 库,而 libgcj 是 PDFtk 的依赖项。
lambdazip.sh 似乎可以设置和修改 PATH 变量。
我已经尝试将所有 3 个作为 1 个 lambda 层上传。
我已经尝试将所有 3 个作为 3 个单独的 lambda 层上传。
我没有尝试自定义 .zip 文件名,我知道有时 Lambda 层希望您根据语言将 .zip 文件命名为特定名称。
我没有尝试自定义“兼容架构”和“兼容运行时”lambda 层设置。
编辑 2:
我尝试将 Lambda 层重命名为 Python.zip,因为我听说有时您需要特定的命名约定才能使 Lambda 层正常工作。这也失败并产生了同样的错误。
编辑:
我尝试将 .py 文件从 /surveys 目录中拉出,因此当它们被压缩时,它们位于根文件夹中,但我仍然收到相同的错误:Runtime.ImportModuleError: Unable to import module 'lambda_function': No module名为“调查”
我需要压缩哪些文件?我需要将某些文件移动到根目录吗?
我了解到我不小心压缩了通常导致此错误的目录。
我需要压缩目录的内容,这是一个常见的解决方案。
不幸的是,这对我不起作用。
我有一个 Lambda 函数,我上传的代码是我的 /Archive 目录的压缩文件夹。
据我了解,许多遇到此 "[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_function':" 的人都因为他们的 Lambda 处理程序而遇到问题。
我的 Lambda 处理程序是:lambda_function.lambda_handler 所以这似乎不是我的问题。
我在 Stackoverflow 上注意到的另一个常见问题似乎是人们如何压缩和压缩他们上传到 Lambda 函数的文件。
我需要移动我的 lambda_function.py 吗?有时会出现此 CloudWatch 错误,因为 lambda_function.py 不在 ROOT 目录中。
我的调查目录需要移动吗?
我认为我在这里拥有的文件夹和目录可能会导致我的问题。
我需要单独压缩目录吗?
我可以通过压缩整个项目来解决这个错误吗?
有关更多信息,我还有一个用于 PDF 工具包的 Lambda 层,在代码库中称为 pyPDFtk。在那个 Lambda 层中是一个压缩的 /bin,里面有二进制文件。
如果我可以在我的代码或 AWS 配置中更改/更改任何内容,请告诉我,我可以为您返回新的 CloudWatch 错误日志。
lambda_function.py
"""
cl_boost-pdfgen manages form to
pdf merge and mail
"""
import json, base64
import os, sys
from string import Template
from boost import PageCalc, AwsWrapper
from boost.tools import helper
from boost.surveys import ALLOWED_SURVEYS
os.environ['LAMBDA_TASK_ROOT'] = os.environ['LAMBDA_TASK_ROOT'] if 'LAMBDA_TASK_ROOT' in os.environ else '/usr/local'
os.environ['PDFTK_PATH'] = os.environ['LAMBDA_TASK_ROOT'] + '/bin/pdftk'
os.environ['LD_LIBRARY_PATH'] = os.environ['LAMBDA_TASK_ROOT'] + '/bin'
# must import after setting env vars for pdftk
import pypdftk
# Constants
BUCKET_NAME = os.environ['BUCKET_NAME'] if 'BUCKET_NAME' in os.environ else 'cl-boost-us-east-1-np'
RAW_MESSAGE = Template(b'''From: ${from}
To: ${to}
Subject: MySteadyMind Survey results for ${subjname}
MIME-Version: 1.0
Content-type: Multipart/Mixed; boundary = "NextPart"
--NextPart
Content-Type: multipart/alternative; boundary="AlternativeBoundaryString"
--AlternativeBoundaryString
Content-Type: text/plain;charset="utf-8"
Content-Transfer-Encoding: quoted-printable
See attachment for MySteadyMind report on ${subjname}
--AlternativeBoundaryString
Content-Type: text/html;charset="utf-8"
Content-Transfer-Encoding: quoted-printable
<html>
<body>=0D
<p>See attachment for MySteadyMind Report on </b> ${subjname} </b>.</p>=0D
</body>=0D
</html>=0D
--AlternativeBoundaryString--
--NextPart
Content-type: application / pdf
Content-Type: application/pdf;name="${filename}"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;filename="${filename}"
${pdfdata}
--NextPart--''')
EMAIL_TAKER = True
#DEFAULT_EMAIL = os.environ['DEFAULT_EMAIL'] if 'DEFAULT_EMAIL' in os.environ else 'support@mysteadymind.com'
DEFAULT_EMAIL = os.environ['DEFAULT_EMAIL'] if 'DEFAULT_EMAIL' in os.environ else 'marshall@volociti.com'
SUBJECT = 'Evaluation for %s'
NAME_PATH = ['Entry', 'Name']
#EXTRA_EMAILS = os.environ['EXTRA_EMAILS'].split(",") if 'EXTRA_EMAILS' in os.environ else ['seth@mysteadymind.com']
EXTRA_EMAILS = os.environ['EXTRA_EMAILS'].split(",") if 'EXTRA_EMAILS' in os.environ else ['marshall@volociti.com']
# Lambda response
def respond(err, res=None):
"""
parameters are expected to either be
None or valid JSON ready to go.
:param err:
:param res:
:return:
"""
return {
'statusCode': '400' if err else '200',
'body': err if err else res,
'headers': {
'Content-Type': 'application/json',
},
}
def check_basic_auth(headers):
"""
pull out the auth header and validate.
:param headers:
:return:
# Retrieve values from env
vid = os.environ['uid']
vpw = os.environ['pwd']
encoded = "Basic " + base64.b64encode("%s:%s" % (vid,vpw))
# compare
return headers['Authorization'] == encoded
"""
return True
def lambda_handler(event, context):
"""
receive JSON, produce PDF, save to S3,
email via SES.... bring it!
"""
err = None
rsp = None
#Have to none out addresses for future lambda runs to not cause issues with appending.
ADDRESSES = None
ADDRESSES = {'from': "marshall@volociti.com",
'to': [DEFAULT_EMAIL] + EXTRA_EMAILS}
"""
ADDRESSES = {'from': "support@mysteadymind.com",
'to': [DEFAULT_EMAIL] + EXTRA_EMAILS}
"""
# check auth
if not check_basic_auth(event['headers']):
print ("Failed to authenticate")
return False
# get dataq
data = json.loads(event['body'])
# make sure its legit
if (data['Form']['InternalName'] not in ALLOWED_SURVEYS):
return False
# read in template and prep survey type and scoreit
pcalc = PageCalc(data, data['Form']['InternalName'])
pcalc.score()
pcalc.flat['Name'] = data['Section']['FirstName'] + \
" " + data['Section']['LastName']
# output pdf to temp space
# baseName = str(data['Entry']['Number']) + "-" + pcalc.survey + "-" + \
# data['Section']['LastName'].replace(' ','') + ".pdf"
baseName = str(data['Entry']['Number']) + "-MySteadyMind-" + \
data['Section']['LastName'].replace(' ','') + ".pdf"
filename = "/tmp/" + baseName
pypdftk.fill_form(pcalc.pdf_path, pcalc.flat, out_file=filename)
# -- Post Processing after PDF Generation -- #
# fetch the client wrapper(s)
aws = AwsWrapper()
# get PDF data and prep for email
try:
# save the pdf to S3
print("save %s to S3" % filename)
aws.save_file(BUCKET_NAME, pcalc.survey,filename)
# read in the pdf file data and
# base64 encode it
buff = open(filename, "rb")
pdfdata = base64.b64encode(buff.read())
buff.close()
ADDRESSES['to'].append(data['Section']['Email']) if EMAIL_TAKER else None
# gather data needed for email body
data = {"from": ADDRESSES['from'],
"to": ', '.join(ADDRESSES['to']),
"subjname": pcalc.flat["Name"],
"filename": baseName,
"pdfdata": pdfdata
}
print("sending email via SES to: %s" % ', '.join(ADDRESSES['to']))
# build MMM email and send via SES
response = aws.send_raw_mail(ADDRESSES['from'],
ADDRESSES['to'],
RAW_MESSAGE.substitute(data))
# send JSON response
rsp = '{"Code": 200, "Message": "%s"}' % response
except Exception as ex:
# error trap for all occassions
errmsg = "Exception Caught: %s" % ex
# notify local log
print(errmsg)
# and lambda response
err = '{"Code":500, "Message":"%s"}' % errmsg
# done
return respond(err, rsp)
if __name__ == '__main__':
# use this option to manually generate from raw csv of cognitoforms
if len(sys.argv) > 1:
import csv
with open(sys.argv[1], 'rU') as csvfile:
csvreader = csv.DictReader(csvfile, delimiter=',',)
for row in csvreader:
jsondata = helper.create_json(row)
fakeevent = {'body': json.dumps(jsondata), "headers": []}
lambda_handler(fakeevent, None)
# use this option to manually generate from raw json webhook response from cognito in a dir called generated/
else:
rng = range(3,4)
print (rng)
print ("Attempting to parse files: " + str(rng))
for i in rng:
try:
print ('./generated/queue/' + str(i) + '.json')
f = open('./generated/queue/' + str(i) + '.json', 'r')
jsondata = f.read().replace('\n', '')
f.close()
#jdata = json.loads(jsondata)
fakeevent = {'body': jsondata, "headers": []}
lambda_handler(fakeevent, None)
except:
print ("error. file not found: " + str(i))
lambdazip.sh
#!/bin/bash
PYTHON_PATH=$VIRTUAL_ENV/..
BASE_PATH=$PWD
cd $VIRTUAL_ENV/lib/python3.9/site-packages/
zip -x "*pyc" -r9 $BASE_PATH/dist/cl-boost.zip pypdftk*
cd $BASE_PATH
zip -r $BASE_PATH/dist/cl-boost.zip bin
cd $BASE_PATH
zip -x "*pyc" -r9 $BASE_PATH/dist/cl-boost.zip boost* pdf_surveys
【问题讨论】:
-
我建议你分享 lambda 代码,或者至少分享足够多的代码,以便它是一个可重现的示例
-
添加了 lambda_function.py & 我可以根据要求添加更多内容。
-
我了解到我错误地压缩了项目。最初,我只是压缩根目录。现在我已经正确压缩了每个文件,但我得到了相同的“没有名为 /surveys 的模块”错误
-
“未命名模块”错误似乎暗示您配置的层没有所需的库。见stackoverflow.com/a/64462403/3390419
-
'surveys' 是我项目中的一个目录,包含 5 个 .py 文件。我会查看我的图层和您提供的链接。
标签: python amazon-web-services aws-lambda