diff options
Diffstat (limited to 'numpy/core/function_base.py')
-rw-r--r-- | numpy/core/function_base.py | 108 |
1 files changed, 76 insertions, 32 deletions
diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py index c1067299d..538ac8b84 100644 --- a/numpy/core/function_base.py +++ b/numpy/core/function_base.py @@ -3,6 +3,7 @@ from __future__ import division, absolute_import, print_function import functools import warnings import operator +import types from . import numeric as _nx from .numeric import (result_type, NaN, shares_memory, MAY_SHARE_BOUNDS, @@ -17,18 +18,6 @@ array_function_dispatch = functools.partial( overrides.array_function_dispatch, module='numpy') -def _index_deprecate(i, stacklevel=2): - try: - i = operator.index(i) - except TypeError: - msg = ("object of type {} cannot be safely interpreted as " - "an integer.".format(type(i))) - i = int(i) - stacklevel += 1 - warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel) - return i - - def _linspace_dispatcher(start, stop, num=None, endpoint=None, retstep=None, dtype=None, axis=None): return (start, stop) @@ -124,8 +113,13 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, >>> plt.show() """ - # 2016-02-25, 1.12 - num = _index_deprecate(num) + try: + num = operator.index(num) + except TypeError: + raise TypeError( + "object of type {} cannot be safely interpreted as an integer." + .format(type(num))) + if num < 0: raise ValueError("Number of samples, %s, must be non-negative." % num) div = (num - 1) if endpoint else num @@ -145,7 +139,7 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, # from overriding what class is produced, and thus prevents, e.g. use of Quantities, # see gh-7142. Hence, we multiply in place only for standard scalar types. _mult_inplace = _nx.isscalar(delta) - if num > 1: + if div > 0: step = delta / div if _nx.any(step == 0): # Special handling for denormal numbers, gh-5437 @@ -160,7 +154,8 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, else: y = y * step else: - # 0 and 1 item long sequences have an undefined step + # sequences with 0 items or 1 item with endpoint=True (i.e. div <= 0) + # have an undefined step step = NaN # Multiply with delta to allow possible override of output class. y = y * delta @@ -430,28 +425,66 @@ def geomspace(start, stop, num=50, endpoint=True, dtype=None, axis=0): return result.astype(dtype, copy=False) -#always succeed -def _add_docstring(obj, doc): +def _needs_add_docstring(obj): + """ + Returns true if the only way to set the docstring of `obj` from python is + via add_docstring. + + This function errs on the side of being overly conservative. + """ + Py_TPFLAGS_HEAPTYPE = 1 << 9 + + if isinstance(obj, (types.FunctionType, types.MethodType, property)): + return False + + if isinstance(obj, type) and obj.__flags__ & Py_TPFLAGS_HEAPTYPE: + return False + + return True + + +def _add_docstring(obj, doc, warn_on_python): + if warn_on_python and not _needs_add_docstring(obj): + warnings.warn( + "add_newdoc was used on a pure-python object {}. " + "Prefer to attach it directly to the source." + .format(obj), + UserWarning, + stacklevel=3) try: add_docstring(obj, doc) except Exception: pass -def add_newdoc(place, obj, doc): +def add_newdoc(place, obj, doc, warn_on_python=True): """ - Adds documentation to obj which is in module place. + Add documentation to an existing object, typically one defined in C - If doc is a string add it to obj as a docstring + The purpose is to allow easier editing of the docstrings without requiring + a re-compile. This exists primarily for internal use within numpy itself. - If doc is a tuple, then the first element is interpreted as - an attribute of obj and the second as the docstring - (method, docstring) - - If doc is a list, then each element of the list should be a - sequence of length two --> [(method1, docstring1), - (method2, docstring2), ...] + Parameters + ---------- + place : str + The absolute name of the module to import from + obj : str + The name of the object to add documentation to, typically a class or + function name + doc : {str, Tuple[str, str], List[Tuple[str, str]]} + If a string, the documentation to apply to `obj` + + If a tuple, then the first element is interpreted as an attribute of + `obj` and the second as the docstring to apply - ``(method, docstring)`` + + If a list, then each element of the list should be a tuple of length + two - ``[(method1, docstring1), (method2, docstring2), ...]`` + warn_on_python : bool + If True, the default, emit `UserWarning` if this is used to attach + documentation to a pure-python object. + Notes + ----- This routine never raises an error if the docstring can't be written, but will raise an error if the object being documented does not exist. @@ -459,12 +492,23 @@ def add_newdoc(place, obj, doc): in new-style classes or built-in functions. Because this routine never raises an error the caller must check manually that the docstrings were changed. + + Since this function grabs the ``char *`` from a c-level str object and puts + it into the ``tp_doc`` slot of the type of `obj`, it violates a number of + C-API best-practices, by: + + - modifying a `PyTypeObject` after calling `PyType_Ready` + - calling `Py_INCREF` on the str and losing the reference, so the str + will never be released + + If possible it should be avoided. """ new = getattr(__import__(place, globals(), {}, [obj]), obj) if isinstance(doc, str): - _add_docstring(new, doc.strip()) + _add_docstring(new, doc.strip(), warn_on_python) elif isinstance(doc, tuple): - _add_docstring(getattr(new, doc[0]), doc[1].strip()) + attr, docstring = doc + _add_docstring(getattr(new, attr), docstring.strip(), warn_on_python) elif isinstance(doc, list): - for val in doc: - _add_docstring(getattr(new, val[0]), val[1].strip()) + for attr, docstring in doc: + _add_docstring(getattr(new, attr), docstring.strip(), warn_on_python) |