Skip to content

Commit 830d3e9

Browse files
authored
Merge pull request #35 from configcat/singleton
Singleton
2 parents 5c0cd96 + c09a712 commit 830d3e9

18 files changed

+500
-224
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ else:
5050
```
5151

5252
### 6. Stop *ConfigCat* client on application exit
53+
5354
```python
54-
configcat_client.stop()
55+
configcat_client.close()
5556
```
5657

5758
## Getting user specific setting values with Targeting

configcatclient/__init__.py

Lines changed: 52 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from .configcatclient import ConfigCatClient
22
from .interfaces import ConfigCatClientException
33
from .datagovernance import DataGovernance
4+
from .configcatoptions import ConfigCatOptions
5+
from .pollingmode import PollingMode
46

57

68
def create_client(sdk_key, data_governance=DataGovernance.Global):
@@ -18,7 +20,8 @@ def create_client(sdk_key, data_governance=DataGovernance.Global):
1820

1921
def create_client_with_auto_poll(sdk_key, poll_interval_seconds=60, max_init_wait_time_seconds=5,
2022
on_configuration_changed_callback=None, config_cache_class=None,
21-
base_url=None, proxies=None, proxy_auth=None, connect_timeout=10, read_timeout=30,
23+
base_url=None, proxies=None, proxy_auth=None,
24+
connect_timeout_seconds=10, read_timeout_seconds=30,
2225
flag_overrides=None,
2326
data_governance=DataGovernance.Global,
2427
default_user=None):
@@ -34,9 +37,9 @@ def create_client_with_auto_poll(sdk_key, poll_interval_seconds=60, max_init_wai
3437
:param base_url: You can set a base_url if you want to use a proxy server between your application and ConfigCat
3538
:param proxies: Proxy addresses. e.g. { 'https': 'your_proxy_ip:your_proxy_port' }
3639
:param proxy_auth: Proxy authentication. e.g. HTTPProxyAuth('username', 'password')
37-
:param connect_timeout: The number of seconds to wait for the server to make the initial connection
40+
:param connect_timeout_seconds: The number of seconds to wait for the server to make the initial connection
3841
(i.e. completing the TCP connection handshake). Default: 10 seconds.
39-
:param read_timeout: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
42+
:param read_timeout_seconds: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
4043
:param flag_overrides: An OverrideDataSource implementation used to override feature flags & settings.
4144
:param data_governance:
4245
Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard: \n
@@ -46,33 +49,25 @@ def create_client_with_auto_poll(sdk_key, poll_interval_seconds=60, max_init_wai
4649
ConfigCatClient.get_value, ConfigCatClient.get_all_values, etc. methods.
4750
"""
4851

49-
if sdk_key is None:
50-
raise ConfigCatClientException('SDK Key is required.')
51-
52-
if poll_interval_seconds < 1:
53-
poll_interval_seconds = 1
54-
55-
if max_init_wait_time_seconds < 0:
56-
max_init_wait_time_seconds = 0
57-
58-
return ConfigCatClient(sdk_key=sdk_key,
59-
poll_interval_seconds=poll_interval_seconds,
60-
max_init_wait_time_seconds=max_init_wait_time_seconds,
61-
on_configuration_changed_callback=on_configuration_changed_callback,
62-
cache_time_to_live_seconds=0,
63-
config_cache_class=config_cache_class,
64-
base_url=base_url,
65-
proxies=proxies,
66-
proxy_auth=proxy_auth,
67-
connect_timeout=connect_timeout,
68-
read_timeout=read_timeout,
69-
flag_overrides=flag_overrides,
70-
data_governance=data_governance,
71-
default_user=default_user)
52+
options = ConfigCatOptions(
53+
base_url=base_url,
54+
polling_mode=PollingMode.auto_poll(auto_poll_interval_seconds=poll_interval_seconds,
55+
max_init_wait_time_seconds=max_init_wait_time_seconds,
56+
on_config_changed=on_configuration_changed_callback),
57+
config_cache=config_cache_class,
58+
proxies=proxies,
59+
proxy_auth=proxy_auth,
60+
connect_timeout_seconds=connect_timeout_seconds,
61+
read_timeout_seconds=read_timeout_seconds,
62+
flag_overrides=flag_overrides,
63+
data_governance=data_governance,
64+
default_user=default_user)
65+
return ConfigCatClient.get(sdk_key=sdk_key, options=options)
7266

7367

7468
def create_client_with_lazy_load(sdk_key, cache_time_to_live_seconds=60, config_cache_class=None,
75-
base_url=None, proxies=None, proxy_auth=None, connect_timeout=10, read_timeout=30,
69+
base_url=None, proxies=None, proxy_auth=None,
70+
connect_timeout_seconds=10, read_timeout_seconds=30,
7671
flag_overrides=None,
7772
data_governance=DataGovernance.Global,
7873
default_user=None):
@@ -86,9 +81,9 @@ def create_client_with_lazy_load(sdk_key, cache_time_to_live_seconds=60, config_
8681
:param base_url: You can set a base_url if you want to use a proxy server between your application and ConfigCat
8782
:param proxies: Proxy addresses. e.g. { "https": "your_proxy_ip:your_proxy_port" }
8883
:param proxy_auth: Proxy authentication. e.g. HTTPProxyAuth('username', 'password')
89-
:param connect_timeout: The number of seconds to wait for the server to make the initial connection
84+
:param connect_timeout_seconds: The number of seconds to wait for the server to make the initial connection
9085
(i.e. completing the TCP connection handshake). Default: 10 seconds.
91-
:param read_timeout: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
86+
:param read_timeout_seconds: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
9287
:param flag_overrides: An OverrideDataSource implementation used to override feature flags & settings.
9388
:param data_governance:
9489
Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard: \n
@@ -98,30 +93,23 @@ def create_client_with_lazy_load(sdk_key, cache_time_to_live_seconds=60, config_
9893
ConfigCatClient.get_value, ConfigCatClient.get_all_values, etc. methods.
9994
"""
10095

101-
if sdk_key is None:
102-
raise ConfigCatClientException('SDK Key is required.')
103-
104-
if cache_time_to_live_seconds < 1:
105-
cache_time_to_live_seconds = 1
106-
107-
return ConfigCatClient(sdk_key=sdk_key,
108-
poll_interval_seconds=0,
109-
max_init_wait_time_seconds=0,
110-
on_configuration_changed_callback=None,
111-
cache_time_to_live_seconds=cache_time_to_live_seconds,
112-
config_cache_class=config_cache_class,
113-
base_url=base_url,
114-
proxies=proxies,
115-
proxy_auth=proxy_auth,
116-
connect_timeout=connect_timeout,
117-
read_timeout=read_timeout,
118-
flag_overrides=flag_overrides,
119-
data_governance=data_governance,
120-
default_user=default_user)
96+
options = ConfigCatOptions(
97+
base_url=base_url,
98+
polling_mode=PollingMode.lazy_load(cache_refresh_interval_seconds=cache_time_to_live_seconds),
99+
config_cache=config_cache_class,
100+
proxies=proxies,
101+
proxy_auth=proxy_auth,
102+
connect_timeout_seconds=connect_timeout_seconds,
103+
read_timeout_seconds=read_timeout_seconds,
104+
flag_overrides=flag_overrides,
105+
data_governance=data_governance,
106+
default_user=default_user)
107+
return ConfigCatClient.get(sdk_key=sdk_key, options=options)
121108

122109

123110
def create_client_with_manual_poll(sdk_key, config_cache_class=None,
124-
base_url=None, proxies=None, proxy_auth=None, connect_timeout=10, read_timeout=30,
111+
base_url=None, proxies=None, proxy_auth=None,
112+
connect_timeout_seconds=10, read_timeout_seconds=30,
125113
flag_overrides=None,
126114
data_governance=DataGovernance.Global,
127115
default_user=None):
@@ -134,9 +122,9 @@ def create_client_with_manual_poll(sdk_key, config_cache_class=None,
134122
:param base_url: You can set a base_url if you want to use a proxy server between your application and ConfigCat
135123
:param proxies: Proxy addresses. e.g. { "https": "your_proxy_ip:your_proxy_port" }
136124
:param proxy_auth: Proxy authentication. e.g. HTTPProxyAuth('username', 'password')
137-
:param connect_timeout: The number of seconds to wait for the server to make the initial connection
125+
:param connect_timeout_seconds: The number of seconds to wait for the server to make the initial connection
138126
(i.e. completing the TCP connection handshake). Default: 10 seconds.
139-
:param read_timeout: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
127+
:param read_timeout_seconds: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
140128
:param flag_overrides: An OverrideDataSource implementation used to override feature flags & settings.
141129
:param data_governance:
142130
Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard: \n
@@ -146,20 +134,15 @@ def create_client_with_manual_poll(sdk_key, config_cache_class=None,
146134
ConfigCatClient.get_value, ConfigCatClient.get_all_values, etc. methods.
147135
"""
148136

149-
if sdk_key is None:
150-
raise ConfigCatClientException('SDK Key is required.')
151-
152-
return ConfigCatClient(sdk_key=sdk_key,
153-
poll_interval_seconds=0,
154-
max_init_wait_time_seconds=0,
155-
on_configuration_changed_callback=None,
156-
cache_time_to_live_seconds=0,
157-
config_cache_class=config_cache_class,
158-
base_url=base_url,
159-
proxies=proxies,
160-
proxy_auth=proxy_auth,
161-
connect_timeout=connect_timeout,
162-
read_timeout=read_timeout,
163-
flag_overrides=flag_overrides,
164-
data_governance=data_governance,
165-
default_user=default_user)
137+
options = ConfigCatOptions(
138+
base_url=base_url,
139+
polling_mode=PollingMode.manual_poll(),
140+
config_cache=config_cache_class,
141+
proxies=proxies,
142+
proxy_auth=proxy_auth,
143+
connect_timeout_seconds=connect_timeout_seconds,
144+
read_timeout_seconds=read_timeout_seconds,
145+
flag_overrides=flag_overrides,
146+
data_governance=data_governance,
147+
default_user=default_user)
148+
return ConfigCatClient.get(sdk_key=sdk_key, options=options)

configcatclient/configcatclient.py

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,76 +5,100 @@
55
from .autopollingcachepolicy import AutoPollingCachePolicy
66
from .configfetcher import ConfigFetcher
77
from .configcache import InMemoryConfigCache
8-
from .datagovernance import DataGovernance
8+
from .configcatoptions import ConfigCatOptions
99
from .overridedatasource import OverrideBehaviour
10+
from .pollingmode import AutoPollingMode, LazyLoadingMode
1011
from .rolloutevaluator import RolloutEvaluator
1112
import logging
1213
import sys
1314
import hashlib
1415
from collections import namedtuple
1516
import copy
17+
from .utils import method_is_called_from
1618

1719
log = logging.getLogger(sys.modules[__name__].__name__)
1820

1921
KeyValue = namedtuple('KeyValue', 'key value')
2022

2123

2224
class ConfigCatClient(object):
23-
sdk_keys = []
25+
_instances = {}
26+
27+
@classmethod
28+
def get(cls, sdk_key, options=None):
29+
client = cls._instances.get(sdk_key)
30+
if client is not None:
31+
if options is not None:
32+
log.warning('Client for sdk_key `%s` is already created and will be reused; '
33+
'options passed are being ignored.' % sdk_key)
34+
return client
35+
36+
if options is None:
37+
options = ConfigCatOptions()
38+
39+
client = ConfigCatClient(sdk_key=sdk_key,
40+
options=options)
41+
cls._instances[sdk_key] = client
42+
return client
43+
44+
@classmethod
45+
def close_all(cls):
46+
# Closes all ConfigCatClient instances.
47+
for key, value in list(cls._instances.items()):
48+
value.close_resources()
49+
cls._instances.clear()
2450

2551
def __init__(self,
2652
sdk_key,
27-
poll_interval_seconds=60,
28-
max_init_wait_time_seconds=5,
29-
on_configuration_changed_callback=None,
30-
cache_time_to_live_seconds=60,
31-
config_cache_class=None,
32-
base_url=None,
33-
proxies=None,
34-
proxy_auth=None,
35-
connect_timeout=10,
36-
read_timeout=30,
37-
flag_overrides=None,
38-
data_governance=DataGovernance.Global,
39-
default_user=None):
53+
options=ConfigCatOptions()):
54+
55+
if not method_is_called_from(ConfigCatClient.get):
56+
log.warning('ConfigCatClient.__init__() is deprecated. '
57+
'Create the ConfigCat Client as a Singleton object with `ConfigCatClient.get()` instead')
4058

4159
if sdk_key is None:
4260
raise ConfigCatClientException('SDK Key is required.')
4361

44-
if sdk_key in ConfigCatClient.sdk_keys:
45-
log.warning('A ConfigCat Client is already initialized with sdk_key %s. '
46-
'We strongly recommend you to use the ConfigCat Client as '
47-
'a Singleton object in your application.' % sdk_key)
48-
else:
49-
ConfigCatClient.sdk_keys.append(sdk_key)
50-
5162
self._sdk_key = sdk_key
52-
self._default_user = default_user
53-
self._override_data_source = flag_overrides
63+
self._default_user = options.default_user
64+
self._override_data_source = options.flag_overrides
5465
self._rollout_evaluator = RolloutEvaluator()
5566

56-
if config_cache_class:
57-
self._config_cache = config_cache_class()
58-
else:
59-
self._config_cache = InMemoryConfigCache()
67+
self._config_cache = options.config_cache if options.config_cache is not None else InMemoryConfigCache()
6068

6169
if self._override_data_source and self._override_data_source.get_behaviour() == OverrideBehaviour.LocalOnly:
6270
self._config_fetcher = None
6371
self._cache_policy = None
6472
else:
65-
if poll_interval_seconds > 0:
66-
self._config_fetcher = ConfigFetcher(sdk_key, 'a', base_url, proxies, proxy_auth, connect_timeout, read_timeout, data_governance)
73+
if isinstance(options.polling_mode, AutoPollingMode):
74+
self._config_fetcher = ConfigFetcher(sdk_key,
75+
options.polling_mode.identifier(),
76+
options.base_url,
77+
options.proxies, options.proxy_auth,
78+
options.connect_timeout_seconds, options.read_timeout_seconds,
79+
options.data_governance)
6780
self._cache_policy = AutoPollingCachePolicy(self._config_fetcher, self._config_cache,
6881
self.__get_cache_key(),
69-
poll_interval_seconds, max_init_wait_time_seconds,
70-
on_configuration_changed_callback)
71-
elif cache_time_to_live_seconds > 0:
72-
self._config_fetcher = ConfigFetcher(sdk_key, 'l', base_url, proxies, proxy_auth, connect_timeout, read_timeout, data_governance)
82+
options.polling_mode.auto_poll_interval_seconds,
83+
options.polling_mode.max_init_wait_time_seconds,
84+
options.polling_mode.on_config_changed)
85+
elif isinstance(options.polling_mode, LazyLoadingMode):
86+
self._config_fetcher = ConfigFetcher(sdk_key,
87+
options.polling_mode.identifier(),
88+
options.base_url,
89+
options.proxies, options.proxy_auth,
90+
options.connect_timeout_seconds, options.read_timeout_seconds,
91+
options.data_governance)
7392
self._cache_policy = LazyLoadingCachePolicy(self._config_fetcher, self._config_cache,
7493
self.__get_cache_key(),
75-
cache_time_to_live_seconds)
94+
options.polling_mode.cache_refresh_interval_seconds)
7695
else:
77-
self._config_fetcher = ConfigFetcher(sdk_key, 'm', base_url, proxies, proxy_auth, connect_timeout, read_timeout, data_governance)
96+
self._config_fetcher = ConfigFetcher(sdk_key,
97+
options.polling_mode.identifier(),
98+
options.base_url,
99+
options.proxies, options.proxy_auth,
100+
options.connect_timeout_seconds, options.read_timeout_seconds,
101+
options.data_governance)
78102
self._cache_policy = ManualPollingCachePolicy(self._config_fetcher, self._config_cache,
79103
self.__get_cache_key())
80104

@@ -171,10 +195,13 @@ def set_default_user(self, user):
171195
def clear_default_user(self):
172196
self._default_user = None
173197

174-
def stop(self):
198+
def close_resources(self):
175199
if self._cache_policy:
176200
self._cache_policy.stop()
177-
ConfigCatClient.sdk_keys.remove(self._sdk_key)
201+
202+
def close(self):
203+
self.close_resources()
204+
ConfigCatClient._instances.pop(self._sdk_key)
178205

179206
def __get_settings(self):
180207
if self._override_data_source:

0 commit comments

Comments
 (0)