Skip to content

Commit

Permalink
feature(inputs): add some default values and cli options
Browse files Browse the repository at this point in the history
- docs: update changelog
- feature(app): input description and python-version as cli options
- fix(poetry): allow no repository url in pyproject.toml
- feature(inputs): add default value to packageName and packageVersion
- style: format with newest prettier
  • Loading branch information
davla committed Sep 29, 2023
2 parents 4fbbd39 + 62d3a32 commit 12d53ab
Show file tree
Hide file tree
Showing 29 changed files with 1,152 additions and 245 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/test-and-publish.yml
Expand Up @@ -41,6 +41,11 @@ jobs:
path: ${{ env.unit-test-results-path }}
reporter: mocha-json

- name: Install python
uses: actions/setup-python@v4
with:
python-version: 3.x

- name: Install end-to-end dependencies
run: pipx install poetry

Expand Down
2 changes: 1 addition & 1 deletion .mocharc.yaml
Expand Up @@ -5,7 +5,7 @@ checkLeaks: true
failZero: true
parallel: false
recursive: true
timeout: 10000
timeout: 20000

require:
- ./test/lib/global-setup.js
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- The description and python version prompts are available as command line
options.
- The package name and package version inputs have default prompt values when
there is no pre-existing `pyproject.toml` file.

### Fixed

- The repository input is no longer mandatory, as per
[the specification](https://python-poetry.org/docs/pyproject/#repository)
of the `tool.poetry` section in `pyproject.toml`.

## [1.1.2] - 2023-09-27

### Changed
Expand Down
20 changes: 11 additions & 9 deletions generators/app/index.js
Expand Up @@ -4,7 +4,7 @@ import chalk from "chalk";
import yosay from "yosay";

import BaseGenerator from "../../lib/base-generator.js";
import sharedInputs from "../../lib/shared/inputs.js";
import inputs from "../../lib/inputs.js";
import PoetryGenerator from "../poetry/index.js";
import PythonPackageGenerator from "../python-package/index.js";
import VSCodeGenerator from "../vscode/index.js";
Expand All @@ -13,7 +13,7 @@ const require = createRequire(import.meta.url);

export default class PythonPoetryVSCodeGenerator extends BaseGenerator {
constructor(args, opts) {
super(args, opts, Object.values(sharedInputs));
super(args, opts, Object.values(inputs));
}

initializing() {
Expand All @@ -34,7 +34,7 @@ export default class PythonPoetryVSCodeGenerator extends BaseGenerator {
"authorEmail",
"repository",
"license",
"packageName"
"packageName",
);

this.composeWith(require.resolve("generator-editorconf"), {
Expand All @@ -59,6 +59,8 @@ export default class PythonPoetryVSCodeGenerator extends BaseGenerator {
"authorName",
"authorEmail",
"repository",
"description",
"pythonVersion",
]);
this._compose(PythonPackageGenerator, "../python-package/index.js", [
"packageName",
Expand All @@ -70,7 +72,7 @@ export default class PythonPoetryVSCodeGenerator extends BaseGenerator {
async install() {
const poetryInstall = chalk.green("poetry install");
this.log(
yosay(`I'll now run ${poetryInstall} to bootstrap your workspace.`)
yosay(`I'll now run ${poetryInstall} to bootstrap your workspace.`),
);

try {
Expand All @@ -91,7 +93,7 @@ export default class PythonPoetryVSCodeGenerator extends BaseGenerator {
Generator: generatorClass,
path: require.resolve(generatorPath),
},
this.getOptionValues(...optionNames)
this.getOptionValues(...optionNames),
);
}

Expand All @@ -103,8 +105,8 @@ export default class PythonPoetryVSCodeGenerator extends BaseGenerator {
yosay(
`I'll now ask you some questions. The ${defaultAnswers} are derived` +
`from the environment (e.g. existing ${pyprojectToml}, ` +
`${git} configuration...).`
)
`${git} configuration...).`,
),
);
}

Expand All @@ -119,8 +121,8 @@ export default class PythonPoetryVSCodeGenerator extends BaseGenerator {
`You can install it here: ${intallLink}.`,
{
maxLength: url.length + 1,
}
)
},
),
);
}
}
87 changes: 19 additions & 68 deletions generators/poetry/index.js
Expand Up @@ -3,72 +3,29 @@ import path from "node:path";
import _ from "lodash";

import BaseGenerator from "../../lib/base-generator.js";
import { PyProjectTomlInputFactory } from "../../lib/input-factories.js";
import inputs from "../../lib/inputs.js";
import mergeConfig from "../../lib/merge-config.js";
import { moduleDirName } from "../../lib/paths.js";
import {
pyProjectTomlPath,
readPyProjectToml,
} from "../../lib/pyproject-toml-utils.js";
import sharedInputs from "../../lib/shared/inputs.js";

import {
validateDescription,
validatePoetryVersionRange,
} from "./validate-input.js";

const parentDir = moduleDirName(import.meta);

export default class PoetryGenerator extends BaseGenerator {
static authorInputNames = ["authorName", "authorEmail"];

static inputFactories = [
new PyProjectTomlInputFactory({
name: "description",
ioConfig: {
option: {
desc: "The description of the Python package.",
type: String,
},
prompt: {
message: "Python package description",
type: "input",
},
},
valueFunctions: { validate: validateDescription },
}),
new PyProjectTomlInputFactory({
name: "pythonVersion",
toolPoetryPath: "dependencies.python",
ioConfig: {
option: {
name: "python-version",
desc: "The range of Python versions compatible with the package ",
type: String,
},
prompt: {
message: "Python versions compatible with the package",
type: "input",
},
},
valueFunctions: {
async default() {
return `^${await this._queryCurrentPythonVersion()}`;
},
validate: validatePoetryVersionRange,
},
}),
];

constructor(args, opts) {
super(args, opts, [
sharedInputs.packageName,
sharedInputs.packageVersion,
sharedInputs.authorName,
sharedInputs.authorEmail,
sharedInputs.repository,
sharedInputs.license,
...PoetryGenerator.inputFactories,
inputs.packageName,
inputs.packageVersion,
inputs.authorName,
inputs.authorEmail,
inputs.repository,
inputs.license,
inputs.description,
inputs.pythonVersion,
]);
}

Expand All @@ -85,7 +42,7 @@ export default class PoetryGenerator extends BaseGenerator {
const diskPyProjectToml = readPyProjectToml.call(this);
const statePyProjectToml = { tool: { poetry: this._toolPoetry() } };
const newPyProjectToml = this._applyDefaultBuildSystem(
mergeConfig(diskPyProjectToml, statePyProjectToml)
mergeConfig(diskPyProjectToml, statePyProjectToml),
);
this._writeToml(pyProjectTomlPath.call(this), newPyProjectToml);
}
Expand All @@ -97,36 +54,30 @@ export default class PoetryGenerator extends BaseGenerator {
*/
return _.assign(
this._readToml(this.templatePath("pyproject.toml")),
pyProjectToml
pyProjectToml,
);
}

_makeAuthors() {
const { authorName, authorEmail } = this.getInputValues(
"authorName",
"authorEmail"
"authorEmail",
);
return { authors: [`${authorName} <${authorEmail}>`] };
}

_toolPoetry() {
const verbatimInputs = this.inputs.filter(
(input) => !PoetryGenerator.authorInputNames.includes(input.name)
(input) => !PoetryGenerator.authorInputNames.includes(input.name),
);
const inputPaths = verbatimInputs.map(
(input) => input.extras.toolPoetryPath
const toolPoertyPaths = verbatimInputs.map(
(input) => input.extras.toolPoetryPath,
);
const inputValues = verbatimInputs.map((input) => input.value);
return {
..._.zipObjectDeep(inputPaths, inputValues),
const toolPoetryValues = verbatimInputs.map((input) => input.value);
const toolPoetry = {
..._.zipObjectDeep(toolPoertyPaths, toolPoetryValues),
...this._makeAuthors(),
};
}

async _queryCurrentPythonVersion() {
const { stdout } = await this.spawnCommand("python", ["--version"], {
stdio: "pipe",
});
return stdout.split(" ")[1];
return _.pickBy(toolPoetry, (value) => value !== null);
}
}
23 changes: 0 additions & 23 deletions generators/poetry/validate-input.js

This file was deleted.

12 changes: 6 additions & 6 deletions generators/python-package/index.js
@@ -1,13 +1,13 @@
import path from "node:path";

import InputGenerator from "../../lib/input-generator.js";
import inputs from "../../lib/inputs.js";
import { moduleDirName } from "../../lib/paths.js";
import sharedInputs from "../../lib/shared/inputs.js";

const parentDir = moduleDirName(import.meta);

export default class PythonPackageGenerator extends InputGenerator {
static inputs = [sharedInputs.packageName, sharedInputs.packageVersion];
static inputs = [inputs.packageName, inputs.packageVersion];

constructor(args, opts) {
super(args, opts, PythonPackageGenerator.inputs);
Expand All @@ -25,21 +25,21 @@ export default class PythonPackageGenerator extends InputGenerator {
writing() {
const { packageName, packageVersion: version } = this.getInputValues(
"packageName",
"packageVersion"
"packageVersion",
);
this.fs.copyTpl(
this.templatePath("__init__.py"),
this.destinationPath(packageName, "__init__.py"),
{ version }
{ version },
);
this.fs.copy(
this.templatePath("tests", "__init__.py"),
this.destinationPath("tests", "__init__.py")
this.destinationPath("tests", "__init__.py"),
);
this.fs.copyTpl(
this.templatePath("tests", "test.py"),
this.destinationPath("tests", `test_${packageName}.py`),
{ packageName, version }
{ packageName, version },
);
}
}
9 changes: 8 additions & 1 deletion lib/base-generator.js
Expand Up @@ -20,7 +20,7 @@ export default class BaseGenerator extends InputGenerator {
const dstFilePath = this.destinationPath(
dstDir === null
? srcFilePath
: path.join(dstDir, path.basename(srcFilePath))
: path.join(dstDir, path.basename(srcFilePath)),
);
const dst = serializer.read(dstFilePath);

Expand All @@ -38,6 +38,13 @@ export default class BaseGenerator extends InputGenerator {
return gitOriginUrl();
}

async _queryCurrentPythonVersion() {
const { stdout } = await this.spawnCommand("python", ["--version"], {
stdio: "pipe",
});
return stdout.split(" ")[1];
}

_readToml(filePath, defaults = "") {
return TOML.parse(this.fs.read(filePath, { defaults }));
}
Expand Down
2 changes: 1 addition & 1 deletion lib/input-factories.js
Expand Up @@ -12,7 +12,7 @@ export class InputFactory {

create(generator) {
const valueFuctions = _.mapValues(this.valueFunctions, (fn) =>
fn.bind(generator)
fn.bind(generator),
);
return new Input(this.ioConfig, valueFuctions, this.extras);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/input-generator.js
Expand Up @@ -24,14 +24,14 @@ export default class InputGenerator extends Generator {

throw new TypeError(
`Value "${error.value}" for option --${error.input.optionName} is ` +
`invalid: ${_.lowerFirst(error.reason)}`
`invalid: ${_.lowerFirst(error.reason)}`,
);
}
}

async prompting() {
const answers = await this.prompt(
this.inputs.map((input) => input.asPrompt())
this.inputs.map((input) => input.asPrompt()),
);
/*
* No need to catch InvalidInputValueError here, as inquirer doesn't allow
Expand Down

0 comments on commit 12d53ab

Please sign in to comment.