flagsmith 6.2.2__tar.gz → 6.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flagsmith
3
- Version: 6.2.2
3
+ Version: 6.3.0
4
4
  Summary: Flagsmith Python SDK
5
5
  License: BSD3
6
6
  License-File: LICENSE
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Programming Language :: Python :: 3.13
17
17
  Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Programming Language :: Python :: 3.15
18
19
  Requires-Dist: flagsmith-flag-engine (>=11.0.0,<12.0.0)
19
20
  Requires-Dist: iso8601 (>=2.1.0,<3.0.0) ; python_version < "3.11"
20
21
  Requires-Dist: requests (>=2.32.3,<3.0.0)
@@ -8,7 +8,7 @@ from datetime import datetime
8
8
 
9
9
  from requests_futures.sessions import FuturesSession # type: ignore
10
10
 
11
- from flagsmith.version import __version__
11
+ from flagsmith.version import DEFAULT_USER_AGENT, __version__
12
12
 
13
13
  logger = logging.getLogger(__name__)
14
14
 
@@ -218,7 +218,7 @@ class EventProcessor:
218
218
  headers={
219
219
  "Content-Type": "application/json; charset=utf-8",
220
220
  "X-Environment-Key": self._environment_key,
221
- "Flagsmith-SDK-User-Agent": f"flagsmith-python-client/{__version__}",
221
+ "Flagsmith-SDK-User-Agent": DEFAULT_USER_AGENT,
222
222
  },
223
223
  )
224
224
  except RuntimeError:
@@ -41,13 +41,12 @@ from flagsmith.types import (
41
41
  TraitMapping,
42
42
  )
43
43
  from flagsmith.utils.identities import generate_identity_data
44
- from flagsmith.version import __version__
44
+ from flagsmith.version import DEFAULT_USER_AGENT
45
45
 
46
46
  logger = logging.getLogger(__name__)
47
47
 
48
48
  DEFAULT_API_URL = "https://edge.api.flagsmith.com/api/v1/"
49
49
  DEFAULT_REALTIME_API_URL = "https://realtime.flagsmith.com/"
50
- DEFAULT_USER_AGENT = f"flagsmith-python-sdk/{__version__}"
51
50
 
52
51
 
53
52
  class Flagsmith:
@@ -369,7 +368,9 @@ class Flagsmith:
369
368
  Resolve a flag for an identity and record an exposure event.
370
369
 
371
370
  The exposure event's ``value`` is the flag's variant key. It is only
372
- sent when the flag exists, is enabled and carries a variant; any
371
+ sent when the flag exists, is enabled and the identity is enrolled in
372
+ the feature's experiment (``flag.experiment.in_experiment``) with a
373
+ variant; any
373
374
  other outcome is logged and skipped to keep experimentation data
374
375
  clean. A `DefaultFlag` served via the `default_flag_handler` counts
375
376
  as the feature not existing.
@@ -389,6 +390,12 @@ class Flagsmith:
389
390
  FLAG_EXPOSURE_EVENT,
390
391
  feature_name,
391
392
  )
393
+ elif not (flag.experiment and flag.experiment.in_experiment):
394
+ logger.debug(
395
+ "Not sending %s for feature %s: identity is not in a running experiment.",
396
+ FLAG_EXPOSURE_EVENT,
397
+ feature_name,
398
+ )
392
399
  elif flag.variant is None:
393
400
  logger.debug(
394
401
  "Not sending %s for feature %s: flag has no variant.",
@@ -401,6 +408,7 @@ class Flagsmith:
401
408
  identifier=identifier,
402
409
  value=flag.variant,
403
410
  traits=traits,
411
+ metadata={"experiment_id": flag.experiment.id},
404
412
  )
405
413
  return flag
406
414
 
@@ -41,6 +41,28 @@ def build_segment_overrides_index(
41
41
  return index
42
42
 
43
43
 
44
+ @dataclass(frozen=True)
45
+ class ExperimentMetadata:
46
+ """The running experiment a flag was evaluated under; identity evaluations only."""
47
+
48
+ id: int
49
+ name: str
50
+ in_experiment: bool
51
+
52
+ @classmethod
53
+ def from_api_metadata(
54
+ cls,
55
+ metadata: typing.Optional[typing.Mapping[str, typing.Any]],
56
+ ) -> typing.Optional[ExperimentMetadata]:
57
+ if not metadata or not (experiment := metadata.get("experiment")):
58
+ return None
59
+ return cls(
60
+ id=experiment["id"],
61
+ name=experiment["name"],
62
+ in_experiment=experiment["in_experiment"],
63
+ )
64
+
65
+
44
66
  @dataclass
45
67
  class BaseFlag:
46
68
  enabled: bool
@@ -59,6 +81,7 @@ class Flag(BaseFlag):
59
81
  variant: typing.Optional[str] = None
60
82
  reason: typing.Optional[str] = None
61
83
  is_default: bool = field(default=False)
84
+ experiment: typing.Optional[ExperimentMetadata] = None
62
85
 
63
86
  @classmethod
64
87
  def from_evaluation_result(
@@ -88,6 +111,7 @@ class Flag(BaseFlag):
88
111
  feature_id=flag_data["feature"]["id"],
89
112
  variant=flag_data.get("variant"),
90
113
  reason=flag_data.get("reason"),
114
+ experiment=ExperimentMetadata.from_api_metadata(flag_data.get("metadata")),
91
115
  )
92
116
 
93
117
 
@@ -1,3 +1,5 @@
1
1
  from importlib.metadata import version
2
2
 
3
3
  __version__ = version("flagsmith")
4
+
5
+ DEFAULT_USER_AGENT = f"flagsmith-python-sdk/{__version__}"
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "flagsmith"
3
- version = "6.2.2"
3
+ version = "6.3.0"
4
4
  description = "Flagsmith Python SDK"
5
5
  authors = ["Flagsmith <support@flagsmith.com>"]
6
6
  license = "BSD3"
File without changes
File without changes
File without changes
File without changes