source: orange/orange/Orange/misc/__init__.py @ 7778:c8a8cfa692ee

Revision 7778:c8a8cfa692ee, 12.7 KB checked in by ales_erjavec <ales.erjavec@…>, 3 years ago (diff)

Included deprecation functions to the rst documentation.

Line 
1"""
2
3.. index:: misc
4
5Module Orange.misc contains common functions and classes which are used in other modules.
6
7==================
8Counters
9==================
10
11.. index:: misc
12.. index::
13   single: misc; counters
14
15.. automodule:: Orange.misc.counters
16  :members:
17
18==================
19Render
20==================
21
22.. index:: misc
23.. index::
24   single: misc; render
25
26.. automodule:: Orange.misc.render
27  :members:
28
29==================
30Selection
31==================
32
33.. index:: selection
34.. index::
35   single: misc; selection
36
37Many machine learning techniques generate a set different solutions or have to
38choose, as for instance in classification tree induction, between different
39features. The most trivial solution is to iterate through the candidates,
40compare them and remember the optimal one. The problem occurs, however, when
41there are multiple candidates that are equally good, and the naive approaches
42would select the first or the last one, depending upon the formulation of
43the if-statement.
44
45:class:`Orange.misc.selection` provides a class that makes a random choice
46in such cases. Each new candidate is compared with the currently optimal
47one; it replaces the optimal if it is better, while if they are equal,
48one is chosen by random. The number of competing optimal candidates is stored,
49so in this random choice the probability to select the new candidate (over the
50current one) is 1/w, where w is the current number of equal candidates,
51including the present one. One can easily verify that this gives equal
52chances to all candidates, independent of the order in which they are presented.
53
54.. automodule:: Orange.misc.selection
55  :members:
56
57Example
58--------
59
60The following snippet loads the data set lymphography and prints out the
61feature with the highest information gain.
62
63part of `misc-selection-bestonthefly.py`_ (uses `lymphography.tab`_)
64
65.. literalinclude:: code/misc-selection-bestonthefly.py
66  :lines: 7-16
67
68Our candidates are tuples gain ratios and features, so we set
69:obj:`callCompareOn1st` to make the compare function compare the first element
70(gain ratios). We could achieve the same by initializing the object like this:
71
72part of `misc-selection-bestonthefly.py`_ (uses `lymphography.tab`_)
73
74.. literalinclude:: code/misc-selection-bestonthefly.py
75  :lines: 18-18
76
77
78The other way to do it is through indices.
79
80`misc-selection-bestonthefly.py`_ (uses `lymphography.tab`_)
81
82.. literalinclude:: code/misc-selection-bestonthefly.py
83  :lines: 25-
84
85.. _misc-selection-bestonthefly.py: code/misc-selection-bestonthefly.py.py
86.. _lymphography.tab: code/lymphography.tab
87
88Here we only give gain ratios to :obj:`bestOnTheFly`, so we don't have to specify a
89special compare operator. After checking all features we get the index of the
90optimal one by calling :obj:`winnerIndex`.
91
92==================
93Server files
94==================
95
96.. index:: server files
97
98.. automodule:: Orange.misc.serverfiles
99
100"""
101
102import counters
103import selection
104import render
105import serverfiles
106
107__all__ = ["counters", "selection", "render", "serverfiles",
108           "deprecated_members", "deprecated_keywords",
109           "deprecated_attribute", "deprecation_warning"]
110
111import random, types, sys
112import time
113
114def getobjectname(x, default=""):
115    if type(x)==types.StringType:
116        return x
117     
118    for i in ["name", "shortDescription", "description", "func_doc", "func_name"]:
119        if getattr(x, i, ""):
120            return getattr(x, i)
121
122    if hasattr(x, "__class__"):
123        r = repr(x.__class__)
124        if r[1:5]=="type":
125            return str(x.__class__)[7:-2]
126        elif r[1:6]=="class":
127            return str(x.__class__)[8:-2]
128    return default
129
130
131def demangleExamples(x):
132    if type(x)==types.TupleType:
133        return x
134    else:
135        return x, 0
136
137
138def frange(*argw):
139    start, stop, step = 0.0, 1.0, 0.1
140    if len(argw)==1:
141        start=step=argw[0]
142    elif len(argw)==2:
143        stop, step = argw
144    elif len(argw)==3:
145        start, stop, step = argw
146    elif len(argw)>3:
147        raise AttributeError, "1-3 arguments expected"
148
149    stop+=1e-10
150    i=0
151    res=[]
152    while 1:
153        f=start+i*step
154        if f>stop:
155            break
156        res.append(f)
157        i+=1
158    return res
159
160verbose = 0
161
162def printVerbose(text, *verb):
163    if len(verb) and verb[0] or verbose:
164        print text
165
166class ConsoleProgressBar(object):
167    def __init__(self, title="", charwidth=40, step=1, output=sys.stderr):
168        self.title = title + " "
169        self.charwidth = charwidth
170        self.step = step
171        self.currstring = ""
172        self.state = 0
173        self.output = output
174
175    def clear(self, i=-1):
176        try:
177            if hasattr(self.output, "isatty") and self.output.isatty():
178                self.output.write("\b" * (i if i != -1 else len(self.currstring)))
179            else:
180                self.output.seek(-i if i != -1 else -len(self.currstring), 2)
181        except Exception: ## If for some reason we failed
182            self.output.write("\n")
183
184    def getstring(self):
185        progchar = int(round(float(self.state) * (self.charwidth - 5) / 100.0))
186        return self.title + "=" * (progchar) + ">" + " " * (self.charwidth\
187            - 5 - progchar) + "%3i" % int(round(self.state)) + "%"
188
189    def printline(self, string):
190        try:
191            self.clear()
192            self.output.write(string)
193            self.output.flush()
194        except Exception:
195            pass
196        self.currstring = string
197
198    def __call__(self, newstate=None):
199        if newstate == None:
200            newstate = self.state + self.step
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 finish(self):
208        self.__call__(100)
209        self.output.write("\n")
210
211def progressBarMilestones(count, iterations=100):
212    return set([int(i*count/float(iterations)) for i in range(iterations)])
213
214def lru_cache(maxsize=100):
215    """ A least recently used cache function decorator.
216    (Similar to the functools.lru_cache in python 3.2)
217    """
218   
219    def decorating_function(func):
220        import functools
221        cache = {}
222       
223        functools.wraps(func)
224        def wrapped(*args, **kwargs):
225            key = args + tuple(sorted(kwargs.items()))
226            if key not in cache:
227                res = func(*args, **kwargs)
228                cache[key] = (time.time(), res)
229                if len(cache) > maxsize:
230                    key, (_, _) = min(cache.iteritems(), key=lambda item: item[1][0])
231                    del cache[key]
232            else:
233                _, res = cache[key]
234                cache[key] = (time.time(), res) # update the time
235               
236            return res
237       
238        def clear():
239            cache.clear()
240       
241        wrapped.clear = clear
242       
243        return wrapped
244    return decorating_function
245
246
247__doc__ += """\
248=============================
249Deprecation utility functions
250=============================
251
252.. autofunction:: Orange.misc.deprecation_warning
253
254.. autofunction:: Orange.misc.deprecated_members
255
256.. autofunction:: Orange.misc.deprecated_keywords
257
258.. autofunction:: Orange.misc.deprecated_attribute
259
260"""
261
262import warnings
263def deprecation_warning(old, new, stacklevel=-2):
264    """ Raise a deprecation warning of an obsolete attribute access.
265   
266    :param old: Old attribute name (used in warning message).
267    :param new: New attribute name (used in warning message).
268   
269    """
270    warnings.warn("'%s' is deprecated. Use '%s' instead!" % (old, new), DeprecationWarning, stacklevel=stacklevel)
271   
272# We need to get the instancemethod type
273class _Foo():
274    def bar(self):
275        pass
276instancemethod = type(_Foo.bar)
277del _Foo
278
279function = type(lambda: None)
280
281class universal_set(set):
282    """ A universal set, pretends it contains everything.
283    """
284    def __contains__(self, value):
285        return True
286   
287from functools import wraps
288
289def deprecated_members(name_map, wrap_methods="all", in_place=True):
290    """ Decorate a class with properties for accessing attributes, and methods
291    with deprecated names. In addition methods from the `wrap_methods` list
292    will be wrapped to receive mapped keyword arguments.
293   
294    :param name_map: A dictionary mapping old into new names.
295    :type name_map: dict
296   
297    :param wrap_methods: A list of method names to wrap. Wrapped methods will
298        be called with mapped keyword arguments (by default all methods will
299        be wrapped).
300    :type wrap_methods: list
301   
302    :param in_place: If True the class will be modified in place, otherwise
303        it will be subclassed (default True).
304    :type in_place: bool
305   
306    Example ::
307           
308        >>> @deprecated_members({"fooBar": "foo_bar", "setFooBar":"set_foo_bar"},
309        ...                    wrap_methods=["set_foo_bar", "__init__"])
310        ... class A(object):
311        ...     def __init__(self, foo_bar="bar"):
312        ...         self.set_foo_bar(foo_bar)
313        ...     
314        ...     def set_foo_bar(self, foo_bar="bar"):
315        ...         self.foo_bar = foo_bar
316        ...         
317        ...
318        >>> a = A(fooBar="foo")
319        __main__:1: DeprecationWarning: 'fooBar' is deprecated. Use 'foo_bar' instead!
320        >>> print a.fooBar, a.foo_bar
321        foo foo
322        >>> a.setFooBar("FooBar!")
323        __main__:1: DeprecationWarning: 'setFooBar' is deprecated. Use 'set_foo_bar' instead!
324       
325    """
326    def is_wrapped(method):
327        """ Is member method already wrapped.
328        """
329        if getattr(method, "_deprecate_members_wrapped", False):
330            return True
331        elif hasattr(method, "im_func"):
332            im_func = method.im_func
333            return getattr(im_func, "_deprecate_members_wrapped", False)
334        else:
335            return False
336       
337    if wrap_methods == "all":
338        wrap_methods = universal_set()
339    elif not wrap_methods:
340        wrap_methods = set()
341       
342    def wrapper(cls):
343        cls_names = {}
344        # Create properties for accessing deprecated members
345        for old_name, new_name in name_map.items():
346            cls_names[old_name] = deprecated_attribute(old_name, new_name)
347           
348        # wrap member methods to map keyword arguments
349        for key, value in cls.__dict__.items():
350            if isinstance(value, (instancemethod, function)) \
351                and not is_wrapped(value) and key in wrap_methods:
352               
353                wrapped = deprecated_keywords(name_map)(value)
354                wrapped._deprecate_members_wrapped = True # A flag indicating this function already maps keywords
355                cls_names[key] = wrapped
356        if in_place:
357            for key, val in cls_names.items():
358                setattr(cls, key, val)
359            return cls
360        else:
361            return type(cls.__name__, (cls,), cls_names)
362       
363    return wrapper
364
365def deprecated_keywords(name_map):
366    """ Deprecates the keyword arguments of the function.
367   
368    Example ::
369   
370        >>> @deprecated_keywords({"myArg": "my_arg"})
371        ... def my_func(my_arg=None):
372        ...     print my_arg
373        ...
374        ...
375        >>> my_func(myArg="Arg")
376        __main__:1: DeprecationWarning: 'myArg' is deprecated. Use 'my_arg' instead!
377        Arg
378       
379    """
380    def decorator(func):
381        @wraps(func)
382        def wrap_call(*args, **kwargs):
383            kwargs = dict(kwargs)
384            for name in name_map:
385                if name in kwargs:
386                    deprecation_warning(name, name_map[name], stacklevel=3)
387                    kwargs[name_map[name]] = kwargs[name]
388                    del kwargs[name]
389            return func(*args, **kwargs)
390        return wrap_call
391    return decorator
392
393def deprecated_attribute(old_name, new_name):
394    """ Return a property object that accesses an attribute named `new_name`
395    and raises a deprecation warning when doing so.
396   
397    Example ::
398   
399        >>> class A(object):
400        ...     def __init__(self):
401        ...         self.my_attr = "123"
402        ...     myAttr = deprecated_attribute("myAttr", "my_attr")
403        ...
404        ...
405        >>> a = A()
406        >>> print a.myAttr
407        __main__:1: DeprecationWarning: 'myAttr' is deprecated. Use 'my_attr' instead!
408        123
409       
410    """
411    def fget(self):
412        deprecation_warning(old_name, new_name, stacklevel=3)
413        return getattr(self, new_name)
414   
415    def fset(self, value):
416        deprecation_warning(old_name, new_name, stacklevel=3)
417        setattr(self, new_name, value)
418   
419    def fdel(self):
420        deprecation_warning(old_name, new_name, stacklevel=3)
421        delattr(self, new_name)
422   
423    prop = property(fget, fset, fdel,
424                    doc="A deprecated member '%s'. Use '%s' instead." % (old_name, new_name))
425    return prop
426   
427def _test():
428    import doctest
429    doctest.testmod()
430   
431if __name__ == "__main__":
432    _test()
Note: See TracBrowser for help on using the repository browser.