Skip to content

Support PEP-621 pyproject.toml #634

@KerberosMorphy

Description

@KerberosMorphy

I know you already support poetry and pipenv, but I would like to request the following feature to support PDM to.
Supporting official PEP-621 pyproject.toml and their extra.

I've try to create this addition since I wanted to contribute, but my javascript isn't really up to date and I have some difficulties to implements tests and understand some part of your code.

What I would like to:

custom:
  pythonRequirements:
    usePyproject: true  # don't know if it's necessary
    extra:
      - rich

so for this pyproject:

[project]
name = "pyproject"
version = "0.1.0"
description = ""
requires-python = ">=3.6"
authors = [
  {name = "Your Name"},
  {email = "you@example.com"},
]
dependencies = [
  "Flask>=1.0",
  "bottle @ git+https://git@github.com/bottlepy/bottle.git@v21.8.0",
  "boto3>=1.9",
]

[project.optional-dependencies]
test = [
  "tox",
]
rich = [
  "rich",
]

It would generate:

requirements.txt

Flask>=1.0
bottle @ git+https://git@github.com/bottlepy/bottle.git@v21.8.0
boto3>=1.9
rich

What I have tried to do:

const fs = require('fs');
const fse = require('fs-extra');
const path = require('path');
const { spawnSync } = require('child_process');
const tomlParse = require('@iarna/toml/parse-string');

/**
 * pyproject install
 */
function pyprojectTomlToRequirements() {
  if (!this.options.usePyproject) {
    return;
  }

  this.serverless.cli.log('Generating requirements.txt from pyproject.toml...');

  const destination = path.join(this.servicePath, 'requirements.txt');
  const requirementsContents = extractDependencies(this.servicePath);

  const editableFlag = new RegExp(/^-e /gm);
  const sourceRequirements = path.join(this.servicePath, 'requirements.txt');

  if (requirementsContents.match(editableFlag)) {
    this.serverless.cli.log(
      'The generated file contains -e flags, removing them...'
    );
    fse.writeFileSync(
      sourceRequirements,
      requirementsContents.replace(editableFlag, '')
    );
  }

  fse.ensureDirSync(path.join(this.servicePath, '.serverless'));
  fse.moveSync(
    sourceRequirements,
    path.join(this.servicePath, '.serverless', 'requirements.txt'),
    { overwrite: true }
  );
}

/**
 * Extract dependencies and extra from pyproject.toml
 */
function extractDependencies(servicePath, extra) {
  const pyprojectPath = path.join(servicePath, 'pyproject.toml');

  if (!fse.existsSync(pyprojectPath)) {
    return false;
  }

  const pyprojectToml = fs.readFileSync(pyprojectPath);
  const pyproject = tomlParse(pyprojectToml);

  const dependencies =
    (pyproject['project'] && pyproject['project']['dependencies']) || [];

  const extraDependencies =
    (pyproject['project'] && pyproject['project']['optional-dependencies']) || {};

  for (const [extraName, dependencies] of Object.entries(extraDependencies)) {
    if (extra.includes(extraName)) {
      dependencies = dependencies.concat(dependencies);
    }
  }
  return dependencies.join("\n");
}

module.exports = { pyprojectTomlToRequirements, isPyprojectProject };

Hope that what I've done could help, sorry if I couldn't do more.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions