Repository: microsoft/nni

Issue:
nnictl package install not working for venv
It seems that `nnictl package install` makes the package going to user level, which makes it not accessible from a venv.

Warning:

```
Installing collected packages: ConfigSpaceNNI, docutils, pynisher, pyrfr, sphinxcontrib-htmlhelp, sphinxcontrib-qthelp, sphinxcontrib-devhelp, imagesize, Jinja2, sphinxcontrib-applehelp, alabaster, sphinxcontrib-jsmath, Pygments, snowballstemmer, sphinxcontrib-serializinghtml, packaging, sphinx, sphinx-rtd-theme, smac
  Running setup.py install for ConfigSpaceNNI ... done
  The script pygmentize is installed in '/home/zhangyuge/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  Running setup.py install for snowballstemmer ... done
  The scripts sphinx-apidoc, sphinx-autogen, sphinx-build and sphinx-quickstart are installed in '/home/zhangyuge/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  Running setup.py install for smac ... done
```

Corresponding code:

https://github.com/microsoft/nni/blob/958efabf066b975a5cdcde293c9de33ec1430cb2/tools/nni_cmd/command_utils.py#L49

We could consider removing this `--user`.


Discussion:
@ultmaster Could you try to fix it?
Sure. By removing this `--user`. I just don't understand why we have it in the first place.
If users installed nni locally (not in virtual env), no `--user` means users have to use `sudo` to run pip command.
I don't know about this. As far as I remember, last time I run pip outside a virtual env, it automatically goes into some `~/.pip` without asking `sudo`. I think removing `--user` is better than adding this, because `--user` might contaminate the outside environment, which is definitely not wanted.

Meanwhile, I will check whether there is a way to tell `pip` is inside a virtualenv/conda or not.

Resolving patch:
diff --git a/tools/nni_cmd/command_utils.py b/tools/nni_cmd/command_utils.py
--- a/tools/nni_cmd/command_utils.py
+++ b/tools/nni_cmd/command_utils.py
@@ -3,10 +3,11 @@
 import os
 import signal
 import psutil
-from .common_utils import  print_error, print_normal, print_warning
+from .common_utils import print_error, print_normal, print_warning
+
 
 def check_output_command(file_path, head=None, tail=None):
-    '''call check_output command to read content from a file'''
+    """call check_output command to read content from a file"""
     if os.path.exists(file_path):
         if sys.platform == 'win32':
             cmds = ['powershell.exe', 'type', file_path]
@@ -26,8 +27,9 @@ def check_output_command(file_path, head=None, tail=None):
         print_error('{0} does not exist!'.format(file_path))
         exit(1)
 
+
 def kill_command(pid):
-    '''kill command'''
+    """kill command"""
     if sys.platform == 'win32':
         process = psutil.Process(pid=pid)
         process.send_signal(signal.CTRL_BREAK_EVENT)
@@ -35,21 +37,35 @@ def kill_command(pid):
         cmds = ['kill', str(pid)]
         call(cmds)
 
+
 def install_package_command(package_name):
-    '''install python package from pip'''
-    #TODO refactor python logic
-    if sys.platform == "win32":
-        cmds = 'python -m pip install --user {0}'.format(package_name)
-    else:
-        cmds = 'python3 -m pip install --user {0}'.format(package_name)
-    call(cmds, shell=True)
+    """
+    Install python package from pip.
+
+    Parameters
+    ----------
+    package_name: str
+        The name of package to be installed.
+    """
+    call(_get_pip_install() + [package_name], shell=False)
+
 
 def install_requirements_command(requirements_path):
-    '''install requirements.txt'''
-    cmds = 'cd ' + requirements_path + ' && {0} -m pip install --user -r requirements.txt'
-    #TODO refactor python logic
-    if sys.platform == "win32":
-        cmds = cmds.format('python')
-    else:
-        cmds = cmds.format('python3')
-    call(cmds, shell=True)
+    """
+    Install packages from `requirements.txt` in `requirements_path`.
+
+    Parameters
+    ----------
+    requirements_path: str
+        Path to the directory that contains `requirements.txt`.
+    """
+    call(_get_pip_install() + ["-r", os.path.join(requirements_path, "requirements.txt")], shell=False)
+
+
+def _get_pip_install():
+    python = "python" if sys.platform == "win32" else "python3"
+    ret = [python, "-m", "pip", "install"]
+    if "CONDA_DEFAULT_ENV" not in os.environ and "VIRTUAL_ENV" not in os.environ and \
+            (sys.platform != "win32" and os.getuid() != 0):  # on unix and not running in root
+        ret.append("--user")  # not in virtualenv or conda
+    return ret


Test patch: