Ignore:
Timestamp:
03/23/11 11:09:39 (3 years ago)
Author:
ales_erjavec <ales.erjavec@…>
Branch:
default
Convert:
5fdc3540a0fd5a1f35cf912d75b865ef64711a4e
Message:

Added deprecation utility functions.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • orange/Orange/misc/__init__.py

    r7742 r7776  
    9090optimal one by calling :obj:`winnerIndex`. 
    9191 
    92 ================== 
    93 Server files 
    94 ================== 
    95  
    96 .. index:: server files 
    97  
    98 .. automodule:: Orange.misc.serverfiles 
    99  
    10092""" 
    10193 
    102 import counters 
    103 import selection 
    104 import render 
    105 import serverfiles 
    106  
    107 __all__ = ["counters", "selection", "render", "serverfiles"] 
     94 
     95__all__ = ["counters", "selection", "render", "deprecated_members", 
     96           "deprecated_keywords", "deprecated_attribute", "deprecation_warning"] 
    10897 
    10998import random, types, sys 
     99import time 
    110100 
    111101def getobjectname(x, default=""): 
     
    208198def progressBarMilestones(count, iterations=100): 
    209199    return set([int(i*count/float(iterations)) for i in range(iterations)]) 
     200 
     201def lru_cache(maxsize=100): 
     202    """ A least recently used cache function decorator. 
     203    (Similar to the functools.lru_cache in python 3.2) 
     204    """ 
     205     
     206    def decorating_function(func): 
     207        import functools 
     208        cache = {} 
     209         
     210        functools.wraps(func) 
     211        def wrapped(*args, **kwargs): 
     212            key = args + tuple(sorted(kwargs.items())) 
     213            if key not in cache: 
     214                res = func(*args, **kwargs) 
     215                cache[key] = (time.time(), res) 
     216                if len(cache) > maxsize: 
     217                    key, (_, _) = min(cache.iteritems(), key=lambda item: item[1][0]) 
     218                    del cache[key] 
     219            else: 
     220                _, res = cache[key] 
     221                cache[key] = (time.time(), res) # update the time 
     222                 
     223            return res 
     224         
     225        def clear(): 
     226            cache.clear() 
     227         
     228        wrapped.clear = clear 
     229         
     230        return wrapped 
     231    return decorating_function 
     232 
     233 
     234"""\ 
     235Deprecation utility functions. 
     236 
     237""" 
     238 
     239import warnings 
     240def deprecation_warning(old, new, stacklevel=-2): 
     241    warnings.warn("'%s' is deprecated. Use '%s' instead!" % (old, new), DeprecationWarning, stacklevel=stacklevel) 
     242    
     243# We need to get the instancemethod type  
     244class _Foo(): 
     245    def bar(self): 
     246        pass 
     247instancemethod = type(_Foo.bar) 
     248del _Foo 
     249 
     250function = type(lambda: None) 
     251 
     252class universal_set(set): 
     253    """ A universal set, pretends it contains everything. 
     254    """ 
     255    def __contains__(self, value): 
     256        return True 
     257     
     258from functools import wraps 
     259 
     260def deprecated_members(name_map, wrap_methods="all", in_place=True): 
     261    """ Decorate a class with properties for accessing attributes, and methods 
     262    with deprecated names. In addition methods from the `wrap_methods` list 
     263    will be wrapped to receive mapped keyword arguments. 
     264     
     265    :param name_map: A dictionary mapping old into new names. 
     266    :type name_map: dict 
     267     
     268    :param wrap_methods: A list of method names to wrap. Wrapped methods will 
     269        be called with mapped keyword arguments (by default all methods will 
     270        be wrapped). 
     271    :type wrap_methods: list 
     272     
     273    Example :: 
     274             
     275        >>> @deprecated_members({"fooBar": "foo_bar", "setFooBar":"set_foo_bar"}, 
     276        ...                    wrap_methods=["set_foo_bar", "__init__"]) 
     277        ... class A(object): 
     278        ...     def __init__(self, foo_bar="bar"): 
     279        ...         self.set_foo_bar(foo_bar) 
     280        ...      
     281        ...     def set_foo_bar(self, foo_bar="bar"): 
     282        ...         self.foo_bar = foo_bar 
     283        ...          
     284        ... 
     285        >>> a = A(fooBar="foo") 
     286        __main__:1: DeprecationWarning: 'fooBar' is deprecated. Use 'foo_bar' instead! 
     287        >>> print a.fooBar, a.foo_bar 
     288        foo foo 
     289        >>> a.setFooBar("FooBar!") 
     290        __main__:1: DeprecationWarning: 'setFooBar' is deprecated. Use 'set_foo_bar' instead! 
     291         
     292    """ 
     293    def is_wrapped(method): 
     294        """ Is member method already wrapped. 
     295        """ 
     296        if getattr(method, "_deprecate_members_wrapped", False): 
     297            return True 
     298        elif hasattr(method, "im_func"): 
     299            im_func = method.im_func 
     300            return getattr(im_func, "_deprecate_members_wrapped", False) 
     301        else: 
     302            return False 
     303         
     304    if wrap_methods == "all": 
     305        wrap_methods = universal_set() 
     306    elif not wrap_methods: 
     307        wrap_methods = set() 
     308         
     309    def wrapper(cls): 
     310        cls_names = {} 
     311        # Create properties for accessing deprecated members 
     312        for old_name, new_name in name_map.items(): 
     313            cls_names[old_name] = deprecated_attribute(old_name, new_name) 
     314             
     315        # wrap member methods to map keyword arguments 
     316        for key, value in cls.__dict__.items(): 
     317            if isinstance(value, (instancemethod, function)) \ 
     318                and not is_wrapped(value) and key in wrap_methods: 
     319                 
     320                wrapped = deprecated_keywords(name_map)(value) 
     321                wrapped._deprecate_members_wrapped = True # A flag indicating this function already maps keywords 
     322                cls_names[key] = wrapped 
     323        if in_place: 
     324            for key, val in cls_names.items(): 
     325                setattr(cls, key, val) 
     326            return cls 
     327        else: 
     328            return type(cls.__name__, (cls,), cls_names) 
     329         
     330    return wrapper 
     331 
     332def deprecated_keywords(name_map): 
     333    """ Deprecates the keyword arguments of the function. 
     334     
     335    Example :: 
     336     
     337        >>> @deprecated_keywords({"myArg": "my_arg"}) 
     338        ... def my_func(my_arg=None): 
     339        ...     print my_arg 
     340        ... 
     341        ... 
     342        >>> my_func(myArg="Arg") 
     343        __main__:1: DeprecationWarning: 'myArg' is deprecated. Use 'my_arg' instead! 
     344        Arg 
     345         
     346    """ 
     347    def decorator(func): 
     348        @wraps(func) 
     349        def wrap_call(*args, **kwargs): 
     350            kwargs = dict(kwargs) 
     351            for name in name_map: 
     352                if name in kwargs: 
     353                    deprecation_warning(name, name_map[name], stacklevel=3) 
     354                    kwargs[name_map[name]] = kwargs[name] 
     355                    del kwargs[name] 
     356            return func(*args, **kwargs) 
     357        return wrap_call 
     358    return decorator 
     359 
     360def deprecated_attribute(old_name, new_name): 
     361    """ Return a property object that accesses an attribute named `new_name` 
     362    and raises a deprecation warning when doing so. 
     363     
     364    Example :: 
     365     
     366        >>> class A(object): 
     367        ...     def __init__(self): 
     368        ...         self.my_attr = "123" 
     369        ...     myAttr = deprecated_attribute("myAttr", "my_attr") 
     370        ... 
     371        ... 
     372        >>> a = A() 
     373        >>> print a.myAttr 
     374        __main__:1: DeprecationWarning: 'myAttr' is deprecated. Use 'my_attr' instead! 
     375        123 
     376         
     377    """ 
     378    def fget(self): 
     379        deprecation_warning(old_name, new_name, stacklevel=3) 
     380        return getattr(self, new_name) 
     381     
     382    def fset(self, value): 
     383        deprecation_warning(old_name, new_name, stacklevel=3) 
     384        setattr(self, new_name, value) 
     385     
     386    def fdel(self): 
     387        deprecation_warning(old_name, new_name, stacklevel=3) 
     388        delattr(self, new_name) 
     389     
     390    prop = property(fget, fset, fdel, 
     391                    doc="A deprecated member '%s'. Use '%s' instead." % (old_name, new_name)) 
     392    return prop 
     393     
     394def _test(): 
     395    import doctest 
     396    doctest.testmod() 
     397     
     398if __name__ == "__main__": 
     399    _test() 
Note: See TracChangeset for help on using the changeset viewer.