Skip to content

Commit fa37c6c

Browse files
fix: PERCENTAGE_SPLIT working for undefined keys (#256)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent c58dc38 commit fa37c6c

File tree

7 files changed

+490
-297
lines changed

7 files changed

+490
-297
lines changed

.github/workflows/pull-request.yml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,6 @@ jobs:
3535
python -m pip install --upgrade pip
3636
pip install -r requirements.txt -r requirements-dev.txt
3737
38-
- name: Check Formatting
39-
run: black --check .
40-
41-
- name: Check Imports
42-
run: |
43-
git ls-files | grep '\.py$' | xargs absolufy-imports
44-
isort . --check
45-
46-
- name: Check flake8 linting
47-
run: flake8 .
48-
4938
- name: Check Typing
5039
run: mypy --strict .
5140

.pre-commit-config.yaml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
repos:
2-
- repo: https://github.com/pre-commit/mirrors-mypy
3-
rev: v1.5.1
4-
hooks:
5-
- id: mypy
6-
args: [--strict]
7-
additional_dependencies: [pydantic, pytest, pytest_mock, types-pytest-lazy-fixture, types-setuptools, semver]
82
- repo: https://github.com/MarcoGorelli/absolufy-imports
93
rev: v0.3.1
104
hooks:
@@ -33,3 +27,15 @@ repos:
3327
rev: v2.7.1
3428
hooks:
3529
- id: prettier
30+
- repo: local
31+
hooks:
32+
- id: python-typecheck
33+
name: python-typecheck
34+
language: system
35+
entry: mypy --strict flag_engine tests
36+
require_serial: true
37+
pass_filenames: false
38+
types: [python]
39+
40+
ci:
41+
skip: [python-typecheck]

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "7.0.0"
2+
".": "7.0.0"
33
}

CHANGELOG.md

Lines changed: 360 additions & 216 deletions
Large diffs are not rendered by default.

flag_engine/segments/evaluator.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,15 @@ def context_matches_condition(
216216
if condition["operator"] == constants.PERCENTAGE_SPLIT:
217217
if context_value is not None:
218218
object_ids = [segment_key, context_value]
219+
elif identity_context := context.get("identity"):
220+
object_ids = [segment_key, identity_context["key"]]
219221
else:
220-
object_ids = [segment_key, get_context_value(context, "$.identity.key")]
222+
return False
221223

222-
float_value = float(condition["value"])
224+
try:
225+
float_value = float(condition["value"])
226+
except ValueError:
227+
return False
223228
return get_hashed_percentage_for_object_ids(object_ids) <= float_value
224229

225230
if condition["operator"] == constants.IS_NOT_SET:

release-please-config.json

Lines changed: 60 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,62 @@
11
{
2-
"bootstrap-sha": "902aa899ce156012e6551fe9a8499848ccafdfbf",
3-
"packages": {
4-
".": {
5-
"release-type": "python",
6-
"changelog-path": "CHANGELOG.md",
7-
"bump-minor-pre-major": false,
8-
"bump-patch-for-minor-pre-major": false,
9-
"draft": false,
10-
"prerelease": false,
11-
"include-component-in-tag": false
12-
}
13-
},
14-
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
15-
"changelog-sections": [
16-
{
17-
"type": "feat",
18-
"hidden": false,
19-
"section": "Features"
20-
},
21-
{
22-
"type": "fix",
23-
"hidden": false,
24-
"section": "Bug Fixes"
25-
},
26-
{
27-
"type": "ci",
28-
"hidden": false,
29-
"section": "CI"
30-
},
31-
{
32-
"type": "docs",
33-
"hidden": false,
34-
"section": "Docs"
35-
},
36-
{
37-
"type": "deps",
38-
"hidden": false,
39-
"section": "Dependency Updates"
40-
},
41-
{
42-
"type": "perf",
43-
"hidden": false,
44-
"section": "Performance Improvements"
45-
},
46-
{
47-
"type": "refactor",
48-
"hidden": false,
49-
"section": "Refactoring"
50-
},
51-
{
52-
"type": "test",
53-
"hidden": false,
54-
"section": "Tests"
55-
},
56-
{
57-
"type": "chore",
58-
"hidden": false,
59-
"section": "Other"
60-
}
61-
]
2+
"bootstrap-sha": "902aa899ce156012e6551fe9a8499848ccafdfbf",
3+
"packages": {
4+
".": {
5+
"release-type": "python",
6+
"changelog-path": "CHANGELOG.md",
7+
"bump-minor-pre-major": false,
8+
"bump-patch-for-minor-pre-major": false,
9+
"draft": false,
10+
"prerelease": false,
11+
"include-component-in-tag": false
12+
}
13+
},
14+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
15+
"changelog-sections": [
16+
{
17+
"type": "feat",
18+
"hidden": false,
19+
"section": "Features"
20+
},
21+
{
22+
"type": "fix",
23+
"hidden": false,
24+
"section": "Bug Fixes"
25+
},
26+
{
27+
"type": "ci",
28+
"hidden": false,
29+
"section": "CI"
30+
},
31+
{
32+
"type": "docs",
33+
"hidden": false,
34+
"section": "Docs"
35+
},
36+
{
37+
"type": "deps",
38+
"hidden": false,
39+
"section": "Dependency Updates"
40+
},
41+
{
42+
"type": "perf",
43+
"hidden": false,
44+
"section": "Performance Improvements"
45+
},
46+
{
47+
"type": "refactor",
48+
"hidden": false,
49+
"section": "Refactoring"
50+
},
51+
{
52+
"type": "test",
53+
"hidden": false,
54+
"section": "Tests"
55+
},
56+
{
57+
"type": "chore",
58+
"hidden": false,
59+
"section": "Other"
60+
}
61+
]
6262
}

tests/unit/segments/test_segments_evaluator.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,13 @@ def test_context_in_segment(
224224

225225
@pytest.mark.parametrize(
226226
"segment_split_value, identity_hashed_percentage, expected_result",
227-
((10, 1, True), (100, 50, True), (0, 1, False), (10, 20, False)),
227+
(
228+
(10, 1, True),
229+
(100, 50, True),
230+
(0, 1, False),
231+
(10, 20, False),
232+
("invalid", 100, False),
233+
),
228234
)
229235
def test_context_in_segment_percentage_split(
230236
mocker: MockerFixture,
@@ -270,6 +276,49 @@ def test_context_in_segment_percentage_split(
270276
assert result == expected_result
271277

272278

279+
def test_context_in_segment_percentage_split__no_identity__returns_expected(
280+
mocker: MockerFixture,
281+
context: EvaluationContext,
282+
) -> None:
283+
# Given
284+
del context["identity"]
285+
286+
segment_context: SegmentContext = {
287+
"key": "1",
288+
"name": "% split",
289+
"rules": [
290+
{
291+
"type": constants.ALL_RULE,
292+
"conditions": [],
293+
"rules": [
294+
{
295+
"type": constants.ALL_RULE,
296+
"conditions": [
297+
{
298+
"operator": constants.PERCENTAGE_SPLIT,
299+
"property": "",
300+
"value": "10",
301+
}
302+
],
303+
"rules": [],
304+
}
305+
],
306+
}
307+
],
308+
}
309+
310+
mock_get_hashed_percentage = mocker.patch(
311+
"flag_engine.segments.evaluator.get_hashed_percentage_for_object_ids"
312+
)
313+
314+
# When
315+
result = is_context_in_segment(context=context, segment_context=segment_context)
316+
317+
# Then
318+
mock_get_hashed_percentage.assert_not_called()
319+
assert result is False
320+
321+
273322
def test_context_in_segment_percentage_split__trait_value__calls_expected(
274323
mocker: MockerFixture,
275324
context: EvaluationContext,

0 commit comments

Comments
 (0)