Unverified Commit e21a6984 authored by liuzhe-lz's avatar liuzhe-lz Committed by GitHub
Browse files

[v2.0] Refactor code hierarchy (part 2) (#2987)

parent f98ee672
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
# Licensed under the MIT license. # Licensed under the MIT license.
from .utils import to_json from .utils import to_json
from .env_vars import trial_env_vars from .runtime.env_vars import trial_env_vars
from . import platform from .runtime import platform
__all__ = [ __all__ = [
......
...@@ -57,14 +57,14 @@ class Tuner(Recoverable): ...@@ -57,14 +57,14 @@ class Tuner(Recoverable):
See Also See Also
-------- --------
Builtin tuners: Builtin tuners:
:class:`~nni.hyperopt_tuner.hyperopt_tuner.HyperoptTuner` :class:`~nni.algorithms.hpo.hyperopt_tuner.hyperopt_tuner.HyperoptTuner`
:class:`~nni.evolution_tuner.evolution_tuner.EvolutionTuner` :class:`~nni.algorithms.hpo.evolution_tuner.evolution_tuner.EvolutionTuner`
:class:`~nni.smac_tuner.SMACTuner` :class:`~nni.algorithms.hpo.smac_tuner.SMACTuner`
:class:`~nni.gridsearch_tuner.GridSearchTuner` :class:`~nni.algorithms.hpo.gridsearch_tuner.GridSearchTuner`
:class:`~nni.networkmorphism_tuner.networkmorphism_tuner.NetworkMorphismTuner` :class:`~nni.algorithms.hpo.networkmorphism_tuner.networkmorphism_tuner.NetworkMorphismTuner`
:class:`~nni.metis_tuner.mets_tuner.MetisTuner` :class:`~nni.algorithms.hpo.metis_tuner.mets_tuner.MetisTuner`
:class:`~nni.ppo_tuner.PPOTuner` :class:`~nni.algorithms.hpo.ppo_tuner.PPOTuner`
:class:`~nni.gp_tuner.gp_tuner.GPTuner` :class:`~nni.algorithms.hpo.gp_tuner.gp_tuner.GPTuner`
""" """
def generate_parameters(self, parameter_id, **kwargs): def generate_parameters(self, parameter_id, **kwargs):
......
...@@ -9,8 +9,8 @@ import json_tricks ...@@ -9,8 +9,8 @@ import json_tricks
from schema import And from schema import And
from . import parameter_expressions from . import parameter_expressions
from .common import init_logger from .runtime.common import init_logger
from .env_vars import dispatcher_env_vars from .runtime.env_vars import dispatcher_env_vars
to_json = functools.partial(json_tricks.dumps, allow_nan=True) to_json = functools.partial(json_tricks.dumps, allow_nan=True)
......
# Copyright (c) Microsoft Corporation. # Copyright (c) Microsoft Corporation.
# Licensed under the MIT license. # Licensed under the MIT license.
"""
Script for installation and distribution.
You can use environment variable `NNI_RELEASE` to set release version.
If release version is not set, default to a development build whose version string will be `999.dev0`.
## Development ##
Build and install for development:
$ python setup.py develop
Uninstall:
$ pip uninstall nni
Remove generated files: (use "--all" to remove toolchain and built wheel)
$ python setup.py clean [--all]
Build TypeScript modules without install:
$ python setup.py build_ts
## Release ##
Build wheel package:
$ NNI_RELEASE=2.0 python setup.py build_ts
$ NNI_RELEASE=2.0 python setup.py bdist_wheel -p manylinux1_x86_64
Where "2.0" is version string and "manylinux1_x86_64" is platform.
The platform may also be "macosx_10_9_x86_64" or "win_amd64".
`build_ts` must be manually invoked before `bdist_wheel`,
or setuptools cannot locate JS files which should be packed into wheel.
"""
from distutils.cmd import Command
from distutils.command.build import build
from distutils.command.clean import clean
import glob
import os import os
from setuptools import setup import shutil
import setuptools
from setuptools.command.develop import develop
import setup_ts
version = '999.0.0-developing' dependencies = [
'astor',
'hyperopt==0.1.2',
'json_tricks',
'netifaces',
'numpy',
'psutil',
'ruamel.yaml',
'requests',
'responses',
'scipy',
'schema',
'PythonWebHDFS',
'colorama',
'scikit-learn>=0.23.2',
'pkginfo',
'websockets'
]
release = os.environ.get('NNI_RELEASE')
def _setup():
setuptools.setup(
name = 'nni',
version = release or '999.dev0',
description = 'Neural Network Intelligence project',
long_description = open('README.md', encoding='utf-8').read(),
long_description_content_type = 'text/markdown',
url = 'https://github.com/Microsoft/nni',
author = 'Microsoft NNI Team',
author_email = 'nni@microsoft.com',
license = 'MIT',
classifiers = [
'License :: OSI Approved :: MIT License',
'Operating System :: MacOS :: MacOS X',
'Operating System :: Microsoft :: Windows :: Windows 10',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python :: 3 :: Only',
'Topic :: Scientific/Engineering :: Artificial Intelligence',
],
packages = _find_python_packages(),
package_data = {
'nni': ['**/requirements.txt'],
'nni_node': _find_node_files() # note: this does not work before building
},
python_requires = '>=3.6',
install_requires = dependencies,
setup_requires = ['requests'],
entry_points = {
'console_scripts' : [
'nnictl = nni.tools.nnictl.nnictl:parse_args'
]
},
cmdclass = {
'build': Build,
'build_ts': BuildTs,
'clean': Clean,
'develop': Develop,
}
)
def _find_python_packages(): def _find_python_packages():
...@@ -24,45 +138,65 @@ def _find_node_files(): ...@@ -24,45 +138,65 @@ def _find_node_files():
return sorted(files) return sorted(files)
setup( class BuildTs(Command):
name = 'nni', description = 'build TypeScript modules'
version = version,
author = 'Microsoft NNI Team', user_options = []
author_email = 'nni@microsoft.com',
description = 'Neural Network Intelligence project', def initialize_options(self):
long_description = open('README.md', encoding='utf-8').read(), pass
license = 'MIT',
url = 'https://github.com/Microsoft/nni', def finalize_options(self):
pass
packages = _find_python_packages(),
package_data = { def run(self):
'nni': ['**/requirements.txt'], setup_ts.build(release)
'nni_node': _find_node_files()
}, class Build(build):
def run(self):
python_requires = '>=3.6', assert release, 'Please set environment variable "NNI_RELEASE=<release_version>"'
install_requires = [ assert os.path.isfile('nni_node/main.js'), 'Please run "build_ts" before "build"'
'astor', assert not os.path.islink('nni_node/main.js'), 'This is a development build'
'hyperopt==0.1.2', super().run()
'json_tricks',
'netifaces', class Develop(develop):
'numpy', def finalize_options(self):
'psutil', self.user = True # always use `develop --user`
'ruamel.yaml', super().finalize_options()
'requests',
'responses', def run(self):
'scipy', setup_ts.build(release=None)
'schema', super().run()
'PythonWebHDFS',
'colorama', class Clean(clean):
'scikit-learn>=0.23.2', def finalize_options(self):
'pkginfo', self._all = self.all
'websockets' self.all = True # always use `clean --all`
], super().finalize_options()
entry_points = { def run(self):
'console_scripts' : [ super().run()
'nnictl = nni.nni_cmd.nnictl:parse_args' setup_ts.clean(self._all)
] _clean_temp_files()
} shutil.rmtree('nni.egg-info', ignore_errors=True)
) if self._all:
shutil.rmtree('dist', ignore_errors=True)
def _clean_temp_files():
for pattern in _temp_files:
for path in glob.glob(pattern):
if os.path.islink(path) or os.path.isfile(path):
os.remove(path)
else:
shutil.rmtree(path)
_temp_files = [
# unit test
'test/model_path/',
'test/temp.json',
'test/ut/sdk/*.pth'
]
_setup()
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT license.
"""
Script for building TypeScript modules.
This script is called by `setup.py` and common users should avoid using this directly.
It compiles TypeScript source files in `ts` directory,
and copies (or links) JavaScript output as well as dependencies to `nni_node`.
You can set environment `GLOBAL_TOOLCHAIN=1` to use global node and yarn, if you know what you are doing.
"""
from io import BytesIO
import json
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tarfile
from zipfile import ZipFile
node_version = 'v10.22.1'
yarn_version = 'v1.22.10'
def build(release):
"""
Compile TypeScript modules and copy or symlink to nni_node directory.
`release` is the version number without leading letter "v".
If `release` is None or empty, this is a development build and uses symlinks;
otherwise this is a release build and copies files instead.
"""
if release or not os.environ.get('GLOBAL_TOOLCHAIN'):
download_toolchain()
compile_ts()
if release:
copy_nni_node(release)
else:
symlink_nni_node()
def clean(clean_all=False):
"""
Remove TypeScript-related intermediate files.
Python intermediate files are not touched here.
"""
clear_nni_node()
for path in generated_directories:
shutil.rmtree(path, ignore_errors=True)
if clean_all:
shutil.rmtree('toolchain', ignore_errors=True)
Path('nni_node', node_executable).unlink()
if sys.platform == 'linux' or sys.platform == 'darwin':
node_executable = 'node'
node_spec = f'node-{node_version}-{sys.platform}-x64'
node_download_url = f'https://nodejs.org/dist/latest-v10.x/{node_spec}.tar.xz'
node_extractor = lambda data: tarfile.open(fileobj=BytesIO(data), mode='r:xz')
node_executable_in_tarball = 'bin/node'
elif sys.platform == 'win32':
node_executable = 'node.exe'
node_spec = f'node-{node_version}-win-x64'
node_download_url = f'https://nodejs.org/dist/latest-v10.x/{node_spec}.zip'
node_extractor = lambda data: ZipFile(BytesIO(data))
node_executable_in_tarball = 'node.exe'
else:
raise RuntimeError('Unsupported system')
yarn_executable = 'yarn' if sys.platform != 'win32' else 'yarn.cmd'
yarn_download_url = f'https://github.com/yarnpkg/yarn/releases/download/{yarn_version}/yarn-{yarn_version}.tar.gz'
def download_toolchain():
"""
Download and extract node and yarn,
then copy node executable to nni_node directory.
"""
if Path('nni_node', node_executable).is_file():
return
Path('toolchain').mkdir(exist_ok=True)
import requests # place it here so setup.py can install it before importing
_print(f'Downloading node.js from {node_download_url}')
resp = requests.get(node_download_url)
resp.raise_for_status()
_print('Extracting node.js')
tarball = node_extractor(resp.content)
tarball.extractall('toolchain')
shutil.rmtree('toolchain/node', ignore_errors=True)
Path('toolchain', node_spec).rename('toolchain/node')
_print(f'Downloading yarn from {yarn_download_url}')
resp = requests.get(yarn_download_url)
resp.raise_for_status()
_print('Extracting yarn')
tarball = tarfile.open(fileobj=BytesIO(resp.content), mode='r:gz')
tarball.extractall('toolchain')
shutil.rmtree('toolchain/yarn', ignore_errors=True)
Path(f'toolchain/yarn-{yarn_version}').rename('toolchain/yarn')
src = Path('toolchain/node', node_executable_in_tarball)
dst = Path('nni_node', node_executable)
shutil.copyfile(src, dst)
def compile_ts():
"""
Use yarn to download dependencies and compile TypeScript code.
"""
_print('Building NNI manager')
_yarn('ts/nni_manager')
_yarn('ts/nni_manager', 'build')
# todo: I don't think these should be here
shutil.rmtree('ts/nni_manager/dist/config', ignore_errors=True)
shutil.copytree('ts/nni_manager/config', 'ts/nni_manager/dist/config')
_print('Building web UI')
_yarn('ts/webui')
_yarn('ts/webui', 'build')
_print('Building NAS UI')
_yarn('ts/nasui')
_yarn('ts/nasui', 'build')
def symlink_nni_node():
"""
Create symlinks to compiled JS files.
If you manually modify and compile TS source files you don't need to install again.
"""
_print('Creating symlinks')
clear_nni_node()
for path in Path('ts/nni_manager/dist').iterdir():
_symlink(path, Path('nni_node', path.name))
_symlink('ts/nni_manager/package.json', 'nni_node/package.json')
_symlink('ts/nni_manager/node_modules', 'nni_node/node_modules')
_symlink('ts/webui/build', 'nni_node/static')
Path('nni_node/nasui').mkdir(exist_ok=True)
_symlink('ts/nasui/build', 'nni_node/nasui/build')
_symlink('ts/nasui/server.js', 'nni_node/nasui/server.js')
def copy_nni_node(version):
"""
Copy compiled JS files to nni_node.
This is meant for building release package, so you need to provide version string.
The version will written to `package.json` in nni_node directory,
while `package.json` in ts directory will be left unchanged.
"""
_print('Copying files')
clear_nni_node()
# copytree(..., dirs_exist_ok=True) is not supported by Python 3.6
for path in Path('ts/nni_manager/dist').iterdir():
if path.is_file():
shutil.copyfile(path, Path('nni_node', path.name))
else:
shutil.copytree(path, Path('nni_node', path.name))
package_json = json.load(open('ts/nni_manager/package.json'))
if version.count('.') == 1: # node.js semver requires at least three parts
version = version + '.0'
package_json['version'] = version
json.dump(package_json, open('nni_node/package.json', 'w'), indent=2)
_yarn('ts/nni_manager', '--prod', '--cwd', str(Path('nni_node').resolve()))
shutil.copytree('ts/webui/build', 'nni_node/static')
Path('nni_node/nasui').mkdir(exist_ok=True)
shutil.copytree('ts/nasui/build', 'nni_node/nasui/build')
shutil.copyfile('ts/nasui/server.js', 'nni_node/nasui/server.js')
def clear_nni_node():
"""
Remove compiled files in nni_node.
Use `clean()` if you what to remove files in ts as well.
"""
for path in Path('nni_node').iterdir():
if path.name not in ('__init__.py', 'node', 'node.exe'):
if path.is_symlink() or path.is_file():
path.unlink()
else:
shutil.rmtree(path)
_yarn_env = dict(os.environ)
_yarn_env['PATH'] = str(Path('nni_node').resolve()) + ':' + os.environ['PATH']
_yarn_path = Path('toolchain/yarn/bin', yarn_executable).resolve()
def _yarn(path, *args):
if os.environ.get('GLOBAL_TOOLCHAIN'):
subprocess.run(['yarn', *args], cwd=path, check=True)
else:
subprocess.run([_yarn_path, *args], cwd=path, check=True, env=_yarn_env)
def _symlink(target_file, link_location):
target = Path(target_file)
link = Path(link_location)
relative = os.path.relpath(target, link.parent)
link.symlink_to(relative, target.is_dir())
def _print(*args):
print('\033[1;36m# ', end='')
print(*args, end='')
print('\033[0m')
generated_directories = [
'ts/nni_manager/dist',
'ts/nni_manager/node_modules',
'ts/webui/build',
'ts/webui/node_modules',
'ts/nasui/build',
'ts/nasui/node_modules',
]
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
branch = True branch = True
parallel = True parallel = True
data_file = ${COVERAGE_DATA_FILE} data_file = ${COVERAGE_DATA_FILE}
source = nni, nni_cmd, nni_trial_tool source = nni, nni.tools.cmd, nni.tools.trial_tool
concurrency = multiprocessing concurrency = multiprocessing
......
...@@ -147,8 +147,8 @@ testCases: ...@@ -147,8 +147,8 @@ testCases:
config: config:
maxTrialNum: 4 maxTrialNum: 4
trialConcurrency: 4 trialConcurrency: 4
launchCommand: python3 -c 'from nni.nnicli import Experiment; exp = Experiment(); exp.start_experiment("$configFile")' launchCommand: python3 -c 'from nni.experiment import Experiment; exp = Experiment(); exp.start_experiment("$configFile")'
stopCommand: python3 -c 'from nni.nnicli import Experiment; exp = Experiment(); exp.connect_experiment("http://localhost:8080/"); exp.stop_experiment()' stopCommand: python3 -c 'from nni.experiment import Experiment; exp = Experiment(); exp.connect_experiment("http://localhost:8080/"); exp.stop_experiment()'
validator: validator:
class: NnicliValidator class: NnicliValidator
platform: linux darwin platform: linux darwin
......
...@@ -110,8 +110,8 @@ testCases: ...@@ -110,8 +110,8 @@ testCases:
config: config:
maxTrialNum: 4 maxTrialNum: 4
trialConcurrency: 4 trialConcurrency: 4
launchCommand: python3 -c 'from nni.nnicli import Experiment; exp = Experiment(); exp.start_experiment("$configFile")' launchCommand: python3 -c 'from nni.experiment import Experiment; exp = Experiment(); exp.start_experiment("$configFile")'
stopCommand: python3 -c 'from nni.nnicli import Experiment; exp = Experiment(); exp.connect_experiment("http://localhost:8080/"); exp.stop_experiment()' stopCommand: python3 -c 'from nni.experiment import Experiment; exp = Experiment(); exp.connect_experiment("http://localhost:8080/"); exp.stop_experiment()'
validator: validator:
class: NnicliValidator class: NnicliValidator
platform: linux darwin platform: linux darwin
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment