platzky 1.4.1__py3-none-any.whl → 1.4.2__py3-none-any.whl
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.
- platzky/__init__.py +1 -0
- platzky/config.py +4 -6
- platzky/feature_flags.py +49 -48
- platzky/feature_flags_wrapper.py +4 -15
- {platzky-1.4.1.dist-info → platzky-1.4.2.dist-info}/METADATA +1 -1
- {platzky-1.4.1.dist-info → platzky-1.4.2.dist-info}/RECORD +8 -8
- {platzky-1.4.1.dist-info → platzky-1.4.2.dist-info}/WHEEL +1 -1
- {platzky-1.4.1.dist-info → platzky-1.4.2.dist-info}/licenses/LICENSE +0 -0
platzky/__init__.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from platzky.engine import Engine as Engine
|
|
2
|
+
from platzky.feature_flags import BUILTIN_FLAGS as BUILTIN_FLAGS
|
|
2
3
|
from platzky.feature_flags import FakeLogin as FakeLogin
|
|
3
4
|
from platzky.feature_flags import FeatureFlag as FeatureFlag
|
|
4
5
|
from platzky.feature_flags import all_flags as all_flags
|
platzky/config.py
CHANGED
|
@@ -12,7 +12,6 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
12
12
|
from platzky.attachment.constants import BLOCKED_EXTENSIONS, DEFAULT_MAX_ATTACHMENT_SIZE
|
|
13
13
|
from platzky.db.db import DBConfig
|
|
14
14
|
from platzky.db.db_loader import get_db_module
|
|
15
|
-
from platzky.feature_flags import build_flag_set
|
|
16
15
|
from platzky.feature_flags_wrapper import FeatureFlagSet
|
|
17
16
|
|
|
18
17
|
|
|
@@ -257,7 +256,7 @@ class Config(BaseModel):
|
|
|
257
256
|
debug: bool = Field(default=False, alias="DEBUG")
|
|
258
257
|
testing: bool = Field(default=False, alias="TESTING")
|
|
259
258
|
feature_flags: FeatureFlagSet = Field(
|
|
260
|
-
default_factory=
|
|
259
|
+
default_factory=lambda: FeatureFlagSet({}),
|
|
261
260
|
alias="FEATURE_FLAGS",
|
|
262
261
|
)
|
|
263
262
|
telemetry: TelemetryConfig = Field(default_factory=TelemetryConfig, alias="TELEMETRY")
|
|
@@ -270,9 +269,9 @@ class Config(BaseModel):
|
|
|
270
269
|
if isinstance(v, FeatureFlagSet):
|
|
271
270
|
return v
|
|
272
271
|
if isinstance(v, dict):
|
|
273
|
-
return
|
|
272
|
+
return FeatureFlagSet(v)
|
|
274
273
|
if v is None:
|
|
275
|
-
return
|
|
274
|
+
return FeatureFlagSet({})
|
|
276
275
|
return v
|
|
277
276
|
|
|
278
277
|
@classmethod
|
|
@@ -286,8 +285,7 @@ class Config(BaseModel):
|
|
|
286
285
|
) -> "Config":
|
|
287
286
|
"""Validate and construct Config from dictionary.
|
|
288
287
|
|
|
289
|
-
Parses the raw FEATURE_FLAGS dict into a
|
|
290
|
-
FeatureFlag types via ``parse_flags()``.
|
|
288
|
+
Parses the raw FEATURE_FLAGS dict into a ``FeatureFlagSet``.
|
|
291
289
|
|
|
292
290
|
Args:
|
|
293
291
|
obj: Configuration dictionary
|
platzky/feature_flags.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
"""Feature flags system
|
|
1
|
+
"""Feature flags system for Platzky.
|
|
2
2
|
|
|
3
|
-
Flags are created as instances of ``FeatureFlag``.
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Flags are created as instances of ``FeatureFlag``. The primary API is
|
|
4
|
+
``engine.is_enabled(flag_instance)``. Resolution is dynamic — the
|
|
5
|
+
``FeatureFlagSet`` checks the raw config dict using each flag's ``alias``
|
|
6
|
+
and ``default`` at lookup time. No global registry is needed.
|
|
6
7
|
|
|
7
8
|
Example::
|
|
8
9
|
|
|
@@ -16,11 +17,11 @@ from __future__ import annotations
|
|
|
16
17
|
|
|
17
18
|
from typing import TYPE_CHECKING
|
|
18
19
|
|
|
20
|
+
import deprecation
|
|
21
|
+
|
|
19
22
|
if TYPE_CHECKING:
|
|
20
23
|
from platzky.feature_flags_wrapper import FeatureFlagSet
|
|
21
24
|
|
|
22
|
-
_registry: set[FeatureFlag] = set()
|
|
23
|
-
|
|
24
25
|
|
|
25
26
|
class FeatureFlag:
|
|
26
27
|
"""A feature flag.
|
|
@@ -52,7 +53,6 @@ class FeatureFlag:
|
|
|
52
53
|
default: bool = False,
|
|
53
54
|
description: str = "",
|
|
54
55
|
production_warning: bool = False,
|
|
55
|
-
register: bool = True,
|
|
56
56
|
) -> None:
|
|
57
57
|
if not alias:
|
|
58
58
|
raise ValueError("FeatureFlag requires a non-empty 'alias'")
|
|
@@ -60,8 +60,6 @@ class FeatureFlag:
|
|
|
60
60
|
self.default = default
|
|
61
61
|
self.description = description
|
|
62
62
|
self.production_warning = production_warning
|
|
63
|
-
if register:
|
|
64
|
-
_registry.add(self)
|
|
65
63
|
|
|
66
64
|
def __repr__(self) -> str:
|
|
67
65
|
return f"FeatureFlag(alias={self.alias!r})"
|
|
@@ -82,60 +80,63 @@ FakeLogin = FeatureFlag(
|
|
|
82
80
|
production_warning=True,
|
|
83
81
|
)
|
|
84
82
|
|
|
83
|
+
BUILTIN_FLAGS: tuple[FeatureFlag, ...] = (FakeLogin,)
|
|
85
84
|
|
|
86
|
-
def all_flags() -> frozenset[FeatureFlag]:
|
|
87
|
-
"""Return all registered feature flags.
|
|
88
85
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"""
|
|
93
|
-
return frozenset(_registry)
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# Deprecated shims — will be removed in 2.0.0
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
94
89
|
|
|
95
90
|
|
|
96
|
-
|
|
97
|
-
"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def
|
|
102
|
-
"""
|
|
103
|
-
|
|
91
|
+
@deprecation.deprecated(
|
|
92
|
+
deprecated_in="1.5.0",
|
|
93
|
+
removed_in="2.0.0",
|
|
94
|
+
details="Use BUILTIN_FLAGS instead.",
|
|
95
|
+
)
|
|
96
|
+
def all_flags() -> frozenset[FeatureFlag]:
|
|
97
|
+
"""Return all built-in feature flags."""
|
|
98
|
+
return frozenset(BUILTIN_FLAGS)
|
|
104
99
|
|
|
105
100
|
|
|
101
|
+
@deprecation.deprecated(
|
|
102
|
+
deprecated_in="1.5.0",
|
|
103
|
+
removed_in="2.0.0",
|
|
104
|
+
details="Use FeatureFlagSet(raw_data) and check membership with 'flag in flag_set' instead.",
|
|
105
|
+
)
|
|
106
106
|
def parse_flags(
|
|
107
107
|
raw_data: dict[str, bool] | None = None,
|
|
108
108
|
) -> frozenset[FeatureFlag]:
|
|
109
|
-
"""Build a frozenset of
|
|
110
|
-
|
|
111
|
-
Uses ``all_flags()`` for discovery. Unknown keys in *raw_data* are
|
|
112
|
-
silently ignored.
|
|
113
|
-
|
|
114
|
-
Args:
|
|
115
|
-
raw_data: Dict of flag alias -> value from config / YAML.
|
|
116
|
-
|
|
117
|
-
Returns:
|
|
118
|
-
A frozenset containing the enabled FeatureFlag instances.
|
|
119
|
-
"""
|
|
109
|
+
"""Build a frozenset of enabled flags from raw config data."""
|
|
120
110
|
if raw_data is None:
|
|
121
111
|
raw_data = {}
|
|
122
|
-
|
|
123
|
-
return frozenset(flag for flag in all_flags() if raw_data.get(flag.alias, flag.default))
|
|
112
|
+
return frozenset(flag for flag in BUILTIN_FLAGS if raw_data.get(flag.alias, flag.default))
|
|
124
113
|
|
|
125
114
|
|
|
115
|
+
@deprecation.deprecated(
|
|
116
|
+
deprecated_in="1.5.0",
|
|
117
|
+
removed_in="2.0.0",
|
|
118
|
+
details="Use FeatureFlagSet(raw_data) directly.",
|
|
119
|
+
)
|
|
126
120
|
def build_flag_set(raw_data: dict[str, bool] | None = None) -> FeatureFlagSet:
|
|
127
|
-
"""Build a FeatureFlagSet from raw config data.
|
|
128
|
-
|
|
129
|
-
Preserves ALL keys (including unregistered ones) for backward
|
|
130
|
-
compatibility with consumers that use dict-like access.
|
|
131
|
-
"""
|
|
121
|
+
"""Build a FeatureFlagSet from raw config data."""
|
|
132
122
|
from platzky.feature_flags_wrapper import FeatureFlagSet
|
|
133
123
|
|
|
134
|
-
|
|
135
|
-
|
|
124
|
+
return FeatureFlagSet(raw_data or {})
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@deprecation.deprecated(
|
|
128
|
+
deprecated_in="1.5.0",
|
|
129
|
+
removed_in="2.0.0",
|
|
130
|
+
details="No replacement needed — the global registry has been removed.",
|
|
131
|
+
)
|
|
132
|
+
def unregister(_flag: FeatureFlag) -> None:
|
|
133
|
+
"""No-op. The global registry has been removed."""
|
|
136
134
|
|
|
137
|
-
enabled_flags = frozenset(
|
|
138
|
-
flag for flag in all_flags() if raw_data.get(flag.alias, flag.default)
|
|
139
|
-
)
|
|
140
135
|
|
|
141
|
-
|
|
136
|
+
@deprecation.deprecated(
|
|
137
|
+
deprecated_in="1.5.0",
|
|
138
|
+
removed_in="2.0.0",
|
|
139
|
+
details="No replacement needed — the global registry has been removed.",
|
|
140
|
+
)
|
|
141
|
+
def clear_registry() -> None:
|
|
142
|
+
"""No-op. The global registry has been removed."""
|
platzky/feature_flags_wrapper.py
CHANGED
|
@@ -42,16 +42,8 @@ class FeatureFlagSet(dict[str, bool]):
|
|
|
42
42
|
- ``flag_set.KEY`` -- deprecated Jinja2 attribute access.
|
|
43
43
|
"""
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def __init__(
|
|
48
|
-
self,
|
|
49
|
-
enabled_flags: frozenset[FeatureFlag],
|
|
50
|
-
raw_data: dict[str, bool],
|
|
51
|
-
) -> None:
|
|
45
|
+
def __init__(self, raw_data: dict[str, bool]) -> None:
|
|
52
46
|
super().__init__(raw_data)
|
|
53
|
-
# Use object.__setattr__ to bypass __getattr__ override
|
|
54
|
-
object.__setattr__(self, "_enabled_flags", enabled_flags)
|
|
55
47
|
|
|
56
48
|
def __getitem__(self, key: str) -> bool:
|
|
57
49
|
"""Dict bracket access with deprecation warning."""
|
|
@@ -77,7 +69,9 @@ class FeatureFlagSet(dict[str, bool]):
|
|
|
77
69
|
def __contains__(self, item: object) -> bool:
|
|
78
70
|
"""Support both FeatureFlag membership and string key lookup."""
|
|
79
71
|
if isinstance(item, FeatureFlag):
|
|
80
|
-
|
|
72
|
+
if super().__contains__(item.alias):
|
|
73
|
+
return bool(super().__getitem__(item.alias))
|
|
74
|
+
return item.default
|
|
81
75
|
return super().__contains__(item)
|
|
82
76
|
|
|
83
77
|
def __getattr__(self, name: str) -> bool:
|
|
@@ -86,8 +80,3 @@ class FeatureFlagSet(dict[str, bool]):
|
|
|
86
80
|
return self[name]
|
|
87
81
|
except KeyError:
|
|
88
82
|
raise AttributeError(f"'{type(self).__name__}' has no attribute {name!r}") from None
|
|
89
|
-
|
|
90
|
-
@property
|
|
91
|
-
def enabled_flags(self) -> frozenset[FeatureFlag]:
|
|
92
|
-
"""The set of enabled typed FeatureFlag instances."""
|
|
93
|
-
return self._enabled_flags
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
platzky/__init__.py,sha256=
|
|
1
|
+
platzky/__init__.py,sha256=kqK72dPH79dNogz8yB5K8XnwNM3PTC7rd-tCVgULks0,623
|
|
2
2
|
platzky/admin/admin.py,sha256=_4Q1nId_QGrbSMaAZzl7ziquza3syQxXlBYYXjD_4XM,1660
|
|
3
3
|
platzky/admin/templates/admin.html,sha256=zgjROhSezayZqnNFezvVa0MEfgmXLvOM8HRRaZemkQw,688
|
|
4
4
|
platzky/admin/templates/login.html,sha256=oBNuv130iMTwXrtRnDUDcGIGvu0O2VsIbjQxw-Tjd7Y,380
|
|
@@ -10,7 +10,7 @@ platzky/attachment/mime_validation.py,sha256=VO272nF7K_Mx_Zefuw-10M-Tapj0ZxX197s
|
|
|
10
10
|
platzky/blog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
platzky/blog/blog.py,sha256=UaAS7QpcEsMswMrdAcyoSyuDX1NNQX05ll8D7MKSR3w,5468
|
|
12
12
|
platzky/blog/comment_form.py,sha256=yOuXvX9PZLc6qQLIWZWLFcbwFQD4a849X82PlXKUzdk,805
|
|
13
|
-
platzky/config.py,sha256=
|
|
13
|
+
platzky/config.py,sha256=Op4Bbrqvny48JKL7mWQRCP7aCSppTcysAkSgAtB75kc,11504
|
|
14
14
|
platzky/db/README.md,sha256=IO-LoDsd4dLBZenaz423EZjvEOQu_8m2OC0G7du170w,1753
|
|
15
15
|
platzky/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
platzky/db/db.py,sha256=gi5uxvY8Ww8O4y2rxaH1Zj_12Yno8SbILvIaWnQPbYQ,4778
|
|
@@ -25,8 +25,8 @@ platzky/debug/__init__.py,sha256=5VUFjOOJzuXTuCgzQRe9aNjXHoCJSVUerR3RLKCTZC0,301
|
|
|
25
25
|
platzky/debug/blueprint.py,sha256=_rtrtrOOsj4Pz8AKE_5kLFVedEtipAyN42j-pBVZqnM,1893
|
|
26
26
|
platzky/debug/fake_login.py,sha256=3AmS11Kb4h5gDwWjUY26iDMif0b0x69S97veotnXUp4,3390
|
|
27
27
|
platzky/engine.py,sha256=JeyRS-A0R0fK2BLude_Y4U8G3d-7nRUkuu4OeX86sRI,7400
|
|
28
|
-
platzky/feature_flags.py,sha256=
|
|
29
|
-
platzky/feature_flags_wrapper.py,sha256=
|
|
28
|
+
platzky/feature_flags.py,sha256=6y5ErUgTAiKYNyvbUkGMVJl15sc7ouOvrqnMVIRCbuc,4095
|
|
29
|
+
platzky/feature_flags_wrapper.py,sha256=_tH4aeAx33Czv7gvPEyYYLbiSRhWYiRooAFEtoikeF4,2822
|
|
30
30
|
platzky/locale/en/LC_MESSAGES/messages.po,sha256=WaZGlFAegKRq7CSz69dWKic-mKvQFhVvssvExxNmGaU,1400
|
|
31
31
|
platzky/locale/pl/LC_MESSAGES/messages.po,sha256=sUPxMKDeEOoZ5UIg94rGxZD06YVWiAMWIby2XE51Hrc,1624
|
|
32
32
|
platzky/models.py,sha256=Ws5ZSWf5EhcpFxl3Yeze2pQiesjnjAA_haBJ90bN6lk,7435
|
|
@@ -50,7 +50,7 @@ platzky/templates/post.html,sha256=GSgjIZsOQKtNx3cEbquSjZ5L4whPnG6MzRyoq9k4B8Q,1
|
|
|
50
50
|
platzky/templates/robots.txt,sha256=2_j2tiYtYJnzZUrANiX9pvBxyw5Dp27fR_co18BPEJ0,116
|
|
51
51
|
platzky/templates/sitemap.xml,sha256=iIJZ91_B5ZuNLCHsRtsGKZlBAXojOTP8kffqKLacgvs,578
|
|
52
52
|
platzky/www_handler.py,sha256=pF6Rmvem1sdVqHD7z3RLrDuG-CwAqfGCti50_NPsB2w,725
|
|
53
|
-
platzky-1.4.
|
|
54
|
-
platzky-1.4.
|
|
55
|
-
platzky-1.4.
|
|
56
|
-
platzky-1.4.
|
|
53
|
+
platzky-1.4.2.dist-info/METADATA,sha256=TeusSpm8WupVralLVXnSzZ_UxPefW0AIdbevUTWW4yg,2595
|
|
54
|
+
platzky-1.4.2.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
|
|
55
|
+
platzky-1.4.2.dist-info/licenses/LICENSE,sha256=wCdfk-qEosi6BDwiBulMfKMi0hxp1UXV0DdjLrRm788,1077
|
|
56
|
+
platzky-1.4.2.dist-info/RECORD,,
|
|
File without changes
|