{ "info": { "author": "KubeMQ", "author_email": "info@kubemq.io", "bugtrack_url": null, "classifiers": [], "description": "# KubeMQ SDK for Python\n\nThe **KubeMQ SDK for Python** enables Python developers to easily work with [KubeMQ](https://kubemq.io/). \n\n## Getting Started\n\n### Prerequisites\n\nKubeMQ-SDK-Python works with **Python 2.7** or newer.\n\n### Installing\n\nThe recommended way to use the SDK for Python in your project is to consume it from pip.\n\n```\npip install kubemq\n```\n\n## Generating Documentation\n\nSphinx is used for documentation. Use the Makefile to build the docs, like so:\n\n```\n$ pip install -r requirements-docs.txt\n$ cd docs\n$ make html\n```\n(`make latex` or `make linkcheck` supported)\n\n## Building from source\n\nOnce you check out the code from GitHub, you can install the package locally with:\n\n```\n$ pip install .\n```\n\nYou can also install the package with a symlink, \nso that changes to the source files will be immediately available:\n\n```\n$ pip install -e .\n```\n\nInstallation:\n$ pip install kubemq\n\nCore Basics\nKubeMQ messaging broker has five messaging patterns:\n\nQueues - FIFO based, exactly one durable queue pattern\nEvents - real-time pub/sub pattern\nEvents Store - pub/sub with persistence pattern\nCommands - the Command part of CQRS pattern, which sends commands with the response for executed or not (with proper error messaging)\nQueries - the Query part of CQRS pattern, which sends a query and gets a response with the relevant query result back\nFor each one of the patterns, we can distinguish between the senders and the receivers.\n\nFor events and events store, the KubeMQ supports both RPC and upstream calls.\n\nthe data model is almost identical between all the pattern with some data added related to the specific patter.\n\nThe common part of all the patterns are:\n\nId - the sender can set the Id for each type of message, or the Id is automatically generated a UUID Id for him.\nMetadata - a string field that can hold any metadata related to the message\nBody - a Bytes array which contains the actual payload to be sent from the sender to the receiver\nTags - a Map of string, string for user define data\nThe KubeMQ core transport is based on gRPC, and the library is a wrapper around the client-side of gRPC complied protobuf hence leveraging the gRPC benefits and advantages.\n\nBefore any transactions to be performed with KubeMQ server, the Client should connect and dial KubeMQ server and obtain Client connection.\n\nWith the Client connection object, the user can perform all transactions to and from KubeMQ server.\n\nA Client connection object is thread-safe and can be shared between all process needed to communicate with KubeMQ.\n\nIMPORTANT - it's the user responsibility to close the Client connection when no further communication with KubeMQ is needed.\n\nConnection\nConnecting to KubeMQ server can be by creating the type needed:\n```\n pub/sub:\n sender:\n sender = Sender(\"localhost:50000\")\n Subscriber:\n subscriber = Subscriber(\"localhost:50000)\n\n command/query:\n Initiator:\n initiator = Initiator(\"localhost:50000\")\n Responder:\n responder = Responder(\"localhost:50000\")\n\n then to check connection call ping as such:\n #\n def create_check_connection():\n sender= Sender(\"localhost:50000\")\n try:\n result=sender.ping()\n except Exception as identifier:\n print('error {}'.format(identifier))\n exit()\n print(result)\n\n```\nExamples\nPlease visit our extensive examples folder Please find usage examples on the examples folders.\n\nQueues\nCore features\nKubeMQ supports distributed durable FIFO based queues with the following core features:\n\nExactly One Delivery - Only one message guarantee will deliver to the subscriber\n\nSingle and Batch Messages Send and Receive - Single and multiple messages in one call\n\nRPC and Stream Flows - RPC flow allows an insert and pulls messages in one call. Stream flow allows single message consuming in a transactional way\n\nMessage Policy - Each message can be configured with expiration and delay timers. Also, each message can specify a dead-letter queue for un-processed messages attempts\n\nLong Polling - Consumers can wait until a message available in the queue to consume\n\nPeak Messages - Consumers can peak into a queue without removing them from the queue\n\nAck All Queue Messages - Any client can mark all the messages in a queue as discarded and will not be available anymore to consume\n\nVisibility timers - Consumers can pull a message from the queue and set a timer which will cause the message not be visible to other consumers. This timer can be extended as needed.\n\nResend Messages - Consumers can send back a message they pulled to a new queue or send a modified message to the same queue for further processing.\n\n\nSend Message to a Queue:\n```\n queue = MessageQueue(queue_name, client_id, kube_add)\n message = create_queue_message(\"someMeta\", \"some-simple_queue-queue-message\".encode('UTF-8'))\n queue_send_response = queue.send_queue_message(message)\n print(\"finished sending to queue answer. message_id: %s, body: %s\" % (queue_send_response.message_id, message.body))\n```\ncreate_queue_message:\n```\n def create_queue_message(meta_data, body, policy=None):\n message = Message()\n message.metadata = meta_data\n message.body = body\n message.tags = [\n ('key', 'value'),\n ('key2', 'value2')\n ]\n message.attributes = None\n message.policy = policy\n return message\n```\nSend Message to a Queue with Expiration:\n```\n queue = MessageQueue(queue_name, client_id, kube_add, max_number_messages, max_timeout)\n policy = QueueMessagePolicy()\n policy.ExpirationSeconds = 5\n message = create_queue_message(\"someMeta\", \"some-simple_queue-queue-message\".encode('UTF-8'), policy)\n queue_send_message_to_queue_with_expiration_response = queue.send_queue_message(message)\n print(\"finished sending message to queue with expiration answer: {} \".format(\n queue_send_message_to_queue_with_expiration_response))\n```\n\nSend Message to a Queue with Delay:\n```\n queue = MessageQueue(queue_name, client_id, kube_add, max_number_messages, max_timeout)\n policy = QueueMessagePolicy()\n policy.DelaySeconds = 5\n message = create_queue_message(\"someMeta\", \"some-simple_queue-queue-message\".encode('UTF-8'), policy)\n queue_send_message_to_queue_with_delay_response = queue.send_queue_message(message)\n print(\"finished sending message to queue with delay answer: {} \".format(\n queue_send_message_to_queue_with_delay_response))\n```\n\nSend Message to a Queue with Dead-letter Queue:\n```\n queue = MessageQueue(queue_name, client_id, kube_add, max_number_messages, max_timeout)\n policy = QueueMessagePolicy()\n policy.MaxReceiveCount = 3\n policy.MaxReceiveQueue = \"DeadLetterQueue\"\n message = create_queue_message(\"someMeta\", \"some-simple_queue-queue-message\".encode('UTF-8'), policy)\n queue_send_message_to_queue_with_deadletter_response = queue.send_queue_message(message)\n print(\"finished sending message to queue with deadletter answer: {} \".format(\n queue_send_message_to_queue_with_deadletter_response))\n```\n\nSend Batch Messages:\n```\n queue = MessageQueue(queue_name, client_id, kube_add, max_number_messages, max_timeout)\n mm = []\n for i in range(2):\n message = create_queue_message(\"queueName {}\".format(i), \"some-simple_queue-queue-message\".encode('UTF-8'))\n mm.append(message)\n queue_send_batch_response = queue.send_queue_messages_batch(mm)\n print(\"finished sending message to queue with batch answer: {} \".format(queue_send_batch_response))\n```\n\nReceive Messages from a Queue:\n```\n queue = MessageQueue(queue_name, client_id, kube_add, max_number_messages, max_timeout)\n queue_receive_response = queue.receive_queue_messages()\n print(\"finished sending message to receive_queue answer: {} \".format(queue_receive_response))\n```\n\nPeak Messages from a Queue:\n```\n queue = MessageQueue(queue_name, client_id, kube_add, max_number_messages, max_timeout)\n queue_receive_response = queue.peek_queue_message(5)\n print(\"finished sending message to peek answer: {} \".format(queue_receive_response))\n```\n\nAck All Messages In a Queue:\n```\n queue = MessageQueue(queue_name, client_id, kube_add, max_number_messages, max_timeout)\n queue_ack_response = queue.ack_all_queue_messages()\n print(\"finished sending message to ack answer: {} \".format(queue_ack_response))\n```\nTransactional Queue - Ack:\n```\n queue = MessageQueue(queue_name, client_id, kube_add)\n transaction = queue.create_transaction()\n res_rec = transaction.receive(10, 10)\n\n if res_rec.is_error:\n raise \"Message dequeue error, error: %s\" % res_rec.is_error\n\n print(\"Received message id: %s, body: %s\" % (res_rec.message.MessageID, res_rec.message.Body))\n print(\"tags: %s\" % res_rec.message.Tags)\n\n res_ack = transaction.ack_message(res_rec.message.Attributes.Sequence)\n if res_ack.is_error:\n raise Exception(\"Ack message error: %s\" % res_ack.error)\n\n print(\"Received message of type: %s\" % StreamRequestType(res_ack.stream_request_type).name)\n```\nTransactional Queue - Reject:\n```\n queue = MessageQueue(queue_name, client_id, kube_add)\n transaction = queue.create_transaction()\n res_rec = transaction.receive(10, 10)\n\n if res_rec.is_error:\n raise \"Message dequeue error, error: %s\" % res_rec.is_error\n\n print(\"Received message id: {}, body: {} tags:{}\".format(res_rec.message.MessageID, res_rec.message.Body,res_rec.message.Tags))\n\n res_rej = transaction.rejected_message(res_rec.message.Attributes.Sequence)\n if res_rej.is_error:\n raise Exception(\"Ack message error: %s\" % res_rej.error)\n\n print(\"rejected message message of type: %s\" % StreamRequestType(res_rej.stream_request_type).name)\n```\n\nTransactional Queue - Extend Visibility:\n```\n queue_rej = MessageQueue(\"reject_test\", client_id, kube_add)\n\n message = create_queue_message(\"queueName {}\".format(0), \"my reject\".encode('UTF-8'))\n queue_rej.send_queue_message(message)\n\n queue= MessageQueue(\"reject_test\", client_id, kube_add)\n tran=queue.create_transaction()\n\n res_rec=tran.receive(5,10)\n\n if res_rec.is_error:\n raise \"Message dequeue error, error: %s\" % res_rec.is_error\n\n print(\"Received message id: {}, body: {} tags: {}\".format(res_rec.message.MessageID, res_rec.message.Body,res_rec.message.Tags))\n\n print(\"work for 1 second\")\n\n time.sleep(1)\n\n print(\"Need more time to process, extend visibility for more 3 seconds\")\n\n res_ext=tran.extend_visibility(3)\n\n if res_ext.is_error:\n raise Exception(\"Ack message error: %s\" % res_ext.error)\n\n print(\"Approved. work for 2.5 seconds\")\n\n time.sleep(2.5)\n\n print(\"Work done... ack the message\")\n\n\n res_ack=tran.ack_message(res_rec.message.Attributes.Sequence)\n\n if res_ack.is_error:\n raise Exception(\"Ack message error: %s\" % res_ack.error)\n\n print(\"ack done\")\n```\n\nTransactional Queue - Resend to New Queue:\n```\n queue_rej = MessageQueue(\"resend_to_new_queue\", client_id, kube_add)\n\n message = create_queue_message(\"resend to new queue {}\".format(0), \"my resend\".encode('UTF-8'))\n queue_rej.send_queue_message(message)\n\n queue= MessageQueue(\"resend_to_new_queue\", client_id, kube_add)\n tran=queue.create_transaction()\n\n res_rec=tran.receive(5,10)\n\n if res_rec.is_error:\n raise \"Message dequeue error, error: %s\" % res_rec.is_error\n\n print(\"Received message id: {}, body: {} tags:{}\".format(res_rec.message.MessageID, res_rec.message.Body, res_rec.message.Tags))\n\n print(\"resend to new queue\")\n\n res_resend=tran.resend(\"new-queue\")\n\n if res_resend.is_error:\n raise \"Message resend error, error: %s\" % res_resend.is_error\n\n print(\"Done\")\n```\nTransactional Queue - Resend Modified Message:\n```\n queue_res = MessageQueue(\"resend_modify_message\", client_id, kube_add)\n\n message = create_queue_message(\"resend to new queue {}\".format(0), \"my resend modify\".encode('UTF-8'))\n queue_res.send_queue_message(message)\n\n queue= MessageQueue(\"resend_modify_message\", client_id, kube_add)\n tran=queue.create_transaction()\n\n res_rec=tran.receive(3,5)\n\n if res_rec.is_error:\n raise \"Message dequeue error, error: %s\" % res_rec.is_error\n\n print(\"Received message id: {}, body: {} tags:{}\".format(res_rec.message.MessageID, res_rec.message.Body,res_rec.message.Tags))\n\n mod_msg=res_rec.message\n mod_msg.Channel=\"receiverB\"\n\n mod_msg.Metadata=\"new Metadata\"\n\n res_mod=tran.modify(mod_msg)\n\n if res_mod.is_error:\n raise \"Message modify error, error: %s\" % res_mod.is_error\n\n print(\"Done\")\n```\nEvents\nSending Events\nSingle Event:\n```\n def send_single_event():\n sender = Sender(kube_add)\n event = Event(\n metadata=\"EventMetaData\",\n body=(\"Event Created on time %s\" % datetime.datetime.utcnow()).encode('UTF-8'),\n store=False,\n channel=\"MyTestChannelName\",\n client_id=\"EventSender\"\n )\n event.tags=[\n ('key', 'value'),\n ('key2', 'value2'),\n ]\n sender.send_event(event)\n```\nStream Events:\n```\n sender = Sender(kube_add)\n\n\n def async_streamer():\n for counter in range(3):\n yield Event(\n metadata=\"EventMetaData\",\n body=(\"Event %s Created on time %s\" % (counter, datetime.datetime.utcnow())).encode('UTF-8'),\n store=False,\n channel=\"MyTestChannelName\",\n client_id=\"EventSenderStream\",\n )\n\n\n def result_handler(result):\n print(result)\n\n\n sender.stream_event(async_streamer(), result_handler)\n```\n\nReceiving Events\nFirst you should subscribe to Events:\n```\n def event_subscriber():\n subscriber = Subscriber(kube_add)\n cancel_token=ListenerCancellationToken()\n sub_req= SubscribeRequest(\n channel=\"MyTestChannelName\",\n client_id=str(randint(9, 19999)),\n events_store_type=EventsStoreType.Undefined,\n events_store_type_value=0,\n group=\"\",\n subscribe_type=SubscribeType.Events\n )\n subscriber.subscribe_to_events(sub_req, handle_incoming_events,handle_incoming_error,cancel_token)\n print(\"sub for 2 seconds\")\n time.sleep(2.0)\n print(\"Canceled token\")\n cancel_token.cancel()\n\n def handle_incoming_events(event):\n if event:\n print(\"Subscriber Received Event: Metadata:'%s', Channel:'%s', Body:'%s tags:%s'\" % (\n event.metadata,\n event.channel,\n event.body,\n event.tags\n ))\n\n def handle_incoming_error(error_msg):\n print(\"received error:%s'\" % (\n error_msg\n ))\n```\nEvents Store\nSending Events Store\nSingle Event to Store:\n```\n sender = Sender(kube_add)\n event = Event(\n metadata=\"EventMetaData\",\n body=(\"Event Created on time %s\" % datetime.datetime.utcnow()).encode('UTF-8'),\n store=True,\n channel=\"MyTestChannelNameStore\",\n client_id=\"EventSenderStore\"\n )\n event.tags=[\n ('key', 'value'),\n ('key2', 'value2'),\n ]\n sender.send_event(event)\n```\nStream Events Store:\n```\n sender = Sender(kube_add)\n\n\n def async_streamer():\n for counter in range(3):\n yield Event(\n metadata=\"EventMetaData\",\n body=(\"Event %s Created on time %s\" % (counter, datetime.datetime.utcnow())).encode('UTF-8'),\n store=True,\n channel=\"MyTestChannelNameStore\",\n client_id=\"EventSenderStore\",\n )\n\n\n def result_handler(result):\n print(result)\n\n\n sender.stream_event(async_streamer(), result_handler)\n\n```\nReceiving Events Store\nFirst you should subscribe to Events Store and get a channel:\n```\n subscriber = Subscriber(kube_add)\n cancel_token=ListenerCancellationToken()\n sub_req= SubscribeRequest(\n channel=\"MyTestChannelNameStore\",\n client_id=str(randint(9, 19999)),\n events_store_type=EventsStoreType.StartFromFirst,\n events_store_type_value=0,\n group=\"\",\n subscribe_type=SubscribeType.EventsStore\n )\n subscriber.subscribe_to_events(sub_req, handle_incoming_events,handle_incoming_error,cancel_token)\n print(\"sub for 2 seconds\")\n time.sleep(2.0)\n print(\"Canceled token\")\n cancel_token.cancel()\n```\nSubscription Options\nKubeMQ supports six types of subscriptions:\n\nStartFromNewEvents - start event store subscription with only new events\n\nStartFromFirstEvent - replay all the stored events from the first available sequence and continue stream new events from this point\n\nStartFromLastEvent - replay the last event and continue stream new events from this point\n\nStartFromSequence - replay events from specific event sequence number and continue stream new events from this point\n\nStartFromTime - replay events from specific time continue stream new events from this point\n\nStartFromTimeDelta - replay events from specific current time - delta duration in seconds, continue stream new events from this point\n\nCommands\nConcept\nCommands implement synchronous messaging pattern which the sender send a request and wait for a specific amount of time to get a response.\n\nThe response can be successful or not. This is the responsibility of the responder to return with the result of the command within the time the sender set in the request.\n\nSending Command Requests:\nIn this example, the responder should send his response withing one second. Otherwise, an error will be return as a timeout:\n```\n request = Request(\n body=\"Request\".encode('UTF-8'),\n metadata=\"MyMetadata\",\n cache_key=\"\",\n cache_ttl=0,\n channel=\"MyTestChannelName\",\n client_id=\"CommandQueryInitiator\",\n timeout=1000,\n request_type=RequestType.Command,\n tags=[\n ('key', 'value'),\n ('key2', 'value2'),\n ]\n )\n initiator = Initiator(kube_add)\n response = initiator.send_request(request)\n\n```\nQueries\nConcept\nQueries implement synchronous messaging pattern which the sender send a request and wait for a specific amount of time to get a response.\n\nThe response must include metadata or body together with an indication of successful or not operation. This is the responsibility of the responder to return with the result of the query within the time the sender set in the request.\n\nSending Query Requests:\n```\n request = Request(\n body=\"Request\".encode('UTF-8'),\n metadata=\"MyMetadata\",\n cache_key=\"\",\n cache_ttl=0,\n channel=\"MyTestChannelName\",\n client_id=\"QueryInitiator\",\n timeout=1000,\n request_type=RequestType.Query,\n tags=[\n ('key', 'value'),\n ('key2', 'value2'),\n ]\n )\n initiator = Initiator(kube_add)\n response = initiator.send_request(request)\n```\nReceiving Query Requests and sending response\nFirst get a channel of queries:\n```\n responder = Responder(kube_add)\n cancel_token=ListenerCancellationToken()\n sub_req= SubscribeRequest(\n channel=\"MyTestRequestChannelName\",\n client_id=str(randint(9, 19999)),\n events_store_type=EventsStoreType.Undefined,\n events_store_type_value=0,\n group=\"\",\n subscribe_type=SubscribeType.Queries\n )\n responder.subscribe_to_requests(sub_req, handle_incoming_events,handle_incoming_error,cancel_token)\n print(\"sub for 10 seconds\")\n time.sleep(10.0)\n print(\"Canceled token\")\n cancel_token.cancel()\n\ndef handle_incoming_request(request):\n if request:\n print(\"Subscriber Received request: Metadata:'%s', Channel:'%s', Body:'%s' tags:%s\" % (\n request.metadata,\n request.channel,\n request.body,\n request.tags\n ))\n\n response = Response(request)\n response.body = \"OK\".encode('UTF-8')\n response.cache_hit = False\n response.error = \"None\"\n response.client_id = client_id\n response.executed = True\n response.metadata = \"OK\"\n response.timestamp = datetime.datetime.now()\n response.tags=request.tags\n #Return response\n return response\n\ndef handle_request_incoming_error(error_msg):\n print(\"received error:%s'\" % (\n error_msg\n ))\n```\n\nLicense\nThis project is licensed under the MIT License - see the LICENSE file for details\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/kubemq-io/kubemq-Python", "keywords": "", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "kubemq", "package_url": "https://pypi.org/project/kubemq/", "platform": "", "project_url": "https://pypi.org/project/kubemq/", "project_urls": { "Homepage": "https://github.com/kubemq-io/kubemq-Python" }, "release_url": "https://pypi.org/project/kubemq/1.0.4/", "requires_dist": [ "future (==0.17.1)", "grpcio (==1.17.1)", "protobuf (==3.6.1)" ], "requires_python": "", "summary": "KubeMQ SDK for Python", "version": "1.0.4" }, "last_serial": 5953376, "releases": { "1.0.2": [ { "comment_text": "", "digests": { "md5": "7adb8c6527e2deb54ee9d8090e09a988", "sha256": "d867c311c0a69977b5a2fc89a7628df45fc23a123857fcfd4d8805977595dd32" }, "downloads": -1, "filename": "Kubemq-1.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "7adb8c6527e2deb54ee9d8090e09a988", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 45405, "upload_time": "2019-10-10T06:56:56", "url": "https://files.pythonhosted.org/packages/65/05/e6f4ff4a0cdde69310ec552e01d22c124e5328f9ba50c2790e50bb3d7972/Kubemq-1.0.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "40bdc207c8b3a5c0bf2878ee480b458e", "sha256": "045e5353c086dfefdc0822a4af84705460dc64adf6759f6051d87b88769a0aa2" }, "downloads": -1, "filename": "Kubemq-1.0.2.tar.gz", "has_sig": false, "md5_digest": "40bdc207c8b3a5c0bf2878ee480b458e", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 20162, "upload_time": "2019-10-10T06:56:58", "url": "https://files.pythonhosted.org/packages/bb/b7/e6e626583d5ac7fc8510562467b9f037b36c6dc0180364ca6b33bac0dbb2/Kubemq-1.0.2.tar.gz" } ], "1.0.4": [ { "comment_text": "", "digests": { "md5": "418cf806ac7a22c299341e8f9e34a7cf", "sha256": "873b5feaa9d200bb937e9e94cc695e8b857e24bd3bd7c0c8fd6b42299f4e6cfc" }, "downloads": -1, "filename": "kubemq-1.0.4-py3-none-any.whl", "has_sig": false, "md5_digest": "418cf806ac7a22c299341e8f9e34a7cf", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 68211, "upload_time": "2019-10-07T13:01:02", "url": "https://files.pythonhosted.org/packages/67/97/cc86acf278382e1bb5a425532798a79087fa66ef71bb15c9a1a26d3f49f6/kubemq-1.0.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3f29078a86174c2d9551b52092902693", "sha256": "a01b390a1481d61a1b649fefcb4c0943125dca1f131a8b0879683b98a4c00620" }, "downloads": -1, "filename": "kubemq-1.0.4.tar.gz", "has_sig": false, "md5_digest": "3f29078a86174c2d9551b52092902693", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36242, "upload_time": "2019-10-07T13:01:04", "url": "https://files.pythonhosted.org/packages/c8/b6/be5b53da22902bf08ac3d342e69efb07057c447a88e72601358beb258159/kubemq-1.0.4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "418cf806ac7a22c299341e8f9e34a7cf", "sha256": "873b5feaa9d200bb937e9e94cc695e8b857e24bd3bd7c0c8fd6b42299f4e6cfc" }, "downloads": -1, "filename": "kubemq-1.0.4-py3-none-any.whl", "has_sig": false, "md5_digest": "418cf806ac7a22c299341e8f9e34a7cf", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 68211, "upload_time": "2019-10-07T13:01:02", "url": "https://files.pythonhosted.org/packages/67/97/cc86acf278382e1bb5a425532798a79087fa66ef71bb15c9a1a26d3f49f6/kubemq-1.0.4-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3f29078a86174c2d9551b52092902693", "sha256": "a01b390a1481d61a1b649fefcb4c0943125dca1f131a8b0879683b98a4c00620" }, "downloads": -1, "filename": "kubemq-1.0.4.tar.gz", "has_sig": false, "md5_digest": "3f29078a86174c2d9551b52092902693", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 36242, "upload_time": "2019-10-07T13:01:04", "url": "https://files.pythonhosted.org/packages/c8/b6/be5b53da22902bf08ac3d342e69efb07057c447a88e72601358beb258159/kubemq-1.0.4.tar.gz" } ] }