Skip to content

Commit 4a26691

Browse files
prepare 7.0.0 release (launchdarkly#147)
1 parent 881d1c3 commit 4a26691

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+566
-1036
lines changed

Diff for: .circleci/config.yml

+21-47
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,6 @@ orbs:
66
workflows:
77
test:
88
jobs:
9-
- test-linux:
10-
name: Python 2.7
11-
docker-image: circleci/python:2.7-jessie
12-
test-with-codeclimate: true # we only need to run CodeClimate in one job
13-
- test-linux:
14-
name: Python 3.3
15-
docker-image: circleci/python:3.3-jessie
16-
consul-supported: false # Consul isn't supported in 3.3
17-
filesource-supported: false # FileDataSource isn't supported in 3.3
18-
test-packaging: false # packaging test requires virtualenv, which isn't supported in 3.3
19-
- test-linux:
20-
name: Python 3.4
21-
docker-image: circleci/python:3.4-jessie
22-
consul-supported: false # Consul isn't supported in 3.4
239
- test-linux:
2410
name: Python 3.5
2511
docker-image: circleci/python:3.5-jessie
@@ -32,30 +18,27 @@ workflows:
3218
- test-linux:
3319
name: Python 3.8
3420
docker-image: circleci/python:3.8-buster
21+
- test-linux:
22+
name: Python 3.9
23+
docker-image: circleci/python:3.9-rc-buster
3524
- test-windows:
36-
name: Windows Py2.7
37-
py3: false
38-
- test-windows:
39-
name: Windows Py3.3
25+
name: Windows Python 3
4026
py3: true
4127

4228
jobs:
4329
test-linux:
4430
parameters:
4531
docker-image:
4632
type: string
47-
consul-supported:
48-
type: boolean
49-
default: true
50-
filesource-supported:
51-
type: boolean
52-
default: true
5333
test-packaging:
5434
type: boolean
5535
default: true
5636
test-with-codeclimate:
5737
type: boolean
5838
default: false
39+
test-with-mypy:
40+
type: boolean
41+
default: true
5942
docker:
6043
- image: <<parameters.docker-image>>
6144
- image: redis
@@ -68,12 +51,8 @@ jobs:
6851
command: |
6952
sudo pip install --upgrade pip virtualenv;
7053
sudo pip install -r test-requirements.txt;
71-
if [[ "<<parameters.filesource-supported>>" == "true" ]]; then
72-
sudo pip install -r test-filesource-optional-requirements.txt;
73-
fi;
74-
if [[ "<<parameters.consul-supported>>" == "true" ]]; then
75-
sudo pip install -r consul-requirements.txt;
76-
fi;
54+
sudo pip install -r test-filesource-optional-requirements.txt;
55+
sudo pip install -r consul-requirements.txt;
7756
sudo python setup.py install;
7857
pip freeze
7958
- when:
@@ -101,6 +80,15 @@ jobs:
10180
command: |
10281
sudo rm -rf dist *.egg-info
10382
./test-packaging/test-packaging.sh
83+
- when:
84+
condition: <<parameters.test-with-mypy>>
85+
steps:
86+
- run:
87+
name: verify typehints
88+
command: |
89+
pip install mypy
90+
export PATH="/home/circleci/.local/bin:$PATH"
91+
mypy --config-file mypy.ini --python-version 3.5 ldclient/*.py testing/*.py
10492
- store_test_results:
10593
path: test-reports
10694
- store_artifacts:
@@ -115,21 +103,9 @@ jobs:
115103
type: boolean
116104
steps:
117105
- checkout
118-
- when:
119-
condition: <<parameters.py3>>
120-
steps:
121-
- run:
122-
name: install Python 3
123-
command: choco install python --no-progress
124-
- unless:
125-
condition: <<parameters.py3>>
126-
steps:
127-
- run:
128-
name: install Python 2.7
129-
command: |
130-
$ProgressPreference = "SilentlyContinue" # prevents console errors from CircleCI host
131-
iwr -outf python-2.7.16.amd64.msi https://www.python.org/ftp/python/2.7.16/python-2.7.16.amd64.msi
132-
Start-Process msiexec.exe -Wait -ArgumentList '/I python-2.7.16.amd64.msi /quiet'
106+
- run:
107+
name: install Python 3
108+
command: choco install python --no-progress
133109
- run:
134110
name: set up DynamoDB
135111
command: |
@@ -165,7 +141,6 @@ jobs:
165141
- run:
166142
name: install requirements
167143
command: |
168-
$env:Path += ";C:\Python27\;C:\Python27\Scripts\" # has no effect if 2.7 isn't installed
169144
python --version
170145
pip install -r test-requirements.txt
171146
pip install -r consul-requirements.txt
@@ -174,7 +149,6 @@ jobs:
174149
name: run tests
175150
command: |
176151
mkdir test-reports
177-
$env:Path += ";C:\Python27\;C:\Python27\Scripts\" # has no effect if 2.7 isn't installed
178152
python -m pytest -s --junitxml=test-reports/junit.xml testing;
179153
- store_test_results:
180154
path: test-reports

Diff for: MANIFEST.in

-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,3 @@ include test-requirements.txt
44
include consul-requirements.txt
55
include dynamodb-requirements.txt
66
include redis-requirements.txt
7-
include python2.6-requirements.txt

Diff for: README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
## LaunchDarkly overview
99

1010
[LaunchDarkly](https://www.launchdarkly.com) is a feature management platform that serves over 100 billion feature flags daily to help teams build better software, faster. [Get started](https://docs.launchdarkly.com/docs/getting-started) using LaunchDarkly today!
11-
11+
1212
[![Twitter Follow](https://img.shields.io/twitter/follow/launchdarkly.svg?style=social&label=Follow&maxAge=2592000)](https://twitter.com/intent/follow?screen_name=launchdarkly)
1313

1414
## Supported Python versions
1515

16-
This version of the LaunchDarkly SDK is compatible with Python 2.7 and 3.3 through 3.7. It is tested with the most recent patch releases of those versions. Python 2.6 is no longer supported.
16+
This version of the LaunchDarkly SDK is compatible with Python 3.5 through 3.9. It is tested with the most recent patch releases of those versions. Python versions 2.7 to 3.4 are no longer supported.
1717

1818
## Getting started
1919

Diff for: demo/demo.py

-22
This file was deleted.

Diff for: docs/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ help:
1313
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
1414

1515
install:
16-
pip install -r requirements.txt
16+
pip3 install -r requirements.txt
1717

1818
html: install
1919
@$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

Diff for: docs/api-main.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ldclient module
55
---------------
66

77
.. automodule:: ldclient
8-
:members: get,set_config,set_sdk_key
8+
:members: get,set_config
99

1010
ldclient.client module
1111
----------------------

Diff for: docs/conf.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
#
2+
# type: ignore
33
# Configuration file for the Sphinx documentation builder.
44
#
55
# This file does only contain a selection of the most common options. For a
@@ -46,6 +46,7 @@
4646
# ones.
4747
extensions = [
4848
'sphinx.ext.autodoc',
49+
'sphinx_autodoc_typehints',
4950
'sphinx.ext.coverage',
5051
'sphinx.ext.viewcode',
5152
]

Diff for: docs/requirements.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
sphinx<2.0
1+
sphinx
22
sphinx_rtd_theme
3+
sphinx-autodoc-typehints
34

45
backoff>=1.4.3
56
certifi>=2018.4.16
67
expiringdict>=1.1.4
7-
six>=1.10.0
88
pyRFC3339>=1.0
99
jsonpickle==0.9.3
1010
semver>=2.7.9

Diff for: ldclient/__init__.py

+12-77
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
The ldclient module contains the most common top-level entry points for the SDK.
33
"""
44

5-
import logging
6-
75
from ldclient.rwlock import ReadWriteLock
86
from ldclient.version import VERSION
97
from .client import *
@@ -20,27 +18,27 @@
2018
start_wait = 5
2119

2220
__client = None
23-
__config = Config()
21+
__config = None
2422
__lock = ReadWriteLock()
2523

2624

27-
def set_config(config):
25+
def set_config(config: Config):
2826
"""Sets the configuration for the shared SDK client instance.
2927
3028
If this is called prior to :func:`ldclient.get()`, it stores the configuration that will be used when the
3129
client is initialized. If it is called after the client has already been initialized, the client will be
3230
re-initialized with the new configuration (this will result in the next call to :func:`ldclient.get()`
3331
returning a new client instance).
3432
35-
:param ldclient.config.Config config: the client configuration
33+
:param config: the client configuration
3634
"""
3735
global __config
3836
global __client
3937
global __lock
4038
try:
4139
__lock.lock()
4240
if __client:
43-
log.info("Reinitializing LaunchDarkly Client " + version.VERSION + " with new config")
41+
log.info("Reinitializing LaunchDarkly Client " + VERSION + " with new config")
4442
new_client = LDClient(config=config, start_wait=start_wait)
4543
old_client = __client
4644
__client = new_client
@@ -50,57 +48,15 @@ def set_config(config):
5048
__lock.unlock()
5149

5250

53-
def set_sdk_key(sdk_key):
54-
"""Sets the SDK key for the shared SDK client instance.
55-
56-
If this is called prior to :func:`ldclient.get()`, it stores the SDK key that will be used when the client is
57-
initialized. If it is called after the client has already been initialized, the client will be
58-
re-initialized with the new SDK key (this will result in the next call to :func:`ldclient.get()` returning a
59-
new client instance).
60-
61-
If you need to set any configuration options other than the SDK key, use :func:`ldclient.set_config()` instead.
62-
63-
:param string sdk_key: the new SDK key
64-
"""
65-
global __config
66-
global __client
67-
global __lock
68-
sdk_key_changed = False
69-
try:
70-
__lock.rlock()
71-
if sdk_key == __config.sdk_key:
72-
log.info("New sdk_key is the same as the existing one. doing nothing.")
73-
else:
74-
sdk_key_changed = True
75-
finally:
76-
__lock.runlock()
77-
78-
if sdk_key_changed:
79-
try:
80-
__lock.lock()
81-
__config = __config.copy_with_new_sdk_key(new_sdk_key=sdk_key)
82-
if __client:
83-
log.info("Reinitializing LaunchDarkly Client " + version.VERSION + " with new sdk key")
84-
new_client = LDClient(config=__config, start_wait=start_wait)
85-
old_client = __client
86-
__client = new_client
87-
old_client.close()
88-
finally:
89-
__lock.unlock()
90-
91-
92-
def get():
51+
def get() -> LDClient:
9352
"""Returns the shared SDK client instance, using the current global configuration.
9453
95-
To use the SDK as a singleton, first make sure you have called :func:`ldclient.set_sdk_key()` or
96-
:func:`ldclient.set_config()` at startup time. Then ``get()`` will return the same shared
97-
:class:`ldclient.client.LDClient` instance each time. The client will be initialized if it has
98-
not been already.
54+
To use the SDK as a singleton, first make sure you have called :func:`ldclient.set_config()`
55+
at startup time. Then ``get()`` will return the same shared :class:`ldclient.client.LDClient`
56+
instance each time. The client will be initialized if it has not been already.
9957
10058
If you need to create multiple client instances with different configurations, instead of this
10159
singleton approach you can call the :class:`ldclient.client.LDClient` constructor directly instead.
102-
103-
:rtype: ldclient.client.LDClient
10460
"""
10561
global __config
10662
global __client
@@ -109,13 +65,15 @@ def get():
10965
__lock.rlock()
11066
if __client:
11167
return __client
68+
if __config is None:
69+
raise Exception("set_config was not called")
11270
finally:
11371
__lock.runlock()
11472

11573
try:
11674
__lock.lock()
11775
if not __client:
118-
log.info("Initializing LaunchDarkly Client " + version.VERSION)
76+
log.info("Initializing LaunchDarkly Client " + VERSION)
11977
__client = LDClient(config=__config, start_wait=start_wait)
12078
return __client
12179
finally:
@@ -136,27 +94,4 @@ def _reset_client():
13694
c.close()
13795

13896

139-
# currently hidden from documentation - see docs/README.md
140-
class NullHandler(logging.Handler):
141-
"""A :class:`logging.Handler` implementation that does nothing.
142-
143-
.. deprecated:: 6.0.0
144-
You should not need to use this class. It was originally used in order to support Python 2.6,
145-
which requires that at least one logging handler must always be configured. However, the SDK
146-
no longer supports Python 2.6.
147-
"""
148-
def emit(self, record):
149-
pass
150-
151-
152-
if not log.handlers:
153-
log.addHandler(NullHandler())
154-
155-
try:
156-
# noinspection PyUnresolvedReferences
157-
unicode
158-
except NameError:
159-
__BASE_TYPES__ = (str, float, int, bool)
160-
else:
161-
# noinspection PyUnresolvedReferences
162-
__BASE_TYPES__ = (str, float, int, bool, unicode)
97+
__BASE_TYPES__ = (str, float, int, bool)

0 commit comments

Comments
 (0)