source: orange/Orange/utils/__init__.py @ 11879:79f4f55e2826

Revision 11879:79f4f55e2826, 20.3 KB checked in by Ales Erjavec <ales.erjavec@…>, 6 weeks ago (diff)

Cleanup of 'Orange.utils' import statements.

Line 
1"""
2.. index:: utils
3
4Orange.utils contains developer utilities.
5
6------------------
7Reporting progress
8------------------
9
10.. autoclass:: Orange.utils.ConsoleProgressBar
11    :members:
12
13-----------------------------
14Deprecation utility functions
15-----------------------------
16
17.. autofunction:: Orange.utils.deprecation_warning
18
19.. autofunction:: Orange.utils.deprecated_members
20
21.. autofunction:: Orange.utils.deprecated_keywords
22
23.. autofunction:: Orange.utils.deprecated_attribute
24
25.. autofunction:: Orange.utils.deprecated_function_name
26
27----------------
28Submodules
29----------------
30
31.. automodule:: Orange.utils.environ
32
33.. automodule:: Orange.utils.counters
34  :members:
35
36.. automodule:: Orange.utils.render
37  :members:
38
39.. automodule:: Orange.utils.addons
40
41.. automodule:: Orange.utils.selection
42
43.. automodule:: Orange.utils.serverfiles
44
45"""
46
47import sys
48import os
49import types
50import random
51import time
52import urllib2
53import posixpath
54import warnings
55import StringIO
56
57from functools import wraps
58from contextlib import contextmanager
59
60from . import environ
61
62__all__ = ["deprecated_members", "deprecated_keywords",
63           "deprecated_attribute", "deprecation_warning",
64           "deprecated_function_name",
65           "counters", "render", "serverfiles"]
66
67
68def deprecation_warning(old, new, stacklevel=-2):
69    """ Raise a deprecation warning of an obsolete attribute access.
70   
71    :param old: Old attribute name (used in warning message).
72    :param new: New attribute name (used in warning message).
73   
74    """
75    warnings.warn("'%s' is deprecated. Use '%s' instead!" % (old, new), DeprecationWarning, stacklevel=stacklevel)
76   
77# We need to get the instancemethod type
78class _Foo():
79    def bar(self):
80        pass
81instancemethod = type(_Foo.bar)
82del _Foo
83
84function = type(lambda: None)
85
86class universal_set(set):
87    """ A universal set, pretends it contains everything.
88    """
89    def __contains__(self, value):
90        return True
91
92
93def deprecated_members(name_map, wrap_methods="all", in_place=True):
94    """ Decorate a class with properties for accessing attributes, and methods
95    with deprecated names. In addition methods from the `wrap_methods` list
96    will be wrapped to receive mapped keyword arguments.
97   
98    :param name_map: A dictionary mapping old into new names.
99    :type name_map: dict
100   
101    :param wrap_methods: A list of method names to wrap. Wrapped methods will
102        be called with mapped keyword arguments (by default all methods will
103        be wrapped).
104    :type wrap_methods: list
105   
106    :param in_place: If True the class will be modified in place, otherwise
107        it will be subclassed (default True).
108    :type in_place: bool
109   
110    Example ::
111           
112        >>> class A(object):
113        ...     def __init__(self, foo_bar="bar"):
114        ...         self.set_foo_bar(foo_bar)
115        ...     
116        ...     def set_foo_bar(self, foo_bar="bar"):
117        ...         self.foo_bar = foo_bar
118        ...
119        ... A = deprecated_members(
120        ... {"fooBar": "foo_bar",
121        ...  "setFooBar":"set_foo_bar"},
122        ... wrap_methods=["set_foo_bar", "__init__"])(A)
123        ...
124        ...
125        >>> a = A(fooBar="foo")
126        __main__:1: DeprecationWarning: 'fooBar' is deprecated. Use 'foo_bar' instead!
127        >>> print a.fooBar, a.foo_bar
128        foo foo
129        >>> a.setFooBar("FooBar!")
130        __main__:1: DeprecationWarning: 'setFooBar' is deprecated. Use 'set_foo_bar' instead!
131       
132    .. note:: This decorator does nothing if \
133        :obj:`Orange.utils.environ.orange_no_deprecated_members` environment \
134        variable is set to `True`.
135       
136    """
137    if environ.orange_no_deprecated_members:
138        return lambda cls: cls
139   
140    def is_wrapped(method):
141        """ Is member method already wrapped.
142        """
143        if getattr(method, "_deprecate_members_wrapped", False):
144            return True
145        elif hasattr(method, "im_func"):
146            im_func = method.im_func
147            return getattr(im_func, "_deprecate_members_wrapped", False)
148        else:
149            return False
150       
151    if wrap_methods == "all":
152        wrap_methods = universal_set()
153    elif not wrap_methods:
154        wrap_methods = set()
155       
156    def wrapper(cls):
157        cls_names = {}
158        # Create properties for accessing deprecated members
159        for old_name, new_name in name_map.items():
160            cls_names[old_name] = deprecated_attribute(old_name, new_name)
161           
162        # wrap member methods to map keyword arguments
163        for key, value in cls.__dict__.items():
164            if isinstance(value, (instancemethod, function)) \
165                and not is_wrapped(value) and key in wrap_methods:
166               
167                wrapped = deprecated_keywords(name_map)(value)
168                wrapped._deprecate_members_wrapped = True # A flag indicating this function already maps keywords
169                cls_names[key] = wrapped
170        if in_place:
171            for key, val in cls_names.items():
172                setattr(cls, key, val)
173            return cls
174        else:
175            return type(cls.__name__, (cls,), cls_names)
176       
177    return wrapper
178
179def deprecated_keywords(name_map):
180    """ Deprecates the keyword arguments of the function.
181   
182    Example ::
183   
184        >>> @deprecated_keywords({"myArg": "my_arg"})
185        ... def my_func(my_arg=None):
186        ...     print my_arg
187        ...
188        ...
189        >>> my_func(myArg="Arg")
190        __main__:1: DeprecationWarning: 'myArg' is deprecated. Use 'my_arg' instead!
191        Arg
192       
193    .. note:: This decorator does nothing if \
194        :obj:`Orange.utils.environ.orange_no_deprecated_members` environment \
195        variable is set to `True`.
196       
197    """
198    if environ.orange_no_deprecated_members:
199        return lambda func: func
200    for name in name_map.values():
201        if name in name_map:
202            raise ValueError("Deprecation keys and values overlap; this could"
203                             " cause trouble!")
204   
205    def decorator(func):
206        @wraps(func)
207        def wrap_call(*args, **kwargs):
208            kwargs = dict(kwargs)
209            for name in name_map:
210                if name in kwargs:
211                    deprecation_warning(name, name_map[name], stacklevel=3)
212                    kwargs[name_map[name]] = kwargs[name]
213                    del kwargs[name]
214            return func(*args, **kwargs)
215        return wrap_call
216    return decorator
217
218def deprecated_attribute(old_name, new_name):
219    """ Return a property object that accesses an attribute named `new_name`
220    and raises a deprecation warning when doing so.
221
222    ..
223
224        >>> sys.stderr = sys.stdout
225   
226    Example ::
227   
228        >>> class A(object):
229        ...     def __init__(self):
230        ...         self.my_attr = "123"
231        ...     myAttr = deprecated_attribute("myAttr", "my_attr")
232        ...
233        ...
234        >>> a = A()
235        >>> print a.myAttr
236        ...:1: DeprecationWarning: 'myAttr' is deprecated. Use 'my_attr' instead!
237        123
238       
239    .. note:: This decorator does nothing and returns None if \
240        :obj:`Orange.utils.environ.orange_no_deprecated_members` environment \
241        variable is set to `True`.
242       
243    """
244    if environ.orange_no_deprecated_members:
245        return None
246   
247    def fget(self):
248        deprecation_warning(old_name, new_name, stacklevel=3)
249        return getattr(self, new_name)
250   
251    def fset(self, value):
252        deprecation_warning(old_name, new_name, stacklevel=3)
253        setattr(self, new_name, value)
254   
255    def fdel(self):
256        deprecation_warning(old_name, new_name, stacklevel=3)
257        delattr(self, new_name)
258   
259    prop = property(fget, fset, fdel,
260                    doc="A deprecated member '%s'. Use '%s' instead." % (old_name, new_name))
261    return prop
262
263class class_property(object):
264    def __init__(self, fget=None, fset=None, fdel=None, doc="class property"):
265        self.fget = fget
266        self.fset = fset
267        self.fdel = fdel
268        self.__doc__ = doc
269       
270    def __get__(self, instance, owner):
271        if instance is None:
272            return self.fget(owner)
273        else:
274            return self.fget(instance)               
275           
276def deprecated_class_attribute(old_name, new_name):
277    """ Return a property object that accesses an class attribute
278    named `new_name` and raises a deprecation warning when doing so.
279   
280    """
281    if environ.orange_no_deprecated_members:
282        return None
283   
284    def fget(self):
285        deprecation_warning(old_name, new_name, stacklevel=3)
286        return getattr(self, new_name)
287       
288    prop = class_property(fget,
289                    doc="A deprecated class member '%s'. Use '%s' instead." % (old_name, new_name))
290    return prop
291
292def deprecated_function_name(func):
293    """ Return a wrapped function that raises an deprecation warning when
294    called. This should be used for deprecation of module level function names.
295   
296    Example ::
297   
298        >>> def func_a(arg):
299        ...    print "This is func_a  (used to be named funcA) called with", arg
300        ...
301        ...
302        >>> funcA = deprecated_function_name(func_a)
303        >>> funcA(None)
304         
305   
306    .. note:: This decorator does nothing and if \
307        :obj:`Orange.utils.environ.orange_no_deprecated_members` environment \
308        variable is set to `True`.
309       
310    """
311    if environ.orange_no_deprecated_members:
312        return func
313   
314    @wraps(func)
315    def wrapped(*args, **kwargs):
316        warnings.warn("Deprecated function name. Use %r instead!" % func.__name__,
317                      DeprecationWarning, stacklevel=2)
318        return func(*args, **kwargs)
319    return wrapped
320   
321class ConsoleProgressBar(object):
322    """ A class to for printing progress bar reports in the console.
323   
324    Example ::
325   
326        >>> import sys, time
327        >>> progress = ConsoleProgressBar("Example", output=sys.stdout)
328        >>> for i in range(100):
329        ...    progress.advance()
330        ...    # Or progress.set_state(i)
331        ...    time.sleep(0.01)
332        ...
333        ...
334        Example ===================================>100%
335       
336    """
337    def __init__(self, title="", charwidth=40, step=1, output=None):
338        """ Initialize the progress bar.
339       
340        :param title: The title for the progress bar.
341        :type title: str
342        :param charwidth: The maximum progress bar width in characters.
343       
344            .. todo:: Get the console width from the ``output`` if the
345                information can be retrieved.
346               
347        :type charwidth: int
348        :param step: A default step used if ``advance`` is called without
349            any  arguments
350       
351        :type step: int
352        :param output: The output file. If None (default) then ``sys.stderr``
353            is used.
354           
355        :type output: An file like object to print the progress report to.
356         
357        """
358        self.title = title + " "
359        self.charwidth = charwidth
360        self.step = step
361        self.currstring = ""
362        self.state = 0
363        if output is None:
364            output = sys.stderr
365        self.output = output
366
367    def clear(self, i=-1):
368        """ Clear the current progress line indicator string.
369        """
370        try:
371            if hasattr(self.output, "isatty") and self.output.isatty():
372                self.output.write("\b" * (i if i != -1 else len(self.currstring)))
373            else:
374                self.output.seek(-i if i != -1 else -len(self.currstring), 2)
375        except Exception: ## If for some reason we failed
376            self.output.write("\n")
377
378    def getstring(self):
379        """ Return the progress indicator string.
380        """
381        progchar = int(round(float(self.state) * (self.charwidth - 5) / 100.0))
382        return self.title + "=" * (progchar) + ">" + " " * (self.charwidth\
383            - 5 - progchar) + "%3i" % int(round(self.state)) + "%"
384
385    def printline(self, string):
386        """ Print the ``string`` to the output file.
387        """
388        try:
389            self.clear()
390            self.output.write(string)
391            self.output.flush()
392        except Exception:
393            pass
394        self.currstring = string
395
396    def __call__(self, newstate=None):
397        """ Set the ``newstate`` as the current state of the progress bar.
398        ``newstate`` must be in the interval [0, 100].
399       
400        .. note:: ``set_state`` is the prefered way to set a new steate.
401       
402        :param newstate: The new state of the progress bar.
403        :type newstate: float
404         
405        """
406        if newstate is None:
407            self.advance()
408        else:
409            self.set_state(newstate)
410           
411    def set_state(self, newstate):
412        """ Set the ``newstate`` as the current state of the progress bar.
413        ``newstate`` must be in the interval [0, 100].
414       
415        :param newstate: The new state of the progress bar.
416        :type newstate: float
417       
418        """
419        if int(newstate) != int(self.state):
420            self.state = newstate
421            self.printline(self.getstring())
422        else:
423            self.state = newstate
424           
425    def advance(self, step=None):
426        """ Advance the current state by ``step``. If ``step`` is None use
427        the default step as set at class initialization.
428         
429        """
430        if step is None:
431            step = self.step
432           
433        newstate = self.state + step
434        self.set_state(newstate)
435
436    def finish(self):
437        """ Finish the progress bar (i.e. set the state to 100 and
438        print the final newline to the ``output`` file).
439        """
440        self.__call__(100)
441        self.output.write("\n")
442
443def progress_bar_milestones(count, iterations=100):
444    return set([int(i*count/float(iterations)) for i in range(iterations)])
445
446progressBarMilestones = deprecated_function_name(progress_bar_milestones)
447
448
449def getobjectname(x, default=""):
450    if type(x)==types.StringType:
451        return x
452     
453    for i in ["name", "shortDescription", "description", "func_doc", "func_name"]:
454        if getattr(x, i, ""):
455            return getattr(x, i)
456
457    if hasattr(x, "__class__"):
458        r = repr(x.__class__)
459        if r[1:5]=="type":
460            return str(x.__class__)[7:-2]
461        elif r[1:6]=="class":
462            return str(x.__class__)[8:-2]
463    return default
464
465
466def demangle_examples(x):
467    if type(x)==types.TupleType:
468        return x
469    else:
470        return x, 0
471
472def frange(*argw):
473    """ Like builtin `range` but works with floats
474    """
475    start, stop, step = 0.0, 1.0, 0.1
476    if len(argw)==1:
477        start=step=argw[0]
478    elif len(argw)==2:
479        stop, step = argw
480    elif len(argw)==3:
481        start, stop, step = argw
482    elif len(argw)>3:
483        raise AttributeError, "1-3 arguments expected"
484
485    stop+=1e-10
486    i=0
487    res=[]
488    while 1:
489        f=start+i*step
490        if f>stop:
491            break
492        res.append(f)
493        i+=1
494    return res
495
496verbose = 0
497
498def print_verbose(text, *verb):
499    if len(verb) and verb[0] or verbose:
500        print text
501
502def lru_cache(maxsize=100):
503    """ A least recently used cache function decorator.
504    (Similar to the functools.lru_cache in python 3.2)
505    """
506   
507    def decorating_function(func):
508        cache = {}
509       
510        @wraps(func)
511        def wrapped(*args, **kwargs):
512            key = args + tuple(sorted(kwargs.items()))
513            if key not in cache:
514                res = func(*args, **kwargs)
515                cache[key] = (time.time(), res)
516                if len(cache) > maxsize:
517                    key, (_, _) = min(cache.iteritems(), key=lambda item: item[1][0])
518                    del cache[key]
519            else:
520                _, res = cache[key]
521                cache[key] = (time.time(), res) # update the time
522               
523            return res
524       
525        def clear():
526            cache.clear()
527       
528        wrapped.clear = clear
529        wrapped._cache = cache
530       
531        return wrapped
532    return decorating_function
533
534
535@contextmanager
536def member_set(obj, name, val):
537    """ A context manager that sets member ``name`` on ``obj`` to ``val``
538    and restores the previous value on exit.
539    """
540    old_val = getattr(obj, name, val)
541    setattr(obj, name, val)
542    yield
543    setattr(obj, name, old_val)
544   
545   
546class recursion_limit(object):
547    """ A context manager that sets a new recursion limit.
548   
549    """
550    def __init__(self, limit=1000):
551        self.limit = limit
552       
553    def __enter__(self):
554        self.old_limit = sys.getrecursionlimit()
555        sys.setrecursionlimit(self.limit)
556   
557    def __exit__(self, exc_type, exc_val, exc_tb):
558        sys.setrecursionlimit(self.old_limit)
559       
560"""
561Some utility functions common to Orange classes.
562
563"""
564
565
566def _orange_learner__new__(base):
567    """Return an 'schizophrenic' __new__ class method following
568    `Orange.core.Learner.__new__` calling convention.
569
570    :param base: base class.
571    :type base: type
572
573    """
574    import Orange
575
576    @wraps(base.__new__)
577    def _orange_learner__new__wrapped(cls, data=None, weight=0, **kwargs):
578        self = base.__new__(cls, **kwargs)
579        if data is not None:
580            self.__init__(**kwargs)
581            return self.__call__(data, weight)
582        else:
583            return self
584
585    return _orange_learner__new__wrapped
586
587
588def _orange__new__(base=None):
589    """Return an orange 'schizophrenic' __new__ class method.
590
591    :param base: base orange class (default `Orange.core.Learner`)
592    :type base: type
593
594    Example::
595        class NewOrangeLearner(Orange.core.Learner):
596            __new__ = _orange__new(Orange.core.Learner)
597
598    """
599    import Orange
600    if base is None:
601        base = Orange.core.Learner
602
603    if issubclass(base, Orange.core.Learner):
604        return _orange_learner__new__(base)
605    else:
606        @wraps(base.__new__)
607        def _orange__new_wrapped(cls, data=None, **kwargs):
608            if base == object:
609                self = base.__new__(cls)
610            else:
611                self = base.__new__(cls, **kwargs)
612
613            if data:
614                self.__init__(**kwargs)
615                return self.__call__(data)
616            else:
617                return self
618
619    return _orange__new_wrapped
620
621
622def _orange__reduce__(self):
623    """ A default __reduce__ method for orange types. Assumes the object
624    can be reconstructed with the call `constructor(__dict__)` where __dict__
625    if the stored (pickled) __dict__ attribute.
626
627    Example::
628        class NewOrangeType(Orange.core.Learner):
629            __reduce__ = _orange__reduce()
630    """
631    return type(self), (), dict(self.__dict__)
632
633demangleExamples = deprecated_function_name(demangle_examples)
634printVerbose = deprecated_function_name(print_verbose)
635
636
637@contextmanager
638def finishing(obj):
639    """ Calls obj.finish() on context exit.
640    """
641    yield obj
642    obj.finish()
643
644def guess_size(fileobj):
645    try:
646        if isinstance(fileobj, file):
647            return os.fstat(fileobj.fileno()).st_size
648        elif isinstance(fileobj, StringIO.StringIO):
649            pos = fileobj.tell()
650            fileobj.seek(0, 2)
651            length = fileobj.tell() - pos
652            fileobj.seek(pos, 0)
653            return length
654        elif isinstance(fileobj, urllib.addinfourl):
655            length = fileobj.headers.get("content-length", None)
656            return length
657    except Exception, ex:
658        pass
659
660def copyfileobj(src, dst, buffer=2**10, content_len=None, progress=None):
661    """
662    shutil.copyfileobj with progress reporting.
663    """
664    count = 0
665    if content_len is None:
666        content_len = guess_size(src) or sys.maxint
667    while True:
668        data = src.read(buffer)
669        dst.write(data)
670        count += len(data)
671        if progress:
672            progress(100.0 * count / content_len)
673        if not data:
674            break
675           
676def wget(url, directory=".", dst_obj=None, progress=None):
677    stream = urllib2.urlopen(url)
678    length = stream.headers.get("content-length", None)
679    if length is None:
680        length = sys.maxint
681    else:
682        length = int(length)
683   
684    basename = posixpath.basename(url)
685       
686    if dst_obj is None:
687        dst_obj = open(os.path.join(directory, basename), "wb")
688   
689    if progress == True:
690        progress = ConsoleProgressBar("Downloading %r." % basename)
691        with finishing(progress):
692            copyfileobj(stream, dst_obj, buffer=2**10, content_len=length,
693                        progress=progress)
694    else:
695        copyfileobj(stream, dst_obj, buffer=2**10, content_len=length,
696                    progress=progress)
697
698
699from . import selection
700from . import render
Note: See TracBrowser for help on using the repository browser.