diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 5aa63bba..71b8a6d1 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -85,7 +85,7 @@ jobs: cp sdk-specifications/features/subscribe/event-engine/happy-path_Legacy.feature tests/acceptance/subscribe/happy-path_Legacy.feature cp sdk-specifications/features/presence/event-engine/presence-engine_Legacy.feature tests/acceptance/subscribe/presence-engine_Legacy.feature - sudo pip3 install -r requirements-dev.txt + pip3 install --user --ignore-installed -r requirements-dev.txt behave --junit tests/acceptance/pam behave --junit tests/acceptance/encryption/cryptor-module.feature -t=~na=python -k behave --junit tests/acceptance/subscribe diff --git a/.github/workflows/run-validations.yml b/.github/workflows/run-validations.yml index a6dcf056..2f3b2b4e 100644 --- a/.github/workflows/run-validations.yml +++ b/.github/workflows/run-validations.yml @@ -16,7 +16,7 @@ jobs: python-version: "3.11" - name: Install Python dependencies and run acceptance tests run: | - sudo pip3 install -r requirements-dev.txt + pip3 install --user --ignore-installed -r requirements-dev.txt flake8 --exclude=scripts/,src/,.cache,.git,.idea,.tox,._trial_temp/,venv/ --ignore F811,E402 - name: Cancel workflow runs for commit on error if: failure() diff --git a/.pubnub.yml b/.pubnub.yml index 485c9548..3c065c7e 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,5 +1,5 @@ name: python -version: 10.0.0 +version: 10.0.1 schema: 1 scm: github.com/pubnub/python sdks: @@ -18,7 +18,7 @@ sdks: distributions: - distribution-type: library distribution-repository: package - package-name: pubnub-10.0.0 + package-name: pubnub-10.0.1 location: https://pypi.org/project/pubnub/ supported-platforms: supported-operating-systems: @@ -91,8 +91,8 @@ sdks: - distribution-type: library distribution-repository: git release - package-name: pubnub-10.0.0 - location: https://github.com/pubnub/python/releases/download/10.0.0/pubnub-10.0.0.tar.gz + package-name: pubnub-10.0.1 + location: https://github.com/pubnub/python/releases/download/10.0.1/pubnub-10.0.1.tar.gz supported-platforms: supported-operating-systems: Linux: @@ -163,6 +163,11 @@ sdks: license-url: https://github.com/encode/httpx/blob/master/LICENSE.md is-required: Required changelog: + - date: 2025-01-28 + version: 10.0.1 + changes: + - type: bug + text: "Fix issue because of which custom message type wasn't set to the parsed subscription response objects." - date: 2025-01-13 version: 10.0.0 changes: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e47ca5a..3ec8167c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 10.0.1 +January 28 2025 + +#### Fixed +- Fix issue because of which custom message type wasn't set to the parsed subscription response objects. + ## 10.0.0 January 13 2025 diff --git a/pubnub/models/consumer/pubsub.py b/pubnub/models/consumer/pubsub.py index 5564ee11..c6af8462 100644 --- a/pubnub/models/consumer/pubsub.py +++ b/pubnub/models/consumer/pubsub.py @@ -42,9 +42,10 @@ class PNFileMessageResult(PNMessageResult): def __init__( self, message, subscription, channel, timetoken, publisher, - file_url, file_id, file_name + file_url, file_id, file_name, user_metadata=None, custom_message_type=None ): - super(PNFileMessageResult, self).__init__(message, subscription, channel, timetoken, publisher=publisher) + super(PNFileMessageResult, self).__init__(message, subscription, channel, timetoken, user_metadata, publisher, + custom_message_type=custom_message_type) self.file_url = file_url self.file_id = file_id self.file_name = file_name diff --git a/pubnub/models/server/subscribe.py b/pubnub/models/server/subscribe.py index 7bf1d194..054e2c79 100644 --- a/pubnub/models/server/subscribe.py +++ b/pubnub/models/server/subscribe.py @@ -28,6 +28,7 @@ def __init__(self): self.subscribe_key = None self.origination_timetoken = None self.publish_metadata = None + self.user_metadata = None self.only_channel_subscription = False self.type = 0 self.custom_message_type = None @@ -48,6 +49,8 @@ def from_json(cls, json_input): if 'o' in json_input: message.origination_timetoken = json_input['o'] message.publish_metadata = PublishMetadata.from_json(json_input['p']) + if 'u' in json_input: + message.user_metadata = json_input['u'] if 'e' in json_input: message.type = json_input['e'] if 'cmt' in json_input: diff --git a/pubnub/pubnub.py b/pubnub/pubnub.py index 5ad22224..ca66d1a5 100644 --- a/pubnub/pubnub.py +++ b/pubnub/pubnub.py @@ -450,8 +450,9 @@ def presence(self, pubnub, presence): def wait_for_connect(self): if not self.connected_event.is_set(): self.connected_event.wait() - else: - raise Exception("the instance is already connected") + # failing because you don't have to wait is illogical + # else: + # raise Exception("the instance is already connected") def channel(self, pubnub, channel): self.channel_queue.put(channel) @@ -465,8 +466,9 @@ def membership(self, pubnub, membership): def wait_for_disconnect(self): if not self.disconnected_event.is_set(): self.disconnected_event.wait() - else: - raise Exception("the instance is already disconnected") + # failing because you don't have to wait is illogical + # else: + # raise Exception("the instance is already disconnected") def wait_for_message_on(self, *channel_names): channel_names = list(channel_names) diff --git a/pubnub/pubnub_asyncio.py b/pubnub/pubnub_asyncio.py index 2e4ab0d0..f0a7f6a6 100644 --- a/pubnub/pubnub_asyncio.py +++ b/pubnub/pubnub_asyncio.py @@ -593,14 +593,16 @@ async def _wait_for(self, coro): async def wait_for_connect(self): if not self.connected_event.is_set(): await self._wait_for(self.connected_event.wait()) - else: - raise Exception("instance is already connected") + # failing because you don't have to wait is illogical + # else: + # raise Exception("instance is already connected") async def wait_for_disconnect(self): if not self.disconnected_event.is_set(): await self._wait_for(self.disconnected_event.wait()) - else: - raise Exception("instance is already disconnected") + # failing because you don't have to wait is illogical + # else: + # raise Exception("instance is already disconnected") async def wait_for_message_on(self, *channel_names): channel_names = list(channel_names) diff --git a/pubnub/pubnub_core.py b/pubnub/pubnub_core.py index 36f064d3..6383532a 100644 --- a/pubnub/pubnub_core.py +++ b/pubnub/pubnub_core.py @@ -94,7 +94,7 @@ class PubNubCore: """A base class for PubNub Python API implementations""" - SDK_VERSION = "10.0.0" + SDK_VERSION = "10.0.1" SDK_NAME = "PubNub-Python" TIMESTAMP_DIVIDER = 1000 diff --git a/pubnub/workers.py b/pubnub/workers.py index 5024771e..cf72c948 100644 --- a/pubnub/workers.py +++ b/pubnub/workers.py @@ -162,6 +162,8 @@ def _process_incoming_payload(self, message: SubscribeMessage): subscription=subscription_match, timetoken=publish_meta_data.publish_timetoken, publisher=message.issuing_client_id, + custom_message_type=message.custom_message_type, + user_metadata=message.user_metadata, file_url=download_url, file_id=extracted_message["file"]["id"], file_name=extracted_message["file"]["name"] @@ -182,7 +184,10 @@ def _process_incoming_payload(self, message: SubscribeMessage): channel=channel, subscription=subscription_match, timetoken=publish_meta_data.publish_timetoken, - publisher=publisher + publisher=publisher, + custom_message_type=message.custom_message_type, + user_metadata=message.user_metadata, + error=error ) self.announce(pn_signal_result) return pn_signal_result @@ -202,6 +207,8 @@ def _process_incoming_payload(self, message: SubscribeMessage): subscription=subscription_match, timetoken=publish_meta_data.publish_timetoken, publisher=publisher, + custom_message_type=message.custom_message_type, + user_metadata=message.user_metadata, error=error ) self.announce(pn_message_result) diff --git a/setup.py b/setup.py index fa5a9ee6..2d95e496 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='pubnub', - version='10.0.0', + version='10.0.1', description='PubNub Real-time push service in the cloud', author='PubNub', author_email='support@pubnub.com', diff --git a/tests/integrational/fixtures/native_threads/subscribe/subscribe_cg_publish_unsubscribe.json b/tests/integrational/fixtures/native_threads/subscribe/subscribe_cg_publish_unsubscribe.json index 9b523397..ba47509e 100644 --- a/tests/integrational/fixtures/native_threads/subscribe/subscribe_cg_publish_unsubscribe.json +++ b/tests/integrational/fixtures/native_threads/subscribe/subscribe_cg_publish_unsubscribe.json @@ -5,19 +5,22 @@ "request": { "method": "GET", "uri": "https://ps.pndsn.com/v1/channel-registration/sub-key/{PN_KEY_SUBSCRIBE}/channel-group/test-subscribe-unsubscribe-group?add=test-subscribe-unsubscribe-channel&uuid=uuid-mock", - "body": null, + "body": "", "headers": { - "User-Agent": [ - "PubNub-Python/7.4.2" + "host": [ + "ps.pndsn.com" ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Accept": [ + "accept": [ "*/*" ], - "Connection": [ + "accept-encoding": [ + "gzip, deflate" + ], + "connection": [ "keep-alive" + ], + "user-agent": [ + "PubNub-Python/10.0.0" ] } }, @@ -27,14 +30,8 @@ "message": "OK" }, "headers": { - "Access-Control-Allow-Methods": [ - "GET, POST, DELETE, OPTIONS" - ], - "Server": [ - "Pubnub Storage" - ], - "Accept-Ranges": [ - "bytes" + "Date": [ + "Mon, 27 Jan 2025 15:49:56 GMT" ], "Content-Type": [ "application/json" @@ -42,24 +39,30 @@ "Content-Length": [ "72" ], - "Access-Control-Allow-Origin": [ - "*" + "Connection": [ + "keep-alive" ], - "Date": [ - "Mon, 25 Mar 2024 18:21:17 GMT" + "Access-Control-Allow-Methods": [ + "GET, POST, DELETE, OPTIONS" + ], + "Age": [ + "0" ], "Cache-Control": [ "no-cache" ], - "Age": [ - "0" + "Accept-Ranges": [ + "bytes" ], - "Connection": [ - "keep-alive" + "Access-Control-Allow-Credentials": [ + "true" + ], + "Access-Control-Expose-Headers": [ + "*" ] }, "body": { - "string": "{\"error\":false,\"message\":\"OK\",\"service\":\"channel-registry\",\"status\":200}" + "pickle": "gASVWAAAAAAAAAB9lIwGc3RyaW5nlIxIeyJlcnJvciI6ZmFsc2UsIm1lc3NhZ2UiOiJPSyIsInNlcnZpY2UiOiJjaGFubmVsLXJlZ2lzdHJ5Iiwic3RhdHVzIjoyMDB9lHMu" } } }, @@ -67,19 +70,22 @@ "request": { "method": "GET", "uri": "https://ps.pndsn.com/v2/subscribe/{PN_KEY_SUBSCRIBE}/,/0?channel-group=test-subscribe-unsubscribe-group&uuid=uuid-mock", - "body": null, + "body": "", "headers": { - "User-Agent": [ - "PubNub-Python/7.4.2" - ], - "Accept-Encoding": [ - "gzip, deflate" + "host": [ + "ps.pndsn.com" ], - "Accept": [ + "accept": [ "*/*" ], - "Connection": [ + "accept-encoding": [ + "gzip, deflate" + ], + "connection": [ "keep-alive" + ], + "user-agent": [ + "PubNub-Python/10.0.0" ] } }, @@ -89,8 +95,8 @@ "message": "OK" }, "headers": { - "Access-Control-Allow-Methods": [ - "GET" + "Date": [ + "Mon, 27 Jan 2025 15:49:56 GMT" ], "Content-Type": [ "text/javascript; charset=\"UTF-8\"" @@ -98,21 +104,24 @@ "Content-Length": [ "45" ], - "Access-Control-Allow-Origin": [ - "*" - ], - "Date": [ - "Mon, 25 Mar 2024 18:21:17 GMT" + "Connection": [ + "keep-alive" ], "Cache-Control": [ "no-cache" ], - "Connection": [ - "keep-alive" + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Credentials": [ + "true" + ], + "Access-Control-Expose-Headers": [ + "*" ] }, "body": { - "string": "{\"t\":{\"t\":\"17113908770846110\",\"r\":41},\"m\":[]}" + "pickle": "gASVPQAAAAAAAAB9lIwGc3RyaW5nlIwteyJ0Ijp7InQiOiIxNzM3OTkyOTk2NDEyODI1NCIsInIiOjQyfSwibSI6W119lHMu" } } }, @@ -120,19 +129,22 @@ "request": { "method": "GET", "uri": "https://ps.pndsn.com/publish/{PN_KEY_PUBLISH}/{PN_KEY_SUBSCRIBE}/0/test-subscribe-unsubscribe-channel/0/%22hey%22?uuid=uuid-mock", - "body": null, + "body": "", "headers": { - "User-Agent": [ - "PubNub-Python/7.4.2" + "host": [ + "ps.pndsn.com" ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Accept": [ + "accept": [ "*/*" ], - "Connection": [ + "accept-encoding": [ + "gzip, deflate" + ], + "connection": [ "keep-alive" + ], + "user-agent": [ + "PubNub-Python/10.0.0" ] } }, @@ -142,8 +154,8 @@ "message": "OK" }, "headers": { - "Access-Control-Allow-Methods": [ - "GET" + "Date": [ + "Mon, 27 Jan 2025 15:49:56 GMT" ], "Content-Type": [ "text/javascript; charset=\"UTF-8\"" @@ -151,21 +163,24 @@ "Content-Length": [ "30" ], - "Access-Control-Allow-Origin": [ - "*" - ], - "Date": [ - "Mon, 25 Mar 2024 18:21:17 GMT" + "Connection": [ + "keep-alive" ], "Cache-Control": [ "no-cache" ], - "Connection": [ - "keep-alive" + "Access-Control-Allow-Methods": [ + "GET" + ], + "Access-Control-Allow-Credentials": [ + "true" + ], + "Access-Control-Expose-Headers": [ + "*" ] }, "body": { - "string": "[1,\"Sent\",\"17113908772201101\"]" + "pickle": "gASVLgAAAAAAAAB9lIwGc3RyaW5nlIweWzEsIlNlbnQiLCIxNzM3OTkyOTk2NTQ1NzYxMCJdlHMu" } } }, @@ -173,19 +188,22 @@ "request": { "method": "GET", "uri": "https://ps.pndsn.com/v2/subscribe/{PN_KEY_SUBSCRIBE}/,/0?channel-group=test-subscribe-unsubscribe-group&uuid=uuid-mock", - "body": null, + "body": "", "headers": { - "User-Agent": [ - "PubNub-Python/7.4.2" - ], - "Accept-Encoding": [ - "gzip, deflate" + "host": [ + "ps.pndsn.com" ], - "Accept": [ + "accept": [ "*/*" ], - "Connection": [ + "accept-encoding": [ + "gzip, deflate" + ], + "connection": [ "keep-alive" + ], + "user-agent": [ + "PubNub-Python/10.0.0" ] } }, @@ -195,33 +213,36 @@ "message": "OK" }, "headers": { - "Content-Encoding": [ - "gzip" - ], - "Access-Control-Allow-Methods": [ - "GET" + "Date": [ + "Mon, 27 Jan 2025 15:49:56 GMT" ], "Content-Type": [ "text/javascript; charset=\"UTF-8\"" ], - "Access-Control-Allow-Origin": [ - "*" + "Transfer-Encoding": [ + "chunked" ], - "Date": [ - "Mon, 25 Mar 2024 18:21:17 GMT" + "Connection": [ + "keep-alive" ], "Cache-Control": [ "no-cache" ], - "Connection": [ - "keep-alive" + "Access-Control-Allow-Methods": [ + "GET" ], - "Transfer-Encoding": [ - "chunked" + "Access-Control-Allow-Credentials": [ + "true" + ], + "Access-Control-Expose-Headers": [ + "*" + ], + "Content-Encoding": [ + "gzip" ] }, "body": { - "binary": "H4sIAAAAAAAAA4yMSw7CMAxE7+J1LMUlqGmugljE+dCq9KOmWaCqd8es2CE21nj03hywgzs+B6glunTatm3TaCJNoGADZ+hUMIG7HeCFukqbwWkFg3y1DhGnJYzSFnCkYP1nbhS1VMaA1nIX2TM2OhMaGw3amC/YcfbMWgfNRraDCHsqO4pVwjZwwjp/c+j9PKengFHAPr0k8W/lsS11hfN+vgEAAP//AwBkxY98AgEAAA==" + "pickle": "gASVwwAAAAAAAAB9lIwGc3RyaW5nlEOzH4sIAAAAAAAAA4yMSw7CMAxE7+J1LCUhaXGugljkV1qVftQ0C1T17pgVO8TGGo/emwN2cMfngGovLZEmaqyxbaMkCNjAGX0KmMDdDvBMXbjtwEkBA3+1DgmnJY7cFnBKwPrP3MhqqQEjJhmu2RqNSnuJRkWDRNSh18k2lKLR1vJ2ZGHPZUe2StyGkLHO3xx7P8/5yWBisM8vTuG38tiWusJ5P98AAAD//wMAiD18SwIBAACUcy4=" } } }, @@ -229,19 +250,22 @@ "request": { "method": "GET", "uri": "https://ps.pndsn.com/v2/presence/sub-key/{PN_KEY_SUBSCRIBE}/channel/,/leave?channel-group=test-subscribe-unsubscribe-group&uuid=uuid-mock", - "body": null, + "body": "", "headers": { - "User-Agent": [ - "PubNub-Python/7.4.2" + "host": [ + "ps.pndsn.com" ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Accept": [ + "accept": [ "*/*" ], - "Connection": [ + "accept-encoding": [ + "gzip, deflate" + ], + "connection": [ "keep-alive" + ], + "user-agent": [ + "PubNub-Python/10.0.0" ] } }, @@ -251,14 +275,8 @@ "message": "OK" }, "headers": { - "Access-Control-Allow-Methods": [ - "OPTIONS, GET, POST" - ], - "Server": [ - "Pubnub Presence" - ], - "Accept-Ranges": [ - "bytes" + "Date": [ + "Mon, 27 Jan 2025 15:49:56 GMT" ], "Content-Type": [ "text/javascript; charset=\"UTF-8\"" @@ -266,24 +284,30 @@ "Content-Length": [ "74" ], - "Access-Control-Allow-Origin": [ - "*" + "Connection": [ + "keep-alive" ], - "Date": [ - "Mon, 25 Mar 2024 18:21:17 GMT" + "Access-Control-Allow-Methods": [ + "OPTIONS, GET, POST" + ], + "Age": [ + "0" ], "Cache-Control": [ "no-cache" ], - "Age": [ - "0" + "Accept-Ranges": [ + "bytes" ], - "Connection": [ - "keep-alive" + "Access-Control-Allow-Credentials": [ + "true" + ], + "Access-Control-Expose-Headers": [ + "*" ] }, "body": { - "string": "{\"status\": 200, \"message\": \"OK\", \"action\": \"leave\", \"service\": \"Presence\"}" + "pickle": "gASVWgAAAAAAAAB9lIwGc3RyaW5nlIxKeyJzdGF0dXMiOiAyMDAsICJtZXNzYWdlIjogIk9LIiwgImFjdGlvbiI6ICJsZWF2ZSIsICJzZXJ2aWNlIjogIlByZXNlbmNlIn2Ucy4=" } } }, @@ -291,19 +315,22 @@ "request": { "method": "GET", "uri": "https://ps.pndsn.com/v1/channel-registration/sub-key/{PN_KEY_SUBSCRIBE}/channel-group/test-subscribe-unsubscribe-group?remove=test-subscribe-unsubscribe-channel&uuid=uuid-mock", - "body": null, + "body": "", "headers": { - "User-Agent": [ - "PubNub-Python/7.4.2" + "host": [ + "ps.pndsn.com" ], - "Accept-Encoding": [ - "gzip, deflate" - ], - "Accept": [ + "accept": [ "*/*" ], - "Connection": [ + "accept-encoding": [ + "gzip, deflate" + ], + "connection": [ "keep-alive" + ], + "user-agent": [ + "PubNub-Python/10.0.0" ] } }, @@ -313,14 +340,8 @@ "message": "OK" }, "headers": { - "Access-Control-Allow-Methods": [ - "GET, POST, DELETE, OPTIONS" - ], - "Server": [ - "Pubnub Storage" - ], - "Accept-Ranges": [ - "bytes" + "Date": [ + "Mon, 27 Jan 2025 15:49:56 GMT" ], "Content-Type": [ "application/json" @@ -328,24 +349,30 @@ "Content-Length": [ "72" ], - "Access-Control-Allow-Origin": [ - "*" + "Connection": [ + "keep-alive" ], - "Date": [ - "Mon, 25 Mar 2024 18:21:17 GMT" + "Access-Control-Allow-Methods": [ + "GET, POST, DELETE, OPTIONS" + ], + "Age": [ + "0" ], "Cache-Control": [ "no-cache" ], - "Age": [ - "0" + "Accept-Ranges": [ + "bytes" ], - "Connection": [ - "keep-alive" + "Access-Control-Allow-Credentials": [ + "true" + ], + "Access-Control-Expose-Headers": [ + "*" ] }, "body": { - "string": "{\"error\":false,\"message\":\"OK\",\"service\":\"channel-registry\",\"status\":200}" + "pickle": "gASVWAAAAAAAAAB9lIwGc3RyaW5nlIxIeyJlcnJvciI6ZmFsc2UsIm1lc3NhZ2UiOiJPSyIsInNlcnZpY2UiOiJjaGFubmVsLXJlZ2lzdHJ5Iiwic3RhdHVzIjoyMDB9lHMu" } } } diff --git a/tests/integrational/native_threads/test_subscribe.py b/tests/integrational/native_threads/test_subscribe.py index 29b6aa6b..e016475c 100644 --- a/tests/integrational/native_threads/test_subscribe.py +++ b/tests/integrational/native_threads/test_subscribe.py @@ -290,6 +290,8 @@ def test_subscribe_pub_unencrypted_unsubscribe(self): subscribe_listener = SubscribeListener() publish_operation = NonSubscribeListener() + metadata = {'test': 'publish'} + custom_message_type = "test" message = "hey" try: @@ -298,7 +300,12 @@ def test_subscribe_pub_unencrypted_unsubscribe(self): pubnub.subscribe().channels(ch).execute() subscribe_listener.wait_for_connect() - pubnub_plain.publish().channel(ch).message(message).pn_async(publish_operation.callback) + pubnub_plain.publish() \ + .channel(ch) \ + .message(message) \ + .custom_message_type(custom_message_type) \ + .meta(metadata) \ + .pn_async(publish_operation.callback) if publish_operation.pn_await() is False: self.fail("Publish operation timeout") @@ -313,6 +320,8 @@ def test_subscribe_pub_unencrypted_unsubscribe(self): assert result.subscription is None assert result.timetoken > 0 assert result.message == message + assert result.custom_message_type == custom_message_type + assert result.user_metadata == metadata assert result.error is not None assert isinstance(result.error, binascii.Error) diff --git a/tests/integrational/native_threads/test_where_now.py b/tests/integrational/native_threads/test_where_now.py index b4bbb72a..218bf7d6 100644 --- a/tests/integrational/native_threads/test_where_now.py +++ b/tests/integrational/native_threads/test_where_now.py @@ -34,7 +34,7 @@ def test_single_channel(self): subscribe_listener.wait_for_connect() # the delay is needed for the server side to propagate presence - time.sleep(1) + time.sleep(3) pubnub.where_now() \ .uuid(uuid) \ .pn_async(where_now_listener.callback) @@ -69,7 +69,7 @@ def test_multiple_channels(self): subscribe_listener.wait_for_connect() # the delay is needed for the server side to propagate presence - time.sleep(1) + time.sleep(3) pubnub.where_now() \ .uuid(uuid) \ .pn_async(where_now_listener.callback)