Skip to content
This repository was archived by the owner on Oct 24, 2025. It is now read-only.
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,38 @@ custom:
```
This will remove all folders within the installed requirements that match
the names in `slimPatterns`

### Lamba Layer
Another method for dealing with large dependencies is to put them into a
[Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html).
Simply add the `layer` option to the configuration.
```yaml
custom:
pythonRequirements:
layer: true
```
The requirements will be zipped up and a layer will be created automatically.
Now just add the reference to the functions that will use the layer.
```yaml
functions:
hello:
handler: handler.hello
layers:
- {Ref: PythonRequirementsLambdaLayer}
```
If the layer requires additional or custom configuration, add them onto the `layer` option.
```yaml
custom:
pythonRequirements:
layer:
name: ${self:provider.stage}-layerName
description: Python requirements lamba layer
compatibleRuntimes:
- python3.7
licenseInfo: GPLv3
allowedAccounts:
- '*'
```
## Omitting Packages
You can omit a package from deployment with the `noDeploy` option. Note that
dependencies of omitted packages must explicitly be omitted too. By default,
Expand Down Expand Up @@ -423,3 +455,4 @@ zipinfo .serverless/xxx.zip
* [@andrewfarley](https://github.com/andrewfarley) - Implemented download caching and static caching
* [@bweigel](https://github.com/bweigel) - adding the `slimPatternsAppendDefaults` option & fixing per-function packaging when some functions don't have requirements & Porting tests from bats to js!
* [@squaresurf](https://github.com/squaresurf) - adding usePoetry option
* [@david-mk-lawrence](https://github.com/david-mk-lawrence) - added Lambda Layer support
9 changes: 9 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const {
packRequirements
} = require('./lib/zip');
const { injectAllRequirements } = require('./lib/inject');
const { layerRequirements } = require('./lib/layer');
const { installAllRequirements } = require('./lib/pip');
const { pipfileToRequirements } = require('./lib/pipenv');
const { pyprojectTomlToRequirements } = require('./lib/poetry');
Expand All @@ -32,6 +33,7 @@ class ServerlessPythonRequirements {
slimPatterns: false,
slimPatternsAppendDefaults: true,
zip: false,
layer: false,
cleanupZipHelper: true,
invalidateCaches: false,
fileName: 'requirements.txt',
Expand Down Expand Up @@ -96,6 +98,12 @@ class ServerlessPythonRequirements {
}`;
options.dockerImage = options.dockerImage || defaultImage;
}
if (options.layer) {
// If layer was set as a boolean, set it to an empty object to use the layer defaults.
if (options.layer === true) {
options.layer = {};
}
}
return options;
}

Expand Down Expand Up @@ -170,6 +178,7 @@ class ServerlessPythonRequirements {
}
return BbPromise.bind(this)
.then(removeVendorHelper)
.then(layerRequirements)
.then(() =>
injectAllRequirements.bind(this)(
arguments[1].functionObj &&
Expand Down
5 changes: 5 additions & 0 deletions lib/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ function moveModuleUp(source, target, module) {
* @return {Promise} the combined promise for requirements injection.
*/
function injectAllRequirements(funcArtifact) {
if (this.options.layer) {
// The requirements will be placed in a Layer, so just resolve
return BbPromise.resolve();
}

this.serverless.cli.log('Injecting required Python packages to package...');

if (this.serverless.service.package.individually) {
Expand Down
60 changes: 60 additions & 0 deletions lib/layer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const BbPromise = require('bluebird');
const fse = require('fs-extra');
const path = require('path');
const JSZip = require('jszip');
const { writeZip, addTree } = require('./zipTree');

BbPromise.promisifyAll(fse);

/**
* Zip up requirements to be used as layer package.
* @return {Promise} the JSZip object constructed.
*/
function zipRequirements() {
const rootZip = new JSZip();
const src = path.join('.serverless', 'requirements');
const runtimepath = 'python';

return addTree(rootZip.folder(runtimepath), src).then(() =>
writeZip(rootZip, path.join('.serverless', 'pythonRequirements.zip'))
);
}

/**
* Creates a layer on the serverless service for the requirements zip.
* @return {Promise} empty promise
*/
function createLayers() {
this.serverless.service.layers['pythonRequirements'] = Object.assign(
{
artifact: path.join('.serverless', 'pythonRequirements.zip'),
name: `${this.serverless.service.stage}-python-requirements`,
description:
'Python requirements generated by serverless-python-requirements.',
compatibleRuntimes: [this.serverless.service.provider.runtime]
},
this.options.layer
);

return BbPromise.resolve();
}

/**
* Creates a layer from the installed requirements.
* @return {Promise} the combined promise for requirements layer.
*/
function layerRequirements() {
if (!this.options.layer) {
return BbPromise.resolve();
}

this.serverless.cli.log('Packaging Python Requirements Lambda Layer...');

return BbPromise.bind(this)
.then(zipRequirements)
.then(createLayers);
}

module.exports = {
layerRequirements
};