Skip to content

Commit 8646c1c

Browse files
Merge branch 'master' into la-pipenv-modules
2 parents 64631a5 + e31a10c commit 8646c1c

File tree

12 files changed

+151
-10
lines changed

12 files changed

+151
-10
lines changed

README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ custom:
174174
strip: false
175175
```
176176

177-
### Lamba Layer
177+
### Lambda Layer
178178
Another method for dealing with large dependencies is to put them into a
179179
[Lambda Layer](https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html).
180180
Simply add the `layer` option to the configuration.
@@ -439,6 +439,35 @@ zipinfo .serverless/xxx.zip
439439
```
440440
(If you can't see the library, you might need to adjust your package include/exclude configuration in `serverless.yml`.)
441441

442+
## Optimising packaging time
443+
444+
If you wish to exclude most of the files in your project, and only include the source files of your lambdas and their dependencies you may well use an approach like this:
445+
446+
```yaml
447+
package:
448+
individually: false
449+
include:
450+
- "./src/lambda_one/**"
451+
- "./src/lambda_two/**"
452+
exclude:
453+
- "**"
454+
```
455+
456+
This will be very slow. Serverless adds a default `"**"` include. If you are using the `cacheLocation` parameter to this plugin, this will result in all of the cached files' names being loaded and then subsequently discarded because of the exclude pattern. To avoid this happening you can add a negated include pattern, as is observed in https://github.com/serverless/serverless/pull/5825.
457+
458+
Use this approach instead:
459+
460+
```yaml
461+
package:
462+
individually: false
463+
include:
464+
- "!./**"
465+
- "./src/lambda_one/**"
466+
- "./src/lambda_two/**"
467+
exclude:
468+
- "**"
469+
```
470+
442471
## Contributors
443472
* [@dschep](https://github.com/dschep) - Lead developer & maintainer
444473
* [@azurelogic](https://github.com/azurelogic) - logging & documentation fixes

lib/layer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ function zipRequirements() {
2525
* @return {Promise} empty promise
2626
*/
2727
function createLayers() {
28+
if (!this.serverless.service.layers) {
29+
this.serverless.service.layers = {};
30+
}
2831
this.serverless.service.layers['pythonRequirements'] = Object.assign(
2932
{
3033
artifact: path.join('.serverless', 'pythonRequirements.zip'),

lib/pip.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -613,10 +613,9 @@ function installAllRequirements() {
613613
!fse.existsSync(symlinkPath) &&
614614
reqsInstalledAt != symlinkPath
615615
) {
616-
// Windows can't symlink so we have to copy on Windows,
617-
// it's not as fast, but at least it works
616+
// Windows can't symlink so we have to use junction on Windows
618617
if (process.platform == 'win32') {
619-
fse.copySync(reqsInstalledAt, symlinkPath);
618+
fse.symlink(reqsInstalledAt, symlinkPath, 'junction');
620619
} else {
621620
fse.symlink(reqsInstalledAt, symlinkPath);
622621
}

lib/poetry.js

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1+
const fs = require('fs');
12
const fse = require('fs-extra');
23
const path = require('path');
34
const { spawnSync } = require('child_process');
5+
const tomlParse = require('@iarna/toml/parse-string');
46

57
/**
68
* poetry install
79
*/
810
function pyprojectTomlToRequirements() {
9-
if (
10-
!this.options.usePoetry ||
11-
!fse.existsSync(path.join(this.servicePath, 'pyproject.toml'))
12-
) {
11+
if (!this.options.usePoetry || !isPoetryProject(this.servicePath)) {
1312
return;
1413
}
1514

@@ -40,4 +39,29 @@ function pyprojectTomlToRequirements() {
4039
);
4140
}
4241

42+
/**
43+
* Check if pyproject.toml file exists and is a poetry project.
44+
*/
45+
function isPoetryProject(servicePath) {
46+
const pyprojectPath = path.join(servicePath, 'pyproject.toml');
47+
48+
if (!fse.existsSync(pyprojectPath)) {
49+
return false;
50+
}
51+
52+
const pyprojectToml = fs.readFileSync(pyprojectPath);
53+
const pyproject = tomlParse(pyprojectToml);
54+
55+
const buildSystemReqs =
56+
(pyproject['build-system'] && pyproject['build-system']['requires']) || [];
57+
58+
for (var i = 0; i < buildSystemReqs.length; i++) {
59+
if (buildSystemReqs[i].startsWith('poetry')) {
60+
return true;
61+
}
62+
}
63+
64+
return false;
65+
}
66+
4367
module.exports = { pyprojectTomlToRequirements };

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"tape": "*"
5151
},
5252
"dependencies": {
53+
"@iarna/toml": "^2.2.3",
5354
"appdirectory": "^0.1.0",
5455
"bluebird": "^3.0.6",
5556
"fs-extra": "^7.0.0",
@@ -61,8 +62,8 @@
6162
"lodash.uniqby": "^4.0.0",
6263
"lodash.values": "^4.3.0",
6364
"rimraf": "^2.6.2",
64-
"shell-quote": "^1.6.1",
65-
"sha256-file": "1.0.0"
65+
"sha256-file": "1.0.0",
66+
"shell-quote": "^1.6.1"
6667
},
6768
"eslintConfig": {
6869
"extends": "eslint:recommended",

test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,17 @@ test("pipenv py3.6 doesn't package bottle with noDeploy option", t => {
712712
t.end();
713713
});
714714

715+
test('non build pyproject.toml uses requirements.txt', t => {
716+
process.chdir('tests/non_build_pyproject');
717+
const path = npm(['pack', '../..']);
718+
npm(['i', path]);
719+
sls(['package']);
720+
const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip');
721+
t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged');
722+
t.false(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is NOT packaged');
723+
t.end();
724+
});
725+
715726
test('poetry py3.6 can package flask with default options', t => {
716727
process.chdir('tests/poetry');
717728
const path = npm(['pack', '../..']);

tests/non_build_pyproject/.gitignore

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Distribution / packaging
2+
.Python
3+
env/
4+
build/
5+
develop-eggs/
6+
dist/
7+
downloads/
8+
eggs/
9+
.eggs/
10+
lib/
11+
lib64/
12+
parts/
13+
sdist/
14+
var/
15+
*.egg-info/
16+
.installed.cfg
17+
*.egg
18+
19+
# Serverless
20+
.serverless
21+
.requirements
22+
unzip_requirements.py

tests/non_build_pyproject/handler.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import requests
2+
3+
4+
def hello(event, context):
5+
return requests.get('https://httpbin.org/get').json()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "example",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"author": "",
10+
"license": "ISC",
11+
"dependencies": {
12+
"serverless-python-requirements": "file:serverless-python-requirements-4.2.5.tgz"
13+
}
14+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[tool.black]
2+
line-length = 79
3+
py36 = true
4+
skip-string-normalization = true
5+
exclude = '''
6+
/(
7+
\.serverless
8+
| node_modules
9+
)/
10+
'''
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
flask
2+
boto3
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
service: sls-py-req-test
2+
3+
provider:
4+
name: aws
5+
runtime: python3.6
6+
7+
plugins:
8+
- serverless-python-requirements
9+
custom:
10+
pythonRequirements:
11+
usePoetry: false
12+
13+
package:
14+
exclude:
15+
- '**/*'
16+
include:
17+
- handler.py
18+
19+
functions:
20+
hello:
21+
handler: handler.hello

0 commit comments

Comments
 (0)