source: orange/Orange/misc/__init__.py @ 10521:51b7f0b1ea41

Revision 10521:51b7f0b1ea41, 27.8 KB checked in by markotoplak, 2 years ago (diff)

Orange.misc._orangenew c can now handle the object base type without warning.

Line 
1"""
2.. index:: misc
3
4Module Orange.misc contains common functions and classes which are used in other modules.
5
6.. index: CostMatrix
7
8-----------------------
9CostMatrix
10-----------------------
11
12CostMatrix is an object that stores costs of (mis)classifications. Costs can be either negative or positive.
13
14.. class:: CostMatrix
15
16    .. attribute:: class_var
17       
18        The (class) attribute to which the matrix applies. This can
19        also be None.
20       
21    .. attribute:: dimension (read only)
22   
23        Matrix dimension, ie. number of classes.
24       
25    .. method:: CostMatrix(dimension[, default cost])
26   
27        Constructs a matrix of the given size and initializes it with
28        the default cost (1, if not given). All elements of the matrix
29        are assigned the given cost, except for the diagonal that have
30        the default cost of 0.  (Diagonal elements represent correct
31        classifications and these usually have no price; you can,
32        however, change this.)
33       
34        .. literalinclude:: code/CostMatrix.py
35            :lines: 1-8
36       
37        This initializes the matrix and print it out:
38       
39        .. literalinclude:: code/CostMatrix.res
40            :lines: 1-3
41   
42    .. method:: CostMatrix(class descriptor[, default cost])
43   
44        Similar as above, except that classVar is also set to the given descriptor.
45        The number of values of the given attribute (which must be discrete) is used
46        for dimension.
47       
48        .. literalinclude:: code/CostMatrix.py
49            :lines: 10-11
50           
51        This constructs a matrix similar to the one above (the class attribute in iris
52        domain is three-valued) except that the matrix contains 2s instead of 1s.
53       
54    .. method:: CostMatrix([attribute descriptor, ]matrix)
55   
56        Initializes the matrix with the elements given as a sequence of sequences (you
57        can mix lists and tuples if you find it funny). Each subsequence represents a row.
58       
59        .. literalinclude:: code/CostMatrix.py
60            :lines: 13
61
62        If you print this matrix out, will it look like this:
63       
64        .. literalinclude:: code/CostMatrix.res
65            :lines: 5-7
66           
67    .. method:: setcost(predicted, correct, cost)
68   
69        Set the misclassification cost. The matrix above could be
70        constructed by first initializing it with 2s and then changing
71        the prices for virginica's into 1s.
72       
73        .. literalinclude:: code/CostMatrix.py
74            :lines: 15-17
75           
76    .. method:: getcost(predicted, correct)
77   
78        Returns the cost of prediction. Values must be integer
79        indices; if class_var is set, you can also use symbolic values
80        (strings). Note that there's no way to change the size of the
81        matrix. Size is set at construction and does not change.  For
82        the final example, we shall compute the profits of knowing
83        attribute values in the dataset lenses with the same
84        cost-matrix as printed above.
85       
86        .. literalinclude:: code/CostMatrix.py
87            :lines: 19-23
88           
89        As the script shows, you don't have to (and usually won't) call the constructor
90        explicitly. Instead, you will set the corresponding field (in our case meas.cost)
91        to a matrix and let Orange convert it to CostMatrix automatically. Funny as it
92        might look, but since Orange uses constructor to perform such conversion, even
93        the above statement is correct (although the cost matrix is rather dull,
94        with 0s on the diagonal and 1s around):           
95           
96        .. literalinclude:: code/CostMatrix.py
97            :lines: 25
98               
99.. index: SymMatrix
100
101-----------------------
102SymMatrix
103-----------------------
104
105:obj:`SymMatrix` implements symmetric matrices of size fixed at
106construction time (and stored in :obj:`SymMatrix.dim`).
107
108.. class:: SymMatrix
109
110    .. attribute:: dim
111   
112        Matrix dimension.
113           
114    .. attribute:: matrix_type
115
116        Can be ``SymMatrix.Lower`` (0), ``SymMatrix.Upper`` (1),
117        ``SymMatrix.Symmetric`` (2, default), ``SymMatrix.LowerFilled`` (3) or
118        ``SymMatrix.Upper_Filled`` (4).
119
120        If the matrix type is ``Lower`` or ``Upper``, indexing
121        above or below the diagonal, respectively, will fail.
122        With ``LowerFilled`` and ``Upper_Filled``,
123        the elements upper or lower, respectively, still
124        exist and are set to zero, but they cannot be modified. The
125        default matrix type is ``Symmetric``, but can be changed
126        at any time.
127
128        If matrix type is ``Upper``, it is printed as:
129
130        >>> import Orange
131        >>> m = Orange.misc.SymMatrix(
132        ...     [[1],
133        ...      [2, 4],
134        ...      [3, 6, 9],
135        ...      [4, 8, 12, 16]])
136        >>> m.matrix_type = m.Upper
137        >>> print m
138        (( 1.000,  2.000,  3.000,  4.000),
139         (         4.000,  6.000,  8.000),
140         (                 9.000, 12.000),
141         (                        16.000))
142
143        Changing the type to ``LowerFilled`` changes the printout to
144
145        >>> m.matrix_type = m.LowerFilled
146        >>> print m
147        (( 1.000,  0.000,  0.000,  0.000),
148         ( 2.000,  4.000,  0.000,  0.000),
149         ( 3.000,  6.000,  9.000,  0.000),
150         ( 4.000,  8.000, 12.000, 16.000))
151   
152    .. method:: __init__(dim[, value])
153
154        Construct a symmetric matrix of the given dimension.
155
156        :param dim: matrix dimension
157        :type dim: int
158
159        :param value: default value (0 by default)
160        :type value: double
161       
162       
163    .. method:: __init__(data)
164
165        Construct a new symmetric matrix containing the given data.
166        These can be given as Python list containing lists or tuples.
167       
168        The following example fills a matrix created above with
169        data in a list::
170
171            import Orange
172            m = [[],
173                 [ 3],
174                 [ 2, 4],
175                 [17, 5, 4],
176                 [ 2, 8, 3, 8],
177                 [ 7, 5, 10, 11, 2],
178                 [ 8, 4, 1, 5, 11, 13],
179                 [ 4, 7, 12, 8, 10, 1, 5],
180                 [13, 9, 14, 15, 7, 8, 4, 6],
181                 [12, 10, 11, 15, 2, 5, 7, 3, 1]]
182                   
183            matrix = Orange.data.SymMatrix(m)
184
185        SymMatrix also stores diagonal elements. They are set
186        to zero, if they are not specified. The missing elements
187        (shorter lists) are set to zero as well. If a list
188        spreads over the diagonal, the constructor checks
189        for asymmetries. For instance, the matrix
190
191        ::
192
193            m = [[],
194                 [ 3,  0, f],
195                 [ 2,  4]]
196   
197        is only OK if f equals 2. Finally, no row can be longer
198        than matrix size. 
199
200    .. method:: get_values()
201   
202        Return all matrix values in a Python list.
203
204    .. method:: get_KNN(i, k)
205   
206        Return k columns with the lowest value in the i-th row.
207       
208        :param i: i-th row
209        :type i: int
210       
211        :param k: number of neighbors
212        :type k: int
213       
214    .. method:: avg_linkage(clusters)
215   
216        Return a symmetric matrix with average distances between given clusters. 
217     
218        :param clusters: list of clusters
219        :type clusters: list of lists
220       
221    .. method:: invert(type)
222   
223        Invert values in the symmetric matrix.
224       
225        :param type: 0 (-X), 1 (1 - X), 2 (max - X), 3 (1 / X)
226        :type type: int
227
228    .. method:: normalize(type)
229   
230        Normalize values in the symmetric matrix.
231       
232        :param type: 0 (normalize to [0, 1] interval), 1 (Sigmoid)
233        :type type: int
234       
235       
236
237Indexing
238..........
239
240For symmetric matrices the order of indices is not important:
241if ``m`` is a SymMatrix, then ``m[2, 4]`` addresses the same element as ``m[4, 2]``.
242
243..
244    .. literalinclude:: code/symmatrix.py
245        :lines: 1-6
246
247>>> import Orange
248>>> m = Orange.misc.SymMatrix(4)
249>>> for i in range(4):
250...    for j in range(i+1):
251...        m[i, j] = (i+1)*(j+1)
252
253
254Although only the lower left half of the matrix was set explicitely,
255the whole matrix is constructed.
256
257>>> print m
258(( 1.000,  2.000,  3.000,  4.000),
259 ( 2.000,  4.000,  6.000,  8.000),
260 ( 3.000,  6.000,  9.000, 12.000),
261 ( 4.000,  8.000, 12.000, 16.000))
262 
263Entire rows are indexed with a single index. They can be iterated
264over in a for loop or sliced (with, for example, ``m[:3]``):
265
266>>> print m[1]
267(2.0, 4.0, 6.0, 8.0)
268>>> m.matrix_type = m.Lower
269>>> for row in m:
270...     print row
271(1.0,)
272(2.0, 4.0)
273(3.0, 6.0, 9.0)
274(4.0, 8.0, 12.0, 16.0)
275
276.. index: Random number generator
277
278-----------------------
279Random number generator
280-----------------------
281
282:obj:`Random` uses the
283`Mersenne twister <http://en.wikipedia.org/wiki/Mersenne_twister>`_ algorithm
284to generate random numbers.
285
286::
287
288    >>> import Orange
289    >>> rg = Orange.misc.Random(42)
290    >>> rg(10)
291    4
292    >>> rg(10)
293    7
294    >>> rg.uses  # We called rg two times.
295    2
296    >>> rg.reset()
297    >>> rg(10)
298    4
299    >>> rg(10)
300    7
301    >>> rg.uses
302    2
303
304
305.. class:: Random(seed)
306
307    :param initseed: Seed used for initializing the random generator.
308    :type initseed: int
309
310    .. method:: __call__(n)
311
312        Return a random integer R such that 0 <= R < n.
313
314        :type n: int
315
316    .. method:: reset([seed])
317
318        Reinitialize the random generator with `initseed`. If `initseed`
319        is not given use the existing value of attribute `initseed`.
320
321    .. attribute:: uses
322       
323        The number of times the generator was called after
324        initialization/reset.
325   
326    .. attribute:: initseed
327
328        Random seed.
329
330Two examples or random number generator uses found in the documentation
331are :obj:`Orange.evaluation.testing` and :obj:`Orange.data.Table`.
332
333------------------
334Reporting progress
335------------------
336
337.. autoclass:: Orange.misc.ConsoleProgressBar
338    :members:
339
340-----------------------------
341Deprecation utility functions
342-----------------------------
343
344.. autofunction:: Orange.misc.deprecation_warning
345
346.. autofunction:: Orange.misc.deprecated_members
347
348.. autofunction:: Orange.misc.deprecated_keywords
349
350.. autofunction:: Orange.misc.deprecated_attribute
351
352.. autofunction:: Orange.misc.deprecated_function_name
353
354
355----------------
356Other submodules
357----------------
358
359.. automodule:: Orange.misc.counters
360  :members:
361
362.. automodule:: Orange.misc.render
363  :members:
364
365.. automodule:: Orange.misc.selection
366
367.. automodule:: Orange.misc.addons
368
369.. automodule:: Orange.misc.serverfiles
370
371.. automodule:: Orange.misc.environ
372
373"""
374import environ
375import counters
376import render
377
378from Orange.core import RandomGenerator as Random
379from orange import SymMatrix
380from orange import CostMatrix
381
382# addons is intentionally not imported; if it were, add-ons' directories would
383# be added to the python path. If that sounds OK, this can be changed ...
384
385__all__ = ["counters", "selection", "render", "serverfiles",
386           "deprecated_members", "deprecated_keywords",
387           "deprecated_attribute", "deprecation_warning"]
388
389import random, types, sys
390import time
391
392def getobjectname(x, default=""):
393    if type(x)==types.StringType:
394        return x
395     
396    for i in ["name", "shortDescription", "description", "func_doc", "func_name"]:
397        if getattr(x, i, ""):
398            return getattr(x, i)
399
400    if hasattr(x, "__class__"):
401        r = repr(x.__class__)
402        if r[1:5]=="type":
403            return str(x.__class__)[7:-2]
404        elif r[1:6]=="class":
405            return str(x.__class__)[8:-2]
406    return default
407
408
409def demangle_examples(x):
410    if type(x)==types.TupleType:
411        return x
412    else:
413        return x, 0
414
415
416def frange(*argw):
417    """ Like builtin `range` but works with floats
418    """
419    start, stop, step = 0.0, 1.0, 0.1
420    if len(argw)==1:
421        start=step=argw[0]
422    elif len(argw)==2:
423        stop, step = argw
424    elif len(argw)==3:
425        start, stop, step = argw
426    elif len(argw)>3:
427        raise AttributeError, "1-3 arguments expected"
428
429    stop+=1e-10
430    i=0
431    res=[]
432    while 1:
433        f=start+i*step
434        if f>stop:
435            break
436        res.append(f)
437        i+=1
438    return res
439
440verbose = 0
441
442def print_verbose(text, *verb):
443    if len(verb) and verb[0] or verbose:
444        print text
445
446
447class ConsoleProgressBar(object):
448    """ A class to for printing progress bar reports in the console.
449   
450    Example ::
451   
452        >>> import sys, time
453        >>> progress = ConsoleProgressBar("Example", output=sys.stdout)
454        >>> for i in range(100):
455        ...    progress.advance()
456        ...    # Or progress.set_state(i)
457        ...    time.sleep(0.01)
458        ...
459        ...
460        Example ===================================>100%
461       
462    """
463    def __init__(self, title="", charwidth=40, step=1, output=None):
464        """ Initialize the progress bar.
465       
466        :param title: The title for the progress bar.
467        :type title: str
468        :param charwidth: The maximum progress bar width in characters.
469       
470            .. todo:: Get the console width from the ``output`` if the
471                information can be retrieved.
472               
473        :type charwidth: int
474        :param step: A default step used if ``advance`` is called without
475            any  arguments
476       
477        :type step: int
478        :param output: The output file. If None (default) then ``sys.stderr``
479            is used.
480           
481        :type output: An file like object to print the progress report to.
482         
483        """
484        self.title = title + " "
485        self.charwidth = charwidth
486        self.step = step
487        self.currstring = ""
488        self.state = 0
489        if output is None:
490            output = sys.stderr
491        self.output = output
492
493    def clear(self, i=-1):
494        """ Clear the current progress line indicator string.
495        """
496        try:
497            if hasattr(self.output, "isatty") and self.output.isatty():
498                self.output.write("\b" * (i if i != -1 else len(self.currstring)))
499            else:
500                self.output.seek(-i if i != -1 else -len(self.currstring), 2)
501        except Exception: ## If for some reason we failed
502            self.output.write("\n")
503
504    def getstring(self):
505        """ Return the progress indicator string.
506        """
507        progchar = int(round(float(self.state) * (self.charwidth - 5) / 100.0))
508        return self.title + "=" * (progchar) + ">" + " " * (self.charwidth\
509            - 5 - progchar) + "%3i" % int(round(self.state)) + "%"
510
511    def printline(self, string):
512        """ Print the ``string`` to the output file.
513        """
514        try:
515            self.clear()
516            self.output.write(string)
517            self.output.flush()
518        except Exception:
519            pass
520        self.currstring = string
521
522    def __call__(self, newstate=None):
523        """ Set the ``newstate`` as the current state of the progress bar.
524        ``newstate`` must be in the interval [0, 100].
525       
526        .. note:: ``set_state`` is the prefered way to set a new steate.
527       
528        :param newstate: The new state of the progress bar.
529        :type newstate: float
530         
531        """
532        if newstate is None:
533            self.advance()
534        else:
535            self.set_state(newstate)
536           
537    def set_state(self, newstate):
538        """ Set the ``newstate`` as the current state of the progress bar.
539        ``newstate`` must be in the interval [0, 100].
540       
541        :param newstate: The new state of the progress bar.
542        :type newstate: float
543       
544        """
545        if int(newstate) != int(self.state):
546            self.state = newstate
547            self.printline(self.getstring())
548        else:
549            self.state = newstate
550           
551    def advance(self, step=None):
552        """ Advance the current state by ``step``. If ``step`` is None use
553        the default step as set at class initialization.
554         
555        """
556        if step is None:
557            step = self.step
558           
559        newstate = self.state + step
560        self.set_state(newstate)
561
562    def finish(self):
563        """ Finish the progress bar (i.e. set the state to 100 and
564        print the final newline to the ``output`` file).
565        """
566        self.__call__(100)
567        self.output.write("\n")
568
569def progress_bar_milestones(count, iterations=100):
570    return set([int(i*count/float(iterations)) for i in range(iterations)])
571
572def lru_cache(maxsize=100):
573    """ A least recently used cache function decorator.
574    (Similar to the functools.lru_cache in python 3.2)
575    """
576   
577    def decorating_function(func):
578        import functools
579        cache = {}
580       
581        @functools.wraps(func)
582        def wrapped(*args, **kwargs):
583            key = args + tuple(sorted(kwargs.items()))
584            if key not in cache:
585                res = func(*args, **kwargs)
586                cache[key] = (time.time(), res)
587                if len(cache) > maxsize:
588                    key, (_, _) = min(cache.iteritems(), key=lambda item: item[1][0])
589                    del cache[key]
590            else:
591                _, res = cache[key]
592                cache[key] = (time.time(), res) # update the time
593               
594            return res
595       
596        def clear():
597            cache.clear()
598       
599        wrapped.clear = clear
600        wrapped._cache = cache
601       
602        return wrapped
603    return decorating_function
604
605#from Orange.misc.render import contextmanager
606from contextlib import contextmanager
607
608
609@contextmanager
610def member_set(obj, name, val):
611    """ A context manager that sets member ``name`` on ``obj`` to ``val``
612    and restores the previous value on exit.
613    """
614    old_val = getattr(obj, name, val)
615    setattr(obj, name, val)
616    yield
617    setattr(obj, name, old_val)
618   
619   
620class recursion_limit(object):
621    """ A context manager that sets a new recursion limit.
622   
623    """
624    def __init__(self, limit=1000):
625        self.limit = limit
626       
627    def __enter__(self):
628        self.old_limit = sys.getrecursionlimit()
629        sys.setrecursionlimit(self.limit)
630   
631    def __exit__(self, exc_type, exc_val, exc_tb):
632        sys.setrecursionlimit(self.old_limit)
633       
634
635import warnings
636def deprecation_warning(old, new, stacklevel=-2):
637    """ Raise a deprecation warning of an obsolete attribute access.
638   
639    :param old: Old attribute name (used in warning message).
640    :param new: New attribute name (used in warning message).
641   
642    """
643    warnings.warn("'%s' is deprecated. Use '%s' instead!" % (old, new), DeprecationWarning, stacklevel=stacklevel)
644   
645# We need to get the instancemethod type
646class _Foo():
647    def bar(self):
648        pass
649instancemethod = type(_Foo.bar)
650del _Foo
651
652function = type(lambda: None)
653
654class universal_set(set):
655    """ A universal set, pretends it contains everything.
656    """
657    def __contains__(self, value):
658        return True
659   
660from functools import wraps
661
662def deprecated_members(name_map, wrap_methods="all", in_place=True):
663    """ Decorate a class with properties for accessing attributes, and methods
664    with deprecated names. In addition methods from the `wrap_methods` list
665    will be wrapped to receive mapped keyword arguments.
666   
667    :param name_map: A dictionary mapping old into new names.
668    :type name_map: dict
669   
670    :param wrap_methods: A list of method names to wrap. Wrapped methods will
671        be called with mapped keyword arguments (by default all methods will
672        be wrapped).
673    :type wrap_methods: list
674   
675    :param in_place: If True the class will be modified in place, otherwise
676        it will be subclassed (default True).
677    :type in_place: bool
678   
679    Example ::
680           
681        >>> class A(object):
682        ...     def __init__(self, foo_bar="bar"):
683        ...         self.set_foo_bar(foo_bar)
684        ...     
685        ...     def set_foo_bar(self, foo_bar="bar"):
686        ...         self.foo_bar = foo_bar
687        ...
688        ... A = deprecated_members(
689        ... {"fooBar": "foo_bar",
690        ...  "setFooBar":"set_foo_bar"},
691        ... wrap_methods=["set_foo_bar", "__init__"])(A)
692        ...
693        ...
694        >>> a = A(fooBar="foo")
695        __main__:1: DeprecationWarning: 'fooBar' is deprecated. Use 'foo_bar' instead!
696        >>> print a.fooBar, a.foo_bar
697        foo foo
698        >>> a.setFooBar("FooBar!")
699        __main__:1: DeprecationWarning: 'setFooBar' is deprecated. Use 'set_foo_bar' instead!
700       
701    .. note:: This decorator does nothing if \
702        :obj:`Orange.misc.environ.orange_no_deprecated_members` environment \
703        variable is set to `True`.
704       
705    """
706    if environ.orange_no_deprecated_members:
707        return lambda cls: cls
708   
709    def is_wrapped(method):
710        """ Is member method already wrapped.
711        """
712        if getattr(method, "_deprecate_members_wrapped", False):
713            return True
714        elif hasattr(method, "im_func"):
715            im_func = method.im_func
716            return getattr(im_func, "_deprecate_members_wrapped", False)
717        else:
718            return False
719       
720    if wrap_methods == "all":
721        wrap_methods = universal_set()
722    elif not wrap_methods:
723        wrap_methods = set()
724       
725    def wrapper(cls):
726        cls_names = {}
727        # Create properties for accessing deprecated members
728        for old_name, new_name in name_map.items():
729            cls_names[old_name] = deprecated_attribute(old_name, new_name)
730           
731        # wrap member methods to map keyword arguments
732        for key, value in cls.__dict__.items():
733            if isinstance(value, (instancemethod, function)) \
734                and not is_wrapped(value) and key in wrap_methods:
735               
736                wrapped = deprecated_keywords(name_map)(value)
737                wrapped._deprecate_members_wrapped = True # A flag indicating this function already maps keywords
738                cls_names[key] = wrapped
739        if in_place:
740            for key, val in cls_names.items():
741                setattr(cls, key, val)
742            return cls
743        else:
744            return type(cls.__name__, (cls,), cls_names)
745       
746    return wrapper
747
748def deprecated_keywords(name_map):
749    """ Deprecates the keyword arguments of the function.
750   
751    Example ::
752   
753        >>> @deprecated_keywords({"myArg": "my_arg"})
754        ... def my_func(my_arg=None):
755        ...     print my_arg
756        ...
757        ...
758        >>> my_func(myArg="Arg")
759        __main__:1: DeprecationWarning: 'myArg' is deprecated. Use 'my_arg' instead!
760        Arg
761       
762    .. note:: This decorator does nothing if \
763        :obj:`Orange.misc.environ.orange_no_deprecated_members` environment \
764        variable is set to `True`.
765       
766    """
767    if environ.orange_no_deprecated_members:
768        return lambda func: func
769    for name in name_map.values():
770        if name in name_map:
771            raise ValueError("Deprecation keys and values overlap; this could"
772                             " cause trouble!")
773   
774    def decorator(func):
775        @wraps(func)
776        def wrap_call(*args, **kwargs):
777            kwargs = dict(kwargs)
778            for name in name_map:
779                if name in kwargs:
780                    deprecation_warning(name, name_map[name], stacklevel=3)
781                    kwargs[name_map[name]] = kwargs[name]
782                    del kwargs[name]
783            return func(*args, **kwargs)
784        return wrap_call
785    return decorator
786
787def deprecated_attribute(old_name, new_name):
788    """ Return a property object that accesses an attribute named `new_name`
789    and raises a deprecation warning when doing so.
790
791    ..
792
793        >>> sys.stderr = sys.stdout
794   
795    Example ::
796   
797        >>> class A(object):
798        ...     def __init__(self):
799        ...         self.my_attr = "123"
800        ...     myAttr = deprecated_attribute("myAttr", "my_attr")
801        ...
802        ...
803        >>> a = A()
804        >>> print a.myAttr
805        ...:1: DeprecationWarning: 'myAttr' is deprecated. Use 'my_attr' instead!
806        123
807       
808    .. note:: This decorator does nothing and returns None if \
809        :obj:`Orange.misc.environ.orange_no_deprecated_members` environment \
810        variable is set to `True`.
811       
812    """
813    if environ.orange_no_deprecated_members:
814        return None
815   
816    def fget(self):
817        deprecation_warning(old_name, new_name, stacklevel=3)
818        return getattr(self, new_name)
819   
820    def fset(self, value):
821        deprecation_warning(old_name, new_name, stacklevel=3)
822        setattr(self, new_name, value)
823   
824    def fdel(self):
825        deprecation_warning(old_name, new_name, stacklevel=3)
826        delattr(self, new_name)
827   
828    prop = property(fget, fset, fdel,
829                    doc="A deprecated member '%s'. Use '%s' instead." % (old_name, new_name))
830    return prop
831
832class class_property(object):
833    def __init__(self, fget=None, fset=None, fdel=None, doc="class property"):
834        self.fget = fget
835        self.fset = fset
836        self.fdel = fdel
837        self.__doc__ = doc
838       
839    def __get__(self, instance, owner):
840        if instance is None:
841            return self.fget(owner)
842        else:
843            return self.fget(instance)               
844           
845def deprecated_class_attribute(old_name, new_name):
846    """ Return a property object that accesses an class attribute
847    named `new_name` and raises a deprecation warning when doing so.
848   
849    """
850    if environ.orange_no_deprecated_members:
851        return None
852   
853    def fget(self):
854        deprecation_warning(old_name, new_name, stacklevel=3)
855        return getattr(self, new_name)
856       
857    prop = class_property(fget,
858                    doc="A deprecated class member '%s'. Use '%s' instead." % (old_name, new_name))
859    return prop
860
861def deprecated_function_name(func):
862    """ Return a wrapped function that raises an deprecation warning when
863    called. This should be used for deprecation of module level function names.
864   
865    Example ::
866   
867        >>> def func_a(arg):
868        ...    print "This is func_a  (used to be named funcA) called with", arg
869        ...
870        ...
871        >>> funcA = deprecated_function_name(func_a)
872        >>> funcA(None)
873         
874   
875    .. note:: This decorator does nothing and if \
876        :obj:`Orange.misc.environ.orange_no_deprecated_members` environment \
877        variable is set to `True`.
878       
879    """
880    if environ.orange_no_deprecated_members:
881        return func
882   
883    @wraps(func)
884    def wrapped(*args, **kwargs):
885        warnings.warn("Deprecated function name. Use %r instead!" % func.__name__,
886                      DeprecationWarning, stacklevel=2)
887        return func(*args, **kwargs)
888    return wrapped
889   
890
891"""
892Some utility functions common to Orange classes.
893 
894"""
895
896def _orange__new__(base=None):
897    """ Return an orange 'schizofrenic' __new__ class method.
898   
899    :param base: base orange class (default orange.Learner)
900    :type base: type
901         
902    Example::
903        class NewOrangeLearner(orange.Learner):
904            __new__ = _orange__new(orange.Learner)
905       
906    """
907    if base is None:
908        import Orange
909        base = Orange.core.Learner
910       
911    @wraps(base.__new__)
912    def _orange__new_wrapped(cls, data=None, **kwargs):
913        if base == object:
914            self = base.__new__(cls)
915        else:
916            self = base.__new__(cls, **kwargs)
917
918        if data:
919            self.__init__(**kwargs)
920            return self.__call__(data)
921        else:
922            return self
923    return _orange__new_wrapped
924
925
926def _orange__reduce__(self):
927    """ A default __reduce__ method for orange types. Assumes the object
928    can be reconstructed with the call `constructor(__dict__)` where __dict__
929    if the stored (pickled) __dict__ attribute.
930   
931    Example::
932        class NewOrangeType(orange.Learner):
933            __reduce__ = _orange__reduce()
934    """ 
935    return type(self), (), dict(self.__dict__)
936
937
938demangleExamples = deprecated_function_name(demangle_examples)
939progressBarMilestones = deprecated_function_name(progress_bar_milestones)
940printVerbose = deprecated_function_name(print_verbose)
941
942# Must be imported after deprecation function definitions
943import selection
Note: See TracBrowser for help on using the repository browser.