Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prepare 7.2.0 release #157

Merged
merged 334 commits into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
334 commits
Select commit Hold shift + click to select a range
71b821f
fix import
eli-darkly Jan 31, 2019
424db63
Merge pull request #91 from launchdarkly/eb/ch31044/package-subdirs-fix
eli-darkly Jan 31, 2019
b2e6c59
Merge pull request #90 from launchdarkly/eb/ch31044/test-package-import
eli-darkly Jan 31, 2019
bee50e8
merge from public after release
LaunchDarklyCI Jan 31, 2019
06fc3b2
add basic pipeline and install deps
hroederld Feb 5, 2019
0165540
add pytest
hroederld Feb 5, 2019
7c9f4e2
remove explicit install of deps
hroederld Feb 5, 2019
a38b957
add other db deps
hroederld Feb 5, 2019
0b6d28f
major cleanup of doc comments, add Sphinx build script
eli-darkly Feb 5, 2019
71534aa
add consul to test-requirements, remove specific reference to install…
hroederld Feb 5, 2019
6bbd65f
Revert "add consul to test-requirements, remove specific reference to…
hroederld Feb 5, 2019
0ec55a7
remove redis and dynamo explicit dep reference
hroederld Feb 5, 2019
49c5993
add requirements.txt
eli-darkly Feb 5, 2019
5228df7
add config file
eli-darkly Feb 5, 2019
f4e5c86
break up API docs into logical groups with a better home page
eli-darkly Feb 5, 2019
9445a6e
misc cleanup
eli-darkly Feb 5, 2019
c496c35
misc cleanup
eli-darkly Feb 5, 2019
a6f1bca
Merge pull request #93 from launchdarkly/eb/ch17280/docs
eli-darkly Feb 5, 2019
68bb4e4
RTD config fixes
eli-darkly Feb 5, 2019
6766920
minor edit
eli-darkly Feb 5, 2019
f612360
Merge pull request #94 from launchdarkly/eb/ch17280/doc-templates
eli-darkly Feb 5, 2019
74e82c8
misc. doc comment edits
eli-darkly Feb 6, 2019
9316b0d
use RTD theme
eli-darkly Feb 6, 2019
794e59f
Merge pull request #92 from launchdarkly/hr/azure
hroederld Feb 6, 2019
5df3b55
Merge pull request #95 from launchdarkly/eb/ch17280/edits
eli-darkly Feb 6, 2019
5eee9ba
Merge pull request #96 from launchdarkly/eb/ch17280/rtd-theme
eli-darkly Feb 6, 2019
338910c
remove jsonpickle
eli-darkly Feb 11, 2019
f586cd1
misc doc comment/readme edits prior to publishing docs
eli-darkly Feb 11, 2019
125d359
Merge pull request #97 from launchdarkly/eb/ch31867/jsonpickle
eli-darkly Feb 12, 2019
0972671
Merge pull request #98 from launchdarkly/eb/ch17280/doc-edits
eli-darkly Feb 12, 2019
dc1f394
merge from public after release
LaunchDarklyCI Feb 12, 2019
9731f4d
add git placeholders for unused dirs
eli-darkly Feb 13, 2019
acfe58e
Merge branch 'master' of github.com:launchdarkly/python-client
eli-darkly Feb 13, 2019
87336db
use default theme
eli-darkly Feb 13, 2019
c7a67dc
Merge branch 'master' of github.com:launchdarkly/python-client
eli-darkly Feb 13, 2019
2dedbc4
add experimentation event overrides for rules and fallthrough
eli-darkly Feb 25, 2019
6846ba1
a little more test coverage
eli-darkly Feb 25, 2019
c514216
rm unnecessary logic
eli-darkly Feb 26, 2019
afab05d
more factory methods
eli-darkly Feb 26, 2019
e0c563c
Merge pull request #99 from launchdarkly/eb/ch32305/experiment
eli-darkly Feb 28, 2019
84198a3
try python -m instead of pytest directly
hroederld Mar 1, 2019
80411dd
add setuptools
hroederld Mar 1, 2019
52c0a19
use python -m for all of pip
hroederld Mar 1, 2019
5bdea5f
add UsePythonVersion task
hroederld Mar 1, 2019
60a66a8
fix indent
hroederld Mar 1, 2019
1907d75
remove manually adding setuptools
hroederld Mar 1, 2019
7cdf9fc
add on 3.7 stages
hroederld Mar 2, 2019
1023d45
fix mkdir for reports
hroederld Mar 2, 2019
b9778b6
upload test artifacts
hroederld Mar 2, 2019
945fc07
Merge pull request #100 from launchdarkly/hr/azurepytest
hroederld Mar 2, 2019
7738c99
Merge commit '327aaac4a5c419ea45d3723d4a87309f67da4a40'
eli-darkly Mar 13, 2019
e5d5e41
skip trying to load pyyaml in Python 3.3
eli-darkly Mar 26, 2019
fd883cd
can't use watchdog in Python 3.3
eli-darkly Mar 26, 2019
b3dc4c4
mark test as skipped
eli-darkly Mar 26, 2019
bd4daf7
Merge pull request #101 from launchdarkly/eb/ch34481/pyyaml-py3.3
eli-darkly Mar 26, 2019
803a794
coerce user attributes into strings when necessary, don't send events…
eli-darkly Mar 29, 2019
b7035a5
more unit tests
eli-darkly Mar 29, 2019
44101b2
remove redundant sanitize step
eli-darkly Mar 29, 2019
4b8ee84
Merge pull request #102 from launchdarkly/eb/ch35206/stringify-attrs
eli-darkly Mar 29, 2019
dd67a7c
merge from public after release
LaunchDarklyCI Mar 29, 2019
ddfb3c2
ensure that client components are cleaned up correct in every configu…
eli-darkly Apr 9, 2019
4ca26c7
Merge pull request #103 from launchdarkly/eb/ch36211/close-ldd
eli-darkly Apr 9, 2019
7585684
miscellaneous test fixes
eli-darkly Apr 9, 2019
a164906
Merge pull request #104 from launchdarkly/eb/fix-tests
eli-darkly Apr 9, 2019
143b66a
merge from public after release
LaunchDarklyCI Apr 10, 2019
6763b54
Merge branch 'master' into experiment
eli-darkly Apr 13, 2019
3b16ebf
support metric value with track()
eli-darkly Apr 13, 2019
2f6961d
update method description
eli-darkly Apr 17, 2019
a166adb
Merge pull request #105 from launchdarkly/eb/ch32305/metric-value
eli-darkly Apr 17, 2019
950d2b3
Merge branch 'master' of github.com:launchdarkly/python-client
eli-darkly Apr 24, 2019
902be02
update readme format and repo links
eli-darkly Apr 26, 2019
f41f2cc
allow unit tests to be run without databases
eli-darkly Apr 26, 2019
d764fd8
add missing test
eli-darkly Apr 26, 2019
91b1250
Merge pull request #107 from launchdarkly/eb/ch37610/no-db-tests
eli-darkly Apr 26, 2019
ea5d8e8
rm FOSSA link/badge
eli-darkly Apr 26, 2019
6ed12f1
misc fixes
eli-darkly Apr 26, 2019
6cd745f
Merge pull request #106 from launchdarkly/eb/ch36754/readme-update
eli-darkly Apr 26, 2019
1fba96f
merge from public after release
LaunchDarklyCI Apr 26, 2019
cbac044
minor doc link fix
eli-darkly Apr 26, 2019
52c3b23
fix skipping of database tests
eli-darkly May 1, 2019
6d105a9
Merge pull request #109 from launchdarkly/eb/ch37610/fix-skip-db-tests
eli-darkly May 1, 2019
6161055
renaming the package to launchdarkly-server-sdk (#108)
bwoskow-ld May 1, 2019
47f2e3e
Merge branch 'master' of github.com:launchdarkly/python-server-sdk
bwoskow-ld May 2, 2019
ac3b1e8
merge from public after release
LaunchDarklyCI May 2, 2019
b9011c0
Merge branch 'master' of github.com:launchdarkly/python-server-sdk
bwoskow-ld May 3, 2019
83450d9
Merge branch 'master' of github.com:launchdarkly/python-server-sdk-pr…
bwoskow-ld May 3, 2019
34b15f5
use log.warning(), not log.warn() or warnings.warn()
eli-darkly Jun 11, 2019
3a03ea5
Merge pull request #110 from launchdarkly/eb/ch40725/log-warnings
eli-darkly Jun 11, 2019
b472691
merge from public after release
LaunchDarklyCI Jun 11, 2019
c990266
drop events when inbox is full
eli-darkly Aug 19, 2019
e436f77
rm obsolete pytest.raises parameter
eli-darkly Aug 19, 2019
1e068c9
clean up test state management
eli-darkly Aug 20, 2019
4b74fcf
typo
eli-darkly Aug 20, 2019
e5dd5ba
Merge branch 'master' of github.com:launchdarkly/python-server-sdk
eli-darkly Aug 20, 2019
71a24f2
Merge branch 'master' into experiment
eli-darkly Aug 20, 2019
be6ee0d
Merge pull request #111 from launchdarkly/eb/ch42975/inbox-full
eli-darkly Aug 20, 2019
623687d
merge from public after release
LaunchDarklyCI Aug 20, 2019
0b0b636
Merge branch 'master' of github.com:launchdarkly/python-server-sdk
eli-darkly Aug 20, 2019
03f7a86
merge from public after release
eli-darkly Aug 20, 2019
16f3d43
Merge branch 'experiment'
eli-darkly Aug 20, 2019
ee7a51c
store the package version in just one place
eli-darkly Aug 20, 2019
1c10e1e
fix package reference
eli-darkly Aug 20, 2019
d9c96dd
add requirements
eli-darkly Aug 20, 2019
73d20f7
don't import ldclient.version directly
eli-darkly Aug 20, 2019
0a0aa8f
Revert "add requirements"
eli-darkly Aug 20, 2019
11f0da6
fix merge error + adjust for some event properties now being optional
eli-darkly Aug 20, 2019
17bfa5a
fix summary logic again for now-optional event properties
eli-darkly Aug 20, 2019
8bb3375
merge from public after release
LaunchDarklyCI Aug 20, 2019
67eb763
merge from public after release
eli-darkly Aug 20, 2019
53b981a
Merge branch 'master' into eb/test-cleanup
eli-darkly Aug 20, 2019
500fa28
Merge branch 'master' into eb/package-version
eli-darkly Aug 20, 2019
42ad0be
Merge pull request #112 from launchdarkly/eb/test-cleanup
eli-darkly Aug 20, 2019
1154981
Merge pull request #114 from launchdarkly/eb/package-version
eli-darkly Aug 20, 2019
a5da010
Allow explicitly proxying only ld requests (#130)
gangeli Oct 25, 2019
69f2233
fix broken indirect/patch request, add tests for feature requestor
eli-darkly Oct 28, 2019
0fa5e05
Python 2/3 compatibility for HTTPServer
eli-darkly Oct 28, 2019
e75ff0f
Py2/3 compatibility: queue
eli-darkly Oct 28, 2019
68161a2
more Py3 compatibility
eli-darkly Oct 28, 2019
74c9eed
don't need import of builtins
eli-darkly Oct 28, 2019
032b04c
fix string encoding
eli-darkly Oct 28, 2019
89ce3e2
implement setting proxy URL by environment variable
eli-darkly Oct 29, 2019
ae764b5
rm debugging
eli-darkly Oct 29, 2019
28ee4b5
fix autodoc options to exclude magic methods
eli-darkly Oct 29, 2019
4cbcd30
Merge pull request #115 from launchdarkly/eb/ch54217/indirect-patch
eli-darkly Oct 30, 2019
9fb16a6
Merge pull request #117 from launchdarkly/eb/ch38000/fix-doc-config
eli-darkly Oct 30, 2019
d47c315
Merge pull request #116 from launchdarkly/eb/ch53090/proxy-var
eli-darkly Oct 30, 2019
3dddca2
merge from public after release
LaunchDarklyCI Oct 30, 2019
9435aee
add config option for proxy URL
eli-darkly Oct 31, 2019
4fc6ce7
comment
eli-darkly Oct 31, 2019
00432be
add end-to-end unit tests for proxy config
eli-darkly Oct 31, 2019
5911fd9
indents
eli-darkly Oct 31, 2019
ba52cae
Merge pull request #118 from launchdarkly/eb/ch52414/proxy-config
eli-darkly Oct 31, 2019
28b22b8
merge from public after release
LaunchDarklyCI Oct 31, 2019
35faf7d
Merge branch 'master' of github.com:launchdarkly/python-server-sdk
bwoskow-ld Nov 7, 2019
63125f5
add 3.8 build
eli-darkly Nov 21, 2019
3c68cd2
image name
eli-darkly Nov 21, 2019
9b1adf3
fail on SyntaxWarning
eli-darkly Nov 21, 2019
6a954e3
typo
eli-darkly Nov 21, 2019
d6bf44c
command syntax
eli-darkly Nov 21, 2019
7ec6974
Merge pull request #119 from launchdarkly/eb/ch56849/py3.8
eli-darkly Nov 21, 2019
7fda088
Merge branch 'master' of github.com:launchdarkly/python-server-sdk
eli-darkly Nov 21, 2019
7b3177f
pin expiringdict dependency for Python 3.3 compatibility
eli-darkly Nov 21, 2019
9942d77
add Windows CircleCI job
eli-darkly Nov 21, 2019
38f3f43
periods are no longer valid in CircleCI job names
eli-darkly Nov 21, 2019
c969db2
syntax fix
eli-darkly Nov 21, 2019
bc31ec9
install Python in Windows
eli-darkly Nov 21, 2019
64486a3
set path
eli-darkly Nov 21, 2019
37509ff
move command
eli-darkly Nov 21, 2019
3b41766
turn off debug logging
eli-darkly Nov 21, 2019
ef68058
Py3 in Windows
eli-darkly Nov 21, 2019
0c93df7
config param
eli-darkly Nov 21, 2019
86d27a8
rm redundant step
eli-darkly Nov 21, 2019
001e196
choco switch
eli-darkly Nov 21, 2019
121ec89
Merge pull request #120 from launchdarkly/eb/ch55512/py3.3-deps
eli-darkly Nov 21, 2019
9905cd5
Merge branch 'master' into eb/ch56746/windows-ci
eli-darkly Nov 21, 2019
23a4222
refactor Linux jobs using CircleCI 2.1 features
eli-darkly Nov 21, 2019
a5aaa99
set log level before anything else
eli-darkly Nov 21, 2019
9e40321
rm Azure config
eli-darkly Nov 21, 2019
d69fba0
Merge pull request #121 from launchdarkly/eb/ch56746/windows-ci
eli-darkly Nov 21, 2019
3e9c68b
merge from public after release
LaunchDarklyCI Nov 21, 2019
669e772
use yaml.safe_load() to avoid code execution vulnerability in file da…
eli-darkly Dec 9, 2019
4483990
Merge pull request #122 from launchdarkly/eb/ch58025/yaml-load
eli-darkly Dec 9, 2019
8248bae
merge from public after release
LaunchDarklyCI Dec 10, 2019
52238d1
Initial work on wrapper_name, wrapper_version, diagnostic config
gwhelanLD Dec 16, 2019
38d08bd
Python 2 compat changes.
gwhelanLD Dec 17, 2019
e3eb3ee
More event generation code and starting to integrate tracking diagnos…
gwhelanLD Dec 17, 2019
2d80198
Add minimum diagnostic recording interval. Fix diagnostic.py to be
gwhelanLD Dec 20, 2019
7fd454f
don't let user fall outside of last bucket in rollout
bwoskow-ld Dec 24, 2019
588f352
fixing conditional logic
bwoskow-ld Dec 24, 2019
7b357b0
Add docstrings for diagnostic configuration options.
gwhelanLD Dec 28, 2019
af5a162
fix off-by-1 error
eli-darkly Dec 30, 2019
75a9aab
avoid redundant dict lookups
eli-darkly Dec 30, 2019
590ca64
add unit tests for basic bucketing logic and edge case
eli-darkly Dec 30, 2019
8ce7cb8
Merge pull request #123 from launchdarkly/bw/ch43307/bucket-issue
eli-darkly Dec 30, 2019
87617a3
merge from public after release
LaunchDarklyCI Dec 30, 2019
0f09a73
Stream init tracking. Feeding of accumulator object through SDK. Vari…
gwhelanLD Dec 30, 2019
e50ad29
Track events in last batch.
gwhelanLD Dec 30, 2019
f6ad201
Fix sdk version field, some stylistic improvements.
gwhelanLD Dec 30, 2019
781c2ba
Merge branch 'diag-events' into gw/ch59328/diagnostic-events
gwhelanLD Dec 30, 2019
0375f70
Last of diagnostic configuration object fields.
gwhelanLD Dec 31, 2019
5f2ca11
Fill out rest of platform fields.
gwhelanLD Dec 31, 2019
49a4ea9
Cleanup and failed stream initialization tracking.
gwhelanLD Dec 31, 2019
6f9ca76
Add diagnostic config option test.
gwhelanLD Dec 31, 2019
aa703fb
Add tests for diagnostics.py
gwhelanLD Dec 31, 2019
18d7340
Testing rest of diagnostic fields.
gwhelanLD Dec 31, 2019
c6904c7
Test that streaming update processor records successful and unsuccessful
gwhelanLD Dec 31, 2019
0f9f65c
Improvements to testability of event processor.
gwhelanLD Jan 2, 2020
689b231
Rest of event processor tests.
gwhelanLD Jan 2, 2020
08740f1
Remove janky reflection.
gwhelanLD Jan 6, 2020
a26d458
Test change to filesource optional test requirements.
gwhelanLD Jan 6, 2020
6d5945a
[ch59328] Diagnostic events
gwhelanLD Jan 14, 2020
ef256a5
[ch61092] Add event payload ID on event requests.
gwhelanLD Jan 17, 2020
2866b6d
Merge branch 'master' into diag-events
eli-darkly Feb 11, 2020
3a525e3
normalize data store type and OS name in diagnostic events
eli-darkly Feb 11, 2020
425dceb
gitignore
eli-darkly Feb 11, 2020
71c85b8
Merge pull request #126 from launchdarkly/eb/ch62088/spec-updates
eli-darkly Feb 11, 2020
27fb9a7
copyedit to diagnostic event config property comment
eli-darkly Feb 11, 2020
c93e4ff
Merge pull request #127 from launchdarkly/eb/ch59328/diag-config-comment
eli-darkly Feb 11, 2020
1b0be34
merge from public after release
LaunchDarklyCI Feb 11, 2020
13ddc54
fix spurious error after sending diagnostic event
eli-darkly Feb 12, 2020
a4fc092
Merge pull request #128 from launchdarkly/eb/ch65536/diag-events-warn…
eli-darkly Feb 12, 2020
02f5626
merge from public after release
LaunchDarklyCI Feb 12, 2020
ad248d6
make verify_ssl=False turn off certificate verification too (#129)
eli-darkly Mar 19, 2020
e754335
merge from public after release
LaunchDarklyCI Mar 20, 2020
b7d081b
add more TLS config options and collect HTTP/HTTPS config options in …
eli-darkly Mar 25, 2020
770fd71
make stream retry/backoff/jitter behavior consistent with other SDKs …
eli-darkly Mar 27, 2020
4016887
streams shouldn't use the same read timeout as the rest of the SDK (#…
eli-darkly Mar 30, 2020
f7ec18a
merge from public after release
LaunchDarklyCI Mar 30, 2020
02a803f
implement our own retry logic & logging for event posts, don't use ur…
eli-darkly May 9, 2020
50f2d94
remove support for indirect/patch and indirect/put
eli-darkly Jun 23, 2020
a246026
Merge pull request #134 from launchdarkly/eb/ch80666/no-indirect-patch
eli-darkly Jun 23, 2020
a9fe218
remove unused logic for individual flag/segment poll for indirect/patch
eli-darkly Jun 25, 2020
aaa9455
Merge pull request #135 from launchdarkly/eb/ch80666/no-indirect-patch
eli-darkly Jun 26, 2020
89fa623
merge from public after release
LaunchDarklyCI Jul 13, 2020
e1c93da
Ehaisley/84082/remove python2 (#136)
apache-hb Jul 30, 2020
3095315
Allow authenticating with proxy
gangeli Sep 15, 2020
eb7fc76
Merge pull request #145 from gangeli/master
eli-darkly Sep 16, 2020
14dfd8e
Merge branch 'contrib' of github.com:launchdarkly/python-server-sdk i…
eli-darkly Sep 16, 2020
1de0769
reimplement proxy tests for DRY and add test of proxy auth params
eli-darkly Sep 21, 2020
ed01be0
doc comment on auth params in proxy URL
eli-darkly Sep 16, 2020
6d98ae7
Merge pull request #137 from launchdarkly/eb/ch89791/proxy-auth-in-url
eli-darkly Sep 21, 2020
7bff994
Merge branch '6.x' of github.com:launchdarkly/python-server-sdk into 6.x
eli-darkly Sep 21, 2020
8d34668
merge from public after release
LaunchDarklyCI Sep 21, 2020
b5213b2
Merge branch '6.x'
eli-darkly Sep 22, 2020
c35fa61
add type hints to some of the public facing api.
apache-hb Sep 28, 2020
7745881
Revert "add type hints to some of the public facing api."
apache-hb Sep 28, 2020
43b4c31
Ehaisley/ch86857/type hints (#138)
apache-hb Oct 15, 2020
58b5bc3
remove all current deprecations (#139)
apache-hb Oct 27, 2020
b35ec6e
remove global set_sdk_key, make SDK key required in Config (#140)
eli-darkly Oct 27, 2020
5e3e5c5
Merge branch '6.x' of github.com:launchdarkly/python-server-sdk-private
eli-darkly Oct 28, 2020
3b904f3
merge from public after release
LaunchDarklyCI Oct 28, 2020
eb65618
Merge branch 'master' of github.com:launchdarkly/python-server-sdk
eli-darkly Oct 28, 2020
ebd092d
Merge branch 'master' of github.com:launchdarkly/python-server-sdk
eli-darkly Nov 25, 2020
528dc22
merge from public after release
LaunchDarklyCI Nov 26, 2020
e5f6450
Removed the guides link
bwoskow-ld Feb 3, 2021
4d2e999
Pinning mypy and running it against different python versions (#141)
bwoskow-ld Feb 5, 2021
1cc83f5
fix time zone mishandling that could make event debugging not work (#…
eli-darkly Feb 19, 2021
dc955d3
merge from public after release
LaunchDarklyCI Feb 19, 2021
5fa5966
fix 6.x build (#143)
eli-darkly Feb 23, 2021
c9d6ec6
fix time zone mishandling that could make event debugging not work (6…
eli-darkly Feb 23, 2021
f9ce3b9
prepare 6.13.3 release (#154)
LaunchDarklyCI Feb 23, 2021
953c126
Releasing version 6.13.3
LaunchDarklyCI Feb 23, 2021
faec38d
merge from public after release
LaunchDarklyCI Feb 23, 2021
b740df9
Merge branch '6.x'
eli-darkly Feb 24, 2021
532a01b
[ch99756] Add alias events (#145)
hroederld Mar 11, 2021
35227bf
merge from public after release
LaunchDarklyCI Mar 12, 2021
8c327d0
add support for experiment rollouts
robertjneal Apr 29, 2021
31c1c21
fix unit test
robertjneal Apr 29, 2021
f52ab39
address PR comments
robertjneal May 6, 2021
15f9a97
Merge pull request #146 from launchdarkly/rneal/ch101662/add-support-…
robertjneal May 11, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions ldclient/flag.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,11 @@ def _get_off_value(flag, reason):


def _get_value_for_variation_or_rollout(flag, vr, user, reason):
index = _variation_index_for_user(flag, vr, user)
index, inExperiment = _variation_index_for_user(flag, vr, user)
if index is None:
return EvaluationDetail(None, None, error_reason('MALFORMED_FLAG'))
if inExperiment:
reason['inExperiment'] = inExperiment
return _get_variation(flag, index, reason)


Expand All @@ -191,34 +193,38 @@ def _get_user_attribute(user, attr):

def _variation_index_for_user(feature, rule, user):
if rule.get('variation') is not None:
return rule['variation']
return (rule['variation'], False)

rollout = rule.get('rollout')
if rollout is None:
return None
return (None, False)
variations = rollout.get('variations')
seed = rollout.get('seed')
if variations is not None and len(variations) > 0:
bucket_by = 'key'
if rollout.get('bucketBy') is not None:
bucket_by = rollout['bucketBy']
bucket = _bucket_user(user, feature['key'], feature['salt'], bucket_by)
bucket = _bucket_user(seed, user, feature['key'], feature['salt'], bucket_by)
is_experiment = rollout.get('kind') == 'experiment'
sum = 0.0
for wv in variations:
sum += wv.get('weight', 0.0) / 100000.0
if bucket < sum:
return wv.get('variation')
is_experiment_partition = is_experiment and not wv.get('untracked')
return (wv.get('variation'), is_experiment_partition)

# The user's bucket value was greater than or equal to the end of the last bucket. This could happen due
# to a rounding error, or due to the fact that we are scaling to 100000 rather than 99999, or the flag
# data could contain buckets that don't actually add up to 100000. Rather than returning an error in
# this case (or changing the scaling, which would potentially change the results for *all* users), we
# will simply put the user in the last bucket.
return variations[-1].get('variation')
is_experiment_partition = is_experiment and not variations[-1].get('untracked')
return (variations[-1].get('variation'), is_experiment_partition)

return None
return (None, False)


def _bucket_user(user, key, salt, bucket_by):
def _bucket_user(seed, user, key, salt, bucket_by):
u_value, should_pass = _get_user_attribute(user, bucket_by)
bucket_by_value = _bucketable_string_value(u_value)

Expand All @@ -228,7 +234,13 @@ def _bucket_user(user, key, salt, bucket_by):
id_hash = u_value
if user.get('secondary') is not None:
id_hash = id_hash + '.' + user['secondary']
hash_key = '%s.%s.%s' % (key, salt, id_hash)

if seed is not None:
prefix = str(seed)
else:
prefix = '%s.%s' % (key, salt)

hash_key = '%s.%s' % (prefix, id_hash)
hash_val = int(hashlib.sha1(hash_key.encode('utf-8')).hexdigest()[:15], 16)
result = hash_val / __LONG_SCALE__
return result
Expand Down Expand Up @@ -294,7 +306,7 @@ def _segment_rule_matches_user(rule, user, segment_key, salt):

# All of the clauses are met. See if the user buckets in
bucket_by = 'key' if rule.get('bucketBy') is None else rule['bucketBy']
bucket = _bucket_user(user, segment_key, salt, bucket_by)
bucket = _bucket_user(None, user, segment_key, salt, bucket_by)
weight = rule['weight'] / 100000.0
return bucket < weight

Expand Down
2 changes: 2 additions & 0 deletions ldclient/impl/event_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ def _user_to_context_kind(self, user):

def _is_experiment(self, flag, reason):
if reason is not None:
if reason.get('inExperiment'):
return True
kind = reason['kind']
if kind == 'RULE_MATCH':
index = reason['ruleIndex']
Expand Down
72 changes: 72 additions & 0 deletions testing/test_event_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import pytest
from ldclient.flag import EvaluationDetail
from ldclient.impl.event_factory import _EventFactory

_event_factory_default = _EventFactory(False)
_user = { 'key': 'x' }

def make_basic_flag_with_rules(kind, should_track_events):
rule = {
'rollout': {
'variations': [
{ 'variation': 0, 'weight': 50000 },
{ 'variation': 1, 'weight': 50000 }
]
}
}
if kind == 'rulematch':
rule.update({'trackEvents': should_track_events})

flag = {
'key': 'feature',
'on': True,
'rules': [rule],
'fallthrough': { 'variation': 0 },
'variations': [ False, True ],
'salt': ''
}
if kind == 'fallthrough':
flag.update({'trackEventsFallthrough': should_track_events})
return flag

def test_fallthrough_track_event_false():
flag = make_basic_flag_with_rules('fallthrough', False)
detail = EvaluationDetail('b', 1, {'kind': 'FALLTHROUGH'})

eval = _event_factory_default.new_eval_event(flag, _user, detail, 'b', None)
assert eval.get('trackEvents') is None

def test_fallthrough_track_event_true():
flag = make_basic_flag_with_rules('fallthrough', True)
detail = EvaluationDetail('b', 1, {'kind': 'FALLTHROUGH'})

eval = _event_factory_default.new_eval_event(flag, _user, detail, 'b', None)
assert eval['trackEvents'] == True

def test_fallthrough_track_event_false_with_experiment():
flag = make_basic_flag_with_rules('fallthrough', False)
detail = EvaluationDetail('b', 1, {'kind': 'FALLTHROUGH', 'inExperiment': True})

eval = _event_factory_default.new_eval_event(flag, _user, detail, 'b', None)
assert eval['trackEvents'] == True

def test_rulematch_track_event_false():
flag = make_basic_flag_with_rules('rulematch', False)
detail = EvaluationDetail('b', 1, {'kind': 'RULE_MATCH', 'ruleIndex': 0})

eval = _event_factory_default.new_eval_event(flag, _user, detail, 'b', None)
assert eval.get('trackEvents') is None

def test_rulematch_track_event_true():
flag = make_basic_flag_with_rules('rulematch', True)
detail = EvaluationDetail('b', 1, {'kind': 'RULE_MATCH', 'ruleIndex': 0})

eval = _event_factory_default.new_eval_event(flag, _user, detail, 'b', None)
assert eval['trackEvents'] == True

def test_rulematch_track_event_false_with_experiment():
flag = make_basic_flag_with_rules('rulematch', False)
detail = EvaluationDetail('b', 1, {'kind': 'RULE_MATCH', 'ruleIndex': 0, 'inExperiment': True})

eval = _event_factory_default.new_eval_event(flag, _user, detail, 'b', None)
assert eval['trackEvents'] == True
53 changes: 43 additions & 10 deletions testing/test_flag.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ def test_variation_index_is_returned_for_bucket():

# First verify that with our test inputs, the bucket value will be greater than zero and less than 100000,
# so we can construct a rollout whose second bucket just barely contains that value
bucket_value = math.trunc(_bucket_user(user, flag['key'], flag['salt'], 'key') * 100000)
bucket_value = math.trunc(_bucket_user(None, user, flag['key'], flag['salt'], 'key') * 100000)
assert bucket_value > 0 and bucket_value < 100000

bad_variation_a = 0
Expand All @@ -407,14 +407,14 @@ def test_variation_index_is_returned_for_bucket():
}
}
result_variation = _variation_index_for_user(flag, rule, user)
assert result_variation == matched_variation
assert result_variation == (matched_variation, False)

def test_last_bucket_is_used_if_bucket_value_equals_total_weight():
user = { 'key': 'userkey' }
flag = { 'key': 'flagkey', 'salt': 'salt' }

# We'll construct a list of variations that stops right at the target bucket value
bucket_value = math.trunc(_bucket_user(user, flag['key'], flag['salt'], 'key') * 100000)
bucket_value = math.trunc(_bucket_user(None, user, flag['key'], flag['salt'], 'key') * 100000)

rule = {
'rollout': {
Expand All @@ -424,21 +424,35 @@ def test_last_bucket_is_used_if_bucket_value_equals_total_weight():
}
}
result_variation = _variation_index_for_user(flag, rule, user)
assert result_variation == 0
assert result_variation == (0, False)

def test_bucket_by_user_key():
user = { u'key': u'userKeyA' }
bucket = _bucket_user(user, 'hashKey', 'saltyA', 'key')
bucket = _bucket_user(None, user, 'hashKey', 'saltyA', 'key')
assert bucket == pytest.approx(0.42157587)

user = { u'key': u'userKeyB' }
bucket = _bucket_user(user, 'hashKey', 'saltyA', 'key')
bucket = _bucket_user(None, user, 'hashKey', 'saltyA', 'key')
assert bucket == pytest.approx(0.6708485)

user = { u'key': u'userKeyC' }
bucket = _bucket_user(user, 'hashKey', 'saltyA', 'key')
bucket = _bucket_user(None, user, 'hashKey', 'saltyA', 'key')
assert bucket == pytest.approx(0.10343106)

def test_bucket_by_user_key_with_seed():
seed = 61
user = { u'key': u'userKeyA' }
point = _bucket_user(seed, user, 'hashKey', 'saltyA', 'key')
assert point == pytest.approx(0.09801207)

user = { u'key': u'userKeyB' }
point = _bucket_user(seed, user, 'hashKey', 'saltyA', 'key')
assert point == pytest.approx(0.14483777)

user = { u'key': u'userKeyC' }
point = _bucket_user(seed, user, 'hashKey', 'saltyA', 'key')
assert point == pytest.approx(0.9242641)

def test_bucket_by_int_attr():
user = {
u'key': u'userKey',
Expand All @@ -447,9 +461,9 @@ def test_bucket_by_int_attr():
u'stringAttr': u'33333'
}
}
bucket = _bucket_user(user, 'hashKey', 'saltyA', 'intAttr')
bucket = _bucket_user(None, user, 'hashKey', 'saltyA', 'intAttr')
assert bucket == pytest.approx(0.54771423)
bucket2 = _bucket_user(user, 'hashKey', 'saltyA', 'stringAttr')
bucket2 = _bucket_user(None, user, 'hashKey', 'saltyA', 'stringAttr')
assert bucket2 == bucket

def test_bucket_by_float_attr_not_allowed():
Expand All @@ -459,5 +473,24 @@ def test_bucket_by_float_attr_not_allowed():
u'floatAttr': 33.5
}
}
bucket = _bucket_user(user, 'hashKey', 'saltyA', 'floatAttr')
bucket = _bucket_user(None, user, 'hashKey', 'saltyA', 'floatAttr')
assert bucket == 0.0

def test_seed_independent_of_salt_and_hashKey():
seed = 61
user = { u'key': u'userKeyA' }
point1 = _bucket_user(seed, user, 'hashKey', 'saltyA', 'key')
point2 = _bucket_user(seed, user, 'hashKey', 'saltyB', 'key')
point3 = _bucket_user(seed, user, 'hashKey2', 'saltyA', 'key')

assert point1 == point2
assert point2 == point3

def test_seed_changes_hash_evaluation():
seed1 = 61
user = { u'key': u'userKeyA' }
point1 = _bucket_user(seed1, user, 'hashKey', 'saltyA', 'key')
seed2 = 62
point2 = _bucket_user(seed2, user, 'hashKey', 'saltyB', 'key')

assert point1 != point2