Skip to content
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Within HTML:
<script src="https://unpkg.com/python-argparse-generator/lib/index.js"></script>
</head>
<script>
const pythonCode = GenerateCode([
const pythonCode = argparseCode([
{ name: 'folder', type: 'str', variableName: 'folder', default: '"/data"', defaultDisplay: '"/data"' },
{ name: 'limit', type: 'int', variableName: 'limit', default: '10', defaultDisplay: '10' },
]);
Expand All @@ -40,14 +40,14 @@ Within HTML:
Within Typescript:

```ts
import { Argument, GenerateCode } from 'python-argparse-generator';
import { Argument, argparseCode } from 'python-argparse-generator';

const args: Argument[] = [
{ name: 'folder', type: 'str', variableName: 'folder', default: '"/data"', defaultDisplay: '"/data"' },
{ name: 'limit', type: 'int', variableName: 'limit', default: '10', defaultDisplay: '10' },
];

const pythonCode = GenerateCode(args);
const pythonCode = argparseCode(args);
```

Where content of `pythonCode` string is:
Expand Down
104 changes: 93 additions & 11 deletions python-argparse-generator/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import { GenerateCode } from '../index';

test('Basic check of output', () => {
expect(
GenerateCode([
{ name: 'folder', type: 'str', variableName: 'folder', default: '"/data"', defaultDisplay: '"/data"' },
{ name: 'limit', type: 'int', variableName: 'limit', default: '10', defaultDisplay: '10' },
]),
).toBe(`import argparse
import { argparseCode, newArgument, defaultSettings } from '../index';

describe('Check Of argparseCode Output', () => {
const args = [
{
name: 'folder',
type: 'str',
variableName: 'folder',
default: '/data',
required: true,
},
{ name: 'limit', type: 'int', variableName: 'limit', default: '10', required: true },
];
test('Basic check of output', () => {
expect(
argparseCode([
{
name: 'folder',
type: 'str',
variableName: 'folder',
default: '/data',
required: true,
},
{ name: 'limit', type: 'int', variableName: 'limit', default: '10', required: true },
]),
).toBe(`import argparse
from typing import Dict, Any



def main(folder: str, limit: int) -> None:
# Contents of main
return
Expand All @@ -26,8 +44,72 @@ def cli() -> Dict[str, Any]:
return {"folder": args.folder,
"limit": int(args.limit)}


if __name__ == '__main__':
args = cli()
main(folder=args["folder"], limit=args["limit"])
main(folder=args["folder"],
limit=args["limit"])
`);
});
test('Type Hints False', () => {
let settings = defaultSettings();
settings.typeHints = false;
expect(argparseCode(args, settings)).toBe(`import argparse


def main(folder, limit):
# Contents of main
return


def cli():
formatter_class = argparse.ArgumentDefaultsHelpFormatter
parser = argparse.ArgumentParser(formatter_class=formatter_class)

parser.add_argument("folder", type=str, default="/data")
parser.add_argument("limit", type=int, default=10)

args = parser.parse_args()

return {"folder": args.folder,
"limit": int(args.limit)}


if __name__ == '__main__':
args = cli()
main(folder=args["folder"],
limit=args["limit"])
`);
});
});

describe('New Argument Factory', () => {
test('Default Parameters', () => {
expect(newArgument('folder', 'str')).toStrictEqual({
name: 'folder',
type: 'str',
default: '',
variableName: 'folder',
required: false,
});
});
// TODO really I should have a variableNameCreate function
test('Variable Name Creation', () => {
expect(newArgument('time-delay', 'str')).toStrictEqual({
name: 'time-delay',
type: 'str',
default: '',
variableName: 'timedelay',
required: false,
});
});
test('All Params', () => {
expect(newArgument('folder', 'str', 'folder', '/data', true)).toStrictEqual({
name: 'folder',
type: 'str',
default: '/data',
variableName: 'folder',
required: true,
});
});
});
97 changes: 75 additions & 22 deletions python-argparse-generator/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,112 @@
export type Settings = {
parserName: string;
typeHints: boolean;
mainContents: string;
mainName: string;
cliName: string;
argsName: string;
};

export const defaultSettings = (): Settings => {
return {
parserName: 'parser',
typeHints: true,
mainContents: '# Contents of main',
mainName: 'main',
cliName: 'cli',
argsName: 'args',
};
};

export type Argument = {
name: string;
type: string;
variableName: string;
default: string;
defaultDisplay: string;
required: boolean;
};

function argumentToText(argument: Argument) {
export const newArgument = (
name: string,
type: string,
variableName: string = '',
defaultValue: string = '',
required: boolean = false,
): Argument => {
if (variableName === '') {
variableName = name.replace(/-/g, '');
}
return { name, type, variableName, default: defaultValue, required };
};

function argumentToText(argument: Argument, parserName: string) {
switch (argument.type) {
case 'bool':
return `parser.add_argument("${argument.name}", action="store_${argument.default}")`;
break;
return `${parserName}.add_argument("${argument.name}", action="store_${argument.default}")`;
default:
let defaultDisplay;

if (argument.type === 'str') {
defaultDisplay = `"${argument.default}"`;
} else {
defaultDisplay = argument.default;
}

if (argument.default === undefined) {
return `parser.add_argument("${argument.name}", type=${argument.type})`;
return `${parserName}.add_argument("${argument.name}", type=${argument.type})`;
} else {
return `parser.add_argument("${argument.name}", type=${argument.type}, default=${argument.defaultDisplay})`;
return `${parserName}.add_argument("${argument.name}", type=${argument.type}, default=${defaultDisplay})`;
}
}
}

const argumentToMainParams = (argument: Argument) => `${argument.variableName}: ${argument.type}`;
const argumentToMainParams = (argument: Argument, typeHints: boolean) => {
if (typeHints) {
return `${argument.variableName}: ${argument.type}`;
} else {
return `${argument.variableName}`;
}
};

export const GenerateCode = (args: Argument[]) => {
const mainParameters: string[] = args.map((arg) => argumentToMainParams(arg));
const argumentsText: string[] = args.map((arg) => argumentToText(arg));
export const argparseCode = (args: Argument[], settings: Settings = defaultSettings()) => {
const parserName: string = settings.parserName;
const argsName: string = settings.argsName;
const mainParameters: string[] = args.map((arg) => argumentToMainParams(arg, settings.typeHints));
const argumentsText: string[] = args.map((arg) => argumentToText(arg, parserName));
const returnText: string[] = args.map((x) => {
if (x.type === 'str') {
return `"${x.variableName}": args.${x.variableName},`;
return `"${x.variableName}": ${argsName}.${x.variableName},`;
} else {
return `"${x.variableName}": ${x.type}(args.${x.variableName}),`;
return `"${x.variableName}": ${x.type}(${argsName}.${x.variableName}),`;
}
});

const output = `import argparse
from typing import Dict, Any

def main(${mainParameters.join(', ')}) -> None:
# Contents of main
const mainReturnType = settings.typeHints ? ' -> None' : '';
const cliReturnType = settings.typeHints ? ' -> Dict[str, Any]' : '';
const typeImports = settings.typeHints ? '\nfrom typing import Dict, Any' : '';

const output = `import argparse${typeImports}


def ${settings.mainName}(${mainParameters.join(', ')})${mainReturnType}:
${settings.mainContents}
return


def cli() -> Dict[str, Any]:
def ${settings.cliName}()${cliReturnType}:
formatter_class = argparse.ArgumentDefaultsHelpFormatter
parser = argparse.ArgumentParser(formatter_class=formatter_class)
${parserName} = argparse.ArgumentParser(formatter_class=formatter_class)

${argumentsText.join('\n ')}

args = parser.parse_args()
${argsName} = ${parserName}.parse_args()

return {${returnText.join('\n ').slice(0, -1)}}


if __name__ == '__main__':
args = cli()
main(${args.map((x) => `${x.variableName}=args["${x.variableName}"]`).join(', ')})
${argsName} = ${settings.cliName}()
${settings.mainName}(${args.map((x) => `${x.variableName}=${argsName}["${x.variableName}"]`).join(',\n ')})
`;
return output;
};