Skip to content

Commit

Permalink
backwards compatibility added for Python < 3.7 to still provide async…
Browse files Browse the repository at this point in the history
… function and kw arg
  • Loading branch information
irmen committed Nov 24, 2017
1 parent 7f3f894 commit 316b862
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 2 deletions.
8 changes: 7 additions & 1 deletion docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ Change Log

**Pyro 4.64**

- **incompatible API change** for python 3.7 compatibility: renamed ``core.async`` to ``core.asyncproxy``
- **incompatible API change** for python 3.7 compatibility: renaming of ``async`` function and keyword arguments in the API:
Renamed ``Pyro4.core.async`` to ``Pyro4.core.asyncproxy`` (and its occurrence in ``Pyro4``)
and the ``async`` keyword argument in some methods to ``asynchronous``.
This had to be done because ``async`` (and ``await``) are now parsed as keywords in Python 3.7 and using them otherwise will result
in a SyntaxError when loading the module.
It is suggested you stop using the ``asyncproxy`` function and instead create asynchronous proxies using the ``_pyroAsync``
method on the regular proxy.
- For *existing code* running on Python *older than 3.7*, a backwards compatibility feature is present to still provide the
``async`` function and keyword arguments as they were supported on previous Pyro versions.
Still, it's advised to migrate away from them and start using the new names.
- dropped support for Python 3.3 (which has reached end-of-life status). Supported Python versions are now 2.7, and 3.4 or newer.


Expand Down
43 changes: 42 additions & 1 deletion src/Pyro4/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,18 @@ def _pyroAsync(self, asynchronous=True):
or sets it back to normal sync mode if you set asynchronous=False."""
self.__async = asynchronous

if sys.version_info < (3, 7):
# async keyword backwards compatibility
_pyroAsync_37 = _pyroAsync

def _pyroAsync(self, asynchronous=True, **kwargs):
if kwargs:
kword = list(kwargs.keys())
if kword != ["async"]:
raise TypeError("_pyroAsync() got an unexpected keyword argument '{:s}'".format(kword[0]))
asynchronous = kwargs["async"]
return Proxy._pyroAsync_37(self, asynchronous)

def _pyroInvokeBatch(self, calls, oneway=False):
flags = message.FLAGS_BATCH
if oneway:
Expand Down Expand Up @@ -835,6 +847,20 @@ def __call__(self, oneway=False, asynchronous=False):
if not oneway:
return self.__resultsgenerator(results)

if sys.version_info < (3, 7):
# async keyword backwards compatibility
call_37 = __call__

def __call__(self, oneway=False, **kwargs):
if kwargs:
kword = list(kwargs.keys())
if kword != ["async"] and kword != ["asynchronous"]:
raise TypeError("__call__() got an unexpected keyword argument '{:s}'".format(kword[0]))
if kword == ["async"]:
kwargs = {"asynchronous": kwargs["async"]}
kwargs["oneway"] = oneway
return _BatchProxyAdapter.call_37(self, **kwargs)

def _pyroInvoke(self, name, args, kwargs):
# ignore all parameters, we just need to execute the batch
results = self.__proxy._pyroInvokeBatch(self.__calls)
Expand Down Expand Up @@ -951,7 +977,7 @@ def expose(method_or_class):
raise AttributeError("using @expose on a classmethod/staticmethod must be done "
"after @classmethod/@taticmethod. Method: " + attrname)
else:
raise AttributeError("@expose cannot determine what this is: "+repr(method_or_class))
raise AttributeError("@expose cannot determine what this is: " + repr(method_or_class))
if util.is_private_attribute(attrname):
raise AttributeError("exposing private names (starting with _) is not allowed")
if inspect.isclass(method_or_class):
Expand Down Expand Up @@ -1986,3 +2012,18 @@ def deserialized(self):

current_context = _CallContext()
"""the context object for the current call. (thread-local)"""


# 'async' keyword backwards compatibility
if sys.version_info < (3, 7):
def asyncproxy(proxy, asynchronous=True, **kwargs):
"""convenience method to set proxy to asynchronous or sync mode."""
if kwargs:
kword = list(kwargs.keys())
if kword != ["async"]:
raise TypeError("asyncproxy() got an unexpected keyword argument '{:s}'".format(kword[0]))
asynchronous = kwargs["async"]
proxy._pyroAsync(asynchronous)
current_module = sys.modules[__name__]
pyro4_module = __import__("Pyro4")
current_module.__dict__["async"] = pyro4_module.__dict__["async"] = asyncproxy
23 changes: 23 additions & 0 deletions tests/PyroTests/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,29 @@ def testPyro4(self):
self.assertIs(Pyro4.core._resolve, Pyro4.naming.resolve, "old API function location must still be valid")
self.assertIsInstance(Pyro4.current_context, Pyro4.core._CallContext)

@unittest.skipIf(sys.version_info >= (3, 7), "async is kw on 3.7+")
def testAsyncKeywordBackwardsCompatibility(self):
# 'async' function
async_function = getattr(Pyro4, "async")
self.assertIs(async_function, Pyro4.asyncproxy)
async_function = getattr(Pyro4.core, "async")
self.assertIs(async_function, Pyro4.core.asyncproxy)
# 'async' keyword on batch proxy's __call__
proxy = Pyro4.Proxy("PYRO:dummy@localhost:9999")
batch = Pyro4.batch(proxy)
result = batch(**{"async": True})
result.set_cancelled()
result = batch(asynchronous=True)
result.set_cancelled()
# 'async' keyword on 'proxy._pyroAsync' method
proxy._pyroAsync(**{"async": True})
proxy._pyroAsync(asynchronous=True)
proxy._pyroAsync(True)
# 'async' keyword on 'async' function
Pyro4.asyncproxy(proxy, **{"async": True})
Pyro4.asyncproxy(proxy, asynchronous=True)
Pyro4.asyncproxy(proxy, True)


if __name__ == "__main__":
unittest.main()

0 comments on commit 316b862

Please sign in to comment.