def patch_noargs_decorator(decorator): def new_decorator(func): wrapper = decorator(func) wrapper.__wrapped__ = func return wrapper util.mergeFunctionMetadata(decorator, new_decorator) return new_decorator
def onlyOnce(fn): 'Set up FN to only run once within an interpreter instance' def wrap(*args, **kwargs): if hasattr(fn, 'called'): return fn.called = 1 return fn(*args, **kwargs) util.mergeFunctionMetadata(fn, wrap) return wrap
def _maybeUnhandled(fn): def wrap(self, *args, **kwargs): d = fn(self, *args, **kwargs) if self._start_unhandled_deferreds is not None: self._start_unhandled_deferreds.append(d) return d wrap.__wrapped__ = fn twutil.mergeFunctionMetadata(fn, wrap) return wrap
def _maybeUnhandled(fn): def wrap(self, *args, **kwargs): d = fn(self, *args, **kwargs) if self._start_unhandled_deferreds is not None: self._start_unhandled_deferreds.append(d) return d wrap.__wrapped__ = fn twutil.mergeFunctionMetadata(fn, wrap) return wrap
def _f(f): def g(*args, **kw): _close_db() try: ret = f(*args, **kw) finally: _close_db() return ret mergeFunctionMetadata(f, g) return g
def onlyOnce(fn): 'Set up FN to only run once within an interpreter instance' def wrap(*args, **kwargs): if hasattr(fn, 'called'): return fn.called = 1 return fn(*args, **kwargs) util.mergeFunctionMetadata(fn, wrap) return wrap
def _f(f): def g(*args, **kw): starttime = datetime.datetime.utcnow() dbase = database if dbase is None: raise Exception('dbase is None') _transact_begin_log_helper('@transact', f, dbase) if (dbase is not None) and hasattr( dbase, 'store') and (dbase.store is not None): # hasattr + store check is to fix #621, this is also an API issue with rdf.py t = dbase.begin_transaction() else: t = None global toplevel_transaction if toplevel_transaction is None: toplevel_transaction = f try: ret = f(*args, **kw) except: if toplevel_transaction == f: toplevel_transaction = None t_locked_time = None if t is not None: t_locked_time = t.get_locked_time() t.commit() endtime = datetime.datetime.utcnow() _transact_end_log_helper('@transact', f, dbase, False, starttime, endtime, t_locked_time, TRANSACT_LOCK_TIME_WARNING_LIMIT) raise if toplevel_transaction == f: toplevel_transaction = None t_locked_time = None if t is not None: t_locked_time = t.get_locked_time() t.commit() endtime = datetime.datetime.utcnow() _transact_end_log_helper('@transact', f, dbase, True, starttime, endtime, t_locked_time, TRANSACT_LOCK_TIME_WARNING_LIMIT) return ret mergeFunctionMetadata(f, g) return g
def _f(f): def g(self, *args, **kw): if not hasattr(self, 'identify_successful'): raise managementprotocol.InternalServerError('no identify_successful attribute found') elif not isinstance(self.identify_successful, bool): raise managementprotocol.InternalServerError('identify_successful attribute not boolean') elif not self.identify_successful: raise managementprotocol.ProtocolStateError('request requires successful Identify') else: return f(self, *args, **kw) mergeFunctionMetadata(f, g) return g
def _f(f): def g(*args, **kw): starttime = datetime.datetime.utcnow() dbase = database if dbase is None: raise Exception('dbase is None') _transact_begin_log_helper('@untransact', f, dbase) if (dbase is not None) and hasattr(dbase, 'store') and (dbase.store is not None): # hasattr + store check is to fix #621, this is also an API issue with rdf.py t = dbase.begin_untransaction() else: t = None global toplevel_transaction # Log transact locktime here if active if t is not None: # XXX: There is no sensible date value to show here, # because we are not actually logging anything for # current untransaction: we log the locktime of the # toplevel transaction which is committed here. Using # current time as both timestamps for now. _transact_end_log_helper('@transact (in untransact)', toplevel_transaction, dbase, True, datetime.datetime.utcnow(), datetime.datetime.utcnow(), t.get_txn_locked_time(), TRANSACT_LOCK_TIME_WARNING_LIMIT, untransact_point=f) try: ret = f(*args, **kw) except: t_locked_time = None if t is not None: t_locked_time = t.get_locked_time() t.commit() endtime = datetime.datetime.utcnow() _transact_end_log_helper('@untransact', f, dbase, False, starttime, endtime, t_locked_time, UNTRANSACT_LOCK_TIME_WARNING_LIMIT) raise t_locked_time = None if t is not None: t_locked_time = t.get_locked_time() t.commit() endtime = datetime.datetime.utcnow() _transact_end_log_helper('@untransact', f, dbase, True, starttime, endtime, t_locked_time, UNTRANSACT_LOCK_TIME_WARNING_LIMIT) return ret mergeFunctionMetadata(f, g) return g
def _f(f): def g(*args, **kw): starttime = datetime.datetime.utcnow() dbase = database if dbase is None: raise Exception('dbase is None') _transact_begin_log_helper('@transact', f, dbase) if (dbase is not None) and hasattr(dbase, 'store') and (dbase.store is not None): # hasattr + store check is to fix #621, this is also an API issue with rdf.py t = dbase.begin_transaction() else: t = None global toplevel_transaction if toplevel_transaction is None: toplevel_transaction = f try: ret = f(*args, **kw) except: if toplevel_transaction == f: toplevel_transaction = None t_locked_time = None if t is not None: t_locked_time = t.get_locked_time() t.commit() endtime = datetime.datetime.utcnow() _transact_end_log_helper('@transact', f, dbase, False, starttime, endtime, t_locked_time, TRANSACT_LOCK_TIME_WARNING_LIMIT) raise if toplevel_transaction == f: toplevel_transaction = None t_locked_time = None if t is not None: t_locked_time = t.get_locked_time() t.commit() endtime = datetime.datetime.utcnow() _transact_end_log_helper('@transact', f, dbase, True, starttime, endtime, t_locked_time, TRANSACT_LOCK_TIME_WARNING_LIMIT) return ret mergeFunctionMetadata(f, g) return g
def fastInline(f): """This decorator is mostly equivalent to defer.inlineCallbacks except that speed is the primary priority. while inlineCallbacks checks a few different things to make sure you're doing it properly, this method simply assumes you are. changes: * this decorator no longer checks that you're wrapping a generator * this decorator no longer checks that _DefGen_Return is thrown from the decoratored generator only * the generator loop no longer double checks that you're yielding deferreds and simply assumes it, catching the exception if you aren't * the generator stops calling isinstance for logic - profiling reveals that isinstance consumes a nontrivial amount of computing power during heavy use You can force this method to behave exactly like defer.inlineCallbacks by providing the FORCE_STANDARD_INLINE_CALLBACKS environment variable """ def unwind(*args, **kwargs): try: gen = f(*args, **kwargs) except defer._DefGen_Return: # pylint: disable=W0212 raise TypeError("defer.returnValue used from non-generator") return _inlineCallbacks(None, None, gen, defer.Deferred()) return mergeFunctionMetadata(f, unwind)
def deferredGenerator(f): """ See L{waitForDeferred}. """ def unwindGenerator(*args, **kwargs): return _deferGenerator(f(*args, **kwargs)) return mergeFunctionMetadata(f, unwindGenerator)
def goodDecorator(fn): """ Decorate a function and preserve the original name. """ def nameCollision(*args, **kwargs): return fn(*args, **kwargs) return mergeFunctionMetadata(fn, nameCollision)
def goodDecorator(fn): """ Decorate a function and preserve the original name. """ def nameCollision(*args, **kwargs): return fn(*args, **kwargs) return mergeFunctionMetadata(fn, nameCollision)
def deferredGenerator(f): """ deferredGenerator and waitForDeferred help you write L{Deferred}-using code that looks like a regular sequential function. If your code has a minimum requirement of Python 2.5, consider the use of L{inlineCallbacks} instead, which can accomplish the same thing in a more concise manner. There are two important functions involved: L{waitForDeferred}, and L{deferredGenerator}. They are used together, like this:: def thingummy(): thing = waitForDeferred(makeSomeRequestResultingInDeferred()) yield thing thing = thing.getResult() print thing #the result! hoorj! thingummy = deferredGenerator(thingummy) L{waitForDeferred} returns something that you should immediately yield; when your generator is resumed, calling C{thing.getResult()} will either give you the result of the L{Deferred} if it was a success, or raise an exception if it was a failure. Calling C{getResult} is B{absolutely mandatory}. If you do not call it, I{your program will not work}. L{deferredGenerator} takes one of these waitForDeferred-using generator functions and converts it into a function that returns a L{Deferred}. The result of the L{Deferred} will be the last value that your generator yielded unless the last value is a L{waitForDeferred} instance, in which case the result will be C{None}. If the function raises an unhandled exception, the L{Deferred} will errback instead. Remember that C{return result} won't work; use C{yield result; return} in place of that. Note that not yielding anything from your generator will make the L{Deferred} result in C{None}. Yielding a L{Deferred} from your generator is also an error condition; always yield C{waitForDeferred(d)} instead. The L{Deferred} returned from your deferred generator may also errback if your generator raised an exception. For example:: def thingummy(): thing = waitForDeferred(makeSomeRequestResultingInDeferred()) yield thing thing = thing.getResult() if thing == 'I love Twisted': # will become the result of the Deferred yield 'TWISTED IS GREAT!' return else: # will trigger an errback raise Exception('DESTROY ALL LIFE') thingummy = deferredGenerator(thingummy) Put succinctly, these functions connect deferred-using code with this 'fake blocking' style in both directions: L{waitForDeferred} converts from a L{Deferred} to the 'blocking' style, and L{deferredGenerator} converts from the 'blocking' style to a L{Deferred}. """ def unwindGenerator(*args, **kwargs): return _deferGenerator(f(*args, **kwargs), Deferred()) return mergeFunctionMetadata(f, unwindGenerator)
def coroutine(f): # originally inlineCallbacks """Enhanced version of twisted.internet.defer.inlineCallbacks with fuller support coroutine functionality. Please see the documentation for twisted.internet.defer.inlineCallbacks for more information. See also: txcoroutine.noreturn for information on how to use optimized tail recursion with this decorator. """ def unwindGenerator(*args, **kwargs): try: gen = f(*args, **kwargs) except (_DefGen_Return, _NoReturn) as e: badUsage = 'returnValue' if isinstance( e, _DefGen_Return) else 'noreturn' raise TypeError( "inlineCallbacks requires %r to produce a generator; instead " "caught %s being used in a non-generator" % ( f, badUsage, )) if not isinstance(gen, types.GeneratorType): raise TypeError( "inlineCallbacks requires %r to produce a generator; " "instead got %r" % (f, gen)) return _inlineCallbacks(None, gen, Coroutine(canceller=lambda _: gen.close())) return mergeFunctionMetadata(f, unwindGenerator)
def decorator(function): def decorated(*a, **kw): with context_factory(*args, **kwargs): return function(*a, **kw) return mergeFunctionMetadata(function, decorated)
def deferredGenerator(f): """ deferredGenerator and waitForDeferred help you write L{Deferred}-using code that looks like a regular sequential function. If your code has a minimum requirement of Python 2.5, consider the use of L{inlineCallbacks} instead, which can accomplish the same thing in a more concise manner. There are two important functions involved: L{waitForDeferred}, and L{deferredGenerator}. They are used together, like this:: def thingummy(): thing = waitForDeferred(makeSomeRequestResultingInDeferred()) yield thing thing = thing.getResult() print thing #the result! hoorj! thingummy = deferredGenerator(thingummy) L{waitForDeferred} returns something that you should immediately yield; when your generator is resumed, calling C{thing.getResult()} will either give you the result of the L{Deferred} if it was a success, or raise an exception if it was a failure. Calling C{getResult} is B{absolutely mandatory}. If you do not call it, I{your program will not work}. L{deferredGenerator} takes one of these waitForDeferred-using generator functions and converts it into a function that returns a L{Deferred}. The result of the L{Deferred} will be the last value that your generator yielded unless the last value is a L{waitForDeferred} instance, in which case the result will be C{None}. If the function raises an unhandled exception, the L{Deferred} will errback instead. Remember that C{return result} won't work; use C{yield result; return} in place of that. Note that not yielding anything from your generator will make the L{Deferred} result in C{None}. Yielding a L{Deferred} from your generator is also an error condition; always yield C{waitForDeferred(d)} instead. The L{Deferred} returned from your deferred generator may also errback if your generator raised an exception. For example:: def thingummy(): thing = waitForDeferred(makeSomeRequestResultingInDeferred()) yield thing thing = thing.getResult() if thing == 'I love Twisted': # will become the result of the Deferred yield 'TWISTED IS GREAT!' return else: # will trigger an errback raise Exception('DESTROY ALL LIFE') thingummy = deferredGenerator(thingummy) Put succinctly, these functions connect deferred-using code with this 'fake blocking' style in both directions: L{waitForDeferred} converts from a L{Deferred} to the 'blocking' style, and L{deferredGenerator} converts from the 'blocking' style to a L{Deferred}. """ def unwindGenerator(*args, **kwargs): return _deferGenerator(f(*args, **kwargs), Deferred()) return mergeFunctionMetadata(f, unwindGenerator)
def make_it_green(f): def unwindGenerator(*args, **kwargs): g = greenlet(f) # Похоже, что такой тупой ход парента не меняет. # Также похоже, что и без него работает, оставляя выполнение в текущем гринлете. #g.parent = main return _inline_greens(None, g, (args, kwargs), defer.Deferred()) return mergeFunctionMetadata(f, unwindGenerator)
def fireWhenDoneFunc(d, f): """Returns closure that when called calls f and then callbacks d. """ def newf(*args, **kw): rtn = f(*args, **kw) d.callback('') return rtn return util.mergeFunctionMetadata(f, newf)
def deferredGenerator(f): """ See L{waitForDeferred}. """ def unwindGenerator(*args, **kwargs): return _deferGenerator(f(*args, **kwargs)) return mergeFunctionMetadata(f, unwindGenerator)
def _fireWhenDoneFunc(self, d, f): """Returns closure that when called calls f and then callbacks d. """ from twisted.python import util as tputil def newf(*args, **kw): rtn = f(*args, **kw) d.callback('') return rtn return tputil.mergeFunctionMetadata(f, newf)
def _deco(meth): methodName = meth.__name__ def _soapMethod(self, *args, **kwargs): def _parse(body): return body[0] request = getattr(NS, methodName) request, parser = meth(self, request, *args, **kwargs) return self.call(_uj(_nsuri(NS), methodName), request).addCallback(_parse).addCallback(parser) return util.mergeFunctionMetadata(meth, _soapMethod)
def suppressWarnings(f, *suppressedWarnings): """ Wrap C{f} in a callable which suppresses the indicated warnings before invoking C{f} and unsuppresses them afterwards. If f returns a Deferred, warnings will remain suppressed until the Deferred fires. """ def warningSuppressingWrapper(*a, **kw): return runWithWarningsSuppressed(suppressedWarnings, f, *a, **kw) return tputil.mergeFunctionMetadata(f, warningSuppressingWrapper)
def reset_store(func): """Function decorator that resets the main store.""" def reset_store_decorator(*args, **kwargs): try: return func(*args, **kwargs) finally: _get_sqlobject_store().reset() return mergeFunctionMetadata(func, reset_store_decorator)
def __getattr__(self, name): maybe_method = getattr(self._transport, name) if not callable(maybe_method): return maybe_method def defer_it(*args, **kwargs): return defer.maybeDeferred(maybe_method, *args, **kwargs) return mergeFunctionMetadata(maybe_method, defer_it)
def __getattr__(self, name): maybe_method = getattr(self._transport, name) if not callable(maybe_method): return maybe_method def defer_it(*args, **kwargs): return defer.maybeDeferred(maybe_method, *args, **kwargs) return mergeFunctionMetadata(maybe_method, defer_it)
def atSpecifiedTime(when, func): def inner(*a, **kw): orig = time.time time.time = lambda: when try: return func(*a, **kw) finally: time.time = orig return util.mergeFunctionMetadata(func, inner)
def fireWhenDoneFunc(d, f): """Returns closure that when called calls f and then callbacks d. """ def newf(*args, **kw): rtn = f(*args, **kw) d.callback('') return rtn return util.mergeFunctionMetadata(f, newf)
def reset_store(func): """Function decorator that resets the main store.""" def reset_store_decorator(*args, **kwargs): try: return func(*args, **kwargs) finally: _get_sqlobject_store().reset() return mergeFunctionMetadata(func, reset_store_decorator)
def return_fault(function): """Catch any Faults raised by 'function' and return them instead.""" def decorated(*args, **kwargs): try: return function(*args, **kwargs) except Fault as fault: return fault return mergeFunctionMetadata(function, decorated)
def suppressWarnings(f, *suppressedWarnings): """ Wrap C{f} in a callable which suppresses the indicated warnings before invoking C{f} and unsuppresses them afterwards. If f returns a Deferred, warnings will remain suppressed until the Deferred fires. """ def warningSuppressingWrapper(*a, **kw): return runWithWarningsSuppressed(suppressedWarnings, f, *a, **kw) return tputil.mergeFunctionMetadata(f, warningSuppressingWrapper)
def alias(f, name=None): """ Create an alias of another command. """ newCmd = mergeFunctionMetadata(f, lambda *a, **kw: f(*a, **kw)) newCmd.alias = True if name is not None: newCmd.func_name = name newCmd.arglimits = getCommandArgLimits(f) return newCmd
def show_exception(function): """Trap exceptions and print them.""" def decorator(*args, **kwargs): """inner""" # we do want to catch all try: function(*args, **kwargs) except Exception: traceback.print_exc() return mergeFunctionMetadata(function, decorator)
def return_fault(function): """Catch any Faults raised by 'function' and return them instead.""" def decorated(*args, **kwargs): try: return function(*args, **kwargs) except Fault as fault: return fault return mergeFunctionMetadata(function, decorated)
def _fireWhenDoneFunc(self, d, f): """Returns closure that when called calls f and then callbacks d. """ from twisted.python import util as tputil def newf(*args, **kw): rtn = f(*args, **kw) d.callback('') return rtn return tputil.mergeFunctionMetadata(f, newf)
def transacted(func): """ Return a callable which will invoke C{func} in a transaction using the C{store} attribute of the first parameter passed to it. Typically this is used to create Item methods which are automatically run in a transaction. The attributes of the returned callable will resemble those of C{func} as closely as L{twisted.python.util.mergeFunctionMetadata} can make them. """ def transactionified(item, *a, **kw): return item.store.transact(func, item, *a, **kw) return mergeFunctionMetadata(func, transactionified)
def require_connection(method): """This decorator ensures functions that require a connection dont get called without one""" def decorator(self, *args): """inner""" if self.status != "connected": print "ERROR: Must be connected." return else: return method(self, *args) return mergeFunctionMetadata(method, decorator)
def test_nameIsMerged(self): """ Merging C{foo} into C{bar} returns a function with C{foo}'s name. """ def foo(): pass def bar(): pass baz = util.mergeFunctionMetadata(foo, bar) self.assertEqual(baz.__name__, foo.__name__)
def require_connection(method): """This decorator ensures functions that require a connection dont get called without one""" def decorator(self, *args): """inner""" if self.status != "connected": print("ERROR: Must be connected.") return else: return method(self, *args) return mergeFunctionMetadata(method, decorator)
def transacted(func): """ Return a callable which will invoke C{func} in a transaction using the C{store} attribute of the first parameter passed to it. Typically this is used to create Item methods which are automatically run in a transaction. The attributes of the returned callable will resemble those of C{func} as closely as L{twisted.python.util.mergeFunctionMetadata} can make them. """ def transactionified(item, *a, **kw): return item.store.transact(func, item, *a, **kw) return mergeFunctionMetadata(func, transactionified)
def json_twisted_response(f): """ Parse the JSON from an API response. We do this in a decorator so that our Twisted library can reuse the underlying functions """ def wrapper(*args, **kwargs): response = f(*args, **kwargs) response.addCallback(lambda x: json.loads(x)) return response wrapper.func = f wrapper = util.mergeFunctionMetadata(f.func, wrapper) return wrapper
def test_nameIsMerged(self): """ Merging C{foo} into C{bar} returns a function with C{foo}'s name. """ def foo(): pass def bar(): pass baz = util.mergeFunctionMetadata(foo, bar) self.assertEqual(baz.__name__, foo.__name__)
def test_moduleIsMerged(self): """ Merging C{foo} into C{bar} returns a function with C{foo}'s C{__module__}. """ def foo(): pass def bar(): pass bar.__module__ = 'somewhere.else' baz = util.mergeFunctionMetadata(foo, bar) self.assertEqual(baz.__module__, foo.__module__)
def _withCacheness(meth): """ This is a paranoid test wrapper, that calls C{meth} 2 times, clear the cache, and calls it 2 other times. It's supposed to ensure that the plugin system behaves correctly no matter what the state of the cache is. """ def wrapped(self): meth(self) meth(self) self._clearCache() meth(self) meth(self) return mergeFunctionMetadata(meth, wrapped)
def with_sftp_error(func): """Decorator used to translate Bazaar errors into SFTP errors. This assumes that the function being decorated returns a Deferred. See `TransportSFTPServer.translateError` for the details of the translation. """ def decorator(*args, **kwargs): deferred = func(*args, **kwargs) return deferred.addErrback(TransportSFTPServer.translateError, func.__name__) return util.mergeFunctionMetadata(func, decorator)
def test_moduleIsMerged(self): """ Merging C{foo} into C{bar} returns a function with C{foo}'s C{__module__}. """ def foo(): pass def bar(): pass bar.__module__ = 'somewhere.else' baz = util.mergeFunctionMetadata(foo, bar) self.assertEqual(baz.__module__, foo.__module__)
def _withCacheness(meth): """ This is a paranoid test wrapper, that calls C{meth} 2 times, clear the cache, and calls it 2 other times. It's supposed to ensure that the plugin system behaves correctly no matter what the state of the cache is. """ def wrapped(self): meth(self) meth(self) self._clearCache() meth(self) meth(self) return mergeFunctionMetadata(meth, wrapped)
def block_implicit_flushes(func): """A decorator that blocks implicit flushes on the main store.""" def block_implicit_flushes_decorator(*args, **kwargs): try: store = _get_sqlobject_store() except DisallowedStore: return func(*args, **kwargs) store.block_implicit_flushes() try: return func(*args, **kwargs) finally: store.unblock_implicit_flushes() return mergeFunctionMetadata(func, block_implicit_flushes_decorator)
def not_reentrant(function, _calls={}): """Decorates a function as not being re-entrant. The decorated function will raise an error if called from within itself. """ def decorated(*args, **kwargs): if _calls.get(function, False): raise ReentryError(function) _calls[function] = True try: return function(*args, **kwargs) finally: _calls[function] = False return mergeFunctionMetadata(function, decorated)
def not_reentrant(function, _calls={}): """Decorates a function as not being re-entrant. The decorated function will raise an error if called from within itself. """ def decorated(*args, **kwargs): if _calls.get(function, False): raise ReentryError(function) _calls[function] = True try: return function(*args, **kwargs) finally: _calls[function] = False return mergeFunctionMetadata(function, decorated)
def json_twisted_response(f): """ Parse the JSON from an API response. We do this in a decorator so that our Twisted library can reuse the underlying functions """ def wrapper(*args, **kwargs): response = f(*args, **kwargs) response.addCallback(lambda x: json.loads(x)) return response wrapper.func = f wrapper = util.mergeFunctionMetadata(f.func, wrapper) return wrapper
def read_transaction(func): """Decorator used to run the function inside a read only transaction. The transaction will be aborted on successful completion of the function. The transaction will be retried if appropriate. """ @reset_store def read_transaction_decorator(*args, **kwargs): transaction.begin() try: return func(*args, **kwargs) finally: transaction.abort() return retry_transaction(mergeFunctionMetadata( func, read_transaction_decorator))
def deferToThread(f): """Run the given callable in a separate thread and return a Deferred which fires when the function completes. """ def decorated(*args, **kwargs): d = defer.Deferred() def runInThread(): return threads._putResultInDeferred(d, f, args, kwargs) t = threading.Thread(target=runInThread) t.start() return d return mergeFunctionMetadata(f, decorated)
def test_docstringIsMerged(self): """ Merging C{foo} into C{bar} returns a function with C{foo}'s docstring. """ def foo(): """ This is foo. """ def bar(): """ This is bar. """ baz = util.mergeFunctionMetadata(foo, bar) self.assertEqual(baz.__doc__, foo.__doc__)
def deprecationDecorator(function): """ Decorator that marks C{function} as deprecated. """ warningString = getDeprecationWarningString(function, version) def deprecatedFunction(*args, **kwargs): warn(warningString, DeprecationWarning, stacklevel=2) return function(*args, **kwargs) deprecatedFunction = mergeFunctionMetadata(function, deprecatedFunction) _appendToDocstring(deprecatedFunction, _getDeprecationDocstring(version)) deprecatedFunction.deprecatedVersion = version return deprecatedFunction