source: orange/orange/Orange/misc/__init__.py @ 9301:7610090bd67a

Revision 9301:7610090bd67a, 16.9 KB checked in by ales_erjavec <ales.erjavec@…>, 2 years ago (diff)

Renamed members, functions in Orange.misc.selection underscore style and fixed documentation.
Added name mappings to fix_changed_names fixer.

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