{ "info": { "author": "Hazelcast Inc. Developers", "author_email": "hazelcast@googlegroups.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "# Table of Contents\n\n* [Introduction](#introduction)\n* [1. Getting Started](#1-getting-started)\n * [1.1. Requirements](#11-requirements)\n * [1.2. Working with Hazelcast IMDG Clusters](#12-working-with-hazelcast-imdg-clusters)\n * [1.2.1. Setting Up a Hazelcast IMDG Cluster](#121-setting-up-a-hazelcast-imdg-cluster)\n * [1.2.1.1. Running Standalone JARs](#1211-running-standalone-jars)\n * [1.2.1.2. Adding User Library to CLASSPATH](#1212-adding-user-library-to-classpath)\n * [1.3. Downloading and Installing](#13-downloading-and-installing)\n * [1.4. Basic Configuration](#14-basic-configuration)\n * [1.4.1. Configuring Hazelcast IMDG](#141-configuring-hazelcast-imdg)\n * [1.4.2. Configuring Hazelcast Python Client](#142-configuring-hazelcast-python-client)\n * [1.4.2.1. Group Settings](#1421-group-settings)\n * [1.4.2.2. Network Settings](#1422-network-settings)\n * [1.4.3. Client System Properties](#143-client-system-properties)\n * [1.5. Basic Usage](#15-basic-usage)\n * [1.6. Code Samples](#16-code-samples)\n* [2. Features](#2-features)\n* [3. Configuration Overview](#3-configuration-overview)\n * [3.1. Configuration Options](#31-configuration-options)\n * [3.1.1. Programmatic Configuration](#311-programmatic-configuration)\n* [4. Serialization](#4-serialization)\n * [4.1. IdentifiedDataSerializable Serialization](#41-identifieddataserializable-serialization)\n * [4.2. Portable Serialization](#42-portable-serialization)\n * [4.2.1. Versioning for Portable Serialization](#421-versioning-for-portable-serialization)\n * [4.3. Custom Serialization](#43-custom-serialization)\n * [4.4. JSON Serialization](#44-json-serialization)\n * [4.5. Global Serialization](#45-global-serialization)\n* [5. Setting Up Client Network](#5-setting-up-client-network)\n * [5.1. Providing Member Addresses](#51-providing-member-addresses)\n * [5.2. Setting Smart Routing](#52-setting-smart-routing)\n * [5.3. Enabling Redo Operation](#53-enabling-redo-operation)\n * [5.4. Setting Connection Timeout](#54-setting-connection-timeout)\n * [5.5. Setting Connection Attempt Limit](#55-setting-connection-attempt-limit)\n * [5.6. Setting Connection Attempt Period](#56-setting-connection-attempt-period)\n * [5.7. Enabling Client TLS/SSL](#57-enabling-client-tlsssl)\n * [5.8. Enabling Hazelcast Cloud Discovery](#58-enabling-hazelcast-cloud-discovery)\n* [6. Securing Client Connection](#6-securing-client-connection)\n * [6.1. TLS/SSL](#61-tlsssl)\n * [6.1.1. TLS/SSL for Hazelcast Members](#611-tlsssl-for-hazelcast-members)\n * [6.1.2. TLS/SSL for Hazelcast Python Clients](#612-tlsssl-for-hazelcast-python-clients)\n * [6.1.3. Mutual Authentication](#613-mutual-authentication)\n* [7. Using Python Client with Hazelcast IMDG](#7-using-python-client-with-hazelcast-imdg)\n * [7.1. Python Client API Overview](#71-python-client-api-overview)\n * [7.2. Python Client Operation Modes](#72-python-client-operation-modes)\n * [7.2.1. Smart Client](#721-smart-client)\n * [7.2.2. Unisocket Client](#722-unisocket-client)\n * [7.3. Handling Failures](#73-handling-failures)\n * [7.3.1. Handling Client Connection Failure](#731-handling-client-connection-failure)\n * [7.3.2. Handling Retry-able Operation Failure](#732-handling-retry-able-operation-failure)\n * [7.4. Using Distributed Data Structures](#74-using-distributed-data-structures)\n * [7.4.1. Using Map](#741-using-map)\n * [7.4.2. Using MultiMap](#742-using-multimap)\n * [7.4.3. Using Replicated Map](#743-using-replicated-map)\n * [7.4.4. Using Queue](#744-using-queue)\n * [7.4.5. Using Set](#745-using-set)\n * [7.4.6. Using List](#746-using-list)\n * [7.4.7. Using Ringbuffer](#747-using-ringbuffer)\n * [7.4.8. Using Topic](#748-using-topic) \n * [7.4.9. Using Lock](#749-using-lock)\n * [7.4.10. Using Atomic Long](#7410-using-atomic-long)\n * [7.4.11. Using Semaphore](#7411-using-semaphore)\n * [7.4.12. Using Transactions](#7412-using-transactions)\n * [7.4.13. Using PN Counter](#7413-using-pn-counter)\n * [7.4.14. Using Flake ID Generator](#7414-using-flake-id-generator)\n * [7.5. Distributed Events](#75-distributed-events)\n * [7.5.1. Cluster Events](#751-cluster-events)\n * [7.5.1.1. Listening for Member Events](#7511-listening-for-member-events)\n * [7.5.1.2. Listening for Lifecycle Events](#7512-listening-for-lifecycle-events)\n * [7.5.2. Distributed Data Structure Events](#752-distributed-data-structure-events)\n * [7.5.2.1. Listening for Map Events](#7521-listening-for-map-events)\n * [7.6. Distributed Computing](#76-distributed-computing)\n * [7.6.1. Using EntryProcessor](#761-using-entryprocessor)\n * [7.7. Distributed Query](#77-distributed-query)\n * [7.7.1. How Distributed Query Works](#771-how-distributed-query-works)\n * [7.7.1.1. Employee Map Query Example](#7711-employee-map-query-example)\n * [7.7.1.2. Querying by Combining Predicates with AND, OR, NOT](#7712-querying-by-combining-predicates-with-and-or-not)\n * [7.7.1.3. Querying with SQL](#7713-querying-with-sql)\n * [7.7.1.4. Querying with JSON Strings](#7714-querying-with-json-strings)\n * [7.8. Performance](#78-performance)\n * [7.8.1. Near Cache](#781-near-cache)\n * [7.8.1.1. Configuring Near Cache](#7811-configuring-near-cache)\n * [7.8.1.2. Near Cache Example for Map](#7812-near-cache-example-for-map)\n * [7.8.1.3. Near Cache Eviction](#7813-near-cache-eviction)\n * [7.8.1.4. Near Cache Expiration](#7814-near-cache-expiration)\n * [7.8.1.5. Near Cache Invalidation](#7815-near-cache-invalidation)\n * [7.9. Monitoring and Logging](#79-monitoring-and-logging)\n * [7.9.1. Enabling Client Statistics](#791-enabling-client-statistics)\n * [7.9.2. Logging Configuration](#792-logging-configuration)\n* [8. Development and Testing](#8-development-and-testing)\n * [8.1. Building and Using Client From Sources](#81-building-and-using-client-from-sources)\n * [8.2. Testing](#82-testing)\n* [9. Getting Help](#9-getting-help)\n* [10. Contributing](#10-contributing)\n* [11. License](#11-license)\n* [12. Copyright](#12-copyright)\n\n# Introduction\n\nThis document provides information about the Python client for [Hazelcast](https://hazelcast.org/). This client uses Hazelcast's [Open Client Protocol](https://hazelcast.org/documentation/#open-binary) and works with Hazelcast IMDG 3.6 and higher versions.\n\n### Resources\n\nSee the following for more information on Python and Hazelcast IMDG:\n\n* Hazelcast IMDG [website](https://hazelcast.org/)\n* Hazelcast IMDG [Reference Manual](https://hazelcast.org/documentation/#imdg)\n* About [Python](https://www.python.org/about/)\n\n### Release Notes\n\nSee the [Releases](https://github.com/hazelcast/hazelcast-python-client/releases) page of this repository.\n\n# 1. Getting Started\n\nThis chapter provides information on how to get started with your Hazelcast Python client. It outlines the requirements, installation and configuration of the client, setting up a cluster, and provides a simple application that uses a distributed map in Python client.\n\n## 1.1. Requirements\n\n- Windows, Linux/UNIX or Mac OS X\n- Python 2.7 or Python 3.4 or newer\n- Java 6 or newer\n- Hazelcast IMDG 3.6 or newer\n- Latest Hazelcast Python client\n\n## 1.2. Working with Hazelcast IMDG Clusters\n\nHazelcast Python client requires a working Hazelcast IMDG cluster to run. This cluster handles storage and manipulation of the user data.\nClients are a way to connect to the Hazelcast IMDG cluster and access such data.\n\nHazelcast IMDG cluster consists of one or more cluster members. These members generally run on multiple virtual or physical machines\nand are connected to each other via network. Any data put on the cluster is partitioned to multiple members transparent to the user.\nIt is therefore very easy to scale the system by adding new members as the data grows. Hazelcast IMDG cluster also offers resilience. Should\nany hardware or software problem causes a crash to any member, the data on that member is recovered from backups and the cluster\ncontinues to operate without any downtime. Hazelcast clients are an easy way to connect to a Hazelcast IMDG cluster and perform tasks on\ndistributed data structures that live on the cluster.\n\nIn order to use Hazelcast Python client, we first need to setup a Hazelcast IMDG cluster.\n\n### 1.2.1. Setting Up a Hazelcast IMDG Cluster\n\nThere are following options to start a Hazelcast IMDG cluster easily:\n\n* You can run standalone members by downloading and running JAR files from the website.\n* You can embed members to your Java projects. \n\nWe are going to download JARs from the website and run a standalone member for this guide.\n\n#### 1.2.1.1. Running Standalone JARs\n\nFollow the instructions below to create a Hazelcast IMDG cluster:\n\n1. Go to Hazelcast's download [page](https://hazelcast.org/download/) and download either the `.zip` or `.tar` distribution of Hazelcast IMDG.\n2. Decompress the contents into any directory that you\nwant to run members from.\n3. Change into the directory that you decompressed the Hazelcast content and then into the `bin` directory.\n4. Use either `start.sh` or `start.bat` depending on your operating system. Once you run the start script, you should see the Hazelcast IMDG logs in the terminal.\n\nYou should see a log similar to the following, which means that your 1-member cluster is ready to be used:\n\n```\nINFO: [192.168.0.3]:5701 [dev] [3.10.4]\n\nMembers {size:1, ver:1} [\n\tMember [192.168.0.3]:5701 - 65dac4d1-2559-44bb-ba2e-ca41c56eedd6 this\n]\n\nSep 06, 2018 10:50:23 AM com.hazelcast.core.LifecycleService\nINFO: [192.168.0.3]:5701 [dev] [3.10.4] [192.168.0.3]:5701 is STARTED\n```\n\n#### 1.2.1.2. Adding User Library to CLASSPATH\n\nWhen you want to use features such as querying and language interoperability, you might need to add your own Java classes to the Hazelcast member in order to use them from your Python client. This can be done by adding your own compiled code to the `CLASSPATH`. To do this, compile your code with the `CLASSPATH` and add the compiled files to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`). Then, you can start your Hazelcast member by using the start scripts in the `bin` directory. The start scripts will automatically add your compiled classes to the `CLASSPATH`.\n\nNote that if you are adding an `IdentifiedDataSerializable` or a `Portable` class, you need to add its factory too. Then, you should configure the factory in the `hazelcast.xml` configuration file. This file resides in the `bin` directory where you extracted the `hazelcast-.zip` (or `tar`).\n\nThe following is an example configuration when you are adding an `IdentifiedDataSerializable` class:\n\n ```xml\n\n ...\n \n \n >\n IdentifiedFactoryClassName\n \n \n \n ...\n\n```\n\nIf you want to add a `Portable` class, you should use `` instead of `` in the above configuration.\n\nSee the [Hazelcast IMDG Reference Manual](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#getting-started) for more information on setting up the clusters.\n\n## 1.3. Downloading and Installing\n\nYou can download and install the Python client from [PyPI](https://pypi.org/project/hazelcast-python-client/) using pip. Run the following command:\n\n```\npip install hazelcast-python-client\n```\n\nAlternatively, it can be installed from the source using the following command:\n\n```\npython setup.py install\n```\n\n## 1.4. Basic Configuration\n\nIf you are using Hazelcast IMDG and Python client on the same computer, generally the default configuration should be fine. This is great for\ntrying out the client. However, if you run the client on a different computer than any of the cluster members, you may\nneed to do some simple configurations such as specifying the member addresses.\n\nThe Hazelcast IMDG members and clients have their own configuration options. You may need to reflect some of the member side configurations on the client side to properly connect to the cluster.\n\nThis section describes the most common configuration elements to get you started in no time.\nIt discusses some member side configuration options to ease the understanding of Hazelcast's ecosystem. Then, the client side configuration options\nregarding the cluster connection are discussed. The configurations for the Hazelcast IMDG data structures that can be used in the Python client are discussed in the following sections.\n\nSee the [Hazelcast IMDG Reference Manual](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html) and [Configuration Overview section](#3-configuration-overview) for more information.\n\n### 1.4.1. Configuring Hazelcast IMDG\n\nHazelcast IMDG aims to run out-of-the-box for most common scenarios. However if you have limitations on your network such as multicast being disabled,\nyou may have to configure your Hazelcast IMDG members so that they can find each other on the network. Also, since most of the distributed data structures are configurable, you may want to configure them according to your needs. We will show you the basics about network configuration here.\n\nYou can use the following options to configure Hazelcast IMDG:\n\n* Using the `hazelcast.xml` configuration file.\n* Programmatically configuring the member before starting it from the Java code.\n\nSince we use standalone servers, we will use the `hazelcast.xml` file to configure our cluster members.\n\nWhen you download and unzip `hazelcast-.zip` (or `tar`), you see the `hazelcast.xml` in the `bin` directory. When a Hazelcast member starts, it looks for the `hazelcast.xml` file to load the configuration from. A sample `hazelcast.xml` is shown below.\n\n```xml\n\n \n dev\n dev-pass\n \n \n 5701\n \n \n 224.2.2.3\n 54327\n \n \n 127.0.0.1\n \n 127.0.0.1\n \n \n \n \n \n \n \n 1\n \n\n```\n\nWe will go over some important configuration elements in the rest of this section.\n\n- ``: Specifies which cluster this member belongs to. A member connects only to the other members that are in the same group as\nitself. As shown in the above configuration sample, there are `` and `` tags under the `` element with some pre-configured values. You may give your clusters different names so that they can\nlive in the same network without disturbing each other. Note that the cluster name should be the same across all members and clients that belong\n to the same cluster. The `` tag is not in use since Hazelcast 3.9. It is there for backward compatibility\npurposes. You can remove or leave it as it is if you use Hazelcast 3.9 or later.\n- ``\n - ``: Specifies the port number to be used by the member when it starts. Its default value is 5701. You can specify another port number, and if\n you set `auto-increment` to `true`, then Hazelcast will try the subsequent ports until it finds an available port or the `port-count` is reached.\n - ``: Specifies the strategies to be used by the member to find other cluster members. Choose which strategy you want to\n use by setting its `enabled` attribute to `true` and the others to `false`.\n - ``: Members find each other by sending multicast requests to the specified address and port. It is very useful if IP addresses\n of the members are not static.\n - ``: This strategy uses a pre-configured list of known members to find an already existing cluster. It is enough for a member to\n find only one cluster member to connect to the cluster. The rest of the member list is automatically retrieved from that member. We recommend\n putting multiple known member addresses there to avoid disconnectivity should one of the members in the list is unavailable at the time\n of connection.\n\nThese configuration elements are enough for most connection scenarios. Now we will move onto the configuration of the Python client.\n\n### 1.4.2. Configuring Hazelcast Python Client\n\nHazelcast Python client can be configured programmatically.\n\nThis section describes some network configuration settings to cover common use cases in connecting the client to a cluster. See the [Configuration Overview section](#3-configuration-overview)\nand the following sections for information about detailed network configurations and/or additional features of Hazelcast Python client configuration.\n\nAn easy way to configure your Hazelcast Python client is to create a `ClientConfig` object and set the appropriate options. Then you can\nsupply this object to your client at the startup. This is the programmatic configuration approach.\n\nOnce you imported `hazelcast` to your Python project, you may follow programmatic configuration approach.\n\nYou need to create a `ClientConfig` object and adjust its properties. Then you can pass this object to the client when starting it.\n\n```python\nimport hazelcast\n\nconfig = hazelcast.ClientConfig()\nclient = hazelcast.HazelcastClient(config)\n```\n\n---\n\nIf you run the Hazelcast IMDG members in a different server than the client, you most probably have configured the members' ports and cluster\nnames as explained in the previous section. If you did, then you need to make certain changes to the network settings of your client.\n\n\n#### 1.4.2.1. Group Settings\n\nYou need to provide the group name of the cluster, if it is defined on the server side, to which you want the client to connect.\n\n```python\nconfig = hazelcast.ClientConfig()\nconfig.group_config.name = \"group-name-of-your-cluster\"\nconfig.group_config.password = \"group password\"\n```\n\n> **NOTE: If you have a Hazelcast IMDG release older than 3.11, you need to provide also a group password along with the group name.**\n\n#### 1.4.2.2. Network Settings\n\nYou need to provide the IP address and port of at least one member in your cluster so the client can find it.\n\n```python\nimport hazelcast\n\nconfig = hazelcast.ClientConfig()\nconfig.network_config.addresses.append(\"IP-address:port\")\n```\n\n### 1.4.3. Client System Properties\n\nWhile configuring your Python client, you can use various system properties provided by Hazelcast to tune its clients. These properties can be set programmatically through `config.set_property` method or by using an environment variable.\n\nThe value of the any property will be:\n\n* the programmatically configured value, if programmatically set,\n* the environment variable value, if the environment variable is set,\n* the default value, if none of the above is set.\n\nSee the following for an example client system property configuration:\n\n**Programmatically:**\n\n ```python\nfrom hazelcast.config import ClientProperties\n\n# Sets invocation timeout as 2 seconds\nconfig.set_property(ClientProperties.INVOCATION_TIMEOUT_SECONDS.name, 2) \n```\n\nor \n\n ```python\n# Sets invocation timeout as 2 seconds\nconfig.set_property(\"hazelcast.client.invocation.timeout.seconds\", 2)\n```\n\n **By using an environment variable:** \n\n```python\nimport os\n\nenviron = os.environ\nenviron[ClientProperties.INVOCATION_TIMEOUT_SECONDS.name] = \"2\"\n```\n\nIf you set a property both programmatically and via an environment variable, the programmatically set value will be used.\n\nSee the [complete list](http://hazelcast.github.io/hazelcast-python-client/3.10/hazelcast.config.html#hazelcast.config.ClientProperties) of client system properties, along with their descriptions, which can be used to configure your Hazelcast Python client.\n\n## 1.5. Basic Usage\n\nNow that we have a working cluster and we know how to configure both our cluster and client, we can run a simple program to use a\ndistributed map in the Python client.\n\nThe following example first configures the logger for the Python client. You can find more information about the logging options in the [Logging Configuration section](#792-logging-configuration). \n\nThen, it creates a configuration object and starts a client.\n\n```python\nimport hazelcast\n\n# We create a config for illustrative purposes.\n# We do not adjust this config. Therefore it has default settings.\nconfig = hazelcast.ClientConfig() \n\n# Client connects to the cluster with the given configuration. \nclient = hazelcast.HazelcastClient(config) \n```\n\nThis should print logs about the cluster members such as address, port and UUID to the `stderr`.\n\n```\nFeb 15, 2019 12:51:59 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] A non-empty group password is configured for the Hazelcast client. Starting with Hazelcast IMDG version 3.11, clients with the same group name, but with different group passwords (that do not use authentication) will be accepted to a cluster. The group password configuration will be removed completely in a future release.\nFeb 15, 2019 12:51:59 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is STARTING\nFeb 15, 2019 12:51:59 PM HazelcastClient.ClusterService\nINFO: [3.10] [dev] [hz.client_0] Connecting to Address(host=127.0.0.1, port=5701)\nFeb 15, 2019 12:51:59 PM HazelcastClient.ConnectionManager\nINFO: [3.10] [dev] [hz.client_0] Authenticated with Connection(address=('127.0.0.1', 5701), id=0)\nFeb 15, 2019 12:51:59 PM HazelcastClient.ClusterService\nINFO: [3.10] [dev] [hz.client_0] New member list:\n\nMembers [1] {\n\tMember [10.216.1.49]:5701 - 1f4bb35d-b68f-46eb-bd65-61e3f4bc9922\n}\n\nFeb 15, 2019 12:51:59 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is CONNECTED\nFeb 15, 2019 12:51:59 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] Client started.\n```\n\nCongratulations. You just started a Hazelcast Python client.\n\n**Using a Map**\n\nLet's manipulate a distributed map on a cluster using the client.\n\n```python\nimport hazelcast\n\nconfig = hazelcast.ClientConfig()\nclient = hazelcast.HazelcastClient(config)\n\npersonnel_map = client.get_map(\"personnel-map\")\npersonnel_map.put(\"Alice\", \"IT\")\npersonnel_map.put(\"Bob\", \"IT\")\npersonnel_map.put(\"Clark\", \"IT\")\n\nprint(\"Added IT personnel. Printing all known personnel\")\n\nfor person, department in personnel_map.entry_set().result():\n print(\"{} is in {} department\".format(person, department))\n\nclient.shutdown()\n```\n\n**Output**\n\n```\nFeb 15, 2019 12:53:15 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] A non-empty group password is configured for the Hazelcast client. Starting with Hazelcast IMDG version 3.11, clients with the same group name, but with different group passwords (that do not use authentication) will be accepted to a cluster. The group password configuration will be removed completely in a future release.\nFeb 15, 2019 12:53:15 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is STARTING\nFeb 15, 2019 12:53:15 PM HazelcastClient.ClusterService\nINFO: [3.10] [dev] [hz.client_0] Connecting to Address(host=127.0.0.1, port=5701)\nFeb 15, 2019 12:53:15 PM HazelcastClient.ConnectionManager\nINFO: [3.10] [dev] [hz.client_0] Authenticated with Connection(address=('127.0.0.1', 5701), id=0)\nFeb 15, 2019 12:53:15 PM HazelcastClient.ClusterService\nINFO: [3.10] [dev] [hz.client_0] New member list:\n\nMembers [1] {\n\tMember [10.216.1.49]:5701 - 1f4bb35d-b68f-46eb-bd65-61e3f4bc9922\n}\n\nFeb 15, 2019 12:53:15 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is CONNECTED\nFeb 15, 2019 12:53:15 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] Client started.\nAdded IT personnel. Printing all known personnel\nAlice is in IT department\nClark is in IT department\nBob is in IT department\nFeb 15, 2019 12:53:15 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is SHUTTING_DOWN\nFeb 15, 2019 12:53:15 PM HazelcastClient.AsyncoreReactor\nWARNING: [3.10] [dev] [hz.client_0] Connection closed by server\nFeb 15, 2019 12:53:15 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is SHUTDOWN\nFeb 15, 2019 12:53:15 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] Client shutdown.\n```\n\nYou see this example puts all the IT personnel into a cluster-wide `personnel-map` and then prints all the known personnel.\n\nNow, run the following code.\n\n```python\npersonnel_map = client.get_map(\"personnel-map\")\npersonnel_map.put(\"Denise\", \"Sales\")\npersonnel_map.put(\"Erwing\", \"Sales\")\npersonnel_map.put(\"Faith\", \"Sales\")\n\nprint(\"Added Sales personnel. Printing all known personnel\")\n\nfor person, department in personnel_map.entry_set().result():\n print(\"{} is in {} department\".format(person, department))\n```\n\n**Output**\n\n```\nFeb 15, 2019 12:54:05 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] A non-empty group password is configured for the Hazelcast client. Starting with Hazelcast IMDG version 3.11, clients with the same group name, but with different group passwords (that do not use authentication) will be accepted to a cluster. The group password configuration will be removed completely in a future release.\nFeb 15, 2019 12:54:05 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is STARTING\nFeb 15, 2019 12:54:05 PM HazelcastClient.ClusterService\nINFO: [3.10] [dev] [hz.client_0] Connecting to Address(host=127.0.0.1, port=5701)\nFeb 15, 2019 12:54:05 PM HazelcastClient.ConnectionManager\nINFO: [3.10] [dev] [hz.client_0] Authenticated with Connection(address=('127.0.0.1', 5701), id=0)\nFeb 15, 2019 12:54:05 PM HazelcastClient.ClusterService\nINFO: [3.10] [dev] [hz.client_0] New member list:\n\nMembers [1] {\n\tMember [10.216.1.49]:5701 - 1f4bb35d-b68f-46eb-bd65-61e3f4bc9922\n}\n\nFeb 15, 2019 12:54:05 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is CONNECTED\nFeb 15, 2019 12:54:05 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] Client started.\nAdded Sales personnel. Printing all known personnel\nDenise is in Sales department\nErwing is in Sales department\nFaith is in Sales department\nAlice is in IT department\nClark is in IT department\nBob is in IT department\nFeb 15, 2019 12:54:05 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is SHUTTING_DOWN\nFeb 15, 2019 12:54:05 PM HazelcastClient.AsyncoreReactor\nWARNING: [3.10] [dev] [hz.client_0] Connection closed by server\nFeb 15, 2019 12:54:05 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is SHUTDOWN\nFeb 15, 2019 12:54:05 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] Client shutdown.\n```\n\nYou will see this time we add only the sales employees but we get the list of all known employees including the ones in IT.\nThat is because our map lives in the cluster and no matter which client we use, we can access the whole map.\n\nYou may wonder why we have used `result()` method over the `entry_set()` method of the `personnel_map`. That is because \nthe Hazelcast Python client is designed to be fully asynchronous. Every method call over distributed objects such as \n`put()`, `get()`, `entry_set()`, etc. will return a `Future` object that is similar to the [Future](https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Future)\nclass of the [concurrent.futures](https://docs.python.org/3/library/concurrent.futures.html#module-concurrent.futures) module.\n\nWith this design choice, method calls over the distributed objects can be executed asynchronously without blocking the \nexecution order of your program. \n\nYou may get the value returned by the method calls using the `result()` method of the `Future` class.\n`result` will block the execution of your program and will wait until the future finishes running. Then, it will return the\nvalue returned by the call which are key-value pairs in our `entry_set()` method call. \n\nYou may also attach a function to the future objects that will be called, with the future as its only argument, when the future finishes running.\n\nFor example, the part where we printed the personnel in above code can be rewritten with a callback attached to the `entry_set()`, as shown below..\n\n```python\nimport time\n\ndef entry_set_cb(future):\n for person, department in future.result():\n print(\"{} is in {} department\".format(person, department))\n\npersonnel_map.entry_set().add_done_callback(entry_set_cb)\ntime.sleep(0.1) # wait for Future to complete\n```\n\nAsynchronous operations are far more efficient in single threaded Python interpreter but you may want all of your method calls\nover distributed objects to be blocking. For this purpose, Hazelcast Python client provides a helper method called `blocking()`. \nThis method blocks the execution of your program for all the method calls over distributed objects\nuntil the return value of your call is calculated and returns that value directly instead of a `Future` object.\n\nTo make the `personnel_map` presented previously in this section blocking, you need to call `blocking()` method over it.\n\n```python\npersonnel_map = client.get_map(\"personnel-map\").blocking()\n``` \n\nNow, all the methods over the `personnel_map`, such as `put()` and `entry_set()`, will be blocking. So, you don't need to call `result()`\nover it or attach a callback to it anymore.\n\n```python\nfor person, department in personnel_map.entry_set():\n print(\"{} is in {} department\".format(person, department))\n``` \n\n## 1.6. Code Samples\n\nSee the Hazelcast Python [examples](https://github.com/hazelcast/hazelcast-python-client/tree/master/examples) for more code samples.\n\nYou can also see the [latest Hazelcast Python API Documentation](http://hazelcast.github.io/hazelcast-python-client/3.10/index.html) or [global API Documentation page](http://hazelcast.github.io/hazelcast-python-client/).\n\n# 2. Features\n\nHazelcast Python client supports the following data structures and features:\n\n* Map\n* Queue\n* Set\n* List\n* MultiMap\n* Replicated Map\n* Ringbuffer\n* Topic\n* Lock\n* Semaphore\n* AtomicLong\n* CRDT PN Counter\n* AtomicReference\n* IdGenerator\n* CountDownLatch\n* Distributed Executor Service\n* Event Listeners\n* Sub-Listener Interfaces for Map Listener\n* Entry Processor\n* Transactional Map\n* Transactional MultiMap\n* Transactional Queue\n* Transactional List\n* Transactional Set\n* Query (Predicates)\n* Entry Processor\n* Built-in Predicates\n* Listener with Predicate\n* Near Cache Support\n* Programmatic Configuration\n* SSL Support (requires Enterprise server)\n* Mutual Authentication (requires Enterprise server)\n* Authorization\n* Smart Client\n* Unisocket Client\n* Lifecycle Service\n* Hazelcast Cloud Discovery\n* IdentifiedDataSerializable Serialization\n* Portable Serialization\n* Custom Serialization\n* JSON Serialization\n* Global Serialization\n\n# 3. Configuration Overview\n\nThis chapter describes the options to configure your Python client.\n\n## 3.1. Configuration Options\n\nYou can configure Hazelcast Python client programmatically (API).\n\n### 3.1.1. Programmatic Configuration\n\nFor programmatic configuration of the Hazelcast Python client, just instantiate a `ClientConfig` object and configure the\ndesired aspects. An example is shown below.\n\n```python\nconfig = hazelcast.ClientConfig()\nconfig.network_config.addresses.append(\"127.0.0.1:5701\")\nclient = hazelcast.HazelcastClient(config)\n```\n\nSee the `ClientConfig` class documentation at [Hazelcast Python Client API Docs](http://hazelcast.github.io/hazelcast-python-client/3.10/hazelcast.config.html) for details.\n\n# 4. Serialization\n\nSerialization is the process of converting an object into a stream of bytes to store the object in the memory, a file or database, or transmit it through the network. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called deserialization. Hazelcast offers you its own native serialization methods. You will see these methods throughout this chapter.\n\nHazelcast serializes all your objects before sending them to the server. The `bool`, `int`, `long` (for Python 2), `float`, `str`, and `unicode` (for Python 2) types are serialized natively and you cannot override this behavior. The following table is the conversion of types for the Java server side.\n\n| Python | Java |\n|---------|----------------------------------------|\n| bool | Boolean |\n| int | Byte, Short, Integer, Long, BigInteger |\n| long | Byte, Short, Integer, Long, BigInteger |\n| float | Float, Double |\n| str | String |\n| unicode | String |\n\n> Note: A `int` or `long` type is serialized as `Integer` by default. You can configure this behavior using the `SerializationConfig.default_integer_type`.\n\nArrays of the above types can be serialized as `boolean[]`, `byte[]`, `short[]`, `int[]`, `float[]`, `double[]`, `long[]` and `string[]` for the Java server side, respectively. \n\n**Serialization Priority**\n\nWhen Hazelcast Python client serializes an object:\n\n1. It first checks whether the object is `None`.\n\n2. If the above check fails, then it checks if it is an instance of `IdentifiedDataSerializable`.\n\n3. If the above check fails, then it checks if it is an instance of `Portable`.\n\n4. If the above check fails, then it checks if it is an instance of one of the default types (see the default types above).\n\n5. If the above check fails, then it looks for a user-specified [Custom Serialization](#43-custom-serialization).\n\n6. If the above check fails, it will use the registered [Global Serialization](#45-global-serialization) if one exists.\n\n7. If the above check fails, then the Python client uses `cPickle` (for Python 2) or `pickle` (for Python 3) by default.\n\nHowever, `cPickle/pickle Serialization` is not the best way of serialization in terms of performance and interoperability between the clients in different languages. If you want the serialization to work faster or you use the clients in different languages, Hazelcast offers its own native serialization types, such as [`IdentifiedDataSerializable` Serialization](#41-identifieddataserializable-serialization) and [`Portable` Serialization](#42-portable-serialization).\n\nOn top of all, if you want to use your own serialization type, you can use a [Custom Serialization](#43-custom-serialization).\n\n## 4.1. IdentifiedDataSerializable Serialization\n\nFor a faster serialization of objects, Hazelcast recommends to extend the `IdentifiedDataSerializable` class.\n\nThe following is an example of a class that extends `IdentifiedDataSerializable`:\n\n```python\nfrom hazelcast.serialization.api import IdentifiedDataSerializable\n\nclass Address(IdentifiedDataSerializable):\n def __init__(self, street=None, zip_code=None, city=None, state=None):\n self.street = street\n self.zip_code = zip_code\n self.city = city\n self.state = state\n\n def get_class_id(self):\n return 1\n\n def get_factory_id(self):\n return 1\n\n def write_data(self, object_data_output):\n object_data_output.write_utf(self.street)\n object_data_output.write_int(self.zip_code)\n object_data_output.write_utf(self.city)\n object_data_output.write_utf(self.state)\n\n def read_data(self, object_data_input):\n self.street = object_data_input.read_utf()\n self.zip_code = object_data_input.read_int()\n self.city = object_data_input.read_utf()\n self.state = object_data_input.read_utf()\n```\n> Note: For IdentifiedDataSerializable to work in Python client, the class that inherits it should have default valued parameters in its `__init__` method so that an instance of that class can be created without passing any arguments to it. \n\nThe IdentifiedDataSerializable uses `get_class_id()` and `get_factory_id()` methods to reconstitute the object. To complete the implementation, an `IdentifiedDataSerializable factory` should also be created and registered into the `SerializationConfig` which can be accessed from `Config.serialization_config`. A factory is a dictionary that stores class ID and the `IdentifiedDataSerializable` class type pairs as the key and value. The factory's responsibility is to store the right `IdentifiedDataSerializable` class type for the given class ID. \n\nA sample `IdentifiedDataSerializable factory` could be created as follows:\n\n```python\nfactory = {1: Address}\n```\n\nNote that the keys of the dictionary should be the same as the class IDs of their corresponding `IdentifiedDataSerializable` class types.\n\nThe last step is to register the `IdentifiedDataSerializable factory` to the `SerializationConfig`.\n\n```python\nconfig.serialization_config.data_serializable_factories[1] = factory\n```\n\nNote that the ID that is passed to the `SerializationConfig` is same as the factory ID that the `Address` class returns.\n\n## 4.2. Portable Serialization\n\nAs an alternative to the existing serialization methods, Hazelcast offers portable serialization. To use it, you need to extend the `Portable` class. Portable serialization has the following advantages:\n\n- Supporting multiversion of the same object type.\n- Fetching individual fields without having to rely on the reflection.\n- Querying and indexing support without deserialization and/or reflection.\n\nIn order to support these features, a serialized `Portable` object contains meta information like the version and concrete location of the each field in the binary data. This way Hazelcast is able to navigate in the binary data and deserialize only the required field without actually deserializing the whole object which improves the query performance.\n\nWith multiversion support, you can have two members each having different versions of the same object; Hazelcast stores both meta information and uses the correct one to serialize and deserialize portable objects depending on the member. This is very helpful when you are doing a rolling upgrade without shutting down the cluster.\n\nAlso note that portable serialization is totally language independent and is used as the binary protocol between Hazelcast server and clients.\n\nA sample portable implementation of a `Foo` class looks like the following:\n\n```python\nfrom hazelcast.serialization.api import Portable\n\nclass Foo(Portable):\n\n CLASS_ID = 1\n FACTORY_ID = 1\n\n def __init__(self, foo=None):\n self.foo = foo\n\n def get_class_id(self):\n return CLASS_ID\n\n def get_factory_id(self):\n return FACTORY_ID\n\n def write_portable(self, writer):\n writer.write_utf(\"foo\", self.foo)\n\n def read_portable(self, reader):\n self.foo = reader.read_utf(\"foo\")\n```\n\n> **NOTE: For Portable to work in Python client, the class that inherits it should have default valued parameters in its `__init__` method so that an instance of that class can be created without passing any arguments to it.**\n\nSimilar to `IdentifiedDataSerializable`, a `Portable` class must provide the `get_class_id()` and `get_factory_id()` methods. The factory dictionary will be used to create the `Portable` object given the class ID.\n\nA sample `Portable factory` could be created as follows:\n\n```python\nfactory = {1: Foo}\n```\n\nNote that the keys of the dictionary should be the same as the class IDs of their corresponding `Portable` class types.\n\nThe last step is to register the `Portable factory` to the `SerializationConfig`.\n\n```python\nconfig.serialization_config.data_serializable_factories[1] = factory\n```\n\nNote that the ID that is passed to the `SerializationConfig` is same as the factory ID that `Foo` class returns.\n\n### 4.2.1. Versioning for Portable Serialization\n\nMore than one version of the same class may need to be serialized and deserialized. For example, a client may have an older version of a class and the member to which it is connected may have a newer version of the same class.\n\nPortable serialization supports versioning. It is a global versioning, meaning that all portable classes that are serialized through a member get the globally configured portable version.\n\nYou can declare the version in the `hazelcast.xml` configuration file using the `portable-version` element, as shown below.\n\n```xml\n\n ...\n \n 1\n \n ...\n\n```\n\nIf you update the class by changing the type of one of the fields or by adding a new field, it is a good idea to upgrade the version of the class, rather than sticking to the global version specified in the `hazelcast.xml` file.\nIn the Python client, you can achieve this by simply adding the `get_class_version()` method to your class\u2019s implementation of `Portable`, and setting the `CLASS_VERSION` to be different than the default global version.\n\n> **NOTE: If you do not use the `get_class_version()` method in your `Portable` implementation, it will have the global version, by default.**\n\nHere is an example implementation of creating a version 2 for the above Foo class:\n\n```python\nfrom hazelcast.serialization.api import Portable\n\nclass Foo(Portable):\n\n CLASS_ID = 1\n FACTORY_ID = 1\n CLASS_VERSION = 2\n\n def __init__(self, foo=None, foo2=None):\n self.foo = foo \n self.foo2 = foo2\n\n def get_class_id(self):\n return CLASS_ID\n\n def get_factory_id(self):\n return FACTORY_ID\n\n def get_class_version(self):\n return CLASS_VERSION\n\n def write_portable(self, writer):\n writer.write_utf(\"foo\", self.foo)\n writer.write_utf(\"foo2\", self.foo2)\n\n def read_portable(self, reader):\n self.foo = reader.read_utf(\"foo\")\n self.foo2 = reader.read_utf(\"foo2\")\n```\n\nYou should consider the following when you perform versioning:\n\n* It is important to change the version whenever an update is performed in the serialized fields of a class, for example by incrementing the version.\n* If a client performs a Portable deserialization on a field and then that Portable is updated by removing that field on the cluster side, this may lead to problems such as an AttributeError being raised when an older version of the client tries to access the removed field. \n* Portable serialization does not use reflection and hence, fields in the class and in the serialized content are not automatically mapped. Field renaming is a simpler process. Also, since the class ID is stored, renaming the Portable does not lead to problems.\n* Types of fields need to be updated carefully. Hazelcast performs basic type upgradings, such as `int` to `float`.\n\n#### Example Portable Versioning Scenarios:\n\nAssume that a new client joins to the cluster with a class that has been modified and class's version has been upgraded due to this modification.\n\nIf you modified the class by adding a new field, the new client\u2019s put operations include that new field. If this new client tries to get an object that was put from the older clients, it gets null for the newly added field.\n\nIf you modified the class by removing a field, the old clients get null for the objects that are put by the new client.\n\nIf you modified the class by changing the type of a field to an incompatible type (such as from `int` to `String`), a `TypeError` (wrapped as `HazelcastSerializationError`) is generated as the client tries accessing an object with the older version of the class. The same applies if a client with the old version tries to access a new version object.\n\nIf you did not modify a class at all, it works as usual.\n\n## 4.3. Custom Serialization\n\nHazelcast lets you plug a custom serializer to be used for serialization of objects.\n\nLet's say you have a class called `Musician` and you would like to customize the serialization for it, since you may want to use an external serializer for only one class.\n\n```python\nclass Musician:\n def __init__(self, name):\n self.name = name\n```\n\nLet's say your custom `MusicianSerializer` will serialize `Musician`. This time, your custom serializer must extend the `StreamSerializer` class.\n\n```python\nfrom hazelcast.serialization.api import StreamSerializer\n\nclass MusicianSerializer(StreamSerializer):\n def get_type_id(self):\n return 10\n\n def destroy(self):\n pass\n\n def write(self, out, obj):\n out.write_int(len(obj.name))\n for s in obj.name:\n out.write_char(s)\n\n def read(self, inp):\n length = inp.read_int()\n name = \"\"\n for i in range(length):\n name += chr(inp.read_int())\n return Musician(name)\n```\n\nNote that the serializer `id` must be unique as Hazelcast will use it to lookup the `MusicianSerializer` while it deserializes the object. Now the last required step is to register the `MusicianSerializer` to the configuration.\n\n\n```python\nconfig.serialization_config.set_custom_serializer(Musician, MusicianSerializer)\n```\n\nFrom now on, Hazelcast will use `MusicianSerializer` to serialize `Musician` objects.\n\n## 4.4. JSON Serialization\n\nYou can use the JSON formatted strings as objects in Hazelcast cluster. Starting with Hazelcast IMDG 3.12, the JSON serialization is one of the formerly supported serialization methods. Creating JSON objects in the cluster does not require any server side coding and hence you can just send a JSON formatted string object to the cluster and query these objects by fields.\n\nIn order to use JSON serialization, you should use the `HazelcastJsonValue` object for the key or value. Here is an example IMap usage:\n\n`HazelcastJsonValue` is a simple wrapper and identifier for the JSON formatted strings. You can get the JSON string from the `HazelcastJsonValue` object using the `to_string()` method. \n\nYou can construct `HazelcastJsonValue` from strings or JSON serializable Python objects. If a Python object is provided to the constructor, `HazelcastJsonValue` tries to convert it\nto a JSON string. If an error occurs during the conversion, it is raised directly. If a string argument is provided to the constructor, it is used as it is. \n\nNo JSON parsing is performed but it is your responsibility to provide correctly formatted JSON strings. The client will not validate the string, and it will send it to the cluster as it is. If you submit incorrectly formatted JSON strings and, later, if you query those objects, it is highly possible that you will get formatting errors since the server will fail to deserialize or find the query fields.\n\nHere is an example of how you can construct a `HazelcastJsonValue` and put to the map:\n\n```python\n# From JSON string\njson_map.put(\"item1\", HazelcastJsonValue(\"{\\\"age\\\": 4}\"))\n\n# # From JSON serializable object\njson_map.put(\"item2\", HazelcastJsonValue({\"age\": 20}))\n```\n\nYou can query JSON objects in the cluster using the `Predicate`s of your choice. An example JSON query for querying the values whose age is less than 6 is shown below:\n\n```python\n# Get the objects whose age is less than 6\nresult = json_map.values(is_less_than_or_equal_to(\"age\", 6))\nprint(\"Retrieved {} values whose age is less than 6.\".format(len(result)))\nprint(\"Entry is {}\".format(result[0].to_string()))\n```\n\n## 4.5. Global Serialization\n\nThe global serializer is identical to custom serializers from the implementation perspective. The global serializer is registered as a fallback serializer to handle all other objects if a serializer cannot be located for them.\n\nBy default, `cPickle/pickle` serialization is used if the class is not `IdentifiedDataSerializable` or `Portable` or there is no custom serializer for it. When you configure a global serializer, it is used instead of `cPickle/pickle` serialization.\n\n**Use Cases:**\n\n* Third party serialization frameworks can be integrated using the global serializer.\n\n* For your custom objects, you can implement a single serializer to handle all of them.\n\nA sample global serializer that integrates with a third party serializer is shown below.\n\n```python\nimport some_third_party_serializer\nfrom hazelcast.serialization.api import StreamSerializer\n\nclass GlobalSerializer(StreamSerializer):\n def get_type_id(self):\n return 20\n\n def destroy(self):\n pass\n\n def write(self, out, obj):\n out.write_utf(some_third_party_serializer.serialize(obj))\n\n def read(self, inp):\n return some_third_party_serializer.deserialize(inp.read_utf())\n```\n\nYou should register the global serializer in the configuration.\n\n\n```python\nconfig.serialization_config.global_serializer = GlobalSerializer\n```\n\n# 5. Setting Up Client Network\n\nAll network related configuration of Hazelcast Python client is performed via the `ClientNetworkConfig` class when using programmatic configuration. Let's first give the examples for this approach. Then we will look at its sub-elements and attributes.\n\nHere is an example of configuring the network for Python Client programmatically.\n\n```python\nconfig.network_config.addresses.extend([\"10.1.1.21\"\"10.1.1.22:5703\"])\nconfig.network_config.smart_routing = True\nconfig.network_config.redo_operation = True\nconfig.network_config.connection_timeout = 6.0\nconfig.network_config.connection_attempt_period = 5.0\nconfig.network_config.connection_attempt_limit = 5\n```\n\n## 5.1. Providing Member Addresses\n\nAddress list is the initial list of cluster addresses which the client will connect to. The client uses this\nlist to find an alive member. Although it may be enough to give only one address of a member in the cluster\n(since all members communicate with each other), it is recommended that you give the addresses for all the members.\n\n```python\nconfig.network_config.addresses.append(\"10.1.1.21\") # single value\nconfig.network_config.addresses.extend([\"10.1.1.23\", \"10.1.1.22:5703\"]) # multiple values\n```\n\nIf the port part is omitted, then 5701, 5702 and 5703 will be tried in a random order.\n\nYou can specify multiple addresses with or without the port information as seen above. The provided list is shuffled and tried in a random order. Its default value is `localhost`.\n\n## 5.2. Setting Smart Routing\n\nSmart routing defines whether the client mode is smart or unisocket. See the [Python Client Operation Modes section](#72-python-client-operation-modes)\nfor the description of smart and unisocket modes.\n\nThe following is an example configuration.\n\n```python\nconfig.network_config.smart_routing = True\n```\n\nIts default value is `True` (smart client mode).\n\n## 5.3. Enabling Redo Operation\n\nIt enables/disables redo-able operations. While sending the requests to the related members, the operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retry for the other operations, you can set the `redo_operation` to `True`.\n\n```python\nconfig.network_config.redo_operation = True\n```\n\nIts default value is `False` (disabled).\n\n## 5.4. Setting Connection Timeout\n\nConnection timeout is the timeout value in seconds for the members to accept the client connection requests.\nIf the member does not respond within the timeout, the client will retry to connect as many as `ClientNetworkConfig.connection_attempt_limit` times.\n\nThe following is an example configuration.\n\n```python\nconfig.network_config.connection_timeout = 6.0\n```\n\nIts default value is `5.0` seconds.\n\n## 5.5. Setting Connection Attempt Limit\n\nWhile the client is trying to connect initially to one of the members in the `ClientNetworkConfig.addresses`, that member might not be available at that moment. Instead of giving up, throwing an error and stopping the client, the client will retry as many as `ClientNetworkConfig.connection_attempt_limit` times. This is also the case when the previously established connection between the client and that member goes down.\n\nThe following is an example configuration.\n\n```python\nconfig.network_config.connection_attempt_limit = 5\n```\n\nIts default value is `2`.\n\n## 5.6. Setting Connection Attempt Period\n\nConnection attempt period is the duration in seconds between the connection attempts defined by `ClientNetworkConfig.connection_attempt_limit`.\n\nThe following is an example configuration.\n\n```python\nconfig.network_config.connection_attempt_period = 5.0\n```\n\nIts default value is `3.0` seconds.\n\n## 5.7. Enabling Client TLS/SSL\n\nYou can use TLS/SSL to secure the connection between the clients and members. If you want to enable TLS/SSL\nfor the client-cluster connection, you should set the SSL configuration. Please see the [TLS/SSL section](#61-tlsssl).\n\nAs explained in the [TLS/SSL section](#61-tlsssl), Hazelcast members have key stores used to identify themselves (to other members) and Hazelcast Python clients have certificate authorities used to define which members they can trust. Hazelcast has the mutual authentication feature which allows the Python clients also to have their private keys and public certificates, and members to have their certificate authorities so that the members can know which clients they can trust. See the [Mutual Authentication section](#613-mutual-authentication).\n\n## 5.8. Enabling Hazelcast Cloud Discovery\n\nThe purpose of Hazelcast Cloud Discovery is to provide the clients the means to use IP addresses provided by `hazelcast orchestrator`. To enable Hazelcast Cloud Discovery, specify a token for the `discovery_token` field and set the `enabled` field to `True`.\n\nThe following is the example configuration.\n\n```python\nconfig.group_config.name = \"hazel\"\nconfig.group_config.password = \"cast\"\n\nconfig.network_config.ssl_config.enabled = True\n\nconfig.network_config.cloud_config.enabled = True\nconfig.network_config.cloud_config.discovery_token = \"dc9220bc5d9\"\n```\n\nTo be able to connect to the provided IP addresses, you should use secure TLS/SSL connection between the client and members. Therefore, you should enable the SSL configuration as described in the [TLS/SSL for Hazelcast Python Client section](#612-tlsssl-for-hazelcast-python-clients).\n\n# 6. Securing Client Connection\n\nThis chapter describes the security features of Hazelcast Python client. These include using TLS/SSL for connections between members and between clients and members, and mutual authentication. These security features require **Hazelcast IMDG Enterprise** edition.\n\n### 6.1. TLS/SSL\n\nOne of the offers of Hazelcast is the TLS/SSL protocol which you can use to establish an encrypted communication across your cluster with key stores and trust stores.\n\n* A Java `keyStore` is a file that includes a private key and a public certificate. The equivalent of a key store is the combination of `keyfile` and `certfile` at the Python client side.\n\n* A Java `trustStore` is a file that includes a list of certificates trusted by your application which is named certificate authority. The equivalent of a trust store is a `cafile` at the Python client side.\n\nYou should set `keyStore` and `trustStore` before starting the members. See the next section on how to set `keyStore` and `trustStore` on the server side.\n\n#### 6.1.1. TLS/SSL for Hazelcast Members\n\nHazelcast allows you to encrypt socket level communication between Hazelcast members and between Hazelcast clients and members, for end to end encryption. To use it, see the [TLS/SSL for Hazelcast Members section](http://docs.hazelcast.org/docs/latest/manual/html-single/index.html#tls-ssl-for-hazelcast-members).\n\n#### 6.1.2. TLS/SSL for Hazelcast Python Clients\n\nTLS/SSL for the Hazelcast Python client can be configured using the `SSLConfig` class. Let's first give an example of a sample configuration and then go over the configuration options one by one:\n\n```python\nimport hazelcast\nfrom hazelcast.config import PROTOCOL\n\nconfig = hazelcast.ClientConfig()\nconfig.network_config.ssl_config.enabled = True\nconfig.network_config.ssl_config.cafile = \"/home/hazelcast/cafile.pem\"\nconfig.network_config.ssl_config.certfile = \"/home/hazelcast/certfile.pem\"\nconfig.network_config.ssl_config.keyfile = \"/home/hazelcast/keyfile.pem\"\nconfig.network_config.ssl_config.password = \"hazelcast\"\nconfig.network_config.ssl_config.protocol = PROTOCOL.TLSv1_3\nconfig.network_config.ssl_config.ciphers = \"DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA\"\n```\n\n##### Enabling TLS/SSL\n\nTLS/SSL for the Hazelcast Python client can be enabled/disabled using the `enabled` option. When this option is set to `True`, TLS/SSL will be configured with respect to the other `SSLConfig` options. \nSetting this option to `False` will result in discarding the other `SSLConfig` options.\n\nThe following is an example configuration:\n\n```python\nconfig.network_config.ssl_config.enabled = True\n```\n\nDefault value is `False` (disabled). \n\n##### Setting CA File\n\nCertificates of the Hazelcast members can be validated against `cafile`. This option should point to the absolute path of the concatenated CA certificates in PEM format. \nWhen SSL is enabled and `cafile` is not set, a set of default CA certificates from default locations will be used.\n\nThe following is an example configuration:\n\n```python\nconfig.network_config.ssl_config.cafile = \"/home/hazelcast/cafile.pem\"\n```\n\n##### Setting Client Certificate\n\nWhen mutual authentication is enabled on the member side, clients or other members should also provide a certificate file that identifies themselves.\nThen, Hazelcast members can use these certificates to validate the identity of their peers. \n\nClient certificate can be set using the `certfile`. This option should point to the absolute path of the client certificate in PEM format.\n\nThe following is an example configuration:\n\n```python\nconfig.network_config.ssl_config.certfile = \"/home/hazelcast/certfile.pem\"\n```\n\n##### Setting Private Key\n\nPrivate key of the `certfile` can be set using the `keyfile`. This option should point to the absolute path of the private key file for the client certificate in the PEM format.\n\nIf this option is not set, private key will be taken from `certfile`. In this case, `certfile` should be in the following format.\n\n```\n-----BEGIN RSA PRIVATE KEY-----\n... (private key in base64 encoding) ...\n-----END RSA PRIVATE KEY-----\n-----BEGIN CERTIFICATE-----\n... (certificate in base64 PEM encoding) ...\n-----END CERTIFICATE-----\n```\n\nThe following is an example configuration:\n\n```python\nconfig.network_config.ssl_config.keyfile = \"/home/hazelcast/keyfile.pem\"\n```\n\n##### Setting Password of the Private Key\n\nIf the private key is encrypted using a password, `password` will be used to decrypt it. The `password` may be a function to call to get the password.\nIn that case, it will be called with no arguments, and it should return a string, bytes or bytearray. If the return value is a string it will be encoded as UTF-8 before using it to decrypt the key.\n\nAlternatively a string, bytes or bytearray value may be supplied directly as the password.\n\nThe following is an example configuration:\n\n```python\nconfig.network_config.ssl_config.password = \"hazelcast\"\n```\n\n##### Setting the Protocol\n\n`protocol` can be used to select the protocol that will be used in the TLS/SSL communication. Hazelcast Python client offers the following protocols:\n\n* **SSLv2** : SSL 2.0 Protocol. *RFC 6176 prohibits the usage of SSL 2.0.* \n* **SSLv3** : SSL 3.0 Protocol. *RFC 7568 prohibits the usage of SSL 3.0.*\n* **SSL** : Alias for SSL 3.0\n* **TLSv1** : TLS 1.0 Protocol described in RFC 2246\n* **TLSv1_1** : TLS 1.1 Protocol described in RFC 4346\n* **TLSv1_2** : TLS 1.2 Protocol described in RFC 5246\n* **TLSv1_3** : TLS 1.3 Protocol described in RFC 8446\n* **TLS** : Alias for TLS 1.2\n\n> Note that TLSv1+ requires at least Python 2.7.9 or Python 3.4 built with OpenSSL 1.0.1+, and TLSv1_3 requires at least Python 2.7.15 or Python 3.7 built with OpenSSL 1.1.1+.\n\nThese protocol versions can be selected using the `hazelcast.config.PROTOCOL` as follows:\n\n```python\nfrom hazelcast.config import PROTOCOL\n\nconfig.network_config.ssl_config.protocol = PROTOCOL.TLSv1_3\n``` \n\n> Note that the Hazelcast Python client and the Hazelcast members should have the same protocol version in order for TLS/SSL to work. In case of the protocol mismatch, connection attempts will be refused.\n\nDefault value is `PROTOCOL.TLS` which is an alias for `PROTOCOL.TLSv1_2`.\n\n##### Setting Cipher Suites\n\nCipher suites that will be used in the TLS/SSL communication can be set using the `ciphers` option. Cipher suites should be in the\nOpenSSL cipher list format. More than one cipher suite can be set by separating them with a colon.\n\nTLS/SSL implementation will honor the cipher suite order. So, Hazelcast Python client will offer the ciphers to the Hazelcast members with the given order. \n\nNote that, when this option is not set, all the available ciphers will be offered to the Hazelcast members with their default order.\n\nThe following is an example configuration:\n\n```python\nconfig.network_config.ssl_config.ciphers = \"DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA\"\n``` \n\n#### 6.1.3. Mutual Authentication\n\nAs explained above, Hazelcast members have key stores used to identify themselves (to other members) and Hazelcast clients have trust stores used to define which members they can trust.\n\nUsing mutual authentication, the clients also have their key stores and members have their trust stores so that the members can know which clients they can trust.\n\nTo enable mutual authentication, firstly, you need to set the following property on the server side in the `hazelcast.xml` file:\n\n```xml\n\n \n \n REQUIRED\n \n \n\n```\n\nYou can see the details of setting mutual authentication on the server side in the [Mutual Authentication section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#mutual-authentication) of the Hazelcast IMDG Reference Manual.\n\nOn the client side, you have to provide `SSLConfig.cafile`, `SSLConfig.certfile` and `SSLConfig.keyfile` on top of the other TLS/SSL configurations. See the [TLS/SSL for Hazelcast Python Clients](#612-tlsssl-for-hazelcast-python-clients) for the details of these options.\n\n\n# 7. Using Python Client with Hazelcast IMDG\n\nThis chapter provides information on how you can use Hazelcast IMDG's data structures in the Python client, after giving some basic information including an overview to the client API, operation modes of the client and how it handles the failures.\n\n\n## 7.1. Python Client API Overview\n\nHazelcast Python client is designed to be fully asynchronous. See the [Basic Usage section](#15-basic-usage) to learn more about asynchronous nature of the Python Client.\n\nIf you are ready to go, let's start to use Hazelcast Python client.\n\nThe first step is configuration. You can configure the Python client programmatically. \n\nThe following is an example on how to create a `ClientConfig` object and configure it programmatically:\n\n```python\nimport hazelcast\n\nconfig = hazelcast.ClientConfig()\nconfig.group_config.name = \"dev\"\nconfig.network_config.addresses.append(\"10.90.0.1\")\n```\n\nThe second step is initializing the `HazelcastClient` to be connected to the cluster:\n\n```python\nclient = hazelcast.HazelcastClient(config)\n```\n\n**This client object is your gateway to access all the Hazelcast distributed objects.**\n\nLet's create a map and populate it with some data, as shown below.\n\n```python\ncustomer_map = client.get_map(\"customers\").blocking()\ncustomer_map.put(\"1\", \"John Stiles\")\ncustomer_map.put(\"2\", \"Richard Miles\")\ncustomer_map.put(\"3\", \"Judy Doe\")\n```\n\nAs the final step, if you are done with your client, you can shut it down as shown below. This will release all the used resources and close connections to the cluster.\n\n```python\nclient.shutdown()\n```\n\n## 7.2. Python Client Operation Modes\n\nThe client has two operation modes because of the distributed nature of the data and cluster: smart and unisocket.\n\n### 7.2.1. Smart Client\n\nIn the smart mode, the clients connect to each cluster member. Since each data partition uses the well known and consistent hashing algorithm, each client can send an operation to the relevant cluster member, which increases the overall throughput and efficiency. Smart mode is the default mode.\n\n### 7.2.2. Unisocket Client\n\nFor some cases, the clients can be required to connect to a single member instead of each member in the cluster. Firewalls, security or some custom networking issues can be the reason for these cases.\n\nIn the unisocket client mode, the client will only connect to one of the configured addresses. This single member will behave as a gateway to the other members. For any operation requested from the client, it will redirect the request to the relevant member and return the response back to the client returned from this member.\n\n## 7.3. Handling Failures\n\nThere are two main failure cases you should be aware of. Below sections explain these and the configurations you can perform to achieve proper behavior.\n\n### 7.3.1. Handling Client Connection Failure\n\nWhile the client is trying to connect initially to one of the members in the `ClientNetworkConfig.addresses`, all the members might not be available. Instead of giving up, throwing an error and stopping the client, the client will retry as many as `connection_attempt_limit` times. \n\nYou can configure `connection_attempt_limit` for the number of times you want the client to retry connecting. See the [Setting Connection Attempt Limit section](#55-setting-connection-attempt-limit).\n\nThe client executes each operation through the already established connection to the cluster. If this connection(s) disconnects or drops, the client will try to reconnect as configured.\n\n### 7.3.2. Handling Retry-able Operation Failure\n\nWhile sending the requests to the related members, the operations can fail due to various reasons. Read-only operations are retried by default. If you want to enable retrying for the other operations, you can set the `redo_operation` to `True`. See the [Enabling Redo Operation section](#53-enabling-redo-operation).\n\nYou can set a timeout for retrying the operations sent to a member. This can be provided by using the property `hazelcast.client.invocation.timeout.seconds` via `ClientConfig.set_property` method. The client will retry an operation within this given period, of course, if it is a read-only operation or you enabled the `redo_operation` as stated in the above paragraph. This timeout value is important when there is a failure resulted by either of the following causes:\n\n* Member throws an exception.\n\n* Connection between the client and member is closed.\n\n* Client\u2019s heartbeat requests are timed out.\n\nWhen a connection problem occurs, an operation is retried if it is certain that it has not run on the member yet or if it is idempotent such as a read-only operation, i.e., retrying does not have a side effect. If it is not certain whether the operation has run on the member, then the non-idempotent operations are not retried. However, as explained in the first paragraph of this section, you can force all the client operations to be retried (`redo_operation`) when there is a connection failure between the client and member. But in this case, you should know that some operations may run multiple times causing conflicts. For example, assume that your client sent a `queue.offer` operation to the member and then the connection is lost. Since there will be no response for this operation, you will not know whether it has run on the member or not. If you enabled `redo_operation`, it means this operation may run again, which may cause two instances of the same object in the queue.\n\nWhen invocation is being retried, the client may wait some time before it retries again. This duration can be configured using the following property:\n\n ```python\nconfig.set_property(\"hazelcast.client.invocation.retry.pause.millis\", 500)\n```\n\nThe default retry wait time is `1` second.\n\n## 7.4. Using Distributed Data Structures\n\nMost of the distributed data structures are supported by the Python client. In this chapter, you will learn how to use these distributed data structures.\n\n### 7.4.1. Using Map\n\nHazelcast Map is a distributed dictionary. Through the Python client, you can perform operations like reading and writing from/to a Hazelcast Map with the well known get and put methods. For details, see the [Map section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#map) in the Hazelcast IMDG Reference Manual.\n\nA Map usage example is shown below.\n\n```python\n# Get the Distributed Map from Cluster.\nmy_map = client.get_map(\"my-distributed-map\").blocking()\n\n# Standard Put and Get\nmy_map.put(\"key\", \"value\")\nmy_map.get(\"key\")\n\n# Concurrent Map methods, optimistic updating\nmy_map.put_if_absent(\"somekey\", \"somevalue\") \nmy_map.replace_if_same(\"key\", \"value\", \"newvalue\")\n```\n\n### 7.4.2. Using MultiMap\n\nHazelcast MultiMap is a distributed and specialized map where you can store multiple values under a single key. For details, see the [MultiMap section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#multimap) in the Hazelcast IMDG Reference Manual.\n\nA MultiMap usage example is shown below.\n\n```python\n# Get the Distributed MultiMap from Cluster.\nmulti_map = client.get_multi_map(\"my-distributed-multimap\").blocking()\n\n# Put values in the map against the same key\nmulti_map.put(\"my-key\", \"value1\")\nmulti_map.put(\"my-key\", \"value2\")\nmulti_map.put(\"my-key\", \"value3\")\n\n# Print out all the values for associated with key called \"my-key\"\n# Outputs '['value2', 'value1', 'value3']'\nvalues = multi_map.get(\"my-key\") \nprint(values) \n\n# Remove specific key/value pair\nmulti_map.remove(\"my-key\", \"value2\") \n```\n\n### 7.4.3. Using Replicated Map\n\nHazelcast Replicated Map is a distributed key-value data structure where the data is replicated to all members in the cluster. It provides full replication of entries to all members for high speed access. For details, see the [Replicated Map section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#replicated-map) in the Hazelcast IMDG Reference Manual.\n\nA Replicated Map usage example is shown below.\n\n```python\n# Get a Replicated Map called \"my-replicated-map\"\nreplicated_map = client.get_replicated_map(\"my-replicated-map\").blocking()\n\n# Put and Get a value from the Replicated Map\n# key/value replicated to all members\nreplaced_value = replicated_map.put(\"key\", \"value\") \n\n# Will be None as its first update\nprint(\"replaced value = {}\".format(replaced_value)) # Outputs 'replaced value = None'\n\n# The value is retrieved from a random member in the cluster\nvalue = replicated_map.get(\"key\")\n\nprint(\"value for key = {}\".format(value)) # Outputs 'value for key = value'\n```\n\n### 7.4.4. Using Queue\n\nHazelcast Queue is a distributed queue which enables all cluster members to interact with it. For details, see the [Queue section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#queue) in the Hazelcast IMDG Reference Manual.\n\nA Queue usage example is shown below.\n\n```python\n# Get a Blocking Queue called \"my-distributed-queue\"\nqueue = client.get_queue(\"my-distributed-queue\").blocking()\n\n# Offer a String into the Distributed Queue\nqueue.offer(\"item\")\n\n# Poll the Distributed Queue and return the String\nitem = queue.poll()\n\n# Timed blocking Operations\nqueue.offer(\"another-item\", 1)\nanother_item = queue.poll(5)\n\n# Indefinitely blocking Operations\nqueue.put(\"yet-another-item\")\n\nprint(queue.take()) # Outputs 'yet-another-item'\n```\n\n### 7.4.5. Using Set\n\nHazelcast Set is a distributed set which does not allow duplicate elements. For details, see the [Set section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#set) in the Hazelcast IMDG Reference Manual.\n\nA Set usage example is shown below.\n\n```python\n# Get the Distributed Set from Cluster.\nmy_set = client.get_set(\"my-distributed-set\").blocking()\n\n# Add items to the set with duplicates\nmy_set.add(\"item1\")\nmy_set.add(\"item1\")\nmy_set.add(\"item2\")\nmy_set.add(\"item2\")\nmy_set.add(\"item2\")\nmy_set.add(\"item3\")\n\n# Get the items. Note that there are no duplicates.\nfor item in my_set.get_all():\n print(item)\n```\n\n### 7.4.6. Using List\n\nHazelcast List is a distributed list which allows duplicate elements and preserves the order of elements. For details, see the [List section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#list) in the Hazelcast IMDG Reference Manual.\n\nA List usage example is shown below.\n\n```python\n# Get the Distributed List from Cluster.\nmy_list = client.get_list(\"my-distributed-list\").blocking()\n\n# Add element to the list\nmy_list.add(\"item1\")\nmy_list.add(\"item2\")\n\n# Remove the first element\nprint(\"Removed: {}\".format(my_list.remove_at(0))) # Outputs 'Removed: item1'\n\n# There is only one element left\nprint(\"Current size is {}\".format(my_list.size())) # Outputs 'Current size is 1'\n\n# Clear the list\nmy_list.clear()\n```\n\n### 7.4.7. Using Ringbuffer\n\nHazelcast Ringbuffer is a replicated but not partitioned data structure that stores its data in a ring-like structure. You can think of it as a circular array with a given capacity. Each Ringbuffer has a tail and a head. The tail is where the items are added and the head is where the items are overwritten or expired. You can reach each element in a Ringbuffer using a sequence ID, which is mapped to the elements between the head and tail (inclusive) of the Ringbuffer. For details, see the [Ringbuffer section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#ringbuffer) in the Hazelcast IMDG Reference Manual.\n\nA Ringbuffer usage example is shown below.\n\n```python\n# Get a RingBuffer called \"my-ringbuffer\"\nringbuffer = client.get_ringbuffer(\"my-ringbuffer\").blocking()\n\n# Add two items into ring buffer\nringbuffer.add(100)\nringbuffer.add(200)\n\n# We start from the oldest item.\n# If you want to start from the next item, call ringbuffer.tail_sequence()+1\nsequence = ringbuffer.head_sequence()\nprint(ringbuffer.read_one(sequence)) # Outputs '100'\n\nsequence += 1\nprint(ringbuffer.read_one(sequence)) # Outputs '200'\n```\n\n### 7.4.8. Using Topic\n\nHazelcast Topic is a distribution mechanism for publishing messages that are delivered to multiple subscribers. For details, see the [Topic section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#topic) in the Hazelcast IMDG Reference Manual.\n\nA Topic usage example is shown below.\n\n```python\n# Function to be called when a message is published\ndef print_on_message(topic_message):\n print(\"Got message:\", topic_message.message)\n\n# Get a Topic called \"my-distributed-topic\"\ntopic = client.get_topic(\"my-distributed-topic\")\n\n# Add a Listener to the Topic\ntopic.add_listener(print_on_message)\n\n# Publish a message to the Topic\ntopic.publish(\"Hello to distributed world\") # Outputs 'Got message: Hello to distributed world'\n```\n\n### 7.4.9. Using Lock\n\nHazelcast Lock is a distributed lock implementation. You can synchronize Hazelcast members and clients using a `Lock`. For details, see the [Lock section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#lock) in the Hazelcast IMDG Reference Manual.\n\nA Lock usage example is shown below.\n\n```python\n# Get a distributed lock called \"my-distributed-lock\"\nlock = client.get_lock(\"my-distributed-lock\").blocking()\n\n# Now create a lock and execute some guarded code\nlock.lock()\ntry:\n # Do something here\n pass\nfinally:\n lock.unlock()\n```\n\n### 7.4.10. Using Atomic Long\n\nHazelcast Atomic Long is the distributed long which offers most of the operations such as `get`, `set`, `get_and_set`, `compare_and_set` and `increment_and_get`. For details, see the [Atomic Long section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#iatomiclong) in the Hazelcast IMDG Reference Manual.\n\nAn Atomic Long usage example is shown below.\n\n```python\n# Get an Atomic Counter, we'll call it \"counter\"\ncounter = client.get_atomic_long(\"counter\").blocking()\n\n# Add and Get the \"counter\"\ncounter.add_and_get(3) # value is 3\n\n# Display the \"counter\" value\nprint(\"counter: {}\".format(counter.get())) # Outputs 'counter: 3'\n```\n\n### 7.4.11. Using Semaphore\n\nHazelcast Semaphore is a distributed semaphore implementation. For details, see the [Semaphore section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#isemaphore) in the Hazelcast IMDG Reference Manual.\n\nA Semaphore usage example is shown below.\n\n```python\n# Get a Semaphore called \"my-distributed-semaphore\"\nsemaphore = client.get_semaphore(\"my-distributed-semaphore\").blocking()\n\n# Initialize the Semaphore with 10 permits\nsemaphore.init(10)\n\n# Acquire 5 permits\nsemaphore.acquire(5)\n\n# Print the number of the available permits\nprint(semaphore.available_permits()) # Outputs '5'\n```\n\n### 7.4.12. Using Transactions\n\nHazelcast Python client provides transactional operations like beginning transactions, committing transactions and retrieving transactional data structures like the `TransactionalMap`, `TransactionalSet`, `TransactionalList`, `TransactionalQueue` and `TransactionalMultiMap`.\n\nYou can create a `Transaction` object using the Python client to begin, commit and rollback a transaction. You can obtain transaction-aware instances of queues, maps, sets, lists and multimaps via the `Transaction` object, work with them and commit or rollback in one shot. For details, see the [Transactions section](https://docs.hazelcast.org//docs/latest/manual/html-single/index.html#transactions) in the Hazelcast IMDG Reference Manual.\n\n```python\n# Create a Transaction object and begin the transaction\ntransaction = client.new_transaction(timeout=10)\ntransaction.begin()\n\n# Get transactional distributed data structures\ntxn_map = transaction.get_map(\"transactional-map\")\ntxn_queue = transaction.get_queue(\"transactional-queue\")\ntxt_set = transaction.get_set(\"transactional-set\")\ntry:\n obj = txn_queue.poll()\n\n # Process obj\n\n txn_map.put(\"1\", \"value1\")\n txt_set.add(\"value\")\n\n # Do other things\n\n # Commit the above changes done in the cluster.\n transaction.commit() \nexcept Exception as ex:\n # In the case of a transactional failure, rollback the transaction \n transaction.rollback()\n print(\"Transaction failed! {}\".format(ex.args))\n```\nIn a transaction, operations will not be executed immediately. Their changes will be local to the `Transaction` object until committed. However, they will ensure the changes via locks.\n\nFor the above example, when `txn_map.put()` is executed, no data will be put in the map but the key will be locked against changes. While committing, operations will be executed, the value will be put to the map and the key will be unlocked.\n\nThe isolation level in Hazelcast Transactions is `READ_COMMITTED` on the level of a single partition. If you are in a transaction, you can read the data in your transaction and the data that is already committed. If you are not in a transaction, you can only read the committed data.\n\n### 7.4.13. Using PN Counter\n\nHazelcast `PNCounter` (Positive-Negative Counter) is a CRDT positive-negative counter implementation. It is an eventually consistent counter given there is no member failure. For details, see the [PN Counter section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#pn-counter) in the Hazelcast IMDG Reference Manual.\n\nA PN Counter usage example is shown below.\n\n```python\n# Get a PN Counter called 'pn-counter'\npn_counter = client.get_pn_counter(\"pn-counter\").blocking()\n\n# Counter is initialized with 0\nprint(pn_counter.get()) # 0\n\n# .._and_get() variants does the operation\n# and returns the final value\nprint(pn_counter.add_and_get(5)) # 5\nprint(pn_counter.decrement_and_get()) # 4\n\n# get_and_..() variants returns the current \n# value and then does the operation\nprint(pn_counter.get_and_increment()) # 4\nprint(pn_counter.get()) # 5\n```\n\n### 7.4.14. Using Flake ID Generator\n\nHazelcast `FlakeIdGenerator` is used to generate cluster-wide unique identifiers. Generated identifiers are long \nprimitive values and are k-ordered (roughly ordered). IDs are in the range from 0 to `2^63-1` (maximum signed long value). \nFor details, see the [FlakeIdGenerator section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#flakeidgenerator) in the \nHazelcast IMDG Reference Manual.\n\n```python\n# Get a Flake ID Generator called 'flake-id-generator'\ngenerator = client.get_flake_id_generator(\"flake-id-generator\").blocking()\n\n# Generate a some unique identifier\nprint(\"ID: {}\".format(generator.new_id()))\n```\n\n## 7.5. Distributed Events\n\nThis chapter explains when various events are fired and describes how you can add event listeners on a Hazelcast Python client. These events can be categorized as cluster and distributed data structure events.\n\n### 7.5.1. Cluster Events\n\nYou can add event listeners to a Hazelcast Python client. You can configure the following listeners to listen to the events on the client side:\n\n* Membership Listener: Notifies when a member joins to/leaves the cluster.\n\n* Lifecycle Listener: Notifies when the client is starting, started, shutting down and shutdown.\n\n#### 7.5.1.1. Listening for Member Events\n\nYou can add the following types of member events to the `ClusterService`.\n\n* `member_added`: A new member is added to the cluster.\n* `member_removed`: An existing member leaves the cluster.\n\nThe `ClusterService` class exposes an `add_listener()` method that allows one or more functions to be attached to the member events emitted by the class.\n\nThe following is a membership listener registration by using the `add_listener()` method.\n\n```python\nclient.cluster.add_listener(member_added=lambda m: print(\"Member Added: The address is {}\".format(m.address)))\n```\n\n#### 7.5.1.2. Listening for Lifecycle Events\n\nThe `Lifecycle Listener` notifies for the following events:\n\n* `STARTING`: The client is starting.\n* `CONNECTED`: The client is connected.\n* `SHUTTING_DOWN`: The client is shutting down.\n* `SHUTDOWN`: The client\u2019s shutdown has completed.\n\nThe following is an example of the `Lifecycle listener` that is added to the `ClientConfig` object and its output.\n\n```python\nconfig.add_lifecycle_listener(lambda s: print(\"Lifecycle Event >>> {}\".format(s)))\n\nclient = hazelcast.HazelcastClient(config)\n\nclient.shutdown()\n```\n\n**Output:**\n\n```\nLifecycle Event >>> STARTING\nLifecycle Event >>> CONNECTED\nLifecycle Event >>> SHUTTING_DOWN\nLifecycle Event >>> SHUTDOWN\n```\n\n### 7.5.2. Distributed Data Structure Events\n\nYou can add event listeners to the distributed data structures.\n\n#### 7.5.2.1. Listening for Map Events\n\nYou can listen to map-wide or entry-based events by attaching functions to the `Map` objects using the `add_entry_listener()` method. You can listen the following events.\n\n* `added_func` : Function to be called when an entry is added to map.\n* `removed_func` : Function to be called when an entry is removed from map.\n* `updated_func` : Function to be called when an entry is updated. \n* `evicted_func` : Function to be called when an entry is evicted from map.\n* `evict_all_func` : Function to be called when entries are evicted from map.\n* `clear_all_func` : Function to be called when entries are cleared from map.\n* `merged_func` : Function to be called when WAN replicated entry is merged.\n* `expired_func` : Function to be called when an entry's live time is expired.\n\nYou can also filter the events using `key` or `predicate`. There is also an option called `include_value`. When this option is set to true, event will also include the value.\n\nAn entry-based event is fired after the operations that affect a specific entry. For example, `Map.put()`, `Map.remove()` or `Map.evict()`. An `EntryEvent` object is passed to the listener function.\n\nSee the following example.\n\n```python\ndef added(event):\n print(\"Entry Added: {}-{}\".format(event.key, event.value))\n\ncustomer_map.add_entry_listener(include_value=True, added_func=added)\ncustomer_map.put(\"4\", \"Jane Doe\").result() # Outputs 'Entry Added: 4-Jane Doe'\n```\n\nA map-wide event is fired as a result of a map-wide operation. For example, `Map.clear()` or `Map.evict_all()`. An `EntryEvent` object is passed to the listener function.\n\nSee the following example.\n\n```python\ndef cleared(event):\n print(\"Map Cleared: {}\".format(event.number_of_affected_entries))\n\ncustomer_map.add_entry_listener(include_value=True, clear_all_func=cleared)\ncustomer_map.clear().result() # Outputs 'Map Cleared: 4'\n```\n\n## 7.6. Distributed Computing\n\nThis chapter explains how you can use Hazelcast IMDG's entry processor implementation in the Python client.\n\n### 7.6.1. Using EntryProcessor\n\nHazelcast supports entry processing. An entry processor is a function that executes your code on a map entry in an atomic way.\n\nAn entry processor is a good option if you perform bulk processing on a `Map`. Usually you perform a loop of keys -- executing `Map.get(key)`, mutating the value, and finally putting the entry back in the map using `Map.put(key,value)`. If you perform this process from a client or from a member where the keys do not exist, you effectively perform two network hops for each update: the first to retrieve the data and the second to update the mutated value.\n\nIf you are doing the process described above, you should consider using entry processors. An entry processor executes a read and updates upon the member where the data resides. This eliminates the costly network hops described above.\n\n> **NOTE: Entry processor is meant to process a single entry per call. Processing multiple entries and data structures in an entry processor is not supported as it may result in deadlocks on the server side.**\n\nHazelcast sends the entry processor to each cluster member and these members apply it to the map entries. Therefore, if you add more members, your processing completes faster.\n\n#### Processing Entries\n\nThe `Map` class provides the following methods for entry processing:\n\n* `execute_on_key` processes an entry mapped by a key.\n\n* `execute_on_keys` processes entries mapped by a list of keys.\n\n* `execute_on_entries` can process all entries in a map with a defined predicate. Predicate is optional.\n\nIn the Python client, an `EntryProcessor` should be `IdentifiedDataSerializable` or `Portable` because the server should be able to deserialize it to process.\n\nThe following is an example for `EntryProcessor` which is an `IdentifiedDataSerializable`.\n\n```python\nfrom hazelcast.serialization.api import IdentifiedDataSerializable\n\nclass IdentifiedEntryProcessor(IdentifiedDataSerializable):\n def __init__(self, value=None):\n self.value = value\n\n def read_data(self, object_data_input):\n self.value = object_data_input.read_utf()\n\n def write_data(self, object_data_output):\n object_data_output.write_utf(self.value)\n\n def get_factory_id(self):\n return 5\n\n def get_class_id(self):\n return 1\n```\n\nNow, you need to make sure that the Hazelcast member recognizes the entry processor. For this, you need to implement the Java equivalent of your entry processor and its factory, and create your own compiled class or JAR files. For adding your own compiled class or JAR files to the server's `CLASSPATH`, see the [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath).\n\nThe following is the Java equivalent of the entry processor in Python client given above:\n\n```java\nimport com.hazelcast.map.AbstractEntryProcessor;\nimport com.hazelcast.nio.ObjectDataInput;\nimport com.hazelcast.nio.ObjectDataOutput;\nimport com.hazelcast.nio.serialization.IdentifiedDataSerializable;\nimport java.io.IOException;\nimport java.util.Map;\n\npublic class IdentifiedEntryProcessor extends AbstractEntryProcessor implements IdentifiedDataSerializable {\n static final int CLASS_ID = 1;\n private String value;\n\n public IdentifiedEntryProcessor() {\n }\n\n @Override\n public int getFactoryId() {\n return IdentifiedFactory.FACTORY_ID;\n }\n\n @Override\n public int getId() {\n return CLASS_ID;\n }\n\n @Override\n public void writeData(ObjectDataOutput out) throws IOException {\n out.writeUTF(value);\n }\n\n @Override\n public void readData(ObjectDataInput in) throws IOException {\n value = in.readUTF();\n }\n\n @Override\n public Object process(Map.Entry entry) {\n entry.setValue(value);\n return value;\n }\n}\n```\n\nYou can implement the above processor\u2019s factory as follows:\n\n```java\nimport com.hazelcast.nio.serialization.DataSerializableFactory;\nimport com.hazelcast.nio.serialization.IdentifiedDataSerializable;\n\npublic class IdentifiedFactory implements DataSerializableFactory {\n public static final int FACTORY_ID = 5;\n\n @Override\n public IdentifiedDataSerializable create(int typeId) {\n if (typeId == IdentifiedEntryProcessor.CLASS_ID) {\n return new IdentifiedEntryProcessor();\n }\n return null;\n }\n}\n```\n\nNow you need to configure the `hazelcast.xml` to add your factory as shown below.\n\n```xml\n\n \n \n \n IdentifiedFactory\n \n \n \n\n```\n\nThe code that runs on the entries is implemented in Java on the server side. The client side entry processor is used to specify which entry processor should be called. For more details about the Java implementation of the entry processor, see the [Entry Processor section](https://docs.hazelcast.org/docs/latest/manual/html-single/index.html#entry-processor) in the Hazelcast IMDG Reference Manual.\n\nAfter the above implementations and configuration are done and you start the server where your library is added to its `CLASSPATH`, you can use the entry processor in the `Map` methods. See the following example.\n\n```python\ndistributed_map = client.get_map(\"my-distributed-map\").blocking()\n\ndistributed_map.put(\"key\", \"not-processed\")\ndistributed_map.execute_on_key(\"key\", IdentifiedEntryProcessor(\"processed\"))\n\nprint(distributed_map.get(\"key\")) # Outputs 'processed'\n```\n\n## 7.7. Distributed Query\n\nHazelcast partitions your data and spreads it across cluster of members. You can iterate over the map entries and look for certain entries (specified by predicates) you are interested in. However, this is not very efficient because you will have to bring the entire entry set and iterate locally. Instead, Hazelcast allows you to run distributed queries on your distributed map.\n\n### 7.7.1. How Distributed Query Works\n\n1. The requested predicate is sent to each member in the cluster.\n2. Each member looks at its own local entries and filters them according to the predicate. At this stage, key-value pairs of the entries are deserialized and then passed to the predicate.\n3. The predicate requester merges all the results coming from each member into a single set.\n\nDistributed query is highly scalable. If you add new members to the cluster, the partition count for each member is reduced and thus the time spent by each member on iterating its entries is reduced. In addition, the pool of partition threads evaluates the entries concurrently in each member, and the network traffic is also reduced since only filtered data is sent to the requester.\n\n**Predicate Module Operators**\n\nThe `Predicate` module offered by the Python client includes many operators for your query requirements. Some of them are explained below.\n\n* `is_equal_to`: Checks if the result of an expression is equal to a given value.\n* `is_not_equal_to`: Checks if the result of an expression is not equal to a given value.\n* `is_instance_of`: Checks if the result of an expression has a certain type.\n* `is_like`: Checks if the result of an expression matches some string pattern. `%` (percentage sign) is the placeholder for many characters, `_` (underscore) is placeholder for only one character.\n* `is_ilike`: Checks if the result of an expression matches some string pattern in a case-insensitive manner.\n* `is_greater_than`: Checks if the result of an expression is greater than a certain value.\n* `is_greater_than_or_equal_to`: Checks if the result of an expression is greater than or equal to a certain value.\n* `is_less_than`: Checks if the result of an expression is less than a certain value.\n* `is_less_than_or_equal_to`: Checks if the result of an expression is less than or equal to a certain value.\n* `is_between`: Checks if the result of an expression is between two values (this is inclusive).\n* `is_in`: Checks if the result of an expression is an element of a certain list.\n* `is_not`: Checks if the result of an expression is false.\n* `matches_regex`: Checks if the result of an expression matches some regular expression.\n* `true`: Creates an always true predicate that will pass all items.\n* `false`: Creates an always false predicate that will filter out all items.\n\nHazelcast offers the following ways for distributed query purposes:\n\n* Combining Predicates with AND, OR, NOT\n\n* Distributed SQL Query\n\n#### 7.7.1.1. Employee Map Query Example\n\nAssume that you have an `employee` map containing the instances of `Employee` class, as coded below. \n\n```python\nfrom hazelcast.serialization.api import Portable\n\nclass Employee(Portable):\n def __init__(self, name=None, age=None, active=None, salary=None):\n self.name = name\n self.age = age\n self.active = active\n self.salary = salary\n\n def get_class_id(self):\n return 100\n\n def get_factory_id(self):\n return 1000\n\n def read_portable(self, reader):\n self.name = reader.read_utf(\"name\")\n self.age = reader.read_int(\"age\")\n self.active = reader.read_boolean(\"active\")\n self.salary = reader.read_double(\"salary\")\n\n def write_portable(self, writer):\n writer.write_utf(\"name\", self.name)\n writer.write_int(\"age\", self.age)\n writer.write_boolean(\"active\", self.active)\n writer.write_double(\"salary\", self.salary)\n```\n\nNote that `Employee` extends `Portable`. As portable types are not deserialized on the server side for querying, you don\u2019t need to implement its Java equivalent on the server side.\n\nFor types that are not portable, you need to implement its Java equivalent and its data serializable factory on the server side for server to reconstitute the objects from binary formats. In this case, you need to compile the `Employee` and related factory classes with server's `CLASSPATH` and add them to the `user-lib` directory in the extracted `hazelcast-.zip` (or `tar`) before starting the server. See the [Adding User Library to CLASSPATH section](#adding-user-library-to-classpath).\n\n> **NOTE: Querying with `Portable` class is faster as compared to `IdentifiedDataSerializable`.**\n\n#### 7.7.1.2. Querying by Combining Predicates with AND, OR, NOT\n\nYou can combine predicates by using the `and_`, `or_` and `not_` operators, as shown in the below example.\n\n```python\nfrom hazelcast.serialization.predicate import and_, is_equal_to, is_less_than\n\nemployee_map = client.get_map(\"employee\")\n\npredicate = and_(is_equal_to('active', True), is_less_than('age', 30))\n\nemployees = employee_map.values(predicate).result()\n```\n\nIn the above example code, `predicate` verifies whether the entry is active and its `age` value is less than 30. This `predicate` is applied to the `employee` map using the `Map.values` method. This method sends the predicate to all cluster members and merges the results coming from them. \n\n> **NOTE: Predicates can also be applied to `key_set` and `entry_set` of the Hazelcast IMDG's distributed map.**\n\n#### 7.7.1.3. Querying with SQL\n\n`SqlPredicate` takes the regular SQL `where` clause. See the following example:\n\n```python\nfrom hazelcast.serialization.predicate import sql\n\nemployee_map = client.get_map(\"employee\")\n\nemployees = employee_map.values(sql(\"active AND age < 30\")).result()\n```\n\n##### Supported SQL Syntax\n\n**AND/OR:** ` AND AND \u2026`\n\n- `active AND age > 30`\n- `active = false OR age = 45 OR name = 'Joe'`\n- `active AND ( age > 20 OR salary < 60000 )`\n\n**Equality:** `=, !=, <, \u21d0, >, >=`\n\n- ` = value`\n- `age <= 30`\n- `name = 'Joe'`\n- `salary != 50000`\n\n**BETWEEN:** ` [NOT] BETWEEN AND `\n\n- `age BETWEEN 20 AND 33 ( same as age >= 20 AND age \u21d0 33 )`\n- `age NOT BETWEEN 30 AND 40 ( same as age < 30 OR age > 40 )`\n\n**IN:** ` [NOT] IN (val1, val2,\u2026)`\n\n- `age IN ( 20, 30, 40 )`\n- `age NOT IN ( 60, 70 )`\n- `active AND ( salary >= 50000 OR ( age NOT BETWEEN 20 AND 30 ) )`\n- `age IN ( 20, 30, 40 ) AND salary BETWEEN ( 50000, 80000 )`\n\n**LIKE:** ` [NOT] LIKE 'expression'`\n\nThe `%` (percentage sign) is the placeholder for multiple characters, an `_` (underscore) is the placeholder for only one character.\n\n- `name LIKE 'Jo%'` (true for 'Joe', 'Josh', 'Joseph' etc.)\n- `name LIKE 'Jo_'` (true for 'Joe'; false for 'Josh')\n- `name NOT LIKE 'Jo_'` (true for 'Josh'; false for 'Joe')\n- `name LIKE 'J_s%'` (true for 'Josh', 'Joseph'; false 'John', 'Joe')\n\n**ILIKE:** ` [NOT] ILIKE 'expression'`\n\nILIKE is similar to the LIKE predicate but in a case-insensitive manner.\n\n- `name ILIKE 'Jo%'` (true for 'Joe', 'joe', 'jOe','Josh','joSH', etc.)\n- `name ILIKE 'Jo_'` (true for 'Joe' or 'jOE'; false for 'Josh')\n\n**REGEX:** ` [NOT] REGEX 'expression'`\n\n- `name REGEX 'abc-.*'` (true for 'abc-123'; false for 'abx-123')\n\n##### Querying Examples with Predicates\n\nYou can use the `__key` attribute to perform a predicated search for the entry keys. See the following example:\n\n```python\nfrom hazelcast.serialization.predicate import sql\n\nperson_map = client.get_map(\"persons\").blocking()\n\nperson_map.put(\"John\", 28)\nperson_map.put(\"Mary\", 23)\nperson_map.put(\"Judy\", 30)\n\npredicate = sql(\"__key like M%\")\n\npersons = person_map.values(predicate)\n\nprint(persons[0]) # Outputs '23'\n```\n\nIn this example, the code creates a list with the values whose keys start with the letter \"M\u201d.\n\nYou can use the `this` attribute to perform a predicated search for the entry values. See the following example:\n\n```python\nfrom hazelcast.serialization.predicate import is_greater_than_or_equal_to\n\nperson_map = client.get_map(\"persons\").blocking()\n\nperson_map.put(\"John\", 28)\nperson_map.put(\"Mary\", 23)\nperson_map.put(\"Judy\", 30)\n\npredicate = is_greater_than_or_equal_to(\"this\", 27)\n\npersons = person_map.values(predicate)\n\nprint(persons[0], persons[1]) # Outputs '28 30'\n```\n\nIn this example, the code creates a list with the values greater than or equal to \"27\".\n\n#### 7.7.1.4. Querying with JSON Strings\n\nYou can query JSON strings stored inside your Hazelcast clusters. To query the JSON string,\nyou first need to create a `HazelcastJsonValue` from the JSON string or JSON serializable object.\nYou can use ``HazelcastJsonValue``s both as keys and values in the distributed data structures. Then, it is\npossible to query these objects using the Hazelcast query methods explained in this section.\n\n```python\nfrom hazelcast.core import HazelcastJsonValue\nperson1 = \"{ \\\"name\\\": \\\"John\\\", \\\"age\\\": 35 }\"\nperson2 = \"{ \\\"name\\\": \\\"Jane\\\", \\\"age\\\": 24 }\"\nperson3 = {\"name\": \"Trey\", \"age\": 17}\n\nid_person_map = client.get_map(\"json-values\").blocking()\n\n# From JSON string\nid_person_map.put(1, HazelcastJsonValue(person1))\nid_person_map.put(2, HazelcastJsonValue(person2))\n\n# From JSON serializable object\nid_person_map.put(3, HazelcastJsonValue(person3))\n\npeople_under_21 = id_person_map.values(is_less_than(\"age\", 21)) \n```\n\nWhen running the queries, Hazelcast treats values extracted from the JSON documents as Java types so they\ncan be compared with the query attribute. JSON specification defines five primitive types to be used in the JSON\ndocuments: `number`,`string`, `true`, `false` and `null`. The `string`, `true/false` and `null` types are treated\nas `String`, `boolean` and `null`, respectively. We treat the extracted `number` values as `long`s if they\ncan be represented by a `long`. Otherwise, `number`s are treated as `double`s.\n\nIt is possible to query nested attributes and arrays in the JSON documents. The query syntax is the same\nas querying other Hazelcast objects using the `Predicate`s.`\n\n\n```python\n# Sample JSON object\n# {\n# \"departmentId\": 1,\n# \"room\": \"alpha\",\n# \"people\": [\n# {\n# \"name\": \"Peter\",\n# \"age\": 26,\n# \"salary\": 50000\n# },\n# {\n# \"name\": \"Jonah\",\n# \"age\": 50,\n# \"salary\": 140000\n# }\n# ]\n# }\n# The following query finds all the departments that have a person named \"Peter\" working in them.\n\ndepartment_with_peter = departments.values(is_equal_to(\"people[any].name\", \"Peter\"))\n```\n\n`HazelcastJsonValue` is a lightweight wrapper around your JSON strings. It is used merely as a way to indicate that the contained string should be treated as a valid JSON value. Hazelcast does not check the validity of JSON strings put into to the maps. Putting an invalid JSON string into a map is permissible. However, in that case whether such an entry is going to be returned or not from a query is not defined.\n\n## 7.8. Performance\n\n### 7.8.1. Near Cache\n\nMap entries in Hazelcast are partitioned across the cluster members. Hazelcast clients do not have local data at all. Suppose you read the key `k` a number of times from a Hazelcast client and `k` is owned by a member in your cluster. Then each `map.get(k)` will be a remote operation, which creates a lot of network trips. If you have a map that is mostly read, then you should consider creating a local Near Cache, so that reads are sped up and less network traffic is created. \n\nThese benefits do not come for free, please consider the following trade-offs:\n\n- Clients with a Near Cache will have to hold the extra cached data, which increases memory consumption.\n- If invalidation is enabled and entries are updated frequently, then invalidations will be costly.\n- Near Cache breaks the strong consistency guarantees; you might be reading stale data.\n\nNear Cache is highly recommended for maps that are mostly read.\n\n#### 7.8.1.1. Configuring Near Cache\n\nThe following snippet show how a Near Cache is configured in the Python client, presenting all available values for each element:\n\n```python\nfrom hazelcast.config import NearCacheConfig, IN_MEMORY_FORMAT, EVICTION_POLICY\n\nnear_cache_config = NearCacheConfig(\"mostly-read-map\")\nnear_cache_config.invalidate_on_change = False\nnear_cache_config.time_to_live_seconds = 600\nnear_cache_config.max_idle_seconds = 5\nnear_cache_config.in_memory_format = IN_MEMORY_FORMAT.OBJECT\nnear_cache_config.eviction_policy = EVICTION_POLICY.LRU\nnear_cache_config.eviction_max_size = 100\nnear_cache_config.eviction_sampling_count = 8\nnear_cache_config.eviction_sampling_pool_size = 16\n\nconfig.add_near_cache_config(near_cache_config)\n```\n\nFollowing are the descriptions of all configuration elements:\n\n- `in_memory_format`: Specifies in which format data will be stored in your Near Cache. Note that a map\u2019s in-memory format can be different from that of its Near Cache. Available values are as follows:\n - `BINARY`: Data will be stored in serialized binary format (default value).\n - `OBJECT`: Data will be stored in deserialized format.\n- `invalidate_on_change`: Specifies whether the cached entries are evicted when the entries are updated or removed. Its default value is `True`.\n- `time_to_live_seconds`: Maximum number of seconds for each entry to stay in the Near Cache. Entries that are older than this period are automatically evicted from the Near Cache. Regardless of the eviction policy used, `time_to_live_seconds` still applies. Any non-negative number can be assigned. Its default value is `None`. `None` means infinite. \n- `max_idle_seconds`: Maximum number of seconds each entry can stay in the Near Cache as untouched (not read). Entries that are not read more than this period are removed from the Near Cache. Any non-negative number can be assigned. Its default value is `None`. `None` means infinite. \n- `eviction_policy`: Eviction policy configuration. Available values are as follows:\n - `LRU`: Least Recently Used (default value).\n - `LFU`: Least Frequently Used.\n - `NONE`: No items are evicted and the `eviction_max_size` property is ignored. You still can combine it with `time_to_live_seconds` and `max_idle_seconds` to evict items from the Near Cache. \n - `RANDOM`: A random item is evicted.\n- `eviction_max_size`: Maximum number of entries kept in the memory before eviction kicks in.\n- `eviction_sampling_count`: Number of random entries that are evaluated to see if some of them are already expired. If there are expired entries, those are removed and there is no need for eviction.\n- `eviction_sampling_pool_size`: Size of the pool for eviction candidates. The pool is kept sorted according to eviction policy. The entry with the highest score is evicted. \n\n#### 7.8.1.2. Near Cache Example for Map\n\nThe following is an example configuration for a Near Cache defined in the `mostly-read-map` map. According to this configuration, the entries are stored as `OBJECT`'s in this Near Cache and eviction starts when the count of entries reaches `5000`; entries are evicted based on the `LRU` (Least Recently Used) policy. In addition, when an entry is updated or removed on the member side, it is eventually evicted on the client side.\n\n```python\nnear_cache_config = NearCacheConfig(\"mostly-read-map\")\nnear_cache_config.invalidate_on_change = True\nnear_cache_config.in_memory_format = IN_MEMORY_FORMAT.OBJECT\nnear_cache_config.eviction_policy = EVICTION_POLICY.LRU\nnear_cache_config.eviction_max_size = 5000\n\nconfig.add_near_cache_config(near_cache_config)\n```\n\n#### 7.8.1.3. Near Cache Eviction\n\nIn the scope of Near Cache, eviction means evicting (clearing) the entries selected according to the given `eviction_policy` when the specified `eviction_max_size` has been reached.\n\nThe `eviction_max_size` defines the entry count when the Near Cache is full and determines whether the eviction should be triggered. \n\nOnce the eviction is triggered, the configured `eviction_policy` determines which, if any, entries must be evicted.\n\n#### 7.8.1.4. Near Cache Expiration\n\nExpiration means the eviction of expired records. A record is expired:\n\n- If it is not touched (accessed/read) for `max_idle_seconds`\n- `time_to_live_seconds` passed since it is put to Near Cache\n\nThe actual expiration is performed when a record is accessed: it is checked if the record is expired or not. If it is expired, it is evicted and `KeyError` is raised to the caller.\n\n#### 7.8.1.5. Near Cache Invalidation\n\nInvalidation is the process of removing an entry from the Near Cache when its value is updated or it is removed from the original map (to prevent stale reads). See the [Near Cache Invalidation section](https://docs.hazelcast.org/docs/latest/manual/html-single/#near-cache-invalidation) in the Hazelcast IMDG Reference Manual.\n\n## 7.9. Monitoring and Logging\n\n### 7.9.1. Enabling Client Statistics\n\nYou can monitor your clients using Hazelcast Management Center.\n\nAs a prerequisite, you need to enable the client statistics before starting your clients. This can be done by setting the `hazelcast.client.statistics.enabled` system property to `true` on the **member** as the following:\n\n```xml\n\n ...\n \n true\n \n ...\n\n```\n\nAlso, you need to enable the client statistics in the Python client. There are two properties related to client statistics:\n\n- `hazelcast.client.statistics.enabled`: If set to `True`, it enables collecting the client statistics and sending them to the cluster. When it is `True` you can monitor the clients that are connected to your Hazelcast cluster, using Hazelcast Management Center. Its default value is `False`.\n\n- `hazelcast.client.statistics.period.seconds`: Period in seconds the client statistics are collected and sent to the cluster. Its default value is `3`.\n\nYou can enable client statistics and set a non-default period in seconds as follows:\n\n```python\nconfig = hazelcast.ClientConfig()\nconfig.set_property(ClientProperties.STATISTICS_ENABLED.name, True)\nconfig.set_property(ClientProperties.STATISTICS_PERIOD_SECONDS.name, 4)\n```\n\nHazelcast Python client can collect statistics related to the client and Near Caches without an extra dependency. However, to get the statistics about the runtime and operating system, [psutil](https://pypi.org/project/psutil/) is used as an extra dependency.\n\nIf the `psutil` is installed, runtime and operating system statistics will be sent to cluster along with statistics related to the client and Near Caches. If not, only the client and Near Cache statistics will be sent.\n\n`psutil` can be installed independently or with the Hazelcast Python client as follows:\n\n**From PyPI**\n```\npip install hazelcast-python-client[stats]\n```\n\n**From source**\n\n```\npip install -e .[stats]\n```\n\nAfter enabling the client statistics, you can monitor your clients using Hazelcast Management Center. Please refer to the [Monitoring Clients section](https://docs.hazelcast.org/docs/management-center/latest/manual/html/index.html#monitoring-clients) in the Hazelcast Management Center Reference Manual for more information on the client statistics.\n\n### 7.9.2 Logging Configuration\n\nHazelcast Python client allows you to configure the logging through the `LoggerConfig` in the `ClientConfig` class. \n\n`LoggerConfig` contains options that allow you to set the logging level and a custom logging configuration file to the Hazelcast Python client. \n\nBy default, Hazelcast Python client will log to the `sys.stderr` with the `INFO` logging level and `%(asctime)s %(name)s\\n%(levelname)s: %(version_message)s %(message)s` format where the `version_message` contains the information about the client version, group name and client name.\n\nBelow is an example of the default logging configuration.\n\n```python\nimport hazelcast\n\nclient = hazelcast.HazelcastClient()\nclient.shutdown()\n```\n\n**Output to the `sys.stderr`**\n```\nFeb 15, 2019 12:57:13 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] A non-empty group password is configured for the Hazelcast client. Starting with Hazelcast IMDG version 3.11, clients with the same group name, but with different group passwords (that do not use authentication) will be accepted to a cluster. The group password configuration will be removed completely in a future release.\nFeb 15, 2019 12:57:13 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is STARTING\nFeb 15, 2019 12:57:13 PM HazelcastClient.ClusterService\nINFO: [3.10] [dev] [hz.client_0] Connecting to Address(host=127.0.0.1, port=5701)\nFeb 15, 2019 12:57:13 PM HazelcastClient.ConnectionManager\nINFO: [3.10] [dev] [hz.client_0] Authenticated with Connection(address=('127.0.0.1', 5701), id=0)\nFeb 15, 2019 12:57:13 PM HazelcastClient.ClusterService\nINFO: [3.10] [dev] [hz.client_0] New member list:\n\nMembers [1] {\n\tMember [10.216.1.49]:5701 - 1f4bb35d-b68f-46eb-bd65-61e3f4bc9922\n}\n\nFeb 15, 2019 12:57:13 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is CONNECTED\nFeb 15, 2019 12:57:13 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] Client started.\nFeb 15, 2019 12:57:13 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is SHUTTING_DOWN\nFeb 15, 2019 12:57:13 PM HazelcastClient.AsyncoreReactor\nWARNING: [3.10] [dev] [hz.client_0] Connection closed by server\nFeb 15, 2019 12:57:13 PM HazelcastClient.LifecycleService\nINFO: [3.10] [dev] [hz.client_0] (20181119 - 9080a46) HazelcastClient is SHUTDOWN\nFeb 15, 2019 12:57:13 PM HazelcastClient\nINFO: [3.10] [dev] [hz.client_0] Client shutdown.\n```\n\nLet's go over the `LoggerConfig` options one by one.\n\n#### Setting Logging Level\n\nAlthough you can not change the logging levels used within the Hazelcast Python client, you can specify a logging level that is used to threshold the logs that are at least as severe as your specified level using `ClientConfig.logger_config.level`.\n\nHere is the table listing the default logging levels that come with the `logging` module and numeric values that represent their severity:\n\n| Level | Numeric Value |\n|----------|---------------|\n| CRITICAL | 50 |\n| ERROR | 40 |\n| WARNING | 30 |\n| INFO | 20 |\n| DEBUG | 10 |\n| NOTSET | 0 |\n\nFor example, setting the logging level to `logging.DEBUG` will cause all the logging messages that are equal or higher than the `logging.DEBUG` in terms of severity to be emitted by your logger.\n\nBy default, the logging level is set to `logging.INFO`.\n\nTo turn off the logging, you can set `ClientConfig.logger_config.level` to a value greater than the numeric value of `logging.CRITICAL`. For example, the configuration below turns off the logging for the Hazelcast Python client.\n\n```python\nconfig.logger_config.level = 100 # Any value greater than 50 will turn off the logging\nclient = hazelcast.HazelcastClient(config)\n``` \n\n#### Setting a Custom Logging Configuration\n\n`ClientConfig.logger_config.config_file` can be used to configure the logger for the Hazelcast Python client entirely.\n\nWhen set, this field should contain the absolute path of the JSON file that contains the logging configuration as described in the [Configuration dictionary schema](https://docs.python.org/3/library/logging.config.html#logging-config-dictschema). This file will be read and the contents of it will be directly fed into the `logging.dictConfig` function.\n\nWhen this field is set, the `level` field is simply discarded and configuration in this file is used.\n\nAll Hazelcast Python client related loggers have `HazelcastClient` as their parent logger. So, you can configure logging for the `HazelcastClient` base logger and this logging configuration can be used for all client related loggers. \n\nLet's replicate the default configuration used within the Hazelcast client with this configuration method.\n\n**config.json**\n```json\n{\n \"version\": 1,\n \"disable_existing_loggers\": false,\n \"filters\": {\n \"version_message_filter\": {\n \"()\": \"some_package.log.VersionMessageFilter\"\n }\n },\n \"formatters\": {\n \"hazelcast_formatter\": {\n \"()\": \"some_package.log.HazelcastFormatter\",\n \"format\": \"%(asctime)s %(name)s\\n%(levelname)s: %(version_message)s %(message)s\",\n \"datefmt\": \"%b %d, %Y %I:%M:%S %p\"\n }\n },\n \"handlers\": {\n \"console_handler\": {\n \"class\": \"logging.StreamHandler\",\n \"stream\": \"ext://sys.stdout\",\n \"filters\": [\"version_message_filter\"],\n \"formatter\": \"hazelcast_formatter\"\n }\n },\n \"loggers\": {\n \"HazelcastClient\": {\n \"handlers\": [\"console_handler\"],\n \"level\": \"INFO\"\n }\n }\n}\n```\n\n**some_package/log.py**\n```python\nimport logging\n\nfrom hazelcast.version import CLIENT_VERSION\n\nclass VersionMessageFilter(logging.Filter):\n def filter(self, record):\n record.version_message = \"[\" + CLIENT_VERSION + \"]\"\n return True\n\nclass HazelcastFormatter(logging.Formatter):\n def format(self, record):\n client_name = getattr(record, \"client_name\", None)\n group_name = getattr(record, \"group_name\", None)\n if client_name and group_name:\n record.msg = \"[\" + group_name + \"] [\" + client_name + \"] \" + record.msg\n return super(HazelcastFormatter, self).format(record)\n```\n\n**some_package/test.py**\n```python\nimport hazelcast\n\nconfig = hazelcast.ClientConfig()\nconfig.logger_config.config_file = \"/home/hazelcast/config.json\"\n\nclient = hazelcast.HazelcastClient(config)\n\n## Some operations\n\nclient.shutdown()\n``` \n\nTo learn more about the `logging` module and its capabilities, please see the [logging cookbook](https://docs.python.org/3/howto/logging-cookbook.html) and [documentation](https://docs.python.org/3/library/logging.html) of the `logging` module.\n\n# 8. Development and Testing\n\nIf you want to help with bug fixes, develop new features or tweak the implementation to your application's needs, you can follow the steps in this section.\n\n## 8.1. Building and Using Client From Sources\n\nFollow the below steps to build and install Hazelcast Python client from its source:\n\n1. Clone the GitHub repository (https://github.com/hazelcast/hazelcast-python-client.git).\n2. Run `python setup.py install` to install the Python client.\n\nIf you are planning to contribute, please make sure that it fits the guidelines described in [PEP8](https://www.python.org/dev/peps/pep-0008/).\n\n## 8.2. Testing\n\nIn order to test Hazelcast Python client locally, you will need the following:\n\n* Java 6 or newer\n* Maven\n\nFollowing commands starts the tests according to your operating system:\n\n```bash\nsh run-tests.sh\n```\n\nor \n\n```\nPS> .\\run-tests.ps1\n```\n\nTest script automatically downloads `hazelcast-remote-controller` and Hazelcast IMDG. The script uses Maven to download those.\n\n# 9. Getting Help\n\nYou can use the following channels for your questions and development/usage issues:\n\n* This repository by opening an issue.\n* Our Google Groups directory: https://groups.google.com/forum/#!forum/hazelcast\n* Stack Overflow: https://stackoverflow.com/questions/tagged/hazelcast\n\n# 10. Contributing\n\nBesides your development contributions as explained in the [Development and Testing chapter](#8-development-and-testing) above, you can always open a pull request on this repository for your other requests.\n\n# 11. License\n\n[Apache 2 License](https://github.com/hazelcast/hazelcast-python-client/blob/master/LICENSE.txt).\n\n# 12. Copyright\n\nCopyright (c) 2008-2019, Hazelcast, Inc. All Rights Reserved.\n\nVisit [www.hazelcast.com](http://www.hazelcast.com) for more information.\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/hazelcast/hazelcast-python-client", "keywords": "hazelcast,hazelcast client,In-Memory Data Grid,Distributed Computing", "license": "Apache 2.0", "maintainer": "", "maintainer_email": "", "name": "hazelcast-python-client", "package_url": "https://pypi.org/project/hazelcast-python-client/", "platform": "", "project_url": "https://pypi.org/project/hazelcast-python-client/", "project_urls": { "Homepage": "https://github.com/hazelcast/hazelcast-python-client" }, "release_url": "https://pypi.org/project/hazelcast-python-client/3.12.1/", "requires_dist": [ "psutil; extra == 'stats'" ], "requires_python": "", "summary": "Hazelcast Python Client", "version": "3.12.1" }, "last_serial": 5533920, "releases": { "0.1": [ { "comment_text": "", "digests": { "md5": "059680e3a859556c8f0ba5056c754e59", "sha256": "210b4dbeb999c783914f65fe2c751133ab780bb40f3a15aec43cf467ed966ba9" }, "downloads": -1, "filename": "hazelcast-python-client-0.1.tar.gz", "has_sig": false, "md5_digest": "059680e3a859556c8f0ba5056c754e59", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 82123, "upload_time": "2016-02-12T15:17:39", "url": "https://files.pythonhosted.org/packages/f4/c7/7cd10a08e6db67f0324d8b062b20873a928593e1ab095ef2d1382f8cf239/hazelcast-python-client-0.1.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "7e320bfb97e80379f7fb03cfd8a771c8", "sha256": "286c182f3265996ec0cc5b5d357d0b6fe6966fedd4983b73c75df135a5c45d78" }, "downloads": -1, "filename": "hazelcast-python-client-0.2.1.tar.gz", "has_sig": false, "md5_digest": "7e320bfb97e80379f7fb03cfd8a771c8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 91257, "upload_time": "2016-03-15T14:58:58", "url": "https://files.pythonhosted.org/packages/5c/c5/82d4a3ef13ad2bce749ef75e7e3cdda261d0b36f53b9b5c5def5e52d8e9f/hazelcast-python-client-0.2.1.tar.gz" } ], "0.3": [ { "comment_text": "", "digests": { "md5": "1a8edced2171266a50fec8f8c50f85c5", "sha256": "f3b1a0d30e2603f88d328dacfb7fbc2a78ead53ed36e5b8604073ea8569a62e3" }, "downloads": -1, "filename": "hazelcast-python-client-0.3.tar.gz", "has_sig": false, "md5_digest": "1a8edced2171266a50fec8f8c50f85c5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 98314, "upload_time": "2016-04-25T13:05:04", "url": "https://files.pythonhosted.org/packages/60/f5/fade9b73e24613573c41023a32362f969134f0a4e8fab1ad9ec802c4792a/hazelcast-python-client-0.3.tar.gz" } ], "3.10": [ { "comment_text": "", "digests": { "md5": "5585bff57f37efd0be84c6efa209b7ee", "sha256": "f2b65a72fc5110c69cdb5e3ff685700186f36cbbf7251db0aac7414da544df8d" }, "downloads": -1, "filename": "hazelcast_python_client-3.10-py3-none-any.whl", "has_sig": false, "md5_digest": "5585bff57f37efd0be84c6efa209b7ee", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 344395, "upload_time": "2018-11-19T09:31:51", "url": "https://files.pythonhosted.org/packages/39/9f/768a4002537e04555fb21f4c7d67f7f667ba3bd57e57b5af1232a8ae53ae/hazelcast_python_client-3.10-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5d2940722e02cb3f88b0dd50b8634f1d", "sha256": "36ebdcdcccde36a957f940315a94c8c663f39218e0852e864c041260b4dfb0ba" }, "downloads": -1, "filename": "hazelcast-python-client-3.10.tar.gz", "has_sig": false, "md5_digest": "5d2940722e02cb3f88b0dd50b8634f1d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 209243, "upload_time": "2018-11-19T09:31:53", "url": "https://files.pythonhosted.org/packages/53/40/789440d07f76178e4816cd431ce052b087e11bd78f061b49878550bf382a/hazelcast-python-client-3.10.tar.gz" } ], "3.11": [ { "comment_text": "", "digests": { "md5": "63b9b4ed8f5bce0064b3862466e113d1", "sha256": "35080767b086b204d428c044c5d9634e9b3d8cbc3e648eda527c47b3548a174c" }, "downloads": -1, "filename": "hazelcast_python_client-3.11-py3-none-any.whl", "has_sig": false, "md5_digest": "63b9b4ed8f5bce0064b3862466e113d1", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 363010, "upload_time": "2019-03-19T10:18:10", "url": "https://files.pythonhosted.org/packages/9d/66/d5a458cb71a169b1f053e7d19ce1f03f6a63ec6046b3dc7ce6a38eb5a95f/hazelcast_python_client-3.11-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a023742994454e0118d70ed30bede236", "sha256": "56f8adb7f9bff9a686bdf283a75825626135559ca771feb461f7e3c3ecba2683" }, "downloads": -1, "filename": "hazelcast-python-client-3.11.tar.gz", "has_sig": false, "md5_digest": "a023742994454e0118d70ed30bede236", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 226751, "upload_time": "2019-03-19T10:18:13", "url": "https://files.pythonhosted.org/packages/82/09/97dba15a1e6bd38c12f0938034da8aebf5fa9c0630d6b7429abb886af70c/hazelcast-python-client-3.11.tar.gz" } ], "3.12": [ { "comment_text": "", "digests": { "md5": "ae8a6419c8e3e38f38ee54676505f37c", "sha256": "5006829ba6ebee35234839d2b07b1bcfe2bf49bcc652f29a951dc989d90d9b63" }, "downloads": -1, "filename": "hazelcast_python_client-3.12-py3-none-any.whl", "has_sig": false, "md5_digest": "ae8a6419c8e3e38f38ee54676505f37c", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 365381, "upload_time": "2019-04-30T13:56:26", "url": "https://files.pythonhosted.org/packages/6c/25/1f5741c912eed1a9090dd220355e69636005b61899d399405ba651223ea6/hazelcast_python_client-3.12-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "eac415b2f5aac020618e79daae7b9301", "sha256": "b77b84165c4c55209cf5c1ac0762052d19eaa0f23e32377d00869e54b3d4e1b8" }, "downloads": -1, "filename": "hazelcast-python-client-3.12.tar.gz", "has_sig": false, "md5_digest": "eac415b2f5aac020618e79daae7b9301", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 232751, "upload_time": "2019-04-30T13:56:30", "url": "https://files.pythonhosted.org/packages/1e/0c/198cdb86ca59c2a494ca348788d5e88583b0d19ad8375bfb7abf2b531f68/hazelcast-python-client-3.12.tar.gz" } ], "3.12.1": [ { "comment_text": "", "digests": { "md5": "e0e893a4b5e2c2a9010096feed4f6b5e", "sha256": "0ec45499af42aa13cd2bef04b267a0537f342a5df2facd34d0ab506723996fe2" }, "downloads": -1, "filename": "hazelcast_python_client-3.12.1-py3-none-any.whl", "has_sig": false, "md5_digest": "e0e893a4b5e2c2a9010096feed4f6b5e", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 366837, "upload_time": "2019-07-15T08:48:03", "url": "https://files.pythonhosted.org/packages/39/54/e17926b8ba17cd96bbd1bc4367cbd72efc66155b3937d27727167fd43582/hazelcast_python_client-3.12.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b02fa4789128cd1b9bcdf2b449f059c8", "sha256": "5225dda36a69ce4a798cda162a382f92b2882c4c53b17503712029650ef607be" }, "downloads": -1, "filename": "hazelcast-python-client-3.12.1.tar.gz", "has_sig": false, "md5_digest": "b02fa4789128cd1b9bcdf2b449f059c8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 236689, "upload_time": "2019-07-15T08:48:06", "url": "https://files.pythonhosted.org/packages/ac/3e/25e577816124e6705ab442fcba9a4a61532fad6554a2ef66739be175daa7/hazelcast-python-client-3.12.1.tar.gz" } ], "3.7": [ { "comment_text": "", "digests": { "md5": "e633a223422f34a89263e099c2d43e8d", "sha256": "7c32860953a5e4726370bd85ae4b3d97eb017a39de402fbd4d6173ed6a32d995" }, "downloads": -1, "filename": "hazelcast-python-client-3.7.tar.gz", "has_sig": false, "md5_digest": "e633a223422f34a89263e099c2d43e8d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 127646, "upload_time": "2016-10-20T09:14:35", "url": "https://files.pythonhosted.org/packages/0e/e7/7b045dec0f0f091bb06eec4a9b77c3568d2926069965d87b89a2ec6b6ff0/hazelcast-python-client-3.7.tar.gz" } ], "3.7.1": [ { "comment_text": "", "digests": { "md5": "6dca4b8dc30e33b1f4e8f6e37508b3c6", "sha256": "29f27d1fed8c5b56237abb133fe037cffbd39fe9cf67adc734fb37b9105133da" }, "downloads": -1, "filename": "hazelcast-python-client-3.7.1.tar.gz", "has_sig": false, "md5_digest": "6dca4b8dc30e33b1f4e8f6e37508b3c6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 127780, "upload_time": "2016-10-25T08:28:54", "url": "https://files.pythonhosted.org/packages/01/91/a602811ea5317ab7044f2f24f8ed79fb2e46526929de15285b7ddffc4e30/hazelcast-python-client-3.7.1.tar.gz" } ], "3.7.2": [ { "comment_text": "", "digests": { "md5": "2adc30f7814676a9d78fb70fc3f48aa4", "sha256": "7a8ea1eb0bab14d39f04997594467b28d4afc20e1a0ebe223e86b150859b58c8" }, "downloads": -1, "filename": "hazelcast-python-client-3.7.2.tar.gz", "has_sig": false, "md5_digest": "2adc30f7814676a9d78fb70fc3f48aa4", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 127812, "upload_time": "2016-11-23T09:25:05", "url": "https://files.pythonhosted.org/packages/6d/33/a7715277561bc3495316d1d7e56374e760d524123081a32d26fd127af8e8/hazelcast-python-client-3.7.2.tar.gz" } ], "3.8": [ { "comment_text": "", "digests": { "md5": "47f8fc08aaf4e9fa736b6f75b51a5fee", "sha256": "5ec485ee19b9a9b49a82e1fe0accff163305d84ccec8f81a34f0f8543087d853" }, "downloads": -1, "filename": "hazelcast-python-client-3.8.tar.gz", "has_sig": false, "md5_digest": "47f8fc08aaf4e9fa736b6f75b51a5fee", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 127940, "upload_time": "2017-03-01T11:07:25", "url": "https://files.pythonhosted.org/packages/15/c3/8b122a5bd60083233fc0c59424d1862102617ad413a96340308756af054d/hazelcast-python-client-3.8.tar.gz" } ], "3.8.1": [ { "comment_text": "", "digests": { "md5": "7f9b33f31660de8c4075b12ff9690fa9", "sha256": "d30a0289aea3c511e31af585963747c360235d03c7841763cd452ac22c5ed18b" }, "downloads": -1, "filename": "hazelcast-python-client-3.8.1.tar.gz", "has_sig": false, "md5_digest": "7f9b33f31660de8c4075b12ff9690fa9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 108665, "upload_time": "2017-04-24T12:41:36", "url": "https://files.pythonhosted.org/packages/68/ff/b82bf95c0602266a2b24580a091f44916e09ac67b2a46f3e3d6955bbf46c/hazelcast-python-client-3.8.1.tar.gz" } ], "3.9": [ { "comment_text": "", "digests": { "md5": "742c24c7d6b9ff7910837d154df78629", "sha256": "a78883cd3bb7d9ed4d2c2a1ec5c4c3d2e0adb3bfd9f4609cac3c172be26ec863" }, "downloads": -1, "filename": "hazelcast_python_client-3.9-py3-none-any.whl", "has_sig": false, "md5_digest": "742c24c7d6b9ff7910837d154df78629", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 345526, "upload_time": "2018-08-02T14:03:41", "url": "https://files.pythonhosted.org/packages/9e/2f/f7cbcbe576e83644828065eae6f23fb0a7bb18f27f201f5766077fcc4238/hazelcast_python_client-3.9-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "e83ed50fba944044211c312deafedda5", "sha256": "b820d6e485c2c980510e45bed54990b1ade8003b7966446a40920c3b9f67240c" }, "downloads": -1, "filename": "hazelcast-python-client-3.9.tar.gz", "has_sig": false, "md5_digest": "e83ed50fba944044211c312deafedda5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 125403, "upload_time": "2018-08-02T14:03:43", "url": "https://files.pythonhosted.org/packages/0f/a5/4c086753dc34ae12444882bca081ef883a28e2561f33a7c5f1a40043eab0/hazelcast-python-client-3.9.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "e0e893a4b5e2c2a9010096feed4f6b5e", "sha256": "0ec45499af42aa13cd2bef04b267a0537f342a5df2facd34d0ab506723996fe2" }, "downloads": -1, "filename": "hazelcast_python_client-3.12.1-py3-none-any.whl", "has_sig": false, "md5_digest": "e0e893a4b5e2c2a9010096feed4f6b5e", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 366837, "upload_time": "2019-07-15T08:48:03", "url": "https://files.pythonhosted.org/packages/39/54/e17926b8ba17cd96bbd1bc4367cbd72efc66155b3937d27727167fd43582/hazelcast_python_client-3.12.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b02fa4789128cd1b9bcdf2b449f059c8", "sha256": "5225dda36a69ce4a798cda162a382f92b2882c4c53b17503712029650ef607be" }, "downloads": -1, "filename": "hazelcast-python-client-3.12.1.tar.gz", "has_sig": false, "md5_digest": "b02fa4789128cd1b9bcdf2b449f059c8", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 236689, "upload_time": "2019-07-15T08:48:06", "url": "https://files.pythonhosted.org/packages/ac/3e/25e577816124e6705ab442fcba9a4a61532fad6554a2ef66739be175daa7/hazelcast-python-client-3.12.1.tar.gz" } ] }