Revision 9692:7ce66f39dada, 18.1 KB checked in by Jure Zbontar <jure.zbontar@…>, 2 years ago (diff)

Renamed RandomGenerator to Random

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