From 60a3895455367768efef7eb29ba0d05268ce357b Mon Sep 17 00:00:00 2001 From: Daniel Paul Searles Date: Fri, 18 Jan 2019 22:06:09 -0800 Subject: [PATCH] Add support for poetry. Why: * [poetry](https://github.com/sdispater/poetry) is a Python dependency management and packaging tool. This change addresses the need by: * Taking the same approach as pipenv. * Install poetry on CircleCI and AppVeyor for tests. * Use chmodSync in test.js as it works better cross platform for setting exact permissions on files. See: https://github.com/nodejs/node/issues/1104 Side effects: * There may be conflicts or oddities if there is a Pipfile and a pyproject.toml. --- .tool-versions | 2 + README.md | 18 ++++ appveyor.yml | 1 + circle.yml | 11 +- index.js | 3 + lib/pip.js | 55 +++++++--- lib/poetry.js | 43 ++++++++ test.js | 117 +++++++++++++++++++- tests/poetry/.gitignore | 22 ++++ tests/poetry/_slimPatterns.yml | 2 + tests/poetry/handler.py | 5 + tests/poetry/package.json | 14 +++ tests/poetry/poetry.lock | 192 +++++++++++++++++++++++++++++++++ tests/poetry/pyproject.toml | 17 +++ tests/poetry/serverless.yml | 31 ++++++ 15 files changed, 518 insertions(+), 15 deletions(-) create mode 100644 .tool-versions create mode 100644 lib/poetry.js create mode 100644 tests/poetry/.gitignore create mode 100644 tests/poetry/_slimPatterns.yml create mode 100644 tests/poetry/handler.py create mode 100644 tests/poetry/package.json create mode 100644 tests/poetry/poetry.lock create mode 100644 tests/poetry/pyproject.toml create mode 100644 tests/poetry/serverless.yml diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..f9e0b286 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +nodejs 6.16.0 +python 3.6.8 2.7.15 diff --git a/README.md b/README.md index d8e5568a..f396dcb6 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,23 @@ custom: ``` +## Poetry support :sparkles::pencil::sparkles: +NOTE: Only poetry version 1 supports the required `export` command for this +feature. As of the point this feature was added, poetry 1.0.0 was in preview +and requires that poetry is installed with the --preview flag. + +TL;DR Install poetry with the `--preview` flag. + +If you include a `pyproject.toml` and have `poetry` installed instead of a `requirements.txt` this will use +`poetry export --without-hashes -f requirements.txt` to generate them. It is fully compatible with all options such as `zip` and +`dockerizePip`. If you don't want this plugin to generate it for you, set the following option: +```yaml +custom: + pythonRequirements: + usePoetry: false +``` + + ## Dealing with Lambda's size limitations To help deal with potentially large dependencies (for example: `numpy`, `scipy` and `scikit-learn`) there is support for compressing the libraries. This does @@ -405,3 +422,4 @@ zipinfo .serverless/xxx.zip * [@alexjurkiewicz](https://github.com/alexjurkiewicz) - [docs about docker workflows](#native-code-dependencies-during-build) * [@andrewfarley](https://github.com/andrewfarley) - Implemented download caching and static caching * [@bweigel](https://github.com/bweigel) - adding the `slimPatternsAppendDefaults` option & fixing per-function packaging when some functions don't have requirements & Porting tests from bats to js! + * [@squaresurf](https://github.com/squaresurf) - adding usePoetry option diff --git a/appveyor.yml b/appveyor.yml index a8301d47..f938aeb2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,7 @@ version: '{build}' init: - cmd: pip install pipenv + - cmd: pip install poetry==1.0.0a2 - ps: npm i -g serverless build: off test_script: diff --git a/circle.yml b/circle.yml index f86e51af..87341cbe 100644 --- a/circle.yml +++ b/circle.yml @@ -25,6 +25,11 @@ jobs: - run: sudo apt -y update && sudo apt -y install python-pip python2.7 curl unzip # instal pipenv - run: sudo python3.6 -m pip install pipenv pip-tools + # install poetry + - run: | + curl https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py -o get-poetry.py + python get-poetry.py --preview --yes + rm get-poetry.py # install nodejs - run: curl -sL https://deb.nodesource.com/setup_6.x | sudo bash - && sudo apt -y install nodejs # install serverless & depcheck @@ -36,4 +41,8 @@ jobs: # lint: - run: npm run lint # test! - - run: npm run test + - run: | + export PATH="$HOME/.poetry/bin:$PATH" + export LC_ALL=C.UTF-8 + export LANG=C.UTF-8 + npm run test diff --git a/index.js b/index.js index 470fcd67..dd20f7d7 100644 --- a/index.js +++ b/index.js @@ -12,6 +12,7 @@ const { const { injectAllRequirements } = require('./lib/inject'); const { installAllRequirements } = require('./lib/pip'); const { pipfileToRequirements } = require('./lib/pipenv'); +const { pyprojectTomlToRequirements } = require('./lib/poetry'); const { cleanup, cleanupCache } = require('./lib/clean'); BbPromise.promisifyAll(fse); @@ -35,6 +36,7 @@ class ServerlessPythonRequirements { invalidateCaches: false, fileName: 'requirements.txt', usePipenv: true, + usePoetry: true, pythonBin: process.platform === 'win32' ? 'python.exe' @@ -156,6 +158,7 @@ class ServerlessPythonRequirements { } return BbPromise.bind(this) .then(pipfileToRequirements) + .then(pyprojectTomlToRequirements) .then(addVendorHelper) .then(installAllRequirements) .then(packRequirements); diff --git a/lib/pip.js b/lib/pip.js index 538ad55a..df107e23 100644 --- a/lib/pip.js +++ b/lib/pip.js @@ -60,7 +60,22 @@ function generateRequirementsFile( servicePath, options ) { - if (options.usePipenv && fse.existsSync(path.join(servicePath, 'Pipfile'))) { + if ( + options.usePoetry && + fse.existsSync(path.join(servicePath, 'pyproject.toml')) + ) { + filterRequirementsFile( + path.join(servicePath, '.serverless/requirements.txt'), + targetFile, + options + ); + serverless.cli.log( + `Parsed requirements.txt from pyproject.toml in ${targetFile}...` + ); + } else if ( + options.usePipenv && + fse.existsSync(path.join(servicePath, 'Pipfile')) + ) { filterRequirementsFile( path.join(servicePath, '.serverless/requirements.txt'), targetFile, @@ -377,6 +392,31 @@ function copyVendors(vendorFolder, targetFolder, serverless) { }); } +/** + * This checks if requirements file exists. + * @param {string} servicePath + * @param {Object} options + * @param {string} fileName + */ +function requirementsFileExists(servicePath, options, fileName) { + if ( + options.usePoetry && + fse.existsSync(path.join(servicePath, 'pyproject.toml')) + ) { + return true; + } + + if (options.usePipenv && fse.existsSync(path.join(servicePath, 'Pipfile'))) { + return true; + } + + if (fse.existsSync(fileName)) { + return true; + } + + return false; +} + /** * This evaluates if requirements are actually needed to be installed, but fails * gracefully if no req file is found intentionally. It also assists with code @@ -399,17 +439,8 @@ function installRequirementsIfNeeded( const fileName = path.join(servicePath, modulePath, options.fileName); // Skip requirements generation, if requirements file doesn't exist - if (options.usePipenv) { - if ( - !fse.existsSync(path.join(servicePath, 'Pipfile')) && - !fse.existsSync(fileName) - ) { - return false; - } - } else { - if (!fse.existsSync(fileName)) { - return false; - } + if (!requirementsFileExists(servicePath, options, fileName)) { + return false; } let requirementsTxtDirectory; diff --git a/lib/poetry.js b/lib/poetry.js new file mode 100644 index 00000000..0ddf8da7 --- /dev/null +++ b/lib/poetry.js @@ -0,0 +1,43 @@ +const fse = require('fs-extra'); +const path = require('path'); +const { spawnSync } = require('child_process'); + +/** + * poetry install + */ +function pyprojectTomlToRequirements() { + if ( + !this.options.usePoetry || + !fse.existsSync(path.join(this.servicePath, 'pyproject.toml')) + ) { + return; + } + + this.serverless.cli.log('Generating requirements.txt from pyproject.toml...'); + + const res = spawnSync( + 'poetry', + ['export', '--without-hashes', '-f', 'requirements.txt'], + { + cwd: this.servicePath + } + ); + if (res.error) { + if (res.error.code === 'ENOENT') { + throw new Error( + `poetry not found! Install it according to the poetry docs.` + ); + } + throw new Error(res.error); + } + if (res.status !== 0) { + throw new Error(res.stderr); + } + fse.ensureDirSync(path.join(this.servicePath, '.serverless')); + fse.moveSync( + path.join(this.servicePath, 'requirements.txt'), + path.join(this.servicePath, '.serverless', 'requirements.txt') + ); +} + +module.exports = { pyprojectTomlToRequirements }; diff --git a/test.js b/test.js index 64d15221..08dd3f68 100644 --- a/test.js +++ b/test.js @@ -4,6 +4,7 @@ const glob = require('glob-all'); const JSZip = require('jszip'); const tape = require('tape'); const { + chmodSync, removeSync, readFileSync, copySync, @@ -710,6 +711,94 @@ test("pipenv py3.6 doesn't package bottle with noDeploy option", t => { t.end(); }); +test('poetry py3.6 can package flask with default options', t => { + process.chdir('tests/poetry'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.false(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is NOT packaged'); + t.end(); +}); + +test('poetry py3.6 can package flask with slim option', t => { + process.chdir('tests/poetry'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['--slim=true', 'package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.deepEqual( + zipfiles.filter(filename => filename.endsWith('.pyc')), + [], + 'no pyc files packaged' + ); + t.true( + zipfiles.filter(filename => filename.endsWith('__main__.py')).length > 0, + '__main__.py files are packaged' + ); + t.end(); +}); + +test('poetry py3.6 can package flask with slim & slimPatterns options', t => { + process.chdir('tests/poetry'); + + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['--slim=true', 'package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.deepEqual( + zipfiles.filter(filename => filename.endsWith('.pyc')), + [], + 'no pyc files packaged' + ); + t.deepEqual( + zipfiles.filter(filename => filename.endsWith('__main__.py')), + [], + '__main__.py files are NOT packaged' + ); + t.end(); +}); + +test('poetry py3.6 can package flask with zip option', t => { + process.chdir('tests/poetry'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls([`--pythonBin=${getPythonBin(3)}`, '--zip=true', 'package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true( + zipfiles.includes('.requirements.zip'), + 'zipped requirements are packaged' + ); + t.true(zipfiles.includes(`unzip_requirements.py`), 'unzip util is packaged'); + t.false( + zipfiles.includes(`flask${sep}__init__.py`), + "flask isn't packaged on its own" + ); + t.end(); +}); + +test("poetry py3.6 doesn't package bottle with noDeploy option", t => { + process.chdir('tests/poetry'); + const path = npm(['pack', '../..']); + npm(['i', path]); + perl([ + '-p', + '-i.bak', + '-e', + 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', + 'serverless.yml' + ]); + sls(['package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.false(zipfiles.includes(`bottle.py`), 'bottle is NOT packaged'); + t.end(); +}); + test('py3.6 can package flask with zip option and no explicit include', t => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -760,7 +849,8 @@ test( 's/(handler.py.*$)/$1\n - foobar/', 'serverless.yml' ]); - writeFileSync(`foobar`, '', { mode: perm }); + writeFileSync(`foobar`, ''); + chmodSync(`foobar`, perm); sls(['--vendor=./vendor', 'package']); const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); @@ -1001,6 +1091,27 @@ test('pipenv py3.6 can package flask with slim & slimPatterns & slimPatternsAppe t.end(); }); +test('poetry py3.6 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', t => { + process.chdir('tests/poetry'); + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + + sls(['--slim=true', '--slimPatternsAppendDefaults=false', 'package']); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true( + zipfiles.filter(filename => filename.endsWith('.pyc')).length >= 1, + 'pyc files are packaged' + ); + t.deepEqual( + zipfiles.filter(filename => filename.endsWith('__main__.py')), + [], + '__main__.py files are NOT packaged' + ); + t.end(); +}); + test('py3.6 can package flask with package individually option', t => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1449,7 +1560,8 @@ test( process.chdir('tests/individually'); const path = npm(['pack', '../..']); const perm = '775'; - writeFileSync(`module1${sep}foobar`, '', { mode: perm }); + writeFileSync(`module1${sep}foobar`, ''); + chmodSync(`module1${sep}foobar`, perm); npm(['i', path]); sls(['package']); @@ -1475,6 +1587,7 @@ test( const path = npm(['pack', '../..']); const perm = '775'; writeFileSync(`module1${sep}foobar`, '', { mode: perm }); + chmodSync(`module1${sep}foobar`, perm); npm(['i', path]); sls(['--dockerizePip=true', 'package']); diff --git a/tests/poetry/.gitignore b/tests/poetry/.gitignore new file mode 100644 index 00000000..3c2369dc --- /dev/null +++ b/tests/poetry/.gitignore @@ -0,0 +1,22 @@ +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# Serverless +.serverless +.requirements +unzip_requirements.py diff --git a/tests/poetry/_slimPatterns.yml b/tests/poetry/_slimPatterns.yml new file mode 100644 index 00000000..02c631b4 --- /dev/null +++ b/tests/poetry/_slimPatterns.yml @@ -0,0 +1,2 @@ +slimPatterns: + - "**/__main__.py" diff --git a/tests/poetry/handler.py b/tests/poetry/handler.py new file mode 100644 index 00000000..5e2e67ff --- /dev/null +++ b/tests/poetry/handler.py @@ -0,0 +1,5 @@ +import requests + + +def hello(event, context): + return requests.get('https://httpbin.org/get').json() diff --git a/tests/poetry/package.json b/tests/poetry/package.json new file mode 100644 index 00000000..d13fd651 --- /dev/null +++ b/tests/poetry/package.json @@ -0,0 +1,14 @@ +{ + "name": "example", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "serverless-python-requirements": "file:serverless-python-requirements-4.2.5.tgz" + } +} diff --git a/tests/poetry/poetry.lock b/tests/poetry/poetry.lock new file mode 100644 index 00000000..2cc3a756 --- /dev/null +++ b/tests/poetry/poetry.lock @@ -0,0 +1,192 @@ +[[package]] +category = "main" +description = "The AWS SDK for Python" +name = "boto3" +optional = false +python-versions = "*" +version = "1.9.80" + +[package.dependencies] +botocore = ">=1.12.80,<1.13.0" +jmespath = ">=0.7.1,<1.0.0" +s3transfer = ">=0.1.10,<0.2.0" + +[[package]] +category = "main" +description = "Low-level, data-driven core of boto 3." +name = "botocore" +optional = false +python-versions = "*" +version = "1.12.80" + +[package.dependencies] +docutils = ">=0.10" +jmespath = ">=0.7.1,<1.0.0" + +[package.dependencies.python-dateutil] +python = ">=2.7" +version = ">=2.1,<3.0.0" + +[package.dependencies.urllib3] +python = ">=3.4" +version = ">=1.20,<1.25" + +[[package]] +category = "main" +description = "Fast and simple WSGI-framework for small web-applications." +name = "bottle" +optional = false +python-versions = "*" +version = "0.12.16" + +[[package]] +category = "main" +description = "Composable command line interface toolkit" +name = "click" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "7.0" + +[[package]] +category = "main" +description = "Docutils -- Python Documentation Utilities" +name = "docutils" +optional = false +python-versions = "*" +version = "0.14" + +[[package]] +category = "main" +description = "A simple framework for building complex web applications." +name = "flask" +optional = false +python-versions = "*" +version = "1.0.2" + +[package.dependencies] +Jinja2 = ">=2.10" +Werkzeug = ">=0.14" +click = ">=5.1" +itsdangerous = ">=0.24" + +[package.extras] +dev = ["pytest (>=3)", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet"] +docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet"] +dotenv = ["python-dotenv"] + +[[package]] +category = "main" +description = "Various helpers to pass data to untrusted environments and back." +name = "itsdangerous" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.1.0" + +[[package]] +category = "main" +description = "A small but fast and easy to use stand-alone template engine written in pure python." +name = "jinja2" +optional = false +python-versions = "*" +version = "2.10" + +[package.dependencies] +MarkupSafe = ">=0.23" + +[package.extras] +i18n = ["Babel (>=0.8)"] + +[[package]] +category = "main" +description = "JSON Matching Expressions" +name = "jmespath" +optional = false +python-versions = "*" +version = "0.9.3" + +[[package]] +category = "main" +description = "Safely add untrusted strings to HTML/XML markup." +name = "markupsafe" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "1.1.0" + +[[package]] +category = "main" +description = "Extensions to the standard Python datetime module" +marker = "python_version >= \"2.7\"" +name = "python-dateutil" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.7.5" + +[package.dependencies] +six = ">=1.5" + +[[package]] +category = "main" +description = "An Amazon S3 Transfer Manager" +name = "s3transfer" +optional = false +python-versions = "*" +version = "0.1.13" + +[package.dependencies] +botocore = ">=1.3.0,<2.0.0" + +[[package]] +category = "main" +description = "Python 2 and 3 compatibility utilities" +marker = "python_version >= \"2.7\"" +name = "six" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "1.12.0" + +[[package]] +category = "main" +description = "HTTP library with thread-safe connection pooling, file post, and more." +marker = "python_version >= \"3.4\"" +name = "urllib3" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" +version = "1.24.1" + +[package.extras] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + +[[package]] +category = "main" +description = "The comprehensive WSGI web application library." +name = "werkzeug" +optional = false +python-versions = "*" +version = "0.14.1" + +[package.extras] +dev = ["coverage", "pytest", "sphinx", "tox"] +termcolor = ["termcolor"] +watchdog = ["watchdog"] + +[metadata] +content-hash = "fa5a641a2c19871b5899fbc700d6375250a5d2e327832a012296af6a31c8093a" +python-versions = "^3.6" + +[metadata.hashes] +boto3 = ["122603b00f8c458236d1bd09850bdea56fc45f271e75ca38e66dbce37f72cada", "99ec19dc4f0aa8a8354db7baebe1ff57bd18aeb6a539b28693b2e8ca8dc3d85b"] +botocore = ["76a2969278250e010253ddf514f4b54eaa7d2b1430f682874c3c2ab92f25a96d", "8c579bac9abeaff1270a7a25964b01d3db1367f42fa5f826e1303ec8a4b13cef"] +bottle = ["9c310da61e7df2b6ac257d8a90811899ccb3a9743e77e947101072a2e3186726", "ca43beafbdccabbe31b758a4f34d1e44985a9b9539516775208b2b0f903eafa0"] +click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] +docutils = ["02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", "51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", "7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"] +flask = ["2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48", "a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"] +itsdangerous = ["321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", "b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"] +jinja2 = ["74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", "f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"] +jmespath = ["6a81d4c9aa62caf061cb517b4d9ad1dd300374cd4706997aff9cd6aedd61fc64", "f11b4461f425740a1d908e9a3f7365c3d2e569f6ca68a2ff8bc5bcd9676edd63"] +markupsafe = ["048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432", "130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b", "19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9", "1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af", "1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834", "1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd", "1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d", "31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7", "3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b", "4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3", "525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c", "52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2", "52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7", "5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36", "5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1", "5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e", "7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1", "83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c", "857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856", "98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550", "bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492", "d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672", "e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401", "edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6", "efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6", "f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c", "f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd", "fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"] +python-dateutil = ["063df5763652e21de43de7d9e00ccf239f953a832941e37be541614732cdfc93", "88f9287c0174266bb0d8cedd395cfba9c58e87e5ad86b2ce58859bc11be3cf02"] +s3transfer = ["90dc18e028989c609146e241ea153250be451e05ecc0c2832565231dacdf59c1", "c7a9ec356982d5e9ab2d4b46391a7d6a950e2b04c472419f5fdec70cc0ada72f"] +six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] +urllib3 = ["61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", "de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"] +werkzeug = ["c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", "d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"] diff --git a/tests/poetry/pyproject.toml b/tests/poetry/pyproject.toml new file mode 100644 index 00000000..20e85d92 --- /dev/null +++ b/tests/poetry/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "poetry" +version = "0.1.0" +description = "" +authors = ["Your Name "] + +[tool.poetry.dependencies] +python = "^3.6" +Flask = "^1.0" +bottle = "^0.12.16" +boto3 = "^1.9" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" diff --git a/tests/poetry/serverless.yml b/tests/poetry/serverless.yml new file mode 100644 index 00000000..6df76a55 --- /dev/null +++ b/tests/poetry/serverless.yml @@ -0,0 +1,31 @@ +service: sls-py-req-test + +provider: + name: aws + runtime: python3.6 + +plugins: + - serverless-python-requirements +custom: + pythonRequirements: + zip: ${opt:zip, self:custom.defaults.zip} + slim: ${opt:slim, self:custom.defaults.slim} + slimPatterns: ${file(./slimPatterns.yml):slimPatterns, self:custom.defaults.slimPatterns} + slimPatternsAppendDefaults: ${opt:slimPatternsAppendDefaults, self:custom.defaults.slimPatternsAppendDefaults} + dockerizePip: ${opt:dockerizePip, self:custom.defaults.dockerizePip} + defaults: + zip: false + slimPatterns: false + slimPatternsAppendDefaults: true + slim: false + dockerizePip: false + +package: + exclude: + - '**/*' + include: + - handler.py + +functions: + hello: + handler: handler.hello