arguments.ts 2.91 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

/**
 *  Parse NNI manager's command line arguments.
 **/

import assert from 'assert/strict';

import yargs from 'yargs/yargs';

/**
 *  Command line arguments provided by "nni/experiment/launcher.py".
 *
 *  Hyphen-separated words are automatically converted to camelCases by yargs lib, but snake_cases are not.
 *  So it supports "--log-level" but does not support "--log_level".
 *
 *  Unfortunately I misunderstood "experiment_working_directory" config field when deciding the name.
 *  It defaults to "~/nni-experiments" rather than "~/nni-experiments/<experiment-id>",
 *  and further more the working directory is "site-packages/nni_node", not either.
 *  For compatibility concern we cannot change the public API, so there is an inconsistency here.
 **/
export interface NniManagerArgs {
    readonly port: number;
    readonly experimentId: string;
    readonly action: 'create' | 'resume' | 'view';
    readonly experimentsDirectory: string;  // renamed "config.experiment_working_directory", must be absolute
    readonly logLevel: 'critical' | 'error' | 'warning' | 'info' | 'debug';
    readonly foreground: boolean;
    readonly urlPrefix: string;  // leading and trailing "/" must be stripped

    // these are planned to be removed
    readonly mode: string;
    readonly dispatcherPipe: string | undefined;
}

export function parseArgs(rawArgs: string[]): NniManagerArgs {
liuzhe-lz's avatar
liuzhe-lz committed
38
    const parser = yargs(rawArgs).options(yargsOptions).strict().fail(false);
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
    const parsedArgs: NniManagerArgs = parser.parseSync();

    // strip yargs leftovers
    const argsAsAny: any = {};
    for (const key in yargsOptions) {
        argsAsAny[key] = (parsedArgs as any)[key];
        assert(!Number.isNaN(argsAsAny[key]), `Command line arg --${key} is not a number`);
    }
    if (argsAsAny.dispatcherPipe === '') {
        argsAsAny.dispatcherPipe = undefined;
    }
    const args: NniManagerArgs = argsAsAny;

    const prefixErrMsg = `Command line arg --url-prefix "${args.urlPrefix}" is not stripped`;
    assert(!args.urlPrefix.startsWith('/') && !args.urlPrefix.endsWith('/'), prefixErrMsg);

    return args;
}

const yargsOptions = {
    port: {
        demandOption: true,
        type: 'number'
    },
    experimentId: {
        demandOption: true,
        type: 'string'
    },
    action: {
        choices: [ 'create', 'resume', 'view' ] as const,
        demandOption: true
    },
    experimentsDirectory: {
        demandOption: true,
        type: 'string'
    },
    logLevel: {
        choices: [ 'critical', 'error', 'warning', 'info', 'debug' ] as const,
        demandOption: true
    },
    foreground: {
        default: false,
        type: 'boolean'
    },
    urlPrefix: {
        default: '',
        type: 'string'
    },

    mode: {
        default: '',
        type: 'string'
    },
    dispatcherPipe: {
        default: '',
        type: 'string'
    }
} as const;