source: orange/orange/misc/__init__.py @ 9669:165371b04b4a

Revision 9669:165371b04b4a, 16.9 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Moved content of Orange dir to package dir

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