From cb8db5fb1833abb2ad51be5ffc4666dba4021d02 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Fri, 9 Feb 2024 22:52:19 -0500 Subject: [PATCH 01/20] Inital Work to add in changes for PEP631 complience --- index.js | 2 + lib/pip.js | 26 ++++++- lib/poetry.js | 4 +- lib/pyproject.js | 75 +++++++++++++++++++ tests/base/serverless.yml | 3 +- tests/pyproject_packages/handler.py | 5 ++ tests/pyproject_packages/package.json | 14 ++++ tests/pyproject_packages/pyproject.toml | 19 +++++ tests/pyproject_packages/serverless.yml | 23 ++++++ tests/pyproject_packages_optional/handler.py | 5 ++ .../pyproject_packages_optional/package.json | 14 ++++ .../pyproject.toml | 19 +++++ .../serverless.yml | 25 +++++++ 13 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 lib/pyproject.js create mode 100644 tests/pyproject_packages/handler.py create mode 100644 tests/pyproject_packages/package.json create mode 100644 tests/pyproject_packages/pyproject.toml create mode 100644 tests/pyproject_packages/serverless.yml create mode 100644 tests/pyproject_packages_optional/handler.py create mode 100644 tests/pyproject_packages_optional/package.json create mode 100644 tests/pyproject_packages_optional/pyproject.toml create mode 100644 tests/pyproject_packages_optional/serverless.yml diff --git a/index.js b/index.js index 25cc34cd..c3ce1993 100644 --- a/index.js +++ b/index.js @@ -61,6 +61,8 @@ class ServerlessPythonRequirements { poetryWithGroups: [], poetryWithoutGroups: [], poetryOnlyGroups: [], + usePyProject: true, + OptionalDeps: [] }, (this.serverless.service.custom && this.serverless.service.custom.pythonRequirements) || diff --git a/lib/pip.js b/lib/pip.js index 149c0285..92cb05ed 100644 --- a/lib/pip.js +++ b/lib/pip.js @@ -7,7 +7,11 @@ const spawn = require('child-process-ext/spawn'); const { quote } = require('shell-quote'); const { buildImage, getBindPath, getDockerUid } = require('./docker'); const { getStripCommand, getStripMode, deleteFiles } = require('./slim'); -const { isPoetryProject, pyprojectTomlToRequirements } = require('./poetry'); +const { isPoetryProject, poetryTomlToRequirements } = require('./poetry'); +const { + pyprojectTomlToRequirements, + isPyProjectProject, +} = require('./pyproject'); const { checkForAndDeleteMaxCacheVersions, sha256Path, @@ -64,10 +68,12 @@ function generateRequirementsFile( if (options.usePoetry && isPoetryProject(modulePath)) { filterRequirementsFile(targetFile, targetFile, pluginInstance); if (log) { - log.info(`Parsed requirements.txt from pyproject.toml in ${targetFile}`); + log.info( + `Parsed requirements.txt from pyproject.toml poetry in ${targetFile}` + ); } else { serverless.cli.log( - `Parsed requirements.txt from pyproject.toml in ${targetFile}...` + `Parsed requirements.txt from pyproject.toml poetry in ${targetFile}...` ); } } else if ( @@ -86,6 +92,15 @@ function generateRequirementsFile( `Parsed requirements.txt from Pipfile in ${targetFile}...` ); } + } else if (options.usePyProject && isPyProjectProject(modulePath)) { + filterRequirementsFile(targetFile, targetFile, pluginInstance); + if (log) { + log.info(`Parsed requirements.txt from pyproject.toml in ${targetFile}`); + } else { + serverless.cli.log( + `Parsed requirements.txt from pyproject.toml in ${targetFile}...` + ); + } } else { filterRequirementsFile(requirementsPath, targetFile, pluginInstance); if (log) { @@ -585,6 +600,10 @@ function requirementsFileExists(servicePath, options, fileName) { return true; } + if (options.usePyProject && isPyProjectProject(path.dirname(fileName))) { + return true; + } + return false; } @@ -608,6 +627,7 @@ async function installRequirementsIfNeeded( // Our source requirements, under our service path, and our module path (if specified) const fileName = path.join(servicePath, modulePath, options.fileName); + await poetryTomlToRequirements(modulePath, pluginInstance); await pyprojectTomlToRequirements(modulePath, pluginInstance); // Skip requirements generation, if requirements file doesn't exist diff --git a/lib/poetry.js b/lib/poetry.js index 17e3268f..e2943f84 100644 --- a/lib/poetry.js +++ b/lib/poetry.js @@ -8,7 +8,7 @@ const tomlParse = require('@iarna/toml/parse-string'); /** * poetry install */ -async function pyprojectTomlToRequirements(modulePath, pluginInstance) { +async function poetryTomlToRequirements(modulePath, pluginInstance) { const { serverless, servicePath, options, log, progress } = pluginInstance; const moduleProjectPath = path.join(servicePath, modulePath); @@ -140,4 +140,4 @@ function isPoetryProject(servicePath) { return false; } -module.exports = { pyprojectTomlToRequirements, isPoetryProject }; +module.exports = { poetryTomlToRequirements, isPoetryProject }; diff --git a/lib/pyproject.js b/lib/pyproject.js new file mode 100644 index 00000000..d9eb0016 --- /dev/null +++ b/lib/pyproject.js @@ -0,0 +1,75 @@ +const fs = require('fs'); +const fse = require('fs-extra'); +const path = require('path'); + +const tomlParse = require('@iarna/toml/parse-string'); + +function pyprojectTomlToRequirements(modulePath, pluginInstance) { + const { serverless, servicePath, options, log, progress } = pluginInstance; + + const moduleProjectPath = path.join(servicePath, modulePath); + if (!options.usePyProject || !isPyProjectProject(moduleProjectPath)) { + return; + } + + let generateRequirementsProgress; + if (progress && log) { + generateRequirementsProgress = progress.get( + 'python-generate-requirements-toml' + ); + } else { + serverless.cli.log('Generating requirements.txt from pyproject.toml'); + } + + try { + const pyprojectPath = path.join(servicePath, 'pyproject.toml'); + const pyprojectToml = fs.readFileSync(pyprojectPath); + const pyproject = tomlParse(pyprojectToml); + + const dependencies = pyproject['project']['dependencies']; + + if (options.OptionalDeps) { + for (const optionalDep of options.OptionalDeps) { + try { + dependencies.push( + pyproject['project']['optional-dependencies'][optionalDep] + ); + } catch (e) { + if (log) { + log.warn( + 'Optional dependency (%s) not found in pyproject.toml', + optionalDep + ); + } + } + } + } + + fse.ensureDirSync(path.join(this.servicePath, '.serverless')); + fse.writeFileSync( + path.join(this.servicePath, '.serverless/requirements.txt'), + dependencies.join('\n') + ); + } finally { + generateRequirementsProgress && generateRequirementsProgress.remove(); + } +} + +function isPyProjectProject(servicePath) { + const pyprojectPath = path.join(servicePath, 'pyproject.toml'); + + if (!fse.existsSync(pyprojectPath)) { + return false; + } + + const pyprojectToml = fs.readFileSync(pyprojectPath); + const pyproject = tomlParse(pyprojectToml); + + if (pyproject['project'] && pyproject['project']['dependencies']) { + return true; + } + + return false; +} + +module.exports = { pyprojectTomlToRequirements, isPyProjectProject }; diff --git a/tests/base/serverless.yml b/tests/base/serverless.yml index a82187ff..c67bcc88 100644 --- a/tests/base/serverless.yml +++ b/tests/base/serverless.yml @@ -3,6 +3,7 @@ service: sls-py-req-test provider: name: aws runtime: ${env:runtime, 'python3.9'} + configValidationMode: warn plugins: - serverless-python-requirements @@ -47,7 +48,7 @@ functions: handler: handler.hello hello3: handler: handler.hello - runtime: nodejs8.10 + runtime: nodejs18.19 hello4: handler: fn2_handler.hello module: fn2 diff --git a/tests/pyproject_packages/handler.py b/tests/pyproject_packages/handler.py new file mode 100644 index 00000000..5e2e67ff --- /dev/null +++ b/tests/pyproject_packages/handler.py @@ -0,0 +1,5 @@ +import requests + + +def hello(event, context): + return requests.get('https://httpbin.org/get').json() diff --git a/tests/pyproject_packages/package.json b/tests/pyproject_packages/package.json new file mode 100644 index 00000000..f771e78e --- /dev/null +++ b/tests/pyproject_packages/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:.." + } +} diff --git a/tests/pyproject_packages/pyproject.toml b/tests/pyproject_packages/pyproject.toml new file mode 100644 index 00000000..232f0d07 --- /dev/null +++ b/tests/pyproject_packages/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "example" +version = "1.0.0" +authors = [ + { name="Jackson Borneman", email="11304426+jax-b@users.noreply.github.com" }, +] +description = "example" +requires-python = ">=3.10" + +dependencies = [ + "flask", + "hug", + "boto3" +] + +[project.optional-dependencies] +test = [ + "pytest", +] diff --git a/tests/pyproject_packages/serverless.yml b/tests/pyproject_packages/serverless.yml new file mode 100644 index 00000000..e5c4c924 --- /dev/null +++ b/tests/pyproject_packages/serverless.yml @@ -0,0 +1,23 @@ +service: sls-py-req-test + +provider: + name: aws + runtime: python3.9 + +plugins: + - serverless-python-requirements +custom: + pythonRequirements: + dockerizePip: false + pythonBin: ${opt:pythonBin, self:provider.runtime} + +package: + individually: false + exclude: + - '**/*' + include: + - handler.py + +functions: + hello: + handler: handler.hello diff --git a/tests/pyproject_packages_optional/handler.py b/tests/pyproject_packages_optional/handler.py new file mode 100644 index 00000000..5e2e67ff --- /dev/null +++ b/tests/pyproject_packages_optional/handler.py @@ -0,0 +1,5 @@ +import requests + + +def hello(event, context): + return requests.get('https://httpbin.org/get').json() diff --git a/tests/pyproject_packages_optional/package.json b/tests/pyproject_packages_optional/package.json new file mode 100644 index 00000000..f771e78e --- /dev/null +++ b/tests/pyproject_packages_optional/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:.." + } +} diff --git a/tests/pyproject_packages_optional/pyproject.toml b/tests/pyproject_packages_optional/pyproject.toml new file mode 100644 index 00000000..232f0d07 --- /dev/null +++ b/tests/pyproject_packages_optional/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "example" +version = "1.0.0" +authors = [ + { name="Jackson Borneman", email="11304426+jax-b@users.noreply.github.com" }, +] +description = "example" +requires-python = ">=3.10" + +dependencies = [ + "flask", + "hug", + "boto3" +] + +[project.optional-dependencies] +test = [ + "pytest", +] diff --git a/tests/pyproject_packages_optional/serverless.yml b/tests/pyproject_packages_optional/serverless.yml new file mode 100644 index 00000000..a611e49d --- /dev/null +++ b/tests/pyproject_packages_optional/serverless.yml @@ -0,0 +1,25 @@ +service: sls-py-req-test + +provider: + name: aws + runtime: python3.9 + +plugins: + - serverless-python-requirements +custom: + pythonRequirements: + dockerizePip: false + pythonBin: ${opt:pythonBin, self:provider.runtime} + OptionalDeps: + - "test" + +package: + individually: false + exclude: + - '**/*' + include: + - handler.py + +functions: + hello: + handler: handler.hello From 80a80291070e55136cb982934e3bde6baeca00a1 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Fri, 9 Feb 2024 23:19:42 -0500 Subject: [PATCH 02/20] Bumped node version as it was giving sls errors --- tests/base/serverless.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/base/serverless.yml b/tests/base/serverless.yml index a82187ff..87423210 100644 --- a/tests/base/serverless.yml +++ b/tests/base/serverless.yml @@ -47,7 +47,7 @@ functions: handler: handler.hello hello3: handler: handler.hello - runtime: nodejs8.10 + runtime: nodejs14.x hello4: handler: fn2_handler.hello module: fn2 From 6f7dceba38995ca39da4d0aade1708a22255f512 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 10 Feb 2024 10:37:11 -0500 Subject: [PATCH 03/20] Bumped Python to 310 to follow ubuntu lts --- .gitignore | 2 + CONTRIBUTING.md | 10 +- README.md | 4 +- example/serverless.yml | 2 +- example_native_deps/serverless.yml | 2 +- index.js | 2 +- test.js | 112 +++++++++--------- tests/base/serverless.yml | 3 +- tests/individually/serverless.yml | 2 +- tests/non_build_pyproject/serverless.yml | 2 +- tests/non_poetry_pyproject/serverless.yml | 2 +- tests/pipenv/serverless.yml | 3 +- tests/poetry/pyproject.toml | 2 +- tests/poetry/serverless.yml | 2 +- tests/poetry_individually/serverless.yml | 2 +- tests/poetry_packages/serverless.yml | 2 +- tests/pyproject_packages/serverless.yml | 2 +- .../serverless.yml | 6 +- 18 files changed, 83 insertions(+), 79 deletions(-) diff --git a/.gitignore b/.gitignore index 64bdbd6a..194c199c 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ admin.env #PYTHON STUFF *.py[co] __pycache__ +.venv #NODE STUFF package-lock.json @@ -66,6 +67,7 @@ var/ *.egg-info/ .installed.cfg *.egg +tests/base/.requirements-cache # Serverless directories .serverless diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4616858b..cfa8e4f5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,12 +13,14 @@ Welcome, and thanks in advance for your help! ## Setup Pre-Reqs: -* Python 3.7 -* [poetry](https://python-poetry.org/docs/) (if you use multiple versions of Python be sure to install it with python 3.7) -* Perl (used in the tests) -* Node v14 or v16 + +- Python 3.7 +- [poetry](https://python-poetry.org/docs/) (if you use multiple versions of Python be sure to install it with python 3.7) +- Perl (used in the tests) +- Node v14 or v16 Then, to begin development: + 1. fork the repository 2. `npm install -g serverless@` (check the peer dependencies in the root `package.json` file for the version) 3. run `npm install` in its root folder diff --git a/README.md b/README.md index d9127adb..507c6200 100644 --- a/README.md +++ b/README.md @@ -515,7 +515,7 @@ For usage of `dockerizePip` on Windows do Step 1 only if running serverless on w Some Python packages require extra OS dependencies to build successfully. To deal with this, replace the default image with a `Dockerfile` like: ```dockerfile -FROM public.ecr.aws/sam/build-python3.9 +FROM public.ecr.aws/sam/build-python3.10 # Install your dependencies RUN yum -y install mysql-devel @@ -602,7 +602,7 @@ To handle native dependencies, it's recommended to use the Docker builder with t custom: pythonRequirements: # Can use any Python version supported by Scaleway - dockerImage: rg.fr-par.scw.cloud/scwfunctionsruntimes-public/python-dep:3.11 + dockerImage: rg.fr-par.scw.cloud/scwfunctionsruntimes-public/python-dep:3.10 ``` ## Contributors diff --git a/example/serverless.yml b/example/serverless.yml index e5c4c924..3e56a2fe 100644 --- a/example/serverless.yml +++ b/example/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements diff --git a/example_native_deps/serverless.yml b/example_native_deps/serverless.yml index cfbd4913..bb20b632 100644 --- a/example_native_deps/serverless.yml +++ b/example_native_deps/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements diff --git a/index.js b/index.js index c3ce1993..7abb7e29 100644 --- a/index.js +++ b/index.js @@ -62,7 +62,7 @@ class ServerlessPythonRequirements { poetryWithoutGroups: [], poetryOnlyGroups: [], usePyProject: true, - OptionalDeps: [] + OptionalDeps: [], }, (this.serverless.service.custom && this.serverless.service.custom.pythonRequirements) || diff --git a/test.js b/test.js index fad21273..7d91f362 100644 --- a/test.js +++ b/test.js @@ -234,7 +234,7 @@ test('default pythonBin can package flask with default options', async (t) => { t.end(); }); -test('py3.9 packages have the same hash', async (t) => { +test('py3.10 packages have the same hash', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -249,7 +249,7 @@ test('py3.9 packages have the same hash', async (t) => { t.end(); }); -test('py3.9 can package flask with default options', async (t) => { +test('py3.10 can package flask with default options', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -261,7 +261,7 @@ test('py3.9 can package flask with default options', async (t) => { }); test( - 'py3.9 can package flask with hashes', + 'py3.10 can package flask with hashes', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -279,7 +279,7 @@ test( { skip: brokenOn('win32') } ); -test('py3.9 can package flask with nested', async (t) => { +test('py3.10 can package flask with nested', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -295,7 +295,7 @@ test('py3.9 can package flask with nested', async (t) => { t.end(); }); -test('py3.9 can package flask with zip option', async (t) => { +test('py3.10 can package flask with zip option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -313,7 +313,7 @@ test('py3.9 can package flask with zip option', async (t) => { t.end(); }); -test('py3.9 can package flask with slim option', async (t) => { +test('py3.10 can package flask with slim option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -332,7 +332,7 @@ test('py3.9 can package flask with slim option', async (t) => { t.end(); }); -test('py3.9 can package flask with slim & slimPatterns options', async (t) => { +test('py3.10 can package flask with slim & slimPatterns options', async (t) => { process.chdir('tests/base'); copySync('_slimPatterns.yml', 'slimPatterns.yml'); const path = npm(['pack', '../..']); @@ -353,7 +353,7 @@ test('py3.9 can package flask with slim & slimPatterns options', async (t) => { t.end(); }); -test("py3.9 doesn't package bottle with noDeploy option", async (t) => { +test("py3.10 doesn't package bottle with noDeploy option", async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -371,7 +371,7 @@ test("py3.9 doesn't package bottle with noDeploy option", async (t) => { t.end(); }); -test('py3.9 can package boto3 with editable', async (t) => { +test('py3.10 can package boto3 with editable', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -391,7 +391,7 @@ test('py3.9 can package boto3 with editable', async (t) => { }); test( - 'py3.9 can package flask with dockerizePip option', + 'py3.10 can package flask with dockerizePip option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -406,7 +406,7 @@ test( ); test( - 'py3.9 can package flask with slim & dockerizePip option', + 'py3.10 can package flask with slim & dockerizePip option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -430,7 +430,7 @@ test( ); test( - 'py3.9 can package flask with slim & dockerizePip & slimPatterns options', + 'py3.10 can package flask with slim & dockerizePip & slimPatterns options', async (t) => { process.chdir('tests/base'); copySync('_slimPatterns.yml', 'slimPatterns.yml'); @@ -455,7 +455,7 @@ test( ); test( - 'py3.9 can package flask with zip & dockerizePip option', + 'py3.10 can package flask with zip & dockerizePip option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -487,7 +487,7 @@ test( ); test( - 'py3.9 can package flask with zip & slim & dockerizePip option', + 'py3.10 can package flask with zip & slim & dockerizePip option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -520,7 +520,7 @@ test( { skip: !canUseDocker() || brokenOn('win32') } ); -test('pipenv py3.9 can package flask with default options', async (t) => { +test('pipenv py3.10 can package flask with default options', async (t) => { process.chdir('tests/pipenv'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -535,7 +535,7 @@ test('pipenv py3.9 can package flask with default options', async (t) => { t.end(); }); -test('pipenv py3.9 can package flask with slim option', async (t) => { +test('pipenv py3.10 can package flask with slim option', async (t) => { process.chdir('tests/pipenv'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -554,7 +554,7 @@ test('pipenv py3.9 can package flask with slim option', async (t) => { t.end(); }); -test('pipenv py3.9 can package flask with slim & slimPatterns options', async (t) => { +test('pipenv py3.10 can package flask with slim & slimPatterns options', async (t) => { process.chdir('tests/pipenv'); copySync('_slimPatterns.yml', 'slimPatterns.yml'); @@ -576,7 +576,7 @@ test('pipenv py3.9 can package flask with slim & slimPatterns options', async (t t.end(); }); -test('pipenv py3.9 can package flask with zip option', async (t) => { +test('pipenv py3.10 can package flask with zip option', async (t) => { process.chdir('tests/pipenv'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -594,7 +594,7 @@ test('pipenv py3.9 can package flask with zip option', async (t) => { t.end(); }); -test("pipenv py3.9 doesn't package bottle with noDeploy option", async (t) => { +test("pipenv py3.10 doesn't package bottle with noDeploy option", async (t) => { process.chdir('tests/pipenv'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -633,7 +633,7 @@ test('non poetry pyproject.toml without requirements.txt packages handler only', t.end(); }); -test('poetry py3.9 can package flask with default options', async (t) => { +test('poetry py3.10 can package flask with default options', async (t) => { process.chdir('tests/poetry'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -645,7 +645,7 @@ test('poetry py3.9 can package flask with default options', async (t) => { t.end(); }); -test('poetry py3.9 can package flask with slim option', async (t) => { +test('poetry py3.10 can package flask with slim option', async (t) => { process.chdir('tests/poetry'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -664,7 +664,7 @@ test('poetry py3.9 can package flask with slim option', async (t) => { t.end(); }); -test('poetry py3.9 can package flask with slim & slimPatterns options', async (t) => { +test('poetry py3.10 can package flask with slim & slimPatterns options', async (t) => { process.chdir('tests/poetry'); copySync('_slimPatterns.yml', 'slimPatterns.yml'); @@ -686,7 +686,7 @@ test('poetry py3.9 can package flask with slim & slimPatterns options', async (t t.end(); }); -test('poetry py3.9 can package flask with zip option', async (t) => { +test('poetry py3.10 can package flask with zip option', async (t) => { process.chdir('tests/poetry'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -704,7 +704,7 @@ test('poetry py3.9 can package flask with zip option', async (t) => { t.end(); }); -test("poetry py3.9 doesn't package bottle with noDeploy option", async (t) => { +test("poetry py3.10 doesn't package bottle with noDeploy option", async (t) => { process.chdir('tests/poetry'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -722,7 +722,7 @@ test("poetry py3.9 doesn't package bottle with noDeploy option", async (t) => { t.end(); }); -test('py3.9 can package flask with zip option and no explicit include', async (t) => { +test('py3.10 can package flask with zip option and no explicit include', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -742,7 +742,7 @@ test('py3.9 can package flask with zip option and no explicit include', async (t t.end(); }); -test('py3.9 can package lambda-decorators using vendor option', async (t) => { +test('py3.10 can package lambda-decorators using vendor option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -805,7 +805,7 @@ test( { skip: process.platform === 'win32' } ); -test('py3.9 can package flask in a project with a space in it', async (t) => { +test('py3.10 can package flask in a project with a space in it', async (t) => { copySync('tests/base', 'tests/base with a space'); process.chdir('tests/base with a space'); const path = npm(['pack', '../..']); @@ -818,7 +818,7 @@ test('py3.9 can package flask in a project with a space in it', async (t) => { }); test( - 'py3.9 can package flask in a project with a space in it with docker', + 'py3.10 can package flask in a project with a space in it with docker', async (t) => { copySync('tests/base', 'tests/base with a space'); process.chdir('tests/base with a space'); @@ -833,7 +833,7 @@ test( { skip: !canUseDocker() || brokenOn('win32') } ); -test('py3.9 supports custom file name with fileName option', async (t) => { +test('py3.10 supports custom file name with fileName option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); writeFileSync('puck', 'requests'); @@ -849,7 +849,7 @@ test('py3.9 supports custom file name with fileName option', async (t) => { t.end(); }); -test("py3.9 doesn't package bottle with zip option", async (t) => { +test("py3.10 doesn't package bottle with zip option", async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -885,7 +885,7 @@ test("py3.9 doesn't package bottle with zip option", async (t) => { t.end(); }); -test('py3.9 can package flask with slim, slimPatterns & slimPatternsAppendDefaults=false options', async (t) => { +test('py3.10 can package flask with slim, slimPatterns & slimPatternsAppendDefaults=false options', async (t) => { process.chdir('tests/base'); copySync('_slimPatterns.yml', 'slimPatterns.yml'); const path = npm(['pack', '../..']); @@ -908,7 +908,7 @@ test('py3.9 can package flask with slim, slimPatterns & slimPatternsAppendDefaul }); test( - 'py3.9 can package flask with slim & dockerizePip & slimPatterns & slimPatternsAppendDefaults=false options', + 'py3.10 can package flask with slim & dockerizePip & slimPatterns & slimPatternsAppendDefaults=false options', async (t) => { process.chdir('tests/base'); copySync('_slimPatterns.yml', 'slimPatterns.yml'); @@ -937,7 +937,7 @@ test( { skip: !canUseDocker() || brokenOn('win32') } ); -test('pipenv py3.9 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { +test('pipenv py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { process.chdir('tests/pipenv'); copySync('_slimPatterns.yml', 'slimPatterns.yml'); const path = npm(['pack', '../..']); @@ -960,7 +960,7 @@ test('pipenv py3.9 can package flask with slim & slimPatterns & slimPatternsAppe t.end(); }); -test('poetry py3.9 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { +test('poetry py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { process.chdir('tests/poetry'); copySync('_slimPatterns.yml', 'slimPatterns.yml'); const path = npm(['pack', '../..']); @@ -983,7 +983,7 @@ test('poetry py3.9 can package flask with slim & slimPatterns & slimPatternsAppe t.end(); }); -test('poetry py3.9 can package flask with package individually option', async (t) => { +test('poetry py3.10 can package flask with package individually option', async (t) => { process.chdir('tests/poetry_individually'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -998,7 +998,7 @@ test('poetry py3.9 can package flask with package individually option', async (t t.end(); }); -test('py3.9 can package flask with package individually option', async (t) => { +test('py3.10 can package flask with package individually option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -1080,7 +1080,7 @@ test('py3.9 can package flask with package individually option', async (t) => { t.end(); }); -test('py3.9 can package flask with package individually & slim option', async (t) => { +test('py3.10 can package flask with package individually & slim option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -1162,7 +1162,7 @@ test('py3.9 can package flask with package individually & slim option', async (t t.end(); }); -test('py3.9 can package only requirements of module', async (t) => { +test('py3.10 can package only requirements of module', async (t) => { process.chdir('tests/individually'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -1218,7 +1218,7 @@ test('py3.9 can package only requirements of module', async (t) => { t.end(); }); -test('py3.9 can package lambda-decorators using vendor and invidiually option', async (t) => { +test('py3.10 can package lambda-decorators using vendor and invidiually option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -1374,7 +1374,7 @@ test( ); test( - 'py3.9 uses download cache by default option', + 'py3.10 uses download cache by default option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1391,7 +1391,7 @@ test( ); test( - 'py3.9 uses download cache by default', + 'py3.10 uses download cache by default', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1407,7 +1407,7 @@ test( ); test( - 'py3.9 uses download cache with dockerizePip option', + 'py3.10 uses download cache with dockerizePip option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1425,7 +1425,7 @@ test( ); test( - 'py3.9 uses download cache with dockerizePip by default option', + 'py3.10 uses download cache with dockerizePip by default option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1444,7 +1444,7 @@ test( ); test( - 'py3.9 uses static and download cache', + 'py3.10 uses static and download cache', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1469,7 +1469,7 @@ test( ); test( - 'py3.9 uses static and download cache with dockerizePip option', + 'py3.10 uses static and download cache with dockerizePip option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1493,7 +1493,7 @@ test( { skip: !canUseDocker() || brokenOn('win32') } ); -test('py3.9 uses static cache', async (t) => { +test('py3.10 uses static cache', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -1514,7 +1514,7 @@ test('py3.9 uses static cache', async (t) => { '.completed_requirements exists in static-cache' ); - // py3.9 checking that static cache actually pulls from cache (by poisoning it) + // py3.10 checking that static cache actually pulls from cache (by poisoning it) writeFileSync( `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, 'injected new file into static cache folder' @@ -1529,7 +1529,7 @@ test('py3.9 uses static cache', async (t) => { t.end(); }); -test('py3.9 uses static cache with cacheLocation option', async (t) => { +test('py3.10 uses static cache with cacheLocation option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -1553,7 +1553,7 @@ test('py3.9 uses static cache with cacheLocation option', async (t) => { }); test( - 'py3.9 uses static cache with dockerizePip & slim option', + 'py3.10 uses static cache with dockerizePip & slim option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1575,7 +1575,7 @@ test( '.completed_requirements exists in static-cache' ); - // py3.9 checking that static cache actually pulls from cache (by poisoning it) + // py3.10 checking that static cache actually pulls from cache (by poisoning it) writeFileSync( `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, 'injected new file into static cache folder' @@ -1598,7 +1598,7 @@ test( ); test( - 'py3.9 uses download cache with dockerizePip & slim option', + 'py3.10 uses download cache with dockerizePip & slim option', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1623,7 +1623,7 @@ test( { skip: !canUseDocker() || brokenOn('win32') } ); -test('py3.9 can ignore functions defined with `image`', async (t) => { +test('py3.10 can ignore functions defined with `image`', async (t) => { process.chdir('tests/base'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -1649,7 +1649,7 @@ test('py3.9 can ignore functions defined with `image`', async (t) => { t.end(); }); -test('poetry py3.9 fails packaging if poetry.lock is missing and flag requirePoetryLockFile is set to true', async (t) => { +test('poetry py3.10 fails packaging if poetry.lock is missing and flag requirePoetryLockFile is set to true', async (t) => { copySync('tests/poetry', 'tests/base with a space'); process.chdir('tests/base with a space'); removeSync('poetry.lock'); @@ -1681,7 +1681,7 @@ test('works with provider.runtime not being python', async (t) => { t.end(); }); -test('poetry py3.9 packages additional optional packages', async (t) => { +test('poetry py3.10 packages additional optional packages', async (t) => { process.chdir('tests/poetry_packages'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -1697,7 +1697,7 @@ test('poetry py3.9 packages additional optional packages', async (t) => { t.end(); }); -test('poetry py3.9 skips additional optional packages specified in withoutGroups', async (t) => { +test('poetry py3.10 skips additional optional packages specified in withoutGroups', async (t) => { process.chdir('tests/poetry_packages'); const path = npm(['pack', '../..']); npm(['i', path]); @@ -1714,7 +1714,7 @@ test('poetry py3.9 skips additional optional packages specified in withoutGroups t.end(); }); -test('poetry py3.9 only installs optional packages specified in onlyGroups', async (t) => { +test('poetry py3.10 only installs optional packages specified in onlyGroups', async (t) => { process.chdir('tests/poetry_packages'); const path = npm(['pack', '../..']); npm(['i', path]); diff --git a/tests/base/serverless.yml b/tests/base/serverless.yml index 68ea2719..43da20ab 100644 --- a/tests/base/serverless.yml +++ b/tests/base/serverless.yml @@ -2,8 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: ${env:runtime, 'python3.9'} - configValidationMode: warn + runtime: ${env:runtime, 'python3.10'} plugins: - serverless-python-requirements diff --git a/tests/individually/serverless.yml b/tests/individually/serverless.yml index 6409532b..cfaf0013 100644 --- a/tests/individually/serverless.yml +++ b/tests/individually/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test-indiv provider: name: aws - runtime: python3.9 + runtime: python3.10 package: individually: true diff --git a/tests/non_build_pyproject/serverless.yml b/tests/non_build_pyproject/serverless.yml index d1bbaee6..df5a9ad5 100644 --- a/tests/non_build_pyproject/serverless.yml +++ b/tests/non_build_pyproject/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements diff --git a/tests/non_poetry_pyproject/serverless.yml b/tests/non_poetry_pyproject/serverless.yml index 7338b10b..d71bfbe4 100644 --- a/tests/non_poetry_pyproject/serverless.yml +++ b/tests/non_poetry_pyproject/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements diff --git a/tests/pipenv/serverless.yml b/tests/pipenv/serverless.yml index 2b471526..0e0b1abc 100644 --- a/tests/pipenv/serverless.yml +++ b/tests/pipenv/serverless.yml @@ -2,12 +2,13 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements custom: pythonRequirements: + noDeploy: [bottle] zip: ${env:zip, self:custom.defaults.zip} slim: ${env:slim, self:custom.defaults.slim} slimPatterns: ${file(./slimPatterns.yml):slimPatterns, self:custom.defaults.slimPatterns} diff --git a/tests/poetry/pyproject.toml b/tests/poetry/pyproject.toml index 896b48e7..e76f295d 100644 --- a/tests/poetry/pyproject.toml +++ b/tests/poetry/pyproject.toml @@ -7,7 +7,7 @@ authors = ["Your Name "] [tool.poetry.dependencies] python = "^3.7" Flask = "2.0" -bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.16"} +bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} boto3 = "1.29.6" [tool.poetry.dev-dependencies] diff --git a/tests/poetry/serverless.yml b/tests/poetry/serverless.yml index d10c4997..b77ebdb7 100644 --- a/tests/poetry/serverless.yml +++ b/tests/poetry/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements diff --git a/tests/poetry_individually/serverless.yml b/tests/poetry_individually/serverless.yml index 86dbb547..43214462 100644 --- a/tests/poetry_individually/serverless.yml +++ b/tests/poetry_individually/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements diff --git a/tests/poetry_packages/serverless.yml b/tests/poetry_packages/serverless.yml index c6972ede..11b0e757 100644 --- a/tests/poetry_packages/serverless.yml +++ b/tests/poetry_packages/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements diff --git a/tests/pyproject_packages/serverless.yml b/tests/pyproject_packages/serverless.yml index e5c4c924..3e56a2fe 100644 --- a/tests/pyproject_packages/serverless.yml +++ b/tests/pyproject_packages/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements diff --git a/tests/pyproject_packages_optional/serverless.yml b/tests/pyproject_packages_optional/serverless.yml index a611e49d..de84719f 100644 --- a/tests/pyproject_packages_optional/serverless.yml +++ b/tests/pyproject_packages_optional/serverless.yml @@ -2,7 +2,7 @@ service: sls-py-req-test provider: name: aws - runtime: python3.9 + runtime: python3.10 plugins: - serverless-python-requirements @@ -10,8 +10,8 @@ custom: pythonRequirements: dockerizePip: false pythonBin: ${opt:pythonBin, self:provider.runtime} - OptionalDeps: - - "test" + OptionalDeps: + - 'test' package: individually: false From fff51fdbcdec75e12905c59402cbd6e19963e49a Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 10 Feb 2024 14:59:52 -0500 Subject: [PATCH 04/20] finished up migration to py310 and fixed failing test due to uninstalled module --- .github/workflows/validate.yml | 4 ++-- .python-version | 2 +- CONTRIBUTING.md | 4 ++-- README.md | 8 ++++---- test.js | 5 +++-- tests/individually_mixed_runtime/serverless.yml | 2 +- tests/pipenv/serverless.yml | 1 - tests/poetry/pyproject.toml | 2 +- tests/poetry_individually/module1/pyproject.toml | 4 ++-- tests/poetry_packages/pyproject.toml | 4 ++-- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 61935c3e..f42bad83 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -43,10 +43,10 @@ jobs: npm-v18-${{ runner.os }}-${{ github.ref }}- npm-v18-${{ runner.os }}-refs/heads/master- - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: 3.10 - name: Install Node.js and npm uses: actions/setup-node@v1 diff --git a/.python-version b/.python-version index bd28b9c5..c8cfe395 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.9 +3.10 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 900a425b..c6156e0c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,8 +14,8 @@ Welcome, and thanks in advance for your help! Pre-Reqs: -- Python 3.9 -- [poetry](https://python-poetry.org/docs/) (if you use multiple versions of Python be sure to install it with python 3.9) +- Python 3.10 +- [poetry](https://python-poetry.org/docs/) (if you use multiple versions of Python be sure to install it with python 3.10) - Perl (used in the tests) - Node v14 or v16 diff --git a/README.md b/README.md index 507c6200..86f0ea70 100644 --- a/README.md +++ b/README.md @@ -160,21 +160,21 @@ Instead of: ```toml [tool.poetry.dependencies] -bottle = {git = "git@github.com/bottlepy/bottle.git", tag = "0.12.16"} +bottle = {git = "git@github.com/bottlepy/bottle.git", tag = "0.12.25"} ``` Use: ```toml [tool.poetry.dependencies] -bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.16"} +bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} ``` Or, if you have an SSH key configured: ```toml [tool.poetry.dependencies] -bottle = {git = "ssh://git@github.com/bottlepy/bottle.git", tag = "0.12.16"} +bottle = {git = "ssh://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} ``` ## Dealing with Lambda's size limitations @@ -285,7 +285,7 @@ custom: name: ${self:provider.stage}-layerName description: Python requirements lambda layer compatibleRuntimes: - - python3.7 + - python3.10 licenseInfo: GPLv3 allowedAccounts: - '*' diff --git a/test.js b/test.js index 59a5514e..044b8aed 100644 --- a/test.js +++ b/test.js @@ -1374,7 +1374,7 @@ test( ); test( - 'py3.9 can package flask running in docker with module runtime & architecture of function', + 'py3.10 can package flask running in docker with module runtime & architecture of function', async (t) => { process.chdir('tests/individually_mixed_runtime'); const path = npm(['pack', '../..']); @@ -1896,10 +1896,11 @@ test('poetry py3.10 only installs optional packages specified in onlyGroups', as t.end(); }); -test('py3.7 injects dependencies into `package` folder when using scaleway provider', async (t) => { +test('py3.10 injects dependencies into `package` folder when using scaleway provider', async (t) => { process.chdir('tests/scaleway_provider'); const path = npm(['pack', '../..']); npm(['i', path]); + sls(['plugin', 'install', '-n', 'serverless-scaleway-functions'], { env: {} }); sls(['package'], { env: {} }); const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); t.true( diff --git a/tests/individually_mixed_runtime/serverless.yml b/tests/individually_mixed_runtime/serverless.yml index 7c602239..c2f4d35f 100644 --- a/tests/individually_mixed_runtime/serverless.yml +++ b/tests/individually_mixed_runtime/serverless.yml @@ -28,7 +28,7 @@ functions: hello2: handler: handler2.hello module: module2 - runtime: python3.9 + runtime: python3.10 architecture: x86_64 package: patterns: diff --git a/tests/pipenv/serverless.yml b/tests/pipenv/serverless.yml index 0e0b1abc..f92ea998 100644 --- a/tests/pipenv/serverless.yml +++ b/tests/pipenv/serverless.yml @@ -8,7 +8,6 @@ plugins: - serverless-python-requirements custom: pythonRequirements: - noDeploy: [bottle] zip: ${env:zip, self:custom.defaults.zip} slim: ${env:slim, self:custom.defaults.slim} slimPatterns: ${file(./slimPatterns.yml):slimPatterns, self:custom.defaults.slimPatterns} diff --git a/tests/poetry/pyproject.toml b/tests/poetry/pyproject.toml index e76f295d..24e2c1b8 100644 --- a/tests/poetry/pyproject.toml +++ b/tests/poetry/pyproject.toml @@ -5,7 +5,7 @@ description = "" authors = ["Your Name "] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.10" Flask = "2.0" bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} boto3 = "1.29.6" diff --git a/tests/poetry_individually/module1/pyproject.toml b/tests/poetry_individually/module1/pyproject.toml index 896b48e7..24e2c1b8 100644 --- a/tests/poetry_individually/module1/pyproject.toml +++ b/tests/poetry_individually/module1/pyproject.toml @@ -5,9 +5,9 @@ description = "" authors = ["Your Name "] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.10" Flask = "2.0" -bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.16"} +bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} boto3 = "1.29.6" [tool.poetry.dev-dependencies] diff --git a/tests/poetry_packages/pyproject.toml b/tests/poetry_packages/pyproject.toml index 0f9fc705..ce56c51a 100644 --- a/tests/poetry_packages/pyproject.toml +++ b/tests/poetry_packages/pyproject.toml @@ -5,11 +5,11 @@ description = "" authors = ["Your Name "] [tool.poetry.dependencies] -python = "^3.7" +python = "^3.10" Flask = "2.0" [tool.poetry.group.custom1.dependencies] -bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.16"} +bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} [tool.poetry.group.custom2.dependencies] boto3 = "1.29.6" From 4ab6122e269e3de59bab3664913d03fd2c8fd891 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 10 Feb 2024 23:42:54 -0500 Subject: [PATCH 05/20] Updated python requrements --- tests/base/requirements-w-hashes.txt | 194 +++++++++++------- tests/base/requirements-w-nested.txt | 2 +- tests/base/requirements.txt | 2 +- tests/individually/module2/requirements.txt | 2 +- .../module2/requirements.txt | 2 +- tests/non_build_pyproject/requirements.txt | 2 +- tests/pipenv/Pipfile | 2 +- tests/poetry/pyproject.toml | 4 +- .../module1/pyproject.toml | 4 +- tests/poetry_packages/pyproject.toml | 2 +- tests/scaleway_provider/requirements.txt | 2 +- 11 files changed, 127 insertions(+), 91 deletions(-) diff --git a/tests/base/requirements-w-hashes.txt b/tests/base/requirements-w-hashes.txt index 428e8528..fef6cea2 100644 --- a/tests/base/requirements-w-hashes.txt +++ b/tests/base/requirements-w-hashes.txt @@ -1,90 +1,126 @@ # -# This file is autogenerated by pip-compile -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # -# pip-compile --generate-hashes --output-file requirements-w-hashes.txt requirements.txt +# pip-compile --generate-hashes --output-file=requirements-w-hashes.txt requirements.txt # -boto3==1.9.50 \ - --hash=sha256:177e9dd53db5028bb43050da20cc7956287889fc172e5e6275a634e42a10beeb \ - --hash=sha256:8c63e616b91907037ab19236afbcf0057efb31411faf38b46f4590e634dc17ea -botocore==1.12.50 \ - --hash=sha256:07fae5a2b8cfb5a92c1dbee3f2feb4da7c471bcead7e18ce735babe5f39e270f \ - --hash=sha256:eeaa190f50ee05a56225ee78c64cb8bf0c3bf090ec605ca6c2f325aa3826a347 \ - # via boto3, s3transfer -bottle==0.12.19 \ - --hash=sha256:f6b8a34fe9aa406f9813c02990db72ca69ce6a158b5b156d2c41f345016a723d \ - --hash=sha256:a9d73ffcbc6a1345ca2d7949638db46349f5b2b77dac65d6494d45c23628da2c -click==7.0 \ - --hash=sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13 \ - --hash=sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7 \ +boto3==1.34.39 \ + --hash=sha256:35bcbecf1b5d3620c93f0062d2994177f8bda25a9d2cba144d6462793c16065b \ + --hash=sha256:476896e70d36c9134d4125834280c597c17b54bff4902baf2e5fcde74f8acec8 + # via -r requirements.txt +botocore==1.34.39 \ + --hash=sha256:9f00bd5e4698bcdd37ce6e224a896baf58d209678ed92834944b767de9061cc5 \ + --hash=sha256:e175360445424b83b0e28ae20d301b99cf44ff2c9d5ab1d8670899bec05a9753 + # via + # boto3 + # s3transfer +bottle==0.12.25 \ + --hash=sha256:d6f15f9d422670b7c073d63bd8d287b135388da187a0f3e3c19293626ce034ea \ + --hash=sha256:e1a9c94970ae6d710b3fb4526294dfeb86f2cb4a81eff3a4b98dc40fb0e5e021 + # via -r requirements.txt +click==8.1.7 \ + --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ + --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de # via flask -docutils==0.14 \ - --hash=sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6 \ - --hash=sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274 \ - --hash=sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6 \ - # via botocore -flask==1.0.2 \ - --hash=sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48 \ - --hash=sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05 -itsdangerous==1.1.0 \ - --hash=sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19 \ - --hash=sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749 \ +flask==2.0.0 \ + --hash=sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e \ + --hash=sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d + # via -r requirements.txt +itsdangerous==2.1.2 \ + --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ + --hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a # via flask -jinja2==2.11.3 \ - --hash=sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419 \ - --hash=sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6 \ +jinja2==3.1.3 \ + --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \ + --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90 # via flask -jmespath==0.9.3 \ - --hash=sha256:6a81d4c9aa62caf061cb517b4d9ad1dd300374cd4706997aff9cd6aedd61fc64 \ - --hash=sha256:f11b4461f425740a1d908e9a3f7365c3d2e569f6ca68a2ff8bc5bcd9676edd63 \ - # via boto3, botocore -markupsafe==1.1.0 \ - --hash=sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432 \ - --hash=sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b \ - --hash=sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9 \ - --hash=sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af \ - --hash=sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834 \ - --hash=sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd \ - --hash=sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d \ - --hash=sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7 \ - --hash=sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b \ - --hash=sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3 \ - --hash=sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c \ - --hash=sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2 \ - --hash=sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7 \ - --hash=sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36 \ - --hash=sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1 \ - --hash=sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e \ - --hash=sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1 \ - --hash=sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c \ - --hash=sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856 \ - --hash=sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550 \ - --hash=sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492 \ - --hash=sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672 \ - --hash=sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401 \ - --hash=sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6 \ - --hash=sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6 \ - --hash=sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c \ - --hash=sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd \ - --hash=sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1 \ - # via jinja2 -python-dateutil==2.7.5 \ - --hash=sha256:063df5763652e21de43de7d9e00ccf239f953a832941e37be541614732cdfc93 \ - --hash=sha256:88f9287c0174266bb0d8cedd395cfba9c58e87e5ad86b2ce58859bc11be3cf02 \ +jmespath==1.0.1 \ + --hash=sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980 \ + --hash=sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe + # via + # boto3 + # botocore +markupsafe==2.1.5 \ + --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \ + --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \ + --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ + --hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \ + --hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \ + --hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \ + --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ + --hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \ + --hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \ + --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ + --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ + --hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \ + --hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \ + --hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \ + --hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \ + --hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \ + --hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \ + --hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \ + --hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \ + --hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \ + --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ + --hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \ + --hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \ + --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ + --hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \ + --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ + --hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \ + --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ + --hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \ + --hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \ + --hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \ + --hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \ + --hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \ + --hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \ + --hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \ + --hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \ + --hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \ + --hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \ + --hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \ + --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ + --hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \ + --hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \ + --hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \ + --hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \ + --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ + --hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \ + --hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \ + --hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \ + --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \ + --hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \ + --hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \ + --hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \ + --hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \ + --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \ + --hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \ + --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \ + --hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \ + --hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \ + --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \ + --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68 + # via + # jinja2 + # werkzeug +python-dateutil==2.8.2 \ + --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ + --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 # via botocore -s3transfer==0.1.13 \ - --hash=sha256:90dc18e028989c609146e241ea153250be451e05ecc0c2832565231dacdf59c1 \ - --hash=sha256:c7a9ec356982d5e9ab2d4b46391a7d6a950e2b04c472419f5fdec70cc0ada72f \ +s3transfer==0.10.0 \ + --hash=sha256:3cdb40f5cfa6966e812209d0994f2a4709b561c88e90cf00c2696d2df4e56b2e \ + --hash=sha256:d0c8bbf672d5eebbe4e57945e23b972d963f07d82f661cabf678a5c88831595b # via boto3 -six==1.11.0 \ - --hash=sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9 \ - --hash=sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb \ +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via python-dateutil -urllib3==1.24.2 \ - --hash=sha256:4c291ca23bbb55c76518905869ef34bdd5f0e46af7afe6861e8375643ffee1a0 \ - --hash=sha256:9a247273df709c4fedb38c711e44292304f73f39ab01beda9f6b9fc375669ac3 \ +urllib3==2.0.7 \ + --hash=sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84 \ + --hash=sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e # via botocore -werkzeug==0.15.3 \ - --hash=sha256:97660b282aa7e29f94f3fe378e5c7162d7ab9d601a8dbb1cbb2ffc8f0e54607d \ - --hash=sha256:cfd1281b1748288e59762c0e174d64d8bcb2b70e7c57bc4a1203c8825af24ac3 \ +werkzeug==3.0.1 \ + --hash=sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc \ + --hash=sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10 # via flask diff --git a/tests/base/requirements-w-nested.txt b/tests/base/requirements-w-nested.txt index b09aa52a..119d17e4 100644 --- a/tests/base/requirements-w-nested.txt +++ b/tests/base/requirements-w-nested.txt @@ -1,3 +1,3 @@ -flask==2.0.3 +flask==2.0.0 bottle -r requirements-common.txt diff --git a/tests/base/requirements.txt b/tests/base/requirements.txt index 23bfb7a6..b7c5a2a7 100644 --- a/tests/base/requirements.txt +++ b/tests/base/requirements.txt @@ -1,3 +1,3 @@ -flask==0.12.5 +flask==2.0.0 bottle boto3 diff --git a/tests/individually/module2/requirements.txt b/tests/individually/module2/requirements.txt index c09d0264..37248b11 100644 --- a/tests/individually/module2/requirements.txt +++ b/tests/individually/module2/requirements.txt @@ -1 +1 @@ -flask==2.0.3 +flask==2.0.0 diff --git a/tests/individually_mixed_runtime/module2/requirements.txt b/tests/individually_mixed_runtime/module2/requirements.txt index c09d0264..37248b11 100644 --- a/tests/individually_mixed_runtime/module2/requirements.txt +++ b/tests/individually_mixed_runtime/module2/requirements.txt @@ -1 +1 @@ -flask==2.0.3 +flask==2.0.0 diff --git a/tests/non_build_pyproject/requirements.txt b/tests/non_build_pyproject/requirements.txt index 09764fc3..ae5ad052 100644 --- a/tests/non_build_pyproject/requirements.txt +++ b/tests/non_build_pyproject/requirements.txt @@ -1,2 +1,2 @@ -flask==2.0.3 +flask==2.0.0 boto3 diff --git a/tests/pipenv/Pipfile b/tests/pipenv/Pipfile index 30e51dda..c40878df 100644 --- a/tests/pipenv/Pipfile +++ b/tests/pipenv/Pipfile @@ -4,7 +4,7 @@ verify_ssl = true name = "pypi" [packages] -Flask = "==2.0.3" +Flask = "==2.0.0" bottle = "*" boto3 = "*" diff --git a/tests/poetry/pyproject.toml b/tests/poetry/pyproject.toml index 24e2c1b8..73cc59fd 100644 --- a/tests/poetry/pyproject.toml +++ b/tests/poetry/pyproject.toml @@ -6,9 +6,9 @@ authors = ["Your Name "] [tool.poetry.dependencies] python = "^3.10" -Flask = "2.0" +Flask = "2.0.0" bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} -boto3 = "1.29.6" +boto3 = "1.34.39" [tool.poetry.dev-dependencies] diff --git a/tests/poetry_individually/module1/pyproject.toml b/tests/poetry_individually/module1/pyproject.toml index 24e2c1b8..73cc59fd 100644 --- a/tests/poetry_individually/module1/pyproject.toml +++ b/tests/poetry_individually/module1/pyproject.toml @@ -6,9 +6,9 @@ authors = ["Your Name "] [tool.poetry.dependencies] python = "^3.10" -Flask = "2.0" +Flask = "2.0.0" bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} -boto3 = "1.29.6" +boto3 = "1.34.39" [tool.poetry.dev-dependencies] diff --git a/tests/poetry_packages/pyproject.toml b/tests/poetry_packages/pyproject.toml index ce56c51a..9c617dc4 100644 --- a/tests/poetry_packages/pyproject.toml +++ b/tests/poetry_packages/pyproject.toml @@ -12,7 +12,7 @@ Flask = "2.0" bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} [tool.poetry.group.custom2.dependencies] -boto3 = "1.29.6" +boto3 = "1.34.39" [build-system] requires = ["poetry-core"] diff --git a/tests/scaleway_provider/requirements.txt b/tests/scaleway_provider/requirements.txt index 23bfb7a6..b7c5a2a7 100644 --- a/tests/scaleway_provider/requirements.txt +++ b/tests/scaleway_provider/requirements.txt @@ -1,3 +1,3 @@ -flask==0.12.5 +flask==2.0.0 bottle boto3 From 32e61b80e93fc5ce17573f3cece0e6de50f16330 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:06:00 -0500 Subject: [PATCH 06/20] updateded bad hashes --- tests/base/requirements-w-hashes.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/base/requirements-w-hashes.txt b/tests/base/requirements-w-hashes.txt index fef6cea2..9f758345 100644 --- a/tests/base/requirements-w-hashes.txt +++ b/tests/base/requirements-w-hashes.txt @@ -23,8 +23,8 @@ click==8.1.7 \ --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de # via flask flask==2.0.0 \ - --hash=sha256:3232e0e9c850d781933cf0207523d1ece087eb8d87b23777ae38456e2fbe7c6e \ - --hash=sha256:822c03f4b799204250a7ee84b1eddc40665395333973dfb9deebfe425fefcb7d + --hash=sha256:168e8507792cb8a3aa06afbe5d4d431d3e07c6318bc3893ceecb81aff09f848d \ + --hash=sha256:1833a4b36ace08dfa1510d86f1bb6fc595d990ec1b838e03ac8dd80ac0705954 # via -r requirements.txt itsdangerous==2.1.2 \ --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ From b9f5f3492784f66757e22526af412a5b1d5a6e85 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:39:10 -0500 Subject: [PATCH 07/20] added tests fixed issues with extra self --- lib/pyproject.js | 4 +-- test.js | 27 ++++++++++++++++++- .../module1/handler1.ts | 2 +- tests/pyproject_packages/pyproject.toml | 1 - tests/pyproject_packages/serverless.yml | 16 +++++++---- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/lib/pyproject.js b/lib/pyproject.js index d9eb0016..621cdef6 100644 --- a/lib/pyproject.js +++ b/lib/pyproject.js @@ -45,9 +45,9 @@ function pyprojectTomlToRequirements(modulePath, pluginInstance) { } } - fse.ensureDirSync(path.join(this.servicePath, '.serverless')); + fse.ensureDirSync(path.join(servicePath, '.serverless')); fse.writeFileSync( - path.join(this.servicePath, '.serverless/requirements.txt'), + path.join(servicePath, '.serverless/requirements.txt'), dependencies.join('\n') ); } finally { diff --git a/test.js b/test.js index 044b8aed..76e96e57 100644 --- a/test.js +++ b/test.js @@ -1900,7 +1900,9 @@ test('py3.10 injects dependencies into `package` folder when using scaleway prov process.chdir('tests/scaleway_provider'); const path = npm(['pack', '../..']); npm(['i', path]); - sls(['plugin', 'install', '-n', 'serverless-scaleway-functions'], { env: {} }); + sls(['plugin', 'install', '-n', 'serverless-scaleway-functions'], { + env: {}, + }); sls(['package'], { env: {} }); const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); t.true( @@ -1913,3 +1915,26 @@ test('py3.10 injects dependencies into `package` folder when using scaleway prov ); t.end(); }); + +test('pyproject.toml py3.10 packages', async (t) => { + process.chdir('tests/pyproject_packages'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { pythonBin: getPythonBin(3) } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test('pyproject.toml py3.10 packages with optional', async (t) => { + process.chdir('tests/pyproject_packages_optional'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { pythonBin: getPythonBin(3) } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.true(zipfiles.includes(`pytest${sep}__init__.py`), 'pytest is packaged'); + t.end(); +}); diff --git a/tests/individually_mixed_runtime/module1/handler1.ts b/tests/individually_mixed_runtime/module1/handler1.ts index b8062f8b..a061a135 100644 --- a/tests/individually_mixed_runtime/module1/handler1.ts +++ b/tests/individually_mixed_runtime/module1/handler1.ts @@ -1,3 +1,3 @@ function hello() { - return "hello" + return 'hello'; } diff --git a/tests/pyproject_packages/pyproject.toml b/tests/pyproject_packages/pyproject.toml index 232f0d07..94f93601 100644 --- a/tests/pyproject_packages/pyproject.toml +++ b/tests/pyproject_packages/pyproject.toml @@ -9,7 +9,6 @@ requires-python = ">=3.10" dependencies = [ "flask", - "hug", "boto3" ] diff --git a/tests/pyproject_packages/serverless.yml b/tests/pyproject_packages/serverless.yml index 3e56a2fe..763197dc 100644 --- a/tests/pyproject_packages/serverless.yml +++ b/tests/pyproject_packages/serverless.yml @@ -8,15 +8,21 @@ plugins: - serverless-python-requirements custom: pythonRequirements: + zip: ${env:zip, self:custom.defaults.zip} + slim: ${env:slim, self:custom.defaults.slim} + slimPatterns: ${file(./slimPatterns.yml):slimPatterns, self:custom.defaults.slimPatterns} + slimPatternsAppendDefaults: ${env:slimPatternsAppendDefaults, self:custom.defaults.slimPatternsAppendDefaults} + dockerizePip: ${env:dockerizePip, self:custom.defaults.dockerizePip} + requirePoetryLockFile: ${env:requirePoetryLockFile, false} + defaults: + zip: false + slimPatterns: false + slimPatternsAppendDefaults: true + slim: false dockerizePip: false - pythonBin: ${opt:pythonBin, self:provider.runtime} package: individually: false - exclude: - - '**/*' - include: - - handler.py functions: hello: From 37c5ff6fb527c4c5335843577f0f8de527bbaf49 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:45:07 -0500 Subject: [PATCH 08/20] added fail param to ensure that opt is not included --- test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test.js b/test.js index 76e96e57..7163599d 100644 --- a/test.js +++ b/test.js @@ -1924,6 +1924,7 @@ test('pyproject.toml py3.10 packages', async (t) => { const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.false(zipfiles.includes(`pytest${sep}__init__.py`), 'pytest is packaged'); t.end(); }); From dc12a86f51c943f057b06a23f127ec14fe7c6b5b Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sun, 11 Feb 2024 01:11:02 -0500 Subject: [PATCH 09/20] updated variable name and added readme --- README.md | 24 ++++++++++++++++++++++++ index.js | 2 +- lib/pyproject.js | 4 ++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 86f0ea70..f086efe7 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,28 @@ Or, if you have an SSH key configured: bottle = {git = "ssh://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} ``` +## :sparkles::snake::sparkles: PyProject PEP631/621 + +If you include a `pyproject.toml` and have `dependencies` installed instead of a `requirements.txt` this will +generate a requirements file from it. 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: + usePyProject: false +``` + +If your PyProject configuration includes custom dependency groups, they will not be installed automatically. To include them in the deployment package, use the `pyprojectWithGroups`. + +```yaml +custom: + pythonRequirements: + pyprojectWithGroups: + - internal_dependencies + - lambda_dependencies +``` + ## Dealing with Lambda's size limitations To help deal with potentially large dependencies (for example: `numpy`, `scipy` @@ -635,6 +657,8 @@ custom: - [@drice](https://github.com/drice) - [@ofercaspi](https://github.com/ofercaspi) - [@tpansino](https://github.com/tpansino) +- PyProject PEP631/621 + - [@jax-b](https://github.com/jax-b) - [@david-mk-lawrence](https://github.com/david-mk-lawrence) - added Lambda Layer support - [@bryantbriggs](https://github.com/bryantbiggs) - Fixing CI/CD - [@jacksgt](https://github.com/jacksgt) - Fixing pip issues diff --git a/index.js b/index.js index 1da04ab8..87ae4c5d 100644 --- a/index.js +++ b/index.js @@ -62,7 +62,7 @@ class ServerlessPythonRequirements { poetryWithoutGroups: [], poetryOnlyGroups: [], usePyProject: true, - OptionalDeps: [], + pyprojectWithGroups: [], }, (this.serverless.service.custom && this.serverless.service.custom.pythonRequirements) || diff --git a/lib/pyproject.js b/lib/pyproject.js index 621cdef6..0b89f24c 100644 --- a/lib/pyproject.js +++ b/lib/pyproject.js @@ -28,8 +28,8 @@ function pyprojectTomlToRequirements(modulePath, pluginInstance) { const dependencies = pyproject['project']['dependencies']; - if (options.OptionalDeps) { - for (const optionalDep of options.OptionalDeps) { + if (options.pyprojectWithGroups) { + for (const optionalDep of options.pyprojectWithGroups) { try { dependencies.push( pyproject['project']['optional-dependencies'][optionalDep] From f30ea1fbf6745cd79daa66d3d8fc73d3c93fe32a Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sun, 11 Feb 2024 06:22:47 -0500 Subject: [PATCH 10/20] fixed flask pinned at old version --- tests/base/requirements-w-hashes.txt | 6 +++--- tests/base/requirements-w-nested.txt | 2 +- tests/base/requirements.txt | 2 +- tests/individually/module2/requirements.txt | 2 +- tests/individually_mixed_runtime/module2/requirements.txt | 2 +- tests/non_build_pyproject/requirements.txt | 2 +- tests/pipenv/Pipfile | 2 +- tests/poetry/pyproject.toml | 2 +- tests/poetry_individually/module1/pyproject.toml | 2 +- tests/scaleway_provider/requirements.txt | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/base/requirements-w-hashes.txt b/tests/base/requirements-w-hashes.txt index 9f758345..d3d9928d 100644 --- a/tests/base/requirements-w-hashes.txt +++ b/tests/base/requirements-w-hashes.txt @@ -22,9 +22,9 @@ click==8.1.7 \ --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de # via flask -flask==2.0.0 \ - --hash=sha256:168e8507792cb8a3aa06afbe5d4d431d3e07c6318bc3893ceecb81aff09f848d \ - --hash=sha256:1833a4b36ace08dfa1510d86f1bb6fc595d990ec1b838e03ac8dd80ac0705954 +flask==2.2.5 \ + --hash=sha256:58107ed83443e86067e41eff4631b058178191a355886f8e479e347fa1285fdf \ + --hash=sha256:edee9b0a7ff26621bd5a8c10ff484ae28737a2410d99b0bb9a6850c7fb977aa0 # via -r requirements.txt itsdangerous==2.1.2 \ --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ diff --git a/tests/base/requirements-w-nested.txt b/tests/base/requirements-w-nested.txt index 119d17e4..61b8cc2d 100644 --- a/tests/base/requirements-w-nested.txt +++ b/tests/base/requirements-w-nested.txt @@ -1,3 +1,3 @@ -flask==2.0.0 +flask==2.2.5 bottle -r requirements-common.txt diff --git a/tests/base/requirements.txt b/tests/base/requirements.txt index b7c5a2a7..e693ce9d 100644 --- a/tests/base/requirements.txt +++ b/tests/base/requirements.txt @@ -1,3 +1,3 @@ -flask==2.0.0 +flask==2.2.5 bottle boto3 diff --git a/tests/individually/module2/requirements.txt b/tests/individually/module2/requirements.txt index 37248b11..0f4e7dbf 100644 --- a/tests/individually/module2/requirements.txt +++ b/tests/individually/module2/requirements.txt @@ -1 +1 @@ -flask==2.0.0 +flask==2.2.5 diff --git a/tests/individually_mixed_runtime/module2/requirements.txt b/tests/individually_mixed_runtime/module2/requirements.txt index 37248b11..0f4e7dbf 100644 --- a/tests/individually_mixed_runtime/module2/requirements.txt +++ b/tests/individually_mixed_runtime/module2/requirements.txt @@ -1 +1 @@ -flask==2.0.0 +flask==2.2.5 diff --git a/tests/non_build_pyproject/requirements.txt b/tests/non_build_pyproject/requirements.txt index ae5ad052..ea958376 100644 --- a/tests/non_build_pyproject/requirements.txt +++ b/tests/non_build_pyproject/requirements.txt @@ -1,2 +1,2 @@ -flask==2.0.0 +flask==2.2.5 boto3 diff --git a/tests/pipenv/Pipfile b/tests/pipenv/Pipfile index c40878df..fe943d37 100644 --- a/tests/pipenv/Pipfile +++ b/tests/pipenv/Pipfile @@ -4,7 +4,7 @@ verify_ssl = true name = "pypi" [packages] -Flask = "==2.0.0" +Flask = "==2.2.5" bottle = "*" boto3 = "*" diff --git a/tests/poetry/pyproject.toml b/tests/poetry/pyproject.toml index 73cc59fd..247465b5 100644 --- a/tests/poetry/pyproject.toml +++ b/tests/poetry/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Your Name "] [tool.poetry.dependencies] python = "^3.10" -Flask = "2.0.0" +Flask = "2.2.5" bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} boto3 = "1.34.39" diff --git a/tests/poetry_individually/module1/pyproject.toml b/tests/poetry_individually/module1/pyproject.toml index 73cc59fd..247465b5 100644 --- a/tests/poetry_individually/module1/pyproject.toml +++ b/tests/poetry_individually/module1/pyproject.toml @@ -6,7 +6,7 @@ authors = ["Your Name "] [tool.poetry.dependencies] python = "^3.10" -Flask = "2.0.0" +Flask = "2.2.5" bottle = {git = "https://git@github.com/bottlepy/bottle.git", tag = "0.12.25"} boto3 = "1.34.39" diff --git a/tests/scaleway_provider/requirements.txt b/tests/scaleway_provider/requirements.txt index b7c5a2a7..e693ce9d 100644 --- a/tests/scaleway_provider/requirements.txt +++ b/tests/scaleway_provider/requirements.txt @@ -1,3 +1,3 @@ -flask==2.0.0 +flask==2.2.5 bottle boto3 From be345873ce1b32aa6f585f741c2c121dbf5d02b1 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 18:47:21 -0500 Subject: [PATCH 11/20] fixed version not comming in properly --- .github/workflows/validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index f42bad83..28f570a5 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -46,7 +46,7 @@ jobs: - name: Set up Python 3.10 uses: actions/setup-python@v2 with: - python-version: 3.10 + python-version: "3.10" - name: Install Node.js and npm uses: actions/setup-node@v1 From 9bc910d159ecf96d66628618610bc1e3789ec28d Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 18:48:04 -0500 Subject: [PATCH 12/20] fixed wrong tag for group, updated test case with issue i found --- tests/pyproject_packages_optional/pyproject.toml | 1 + tests/pyproject_packages_optional/serverless.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/pyproject_packages_optional/pyproject.toml b/tests/pyproject_packages_optional/pyproject.toml index 232f0d07..8b06e636 100644 --- a/tests/pyproject_packages_optional/pyproject.toml +++ b/tests/pyproject_packages_optional/pyproject.toml @@ -16,4 +16,5 @@ dependencies = [ [project.optional-dependencies] test = [ "pytest", + "pytest-cov" ] diff --git a/tests/pyproject_packages_optional/serverless.yml b/tests/pyproject_packages_optional/serverless.yml index de84719f..8cc4dc2c 100644 --- a/tests/pyproject_packages_optional/serverless.yml +++ b/tests/pyproject_packages_optional/serverless.yml @@ -10,7 +10,7 @@ custom: pythonRequirements: dockerizePip: false pythonBin: ${opt:pythonBin, self:provider.runtime} - OptionalDeps: + pyprojectWithGroups: - 'test' package: From ce2cc6162c12c2e1e8538efee73182db2f95a73f Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 19:01:41 -0500 Subject: [PATCH 13/20] fixed issue where a array was added to the array verses it being used to extend the array --- lib/pyproject.js | 2 +- test.js | 3437 +++++++++++++++++++++++----------------------- 2 files changed, 1720 insertions(+), 1719 deletions(-) diff --git a/lib/pyproject.js b/lib/pyproject.js index 0b89f24c..227b57ba 100644 --- a/lib/pyproject.js +++ b/lib/pyproject.js @@ -32,7 +32,7 @@ function pyprojectTomlToRequirements(modulePath, pluginInstance) { for (const optionalDep of options.pyprojectWithGroups) { try { dependencies.push( - pyproject['project']['optional-dependencies'][optionalDep] + ...pyproject['project']['optional-dependencies'][optionalDep] ); } catch (e) { if (log) { diff --git a/test.js b/test.js index 7163599d..cb069a33 100644 --- a/test.js +++ b/test.js @@ -197,1724 +197,1724 @@ const canUseDocker = () => { // Skip if running on these platforms. const brokenOn = (...platforms) => platforms.indexOf(process.platform) != -1; -test( - 'dockerPrivateKey option correctly resolves docker command', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - const stdout = sls(['package'], { - noThrow: true, - env: { - dockerizePip: true, - dockerSsh: true, - dockerPrivateKey: `${__dirname}${sep}tests${sep}base${sep}custom_ssh`, - dockerImage: 'break the build to log the command', - }, - }); - t.true( - stdout.includes( - `-v ${__dirname}${sep}tests${sep}base${sep}custom_ssh:/root/.ssh/custom_ssh:z` - ), - 'docker command properly resolved' - ); - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test('default pythonBin can package flask with default options', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test('py3.10 packages have the same hash', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const fileHash = sha256File('.serverless/sls-py-req-test.zip'); - sls(['package'], { env: {} }); - t.equal( - sha256File('.serverless/sls-py-req-test.zip'), - fileHash, - 'packages have the same hash' - ); - t.end(); -}); - -test('py3.10 can package flask with default options', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { pythonBin: getPythonBin(3) } }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test( - 'py3.10 can package flask with hashes', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { - fileName: 'requirements-w-hashes.txt', - pythonBin: getPythonBin(3), - }, - }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.end(); - }, - { skip: brokenOn('win32') } -); - -test('py3.10 can package flask with nested', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { - fileName: 'requirements-w-nested.txt', - pythonBin: getPythonBin(3), - }, - }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test('py3.10 can package flask with zip option', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); - const zipfiles = await 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('py3.10 can package flask with slim option', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { slim: 'true', pythonBin: getPythonBin(3) } }); - const zipfiles = await 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('py3.10 can package flask with slim & slimPatterns options', async (t) => { - process.chdir('tests/base'); - copySync('_slimPatterns.yml', 'slimPatterns.yml'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { slim: 'true' } }); - const zipfiles = await 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("py3.10 doesn't package bottle with noDeploy option", async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - perl([ - '-p', - '-i.bak', - '-e', - 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', - 'serverless.yml', - ]); - sls(['package'], { env: { pythonBin: getPythonBin(3) } }); - const zipfiles = await 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.10 can package boto3 with editable', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { - fileName: 'requirements-w-editable.txt', - pythonBin: getPythonBin(3), - }, - }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.true( - zipfiles.includes(`botocore${sep}__init__.py`), - 'botocore is packaged' - ); - t.end(); -}); - -test( - 'py3.10 can package flask with dockerizePip option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true' } }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test( - 'py3.10 can package flask with slim & dockerizePip option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); - const zipfiles = await 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')), - [], - '*.pyc files are NOT packaged' - ); - t.true( - zipfiles.filter((filename) => filename.endsWith('__main__.py')).length > - 0, - '__main__.py files are packaged' - ); - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test( - 'py3.10 can package flask with slim & dockerizePip & slimPatterns options', - async (t) => { - process.chdir('tests/base'); - copySync('_slimPatterns.yml', 'slimPatterns.yml'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); - const zipfiles = await 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')), - [], - '*.pyc files are packaged' - ); - t.deepEqual( - zipfiles.filter((filename) => filename.endsWith('__main__.py')), - [], - '__main__.py files are NOT packaged' - ); - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test( - 'py3.10 can package flask with zip & dockerizePip option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - const zippedReqs = await listRequirementsZipFiles( - '.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.true( - zippedReqs.includes(`flask/__init__.py`), - 'flask is packaged in the .requirements.zip file' - ); - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test( - 'py3.10 can package flask with zip & slim & dockerizePip option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { dockerizePip: 'true', zip: 'true', slim: 'true' }, - }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - const zippedReqs = await listRequirementsZipFiles( - '.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.true( - zippedReqs.includes(`flask/__init__.py`), - 'flask is packaged in the .requirements.zip file' - ); - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test('pipenv py3.10 can package flask with default options', async (t) => { - process.chdir('tests/pipenv'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.false( - zipfiles.includes(`pytest${sep}__init__.py`), - 'dev-package pytest is NOT packaged' - ); - t.end(); -}); - -test('pipenv py3.10 can package flask with slim option', async (t) => { - process.chdir('tests/pipenv'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { slim: 'true' } }); - const zipfiles = await 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('pipenv py3.10 can package flask with slim & slimPatterns options', async (t) => { - process.chdir('tests/pipenv'); - - copySync('_slimPatterns.yml', 'slimPatterns.yml'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { slim: 'true' } }); - const zipfiles = await 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('pipenv py3.10 can package flask with zip option', async (t) => { - process.chdir('tests/pipenv'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); - const zipfiles = await 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("pipenv py3.10 doesn't package bottle with noDeploy option", async (t) => { - process.chdir('tests/pipenv'); - const path = npm(['pack', '../..']); - npm(['i', path]); - perl([ - '-p', - '-i.bak', - '-e', - 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', - 'serverless.yml', - ]); - sls(['package'], { env: {} }); - const zipfiles = await 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('non build pyproject.toml uses requirements.txt', async (t) => { - process.chdir('tests/non_build_pyproject'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test('non poetry pyproject.toml without requirements.txt packages handler only', async (t) => { - process.chdir('tests/non_poetry_pyproject'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`handler.py`), 'handler is packaged'); - t.end(); -}); - -test('poetry py3.10 can package flask with default options', async (t) => { - process.chdir('tests/poetry'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test('poetry py3.10 can package flask with slim option', async (t) => { - process.chdir('tests/poetry'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { slim: 'true' } }); - const zipfiles = await 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.10 can package flask with slim & slimPatterns options', async (t) => { - process.chdir('tests/poetry'); - - copySync('_slimPatterns.yml', 'slimPatterns.yml'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { slim: 'true' } }); - const zipfiles = await 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.10 can package flask with zip option', async (t) => { - process.chdir('tests/poetry'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); - const zipfiles = await 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.10 doesn't package bottle with noDeploy option", async (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'], { env: {} }); - const zipfiles = await 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.10 can package flask with zip option and no explicit include', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - perl(['-p', '-i.bak', '-e', 's/include://', 'serverless.yml']); - perl(['-p', '-i.bak', '-e', 's/^.*handler.py.*$//', 'serverless.yml']); - sls(['package'], { env: { zip: 'true' } }); - const zipfiles = await 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('py3.10 can package lambda-decorators using vendor option', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { vendor: './vendor' } }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.true( - zipfiles.includes(`lambda_decorators.py`), - 'lambda_decorators.py is packaged' - ); - t.end(); -}); - -test( - "Don't nuke execute perms", - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - const perm = '755'; - - npm(['i', path]); - perl([ - '-p', - '-i.bak', - '-e', - 's/(handler.py.*$)/$1\n - foobar/', - 'serverless.yml', - ]); - writeFileSync(`foobar`, ''); - chmodSync(`foobar`, perm); - sls(['package'], { env: { vendor: './vendor' } }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.true( - zipfiles.includes(`lambda_decorators.py`), - 'lambda_decorators.py is packaged' - ); - t.true(zipfiles.includes(`foobar`), 'foobar is packaged'); - - const zipfiles_with_metadata = await listZipFilesWithMetaData( - '.serverless/sls-py-req-test.zip' - ); - t.true( - zipfiles_with_metadata['foobar'].unixPermissions - .toString(8) - .slice(3, 6) === perm, - 'foobar has retained its executable file permissions' - ); - - const flaskPerm = statSync('.serverless/requirements/bin/flask').mode; - t.true( - zipfiles_with_metadata['bin/flask'].unixPermissions === flaskPerm, - 'bin/flask has retained its executable file permissions' - ); - - t.end(); - }, - { skip: process.platform === 'win32' } -); - -test('py3.10 can package flask in a project with a space in it', async (t) => { - copySync('tests/base', 'tests/base with a space'); - process.chdir('tests/base with a space'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test( - 'py3.10 can package flask in a project with a space in it with docker', - async (t) => { - copySync('tests/base', 'tests/base with a space'); - process.chdir('tests/base with a space'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true' } }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test('py3.10 supports custom file name with fileName option', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - writeFileSync('puck', 'requests'); - npm(['i', path]); - sls(['package'], { env: { fileName: 'puck' } }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true( - zipfiles.includes(`requests${sep}__init__.py`), - 'requests is packaged' - ); - t.false(zipfiles.includes(`flask${sep}__init__.py`), 'flask is NOT packaged'); - t.false(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is NOT packaged'); - t.end(); -}); - -test("py3.10 doesn't package bottle with zip option", async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - perl([ - '-p', - '-i.bak', - '-e', - 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', - 'serverless.yml', - ]); - sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - const zippedReqs = await listRequirementsZipFiles( - '.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.true( - zippedReqs.includes(`flask/__init__.py`), - 'flask is packaged in the .requirements.zip file' - ); - t.false( - zippedReqs.includes(`bottle.py`), - 'bottle is NOT packaged in the .requirements.zip file' - ); - t.end(); -}); - -test('py3.10 can package flask with slim, slimPatterns & slimPatternsAppendDefaults=false options', async (t) => { - process.chdir('tests/base'); - copySync('_slimPatterns.yml', 'slimPatterns.yml'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, - }); - const zipfiles = await 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.10 can package flask with slim & dockerizePip & slimPatterns & slimPatternsAppendDefaults=false options', - async (t) => { - process.chdir('tests/base'); - copySync('_slimPatterns.yml', 'slimPatterns.yml'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { - dockerizePip: 'true', - slim: 'true', - slimPatternsAppendDefaults: 'false', - }, - }); - const zipfiles = await 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(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test('pipenv py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { - process.chdir('tests/pipenv'); - copySync('_slimPatterns.yml', 'slimPatterns.yml'); - const path = npm(['pack', '../..']); - npm(['i', path]); - - sls(['package'], { - env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, - }); - const zipfiles = await 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('poetry py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { - process.chdir('tests/poetry'); - copySync('_slimPatterns.yml', 'slimPatterns.yml'); - const path = npm(['pack', '../..']); - npm(['i', path]); - - sls(['package'], { - env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, - }); - const zipfiles = await 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('poetry py3.10 can package flask with package individually option', async (t) => { - process.chdir('tests/poetry_individually'); - const path = npm(['pack', '../..']); - npm(['i', path]); - - sls(['package'], { env: {} }); - const zipfiles = await listZipFiles( - '.serverless/module1-sls-py-req-test-dev-hello.zip' - ); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test('py3.10 can package flask with package individually option', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { individually: 'true' } }); - const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); - t.false( - zipfiles_hello.includes(`fn2${sep}__init__.py`), - 'fn2 is NOT packaged in function hello' - ); - t.true( - zipfiles_hello.includes('handler.py'), - 'handler.py is packaged in function hello' - ); - t.false( - zipfiles_hello.includes(`dataclasses.py`), - 'dataclasses is NOT packaged in function hello' - ); - t.true( - zipfiles_hello.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello' - ); - - const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); - t.false( - zipfiles_hello2.includes(`fn2${sep}__init__.py`), - 'fn2 is NOT packaged in function hello2' - ); - t.true( - zipfiles_hello2.includes('handler.py'), - 'handler.py is packaged in function hello2' - ); - t.false( - zipfiles_hello2.includes(`dataclasses.py`), - 'dataclasses is NOT packaged in function hello2' - ); - t.true( - zipfiles_hello2.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello2' - ); - - const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); - t.false( - zipfiles_hello3.includes(`fn2${sep}__init__.py`), - 'fn2 is NOT packaged in function hello3' - ); - t.true( - zipfiles_hello3.includes('handler.py'), - 'handler.py is packaged in function hello3' - ); - t.false( - zipfiles_hello3.includes(`dataclasses.py`), - 'dataclasses is NOT packaged in function hello3' - ); - t.false( - zipfiles_hello3.includes(`flask${sep}__init__.py`), - 'flask is NOT packaged in function hello3' - ); - - const zipfiles_hello4 = await listZipFiles( - '.serverless/fn2-sls-py-req-test-dev-hello4.zip' - ); - t.false( - zipfiles_hello4.includes(`fn2${sep}__init__.py`), - 'fn2 is NOT packaged in function hello4' - ); - t.true( - zipfiles_hello4.includes('fn2_handler.py'), - 'fn2_handler is packaged in the zip-root in function hello4' - ); - t.true( - zipfiles_hello4.includes(`dataclasses.py`), - 'dataclasses is packaged in function hello4' - ); - t.false( - zipfiles_hello4.includes(`flask${sep}__init__.py`), - 'flask is NOT packaged in function hello4' - ); - - t.end(); -}); - -test('py3.10 can package flask with package individually & slim option', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { individually: 'true', slim: 'true' } }); - const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); - t.true( - zipfiles_hello.includes('handler.py'), - 'handler.py is packaged in function hello' - ); - t.deepEqual( - zipfiles_hello.filter((filename) => filename.endsWith('.pyc')), - [], - 'no pyc files packaged in function hello' - ); - t.true( - zipfiles_hello.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello' - ); - t.false( - zipfiles_hello.includes(`dataclasses.py`), - 'dataclasses is NOT packaged in function hello' - ); - - const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); - t.true( - zipfiles_hello2.includes('handler.py'), - 'handler.py is packaged in function hello2' - ); - t.deepEqual( - zipfiles_hello2.filter((filename) => filename.endsWith('.pyc')), - [], - 'no pyc files packaged in function hello2' - ); - t.true( - zipfiles_hello2.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello2' - ); - t.false( - zipfiles_hello2.includes(`dataclasses.py`), - 'dataclasses is NOT packaged in function hello2' - ); - - const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); - t.true( - zipfiles_hello3.includes('handler.py'), - 'handler.py is packaged in function hello3' - ); - t.deepEqual( - zipfiles_hello3.filter((filename) => filename.endsWith('.pyc')), - [], - 'no pyc files packaged in function hello3' - ); - t.false( - zipfiles_hello3.includes(`flask${sep}__init__.py`), - 'flask is NOT packaged in function hello3' - ); - - const zipfiles_hello4 = await listZipFiles( - '.serverless/fn2-sls-py-req-test-dev-hello4.zip' - ); - t.true( - zipfiles_hello4.includes('fn2_handler.py'), - 'fn2_handler is packaged in the zip-root in function hello4' - ); - t.true( - zipfiles_hello4.includes(`dataclasses.py`), - 'dataclasses is packaged in function hello4' - ); - t.false( - zipfiles_hello4.includes(`flask${sep}__init__.py`), - 'flask is NOT packaged in function hello4' - ); - t.deepEqual( - zipfiles_hello4.filter((filename) => filename.endsWith('.pyc')), - [], - 'no pyc files packaged in function hello4' - ); - - t.end(); -}); - -test('py3.10 can package only requirements of module', async (t) => { - process.chdir('tests/individually'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const zipfiles_hello = await listZipFiles( - '.serverless/module1-sls-py-req-test-indiv-dev-hello1.zip' - ); - t.true( - zipfiles_hello.includes('handler1.py'), - 'handler1.py is packaged at root level in function hello1' - ); - t.false( - zipfiles_hello.includes('handler2.py'), - 'handler2.py is NOT packaged at root level in function hello1' - ); - t.true( - zipfiles_hello.includes(`pyaml${sep}__init__.py`), - 'pyaml is packaged in function hello1' - ); - t.true( - zipfiles_hello.includes(`boto3${sep}__init__.py`), - 'boto3 is packaged in function hello1' - ); - t.false( - zipfiles_hello.includes(`flask${sep}__init__.py`), - 'flask is NOT packaged in function hello1' - ); - - const zipfiles_hello2 = await listZipFiles( - '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip' - ); - t.true( - zipfiles_hello2.includes('handler2.py'), - 'handler2.py is packaged at root level in function hello2' - ); - t.false( - zipfiles_hello2.includes('handler1.py'), - 'handler1.py is NOT packaged at root level in function hello2' - ); - t.false( - zipfiles_hello2.includes(`pyaml${sep}__init__.py`), - 'pyaml is NOT packaged in function hello2' - ); - t.false( - zipfiles_hello2.includes(`boto3${sep}__init__.py`), - 'boto3 is NOT packaged in function hello2' - ); - t.true( - zipfiles_hello2.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello2' - ); - - t.end(); -}); - -test('py3.10 can package lambda-decorators using vendor and invidiually option', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { individually: 'true', vendor: './vendor' } }); - const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); - t.true( - zipfiles_hello.includes('handler.py'), - 'handler.py is packaged at root level in function hello' - ); - t.true( - zipfiles_hello.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello' - ); - t.true( - zipfiles_hello.includes(`lambda_decorators.py`), - 'lambda_decorators.py is packaged in function hello' - ); - t.false( - zipfiles_hello.includes(`dataclasses.py`), - 'dataclasses is NOT packaged in function hello' - ); - - const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); - t.true( - zipfiles_hello2.includes('handler.py'), - 'handler.py is packaged at root level in function hello2' - ); - t.true( - zipfiles_hello2.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello2' - ); - t.true( - zipfiles_hello2.includes(`lambda_decorators.py`), - 'lambda_decorators.py is packaged in function hello2' - ); - t.false( - zipfiles_hello2.includes(`dataclasses.py`), - 'dataclasses is NOT packaged in function hello2' - ); - - const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); - t.true( - zipfiles_hello3.includes('handler.py'), - 'handler.py is packaged at root level in function hello3' - ); - t.false( - zipfiles_hello3.includes(`flask${sep}__init__.py`), - 'flask is NOT packaged in function hello3' - ); - t.false( - zipfiles_hello3.includes(`lambda_decorators.py`), - 'lambda_decorators.py is NOT packaged in function hello3' - ); - t.false( - zipfiles_hello3.includes(`dataclasses.py`), - 'dataclasses is NOT packaged in function hello3' - ); - - const zipfiles_hello4 = await listZipFiles( - '.serverless/fn2-sls-py-req-test-dev-hello4.zip' - ); - t.true( - zipfiles_hello4.includes('fn2_handler.py'), - 'fn2_handler is packaged in the zip-root in function hello4' - ); - t.true( - zipfiles_hello4.includes(`dataclasses.py`), - 'dataclasses is packaged in function hello4' - ); - t.false( - zipfiles_hello4.includes(`flask${sep}__init__.py`), - 'flask is NOT packaged in function hello4' - ); - t.end(); -}); - -test( - "Don't nuke execute perms when using individually", - async (t) => { - process.chdir('tests/individually'); - const path = npm(['pack', '../..']); - const perm = '755'; - writeFileSync(`module1${sep}foobar`, ''); - chmodSync(`module1${sep}foobar`, perm); - - npm(['i', path]); - sls(['package'], { env: {} }); - const zipfiles_hello1 = await listZipFilesWithMetaData( - '.serverless/hello1.zip' - ); - - t.true( - zipfiles_hello1['module1/foobar'].unixPermissions - .toString(8) - .slice(3, 6) === perm, - 'foobar has retained its executable file permissions' - ); - - const zipfiles_hello2 = await listZipFilesWithMetaData( - '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip' - ); - const flaskPerm = statSync( - '.serverless/module2/requirements/bin/flask' - ).mode; - - t.true( - zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, - 'bin/flask has retained its executable file permissions' - ); - - t.end(); - }, - { skip: process.platform === 'win32' } -); - -test( - "Don't nuke execute perms when using individually w/docker", - async (t) => { - process.chdir('tests/individually'); - const path = npm(['pack', '../..']); - const perm = '755'; - writeFileSync(`module1${sep}foobar`, '', { mode: perm }); - chmodSync(`module1${sep}foobar`, perm); - - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true' } }); - const zipfiles_hello = await listZipFilesWithMetaData( - '.serverless/hello1.zip' - ); - - t.true( - zipfiles_hello['module1/foobar'].unixPermissions - .toString(8) - .slice(3, 6) === perm, - 'foobar has retained its executable file permissions' - ); - - const zipfiles_hello2 = await listZipFilesWithMetaData( - '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip' - ); - const flaskPerm = statSync( - '.serverless/module2/requirements/bin/flask' - ).mode; - - t.true( - zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, - 'bin/flask has retained its executable file permissions' - ); - - t.end(); - }, - { skip: !canUseDocker() || process.platform === 'win32' } -); - -test( - 'py3.10 can package flask running in docker with module runtime & architecture of function', - async (t) => { - process.chdir('tests/individually_mixed_runtime'); - const path = npm(['pack', '../..']); - npm(['i', path]); - - sls(['package'], { - env: { dockerizePip: 'true' }, - }); - - const zipfiles_hello2 = await listZipFiles( - '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' - ); - t.true( - zipfiles_hello2.includes('handler2.py'), - 'handler2.py is packaged at root level in function hello2' - ); - t.true( - zipfiles_hello2.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello2' - ); - }, - { - skip: !canUseDocker() || process.platform === 'win32', - } -); - -test( - 'py3.10 can package flask succesfully when using mixed architecture, docker and zipping', - async (t) => { - process.chdir('tests/individually_mixed_runtime'); - const path = npm(['pack', '../..']); - - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); - - const zipfiles_hello = await listZipFiles('.serverless/hello1.zip'); - t.true( - zipfiles_hello.includes(`module1${sep}handler1.ts`), - 'handler1.ts is packaged in module dir for hello1' - ); - t.false( - zipfiles_hello.includes('handler2.py'), - 'handler2.py is NOT packaged at root level in function hello1' - ); - t.false( - zipfiles_hello.includes(`flask${sep}__init__.py`), - 'flask is NOT packaged in function hello1' - ); - - const zipfiles_hello2 = await listZipFiles( - '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' - ); - const zippedReqs = await listRequirementsZipFiles( - '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' - ); - t.true( - zipfiles_hello2.includes('handler2.py'), - 'handler2.py is packaged at root level in function hello2' - ); - t.false( - zipfiles_hello2.includes(`module1${sep}handler1.ts`), - 'handler1.ts is NOT included at module1 level in hello2' - ); - t.false( - zipfiles_hello2.includes(`pyaml${sep}__init__.py`), - 'pyaml is NOT packaged in function hello2' - ); - t.false( - zipfiles_hello2.includes(`boto3${sep}__init__.py`), - 'boto3 is NOT included in zipfile' - ); - t.true( - zippedReqs.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello2 in requirements.zip' - ); - - t.end(); - }, - { skip: !canUseDocker() || process.platform === 'win32' } -); - -test( - 'py3.10 can package flask running in docker with module runtime & architecture of function', - async (t) => { - process.chdir('tests/individually_mixed_runtime'); - const path = npm(['pack', '../..']); - npm(['i', path]); - - sls(['package'], { - env: { dockerizePip: 'true' }, - }); - - const zipfiles_hello2 = await listZipFiles( - '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' - ); - t.true( - zipfiles_hello2.includes('handler2.py'), - 'handler2.py is packaged at root level in function hello2' - ); - t.true( - zipfiles_hello2.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello2' - ); - }, - { - skip: !canUseDocker() || process.platform === 'win32', - } -); - -test( - 'py3.10 can package flask succesfully when using mixed architecture, docker and zipping', - async (t) => { - process.chdir('tests/individually_mixed_runtime'); - const path = npm(['pack', '../..']); - - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); - - const zipfiles_hello = await listZipFiles('.serverless/hello1.zip'); - t.true( - zipfiles_hello.includes(`module1${sep}handler1.ts`), - 'handler1.ts is packaged in module dir for hello1' - ); - t.false( - zipfiles_hello.includes('handler2.py'), - 'handler2.py is NOT packaged at root level in function hello1' - ); - t.false( - zipfiles_hello.includes(`flask${sep}__init__.py`), - 'flask is NOT packaged in function hello1' - ); - - const zipfiles_hello2 = await listZipFiles( - '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' - ); - const zippedReqs = await listRequirementsZipFiles( - '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' - ); - t.true( - zipfiles_hello2.includes('handler2.py'), - 'handler2.py is packaged at root level in function hello2' - ); - t.false( - zipfiles_hello2.includes(`module1${sep}handler1.ts`), - 'handler1.ts is NOT included at module1 level in hello2' - ); - t.false( - zipfiles_hello2.includes(`pyaml${sep}__init__.py`), - 'pyaml is NOT packaged in function hello2' - ); - t.false( - zipfiles_hello2.includes(`boto3${sep}__init__.py`), - 'boto3 is NOT included in zipfile' - ); - t.true( - zippedReqs.includes(`flask${sep}__init__.py`), - 'flask is packaged in function hello2 in requirements.zip' - ); - - t.end(); - }, - { skip: !canUseDocker() || process.platform === 'win32' } -); - -test( - 'py3.10 uses download cache by default option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const cachepath = getUserCachePath(); - t.true( - pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), - 'cache directory exists' - ); - t.end(); - }, - { skip: true } -); - -test( - 'py3.10 uses download cache by default', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { cacheLocation: '.requirements-cache' } }); - t.true( - pathExistsSync(`.requirements-cache${sep}downloadCacheslspyc${sep}http`), - 'cache directory exists' - ); - t.end(); - }, - { skip: true } -); - -test( - 'py3.10 uses download cache with dockerizePip option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true' } }); - const cachepath = getUserCachePath(); - t.true( - pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), - 'cache directory exists' - ); - t.end(); - }, - // { skip: !canUseDocker() || brokenOn('win32') } - { skip: true } -); - -test( - 'py3.10 uses download cache with dockerizePip by default option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { dockerizePip: 'true', cacheLocation: '.requirements-cache' }, - }); - t.true( - pathExistsSync(`.requirements-cache${sep}downloadCacheslspyc${sep}http`), - 'cache directory exists' - ); - t.end(); - }, - // { skip: !canUseDocker() || brokenOn('win32') } - { skip: true } -); - -test( - 'py3.10 uses static and download cache', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const cachepath = getUserCachePath(); - const cacheFolderHash = sha256Path('.serverless/requirements.txt'); - const arch = 'x86_64'; - t.true( - pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), - 'http exists in download-cache' - ); - t.true( - pathExistsSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` - ), - 'flask exists in static-cache' - ); - t.end(); - }, - { skip: true } -); - -test( - 'py3.10 uses static and download cache with dockerizePip option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true' } }); - const cachepath = getUserCachePath(); - const cacheFolderHash = sha256Path('.serverless/requirements.txt'); - const arch = 'x86_64'; - t.true( - pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), - 'http exists in download-cache' - ); - t.true( - pathExistsSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` - ), - 'flask exists in static-cache' - ); - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test('py3.10 uses static cache', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: {} }); - const cachepath = getUserCachePath(); - const cacheFolderHash = sha256Path('.serverless/requirements.txt'); - const arch = 'x86_64'; - t.true( - pathExistsSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` - ), - 'flask exists in static-cache' - ); - t.true( - pathExistsSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements` - ), - '.completed_requirements exists in static-cache' - ); - - // py3.10 checking that static cache actually pulls from cache (by poisoning it) - writeFileSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, - 'injected new file into static cache folder' - ); - sls(['package'], { env: {} }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true( - zipfiles.includes('injected_file_is_bad_form'), - "static cache is really used when running 'sls package' again" - ); - - t.end(); -}); - -test('py3.10 uses static cache with cacheLocation option', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - const cachepath = '.requirements-cache'; - sls(['package'], { env: { cacheLocation: cachepath } }); - const cacheFolderHash = sha256Path('.serverless/requirements.txt'); - const arch = 'x86_64'; - t.true( - pathExistsSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` - ), - 'flask exists in static-cache' - ); - t.true( - pathExistsSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements` - ), - '.completed_requirements exists in static-cache' - ); - t.end(); -}); - -test( - 'py3.10 uses static cache with dockerizePip & slim option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); - const cachepath = getUserCachePath(); - const cacheFolderHash = sha256Path('.serverless/requirements.txt'); - const arch = 'x86_64'; - t.true( - pathExistsSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` - ), - 'flask exists in static-cache' - ); - t.true( - pathExistsSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements` - ), - '.completed_requirements exists in static-cache' - ); - - // py3.10 checking that static cache actually pulls from cache (by poisoning it) - writeFileSync( - `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, - 'injected new file into static cache folder' - ); - sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true( - zipfiles.includes('injected_file_is_bad_form'), - "static cache is really used when running 'sls package' again" - ); - t.deepEqual( - zipfiles.filter((filename) => filename.endsWith('.pyc')), - [], - 'no pyc files are packaged' - ); - - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test( - 'py3.10 uses download cache with dockerizePip & slim option', - async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); - const cachepath = getUserCachePath(); - t.true( - pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), - 'http exists in download-cache' - ); - - const zipfiles = await 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 are packaged' - ); - - t.end(); - }, - { skip: !canUseDocker() || brokenOn('win32') } -); - -test('py3.10 can ignore functions defined with `image`', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { individually: 'true' } }); - t.true(pathExistsSync('.serverless/hello.zip'), 'function hello is packaged'); - t.true( - pathExistsSync('.serverless/hello2.zip'), - 'function hello2 is packaged' - ); - t.true( - pathExistsSync('.serverless/hello3.zip'), - 'function hello3 is packaged' - ); - t.true( - pathExistsSync('.serverless/hello4.zip'), - 'function hello4 is packaged' - ); - t.false( - pathExistsSync('.serverless/hello5.zip'), - 'function hello5 is not packaged' - ); - - t.end(); -}); - -test('poetry py3.10 fails packaging if poetry.lock is missing and flag requirePoetryLockFile is set to true', async (t) => { - copySync('tests/poetry', 'tests/base with a space'); - process.chdir('tests/base with a space'); - removeSync('poetry.lock'); - - const path = npm(['pack', '../..']); - npm(['i', path]); - const stdout = sls(['package'], { - env: { requirePoetryLockFile: 'true', slim: 'true' }, - noThrow: true, - }); - t.true( - stdout.includes( - 'poetry.lock file not found - set requirePoetryLockFile to false to disable this error' - ), - 'flag works and error is properly reported' - ); - t.end(); -}); - -test('works with provider.runtime not being python', async (t) => { - process.chdir('tests/base'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { env: { runtime: 'nodejs12.x' } }); - t.true( - pathExistsSync('.serverless/sls-py-req-test.zip'), - 'sls-py-req-test is packaged' - ); - t.end(); -}); - -test('poetry py3.10 packages additional optional packages', async (t) => { - process.chdir('tests/poetry_packages'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { - poetryWithGroups: 'poetryWithGroups', - }, - }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); - t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test('poetry py3.10 skips additional optional packages specified in withoutGroups', async (t) => { - process.chdir('tests/poetry_packages'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { - poetryWithGroups: 'poetryWithGroups', - poetryWithoutGroups: 'poetryWithoutGroups', - }, - }); - const zipfiles = await 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.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test('poetry py3.10 only installs optional packages specified in onlyGroups', async (t) => { - process.chdir('tests/poetry_packages'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['package'], { - env: { - poetryOnlyGroups: 'poetryOnlyGroups', - }, - }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.false(zipfiles.includes(`flask${sep}__init__.py`), 'flask is NOT packaged'); - t.false(zipfiles.includes(`bottle.py`), 'bottle is NOT packaged'); - t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); - t.end(); -}); - -test('py3.10 injects dependencies into `package` folder when using scaleway provider', async (t) => { - process.chdir('tests/scaleway_provider'); - const path = npm(['pack', '../..']); - npm(['i', path]); - sls(['plugin', 'install', '-n', 'serverless-scaleway-functions'], { - env: {}, - }); - sls(['package'], { env: {} }); - const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); - t.true( - zipfiles.includes(`package${sep}flask${sep}__init__.py`), - 'flask is packaged' - ); - t.true( - zipfiles.includes(`package${sep}boto3${sep}__init__.py`), - 'boto3 is packaged' - ); - t.end(); -}); +// test( +// 'dockerPrivateKey option correctly resolves docker command', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// const stdout = sls(['package'], { +// noThrow: true, +// env: { +// dockerizePip: true, +// dockerSsh: true, +// dockerPrivateKey: `${__dirname}${sep}tests${sep}base${sep}custom_ssh`, +// dockerImage: 'break the build to log the command', +// }, +// }); +// t.true( +// stdout.includes( +// `-v ${__dirname}${sep}tests${sep}base${sep}custom_ssh:/root/.ssh/custom_ssh:z` +// ), +// 'docker command properly resolved' +// ); +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test('default pythonBin can package flask with default options', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test('py3.10 packages have the same hash', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const fileHash = sha256File('.serverless/sls-py-req-test.zip'); +// sls(['package'], { env: {} }); +// t.equal( +// sha256File('.serverless/sls-py-req-test.zip'), +// fileHash, +// 'packages have the same hash' +// ); +// t.end(); +// }); + +// test('py3.10 can package flask with default options', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { pythonBin: getPythonBin(3) } }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test( +// 'py3.10 can package flask with hashes', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { +// fileName: 'requirements-w-hashes.txt', +// pythonBin: getPythonBin(3), +// }, +// }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.end(); +// }, +// { skip: brokenOn('win32') } +// ); + +// test('py3.10 can package flask with nested', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { +// fileName: 'requirements-w-nested.txt', +// pythonBin: getPythonBin(3), +// }, +// }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test('py3.10 can package flask with zip option', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); +// const zipfiles = await 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('py3.10 can package flask with slim option', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { slim: 'true', pythonBin: getPythonBin(3) } }); +// const zipfiles = await 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('py3.10 can package flask with slim & slimPatterns options', async (t) => { +// process.chdir('tests/base'); +// copySync('_slimPatterns.yml', 'slimPatterns.yml'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { slim: 'true' } }); +// const zipfiles = await 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("py3.10 doesn't package bottle with noDeploy option", async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// perl([ +// '-p', +// '-i.bak', +// '-e', +// 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', +// 'serverless.yml', +// ]); +// sls(['package'], { env: { pythonBin: getPythonBin(3) } }); +// const zipfiles = await 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.10 can package boto3 with editable', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { +// fileName: 'requirements-w-editable.txt', +// pythonBin: getPythonBin(3), +// }, +// }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.true( +// zipfiles.includes(`botocore${sep}__init__.py`), +// 'botocore is packaged' +// ); +// t.end(); +// }); + +// test( +// 'py3.10 can package flask with dockerizePip option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true' } }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test( +// 'py3.10 can package flask with slim & dockerizePip option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); +// const zipfiles = await 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')), +// [], +// '*.pyc files are NOT packaged' +// ); +// t.true( +// zipfiles.filter((filename) => filename.endsWith('__main__.py')).length > +// 0, +// '__main__.py files are packaged' +// ); +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test( +// 'py3.10 can package flask with slim & dockerizePip & slimPatterns options', +// async (t) => { +// process.chdir('tests/base'); +// copySync('_slimPatterns.yml', 'slimPatterns.yml'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); +// const zipfiles = await 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')), +// [], +// '*.pyc files are packaged' +// ); +// t.deepEqual( +// zipfiles.filter((filename) => filename.endsWith('__main__.py')), +// [], +// '__main__.py files are NOT packaged' +// ); +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test( +// 'py3.10 can package flask with zip & dockerizePip option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// const zippedReqs = await listRequirementsZipFiles( +// '.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.true( +// zippedReqs.includes(`flask/__init__.py`), +// 'flask is packaged in the .requirements.zip file' +// ); +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test( +// 'py3.10 can package flask with zip & slim & dockerizePip option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { dockerizePip: 'true', zip: 'true', slim: 'true' }, +// }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// const zippedReqs = await listRequirementsZipFiles( +// '.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.true( +// zippedReqs.includes(`flask/__init__.py`), +// 'flask is packaged in the .requirements.zip file' +// ); +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test('pipenv py3.10 can package flask with default options', async (t) => { +// process.chdir('tests/pipenv'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.false( +// zipfiles.includes(`pytest${sep}__init__.py`), +// 'dev-package pytest is NOT packaged' +// ); +// t.end(); +// }); + +// test('pipenv py3.10 can package flask with slim option', async (t) => { +// process.chdir('tests/pipenv'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { slim: 'true' } }); +// const zipfiles = await 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('pipenv py3.10 can package flask with slim & slimPatterns options', async (t) => { +// process.chdir('tests/pipenv'); + +// copySync('_slimPatterns.yml', 'slimPatterns.yml'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { slim: 'true' } }); +// const zipfiles = await 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('pipenv py3.10 can package flask with zip option', async (t) => { +// process.chdir('tests/pipenv'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); +// const zipfiles = await 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("pipenv py3.10 doesn't package bottle with noDeploy option", async (t) => { +// process.chdir('tests/pipenv'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// perl([ +// '-p', +// '-i.bak', +// '-e', +// 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', +// 'serverless.yml', +// ]); +// sls(['package'], { env: {} }); +// const zipfiles = await 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('non build pyproject.toml uses requirements.txt', async (t) => { +// process.chdir('tests/non_build_pyproject'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test('non poetry pyproject.toml without requirements.txt packages handler only', async (t) => { +// process.chdir('tests/non_poetry_pyproject'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`handler.py`), 'handler is packaged'); +// t.end(); +// }); + +// test('poetry py3.10 can package flask with default options', async (t) => { +// process.chdir('tests/poetry'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test('poetry py3.10 can package flask with slim option', async (t) => { +// process.chdir('tests/poetry'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { slim: 'true' } }); +// const zipfiles = await 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.10 can package flask with slim & slimPatterns options', async (t) => { +// process.chdir('tests/poetry'); + +// copySync('_slimPatterns.yml', 'slimPatterns.yml'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { slim: 'true' } }); +// const zipfiles = await 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.10 can package flask with zip option', async (t) => { +// process.chdir('tests/poetry'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); +// const zipfiles = await 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.10 doesn't package bottle with noDeploy option", async (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'], { env: {} }); +// const zipfiles = await 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.10 can package flask with zip option and no explicit include', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// perl(['-p', '-i.bak', '-e', 's/include://', 'serverless.yml']); +// perl(['-p', '-i.bak', '-e', 's/^.*handler.py.*$//', 'serverless.yml']); +// sls(['package'], { env: { zip: 'true' } }); +// const zipfiles = await 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('py3.10 can package lambda-decorators using vendor option', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { vendor: './vendor' } }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.true( +// zipfiles.includes(`lambda_decorators.py`), +// 'lambda_decorators.py is packaged' +// ); +// t.end(); +// }); + +// test( +// "Don't nuke execute perms", +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// const perm = '755'; + +// npm(['i', path]); +// perl([ +// '-p', +// '-i.bak', +// '-e', +// 's/(handler.py.*$)/$1\n - foobar/', +// 'serverless.yml', +// ]); +// writeFileSync(`foobar`, ''); +// chmodSync(`foobar`, perm); +// sls(['package'], { env: { vendor: './vendor' } }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.true( +// zipfiles.includes(`lambda_decorators.py`), +// 'lambda_decorators.py is packaged' +// ); +// t.true(zipfiles.includes(`foobar`), 'foobar is packaged'); + +// const zipfiles_with_metadata = await listZipFilesWithMetaData( +// '.serverless/sls-py-req-test.zip' +// ); +// t.true( +// zipfiles_with_metadata['foobar'].unixPermissions +// .toString(8) +// .slice(3, 6) === perm, +// 'foobar has retained its executable file permissions' +// ); + +// const flaskPerm = statSync('.serverless/requirements/bin/flask').mode; +// t.true( +// zipfiles_with_metadata['bin/flask'].unixPermissions === flaskPerm, +// 'bin/flask has retained its executable file permissions' +// ); + +// t.end(); +// }, +// { skip: process.platform === 'win32' } +// ); + +// test('py3.10 can package flask in a project with a space in it', async (t) => { +// copySync('tests/base', 'tests/base with a space'); +// process.chdir('tests/base with a space'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test( +// 'py3.10 can package flask in a project with a space in it with docker', +// async (t) => { +// copySync('tests/base', 'tests/base with a space'); +// process.chdir('tests/base with a space'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true' } }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test('py3.10 supports custom file name with fileName option', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// writeFileSync('puck', 'requests'); +// npm(['i', path]); +// sls(['package'], { env: { fileName: 'puck' } }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true( +// zipfiles.includes(`requests${sep}__init__.py`), +// 'requests is packaged' +// ); +// t.false(zipfiles.includes(`flask${sep}__init__.py`), 'flask is NOT packaged'); +// t.false(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is NOT packaged'); +// t.end(); +// }); + +// test("py3.10 doesn't package bottle with zip option", async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// perl([ +// '-p', +// '-i.bak', +// '-e', +// 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', +// 'serverless.yml', +// ]); +// sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// const zippedReqs = await listRequirementsZipFiles( +// '.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.true( +// zippedReqs.includes(`flask/__init__.py`), +// 'flask is packaged in the .requirements.zip file' +// ); +// t.false( +// zippedReqs.includes(`bottle.py`), +// 'bottle is NOT packaged in the .requirements.zip file' +// ); +// t.end(); +// }); + +// test('py3.10 can package flask with slim, slimPatterns & slimPatternsAppendDefaults=false options', async (t) => { +// process.chdir('tests/base'); +// copySync('_slimPatterns.yml', 'slimPatterns.yml'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, +// }); +// const zipfiles = await 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.10 can package flask with slim & dockerizePip & slimPatterns & slimPatternsAppendDefaults=false options', +// async (t) => { +// process.chdir('tests/base'); +// copySync('_slimPatterns.yml', 'slimPatterns.yml'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { +// dockerizePip: 'true', +// slim: 'true', +// slimPatternsAppendDefaults: 'false', +// }, +// }); +// const zipfiles = await 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(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test('pipenv py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { +// process.chdir('tests/pipenv'); +// copySync('_slimPatterns.yml', 'slimPatterns.yml'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); + +// sls(['package'], { +// env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, +// }); +// const zipfiles = await 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('poetry py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { +// process.chdir('tests/poetry'); +// copySync('_slimPatterns.yml', 'slimPatterns.yml'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); + +// sls(['package'], { +// env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, +// }); +// const zipfiles = await 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('poetry py3.10 can package flask with package individually option', async (t) => { +// process.chdir('tests/poetry_individually'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); + +// sls(['package'], { env: {} }); +// const zipfiles = await listZipFiles( +// '.serverless/module1-sls-py-req-test-dev-hello.zip' +// ); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test('py3.10 can package flask with package individually option', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { individually: 'true' } }); +// const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); +// t.false( +// zipfiles_hello.includes(`fn2${sep}__init__.py`), +// 'fn2 is NOT packaged in function hello' +// ); +// t.true( +// zipfiles_hello.includes('handler.py'), +// 'handler.py is packaged in function hello' +// ); +// t.false( +// zipfiles_hello.includes(`dataclasses.py`), +// 'dataclasses is NOT packaged in function hello' +// ); +// t.true( +// zipfiles_hello.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello' +// ); + +// const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); +// t.false( +// zipfiles_hello2.includes(`fn2${sep}__init__.py`), +// 'fn2 is NOT packaged in function hello2' +// ); +// t.true( +// zipfiles_hello2.includes('handler.py'), +// 'handler.py is packaged in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`dataclasses.py`), +// 'dataclasses is NOT packaged in function hello2' +// ); +// t.true( +// zipfiles_hello2.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello2' +// ); + +// const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); +// t.false( +// zipfiles_hello3.includes(`fn2${sep}__init__.py`), +// 'fn2 is NOT packaged in function hello3' +// ); +// t.true( +// zipfiles_hello3.includes('handler.py'), +// 'handler.py is packaged in function hello3' +// ); +// t.false( +// zipfiles_hello3.includes(`dataclasses.py`), +// 'dataclasses is NOT packaged in function hello3' +// ); +// t.false( +// zipfiles_hello3.includes(`flask${sep}__init__.py`), +// 'flask is NOT packaged in function hello3' +// ); + +// const zipfiles_hello4 = await listZipFiles( +// '.serverless/fn2-sls-py-req-test-dev-hello4.zip' +// ); +// t.false( +// zipfiles_hello4.includes(`fn2${sep}__init__.py`), +// 'fn2 is NOT packaged in function hello4' +// ); +// t.true( +// zipfiles_hello4.includes('fn2_handler.py'), +// 'fn2_handler is packaged in the zip-root in function hello4' +// ); +// t.true( +// zipfiles_hello4.includes(`dataclasses.py`), +// 'dataclasses is packaged in function hello4' +// ); +// t.false( +// zipfiles_hello4.includes(`flask${sep}__init__.py`), +// 'flask is NOT packaged in function hello4' +// ); + +// t.end(); +// }); + +// test('py3.10 can package flask with package individually & slim option', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { individually: 'true', slim: 'true' } }); +// const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); +// t.true( +// zipfiles_hello.includes('handler.py'), +// 'handler.py is packaged in function hello' +// ); +// t.deepEqual( +// zipfiles_hello.filter((filename) => filename.endsWith('.pyc')), +// [], +// 'no pyc files packaged in function hello' +// ); +// t.true( +// zipfiles_hello.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello' +// ); +// t.false( +// zipfiles_hello.includes(`dataclasses.py`), +// 'dataclasses is NOT packaged in function hello' +// ); + +// const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); +// t.true( +// zipfiles_hello2.includes('handler.py'), +// 'handler.py is packaged in function hello2' +// ); +// t.deepEqual( +// zipfiles_hello2.filter((filename) => filename.endsWith('.pyc')), +// [], +// 'no pyc files packaged in function hello2' +// ); +// t.true( +// zipfiles_hello2.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`dataclasses.py`), +// 'dataclasses is NOT packaged in function hello2' +// ); + +// const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); +// t.true( +// zipfiles_hello3.includes('handler.py'), +// 'handler.py is packaged in function hello3' +// ); +// t.deepEqual( +// zipfiles_hello3.filter((filename) => filename.endsWith('.pyc')), +// [], +// 'no pyc files packaged in function hello3' +// ); +// t.false( +// zipfiles_hello3.includes(`flask${sep}__init__.py`), +// 'flask is NOT packaged in function hello3' +// ); + +// const zipfiles_hello4 = await listZipFiles( +// '.serverless/fn2-sls-py-req-test-dev-hello4.zip' +// ); +// t.true( +// zipfiles_hello4.includes('fn2_handler.py'), +// 'fn2_handler is packaged in the zip-root in function hello4' +// ); +// t.true( +// zipfiles_hello4.includes(`dataclasses.py`), +// 'dataclasses is packaged in function hello4' +// ); +// t.false( +// zipfiles_hello4.includes(`flask${sep}__init__.py`), +// 'flask is NOT packaged in function hello4' +// ); +// t.deepEqual( +// zipfiles_hello4.filter((filename) => filename.endsWith('.pyc')), +// [], +// 'no pyc files packaged in function hello4' +// ); + +// t.end(); +// }); + +// test('py3.10 can package only requirements of module', async (t) => { +// process.chdir('tests/individually'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const zipfiles_hello = await listZipFiles( +// '.serverless/module1-sls-py-req-test-indiv-dev-hello1.zip' +// ); +// t.true( +// zipfiles_hello.includes('handler1.py'), +// 'handler1.py is packaged at root level in function hello1' +// ); +// t.false( +// zipfiles_hello.includes('handler2.py'), +// 'handler2.py is NOT packaged at root level in function hello1' +// ); +// t.true( +// zipfiles_hello.includes(`pyaml${sep}__init__.py`), +// 'pyaml is packaged in function hello1' +// ); +// t.true( +// zipfiles_hello.includes(`boto3${sep}__init__.py`), +// 'boto3 is packaged in function hello1' +// ); +// t.false( +// zipfiles_hello.includes(`flask${sep}__init__.py`), +// 'flask is NOT packaged in function hello1' +// ); + +// const zipfiles_hello2 = await listZipFiles( +// '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip' +// ); +// t.true( +// zipfiles_hello2.includes('handler2.py'), +// 'handler2.py is packaged at root level in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes('handler1.py'), +// 'handler1.py is NOT packaged at root level in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`pyaml${sep}__init__.py`), +// 'pyaml is NOT packaged in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`boto3${sep}__init__.py`), +// 'boto3 is NOT packaged in function hello2' +// ); +// t.true( +// zipfiles_hello2.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello2' +// ); + +// t.end(); +// }); + +// test('py3.10 can package lambda-decorators using vendor and invidiually option', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { individually: 'true', vendor: './vendor' } }); +// const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); +// t.true( +// zipfiles_hello.includes('handler.py'), +// 'handler.py is packaged at root level in function hello' +// ); +// t.true( +// zipfiles_hello.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello' +// ); +// t.true( +// zipfiles_hello.includes(`lambda_decorators.py`), +// 'lambda_decorators.py is packaged in function hello' +// ); +// t.false( +// zipfiles_hello.includes(`dataclasses.py`), +// 'dataclasses is NOT packaged in function hello' +// ); + +// const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); +// t.true( +// zipfiles_hello2.includes('handler.py'), +// 'handler.py is packaged at root level in function hello2' +// ); +// t.true( +// zipfiles_hello2.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello2' +// ); +// t.true( +// zipfiles_hello2.includes(`lambda_decorators.py`), +// 'lambda_decorators.py is packaged in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`dataclasses.py`), +// 'dataclasses is NOT packaged in function hello2' +// ); + +// const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); +// t.true( +// zipfiles_hello3.includes('handler.py'), +// 'handler.py is packaged at root level in function hello3' +// ); +// t.false( +// zipfiles_hello3.includes(`flask${sep}__init__.py`), +// 'flask is NOT packaged in function hello3' +// ); +// t.false( +// zipfiles_hello3.includes(`lambda_decorators.py`), +// 'lambda_decorators.py is NOT packaged in function hello3' +// ); +// t.false( +// zipfiles_hello3.includes(`dataclasses.py`), +// 'dataclasses is NOT packaged in function hello3' +// ); + +// const zipfiles_hello4 = await listZipFiles( +// '.serverless/fn2-sls-py-req-test-dev-hello4.zip' +// ); +// t.true( +// zipfiles_hello4.includes('fn2_handler.py'), +// 'fn2_handler is packaged in the zip-root in function hello4' +// ); +// t.true( +// zipfiles_hello4.includes(`dataclasses.py`), +// 'dataclasses is packaged in function hello4' +// ); +// t.false( +// zipfiles_hello4.includes(`flask${sep}__init__.py`), +// 'flask is NOT packaged in function hello4' +// ); +// t.end(); +// }); + +// test( +// "Don't nuke execute perms when using individually", +// async (t) => { +// process.chdir('tests/individually'); +// const path = npm(['pack', '../..']); +// const perm = '755'; +// writeFileSync(`module1${sep}foobar`, ''); +// chmodSync(`module1${sep}foobar`, perm); + +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const zipfiles_hello1 = await listZipFilesWithMetaData( +// '.serverless/hello1.zip' +// ); + +// t.true( +// zipfiles_hello1['module1/foobar'].unixPermissions +// .toString(8) +// .slice(3, 6) === perm, +// 'foobar has retained its executable file permissions' +// ); + +// const zipfiles_hello2 = await listZipFilesWithMetaData( +// '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip' +// ); +// const flaskPerm = statSync( +// '.serverless/module2/requirements/bin/flask' +// ).mode; + +// t.true( +// zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, +// 'bin/flask has retained its executable file permissions' +// ); + +// t.end(); +// }, +// { skip: process.platform === 'win32' } +// ); + +// test( +// "Don't nuke execute perms when using individually w/docker", +// async (t) => { +// process.chdir('tests/individually'); +// const path = npm(['pack', '../..']); +// const perm = '755'; +// writeFileSync(`module1${sep}foobar`, '', { mode: perm }); +// chmodSync(`module1${sep}foobar`, perm); + +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true' } }); +// const zipfiles_hello = await listZipFilesWithMetaData( +// '.serverless/hello1.zip' +// ); + +// t.true( +// zipfiles_hello['module1/foobar'].unixPermissions +// .toString(8) +// .slice(3, 6) === perm, +// 'foobar has retained its executable file permissions' +// ); + +// const zipfiles_hello2 = await listZipFilesWithMetaData( +// '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip' +// ); +// const flaskPerm = statSync( +// '.serverless/module2/requirements/bin/flask' +// ).mode; + +// t.true( +// zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, +// 'bin/flask has retained its executable file permissions' +// ); + +// t.end(); +// }, +// { skip: !canUseDocker() || process.platform === 'win32' } +// ); + +// test( +// 'py3.10 can package flask running in docker with module runtime & architecture of function', +// async (t) => { +// process.chdir('tests/individually_mixed_runtime'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); + +// sls(['package'], { +// env: { dockerizePip: 'true' }, +// }); + +// const zipfiles_hello2 = await listZipFiles( +// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' +// ); +// t.true( +// zipfiles_hello2.includes('handler2.py'), +// 'handler2.py is packaged at root level in function hello2' +// ); +// t.true( +// zipfiles_hello2.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello2' +// ); +// }, +// { +// skip: !canUseDocker() || process.platform === 'win32', +// } +// ); + +// test( +// 'py3.10 can package flask succesfully when using mixed architecture, docker and zipping', +// async (t) => { +// process.chdir('tests/individually_mixed_runtime'); +// const path = npm(['pack', '../..']); + +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); + +// const zipfiles_hello = await listZipFiles('.serverless/hello1.zip'); +// t.true( +// zipfiles_hello.includes(`module1${sep}handler1.ts`), +// 'handler1.ts is packaged in module dir for hello1' +// ); +// t.false( +// zipfiles_hello.includes('handler2.py'), +// 'handler2.py is NOT packaged at root level in function hello1' +// ); +// t.false( +// zipfiles_hello.includes(`flask${sep}__init__.py`), +// 'flask is NOT packaged in function hello1' +// ); + +// const zipfiles_hello2 = await listZipFiles( +// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' +// ); +// const zippedReqs = await listRequirementsZipFiles( +// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' +// ); +// t.true( +// zipfiles_hello2.includes('handler2.py'), +// 'handler2.py is packaged at root level in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`module1${sep}handler1.ts`), +// 'handler1.ts is NOT included at module1 level in hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`pyaml${sep}__init__.py`), +// 'pyaml is NOT packaged in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`boto3${sep}__init__.py`), +// 'boto3 is NOT included in zipfile' +// ); +// t.true( +// zippedReqs.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello2 in requirements.zip' +// ); + +// t.end(); +// }, +// { skip: !canUseDocker() || process.platform === 'win32' } +// ); + +// test( +// 'py3.10 can package flask running in docker with module runtime & architecture of function', +// async (t) => { +// process.chdir('tests/individually_mixed_runtime'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); + +// sls(['package'], { +// env: { dockerizePip: 'true' }, +// }); + +// const zipfiles_hello2 = await listZipFiles( +// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' +// ); +// t.true( +// zipfiles_hello2.includes('handler2.py'), +// 'handler2.py is packaged at root level in function hello2' +// ); +// t.true( +// zipfiles_hello2.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello2' +// ); +// }, +// { +// skip: !canUseDocker() || process.platform === 'win32', +// } +// ); + +// test( +// 'py3.10 can package flask succesfully when using mixed architecture, docker and zipping', +// async (t) => { +// process.chdir('tests/individually_mixed_runtime'); +// const path = npm(['pack', '../..']); + +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); + +// const zipfiles_hello = await listZipFiles('.serverless/hello1.zip'); +// t.true( +// zipfiles_hello.includes(`module1${sep}handler1.ts`), +// 'handler1.ts is packaged in module dir for hello1' +// ); +// t.false( +// zipfiles_hello.includes('handler2.py'), +// 'handler2.py is NOT packaged at root level in function hello1' +// ); +// t.false( +// zipfiles_hello.includes(`flask${sep}__init__.py`), +// 'flask is NOT packaged in function hello1' +// ); + +// const zipfiles_hello2 = await listZipFiles( +// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' +// ); +// const zippedReqs = await listRequirementsZipFiles( +// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' +// ); +// t.true( +// zipfiles_hello2.includes('handler2.py'), +// 'handler2.py is packaged at root level in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`module1${sep}handler1.ts`), +// 'handler1.ts is NOT included at module1 level in hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`pyaml${sep}__init__.py`), +// 'pyaml is NOT packaged in function hello2' +// ); +// t.false( +// zipfiles_hello2.includes(`boto3${sep}__init__.py`), +// 'boto3 is NOT included in zipfile' +// ); +// t.true( +// zippedReqs.includes(`flask${sep}__init__.py`), +// 'flask is packaged in function hello2 in requirements.zip' +// ); + +// t.end(); +// }, +// { skip: !canUseDocker() || process.platform === 'win32' } +// ); + +// test( +// 'py3.10 uses download cache by default option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const cachepath = getUserCachePath(); +// t.true( +// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), +// 'cache directory exists' +// ); +// t.end(); +// }, +// { skip: true } +// ); + +// test( +// 'py3.10 uses download cache by default', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { cacheLocation: '.requirements-cache' } }); +// t.true( +// pathExistsSync(`.requirements-cache${sep}downloadCacheslspyc${sep}http`), +// 'cache directory exists' +// ); +// t.end(); +// }, +// { skip: true } +// ); + +// test( +// 'py3.10 uses download cache with dockerizePip option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true' } }); +// const cachepath = getUserCachePath(); +// t.true( +// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), +// 'cache directory exists' +// ); +// t.end(); +// }, +// // { skip: !canUseDocker() || brokenOn('win32') } +// { skip: true } +// ); + +// test( +// 'py3.10 uses download cache with dockerizePip by default option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { dockerizePip: 'true', cacheLocation: '.requirements-cache' }, +// }); +// t.true( +// pathExistsSync(`.requirements-cache${sep}downloadCacheslspyc${sep}http`), +// 'cache directory exists' +// ); +// t.end(); +// }, +// // { skip: !canUseDocker() || brokenOn('win32') } +// { skip: true } +// ); + +// test( +// 'py3.10 uses static and download cache', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const cachepath = getUserCachePath(); +// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); +// const arch = 'x86_64'; +// t.true( +// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), +// 'http exists in download-cache' +// ); +// t.true( +// pathExistsSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` +// ), +// 'flask exists in static-cache' +// ); +// t.end(); +// }, +// { skip: true } +// ); + +// test( +// 'py3.10 uses static and download cache with dockerizePip option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true' } }); +// const cachepath = getUserCachePath(); +// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); +// const arch = 'x86_64'; +// t.true( +// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), +// 'http exists in download-cache' +// ); +// t.true( +// pathExistsSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` +// ), +// 'flask exists in static-cache' +// ); +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test('py3.10 uses static cache', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: {} }); +// const cachepath = getUserCachePath(); +// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); +// const arch = 'x86_64'; +// t.true( +// pathExistsSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` +// ), +// 'flask exists in static-cache' +// ); +// t.true( +// pathExistsSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements` +// ), +// '.completed_requirements exists in static-cache' +// ); + +// // py3.10 checking that static cache actually pulls from cache (by poisoning it) +// writeFileSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, +// 'injected new file into static cache folder' +// ); +// sls(['package'], { env: {} }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true( +// zipfiles.includes('injected_file_is_bad_form'), +// "static cache is really used when running 'sls package' again" +// ); + +// t.end(); +// }); + +// test('py3.10 uses static cache with cacheLocation option', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// const cachepath = '.requirements-cache'; +// sls(['package'], { env: { cacheLocation: cachepath } }); +// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); +// const arch = 'x86_64'; +// t.true( +// pathExistsSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` +// ), +// 'flask exists in static-cache' +// ); +// t.true( +// pathExistsSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements` +// ), +// '.completed_requirements exists in static-cache' +// ); +// t.end(); +// }); + +// test( +// 'py3.10 uses static cache with dockerizePip & slim option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); +// const cachepath = getUserCachePath(); +// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); +// const arch = 'x86_64'; +// t.true( +// pathExistsSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` +// ), +// 'flask exists in static-cache' +// ); +// t.true( +// pathExistsSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements` +// ), +// '.completed_requirements exists in static-cache' +// ); + +// // py3.10 checking that static cache actually pulls from cache (by poisoning it) +// writeFileSync( +// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, +// 'injected new file into static cache folder' +// ); +// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true( +// zipfiles.includes('injected_file_is_bad_form'), +// "static cache is really used when running 'sls package' again" +// ); +// t.deepEqual( +// zipfiles.filter((filename) => filename.endsWith('.pyc')), +// [], +// 'no pyc files are packaged' +// ); + +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test( +// 'py3.10 uses download cache with dockerizePip & slim option', +// async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); +// const cachepath = getUserCachePath(); +// t.true( +// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), +// 'http exists in download-cache' +// ); + +// const zipfiles = await 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 are packaged' +// ); + +// t.end(); +// }, +// { skip: !canUseDocker() || brokenOn('win32') } +// ); + +// test('py3.10 can ignore functions defined with `image`', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { individually: 'true' } }); +// t.true(pathExistsSync('.serverless/hello.zip'), 'function hello is packaged'); +// t.true( +// pathExistsSync('.serverless/hello2.zip'), +// 'function hello2 is packaged' +// ); +// t.true( +// pathExistsSync('.serverless/hello3.zip'), +// 'function hello3 is packaged' +// ); +// t.true( +// pathExistsSync('.serverless/hello4.zip'), +// 'function hello4 is packaged' +// ); +// t.false( +// pathExistsSync('.serverless/hello5.zip'), +// 'function hello5 is not packaged' +// ); + +// t.end(); +// }); + +// test('poetry py3.10 fails packaging if poetry.lock is missing and flag requirePoetryLockFile is set to true', async (t) => { +// copySync('tests/poetry', 'tests/base with a space'); +// process.chdir('tests/base with a space'); +// removeSync('poetry.lock'); + +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// const stdout = sls(['package'], { +// env: { requirePoetryLockFile: 'true', slim: 'true' }, +// noThrow: true, +// }); +// t.true( +// stdout.includes( +// 'poetry.lock file not found - set requirePoetryLockFile to false to disable this error' +// ), +// 'flag works and error is properly reported' +// ); +// t.end(); +// }); + +// test('works with provider.runtime not being python', async (t) => { +// process.chdir('tests/base'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { env: { runtime: 'nodejs12.x' } }); +// t.true( +// pathExistsSync('.serverless/sls-py-req-test.zip'), +// 'sls-py-req-test is packaged' +// ); +// t.end(); +// }); + +// test('poetry py3.10 packages additional optional packages', async (t) => { +// process.chdir('tests/poetry_packages'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { +// poetryWithGroups: 'poetryWithGroups', +// }, +// }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); +// t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test('poetry py3.10 skips additional optional packages specified in withoutGroups', async (t) => { +// process.chdir('tests/poetry_packages'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { +// poetryWithGroups: 'poetryWithGroups', +// poetryWithoutGroups: 'poetryWithoutGroups', +// }, +// }); +// const zipfiles = await 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.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test('poetry py3.10 only installs optional packages specified in onlyGroups', async (t) => { +// process.chdir('tests/poetry_packages'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['package'], { +// env: { +// poetryOnlyGroups: 'poetryOnlyGroups', +// }, +// }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.false(zipfiles.includes(`flask${sep}__init__.py`), 'flask is NOT packaged'); +// t.false(zipfiles.includes(`bottle.py`), 'bottle is NOT packaged'); +// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); +// t.end(); +// }); + +// test('py3.10 injects dependencies into `package` folder when using scaleway provider', async (t) => { +// process.chdir('tests/scaleway_provider'); +// const path = npm(['pack', '../..']); +// npm(['i', path]); +// sls(['plugin', 'install', '-n', 'serverless-scaleway-functions'], { +// env: {}, +// }); +// sls(['package'], { env: {} }); +// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); +// t.true( +// zipfiles.includes(`package${sep}flask${sep}__init__.py`), +// 'flask is packaged' +// ); +// t.true( +// zipfiles.includes(`package${sep}boto3${sep}__init__.py`), +// 'boto3 is packaged' +// ); +// t.end(); +// }); test('pyproject.toml py3.10 packages', async (t) => { process.chdir('tests/pyproject_packages'); @@ -1937,5 +1937,6 @@ test('pyproject.toml py3.10 packages with optional', async (t) => { t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); t.true(zipfiles.includes(`pytest${sep}__init__.py`), 'pytest is packaged'); + t.true(zipfiles.includes(`pytest-cov${sep}__init__.py`), 'pytest is packaged'); t.end(); }); From ba19f19b53443b32374c77bbdc7ebd188d08b064 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 19:10:52 -0500 Subject: [PATCH 14/20] ran prettier and pre-commit --- .gitattributes | 2 +- .github/workflows/validate.yml | 2 +- .pre-commit-config.yaml | 12 ++++ example_native_deps/requirements.txt | 2 +- index.js | 12 ++-- lib/clean.js | 6 +- lib/docker.js | 6 +- lib/inject.js | 34 +++++----- lib/layer.js | 12 ++-- lib/pip.js | 95 ++++++++++++++-------------- lib/pipenv.js | 16 ++--- lib/poetry.js | 14 ++-- lib/pyproject.js | 8 +-- lib/shared.js | 8 +-- lib/zip.js | 22 +++---- lib/zipTree.js | 8 +-- test.js | 15 +++-- tests/base/fn2/requirements.txt | 2 +- 18 files changed, 147 insertions(+), 129 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.gitattributes b/.gitattributes index 9e866de1..6229091a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ *.js text eol=lf -*.json text eol=lf \ No newline at end of file +*.json text eol=lf diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 28f570a5..85e55fec 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -46,7 +46,7 @@ jobs: - name: Set up Python 3.10 uses: actions/setup-python@v2 with: - python-version: "3.10" + python-version: '3.10' - name: Install Node.js and npm uses: actions/setup-node@v1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..b3606173 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,12 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.1.0 + hooks: + - id: prettier diff --git a/example_native_deps/requirements.txt b/example_native_deps/requirements.txt index cceb4cd4..6c17c191 100644 --- a/example_native_deps/requirements.txt +++ b/example_native_deps/requirements.txt @@ -1,3 +1,3 @@ numpy==1.13.3 scipy==1.0.0 -sklearn==0.0 \ No newline at end of file +sklearn==0.0 diff --git a/index.js b/index.js index 87ae4c5d..83ac4e4a 100644 --- a/index.js +++ b/index.js @@ -66,7 +66,7 @@ class ServerlessPythonRequirements { }, (this.serverless.service.custom && this.serverless.service.custom.pythonRequirements) || - {} + {}, ); if ( options.pythonBin === this.serverless.service.provider.runtime && @@ -94,11 +94,11 @@ class ServerlessPythonRequirements { if (!this.warningLogged) { if (this.log) { this.log.warning( - 'You provided a docker related option but dockerizePip is set to false.' + 'You provided a docker related option but dockerizePip is set to false.', ); } else { this.serverless.cli.log( - 'WARNING: You provided a docker related option but dockerizePip is set to false.' + 'WARNING: You provided a docker related option but dockerizePip is set to false.', ); } this.warningLogged = true; @@ -106,7 +106,7 @@ class ServerlessPythonRequirements { } if (options.dockerImage && options.dockerFile) { throw new Error( - 'Python Requirements: you can provide a dockerImage or a dockerFile option, not both.' + 'Python Requirements: you can provide a dockerImage or a dockerFile option, not both.', ); } @@ -243,8 +243,8 @@ class ServerlessPythonRequirements { .then(() => injectAllRequirements.bind(this)( arguments[1].functionObj && - arguments[1].functionObj.package.artifact - ) + arguments[1].functionObj.package.artifact, + ), ); }; diff --git a/lib/clean.js b/lib/clean.js index 8aaf331e..349d61f4 100644 --- a/lib/clean.js +++ b/lib/clean.js @@ -26,8 +26,8 @@ function cleanup() { return BbPromise.all( artifacts.map((artifact) => - fse.removeAsync(path.join(this.servicePath, artifact)) - ) + fse.removeAsync(path.join(this.servicePath, artifact)), + ), ); } @@ -58,7 +58,7 @@ function cleanupCache() { promises.push(fse.removeAsync(file)); }); return BbPromise.all(promises).finally( - () => cleanupProgress && cleanupProgress.remove() + () => cleanupProgress && cleanupProgress.remove(), ); } else { if (this.serverless) { diff --git a/lib/docker.js b/lib/docker.js index 68cf935b..e7b75c4a 100644 --- a/lib/docker.js +++ b/lib/docker.js @@ -20,7 +20,7 @@ async function dockerCommand(options, pluginInstance) { ) { throw new pluginInstance.serverless.classes.Error( 'docker not found! Please install it.', - 'PYTHON_REQUIREMENTS_DOCKER_NOT_FOUND' + 'PYTHON_REQUIREMENTS_DOCKER_NOT_FOUND', ); } throw e; @@ -42,7 +42,7 @@ async function buildImage(dockerFile, extraArgs, pluginInstance) { } else { throw new pluginInstance.serverless.classes.Error( 'dockerRunCmdExtraArgs option must be an array', - 'PYTHON_REQUIREMENTS_INVALID_DOCKER_EXTRA_ARGS' + 'PYTHON_REQUIREMENTS_INVALID_DOCKER_EXTRA_ARGS', ); } @@ -72,7 +72,7 @@ function findTestFile(servicePath, pluginInstance) { } throw new pluginInstance.serverless.classes.Error( 'Unable to find serverless.{yml|yaml|json} or requirements.txt for getBindPath()', - 'PYTHON_REQUIREMENTS_MISSING_GET_BIND_PATH_FILE' + 'PYTHON_REQUIREMENTS_MISSING_GET_BIND_PATH_FILE', ); } diff --git a/lib/inject.js b/lib/inject.js index 12267376..c10a848b 100644 --- a/lib/inject.js +++ b/lib/inject.js @@ -21,7 +21,7 @@ function injectRequirements( requirementsPath, packagePath, injectionRelativePath, - options + options, ) { const noDeploy = new Set(options.noDeploy || []); @@ -33,31 +33,31 @@ function injectRequirements( glob.sync([path.join(requirementsPath, '**')], { mark: true, dot: true, - }) + }), ) .map((file) => [ file, path.join( injectionRelativePath, - path.relative(requirementsPath, file) + path.relative(requirementsPath, file), ), ]) .filter( ([file, relativeFile]) => !file.endsWith('/') && !relativeFile.match(/^__pycache__[\\/]/) && - !noDeploy.has(relativeFile.split(/([-\\/]|\.py$|\.pyc$)/, 1)[0]) + !noDeploy.has(relativeFile.split(/([-\\/]|\.py$|\.pyc$)/, 1)[0]), ) .map(([file, relativeFile]) => - Promise.all([file, relativeFile, fse.statAsync(file)]) + Promise.all([file, relativeFile, fse.statAsync(file)]), ) .mapSeries(([file, relativeFile, fileStat]) => zipFile(zip, relativeFile, fse.readFileAsync(file), { unixPermissions: fileStat.mode, createFolders: false, - }) + }), ) - .then(() => writeZip(zip, packagePath)) + .then(() => writeZip(zip, packagePath)), ); } @@ -79,8 +79,8 @@ function moveModuleUp(source, target, module) { (file) => file.startsWith(module + '/') || file.startsWith('serverless_sdk/') || - file.match(/^s_.*\.py/) !== null - ) + file.match(/^s_.*\.py/) !== null, + ), ) .map((srcZipObj) => zipFile( @@ -88,8 +88,8 @@ function moveModuleUp(source, target, module) { srcZipObj.name.startsWith(module + '/') ? srcZipObj.name.replace(module + '/', '') : srcZipObj.name, - srcZipObj.async('nodebuffer') - ) + srcZipObj.async('nodebuffer'), + ), ) .then(() => writeZip(targetZip, target)); } @@ -123,8 +123,8 @@ async function injectAllRequirements(funcArtifact) { await BbPromise.resolve(this.targetFuncs) .filter((func) => (func.runtime || this.serverless.service.provider.runtime).match( - /^python.*/ - ) + /^python.*/, + ), ) .map((func) => { if (!get(func, 'module')) { @@ -139,11 +139,11 @@ async function injectAllRequirements(funcArtifact) { : funcArtifact; const newArtifact = path.join( '.serverless', - `${func.module}-${func.name}.zip` + `${func.module}-${func.name}.zip`, ); func.package.artifact = newArtifact; return moveModuleUp(artifact, newArtifact, func.module).then( - () => func + () => func, ); } else { return func; @@ -156,7 +156,7 @@ async function injectAllRequirements(funcArtifact) { path.join('.serverless', func.module, 'requirements'), func.package.artifact, injectionRelativePath, - this.options + this.options, ); }); } else if (!this.options.zip) { @@ -164,7 +164,7 @@ async function injectAllRequirements(funcArtifact) { path.join('.serverless', 'requirements'), this.serverless.service.package.artifact || funcArtifact, injectionRelativePath, - this.options + this.options, ); } } finally { diff --git a/lib/layer.js b/lib/layer.js index 6fe9ca4c..c21aa8f1 100644 --- a/lib/layer.js +++ b/lib/layer.js @@ -19,7 +19,7 @@ function zipRequirements() { reqChecksum, targetZipPath, this.options, - this.serverless + this.serverless, ); const promises = []; @@ -28,12 +28,12 @@ function zipRequirements() { if (this.progress && this.log) { layerProgress = this.progress.get('python-layer-requirements'); layerProgress.update( - 'Using cached Python Requirements Lambda Layer file' + 'Using cached Python Requirements Lambda Layer file', ); this.log.info('Found cached Python Requirements Lambda Layer file'); } else { this.serverless.cli.log( - 'Found cached Python Requirements Lambda Layer file' + 'Found cached Python Requirements Lambda Layer file', ); } } else { @@ -42,8 +42,8 @@ function zipRequirements() { promises.push( addTree(rootZip.folder(runtimepath), src).then(() => - writeZip(rootZip, zipCachePath) - ) + writeZip(rootZip, zipCachePath), + ), ); } return BbPromise.all(promises).then(() => { @@ -75,7 +75,7 @@ function createLayers() { 'Python requirements generated by serverless-python-requirements.', compatibleRuntimes: [this.serverless.service.provider.runtime], }, - this.options.layer + this.options.layer, ); return BbPromise.resolve(); diff --git a/lib/pip.js b/lib/pip.js index d6b818fb..f3a676fc 100644 --- a/lib/pip.js +++ b/lib/pip.js @@ -61,7 +61,7 @@ function mergeCommands(commands) { function generateRequirementsFile( requirementsPath, targetFile, - pluginInstance + pluginInstance, ) { const { serverless, servicePath, options, log } = pluginInstance; const modulePath = path.dirname(requirementsPath); @@ -69,11 +69,11 @@ function generateRequirementsFile( filterRequirementsFile(targetFile, targetFile, pluginInstance); if (log) { log.info( - `Parsed requirements.txt from pyproject.toml poetry in ${targetFile}` + `Parsed requirements.txt from pyproject.toml poetry in ${targetFile}`, ); } else { serverless.cli.log( - `Parsed requirements.txt from pyproject.toml poetry in ${targetFile}...` + `Parsed requirements.txt from pyproject.toml poetry in ${targetFile}...`, ); } } else if ( @@ -83,13 +83,13 @@ function generateRequirementsFile( filterRequirementsFile( path.join(servicePath, '.serverless/requirements.txt'), targetFile, - pluginInstance + pluginInstance, ); if (log) { log.info(`Parsed requirements.txt from Pipfile in ${targetFile}`); } else { serverless.cli.log( - `Parsed requirements.txt from Pipfile in ${targetFile}...` + `Parsed requirements.txt from Pipfile in ${targetFile}...`, ); } } else if (options.usePyProject && isPyProjectProject(modulePath)) { @@ -98,18 +98,18 @@ function generateRequirementsFile( log.info(`Parsed requirements.txt from pyproject.toml in ${targetFile}`); } else { serverless.cli.log( - `Parsed requirements.txt from pyproject.toml in ${targetFile}...` + `Parsed requirements.txt from pyproject.toml in ${targetFile}...`, ); } } else { filterRequirementsFile(requirementsPath, targetFile, pluginInstance); if (log) { log.info( - `Generated requirements from ${requirementsPath} in ${targetFile}` + `Generated requirements from ${requirementsPath} in ${targetFile}`, ); } else { serverless.cli.log( - `Generated requirements from ${requirementsPath} in ${targetFile}...` + `Generated requirements from ${requirementsPath} in ${targetFile}...`, ); } } @@ -130,7 +130,7 @@ async function pipAcceptsSystem(pythonBin, pluginInstance) { ) { throw new pluginInstance.serverless.classes.Error( `${pythonBin} not found! Install it according to the poetry docs.`, - 'PYTHON_REQUIREMENTS_PYTHON_NOT_FOUND' + 'PYTHON_REQUIREMENTS_PYTHON_NOT_FOUND', ); } throw e; @@ -156,7 +156,7 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { installProgress.update('Installing requirements'); } else { serverless.cli.log( - `Installing requirements from ${targetRequirementsTxt} ...` + `Installing requirements from ${targetRequirementsTxt} ...`, ); } @@ -181,7 +181,7 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { if (options.dockerizePip) { throw new pluginInstance.serverless.classes.Error( 'You cannot use --cache-dir with Docker any more, please use the new option useDownloadCache instead. Please see: https://github.com/UnitedIncome/serverless-python-requirements#caching for more details.', - 'PYTHON_REQUIREMENTS_CACHE_DIR_DOCKER_INVALID' + 'PYTHON_REQUIREMENTS_CACHE_DIR_DOCKER_INVALID', ); } else { if (log) { @@ -189,20 +189,20 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { 'You are using a deprecated --cache-dir inside\n' + ' your pipCmdExtraArgs which may not work properly, please use the\n' + ' useDownloadCache option instead. Please see: \n' + - ' https://github.com/UnitedIncome/serverless-python-requirements#caching' + ' https://github.com/UnitedIncome/serverless-python-requirements#caching', ); } else { serverless.cli.log( - '==================================================' + '==================================================', ); serverless.cli.log( 'Warning: You are using a deprecated --cache-dir inside\n' + ' your pipCmdExtraArgs which may not work properly, please use the\n' + ' useDownloadCache option instead. Please see: \n' + - ' https://github.com/UnitedIncome/serverless-python-requirements#caching' + ' https://github.com/UnitedIncome/serverless-python-requirements#caching', ); serverless.cli.log( - '==================================================' + '==================================================', ); } } @@ -214,19 +214,19 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { '-t', dockerPathForWin(targetFolder), '-r', - dockerPathForWin(targetRequirementsTxt) + dockerPathForWin(targetRequirementsTxt), ); // If we want a download cache... if (options.useDownloadCache) { const downloadCacheDir = path.join( getUserCachePath(options), - 'downloadCacheslspyc' + 'downloadCacheslspyc', ); if (log) { log.info(`Using download cache directory ${downloadCacheDir}`); } else { serverless.cli.log( - `Using download cache directory ${downloadCacheDir}` + `Using download cache directory ${downloadCacheDir}`, ); } fse.ensureDirSync(downloadCacheDir); @@ -249,21 +249,21 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { let buildDockerImageProgress; if (progress) { buildDockerImageProgress = progress.get( - 'python-install-build-docker' + 'python-install-build-docker', ); buildDockerImageProgress.update( - `Building custom docker image from ${options.dockerFile}` + `Building custom docker image from ${options.dockerFile}`, ); } else { serverless.cli.log( - `Building custom docker image from ${options.dockerFile}...` + `Building custom docker image from ${options.dockerFile}...`, ); } try { dockerImage = await buildImage( options.dockerFile, options.dockerBuildCmdExtraArgs, - pluginInstance + pluginInstance, ); } finally { buildDockerImageProgress && buildDockerImageProgress.remove(); @@ -279,7 +279,7 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { // Prepare bind path depending on os platform const bindPath = dockerPathForWin( - await getBindPath(targetFolder, pluginInstance) + await getBindPath(targetFolder, pluginInstance), ); dockerCmd.push('docker', 'run', '--rm', '-v', `${bindPath}:/var/task:z`); @@ -297,7 +297,7 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { '-v', `${process.env.SSH_AUTH_SOCK}:/tmp/ssh_sock:z`, '-e', - 'SSH_AUTH_SOCK=/tmp/ssh_sock' + 'SSH_AUTH_SOCK=/tmp/ssh_sock', ); } @@ -306,13 +306,13 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { if (options.useDownloadCache) { const downloadCacheDir = path.join( getUserCachePath(options), - 'downloadCacheslspyc' + 'downloadCacheslspyc', ); if (log) { log.info(`Using download cache directory ${downloadCacheDir}`); } else { serverless.cli.log( - `Using download cache directory ${downloadCacheDir}` + `Using download cache directory ${downloadCacheDir}`, ); } fse.ensureDirSync(downloadCacheDir); @@ -320,7 +320,7 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { // Ugh, this is so ugly, but someone has to fix getBindPath in some other way (eg: make it use // its own temp file) fse.closeSync( - fse.openSync(path.join(downloadCacheDir, 'requirements.txt'), 'w') + fse.openSync(path.join(downloadCacheDir, 'requirements.txt'), 'w'), ); const windowsized = await getBindPath(downloadCacheDir, pluginInstance); // And now push it to a volume mount and to pip... @@ -375,7 +375,7 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { } else { throw new pluginInstance.serverless.classes.Error( 'dockerRunCmdExtraArgs option must be an array', - 'PYTHON_REQUIREMENTS_INVALID_DOCKER_EXTRA_ARGS' + 'PYTHON_REQUIREMENTS_INVALID_DOCKER_EXTRA_ARGS', ); } @@ -425,7 +425,7 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { : 'Please install it'; throw new pluginInstance.serverless.classes.Error( `${cmd} not found! ${advice}`, - 'PYTHON_REQUIREMENTS_COMMAND_NOT_FOUND' + 'PYTHON_REQUIREMENTS_COMMAND_NOT_FOUND', ); } @@ -434,7 +434,7 @@ async function installRequirements(targetFolder, pluginInstance, funcOptions) { `Running "${cmd} ${args.join(' ')}" failed with: "${e.stderrBuffer .toString() .trim()}"`, - 'PYTHON_REQUIREMENTS_DOCKER_COMMAND_FAILED' + 'PYTHON_REQUIREMENTS_DOCKER_COMMAND_FAILED', ); } @@ -528,7 +528,7 @@ function filterRequirementsFile(source, target, { options, serverless, log }) { log.warning(`Stripping -e flag from requirement ${req}`); } else { serverless.cli.log( - `Warning: Stripping -e flag from requirement ${req}` + `Warning: Stripping -e flag from requirement ${req}`, ); } } @@ -564,11 +564,11 @@ function copyVendors(vendorFolder, targetFolder, { serverless, log }) { if (log) { log.info( - `Copying vendor libraries from ${vendorFolder} to ${targetFolder}` + `Copying vendor libraries from ${vendorFolder} to ${targetFolder}`, ); } else { serverless.cli.log( - `Copying vendor libraries from ${vendorFolder} to ${targetFolder}...` + `Copying vendor libraries from ${vendorFolder} to ${targetFolder}...`, ); } @@ -622,7 +622,7 @@ function requirementsFileExists(servicePath, options, fileName) { async function installRequirementsIfNeeded( modulePath, funcOptions, - pluginInstance + pluginInstance, ) { const { servicePath, options, serverless } = pluginInstance; // Our source requirements, under our service path, and our module path (if specified) @@ -642,7 +642,7 @@ async function installRequirementsIfNeeded( requirementsTxtDirectory = path.join( servicePath, '.serverless', - modulePath + modulePath, ); } else { requirementsTxtDirectory = path.join(servicePath, '.serverless'); @@ -656,11 +656,11 @@ async function installRequirementsIfNeeded( if (!fse.existsSync(slsReqsTxt) || fse.statSync(slsReqsTxt).size == 0) { if (pluginInstance.log) { pluginInstance.log.info( - `Skipping empty output requirements.txt file from ${slsReqsTxt}` + `Skipping empty output requirements.txt file from ${slsReqsTxt}`, ); } else { serverless.cli.log( - `Skipping empty output requirements.txt file from ${slsReqsTxt}` + `Skipping empty output requirements.txt file from ${slsReqsTxt}`, ); } return false; @@ -674,7 +674,7 @@ async function installRequirementsIfNeeded( reqChecksum, requirementsTxtDirectory, options, - serverless + serverless, ); // Check if our static cache is present and is valid @@ -685,11 +685,11 @@ async function installRequirementsIfNeeded( ) { if (pluginInstance.log) { pluginInstance.log.info( - `Using static cache of requirements found at ${workingReqsFolder}` + `Using static cache of requirements found at ${workingReqsFolder}`, ); } else { serverless.cli.log( - `Using static cache of requirements found at ${workingReqsFolder} ...` + `Using static cache of requirements found at ${workingReqsFolder} ...`, ); } // We'll "touch" the folder, as to bring it to the start of the FIFO cache @@ -725,7 +725,10 @@ async function installRequirementsIfNeeded( // Then touch our ".completed_requirements" file so we know we can use this for static cache if (options.useStaticCache) { fse.closeSync( - fse.openSync(path.join(workingReqsFolder, '.completed_requirements'), 'w') + fse.openSync( + path.join(workingReqsFolder, '.completed_requirements'), + 'w', + ), ); } return workingReqsFolder; @@ -745,8 +748,8 @@ async function installAllRequirements() { let doneModules = []; const filteredFuncs = this.targetFuncs.filter((func) => (func.runtime || this.serverless.service.provider.runtime).match( - /^python.*/ - ) + /^python.*/, + ), ); for (const f of filteredFuncs) { @@ -759,14 +762,14 @@ async function installAllRequirements() { const reqsInstalledAt = await installRequirementsIfNeeded( f.module, f, - this + this, ); // Add modulePath into .serverless for each module so it's easier for injecting and for users to see where reqs are let modulePath = path.join( this.servicePath, '.serverless', `${f.module}`, - 'requirements' + 'requirements', ); // Only do if we didn't already do it if ( @@ -795,7 +798,7 @@ async function installAllRequirements() { let symlinkPath = path.join( this.servicePath, '.serverless', - `requirements` + `requirements`, ); // Only do if we didn't already do it if ( diff --git a/lib/pipenv.js b/lib/pipenv.js index 1099b651..f5d14e42 100644 --- a/lib/pipenv.js +++ b/lib/pipenv.js @@ -22,7 +22,7 @@ async function getPipenvVersion() { } else { throw new this.serverless.classes.Error( `Unable to parse pipenv version!`, - 'PYTHON_REQUIREMENTS_PIPENV_VERSION_ERROR' + 'PYTHON_REQUIREMENTS_PIPENV_VERSION_ERROR', ); } } catch (e) { @@ -32,7 +32,7 @@ async function getPipenvVersion() { if (stderrBufferContent.includes('command not found')) { throw new this.serverless.classes.Error( `pipenv not found! Install it according to the pipenv docs.`, - 'PYTHON_REQUIREMENTS_PIPENV_NOT_FOUND' + 'PYTHON_REQUIREMENTS_PIPENV_NOT_FOUND', ); } else { throw e; @@ -54,10 +54,10 @@ async function pipfileToRequirements() { let generateRequirementsProgress; if (this.progress && this.log) { generateRequirementsProgress = this.progress.get( - 'python-generate-requirements-pipfile' + 'python-generate-requirements-pipfile', ); generateRequirementsProgress.update( - 'Generating requirements.txt from Pipfile' + 'Generating requirements.txt from Pipfile', ); this.log.info('Generating requirements.txt from Pipfile'); } else { @@ -90,11 +90,11 @@ async function pipfileToRequirements() { // No previous Pipfile.lock, we will try to generate it here if (this.log) { this.log.warning( - 'No Pipfile.lock found! Review https://pipenv.pypa.io/en/latest/pipfile/ for recommendations.' + 'No Pipfile.lock found! Review https://pipenv.pypa.io/en/latest/pipfile/ for recommendations.', ); } else { this.serverless.cli.log( - 'WARNING: No Pipfile.lock found! Review https://pipenv.pypa.io/en/latest/pipfile/ for recommendations.' + 'WARNING: No Pipfile.lock found! Review https://pipenv.pypa.io/en/latest/pipfile/ for recommendations.', ); } await spawn('pipenv', ['lock'], { @@ -114,14 +114,14 @@ async function pipfileToRequirements() { ['lock', '--requirements', '--keep-outdated'], { cwd: this.servicePath, - } + }, ); } fse.ensureDirSync(path.join(this.servicePath, '.serverless')); fse.writeFileSync( path.join(this.servicePath, '.serverless/requirements.txt'), - removeEditableFlagFromRequirementsString(res.stdoutBuffer) + removeEditableFlagFromRequirementsString(res.stdoutBuffer), ); } finally { generateRequirementsProgress && generateRequirementsProgress.remove(); diff --git a/lib/poetry.js b/lib/poetry.js index e2943f84..3028daf0 100644 --- a/lib/poetry.js +++ b/lib/poetry.js @@ -19,7 +19,7 @@ async function poetryTomlToRequirements(modulePath, pluginInstance) { let generateRequirementsProgress; if (progress && log) { generateRequirementsProgress = progress.get( - 'python-generate-requirements-toml' + 'python-generate-requirements-toml', ); } @@ -39,7 +39,7 @@ async function poetryTomlToRequirements(modulePath, pluginInstance) { throw new serverless.classes.Error( 'poetry.lock file not found - set requirePoetryLockFile to false to ' + 'disable this error', - 'MISSING_REQUIRED_POETRY_LOCK' + 'MISSING_REQUIRED_POETRY_LOCK', ); } emitMsg('Generating poetry.lock and requirements.txt from pyproject.toml'); @@ -69,7 +69,7 @@ async function poetryTomlToRequirements(modulePath, pluginInstance) { ], { cwd: moduleProjectPath, - } + }, ); } catch (e) { if ( @@ -78,7 +78,7 @@ async function poetryTomlToRequirements(modulePath, pluginInstance) { ) { throw new serverless.classes.Error( `poetry not found! Install it according to the poetry docs.`, - 'PYTHON_REQUIREMENTS_POETRY_NOT_FOUND' + 'PYTHON_REQUIREMENTS_POETRY_NOT_FOUND', ); } throw e; @@ -95,12 +95,12 @@ async function poetryTomlToRequirements(modulePath, pluginInstance) { log.info('The generated file contains -e flags, removing them'); } else { serverless.cli.log( - 'The generated file contains -e flags, removing them...' + 'The generated file contains -e flags, removing them...', ); } fse.writeFileSync( sourceRequirements, - requirementsContents.replace(editableFlag, '') + requirementsContents.replace(editableFlag, ''), ); } @@ -108,7 +108,7 @@ async function poetryTomlToRequirements(modulePath, pluginInstance) { fse.moveSync( sourceRequirements, path.join(servicePath, '.serverless', modulePath, 'requirements.txt'), - { overwrite: true } + { overwrite: true }, ); } finally { generateRequirementsProgress && generateRequirementsProgress.remove(); diff --git a/lib/pyproject.js b/lib/pyproject.js index 227b57ba..41729df5 100644 --- a/lib/pyproject.js +++ b/lib/pyproject.js @@ -15,7 +15,7 @@ function pyprojectTomlToRequirements(modulePath, pluginInstance) { let generateRequirementsProgress; if (progress && log) { generateRequirementsProgress = progress.get( - 'python-generate-requirements-toml' + 'python-generate-requirements-toml', ); } else { serverless.cli.log('Generating requirements.txt from pyproject.toml'); @@ -32,13 +32,13 @@ function pyprojectTomlToRequirements(modulePath, pluginInstance) { for (const optionalDep of options.pyprojectWithGroups) { try { dependencies.push( - ...pyproject['project']['optional-dependencies'][optionalDep] + ...pyproject['project']['optional-dependencies'][optionalDep], ); } catch (e) { if (log) { log.warn( 'Optional dependency (%s) not found in pyproject.toml', - optionalDep + optionalDep, ); } } @@ -48,7 +48,7 @@ function pyprojectTomlToRequirements(modulePath, pluginInstance) { fse.ensureDirSync(path.join(servicePath, '.serverless')); fse.writeFileSync( path.join(servicePath, '.serverless/requirements.txt'), - dependencies.join('\n') + dependencies.join('\n'), ); } finally { generateRequirementsProgress && generateRequirementsProgress.remove(); diff --git a/lib/shared.js b/lib/shared.js index bebb3f09..d088d4dc 100644 --- a/lib/shared.js +++ b/lib/shared.js @@ -22,7 +22,7 @@ function checkForAndDeleteMaxCacheVersions({ serverless, options, log }) { // Get the list of our cache files const files = glob.sync( [path.join(getUserCachePath(options), '*_slspyc/')], - { mark: true } + { mark: true }, ); // Check if we have too many if (files.length >= options.staticCacheMaxVersions) { @@ -46,11 +46,11 @@ function checkForAndDeleteMaxCacheVersions({ serverless, options, log }) { // Log the number of cache files flushed if (log) { log.info( - `Removed ${items} items from cache because of staticCacheMaxVersions` + `Removed ${items} items from cache because of staticCacheMaxVersions`, ); } else { serverless.cli.log( - `Removed ${items} items from cache because of staticCacheMaxVersions` + `Removed ${items} items from cache because of staticCacheMaxVersions`, ); } } @@ -69,7 +69,7 @@ function getRequirementsWorkingPath( subfolder, requirementsTxtDirectory, options, - serverless + serverless, ) { // If we want to use the static cache if (options && options.useStaticCache) { diff --git a/lib/zip.js b/lib/zip.js index 3c21bbbf..741abeba 100644 --- a/lib/zip.js +++ b/lib/zip.js @@ -34,13 +34,13 @@ function addVendorHelper() { this.log.info(`Adding Python requirements helper to ${f.module}`); } else { this.serverless.cli.log( - `Adding Python requirements helper to ${f.module}...` + `Adding Python requirements helper to ${f.module}...`, ); } return fse.copyAsync( path.resolve(__dirname, '../unzip_requirements.py'), - path.join(this.servicePath, f.module, 'unzip_requirements.py') + path.join(this.servicePath, f.module, 'unzip_requirements.py'), ); }); } else { @@ -58,7 +58,7 @@ function addVendorHelper() { return fse.copyAsync( path.resolve(__dirname, '../unzip_requirements.py'), - path.join(this.servicePath, 'unzip_requirements.py') + path.join(this.servicePath, 'unzip_requirements.py'), ); } } @@ -82,15 +82,15 @@ function removeVendorHelper() { .map((f) => { if (this.log) { this.log.info( - `Removing Python requirements helper from ${f.module}` + `Removing Python requirements helper from ${f.module}`, ); } else { this.serverless.cli.log( - `Removing Python requirements helper from ${f.module}...` + `Removing Python requirements helper from ${f.module}...`, ); } return fse.removeAsync( - path.join(this.servicePath, f.module, 'unzip_requirements.py') + path.join(this.servicePath, f.module, 'unzip_requirements.py'), ); }); } else { @@ -100,7 +100,7 @@ function removeVendorHelper() { this.serverless.cli.log('Removing Python requirements helper...'); } return fse.removeAsync( - path.join(this.servicePath, 'unzip_requirements.py') + path.join(this.servicePath, 'unzip_requirements.py'), ); } } @@ -130,15 +130,15 @@ function packRequirements() { let packProgress; if (this.progress && this.log) { packProgress = this.progress.get( - `python-pack-requirements-${f.module}` + `python-pack-requirements-${f.module}`, ); packProgress.update( - `Zipping required Python packages for ${f.module}` + `Zipping required Python packages for ${f.module}`, ); this.log.info(`Zipping required Python packages for ${f.module}`); } else { this.serverless.cli.log( - `Zipping required Python packages for ${f.module}...` + `Zipping required Python packages for ${f.module}...`, ); } f.package.patterns.push(`${f.module}/.requirements.zip`); @@ -156,7 +156,7 @@ function packRequirements() { this.serverless.service.package.patterns.push('.requirements.zip'); return addTree(new JSZip(), '.serverless/requirements') .then((zip) => - writeZip(zip, path.join(this.servicePath, '.requirements.zip')) + writeZip(zip, path.join(this.servicePath, '.requirements.zip')), ) .finally(() => packProgress && packProgress.remove()); } diff --git a/lib/zipTree.js b/lib/zipTree.js index 1654f665..ac07b1f1 100644 --- a/lib/zipTree.js +++ b/lib/zipTree.js @@ -50,7 +50,7 @@ function writeZip(zip, targetPath) { zip .generateNodeStream(opts) .pipe(fse.createWriteStream(targetPath)) - .on('finish', resolve) + .on('finish', resolve), ).then(() => null); } @@ -74,9 +74,9 @@ function zipFile(zip, zipPath, bufferPromise, fileOpts) { // necessary to get the same hash when zipping the same content date: new Date(0), }, - fileOpts - ) - ) + fileOpts, + ), + ), ) .then(() => zip); } diff --git a/test.js b/test.js index cb069a33..d23e63ae 100644 --- a/test.js +++ b/test.js @@ -26,12 +26,12 @@ const mkCommand = options['env'] = Object.assign( { SLS_DEBUG: 'true' }, process.env, - options['env'] + options['env'], ); const { error, stdout, stderr, status } = crossSpawn.sync( cmd, args, - options + options, ); if (error && !options['noThrow']) { console.error(`Error running: ${quote([cmd, ...args])}`); // eslint-disable-line no-console @@ -41,7 +41,7 @@ const mkCommand = console.error('STDOUT: ', stdout.toString()); // eslint-disable-line no-console console.error('STDERR: ', stderr.toString()); // eslint-disable-line no-console throw new Error( - `${quote([cmd, ...args])} failed with status code ${status}` + `${quote([cmd, ...args])} failed with status code ${status}`, ); } return stdout && stdout.toString().trim(); @@ -84,7 +84,7 @@ const teardown = () => { git(['checkout', 'serverless.yml']); } catch (err) { console.error( - `At ${cwd} failed to checkout 'serverless.yml' with ${err}.` + `At ${cwd} failed to checkout 'serverless.yml' with ${err}.`, ); throw err; } @@ -130,7 +130,7 @@ const availablePythons = (() => { const mapping = {}; if (process.env.USE_PYTHON) { binaries.push( - ...process.env.USE_PYTHON.split(',').map((v) => v.toString().trim()) + ...process.env.USE_PYTHON.split(',').map((v) => v.toString().trim()), ); } else { // For running outside of CI @@ -1937,6 +1937,9 @@ test('pyproject.toml py3.10 packages with optional', async (t) => { t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); t.true(zipfiles.includes(`pytest${sep}__init__.py`), 'pytest is packaged'); - t.true(zipfiles.includes(`pytest-cov${sep}__init__.py`), 'pytest is packaged'); + t.true( + zipfiles.includes(`pytest-cov${sep}__init__.py`), + 'pytest is packaged', + ); t.end(); }); diff --git a/tests/base/fn2/requirements.txt b/tests/base/fn2/requirements.txt index eea18113..a46627b5 100644 --- a/tests/base/fn2/requirements.txt +++ b/tests/base/fn2/requirements.txt @@ -1 +1 @@ -dataclasses \ No newline at end of file +dataclasses From 8278d424124277c3a8473aa3edefe9db9b0640b2 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 19:36:48 -0500 Subject: [PATCH 15/20] fixed issue with prettier caused by precommit not reading in the config. restored missing tests --- .github/workflows/validate.yml | 5 + .pre-commit-config.yaml | 7 + package.json | 3 +- test.js | 3436 ++++++++++++++++---------------- 4 files changed, 1732 insertions(+), 1719 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 85e55fec..512b0964 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -71,13 +71,17 @@ jobs: run: | npm update --no-save npm update --save-dev --no-save + - name: Validate Prettier formatting run: npm run prettier-check:updated + - name: Validate ESLint rules run: npm run lint:updated + - name: Validate commit messages if: github.event.pull_request.base.repo.id == github.event.pull_request.head.repo.id run: npx commitlint -f master + - name: Validate changelog (if new version) run: | NEW_VERSION=`git diff -U0 master package.json | grep '"version": "' | tail -n 1 | grep -oE "[0-9]+\.[0-9]+\.[0-9]+"` || : @@ -85,5 +89,6 @@ jobs: then npx dump-release-notes-from-cc-changelog $NEW_VERSION fi + - name: Unit tests run: npm test diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b3606173..91e9aab5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,14 @@ repos: - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.56.0 + hooks: + - id: eslint - repo: https://github.com/pre-commit/mirrors-prettier rev: v3.1.0 hooks: - id: prettier + types_or: [css, javascript, html, json, markdown, yaml] + args: [--semi, --single-quote, --trailing-comma, all] + verbose: true diff --git a/package.json b/package.json index c420e4d6..0bde328b 100644 --- a/package.json +++ b/package.json @@ -128,6 +128,7 @@ }, "prettier": { "semi": true, - "singleQuote": true + "singleQuote": true, + "trailingComma": "all" } } diff --git a/test.js b/test.js index d23e63ae..04f12851 100644 --- a/test.js +++ b/test.js @@ -197,1724 +197,1724 @@ const canUseDocker = () => { // Skip if running on these platforms. const brokenOn = (...platforms) => platforms.indexOf(process.platform) != -1; -// test( -// 'dockerPrivateKey option correctly resolves docker command', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// const stdout = sls(['package'], { -// noThrow: true, -// env: { -// dockerizePip: true, -// dockerSsh: true, -// dockerPrivateKey: `${__dirname}${sep}tests${sep}base${sep}custom_ssh`, -// dockerImage: 'break the build to log the command', -// }, -// }); -// t.true( -// stdout.includes( -// `-v ${__dirname}${sep}tests${sep}base${sep}custom_ssh:/root/.ssh/custom_ssh:z` -// ), -// 'docker command properly resolved' -// ); -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test('default pythonBin can package flask with default options', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test('py3.10 packages have the same hash', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const fileHash = sha256File('.serverless/sls-py-req-test.zip'); -// sls(['package'], { env: {} }); -// t.equal( -// sha256File('.serverless/sls-py-req-test.zip'), -// fileHash, -// 'packages have the same hash' -// ); -// t.end(); -// }); - -// test('py3.10 can package flask with default options', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { pythonBin: getPythonBin(3) } }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test( -// 'py3.10 can package flask with hashes', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { -// fileName: 'requirements-w-hashes.txt', -// pythonBin: getPythonBin(3), -// }, -// }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.end(); -// }, -// { skip: brokenOn('win32') } -// ); - -// test('py3.10 can package flask with nested', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { -// fileName: 'requirements-w-nested.txt', -// pythonBin: getPythonBin(3), -// }, -// }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test('py3.10 can package flask with zip option', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); -// const zipfiles = await 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('py3.10 can package flask with slim option', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { slim: 'true', pythonBin: getPythonBin(3) } }); -// const zipfiles = await 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('py3.10 can package flask with slim & slimPatterns options', async (t) => { -// process.chdir('tests/base'); -// copySync('_slimPatterns.yml', 'slimPatterns.yml'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { slim: 'true' } }); -// const zipfiles = await 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("py3.10 doesn't package bottle with noDeploy option", async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// perl([ -// '-p', -// '-i.bak', -// '-e', -// 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', -// 'serverless.yml', -// ]); -// sls(['package'], { env: { pythonBin: getPythonBin(3) } }); -// const zipfiles = await 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.10 can package boto3 with editable', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { -// fileName: 'requirements-w-editable.txt', -// pythonBin: getPythonBin(3), -// }, -// }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.true( -// zipfiles.includes(`botocore${sep}__init__.py`), -// 'botocore is packaged' -// ); -// t.end(); -// }); - -// test( -// 'py3.10 can package flask with dockerizePip option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true' } }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test( -// 'py3.10 can package flask with slim & dockerizePip option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); -// const zipfiles = await 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')), -// [], -// '*.pyc files are NOT packaged' -// ); -// t.true( -// zipfiles.filter((filename) => filename.endsWith('__main__.py')).length > -// 0, -// '__main__.py files are packaged' -// ); -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test( -// 'py3.10 can package flask with slim & dockerizePip & slimPatterns options', -// async (t) => { -// process.chdir('tests/base'); -// copySync('_slimPatterns.yml', 'slimPatterns.yml'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); -// const zipfiles = await 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')), -// [], -// '*.pyc files are packaged' -// ); -// t.deepEqual( -// zipfiles.filter((filename) => filename.endsWith('__main__.py')), -// [], -// '__main__.py files are NOT packaged' -// ); -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test( -// 'py3.10 can package flask with zip & dockerizePip option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// const zippedReqs = await listRequirementsZipFiles( -// '.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.true( -// zippedReqs.includes(`flask/__init__.py`), -// 'flask is packaged in the .requirements.zip file' -// ); -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test( -// 'py3.10 can package flask with zip & slim & dockerizePip option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { dockerizePip: 'true', zip: 'true', slim: 'true' }, -// }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// const zippedReqs = await listRequirementsZipFiles( -// '.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.true( -// zippedReqs.includes(`flask/__init__.py`), -// 'flask is packaged in the .requirements.zip file' -// ); -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test('pipenv py3.10 can package flask with default options', async (t) => { -// process.chdir('tests/pipenv'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.false( -// zipfiles.includes(`pytest${sep}__init__.py`), -// 'dev-package pytest is NOT packaged' -// ); -// t.end(); -// }); - -// test('pipenv py3.10 can package flask with slim option', async (t) => { -// process.chdir('tests/pipenv'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { slim: 'true' } }); -// const zipfiles = await 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('pipenv py3.10 can package flask with slim & slimPatterns options', async (t) => { -// process.chdir('tests/pipenv'); - -// copySync('_slimPatterns.yml', 'slimPatterns.yml'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { slim: 'true' } }); -// const zipfiles = await 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('pipenv py3.10 can package flask with zip option', async (t) => { -// process.chdir('tests/pipenv'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); -// const zipfiles = await 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("pipenv py3.10 doesn't package bottle with noDeploy option", async (t) => { -// process.chdir('tests/pipenv'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// perl([ -// '-p', -// '-i.bak', -// '-e', -// 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', -// 'serverless.yml', -// ]); -// sls(['package'], { env: {} }); -// const zipfiles = await 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('non build pyproject.toml uses requirements.txt', async (t) => { -// process.chdir('tests/non_build_pyproject'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test('non poetry pyproject.toml without requirements.txt packages handler only', async (t) => { -// process.chdir('tests/non_poetry_pyproject'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`handler.py`), 'handler is packaged'); -// t.end(); -// }); - -// test('poetry py3.10 can package flask with default options', async (t) => { -// process.chdir('tests/poetry'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test('poetry py3.10 can package flask with slim option', async (t) => { -// process.chdir('tests/poetry'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { slim: 'true' } }); -// const zipfiles = await 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.10 can package flask with slim & slimPatterns options', async (t) => { -// process.chdir('tests/poetry'); - -// copySync('_slimPatterns.yml', 'slimPatterns.yml'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { slim: 'true' } }); -// const zipfiles = await 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.10 can package flask with zip option', async (t) => { -// process.chdir('tests/poetry'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); -// const zipfiles = await 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.10 doesn't package bottle with noDeploy option", async (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'], { env: {} }); -// const zipfiles = await 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.10 can package flask with zip option and no explicit include', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// perl(['-p', '-i.bak', '-e', 's/include://', 'serverless.yml']); -// perl(['-p', '-i.bak', '-e', 's/^.*handler.py.*$//', 'serverless.yml']); -// sls(['package'], { env: { zip: 'true' } }); -// const zipfiles = await 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('py3.10 can package lambda-decorators using vendor option', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { vendor: './vendor' } }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.true( -// zipfiles.includes(`lambda_decorators.py`), -// 'lambda_decorators.py is packaged' -// ); -// t.end(); -// }); - -// test( -// "Don't nuke execute perms", -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// const perm = '755'; - -// npm(['i', path]); -// perl([ -// '-p', -// '-i.bak', -// '-e', -// 's/(handler.py.*$)/$1\n - foobar/', -// 'serverless.yml', -// ]); -// writeFileSync(`foobar`, ''); -// chmodSync(`foobar`, perm); -// sls(['package'], { env: { vendor: './vendor' } }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.true( -// zipfiles.includes(`lambda_decorators.py`), -// 'lambda_decorators.py is packaged' -// ); -// t.true(zipfiles.includes(`foobar`), 'foobar is packaged'); - -// const zipfiles_with_metadata = await listZipFilesWithMetaData( -// '.serverless/sls-py-req-test.zip' -// ); -// t.true( -// zipfiles_with_metadata['foobar'].unixPermissions -// .toString(8) -// .slice(3, 6) === perm, -// 'foobar has retained its executable file permissions' -// ); - -// const flaskPerm = statSync('.serverless/requirements/bin/flask').mode; -// t.true( -// zipfiles_with_metadata['bin/flask'].unixPermissions === flaskPerm, -// 'bin/flask has retained its executable file permissions' -// ); - -// t.end(); -// }, -// { skip: process.platform === 'win32' } -// ); - -// test('py3.10 can package flask in a project with a space in it', async (t) => { -// copySync('tests/base', 'tests/base with a space'); -// process.chdir('tests/base with a space'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test( -// 'py3.10 can package flask in a project with a space in it with docker', -// async (t) => { -// copySync('tests/base', 'tests/base with a space'); -// process.chdir('tests/base with a space'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true' } }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test('py3.10 supports custom file name with fileName option', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// writeFileSync('puck', 'requests'); -// npm(['i', path]); -// sls(['package'], { env: { fileName: 'puck' } }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true( -// zipfiles.includes(`requests${sep}__init__.py`), -// 'requests is packaged' -// ); -// t.false(zipfiles.includes(`flask${sep}__init__.py`), 'flask is NOT packaged'); -// t.false(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is NOT packaged'); -// t.end(); -// }); - -// test("py3.10 doesn't package bottle with zip option", async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// perl([ -// '-p', -// '-i.bak', -// '-e', -// 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', -// 'serverless.yml', -// ]); -// sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// const zippedReqs = await listRequirementsZipFiles( -// '.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.true( -// zippedReqs.includes(`flask/__init__.py`), -// 'flask is packaged in the .requirements.zip file' -// ); -// t.false( -// zippedReqs.includes(`bottle.py`), -// 'bottle is NOT packaged in the .requirements.zip file' -// ); -// t.end(); -// }); - -// test('py3.10 can package flask with slim, slimPatterns & slimPatternsAppendDefaults=false options', async (t) => { -// process.chdir('tests/base'); -// copySync('_slimPatterns.yml', 'slimPatterns.yml'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, -// }); -// const zipfiles = await 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.10 can package flask with slim & dockerizePip & slimPatterns & slimPatternsAppendDefaults=false options', -// async (t) => { -// process.chdir('tests/base'); -// copySync('_slimPatterns.yml', 'slimPatterns.yml'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { -// dockerizePip: 'true', -// slim: 'true', -// slimPatternsAppendDefaults: 'false', -// }, -// }); -// const zipfiles = await 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(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test('pipenv py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { -// process.chdir('tests/pipenv'); -// copySync('_slimPatterns.yml', 'slimPatterns.yml'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); - -// sls(['package'], { -// env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, -// }); -// const zipfiles = await 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('poetry py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { -// process.chdir('tests/poetry'); -// copySync('_slimPatterns.yml', 'slimPatterns.yml'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); - -// sls(['package'], { -// env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, -// }); -// const zipfiles = await 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('poetry py3.10 can package flask with package individually option', async (t) => { -// process.chdir('tests/poetry_individually'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); - -// sls(['package'], { env: {} }); -// const zipfiles = await listZipFiles( -// '.serverless/module1-sls-py-req-test-dev-hello.zip' -// ); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test('py3.10 can package flask with package individually option', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { individually: 'true' } }); -// const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); -// t.false( -// zipfiles_hello.includes(`fn2${sep}__init__.py`), -// 'fn2 is NOT packaged in function hello' -// ); -// t.true( -// zipfiles_hello.includes('handler.py'), -// 'handler.py is packaged in function hello' -// ); -// t.false( -// zipfiles_hello.includes(`dataclasses.py`), -// 'dataclasses is NOT packaged in function hello' -// ); -// t.true( -// zipfiles_hello.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello' -// ); - -// const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); -// t.false( -// zipfiles_hello2.includes(`fn2${sep}__init__.py`), -// 'fn2 is NOT packaged in function hello2' -// ); -// t.true( -// zipfiles_hello2.includes('handler.py'), -// 'handler.py is packaged in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`dataclasses.py`), -// 'dataclasses is NOT packaged in function hello2' -// ); -// t.true( -// zipfiles_hello2.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello2' -// ); - -// const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); -// t.false( -// zipfiles_hello3.includes(`fn2${sep}__init__.py`), -// 'fn2 is NOT packaged in function hello3' -// ); -// t.true( -// zipfiles_hello3.includes('handler.py'), -// 'handler.py is packaged in function hello3' -// ); -// t.false( -// zipfiles_hello3.includes(`dataclasses.py`), -// 'dataclasses is NOT packaged in function hello3' -// ); -// t.false( -// zipfiles_hello3.includes(`flask${sep}__init__.py`), -// 'flask is NOT packaged in function hello3' -// ); - -// const zipfiles_hello4 = await listZipFiles( -// '.serverless/fn2-sls-py-req-test-dev-hello4.zip' -// ); -// t.false( -// zipfiles_hello4.includes(`fn2${sep}__init__.py`), -// 'fn2 is NOT packaged in function hello4' -// ); -// t.true( -// zipfiles_hello4.includes('fn2_handler.py'), -// 'fn2_handler is packaged in the zip-root in function hello4' -// ); -// t.true( -// zipfiles_hello4.includes(`dataclasses.py`), -// 'dataclasses is packaged in function hello4' -// ); -// t.false( -// zipfiles_hello4.includes(`flask${sep}__init__.py`), -// 'flask is NOT packaged in function hello4' -// ); - -// t.end(); -// }); - -// test('py3.10 can package flask with package individually & slim option', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { individually: 'true', slim: 'true' } }); -// const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); -// t.true( -// zipfiles_hello.includes('handler.py'), -// 'handler.py is packaged in function hello' -// ); -// t.deepEqual( -// zipfiles_hello.filter((filename) => filename.endsWith('.pyc')), -// [], -// 'no pyc files packaged in function hello' -// ); -// t.true( -// zipfiles_hello.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello' -// ); -// t.false( -// zipfiles_hello.includes(`dataclasses.py`), -// 'dataclasses is NOT packaged in function hello' -// ); - -// const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); -// t.true( -// zipfiles_hello2.includes('handler.py'), -// 'handler.py is packaged in function hello2' -// ); -// t.deepEqual( -// zipfiles_hello2.filter((filename) => filename.endsWith('.pyc')), -// [], -// 'no pyc files packaged in function hello2' -// ); -// t.true( -// zipfiles_hello2.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`dataclasses.py`), -// 'dataclasses is NOT packaged in function hello2' -// ); - -// const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); -// t.true( -// zipfiles_hello3.includes('handler.py'), -// 'handler.py is packaged in function hello3' -// ); -// t.deepEqual( -// zipfiles_hello3.filter((filename) => filename.endsWith('.pyc')), -// [], -// 'no pyc files packaged in function hello3' -// ); -// t.false( -// zipfiles_hello3.includes(`flask${sep}__init__.py`), -// 'flask is NOT packaged in function hello3' -// ); - -// const zipfiles_hello4 = await listZipFiles( -// '.serverless/fn2-sls-py-req-test-dev-hello4.zip' -// ); -// t.true( -// zipfiles_hello4.includes('fn2_handler.py'), -// 'fn2_handler is packaged in the zip-root in function hello4' -// ); -// t.true( -// zipfiles_hello4.includes(`dataclasses.py`), -// 'dataclasses is packaged in function hello4' -// ); -// t.false( -// zipfiles_hello4.includes(`flask${sep}__init__.py`), -// 'flask is NOT packaged in function hello4' -// ); -// t.deepEqual( -// zipfiles_hello4.filter((filename) => filename.endsWith('.pyc')), -// [], -// 'no pyc files packaged in function hello4' -// ); - -// t.end(); -// }); - -// test('py3.10 can package only requirements of module', async (t) => { -// process.chdir('tests/individually'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const zipfiles_hello = await listZipFiles( -// '.serverless/module1-sls-py-req-test-indiv-dev-hello1.zip' -// ); -// t.true( -// zipfiles_hello.includes('handler1.py'), -// 'handler1.py is packaged at root level in function hello1' -// ); -// t.false( -// zipfiles_hello.includes('handler2.py'), -// 'handler2.py is NOT packaged at root level in function hello1' -// ); -// t.true( -// zipfiles_hello.includes(`pyaml${sep}__init__.py`), -// 'pyaml is packaged in function hello1' -// ); -// t.true( -// zipfiles_hello.includes(`boto3${sep}__init__.py`), -// 'boto3 is packaged in function hello1' -// ); -// t.false( -// zipfiles_hello.includes(`flask${sep}__init__.py`), -// 'flask is NOT packaged in function hello1' -// ); - -// const zipfiles_hello2 = await listZipFiles( -// '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip' -// ); -// t.true( -// zipfiles_hello2.includes('handler2.py'), -// 'handler2.py is packaged at root level in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes('handler1.py'), -// 'handler1.py is NOT packaged at root level in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`pyaml${sep}__init__.py`), -// 'pyaml is NOT packaged in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`boto3${sep}__init__.py`), -// 'boto3 is NOT packaged in function hello2' -// ); -// t.true( -// zipfiles_hello2.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello2' -// ); - -// t.end(); -// }); - -// test('py3.10 can package lambda-decorators using vendor and invidiually option', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { individually: 'true', vendor: './vendor' } }); -// const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); -// t.true( -// zipfiles_hello.includes('handler.py'), -// 'handler.py is packaged at root level in function hello' -// ); -// t.true( -// zipfiles_hello.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello' -// ); -// t.true( -// zipfiles_hello.includes(`lambda_decorators.py`), -// 'lambda_decorators.py is packaged in function hello' -// ); -// t.false( -// zipfiles_hello.includes(`dataclasses.py`), -// 'dataclasses is NOT packaged in function hello' -// ); - -// const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); -// t.true( -// zipfiles_hello2.includes('handler.py'), -// 'handler.py is packaged at root level in function hello2' -// ); -// t.true( -// zipfiles_hello2.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello2' -// ); -// t.true( -// zipfiles_hello2.includes(`lambda_decorators.py`), -// 'lambda_decorators.py is packaged in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`dataclasses.py`), -// 'dataclasses is NOT packaged in function hello2' -// ); - -// const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); -// t.true( -// zipfiles_hello3.includes('handler.py'), -// 'handler.py is packaged at root level in function hello3' -// ); -// t.false( -// zipfiles_hello3.includes(`flask${sep}__init__.py`), -// 'flask is NOT packaged in function hello3' -// ); -// t.false( -// zipfiles_hello3.includes(`lambda_decorators.py`), -// 'lambda_decorators.py is NOT packaged in function hello3' -// ); -// t.false( -// zipfiles_hello3.includes(`dataclasses.py`), -// 'dataclasses is NOT packaged in function hello3' -// ); - -// const zipfiles_hello4 = await listZipFiles( -// '.serverless/fn2-sls-py-req-test-dev-hello4.zip' -// ); -// t.true( -// zipfiles_hello4.includes('fn2_handler.py'), -// 'fn2_handler is packaged in the zip-root in function hello4' -// ); -// t.true( -// zipfiles_hello4.includes(`dataclasses.py`), -// 'dataclasses is packaged in function hello4' -// ); -// t.false( -// zipfiles_hello4.includes(`flask${sep}__init__.py`), -// 'flask is NOT packaged in function hello4' -// ); -// t.end(); -// }); - -// test( -// "Don't nuke execute perms when using individually", -// async (t) => { -// process.chdir('tests/individually'); -// const path = npm(['pack', '../..']); -// const perm = '755'; -// writeFileSync(`module1${sep}foobar`, ''); -// chmodSync(`module1${sep}foobar`, perm); - -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const zipfiles_hello1 = await listZipFilesWithMetaData( -// '.serverless/hello1.zip' -// ); - -// t.true( -// zipfiles_hello1['module1/foobar'].unixPermissions -// .toString(8) -// .slice(3, 6) === perm, -// 'foobar has retained its executable file permissions' -// ); - -// const zipfiles_hello2 = await listZipFilesWithMetaData( -// '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip' -// ); -// const flaskPerm = statSync( -// '.serverless/module2/requirements/bin/flask' -// ).mode; - -// t.true( -// zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, -// 'bin/flask has retained its executable file permissions' -// ); - -// t.end(); -// }, -// { skip: process.platform === 'win32' } -// ); - -// test( -// "Don't nuke execute perms when using individually w/docker", -// async (t) => { -// process.chdir('tests/individually'); -// const path = npm(['pack', '../..']); -// const perm = '755'; -// writeFileSync(`module1${sep}foobar`, '', { mode: perm }); -// chmodSync(`module1${sep}foobar`, perm); - -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true' } }); -// const zipfiles_hello = await listZipFilesWithMetaData( -// '.serverless/hello1.zip' -// ); - -// t.true( -// zipfiles_hello['module1/foobar'].unixPermissions -// .toString(8) -// .slice(3, 6) === perm, -// 'foobar has retained its executable file permissions' -// ); - -// const zipfiles_hello2 = await listZipFilesWithMetaData( -// '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip' -// ); -// const flaskPerm = statSync( -// '.serverless/module2/requirements/bin/flask' -// ).mode; - -// t.true( -// zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, -// 'bin/flask has retained its executable file permissions' -// ); - -// t.end(); -// }, -// { skip: !canUseDocker() || process.platform === 'win32' } -// ); - -// test( -// 'py3.10 can package flask running in docker with module runtime & architecture of function', -// async (t) => { -// process.chdir('tests/individually_mixed_runtime'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); - -// sls(['package'], { -// env: { dockerizePip: 'true' }, -// }); - -// const zipfiles_hello2 = await listZipFiles( -// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' -// ); -// t.true( -// zipfiles_hello2.includes('handler2.py'), -// 'handler2.py is packaged at root level in function hello2' -// ); -// t.true( -// zipfiles_hello2.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello2' -// ); -// }, -// { -// skip: !canUseDocker() || process.platform === 'win32', -// } -// ); - -// test( -// 'py3.10 can package flask succesfully when using mixed architecture, docker and zipping', -// async (t) => { -// process.chdir('tests/individually_mixed_runtime'); -// const path = npm(['pack', '../..']); - -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); - -// const zipfiles_hello = await listZipFiles('.serverless/hello1.zip'); -// t.true( -// zipfiles_hello.includes(`module1${sep}handler1.ts`), -// 'handler1.ts is packaged in module dir for hello1' -// ); -// t.false( -// zipfiles_hello.includes('handler2.py'), -// 'handler2.py is NOT packaged at root level in function hello1' -// ); -// t.false( -// zipfiles_hello.includes(`flask${sep}__init__.py`), -// 'flask is NOT packaged in function hello1' -// ); - -// const zipfiles_hello2 = await listZipFiles( -// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' -// ); -// const zippedReqs = await listRequirementsZipFiles( -// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' -// ); -// t.true( -// zipfiles_hello2.includes('handler2.py'), -// 'handler2.py is packaged at root level in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`module1${sep}handler1.ts`), -// 'handler1.ts is NOT included at module1 level in hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`pyaml${sep}__init__.py`), -// 'pyaml is NOT packaged in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`boto3${sep}__init__.py`), -// 'boto3 is NOT included in zipfile' -// ); -// t.true( -// zippedReqs.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello2 in requirements.zip' -// ); - -// t.end(); -// }, -// { skip: !canUseDocker() || process.platform === 'win32' } -// ); - -// test( -// 'py3.10 can package flask running in docker with module runtime & architecture of function', -// async (t) => { -// process.chdir('tests/individually_mixed_runtime'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); - -// sls(['package'], { -// env: { dockerizePip: 'true' }, -// }); - -// const zipfiles_hello2 = await listZipFiles( -// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' -// ); -// t.true( -// zipfiles_hello2.includes('handler2.py'), -// 'handler2.py is packaged at root level in function hello2' -// ); -// t.true( -// zipfiles_hello2.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello2' -// ); -// }, -// { -// skip: !canUseDocker() || process.platform === 'win32', -// } -// ); - -// test( -// 'py3.10 can package flask succesfully when using mixed architecture, docker and zipping', -// async (t) => { -// process.chdir('tests/individually_mixed_runtime'); -// const path = npm(['pack', '../..']); - -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); - -// const zipfiles_hello = await listZipFiles('.serverless/hello1.zip'); -// t.true( -// zipfiles_hello.includes(`module1${sep}handler1.ts`), -// 'handler1.ts is packaged in module dir for hello1' -// ); -// t.false( -// zipfiles_hello.includes('handler2.py'), -// 'handler2.py is NOT packaged at root level in function hello1' -// ); -// t.false( -// zipfiles_hello.includes(`flask${sep}__init__.py`), -// 'flask is NOT packaged in function hello1' -// ); - -// const zipfiles_hello2 = await listZipFiles( -// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' -// ); -// const zippedReqs = await listRequirementsZipFiles( -// '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip' -// ); -// t.true( -// zipfiles_hello2.includes('handler2.py'), -// 'handler2.py is packaged at root level in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`module1${sep}handler1.ts`), -// 'handler1.ts is NOT included at module1 level in hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`pyaml${sep}__init__.py`), -// 'pyaml is NOT packaged in function hello2' -// ); -// t.false( -// zipfiles_hello2.includes(`boto3${sep}__init__.py`), -// 'boto3 is NOT included in zipfile' -// ); -// t.true( -// zippedReqs.includes(`flask${sep}__init__.py`), -// 'flask is packaged in function hello2 in requirements.zip' -// ); - -// t.end(); -// }, -// { skip: !canUseDocker() || process.platform === 'win32' } -// ); - -// test( -// 'py3.10 uses download cache by default option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const cachepath = getUserCachePath(); -// t.true( -// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), -// 'cache directory exists' -// ); -// t.end(); -// }, -// { skip: true } -// ); - -// test( -// 'py3.10 uses download cache by default', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { cacheLocation: '.requirements-cache' } }); -// t.true( -// pathExistsSync(`.requirements-cache${sep}downloadCacheslspyc${sep}http`), -// 'cache directory exists' -// ); -// t.end(); -// }, -// { skip: true } -// ); - -// test( -// 'py3.10 uses download cache with dockerizePip option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true' } }); -// const cachepath = getUserCachePath(); -// t.true( -// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), -// 'cache directory exists' -// ); -// t.end(); -// }, -// // { skip: !canUseDocker() || brokenOn('win32') } -// { skip: true } -// ); - -// test( -// 'py3.10 uses download cache with dockerizePip by default option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { dockerizePip: 'true', cacheLocation: '.requirements-cache' }, -// }); -// t.true( -// pathExistsSync(`.requirements-cache${sep}downloadCacheslspyc${sep}http`), -// 'cache directory exists' -// ); -// t.end(); -// }, -// // { skip: !canUseDocker() || brokenOn('win32') } -// { skip: true } -// ); - -// test( -// 'py3.10 uses static and download cache', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const cachepath = getUserCachePath(); -// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); -// const arch = 'x86_64'; -// t.true( -// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), -// 'http exists in download-cache' -// ); -// t.true( -// pathExistsSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` -// ), -// 'flask exists in static-cache' -// ); -// t.end(); -// }, -// { skip: true } -// ); - -// test( -// 'py3.10 uses static and download cache with dockerizePip option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true' } }); -// const cachepath = getUserCachePath(); -// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); -// const arch = 'x86_64'; -// t.true( -// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), -// 'http exists in download-cache' -// ); -// t.true( -// pathExistsSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` -// ), -// 'flask exists in static-cache' -// ); -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test('py3.10 uses static cache', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: {} }); -// const cachepath = getUserCachePath(); -// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); -// const arch = 'x86_64'; -// t.true( -// pathExistsSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` -// ), -// 'flask exists in static-cache' -// ); -// t.true( -// pathExistsSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements` -// ), -// '.completed_requirements exists in static-cache' -// ); - -// // py3.10 checking that static cache actually pulls from cache (by poisoning it) -// writeFileSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, -// 'injected new file into static cache folder' -// ); -// sls(['package'], { env: {} }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true( -// zipfiles.includes('injected_file_is_bad_form'), -// "static cache is really used when running 'sls package' again" -// ); - -// t.end(); -// }); - -// test('py3.10 uses static cache with cacheLocation option', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// const cachepath = '.requirements-cache'; -// sls(['package'], { env: { cacheLocation: cachepath } }); -// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); -// const arch = 'x86_64'; -// t.true( -// pathExistsSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` -// ), -// 'flask exists in static-cache' -// ); -// t.true( -// pathExistsSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements` -// ), -// '.completed_requirements exists in static-cache' -// ); -// t.end(); -// }); - -// test( -// 'py3.10 uses static cache with dockerizePip & slim option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); -// const cachepath = getUserCachePath(); -// const cacheFolderHash = sha256Path('.serverless/requirements.txt'); -// const arch = 'x86_64'; -// t.true( -// pathExistsSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask` -// ), -// 'flask exists in static-cache' -// ); -// t.true( -// pathExistsSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements` -// ), -// '.completed_requirements exists in static-cache' -// ); - -// // py3.10 checking that static cache actually pulls from cache (by poisoning it) -// writeFileSync( -// `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, -// 'injected new file into static cache folder' -// ); -// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true( -// zipfiles.includes('injected_file_is_bad_form'), -// "static cache is really used when running 'sls package' again" -// ); -// t.deepEqual( -// zipfiles.filter((filename) => filename.endsWith('.pyc')), -// [], -// 'no pyc files are packaged' -// ); - -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test( -// 'py3.10 uses download cache with dockerizePip & slim option', -// async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); -// const cachepath = getUserCachePath(); -// t.true( -// pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), -// 'http exists in download-cache' -// ); - -// const zipfiles = await 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 are packaged' -// ); - -// t.end(); -// }, -// { skip: !canUseDocker() || brokenOn('win32') } -// ); - -// test('py3.10 can ignore functions defined with `image`', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { individually: 'true' } }); -// t.true(pathExistsSync('.serverless/hello.zip'), 'function hello is packaged'); -// t.true( -// pathExistsSync('.serverless/hello2.zip'), -// 'function hello2 is packaged' -// ); -// t.true( -// pathExistsSync('.serverless/hello3.zip'), -// 'function hello3 is packaged' -// ); -// t.true( -// pathExistsSync('.serverless/hello4.zip'), -// 'function hello4 is packaged' -// ); -// t.false( -// pathExistsSync('.serverless/hello5.zip'), -// 'function hello5 is not packaged' -// ); - -// t.end(); -// }); - -// test('poetry py3.10 fails packaging if poetry.lock is missing and flag requirePoetryLockFile is set to true', async (t) => { -// copySync('tests/poetry', 'tests/base with a space'); -// process.chdir('tests/base with a space'); -// removeSync('poetry.lock'); - -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// const stdout = sls(['package'], { -// env: { requirePoetryLockFile: 'true', slim: 'true' }, -// noThrow: true, -// }); -// t.true( -// stdout.includes( -// 'poetry.lock file not found - set requirePoetryLockFile to false to disable this error' -// ), -// 'flag works and error is properly reported' -// ); -// t.end(); -// }); - -// test('works with provider.runtime not being python', async (t) => { -// process.chdir('tests/base'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { env: { runtime: 'nodejs12.x' } }); -// t.true( -// pathExistsSync('.serverless/sls-py-req-test.zip'), -// 'sls-py-req-test is packaged' -// ); -// t.end(); -// }); - -// test('poetry py3.10 packages additional optional packages', async (t) => { -// process.chdir('tests/poetry_packages'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { -// poetryWithGroups: 'poetryWithGroups', -// }, -// }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); -// t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test('poetry py3.10 skips additional optional packages specified in withoutGroups', async (t) => { -// process.chdir('tests/poetry_packages'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { -// poetryWithGroups: 'poetryWithGroups', -// poetryWithoutGroups: 'poetryWithoutGroups', -// }, -// }); -// const zipfiles = await 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.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test('poetry py3.10 only installs optional packages specified in onlyGroups', async (t) => { -// process.chdir('tests/poetry_packages'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['package'], { -// env: { -// poetryOnlyGroups: 'poetryOnlyGroups', -// }, -// }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.false(zipfiles.includes(`flask${sep}__init__.py`), 'flask is NOT packaged'); -// t.false(zipfiles.includes(`bottle.py`), 'bottle is NOT packaged'); -// t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); -// t.end(); -// }); - -// test('py3.10 injects dependencies into `package` folder when using scaleway provider', async (t) => { -// process.chdir('tests/scaleway_provider'); -// const path = npm(['pack', '../..']); -// npm(['i', path]); -// sls(['plugin', 'install', '-n', 'serverless-scaleway-functions'], { -// env: {}, -// }); -// sls(['package'], { env: {} }); -// const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); -// t.true( -// zipfiles.includes(`package${sep}flask${sep}__init__.py`), -// 'flask is packaged' -// ); -// t.true( -// zipfiles.includes(`package${sep}boto3${sep}__init__.py`), -// 'boto3 is packaged' -// ); -// t.end(); -// }); +test( + 'dockerPrivateKey option correctly resolves docker command', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + const stdout = sls(['package'], { + noThrow: true, + env: { + dockerizePip: true, + dockerSsh: true, + dockerPrivateKey: `${__dirname}${sep}tests${sep}base${sep}custom_ssh`, + dockerImage: 'break the build to log the command', + }, + }); + t.true( + stdout.includes( + `-v ${__dirname}${sep}tests${sep}base${sep}custom_ssh:/root/.ssh/custom_ssh:z`, + ), + 'docker command properly resolved', + ); + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test('default pythonBin can package flask with default options', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test('py3.10 packages have the same hash', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const fileHash = sha256File('.serverless/sls-py-req-test.zip'); + sls(['package'], { env: {} }); + t.equal( + sha256File('.serverless/sls-py-req-test.zip'), + fileHash, + 'packages have the same hash', + ); + t.end(); +}); + +test('py3.10 can package flask with default options', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { pythonBin: getPythonBin(3) } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test( + 'py3.10 can package flask with hashes', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { + fileName: 'requirements-w-hashes.txt', + pythonBin: getPythonBin(3), + }, + }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.end(); + }, + { skip: brokenOn('win32') }, +); + +test('py3.10 can package flask with nested', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { + fileName: 'requirements-w-nested.txt', + pythonBin: getPythonBin(3), + }, + }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test('py3.10 can package flask with zip option', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); + const zipfiles = await 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('py3.10 can package flask with slim option', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { slim: 'true', pythonBin: getPythonBin(3) } }); + const zipfiles = await 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('py3.10 can package flask with slim & slimPatterns options', async (t) => { + process.chdir('tests/base'); + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { slim: 'true' } }); + const zipfiles = await 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("py3.10 doesn't package bottle with noDeploy option", async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + perl([ + '-p', + '-i.bak', + '-e', + 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', + 'serverless.yml', + ]); + sls(['package'], { env: { pythonBin: getPythonBin(3) } }); + const zipfiles = await 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.10 can package boto3 with editable', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { + fileName: 'requirements-w-editable.txt', + pythonBin: getPythonBin(3), + }, + }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.true( + zipfiles.includes(`botocore${sep}__init__.py`), + 'botocore is packaged', + ); + t.end(); +}); + +test( + 'py3.10 can package flask with dockerizePip option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true' } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test( + 'py3.10 can package flask with slim & dockerizePip option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); + const zipfiles = await 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')), + [], + '*.pyc files are NOT packaged', + ); + t.true( + zipfiles.filter((filename) => filename.endsWith('__main__.py')).length > + 0, + '__main__.py files are packaged', + ); + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test( + 'py3.10 can package flask with slim & dockerizePip & slimPatterns options', + async (t) => { + process.chdir('tests/base'); + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); + const zipfiles = await 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')), + [], + '*.pyc files are packaged', + ); + t.deepEqual( + zipfiles.filter((filename) => filename.endsWith('__main__.py')), + [], + '__main__.py files are NOT packaged', + ); + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test( + 'py3.10 can package flask with zip & dockerizePip option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + const zippedReqs = await listRequirementsZipFiles( + '.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.true( + zippedReqs.includes(`flask/__init__.py`), + 'flask is packaged in the .requirements.zip file', + ); + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test( + 'py3.10 can package flask with zip & slim & dockerizePip option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { dockerizePip: 'true', zip: 'true', slim: 'true' }, + }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + const zippedReqs = await listRequirementsZipFiles( + '.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.true( + zippedReqs.includes(`flask/__init__.py`), + 'flask is packaged in the .requirements.zip file', + ); + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test('pipenv py3.10 can package flask with default options', async (t) => { + process.chdir('tests/pipenv'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.false( + zipfiles.includes(`pytest${sep}__init__.py`), + 'dev-package pytest is NOT packaged', + ); + t.end(); +}); + +test('pipenv py3.10 can package flask with slim option', async (t) => { + process.chdir('tests/pipenv'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { slim: 'true' } }); + const zipfiles = await 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('pipenv py3.10 can package flask with slim & slimPatterns options', async (t) => { + process.chdir('tests/pipenv'); + + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { slim: 'true' } }); + const zipfiles = await 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('pipenv py3.10 can package flask with zip option', async (t) => { + process.chdir('tests/pipenv'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); + const zipfiles = await 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("pipenv py3.10 doesn't package bottle with noDeploy option", async (t) => { + process.chdir('tests/pipenv'); + const path = npm(['pack', '../..']); + npm(['i', path]); + perl([ + '-p', + '-i.bak', + '-e', + 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', + 'serverless.yml', + ]); + sls(['package'], { env: {} }); + const zipfiles = await 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('non build pyproject.toml uses requirements.txt', async (t) => { + process.chdir('tests/non_build_pyproject'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test('non poetry pyproject.toml without requirements.txt packages handler only', async (t) => { + process.chdir('tests/non_poetry_pyproject'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`handler.py`), 'handler is packaged'); + t.end(); +}); + +test('poetry py3.10 can package flask with default options', async (t) => { + process.chdir('tests/poetry'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test('poetry py3.10 can package flask with slim option', async (t) => { + process.chdir('tests/poetry'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { slim: 'true' } }); + const zipfiles = await 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.10 can package flask with slim & slimPatterns options', async (t) => { + process.chdir('tests/poetry'); + + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { slim: 'true' } }); + const zipfiles = await 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.10 can package flask with zip option', async (t) => { + process.chdir('tests/poetry'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); + const zipfiles = await 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.10 doesn't package bottle with noDeploy option", async (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'], { env: {} }); + const zipfiles = await 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.10 can package flask with zip option and no explicit include', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + perl(['-p', '-i.bak', '-e', 's/include://', 'serverless.yml']); + perl(['-p', '-i.bak', '-e', 's/^.*handler.py.*$//', 'serverless.yml']); + sls(['package'], { env: { zip: 'true' } }); + const zipfiles = await 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('py3.10 can package lambda-decorators using vendor option', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { vendor: './vendor' } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.true( + zipfiles.includes(`lambda_decorators.py`), + 'lambda_decorators.py is packaged', + ); + t.end(); +}); + +test( + "Don't nuke execute perms", + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + const perm = '755'; + + npm(['i', path]); + perl([ + '-p', + '-i.bak', + '-e', + 's/(handler.py.*$)/$1\n - foobar/', + 'serverless.yml', + ]); + writeFileSync(`foobar`, ''); + chmodSync(`foobar`, perm); + sls(['package'], { env: { vendor: './vendor' } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.true( + zipfiles.includes(`lambda_decorators.py`), + 'lambda_decorators.py is packaged', + ); + t.true(zipfiles.includes(`foobar`), 'foobar is packaged'); + + const zipfiles_with_metadata = await listZipFilesWithMetaData( + '.serverless/sls-py-req-test.zip', + ); + t.true( + zipfiles_with_metadata['foobar'].unixPermissions + .toString(8) + .slice(3, 6) === perm, + 'foobar has retained its executable file permissions', + ); + + const flaskPerm = statSync('.serverless/requirements/bin/flask').mode; + t.true( + zipfiles_with_metadata['bin/flask'].unixPermissions === flaskPerm, + 'bin/flask has retained its executable file permissions', + ); + + t.end(); + }, + { skip: process.platform === 'win32' }, +); + +test('py3.10 can package flask in a project with a space in it', async (t) => { + copySync('tests/base', 'tests/base with a space'); + process.chdir('tests/base with a space'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test( + 'py3.10 can package flask in a project with a space in it with docker', + async (t) => { + copySync('tests/base', 'tests/base with a space'); + process.chdir('tests/base with a space'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true' } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test('py3.10 supports custom file name with fileName option', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + writeFileSync('puck', 'requests'); + npm(['i', path]); + sls(['package'], { env: { fileName: 'puck' } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true( + zipfiles.includes(`requests${sep}__init__.py`), + 'requests is packaged', + ); + t.false(zipfiles.includes(`flask${sep}__init__.py`), 'flask is NOT packaged'); + t.false(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is NOT packaged'); + t.end(); +}); + +test("py3.10 doesn't package bottle with zip option", async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + perl([ + '-p', + '-i.bak', + '-e', + 's/(pythonRequirements:$)/\\1\\n noDeploy: [bottle]/', + 'serverless.yml', + ]); + sls(['package'], { env: { zip: 'true', pythonBin: getPythonBin(3) } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + const zippedReqs = await listRequirementsZipFiles( + '.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.true( + zippedReqs.includes(`flask/__init__.py`), + 'flask is packaged in the .requirements.zip file', + ); + t.false( + zippedReqs.includes(`bottle.py`), + 'bottle is NOT packaged in the .requirements.zip file', + ); + t.end(); +}); + +test('py3.10 can package flask with slim, slimPatterns & slimPatternsAppendDefaults=false options', async (t) => { + process.chdir('tests/base'); + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, + }); + const zipfiles = await 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.10 can package flask with slim & dockerizePip & slimPatterns & slimPatternsAppendDefaults=false options', + async (t) => { + process.chdir('tests/base'); + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { + dockerizePip: 'true', + slim: 'true', + slimPatternsAppendDefaults: 'false', + }, + }); + const zipfiles = await 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(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test('pipenv py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { + process.chdir('tests/pipenv'); + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + + sls(['package'], { + env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, + }); + const zipfiles = await 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('poetry py3.10 can package flask with slim & slimPatterns & slimPatternsAppendDefaults=false option', async (t) => { + process.chdir('tests/poetry'); + copySync('_slimPatterns.yml', 'slimPatterns.yml'); + const path = npm(['pack', '../..']); + npm(['i', path]); + + sls(['package'], { + env: { slim: 'true', slimPatternsAppendDefaults: 'false' }, + }); + const zipfiles = await 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('poetry py3.10 can package flask with package individually option', async (t) => { + process.chdir('tests/poetry_individually'); + const path = npm(['pack', '../..']); + npm(['i', path]); + + sls(['package'], { env: {} }); + const zipfiles = await listZipFiles( + '.serverless/module1-sls-py-req-test-dev-hello.zip', + ); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test('py3.10 can package flask with package individually option', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { individually: 'true' } }); + const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); + t.false( + zipfiles_hello.includes(`fn2${sep}__init__.py`), + 'fn2 is NOT packaged in function hello', + ); + t.true( + zipfiles_hello.includes('handler.py'), + 'handler.py is packaged in function hello', + ); + t.false( + zipfiles_hello.includes(`dataclasses.py`), + 'dataclasses is NOT packaged in function hello', + ); + t.true( + zipfiles_hello.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello', + ); + + const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); + t.false( + zipfiles_hello2.includes(`fn2${sep}__init__.py`), + 'fn2 is NOT packaged in function hello2', + ); + t.true( + zipfiles_hello2.includes('handler.py'), + 'handler.py is packaged in function hello2', + ); + t.false( + zipfiles_hello2.includes(`dataclasses.py`), + 'dataclasses is NOT packaged in function hello2', + ); + t.true( + zipfiles_hello2.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello2', + ); + + const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); + t.false( + zipfiles_hello3.includes(`fn2${sep}__init__.py`), + 'fn2 is NOT packaged in function hello3', + ); + t.true( + zipfiles_hello3.includes('handler.py'), + 'handler.py is packaged in function hello3', + ); + t.false( + zipfiles_hello3.includes(`dataclasses.py`), + 'dataclasses is NOT packaged in function hello3', + ); + t.false( + zipfiles_hello3.includes(`flask${sep}__init__.py`), + 'flask is NOT packaged in function hello3', + ); + + const zipfiles_hello4 = await listZipFiles( + '.serverless/fn2-sls-py-req-test-dev-hello4.zip', + ); + t.false( + zipfiles_hello4.includes(`fn2${sep}__init__.py`), + 'fn2 is NOT packaged in function hello4', + ); + t.true( + zipfiles_hello4.includes('fn2_handler.py'), + 'fn2_handler is packaged in the zip-root in function hello4', + ); + t.true( + zipfiles_hello4.includes(`dataclasses.py`), + 'dataclasses is packaged in function hello4', + ); + t.false( + zipfiles_hello4.includes(`flask${sep}__init__.py`), + 'flask is NOT packaged in function hello4', + ); + + t.end(); +}); + +test('py3.10 can package flask with package individually & slim option', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { individually: 'true', slim: 'true' } }); + const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); + t.true( + zipfiles_hello.includes('handler.py'), + 'handler.py is packaged in function hello', + ); + t.deepEqual( + zipfiles_hello.filter((filename) => filename.endsWith('.pyc')), + [], + 'no pyc files packaged in function hello', + ); + t.true( + zipfiles_hello.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello', + ); + t.false( + zipfiles_hello.includes(`dataclasses.py`), + 'dataclasses is NOT packaged in function hello', + ); + + const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); + t.true( + zipfiles_hello2.includes('handler.py'), + 'handler.py is packaged in function hello2', + ); + t.deepEqual( + zipfiles_hello2.filter((filename) => filename.endsWith('.pyc')), + [], + 'no pyc files packaged in function hello2', + ); + t.true( + zipfiles_hello2.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello2', + ); + t.false( + zipfiles_hello2.includes(`dataclasses.py`), + 'dataclasses is NOT packaged in function hello2', + ); + + const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); + t.true( + zipfiles_hello3.includes('handler.py'), + 'handler.py is packaged in function hello3', + ); + t.deepEqual( + zipfiles_hello3.filter((filename) => filename.endsWith('.pyc')), + [], + 'no pyc files packaged in function hello3', + ); + t.false( + zipfiles_hello3.includes(`flask${sep}__init__.py`), + 'flask is NOT packaged in function hello3', + ); + + const zipfiles_hello4 = await listZipFiles( + '.serverless/fn2-sls-py-req-test-dev-hello4.zip', + ); + t.true( + zipfiles_hello4.includes('fn2_handler.py'), + 'fn2_handler is packaged in the zip-root in function hello4', + ); + t.true( + zipfiles_hello4.includes(`dataclasses.py`), + 'dataclasses is packaged in function hello4', + ); + t.false( + zipfiles_hello4.includes(`flask${sep}__init__.py`), + 'flask is NOT packaged in function hello4', + ); + t.deepEqual( + zipfiles_hello4.filter((filename) => filename.endsWith('.pyc')), + [], + 'no pyc files packaged in function hello4', + ); + + t.end(); +}); + +test('py3.10 can package only requirements of module', async (t) => { + process.chdir('tests/individually'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const zipfiles_hello = await listZipFiles( + '.serverless/module1-sls-py-req-test-indiv-dev-hello1.zip', + ); + t.true( + zipfiles_hello.includes('handler1.py'), + 'handler1.py is packaged at root level in function hello1', + ); + t.false( + zipfiles_hello.includes('handler2.py'), + 'handler2.py is NOT packaged at root level in function hello1', + ); + t.true( + zipfiles_hello.includes(`pyaml${sep}__init__.py`), + 'pyaml is packaged in function hello1', + ); + t.true( + zipfiles_hello.includes(`boto3${sep}__init__.py`), + 'boto3 is packaged in function hello1', + ); + t.false( + zipfiles_hello.includes(`flask${sep}__init__.py`), + 'flask is NOT packaged in function hello1', + ); + + const zipfiles_hello2 = await listZipFiles( + '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip', + ); + t.true( + zipfiles_hello2.includes('handler2.py'), + 'handler2.py is packaged at root level in function hello2', + ); + t.false( + zipfiles_hello2.includes('handler1.py'), + 'handler1.py is NOT packaged at root level in function hello2', + ); + t.false( + zipfiles_hello2.includes(`pyaml${sep}__init__.py`), + 'pyaml is NOT packaged in function hello2', + ); + t.false( + zipfiles_hello2.includes(`boto3${sep}__init__.py`), + 'boto3 is NOT packaged in function hello2', + ); + t.true( + zipfiles_hello2.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello2', + ); + + t.end(); +}); + +test('py3.10 can package lambda-decorators using vendor and invidiually option', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { individually: 'true', vendor: './vendor' } }); + const zipfiles_hello = await listZipFiles('.serverless/hello.zip'); + t.true( + zipfiles_hello.includes('handler.py'), + 'handler.py is packaged at root level in function hello', + ); + t.true( + zipfiles_hello.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello', + ); + t.true( + zipfiles_hello.includes(`lambda_decorators.py`), + 'lambda_decorators.py is packaged in function hello', + ); + t.false( + zipfiles_hello.includes(`dataclasses.py`), + 'dataclasses is NOT packaged in function hello', + ); + + const zipfiles_hello2 = await listZipFiles('.serverless/hello2.zip'); + t.true( + zipfiles_hello2.includes('handler.py'), + 'handler.py is packaged at root level in function hello2', + ); + t.true( + zipfiles_hello2.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello2', + ); + t.true( + zipfiles_hello2.includes(`lambda_decorators.py`), + 'lambda_decorators.py is packaged in function hello2', + ); + t.false( + zipfiles_hello2.includes(`dataclasses.py`), + 'dataclasses is NOT packaged in function hello2', + ); + + const zipfiles_hello3 = await listZipFiles('.serverless/hello3.zip'); + t.true( + zipfiles_hello3.includes('handler.py'), + 'handler.py is packaged at root level in function hello3', + ); + t.false( + zipfiles_hello3.includes(`flask${sep}__init__.py`), + 'flask is NOT packaged in function hello3', + ); + t.false( + zipfiles_hello3.includes(`lambda_decorators.py`), + 'lambda_decorators.py is NOT packaged in function hello3', + ); + t.false( + zipfiles_hello3.includes(`dataclasses.py`), + 'dataclasses is NOT packaged in function hello3', + ); + + const zipfiles_hello4 = await listZipFiles( + '.serverless/fn2-sls-py-req-test-dev-hello4.zip', + ); + t.true( + zipfiles_hello4.includes('fn2_handler.py'), + 'fn2_handler is packaged in the zip-root in function hello4', + ); + t.true( + zipfiles_hello4.includes(`dataclasses.py`), + 'dataclasses is packaged in function hello4', + ); + t.false( + zipfiles_hello4.includes(`flask${sep}__init__.py`), + 'flask is NOT packaged in function hello4', + ); + t.end(); +}); + +test( + "Don't nuke execute perms when using individually", + async (t) => { + process.chdir('tests/individually'); + const path = npm(['pack', '../..']); + const perm = '755'; + writeFileSync(`module1${sep}foobar`, ''); + chmodSync(`module1${sep}foobar`, perm); + + npm(['i', path]); + sls(['package'], { env: {} }); + const zipfiles_hello1 = await listZipFilesWithMetaData( + '.serverless/hello1.zip', + ); + + t.true( + zipfiles_hello1['module1/foobar'].unixPermissions + .toString(8) + .slice(3, 6) === perm, + 'foobar has retained its executable file permissions', + ); + + const zipfiles_hello2 = await listZipFilesWithMetaData( + '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip', + ); + const flaskPerm = statSync( + '.serverless/module2/requirements/bin/flask', + ).mode; + + t.true( + zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, + 'bin/flask has retained its executable file permissions', + ); + + t.end(); + }, + { skip: process.platform === 'win32' }, +); + +test( + "Don't nuke execute perms when using individually w/docker", + async (t) => { + process.chdir('tests/individually'); + const path = npm(['pack', '../..']); + const perm = '755'; + writeFileSync(`module1${sep}foobar`, '', { mode: perm }); + chmodSync(`module1${sep}foobar`, perm); + + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true' } }); + const zipfiles_hello = await listZipFilesWithMetaData( + '.serverless/hello1.zip', + ); + + t.true( + zipfiles_hello['module1/foobar'].unixPermissions + .toString(8) + .slice(3, 6) === perm, + 'foobar has retained its executable file permissions', + ); + + const zipfiles_hello2 = await listZipFilesWithMetaData( + '.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip', + ); + const flaskPerm = statSync( + '.serverless/module2/requirements/bin/flask', + ).mode; + + t.true( + zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, + 'bin/flask has retained its executable file permissions', + ); + + t.end(); + }, + { skip: !canUseDocker() || process.platform === 'win32' }, +); + +test( + 'py3.10 can package flask running in docker with module runtime & architecture of function', + async (t) => { + process.chdir('tests/individually_mixed_runtime'); + const path = npm(['pack', '../..']); + npm(['i', path]); + + sls(['package'], { + env: { dockerizePip: 'true' }, + }); + + const zipfiles_hello2 = await listZipFiles( + '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip', + ); + t.true( + zipfiles_hello2.includes('handler2.py'), + 'handler2.py is packaged at root level in function hello2', + ); + t.true( + zipfiles_hello2.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello2', + ); + }, + { + skip: !canUseDocker() || process.platform === 'win32', + }, +); + +test( + 'py3.10 can package flask succesfully when using mixed architecture, docker and zipping', + async (t) => { + process.chdir('tests/individually_mixed_runtime'); + const path = npm(['pack', '../..']); + + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); + + const zipfiles_hello = await listZipFiles('.serverless/hello1.zip'); + t.true( + zipfiles_hello.includes(`module1${sep}handler1.ts`), + 'handler1.ts is packaged in module dir for hello1', + ); + t.false( + zipfiles_hello.includes('handler2.py'), + 'handler2.py is NOT packaged at root level in function hello1', + ); + t.false( + zipfiles_hello.includes(`flask${sep}__init__.py`), + 'flask is NOT packaged in function hello1', + ); + + const zipfiles_hello2 = await listZipFiles( + '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip', + ); + const zippedReqs = await listRequirementsZipFiles( + '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip', + ); + t.true( + zipfiles_hello2.includes('handler2.py'), + 'handler2.py is packaged at root level in function hello2', + ); + t.false( + zipfiles_hello2.includes(`module1${sep}handler1.ts`), + 'handler1.ts is NOT included at module1 level in hello2', + ); + t.false( + zipfiles_hello2.includes(`pyaml${sep}__init__.py`), + 'pyaml is NOT packaged in function hello2', + ); + t.false( + zipfiles_hello2.includes(`boto3${sep}__init__.py`), + 'boto3 is NOT included in zipfile', + ); + t.true( + zippedReqs.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello2 in requirements.zip', + ); + + t.end(); + }, + { skip: !canUseDocker() || process.platform === 'win32' }, +); + +test( + 'py3.10 can package flask running in docker with module runtime & architecture of function', + async (t) => { + process.chdir('tests/individually_mixed_runtime'); + const path = npm(['pack', '../..']); + npm(['i', path]); + + sls(['package'], { + env: { dockerizePip: 'true' }, + }); + + const zipfiles_hello2 = await listZipFiles( + '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip', + ); + t.true( + zipfiles_hello2.includes('handler2.py'), + 'handler2.py is packaged at root level in function hello2', + ); + t.true( + zipfiles_hello2.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello2', + ); + }, + { + skip: !canUseDocker() || process.platform === 'win32', + }, +); + +test( + 'py3.10 can package flask succesfully when using mixed architecture, docker and zipping', + async (t) => { + process.chdir('tests/individually_mixed_runtime'); + const path = npm(['pack', '../..']); + + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true', zip: 'true' } }); + + const zipfiles_hello = await listZipFiles('.serverless/hello1.zip'); + t.true( + zipfiles_hello.includes(`module1${sep}handler1.ts`), + 'handler1.ts is packaged in module dir for hello1', + ); + t.false( + zipfiles_hello.includes('handler2.py'), + 'handler2.py is NOT packaged at root level in function hello1', + ); + t.false( + zipfiles_hello.includes(`flask${sep}__init__.py`), + 'flask is NOT packaged in function hello1', + ); + + const zipfiles_hello2 = await listZipFiles( + '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip', + ); + const zippedReqs = await listRequirementsZipFiles( + '.serverless/module2-sls-py-req-test-indiv-mixed-runtime-dev-hello2.zip', + ); + t.true( + zipfiles_hello2.includes('handler2.py'), + 'handler2.py is packaged at root level in function hello2', + ); + t.false( + zipfiles_hello2.includes(`module1${sep}handler1.ts`), + 'handler1.ts is NOT included at module1 level in hello2', + ); + t.false( + zipfiles_hello2.includes(`pyaml${sep}__init__.py`), + 'pyaml is NOT packaged in function hello2', + ); + t.false( + zipfiles_hello2.includes(`boto3${sep}__init__.py`), + 'boto3 is NOT included in zipfile', + ); + t.true( + zippedReqs.includes(`flask${sep}__init__.py`), + 'flask is packaged in function hello2 in requirements.zip', + ); + + t.end(); + }, + { skip: !canUseDocker() || process.platform === 'win32' }, +); + +test( + 'py3.10 uses download cache by default option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const cachepath = getUserCachePath(); + t.true( + pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), + 'cache directory exists', + ); + t.end(); + }, + { skip: true }, +); + +test( + 'py3.10 uses download cache by default', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { cacheLocation: '.requirements-cache' } }); + t.true( + pathExistsSync(`.requirements-cache${sep}downloadCacheslspyc${sep}http`), + 'cache directory exists', + ); + t.end(); + }, + { skip: true }, +); + +test( + 'py3.10 uses download cache with dockerizePip option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true' } }); + const cachepath = getUserCachePath(); + t.true( + pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), + 'cache directory exists', + ); + t.end(); + }, + // { skip: !canUseDocker() || brokenOn('win32') } + { skip: true }, +); + +test( + 'py3.10 uses download cache with dockerizePip by default option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { dockerizePip: 'true', cacheLocation: '.requirements-cache' }, + }); + t.true( + pathExistsSync(`.requirements-cache${sep}downloadCacheslspyc${sep}http`), + 'cache directory exists', + ); + t.end(); + }, + // { skip: !canUseDocker() || brokenOn('win32') } + { skip: true }, +); + +test( + 'py3.10 uses static and download cache', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const cachepath = getUserCachePath(); + const cacheFolderHash = sha256Path('.serverless/requirements.txt'); + const arch = 'x86_64'; + t.true( + pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), + 'http exists in download-cache', + ); + t.true( + pathExistsSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask`, + ), + 'flask exists in static-cache', + ); + t.end(); + }, + { skip: true }, +); + +test( + 'py3.10 uses static and download cache with dockerizePip option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true' } }); + const cachepath = getUserCachePath(); + const cacheFolderHash = sha256Path('.serverless/requirements.txt'); + const arch = 'x86_64'; + t.true( + pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), + 'http exists in download-cache', + ); + t.true( + pathExistsSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask`, + ), + 'flask exists in static-cache', + ); + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test('py3.10 uses static cache', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: {} }); + const cachepath = getUserCachePath(); + const cacheFolderHash = sha256Path('.serverless/requirements.txt'); + const arch = 'x86_64'; + t.true( + pathExistsSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask`, + ), + 'flask exists in static-cache', + ); + t.true( + pathExistsSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements`, + ), + '.completed_requirements exists in static-cache', + ); + + // py3.10 checking that static cache actually pulls from cache (by poisoning it) + writeFileSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, + 'injected new file into static cache folder', + ); + sls(['package'], { env: {} }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true( + zipfiles.includes('injected_file_is_bad_form'), + "static cache is really used when running 'sls package' again", + ); + + t.end(); +}); + +test('py3.10 uses static cache with cacheLocation option', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + const cachepath = '.requirements-cache'; + sls(['package'], { env: { cacheLocation: cachepath } }); + const cacheFolderHash = sha256Path('.serverless/requirements.txt'); + const arch = 'x86_64'; + t.true( + pathExistsSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask`, + ), + 'flask exists in static-cache', + ); + t.true( + pathExistsSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements`, + ), + '.completed_requirements exists in static-cache', + ); + t.end(); +}); + +test( + 'py3.10 uses static cache with dockerizePip & slim option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); + const cachepath = getUserCachePath(); + const cacheFolderHash = sha256Path('.serverless/requirements.txt'); + const arch = 'x86_64'; + t.true( + pathExistsSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}flask`, + ), + 'flask exists in static-cache', + ); + t.true( + pathExistsSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}.completed_requirements`, + ), + '.completed_requirements exists in static-cache', + ); + + // py3.10 checking that static cache actually pulls from cache (by poisoning it) + writeFileSync( + `${cachepath}${sep}${cacheFolderHash}_${arch}_slspyc${sep}injected_file_is_bad_form`, + 'injected new file into static cache folder', + ); + sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true( + zipfiles.includes('injected_file_is_bad_form'), + "static cache is really used when running 'sls package' again", + ); + t.deepEqual( + zipfiles.filter((filename) => filename.endsWith('.pyc')), + [], + 'no pyc files are packaged', + ); + + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test( + 'py3.10 uses download cache with dockerizePip & slim option', + async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { dockerizePip: 'true', slim: 'true' } }); + const cachepath = getUserCachePath(); + t.true( + pathExistsSync(`${cachepath}${sep}downloadCacheslspyc${sep}http`), + 'http exists in download-cache', + ); + + const zipfiles = await 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 are packaged', + ); + + t.end(); + }, + { skip: !canUseDocker() || brokenOn('win32') }, +); + +test('py3.10 can ignore functions defined with `image`', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { individually: 'true' } }); + t.true(pathExistsSync('.serverless/hello.zip'), 'function hello is packaged'); + t.true( + pathExistsSync('.serverless/hello2.zip'), + 'function hello2 is packaged', + ); + t.true( + pathExistsSync('.serverless/hello3.zip'), + 'function hello3 is packaged', + ); + t.true( + pathExistsSync('.serverless/hello4.zip'), + 'function hello4 is packaged', + ); + t.false( + pathExistsSync('.serverless/hello5.zip'), + 'function hello5 is not packaged', + ); + + t.end(); +}); + +test('poetry py3.10 fails packaging if poetry.lock is missing and flag requirePoetryLockFile is set to true', async (t) => { + copySync('tests/poetry', 'tests/base with a space'); + process.chdir('tests/base with a space'); + removeSync('poetry.lock'); + + const path = npm(['pack', '../..']); + npm(['i', path]); + const stdout = sls(['package'], { + env: { requirePoetryLockFile: 'true', slim: 'true' }, + noThrow: true, + }); + t.true( + stdout.includes( + 'poetry.lock file not found - set requirePoetryLockFile to false to disable this error', + ), + 'flag works and error is properly reported', + ); + t.end(); +}); + +test('works with provider.runtime not being python', async (t) => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { runtime: 'nodejs12.x' } }); + t.true( + pathExistsSync('.serverless/sls-py-req-test.zip'), + 'sls-py-req-test is packaged', + ); + t.end(); +}); + +test('poetry py3.10 packages additional optional packages', async (t) => { + process.chdir('tests/poetry_packages'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { + poetryWithGroups: 'poetryWithGroups', + }, + }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`bottle.py`), 'bottle is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test('poetry py3.10 skips additional optional packages specified in withoutGroups', async (t) => { + process.chdir('tests/poetry_packages'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { + poetryWithGroups: 'poetryWithGroups', + poetryWithoutGroups: 'poetryWithoutGroups', + }, + }); + const zipfiles = await 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.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test('poetry py3.10 only installs optional packages specified in onlyGroups', async (t) => { + process.chdir('tests/poetry_packages'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { + env: { + poetryOnlyGroups: 'poetryOnlyGroups', + }, + }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.false(zipfiles.includes(`flask${sep}__init__.py`), 'flask is NOT packaged'); + t.false(zipfiles.includes(`bottle.py`), 'bottle is NOT packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + +test('py3.10 injects dependencies into `package` folder when using scaleway provider', async (t) => { + process.chdir('tests/scaleway_provider'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['plugin', 'install', '-n', 'serverless-scaleway-functions'], { + env: {}, + }); + sls(['package'], { env: {} }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true( + zipfiles.includes(`package${sep}flask${sep}__init__.py`), + 'flask is packaged', + ); + t.true( + zipfiles.includes(`package${sep}boto3${sep}__init__.py`), + 'boto3 is packaged', + ); + t.end(); +}); test('pyproject.toml py3.10 packages', async (t) => { process.chdir('tests/pyproject_packages'); From 104db3ebae9aecd6cc116f38a491c8f88ddd28d7 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 19:45:34 -0500 Subject: [PATCH 16/20] seperated out github actions for better clarity --- .github/workflows/validate.yml | 81 +++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 512b0964..12319e56 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -10,8 +10,67 @@ env: FORCE_COLOR: 1 jobs: - linuxNode18: - name: '[Linux] Node.js v18: Lint, Eventual Commitlint, Eventual Changelog, Formatting & Unit tests' + code_quality: + name: '[Linux] Node.js v18: Code Quality and formatting checks' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # For commitlint purpose ensure to have complete list of PR commits + # It's loose and imperfect assumption that PR has no more than 30 commits + fetch-depth: 30 + + - name: Retrieve last master commit (for `git diff` purposes) + run: | + git checkout -b pr + git fetch --prune --depth=30 origin +refs/heads/master:refs/remotes/origin/master + git checkout master + git checkout pr + + - name: Retrieve dependencies from cache + id: cacheNpm + uses: actions/cache@v2 + with: + path: | + ~/.npm + node_modules + key: npm-v18-${{ runner.os }}-${{ github.ref }}-${{ hashFiles('package.json') }} + restore-keys: | + npm-v18-${{ runner.os }}-${{ github.ref }}- + npm-v18-${{ runner.os }}-refs/heads/master- + + - name: Install Node.js and npm + uses: actions/setup-node@v1 + with: + node-version: 18.x + + - name: Install dependencies + if: steps.cacheNpm.outputs.cache-hit != 'true' + run: | + npm update --no-save + npm update --save-dev --no-save + + - name: Validate Prettier formatting + run: npm run prettier-check:updated + + - name: Validate ESLint rules + run: npm run lint:updated + + - name: Validate commit messages + if: github.event.pull_request.base.repo.id == github.event.pull_request.head.repo.id + run: npx commitlint -f master + + - name: Validate changelog (if new version) + run: | + NEW_VERSION=`git diff -U0 master package.json | grep '"version": "' | tail -n 1 | grep -oE "[0-9]+\.[0-9]+\.[0-9]+"` || : + if [ -n "$NEW_VERSION" ]; + then + npx dump-release-notes-from-cc-changelog $NEW_VERSION + fi + + unit_tests: + name: '[Linux] Node.js v18: Unit tests' runs-on: ubuntu-latest strategy: matrix: @@ -72,23 +131,5 @@ jobs: npm update --no-save npm update --save-dev --no-save - - name: Validate Prettier formatting - run: npm run prettier-check:updated - - - name: Validate ESLint rules - run: npm run lint:updated - - - name: Validate commit messages - if: github.event.pull_request.base.repo.id == github.event.pull_request.head.repo.id - run: npx commitlint -f master - - - name: Validate changelog (if new version) - run: | - NEW_VERSION=`git diff -U0 master package.json | grep '"version": "' | tail -n 1 | grep -oE "[0-9]+\.[0-9]+\.[0-9]+"` || : - if [ -n "$NEW_VERSION" ]; - then - npx dump-release-notes-from-cc-changelog $NEW_VERSION - fi - - name: Unit tests run: npm test From 460622effbe0adb07d7d8d53d289e631c588e479 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 20:20:18 -0500 Subject: [PATCH 17/20] fixed scaleway provider version --- tests/scaleway_provider/serverless.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scaleway_provider/serverless.yml b/tests/scaleway_provider/serverless.yml index 5d827bdf..7389343a 100644 --- a/tests/scaleway_provider/serverless.yml +++ b/tests/scaleway_provider/serverless.yml @@ -4,7 +4,7 @@ configValidationMode: off provider: name: scaleway - runtime: python39 + runtime: python3.10 plugins: - serverless-python-requirements From 995dc342413fdf6a96f7adf7a1333b4c2e4cf54f Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:35:32 -0500 Subject: [PATCH 18/20] Added install as package configuration --- index.js | 1 + lib/pip.js | 10 +++++++ test.js | 15 ++++++++++ tests/install_folder_as_package/package.json | 14 +++++++++ .../install_folder_as_package/pyproject.toml | 17 +++++++++++ .../install_folder_as_package/serverless.yml | 30 +++++++++++++++++++ .../src/example/__init__.py | 0 .../src/example/handler.py | 7 +++++ .../src/example/helper.py | 2 ++ 9 files changed, 96 insertions(+) create mode 100644 tests/install_folder_as_package/package.json create mode 100644 tests/install_folder_as_package/pyproject.toml create mode 100644 tests/install_folder_as_package/serverless.yml create mode 100644 tests/install_folder_as_package/src/example/__init__.py create mode 100644 tests/install_folder_as_package/src/example/handler.py create mode 100644 tests/install_folder_as_package/src/example/helper.py diff --git a/index.js b/index.js index 83ac4e4a..70be82c5 100644 --- a/index.js +++ b/index.js @@ -63,6 +63,7 @@ class ServerlessPythonRequirements { poetryOnlyGroups: [], usePyProject: true, pyprojectWithGroups: [], + installFolderAsPackage: false, }, (this.serverless.service.custom && this.serverless.service.custom.pythonRequirements) || diff --git a/lib/pip.js b/lib/pip.js index f3a676fc..b9523710 100644 --- a/lib/pip.js +++ b/lib/pip.js @@ -548,6 +548,16 @@ function filterRequirementsFile(source, target, { options, serverless, log }) { filteredRequirements.unshift(item); } } + + if (options.installFolderAsPackage) { + // Add the install folder as a package + // module_path = path.dirname(source).split(path.sep); + // module_path.pop(); + // filteredRequirements.push(module_path.join(path.sep)); + + filteredRequirements.push('.'); + } + fse.writeFileSync(target, filteredRequirements.join('\n') + '\n'); } diff --git a/test.js b/test.js index 04f12851..2839d887 100644 --- a/test.js +++ b/test.js @@ -1943,3 +1943,18 @@ test('pyproject.toml py3.10 packages with optional', async (t) => { ); t.end(); }); + +test('pyproject.toml py3.10 install code as pacakage', async (t) => { + process.chdir('tests/install_folder_as_package'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls(['package'], { env: { pythonBin: getPythonBin(3) } }); + const zipfiles = await listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.true( + zipfiles.includes(`example${sep}__init__.py`), + 'developers pacakage is packaged', + ); + t.end(); +}); diff --git a/tests/install_folder_as_package/package.json b/tests/install_folder_as_package/package.json new file mode 100644 index 00000000..b07744c9 --- /dev/null +++ b/tests/install_folder_as_package/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-6.0.1.tgz" + } +} diff --git a/tests/install_folder_as_package/pyproject.toml b/tests/install_folder_as_package/pyproject.toml new file mode 100644 index 00000000..2c842c2e --- /dev/null +++ b/tests/install_folder_as_package/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "example" +version = "1.0.0" +authors = [ + { name="Jackson Borneman", email="11304426+jax-b@users.noreply.github.com" }, +] +description = "example" +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3", +] + + +dependencies = [ + "flask", + "boto3" +] diff --git a/tests/install_folder_as_package/serverless.yml b/tests/install_folder_as_package/serverless.yml new file mode 100644 index 00000000..dcf1c84f --- /dev/null +++ b/tests/install_folder_as_package/serverless.yml @@ -0,0 +1,30 @@ +service: sls-py-req-test + +provider: + name: aws + runtime: python3.10 + +plugins: + - serverless-python-requirements +custom: + pythonRequirements: + zip: ${env:zip, self:custom.defaults.zip} + slim: ${env:slim, self:custom.defaults.slim} + slimPatterns: ${file(./slimPatterns.yml):slimPatterns, self:custom.defaults.slimPatterns} + slimPatternsAppendDefaults: ${env:slimPatternsAppendDefaults, self:custom.defaults.slimPatternsAppendDefaults} + dockerizePip: ${env:dockerizePip, self:custom.defaults.dockerizePip} + requirePoetryLockFile: ${env:requirePoetryLockFile, false} + installFolderAsPackage: true + defaults: + zip: false + slimPatterns: false + slimPatternsAppendDefaults: true + slim: false + dockerizePip: false + +package: + individually: false + +functions: + hello: + handler: example.handler.hello diff --git a/tests/install_folder_as_package/src/example/__init__.py b/tests/install_folder_as_package/src/example/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/install_folder_as_package/src/example/handler.py b/tests/install_folder_as_package/src/example/handler.py new file mode 100644 index 00000000..42f37c0a --- /dev/null +++ b/tests/install_folder_as_package/src/example/handler.py @@ -0,0 +1,7 @@ +import requests +from example.helper import helper + +def hello(event, context): + return_data = requests.get('https://httpbin.org/get').json() + return_data["test"] = helper() + return return_data diff --git a/tests/install_folder_as_package/src/example/helper.py b/tests/install_folder_as_package/src/example/helper.py new file mode 100644 index 00000000..1ed7015f --- /dev/null +++ b/tests/install_folder_as_package/src/example/helper.py @@ -0,0 +1,2 @@ +def helper(): + return "helper" From 5b4ec0548486f9880e6b01220de4bb1fefdd3f66 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:40:27 -0500 Subject: [PATCH 19/20] Add custom Python package installation instructions --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index f086efe7..52a3ecb1 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,16 @@ custom: - lambda_dependencies ``` +## :sparkles::snake::sparkles: Custom Python Package installation + +If your function rely on your code being installed as a python package use `installFolderAsPackage`. Ensure your code is structured as a python package or otherwise it will not be installed correctly (`pip install .`) + +```yaml +custom: + pythonRequirements: + installFolderAsPackage: true +``` + ## Dealing with Lambda's size limitations To help deal with potentially large dependencies (for example: `numpy`, `scipy` From 85e8358c3995f0f7aba5c2b2f34fc95b3cf7ccc6 Mon Sep 17 00:00:00 2001 From: Jackson Borneman <11304426+jax-b@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:44:48 -0500 Subject: [PATCH 20/20] fixed issue where test was looking for the wrong package name --- test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.js b/test.js index 04f12851..f3577731 100644 --- a/test.js +++ b/test.js @@ -1938,8 +1938,8 @@ test('pyproject.toml py3.10 packages with optional', async (t) => { t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); t.true(zipfiles.includes(`pytest${sep}__init__.py`), 'pytest is packaged'); t.true( - zipfiles.includes(`pytest-cov${sep}__init__.py`), - 'pytest is packaged', + zipfiles.includes(`pytest_cov${sep}__init__.py`), + 'pytest-cov is packaged', ); t.end(); });