Repository: biopython/biopython

Issue:
Pip throws error when installing.
I'm a simple man with simple needs. I need to install my requirements with pip. 

But whenever I try to do that I get an error that says I haven't installed numpy. I don't need numpy but biopython does and In python wonderland ,unless it's a system level dependency, packages should not throw errors about dependencies, instead they should define them inside "install_requires" option of setup.py.

Why do I have to do pip install numpy before do pip install -r requirements.txt ? Yes that's a first world problem but It's irritating to explicitly install numpy before installing biopython in my requirements.txt... Just, why... 😞 I install 70+ dependencies without problem, except biopython's "unusual desire" for numpy being preinstalled.

I might look like a jerk to you but this is very irritating and I believe you had serious discussions before coding this out-of-standard behavior. Just please try to fix this. Packages should not depend on user behavior, but should depend on setup.py's defined behavior... 😞 



Discussion:
@Cediddi  Can you provide a patch?

IIRC, this dependency mechanism requirements mechanism requires `setuptools` and (thus far) we've worked out the box with the standard library `distutils` when only that is available.

This is probably possible by adding the dependency to `setup_args` conditional on (a) setuptools, (b) C Python or very recent PyPy, but not Jython or IronPython etc. However last time we tried it, this didn't work, and we reverted the changes, see e.g. https://github.com/biopython/biopython/commit/bc828342716218b84908c1a4435163e564f31445 https://github.com/biopython/biopython/commit/742e6ff455cdabf81dee8e1cbc74bb369def4d84

CC @chapmanb

I think it would be better to put all the dependencies in a file `requirements.txt` so we can install them by making a `pip install -r requiremens.txt`

OK, if someone can prepare and test a `requirements.txt` with the relevant conditionals (i.e. do not require NumPy on Jython), that would be great.

Here is a minimal ``requirements.txt`` file which only request NumPy under CPython (not Jython, not PyPy):

```
# We want NumPy under CPython, but not under Jython.
# Currently need to test latest version of PyPy...
numpy; platform_python_implementation == 'CPython'
```

See https://pip.pypa.io/en/stable/reference/pip_install/#requirement-specifiers and https://www.python.org/dev/peps/pep-0496/

I have only tested this minimally on Linux...
I've tested following on both Linux & OSX

```
numpy; platform_version >= '2.7'
numpy; platform_python_version == 'CPython'
```

Included the `platform_version` restriction as biopython no longer supports 
python version 2.6
@m-charlton Shouldn't there be a single line for numpy combining all relevant conditions?

And did you make a copy/paste error because the second example won't work - ``platform_python_version`` returns a version number, not an implementation string, e.g.

```
$ python3 -c "import platform; print(platform.python_version())"
3.5.0
```

i.e. We need ``platform_python_implementation`` as in my example earlier.
Good catch, `platform_python_version` should read `platform_python_implementation`.

I tried to specify the constraints on one line, like so:

```
numpy; platform_version >= '2.7'; platform_python_implementation == 'CPython'
```

And I got a parsing error. Noticed the following example [here](https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format):

```
SomeProject ==5.4 ; python_version < '2.7'
SomeProject; sys_platform == 'win32'
```
And this format works. Seems bit verbose to specify the constraints over multiple lines.

I'm using pip version `9.0.1`

I don't think the documentation means to suggest using multiple lines - I thought they were just multiple examples.

In any case, we don't really need to put a Python version restriction on the dependency for NumPy - the entirety of Biopython now requires Python 2.7 or later.
Hopefully fixed with 363bc0c8c080a1546271bf42e2cd8a456766a9b0 which adds a minimal ``requirements.txt`` as follows:

```
# We want NumPy under CPython or PyPy, but not Jython
numpy; platform_python_implementation != 'Jython'
```

Note since the above discussion we've confirmed our NumPy dependent code works under the latest PyPy release, see #953 

There is scope to add a ``requirements-dev.txt`` file which includes all the optional/soft dependencies (eg ``reportlab``) and other things we currently use for testing like ``flake8``.
Oh, thank you. I believe with this last commit I can create a virtual environment and just run this command and it'll work.

    pip install biopython
This change hasn't yet been uploaded to PyPI (but it would be as part of Biopython 1.69), so for now you must probably still use:

```
pip install numpy
pip install biopython
```

Thank you so so much. This'll really solve a big problem for me.
Now that Biopython 1.69 is out with a simple ``requirements.txt`` file, did it help?
cool!
So I just installed biopython without having numpy first, and I don't quite get how it fails midway then concludes by saying biopython installed successfully. I mean why do I need to reinstall it? I feel like many users would just ignore the failure message if the last message is "Successfully installed biopython-1.69
" only to have inexplicable running issues later:

```
orabi@orabi-Lemur:~$ pip3 install biopython
Collecting biopython
  Downloading biopython-1.69.tar.gz (15.4MB)
    100% |████████████████████████████████| 15.4MB 111kB/s 
Building wheels for collected packages: biopython
  Running setup.py bdist_wheel for biopython ... error
  Complete output from command /usr/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-aa7gspce/biopython/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))" bdist_wheel -d /tmp/tmpp4yruvm0pip-wheel- --python-tag cp35:
  running bdist_wheel
  running build
  running build_py
  
  Numerical Python (NumPy) is not installed.
  
  This package is required for many Biopython features.  Please install
  it before you install Biopython. You can install Biopython anyway, but
  anything dependent on NumPy will not work. If you do this, and later
  install NumPy, you should then re-install Biopython.
  
  You can find NumPy at http://www.numpy.org
  
  
  ----------------------------------------
  Failed building wheel for biopython
  Running setup.py clean for biopython
Failed to build biopython
Installing collected packages: biopython
  Running setup.py install for biopython ... done
Successfully installed biopython-1.69
```
@baraaorabi It didn't fail - you have installed most of Biopython, just not the bits which need numpy. We should revisit this in light of recent changes in Biopython, see #1302.

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -121,28 +121,6 @@ def is_ironpython():
     # TODO - Use platform as in Pypy test?
 
 
-def get_yes_or_no(question, default):
-    if default:
-        option_str = "(Y/n)"
-        default_str = 'y'
-    else:
-        option_str = "(y/N)"
-        default_str = 'n'
-
-    while True:
-        print("%s %s:" % (question, option_str))
-        if sys.version_info[0] == 3:
-            response = input().lower()
-        else:
-            response = raw_input().lower()
-        if not response:
-            response = default_str
-        if response[0] in ['y', 'n']:
-            break
-        print("Please answer y or n.")
-    return response[0] == 'y'
-
-
 # Make sure we have the right Python version.
 if sys.version_info[:2] < (2, 7):
     sys.stderr.write("Biopython requires Python 2.7, or Python 3.4 or later. "
@@ -166,31 +144,6 @@ def check_dependencies_once():
     return _CHECKED
 
 
-def is_automated():
-    """Check for installation with easy_install or pip.
-    """
-    is_automated = False
-    # easy_install: --dist-dir option passed
-    try:
-        dist_dir_i = sys.argv.index("--dist-dir")
-    except ValueError:
-        dist_dir_i = None
-    if dist_dir_i is not None:
-        dist_dir = sys.argv[dist_dir_i + 1]
-        if "egg-dist-tmp" in dist_dir:
-            is_automated = True
-    # pip -- calls from python directly with "-c"
-    if sys.argv in [["-c", "develop", "--no-deps"],
-                    ["--no-deps", "-c", "develop"],
-                    ["-c", "egg_info"]] \
-                    or "pip-egg-info" in sys.argv \
-                    or sys.argv[:3] == ["-c", "install", "--record"] \
-                    or sys.argv[:4] == ['-c', 'install', '--single-version-externally-managed',
-                                        '--record']:
-        is_automated = True
-    return is_automated
-
-
 def check_dependencies():
     """Return whether the installation should continue."""
     # There should be some way for the user to tell specify not to
@@ -204,29 +157,16 @@ def check_dependencies():
     # We only check for NumPy, as this is a compile time dependency
     if is_Numpy_installed():
         return True
-    if is_automated():
-        return True  # For automated builds go ahead with installed packages
     if is_jython():
         return True  # NumPy is not avaliable for Jython (for now)
     if is_ironpython():
         return True  # We're ignoring NumPy under IronPython (for now)
 
-    print("""
-Numerical Python (NumPy) is not installed.
+    sys.exit("""Missing required dependency NumPy (Numerical Python).
 
-This package is required for many Biopython features.  Please install
-it before you install Biopython. You can install Biopython anyway, but
-anything dependent on NumPy will not work. If you do this, and later
-install NumPy, you should then re-install Biopython.
-
-You can find NumPy at http://www.numpy.org
+Unless running under Jython or IronPython, we require NumPy be installed
+when compiling Biopython. See http://www.numpy.org for details.
 """)
-    # exit automatically if running as part of some script
-    # (e.g. PyPM, ActiveState's Python Package Manager)
-    if not sys.stdout.isatty():
-        sys.exit(-1)
-    # We can ask the user
-    return get_yes_or_no("Do you want to continue this installation?", False)
 
 
 class install_biopython(install):
@@ -234,23 +174,7 @@ class install_biopython(install):
 
     This will just run the normal install, and then print warning messages
     if packages are missing.
-
     """
-    # Adds support for the single-version-externally-managed flag
-    # which is present in setuptools but not distutils. pip requires it.
-    # In setuptools this forces installation the "old way" which we
-    # only support here, so we just make it a no-op.
-    user_options = install.user_options + [
-        ('single-version-externally-managed', None,
-            "used by system package builders to create 'flat' eggs"),
-    ]
-    boolean_options = install.boolean_options + [
-        'single-version-externally-managed',
-    ]
-
-    def initialize_options(self):
-        install.initialize_options(self)
-        self.single_version_externally_managed = None
 
     def run(self):
         if check_dependencies_once():
@@ -269,6 +193,8 @@ def run(self):
             self.packages.remove("Bio.Restriction")
         # Add software that requires Numpy to be installed.
         if is_Numpy_installed():
+            # Should be everything (i.e. C Python or PyPy)
+            # except Jython and IronPython
             self.packages.extend(NUMPY_PACKAGES)
         build_py.run(self)
 
@@ -325,6 +251,18 @@ def is_Numpy_installed():
     return bool(can_import("numpy"))
 
 
+# Using requirements.txt is preferred for an application
+# (and likely will pin specific version numbers), using
+# setup.py's install_requires is preferred for a library
+# (and should try not to be overly narrow with versions).
+REQUIRES = [
+    'numpy',
+]
+
+if is_jython() or is_ironpython():
+    REQUIRES.remove("numpy")
+
+
 # --- set up the packages we are going to install
 # standard biopython packages
 PACKAGES = [
@@ -532,4 +470,5 @@ def is_Numpy_installed():
       package_data={
           'Bio.Entrez': ['DTDs/*.dtd', 'DTDs/*.ent', 'DTDs/*.mod'],
       },
+      install_requires=REQUIRES,
       )


Test patch: