source: orange/Orange/utils/__init__.py @ 11497:8bf64455aa84

Revision 11497:8bf64455aa84, 20.5 KB checked in by markotoplak, 12 months ago (diff)

Added server files to the documentation index.

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