serverless-python-requirements icon indicating copy to clipboard operation
serverless-python-requirements copied to clipboard

Mixing nodejs and python results in "ENOENT: no such file or directory, scandir '.serverless/requirement"

Open gerbenoostra opened this issue 3 years ago • 2 comments
trafficstars

I'm having lambda's of both the python and nodejs runtime. When building the dist, it results in the following error:

Serverless: Zipping required Python packages for ....
Serverless: Zipping required Python packages for src/func2...
 
 Exception -----------------------------------------------
 
  [OperationalError: ENOENT: no such file or directory, scandir '.serverless/requirements'] {
    cause: [Error: ENOENT: no such file or directory, scandir '.serverless/requirements'] {
      errno: -2,
      code: 'ENOENT',
      syscall: 'scandir',
      path: '.serverless/requirements'
    },
    isOperational: true,
    errno: -2,
    code: 'ENOENT',
    syscall: 'scandir',
    path: '.serverless/requirements'
  }
 
  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Issues:        forum.serverless.com
 
  Your Environment Information ---------------------------
     Operating System:          darwin
     Node Version:              17.4.0
     Framework Version:         2.72.3 (local)
     Plugin Version:            5.5.4
     SDK Version:               4.3.2
     Components Version:        3.18.2

My serverless.yml contains:

plugins:
  - serverless-python-requirements

package:
  individually: true

custom:
  pythonRequirements:
    zip: true
    slim: true
    dockerizePip: non-linux
    usePoetry: false # <- can't be true with package:individually:true
    cacheLocation: ./.serverless-python-requirements-cache

provider:
  name: aws
  runtime: python3.8

functions:
  func1:
    handler: func1/index.main
    runtime: nodejs12.x
  func2:
    handler: handler.handle
    module: src/func2

My filetree structure is:

serverless.yml
package.json
yarn.lock
src
 - func1
    - index.js
 - func2
   - pyproject.tom
   - poetry.lock
   - handler
     - __init__.py 

I see a unzip_requirements.py being created in the project root

A quick check in the sourcecode at https://github.com/serverless/serverless-python-requirements/blob/3a898e5e707658c76f6063f44938366935b41812/lib/zip.js#L116 shows that it uses this.targetFuncs to determine for which functions to create a zip.

However, the inject.js filters the this.targetFuncs on the python runtime at https://github.com/serverless/serverless-python-requirements/blob/3a898e5e707658c76f6063f44938366935b41812/lib/inject.js#L109 .

Shouldn't all occurrences of this.targetFuncs be replaced with one that filters on the runtime?

update 1: Note that if I add a module to the js function, the mentioned scandir in the exception changes. For example:

functions:
  func1:
    handler: func1/index.main
    runtime: nodejs12.x
    module: src/func1

results in scandir '.serverless/src/func1/requirements'

update 2: As the module is used as the root dir, and requirements are only packed for functions with a python runtime, I can make sls dist succeed by assigning the python module dir to the nodejs function:

functions:
  func1:
    handler: func1/index.main
    runtime: nodejs12.x
    module: src/func2

gerbenoostra avatar Mar 16 '22 09:03 gerbenoostra

A possible workaround is to create an empty lambda:

#serverless.yml

functions:
  empty_python:
    handler: handler.handle
    description: "Empty project to trick python-requirements into preparing requirements."
    module: src/empty_python
  func1:
    handler: func1/index.main
    runtime: nodejs12.x
    module: src/empty_python
    package:
      patterns:
        - "src/func1/**"
  func2:
    handler: handler.handle
    module: src/func2
    package:
      patterns:
        - "src/func2/handler/**"
        - "src/func2/*.py

where src/empty_python only contains an empty requirements.txt.

Because it is specified first, it's requirements (which there are none) will be saved, and then reused by the nodejs functions. Not pretty, but at least it works for now.

gerbenoostra avatar Mar 16 '22 16:03 gerbenoostra

Are you sure this doesn't still happen without node? I'm using just a single python function, and this happens as soon as I add zip: true (or layer: true which implicitly zips).

OJFord avatar Jun 17 '22 11:06 OJFord