Repository: oppia/oppia

Issue:
Update Troubleshooting wiki for error: can't combine user with prefix on linux.
<!--
  - Thanks for taking the time to report a bug in the Oppia project.
  - Before filing a new issue, please do a quick search to check that it hasn't
  - already been filed on the [issue tracker](https://github.com/oppia/oppia/issues)._
  -->

**Describe the bug**
pip install error: (can't combine user with prefix) 
(update wiki)

previous fix:
 line 149 in https://github.com/oppia/oppia/wiki/Troubleshooting
(add --user --prefix= --system after every pip command with --target in install_third_party.sh)


**Desktop (please complete the following information; delete this section if the issue does not arise on desktop):**
 - OS: Ubuntu 18.04

**Fix I think might work**
create a new file called `req.txt`                                                                                                                                                                             
    copy paste this into `req.txt`    
```
pylint==1.9.4    
Pillow==6.0.0     
pylint-quotes==0.1.8    
webtest==2.0.33    
isort==4.3.20    
pycodestyle==2.5.0    
esprima==4.0.1    
browsermob-proxy==0.8.0    
selenium==3.13.0    
PyGithub==1.43.7    
```

And then run
```
pip install -r req.txt --target ../oppia_tools/pyyaml-5.1.2 --user --prefix= --system
```
```
pip install future==0.17.1 --target third_party/future-0.17.1 --user --prefix= --system
```

And change line 109 in install_third_party_libs.py to 
```
         install_path, '--user', '--prefix=', '--system'])
```


Discussion:
Running `pip install -r req.txt --target ../oppia_tools/pyyaml-5.1.2 --user --prefix= --system` returns the error `No such option --system`
> Running `pip install -r req.txt --target ../oppia_tools/pyyaml-5.1.2 --user --prefix= --system` returns the error `No such option --system`

Never faced that error, what version of pip are you using? (`pip --version`) Also what os?
This is what I get:
```
  > pip install -r req.txt --target ../oppia_tools/pyyaml-5.1.2 --user --prefix= --system
  
  Usage:   
  pip install [options] <requirement specifier> [package-index-options] ...
  pip install [options] -r <requirements file> [package-index-options] ...
  pip install [options] [-e] <vcs project url> ...
  pip install [options] [-e] <local project path> ...
  pip install [options] <archive url/path> ...
  
  no such option: --system
```
I am using **pip 19.3.1** on a  **Ubuntu 18.04.3** system.

@ShivanshRakesh 
try this, use a virtual env and install pip 9.0.1. It should work then. 
But would that be a proper fix? I mean, shouldn't it work with all pip versions?
That is the only solution I can think of, I guess --system option got removed in some update (not sure though). 
I guess its better to ask on gitter if someone uses your pip version, or ask @seanlip .
There's going to be a small fix in https://github.com/oppia/oppia/pull/8145 that will tie the pip version with the appropriate python version (and vice versa). Once that PR goes in, give that a try.

Resolving patch:
diff --git a/scripts/install_third_party_libs.py b/scripts/install_third_party_libs.py
--- a/scripts/install_third_party_libs.py
+++ b/scripts/install_third_party_libs.py
@@ -125,9 +125,25 @@ def pip_install(package, version, install_path):
 
     # The call to python -m is used to ensure that Python and Pip versions are
     # compatible.
-    subprocess.check_call([
-        sys.executable, '-m', 'pip', 'install', '%s==%s' % (package, version),
-        '--target', install_path])
+    command = [
+        sys.executable, '-m', 'pip', 'install', '%s==%s'
+        % (package, version), '--target', install_path]
+    process = subprocess.Popen(
+        command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    stdout, stderr = process.communicate()
+    if process.returncode == 0:
+        python_utils.PRINT(stdout)
+    elif 'can\'t combine user with prefix' in stderr:
+        python_utils.PRINT('Trying by setting --user and --prefix flags.')
+        subprocess.check_call([
+            sys.executable, '-m', 'pip', 'install',
+            '%s==%s' % (package, version), '--target', install_path,
+            '--user', '--prefix=', '--system'])
+    else:
+        python_utils.PRINT(stderr)
+        python_utils.PRINT(
+            'Refer to https://github.com/oppia/oppia/wiki/Troubleshooting')
+        raise Exception('Error installing package')
 
 
 def install_skulpt(parsed_args):


Test patch:
diff --git a/scripts/install_third_party_libs_test.py b/scripts/install_third_party_libs_test.py
--- a/scripts/install_third_party_libs_test.py
+++ b/scripts/install_third_party_libs_test.py
@@ -49,18 +49,39 @@ def setUp(self):
         super(InstallThirdPartyLibsTests, self).setUp()
 
         self.check_function_calls = {
-            'check_call_is_called': False
+            'check_call_is_called': False,
         }
         self.print_arr = []
-        def mock_check_call(unused_cmd_tokens):
-            self.check_function_calls['check_call_is_called'] = True
         # pylint: disable=unused-argument
+        def mock_check_call(unused_cmd_tokens, *args, **kwargs):
+            self.check_function_calls['check_call_is_called'] = True
+            class Ret(python_utils.OBJECT):
+                """Return object with required attributes."""
+                def __init__(self):
+                    self.returncode = 0
+                def communicate(self):
+                    """Return required meathod."""
+                    return '', ''
+            return Ret()
+        def mock_popen_error_call(unused_cmd_tokens, *args, **kwargs):
+            class Ret(python_utils.OBJECT):
+                """Return object that gives user-prefix error."""
+                def __init__(self):
+                    self.returncode = 1
+                def communicate(self):
+                    """Return user-prefix error as stderr."""
+                    return '', 'can\'t combine user with prefix'
+            return Ret()
         def mock_print(msg, end=''):
             self.print_arr.append(msg)
         # pylint: enable=unused-argument
 
         self.check_call_swap = self.swap(
             subprocess, 'check_call', mock_check_call)
+        self.Popen_swap = self.swap(
+            subprocess, 'Popen', mock_check_call)
+        self.Popen_error_swap = self.swap(
+            subprocess, 'Popen', mock_popen_error_call)
         self.print_swap = self.swap(python_utils, 'PRINT', mock_print)
 
     def test_tweak_yarn_executable(self):
@@ -96,10 +117,21 @@ def test_get_yarn_command_on_mac(self):
             self.assertEqual(command, 'yarn')
 
     def test_pip_install_without_import_error(self):
-        with self.check_call_swap:
+        with self.Popen_swap:
             install_third_party_libs.pip_install('package', 'version', 'path')
         self.assertTrue(self.check_function_calls['check_call_is_called'])
 
+    def test_pip_install_with_user_prefix_error(self):
+        with self.Popen_error_swap:
+            with self.check_call_swap:
+                install_third_party_libs.pip_install('pkg', 'ver', 'path')
+        self.assertTrue(self.check_function_calls['check_call_is_called'])
+
+    def test_pip_install_exception_handling(self):
+        with self.assertRaises(Exception) as context:
+            install_third_party_libs.pip_install('package', 'version', 'path')
+        self.assertTrue('Error installing package' in context.exception)
+
     def test_pip_install_with_import_error_and_darwin_os(self):
         os_name_swap = self.swap(common, 'OS_NAME', 'Darwin')