// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. /** * Collection of global objects. * * Although global is anti-pattern in OOP, there are two scenarios NNI uses globals. * * 1. Some constant configs (like command line args) are accessed here and there with util functions. * It is possible to pass parameters instead, but not worthy the refactor. * * 2. Some singletons (like root logger) are indeed global. * The singletons need to be registered in `global` to support 3rd-party training services, * because they are compiled outside NNI manager and therefore module scope singletons will not work. **/ import assert from 'assert/strict'; import { NniManagerArgs, parseArgs } from './arguments'; import { NniPaths, createPaths } from './paths'; import { LogStream, initLogStream } from './log_stream'; export { NniManagerArgs, NniPaths }; /** * Collection of global objects. * * It can be obtained with `import globals from 'common/globals'` or `global.nni`. * The former is preferred because it exposes less underlying implementations. **/ export interface NniGlobals { readonly args: NniManagerArgs; readonly paths: NniPaths; readonly logStream: LogStream; } // give type hint to `global.nni` (copied from SO, dunno how it works) declare global { var nni: NniGlobals; // eslint-disable-line } // prepare the namespace object and export it if (global.nni === undefined) { global.nni = {} as NniGlobals; } const globals: NniGlobals = global.nni; export default globals; /** * Initialize globals. * Must and must only be invoked once in "main.ts". **/ export function initGlobals(): void { assert.deepEqual(global.nni, {}); const args = parseArgs(process.argv.slice(2)); const paths = createPaths(args); const logStream = initLogStream(args, paths); const globals: NniGlobals = { args, paths, logStream }; Object.assign(global.nni, globals); }