1# Copyright 2017 Jacob D. Durrant
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Loads external modules (numpy, scipy) if available. Otherwise, uses cheap
16imitations."""
17
18from __future__ import absolute_import
19from __future__ import print_function
20import textwrap
21import sys
22
23# A list of the available dependencies.
24dependencies_available = []
25
26# A flag for supressing errors
27missing_dependency_throws_error = True
28
29# Try to load numpy.
30force_dumbpy = False  # True for debugging.
31
32if '__pypy__' in sys.builtin_module_names:
33    # It's pypy, so don't load numpy
34    force_dumbpy = True
35if len(sys.argv) > 1 and sys.argv[1].upper() == "NODEPENS":
36    # The command-line parameter indicates you shouldn't use dependencies
37    force_dumbpy = True
38
39# Python3 requires soe minor tweaks
40python_version = sys.version_info[0]
41
42try:
43    # Try to load traditional numpy
44    if force_dumbpy: raise ValueError('Using dumbpy')
45
46    from numpy import array
47    from numpy.core.defchararray import strip as defchararray_strip
48    from numpy.core.defchararray import add as defchararray_add
49    from numpy.core.defchararray import rjust as defchararray_rjust
50    from numpy.core.defchararray import upper as defchararray_upper
51    from numpy.core.defchararray import lstrip as defchararray_lstrip
52    from numpy.core.defchararray import split as defchararray_split
53    from numpy.lib.recfunctions import append_fields
54    from numpy import genfromtxt
55    from numpy import nonzero
56    from numpy import logical_or
57    from numpy import core
58    from numpy import dtype
59    from numpy import vstack
60    from numpy import zeros
61    from numpy import max
62    from numpy import unique
63    from numpy import insert
64    from numpy import logical_not
65    from numpy import logical_and
66    from numpy import append
67    from numpy import arange
68    from numpy import savez
69    from numpy import load
70    from numpy import ones
71    from numpy import empty
72    from numpy import sum
73    from numpy import min
74    from numpy import delete
75    from numpy import setdiff1d
76    from numpy import hstack
77    from numpy import intersect1d
78    from numpy import setxor1d
79    from numpy import power
80    from numpy import cos
81    from numpy import sin
82    from numpy import sqrt
83    from numpy import dot
84    from numpy import linalg
85    from numpy import amin
86    from numpy import lib
87    from numpy import identity
88    from numpy import mean
89    from numpy import transpose
90    from numpy import argmax
91    from numpy import ma
92    from numpy import arccos
93    from numpy import cross
94    from numpy import arctan2
95    from numpy import fabs
96    from numpy import ones
97    from numpy import empty
98    from numpy import sum
99    from numpy import delete
100    from numpy import setdiff1d
101    from numpy import power
102    from numpy import cos
103    from numpy import sin
104    from numpy import sqrt
105    from numpy import identity
106    from numpy import mean
107    from numpy.linalg import norm
108    from numpy import fabs
109    from numpy.lib.recfunctions import stack_arrays
110    from numpy import std  # Note that there is no dumbpy equivalent yet.
111    from numpy import ndarray
112    from numpy import degrees
113    from numpy import radians
114
115    def get_col(arr, num):
116        return arr[:, num]
117
118    dependencies_available.append("NUMPY")
119except:
120    # Numpy not available, so load substitute (dumbpy)
121    from .Array import array
122    from .Utils import genfromtxt
123    from .Utils import nonzero
124    from .Utils import logical_or
125    from .Utils import logical_not
126    from .Utils import logical_and
127    from .Utils import defchararray_strip
128    from .Utils import defchararray_add
129    from .Utils import defchararray_rjust
130    from .Utils import defchararray_upper
131    from .Utils import defchararray_lstrip
132    from .Utils import defchararray_split
133    from .DType import dtype
134    from .Utils import vstack
135    from .Utils import append_fields
136    from .Utils import zeros
137    from .Utils import _max as max
138    from .Utils import _min as min
139    from .Utils import unique
140    from .Utils import insert
141    from .Utils import append
142    from .Utils import arange
143    from .Utils import get_col
144    from .Utils import ones
145    from .Utils import empty
146    from .Utils import sum
147    from .Utils import delete
148    from .Utils import setdiff1d
149    from .Utils import power
150    from .Utils import _cos as cos
151    from .Utils import _sin as sin
152    from .Utils import sqrt
153    from .Utils import identity
154    from .Utils import mean
155    from .Utils import norm
156    from .Utils import fabs
157    from .Utils import stack_arrays
158    from .Utils import transpose
159
160    dependencies_available.append("DUMBPY")
161
162try:
163    # Try to load traditional scipy
164    if force_dumbpy: raise ValueError('Using dumbpy')
165
166    from scipy.spatial.distance import squareform
167    from scipy.spatial.distance import pdist
168    from scipy.spatial.distance import cdist
169    dependencies_available.append("SCIPY")
170except:
171    # Use cheap replacement instead. None of these functions are available
172    # through dumbpy.
173    pass
174
175
176def class_dependency(action, dependency):
177    """Determines whether or not a given dependency is available.
178
179        Args:
180            action     -- A string describing the action you'd like to
181                          perform.
182            dependency -- A string, the dependency required for that action.
183
184        Returns:
185            A boolean, true if the dependency is available. Prints a message
186                otherwise.
187    """
188
189    global dependencies_available
190    global missing_dependency_throws_error
191
192    if not dependency in dependencies_available:
193        t = textwrap.TextWrapper(width = 60, subsequent_indent="       ")
194        print()
195        print(("\n".join(t.wrap("Error: Cannot " + action +
196                               ". Install this recommended dependency: " +
197                               dependency))))
198        print()
199        if missing_dependency_throws_error:
200            raise ImportError("The " + dependency + " module is not available.")
201            return False
202        else:
203            return False
204    return True
205