@@ -328,7 +328,7 @@ def key_to_indices(key, length):
328328
329329def _index_generator (new_indices , old_indices ):
330330 """Find locations of new_indicies in the ref. frame of the old_indices.
331-
331+
332332 Example: (1, 3), (1, 3, 5, 10) -> (3, 10)
333333
334334 The point of all this trouble is that this is done lazily, returning
@@ -452,7 +452,7 @@ def __setstate__(self, data_as_list):
452452 return self .__init__ (data_as_list , lambda x : x )
453453
454454
455- def pipeline (func ):
455+ def pipeline (func = None , ** kwargs ):
456456 """Decorator to enable lazy evaluation of a function.
457457
458458 When the function is applied to a Slicerator or Pipeline object, it
@@ -469,22 +469,35 @@ def pipeline(func):
469469 --------
470470 Pipeline
471471
472- Example
473- -------
472+ Examples
473+ --------
474474 Apply the pipeline decorator to your image processing function.
475+
475476 >>> @pipeline
476477 ... def color_channel(image, channel):
477478 ... return image[channel, :, :]
478479 ...
479480
481+
482+ In order to preserve the original function's doc string (i. e. do not add
483+ a note saying that it was made lazy), use the decorator like so:
484+
485+ >>> @pipeline(retain_doc=True)
486+ ... def color_channel(image, channel):
487+ ... '''This doc string will not be changed'''
488+ ... return image[channel, :, :]
489+
490+
480491 Passing a Slicerator the function returns a Pipeline
481492 that "lazily" applies the function when the images come out. Different
482493 functions can be applied to the same underlying images, creating
483494 independent objects.
495+
484496 >>> red_images = color_channel(images, 0)
485497 >>> green_images = color_channel(images, 1)
486498
487499 Pipeline functions can also be composed.
500+
488501 >>> @pipeline
489502 ... def rescale(image):
490503 ... return (image - image.min())/image.ptp()
@@ -493,9 +506,35 @@ def pipeline(func):
493506
494507 The function can still be applied to ordinary images. The decorator
495508 only takes affect when a Slicerator object is passed.
509+
496510 >>> single_img = images[0]
497511 >>> red_img = red_channel(single_img) # normal behavior
498512 """
513+ def wrapper (f ):
514+ return _pipeline (f , ** kwargs )
515+
516+ if func is None :
517+ return wrapper
518+ else :
519+ return wrapper (func )
520+
521+
522+ def _pipeline (func , retain_doc = False ):
523+ """Actual `pipeline` implementation
524+
525+ Parameters
526+ ----------
527+ func : callable
528+ Function for lazy evaluation
529+ retain_doc : bool
530+ If True, don't modify `func`'s doc string to say that it has been
531+ made lazy
532+
533+ Returns
534+ -------
535+ Pipeline
536+ Lazy function evaluation :py:class:`Pipeline` for `func`.
537+ """
499538 @wraps (func )
500539 def process (obj , * args , ** kwargs ):
501540 if hasattr (obj , '_slicerator_flag' ) or isinstance (obj , Slicerator ) \
@@ -508,13 +547,14 @@ def proc_func(x):
508547 # as a single image.
509548 return func (obj , * args , ** kwargs )
510549
511- if process .__doc__ is None :
512- process .__doc__ = ''
513- process .__doc__ = ("This function has been made lazy. When passed\n "
514- "a Slicerator, it will return a \n "
515- "Pipeline of the results. When passed \n "
516- "any other objects, its behavior is "
517- "unchanged.\n \n " ) + process .__doc__
550+ if not retain_doc :
551+ if process .__doc__ is None :
552+ process .__doc__ = ''
553+ process .__doc__ = ("This function has been made lazy. When passed\n "
554+ "a Slicerator, it will return a \n "
555+ "Pipeline of the results. When passed \n "
556+ "any other objects, its behavior is "
557+ "unchanged.\n \n " ) + process .__doc__
518558 process .__name__ = func .__name__
519559 return process
520560
0 commit comments