Skip to content

Commit 2341d7f

Browse files
committed
add a zip method that decompresses! (and remove options) closes #3
Removes zipimport functionality because it does not work with most libraries you want to use it with (ones with shared objects). And remove non-linking non-zip way of using it. This simplifies the usage options and the code base. win-win.
1 parent 1aab175 commit 2341d7f

File tree

5 files changed

+65
-77
lines changed

5 files changed

+65
-77
lines changed

README.md

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -22,66 +22,39 @@ plugins:
2222
```
2323
2424
25-
## Adding the dependencies to `sys.path`
26-
27-
### Automatic
28-
The default behavior of this plugin is to link libraries into the working tree
29-
during deployment so that they are in your handler's `PYTHONPATH` when running
30-
on lambda.
31-
32-
### Manual
33-
This method is required when using [ZipImport](#zipimport) support and can be
34-
enabled manually by adding the following option to your config:
35-
36-
```yaml
37-
custom:
38-
pythonRequirements:
39-
link: false
40-
```
41-
42-
`serverless-python-requirements` adds a module called `requirements` to your
43-
puck. To easily make the bundled dependencies available, simply import it. Eg.
44-
add this to the top of any file using dependencies specified in your
45-
`requirements.txt`:
46-
```python
47-
import requirements
48-
# Now you can use deps you specified in requirements.txt!
49-
import requests
50-
```
51-
5225
## Cross compiling!
53-
Compiling non-pure-Python modules is supported on MacOS via the use of Docker
54-
and the [docker-lambda](https://github.com/lambci/docker-lambda) image.
26+
Compiling non-pure-Python modules or fetching their manylinux wheels is
27+
supported on non-linux OSs via the use of Docker and the
28+
[docker-lambda](https://github.com/lambci/docker-lambda) image.
5529
To enable docker usage, add the following to your `serverless.yml`:
5630
```yaml
5731
custom:
5832
pythonRequirements:
5933
dockerizePip: true
6034
```
6135

62-
## ZipImport!
36+
## Dealing with Lambda's size limitations
6337
To help deal with potentially large dependencies (for example: `numpy`, `scipy`
64-
and `scikit-learn`) there is support for having python import using
65-
[zipimport](https://docs.python.org/2.7/library/zipimport.html). To enable this
66-
add the following to your `serverless.yml`:
38+
and `scikit-learn`) there is support for compressing the libraries. This does
39+
require a minor change to your code to decompress them. To enable this add the
40+
following to your `serverless.yml`:
6741
```yaml
6842
custom:
6943
pythonRequirements:
70-
zipImport: true
44+
zip: true
7145
```
7246

73-
74-
## Limitations
75-
* if using the `package` directive in `serverless.yml` ensure that
76-
`requirements.py` is are included as well as `.requirements` or
77-
`.requirements.zip` if using [ZipImport](#zipimport).
78-
47+
and add this to your handler module before any code that imports your deps:
48+
```python
49+
import unzip_requirements
50+
```
7951

8052
## Manual invocations
8153

82-
The `.requirements` and `requirements.py` files are left behind to simplify
83-
development. To clean them up, run `sls requirements clean`. You can also
84-
install them manually for local development with `sls requirements install`.
54+
The `.requirements` and `requirements.zip`(if using zip support) files are left
55+
behind to speed things up on subsequent deploys. To clean them up, run
56+
`sls requirements clean`. You can also create them manually with
57+
`sls requirements install`.
8558

8659
## Credit
8760
This plugin is influenced by

index.js

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,21 @@ const {zipDirectory} = require('./zipService');
1111
BbPromise.promisifyAll(fse);
1212

1313
class ServerlessPythonRequirements {
14-
packVendorHelper() {
15-
this.serverless.cli.log('Installing Python requirements helper...');
14+
addVendorHelper() {
15+
if (this.custom.zip) {
16+
this.serverless.cli.log('Removing Python requirements helper...');
1617

17-
return fse.copyAsync(
18-
path.resolve(__dirname, 'requirements.py'),
19-
path.join(this.serverless.config.servicePath, 'requirements.py'));
18+
return fse.copyAsync(
19+
path.resolve(__dirname, 'unzip_requirements.py'),
20+
path.join(this.serverless.config.servicePath, 'unzip_requirements.py'));
21+
}
22+
};
23+
24+
removeVendorHelper() {
25+
if (this.custom.zip) {
26+
this.serverless.cli.log('Adding Python requirements helper...');
27+
return fse.removeAsync('unzip_requirements.py');
28+
}
2029
};
2130

2231
installRequirements() {
@@ -60,32 +69,34 @@ class ServerlessPythonRequirements {
6069

6170
packRequirements() {
6271
return this.installRequirements().then(() => {
63-
if (this.custom.zipImport)
72+
if (this.custom.zip) {
73+
this.serverless.cli.log('Zipping required Python packages...');
6474
return zipDirectory('.requirements', '.requirements.zip');
75+
}
6576
});
6677
}
6778

6879
linkRequirements() {
69-
if (!this.custom.zipImport && this.custom.link) {
80+
if (!this.custom.zip) {
7081
this.serverless.cli.log('Linking required Python packages...');
7182
fse.readdirSync('.requirements').map(file =>
7283
fse.symlinkSync(`.requirements/${file}`, `./${file}`));
7384
}
7485
}
7586

7687
unlinkRequirements() {
77-
if (!this.custom.zipImport && this.custom.link) {
88+
if (!this.custom.zip) {
7889
this.serverless.cli.log('Unlinking required Python packages...');
7990
fse.readdirSync('.requirements').map(file => fse.unlinkSync(file));
8091
}
8192
}
8293

8394
cleanup() {
84-
const artifacts = ['requirements.py'];
85-
if (this.custom.zipImport)
86-
artifacts.push('.requirements.zip')
87-
else
88-
artifacts.push('.requirements')
95+
const artifacts = ['.requirements'];
96+
if (this.custom.zip) {
97+
artifacts.push('.requirements.zip');
98+
artifacts.push('unzip_requirements.py');
99+
}
89100

90101
return BbPromise.all(_.map(artifacts, (artifact) =>
91102
fse.removeAsync(path.join(this.serverless.config.servicePath, artifact))));;
@@ -95,16 +106,19 @@ class ServerlessPythonRequirements {
95106
this.serverless = serverless;
96107
this.options = options;
97108
this.custom = Object.assign({
98-
zipImport: false,
99-
link: true,
109+
zip: false,
100110
}, this.serverless.service.custom &&
101111
this.serverless.service.custom.pythonRequirements || {});
102112

113+
if (!_.has(this.serverless.service, ['package', 'exclude']))
114+
_.set(this.serverless.service, ['package', 'exclude'], []);
115+
this.serverless.service.package.exclude.push('.requirements/**');
116+
103117
this.commands = {
104118
'requirements': {
105119
commands: {
106120
'clean': {
107-
usage: 'Remove .requirements and requirements.py',
121+
usage: 'Remove .requirements and requirements.zip',
108122
lifecycleEvents: [
109123
'clean',
110124
],
@@ -121,15 +135,16 @@ class ServerlessPythonRequirements {
121135

122136
this.hooks = {
123137
'before:deploy:createDeploymentArtifacts': () => BbPromise.bind(this)
124-
.then(this.packVendorHelper)
138+
.then(this.addVendorHelper)
125139
.then(this.packRequirements)
126140
.then(this.linkRequirements),
127141

128142
'after:deploy:createDeploymentArtifacts': () => BbPromise.bind(this)
143+
.then(this.removeVendorHelper)
129144
.then(this.unlinkRequirements),
130145

131146
'requirements:install:install': () => BbPromise.bind(this)
132-
.then(this.packVendorHelper)
147+
.then(this.addVendorHelper)
133148
.then(this.packRequirements),
134149

135150
'requirements:clean:clean': () => BbPromise.bind(this)

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "serverless-python-requirements",
3-
"version": "2.0.0-beta.3",
3+
"version": "2.0.0-beta.4",
44
"engines": {
55
"node": ">=4.0"
66
},
@@ -32,7 +32,7 @@
3232
"zipService.js",
3333
"LICENSE",
3434
"package.json",
35-
"requirements.py",
35+
"unzip_requirements.py",
3636
"README.md"
3737
],
3838
"main": "index.js",

requirements.py

Lines changed: 0 additions & 14 deletions
This file was deleted.

unzip_requirements.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import os
2+
import sys
3+
import zipfile
4+
import tempfile
5+
6+
7+
zip_requirements = os.path.join(
8+
os.path.split(__file__)[0], '.requirements.zip')
9+
10+
tempdir = tempfile.mkdtemp()
11+
12+
sys.path.append(tempdir)
13+
14+
zipfile.ZipFile(zip_requirements, 'r').extractall(tempdir)

0 commit comments

Comments
 (0)