Skip to content

Commit a5b4adf

Browse files
Kaustubh22327rohitesh-wingify
authored andcommitted
feat: support for wingify package (rebranded)
1 parent 712f0cc commit a5b4adf

155 files changed

Lines changed: 2269 additions & 1349 deletions

File tree

Some content is hidden

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

CHANGELOG.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,75 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.50.0] - 2026-05-28
9+
10+
This release introduces **Wingify** as the primary SDK branding and package namespace, while keeping existing **VWO** integrations fully supported.
11+
12+
### Added
13+
14+
- **Wingify public API** — use `init`, `WingifyClient`, `WingifyBuilder`, and `WingifyOptionsModel` from the `wingify` package as the recommended entry point for new integrations.
15+
16+
```python
17+
from wingify import init
18+
19+
client = init(
20+
{
21+
"account_id": "123456",
22+
"sdk_key": "32-alpha-numeric-sdk-key",
23+
}
24+
)
25+
26+
context = {"id": "user-123"}
27+
28+
flag = client.get_flag("feature-key", context)
29+
print(flag.is_enabled())
30+
print(flag.get_variables())
31+
```
32+
33+
- Dual-brand support: single codebase builds two PyPI packages — `vwo-fme-python-sdk` (legacy) and `wingify-fme-python-sdk` (new).
34+
- Build script `scripts/build_wheels.sh` to produce both PyPI wheels via `SDK_BRAND` env var; runtime branding via `is_via_vwo` at init.
35+
- Wingify brand uses split hosts: settings on `edge.wingify.net`, events on `collect.wingify.net`.
36+
- Dual support for `_wingify_meta` / `_vwo_meta` and `wingifyBuilder` / `vwoBuilder` option keys.
37+
38+
### Changed
39+
40+
- The SDK implementation now lives under the `wingify` package.
41+
- Log messages and documentation have been updated to reflect Wingify branding.
42+
- **No breaking changes for existing integrations** — server event names, payload keys, and runtime behavior remain compatible with the VWO platform.
43+
44+
### Deprecated
45+
46+
The following **VWO** types in `vwo` are deprecated but **continue to work without modification**:
47+
48+
| Deprecated (still supported) | Use instead |
49+
|---|---|
50+
| `vwo.init` | `wingify.init` |
51+
| `vwo.VWOClient` | `wingify.WingifyClient` |
52+
| `vwo.VWOBuilder` | `wingify.WingifyBuilder` |
53+
| `vwo.VWOOptionsModel` | `wingify.WingifyOptionsModel` |
54+
| `vwo.StorageConnector` | `wingify.StorageConnector` |
55+
| `vwo.LogLevelEnum` | `wingify.LogLevelEnum` |
56+
57+
Existing code does not need to change immediately. We recommend adopting the Wingify API for new projects and migrating when convenient:
58+
59+
```python
60+
# Still supported — no action required today
61+
from vwo import init
62+
63+
client = init(
64+
{
65+
"account_id": "123456",
66+
"sdk_key": "32-alpha-numeric-sdk-key",
67+
}
68+
)
69+
70+
context = {"id": "user-123"}
71+
72+
flag = client.get_flag("feature-key", context)
73+
```
74+
75+
**Migration tip:** Replace imports from `vwo` with `wingify`, and rename types (`VWOClient``WingifyClient`, `VWOBuilder``WingifyBuilder`, `VWOOptionsModel``WingifyOptionsModel`). Method signatures and SDK behavior are unchanged. See [MIGRATE.md](MIGRATE.md) for details.
76+
877
## [1.21.1] - 2026-05-20
978

1079
### Fixed

MANIFEST.in

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ include requirements.txt
33
include requirements-dev.txt
44
include LICENSE
55
recursive-exclude tests *
6-
recursive-include vwo/resources *.json
6+
recursive-include vwo/resources *.json
7+
recursive-include wingify/resources *.json

MIGRATE.md

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Migrate to the Wingify Python FME SDK
2+
3+
This guide explains how to adopt the **Wingify** public API on the Python FME SDK. Existing **VWO** integrations (`vwo-fme-python-sdk`) continue to work without changes.
4+
5+
For installation, requirements, and advanced configuration (storage, logger, gateway, proxy, polling, and more), see [README.md](README.md).
6+
7+
---
8+
9+
## Overview
10+
11+
The FME SDK is published as **two PyPI packages** built from the **same codebase** at the same version:
12+
13+
| PyPI package | Import | Public types |
14+
| --- | --- | --- |
15+
| [`wingify-fme-python-sdk`](https://pypi.org/project/wingify-fme-python-sdk/) | `import wingify` | `WingifyOptionsModel`, `WingifyClient`, … |
16+
| [`vwo-fme-python-sdk`](https://pypi.org/project/vwo-fme-python-sdk/) | `import vwo` | `VWOOptionsModel`, `VWOClient`, … (legacy) |
17+
18+
Pick **one** package for your app — do **not** install both `vwo-fme-python-sdk` and `wingify-fme-python-sdk` in the same environment.
19+
20+
New integrations should use the **Wingify** package and types. When you install and initialize through `wingify-fme-python-sdk`, the SDK uses Wingify edge/collect endpoints and Wingify-branded logging (see [Runtime behavior](#runtime-behavior-wingify-build) below).
21+
22+
---
23+
24+
## Wingify API — implementation guide
25+
26+
Use PyPI package `wingify-fme-python-sdk`. Public types use the `Wingify*` prefix.
27+
28+
Legacy `VWO*` types on `vwo-fme-python-sdk` remain supported; they are thin aliases over the same core implementation.
29+
30+
### Implementation steps
31+
32+
1. **Add the dependency** — use only the Wingify coordinate (same semver you use on VWO today):
33+
34+
```bash
35+
pip install wingify-fme-python-sdk
36+
```
37+
38+
2. **Initialize** — call `init()` with `account_id` and `sdk_key`. Returns a `WingifyClient`.
39+
40+
3. **Build user context** — pass a plain dict with at least `id` (string). Optional: `custom_variables`, `user_agent`, `ip_address`, `bucketingSeed`, etc. See [README.md](README.md).
41+
42+
4. **Evaluate flags**`client.get_flag(feature_key, context)`.
43+
44+
5. **Track and attribute**`track_event`, `set_attribute`, and `set_alias` on the initialized client.
45+
46+
### Python
47+
48+
```python
49+
from wingify import init
50+
51+
client = init({
52+
'account_id': '123456',
53+
'sdk_key': '32-alpha-numeric-sdk-key',
54+
'logger': {'level': 'DEBUG'},
55+
})
56+
57+
context = {'id': 'unique_user_id', 'custom_variables': {'plan': 'pro'}}
58+
59+
flag = client.get_flag('feature_key', context)
60+
61+
if flag.is_enabled():
62+
variable = flag.get_variable('feature_variable', 'default-value')
63+
print('Variable:', variable)
64+
65+
client.track_event('event_name', context, {'cartValue': 10})
66+
client.set_attribute('attribute_key', 'attribute_value', context)
67+
client.set_alias(context, 'alias_id')
68+
```
69+
70+
### Type hints (Wingify package)
71+
72+
```python
73+
from wingify import init, WingifyClient, WingifyOptionsModel
74+
75+
options: WingifyOptionsModel = WingifyOptionsModel({
76+
'accountId': '123456',
77+
'sdkKey': '32-alpha-numeric-sdk-key',
78+
})
79+
80+
client: WingifyClient = init({
81+
'account_id': '123456',
82+
'sdk_key': '32-alpha-numeric-sdk-key',
83+
})
84+
```
85+
86+
For the legacy VWO package, substitute `VWOClient` and `VWOOptionsModel` — behavior is the same.
87+
88+
---
89+
90+
## Public API mapping
91+
92+
| Legacy (VWO package) | Wingify package |
93+
| --- | --- |
94+
| `init` | `init` |
95+
| `getUUID` | `getUUID` |
96+
| `VWOOptionsModel` | `WingifyOptionsModel` |
97+
| `VWOClient` | `WingifyClient` |
98+
| `vwoBuilder` on options | `wingifyBuilder` (preferred); `vwoBuilder` still accepted |
99+
| `StorageConnector`, `LogLevelEnum` | Same export names |
100+
101+
### Options that stay VWO-named (platform compatibility)
102+
103+
| Option / field | Notes |
104+
| --- | --- |
105+
| `_vwo_meta` | Still supported; use for SDK metadata when needed |
106+
| Context `_vwo` | Still supported for UA / device hints; `_wingify` also accepted |
107+
| Event / payload keys (e.g. `_vwo_meta` in network payloads) | Unchanged for server compatibility |
108+
| `vwoBuilder` | Alias of `wingifyBuilder` on init options |
109+
| Local storage key | `vwo_fme_settings` for both brands |
110+
111+
---
112+
113+
## Legacy VWO API
114+
115+
The following remain available for existing apps on **`vwo-fme-python-sdk`**:
116+
117+
- `import vwo` with `init`, `getUUID`
118+
- `VWOClient`, `VWOBuilder`, `VWOOptionsModel`
119+
- `vwoBuilder`, `StorageConnector`, logger hooks
120+
- VWO build-time hosts and `VWO-SDK` log prefix
121+
122+
No breaking change is required to stay on the VWO package.
123+
124+
---
125+
126+
## Runtime behavior (Wingify build)
127+
128+
When you install and run the **Wingify** PyPI build (not `vwo-fme-python-sdk`):
129+
130+
| Area | Wingify build | VWO build (legacy package) |
131+
| --- | --- | --- |
132+
| Settings / pull / location | `edge.wingify.net` | `dev.visualwebsiteoptimizer.com` |
133+
| Events / batch | `collect.wingify.net` | Same host as settings (single host) |
134+
| Log prefix | `Wingify-SDK` | `VWO-SDK` |
135+
| Log message branding | Wingify where templated | VWO |
136+
| PyPI `name` in metadata | `wingify-fme-python-sdk` | `vwo-fme-python-sdk` |
137+
138+
With **`proxy_url`**, all requests go to your proxy host. Without `proxy_url`, the SDK selects hosts automatically per build brand.
139+
140+
Event and API payload field names (for example `_vwo_meta`, `vwo_*` event names) are **unchanged** for compatibility with the FME platform.
141+
142+
---
143+
144+
## Migrating from `vwo-fme-python-sdk` to `wingify-fme-python-sdk`
145+
146+
1. In `requirements.txt`, replace the dependency:
147+
148+
```diff
149+
- vwo-fme-python-sdk==1.50.0
150+
+ wingify-fme-python-sdk==1.50.0
151+
```
152+
153+
2. Update imports:
154+
155+
```diff
156+
- from vwo import init
157+
+ from wingify import init
158+
```
159+
160+
3. Rename types: `VWOClient``WingifyClient`, `VWOOptionsModel``WingifyOptionsModel`.
161+
162+
4. If you pass a custom builder, prefer `wingifyBuilder` instead of `vwoBuilder` (either still works).
163+
164+
5. Reinstall and run your existing tests — flag evaluation, tracking, and attributes behave the same; only package name, exported type names, default hosts, and log branding change.
165+
166+
### What you do **not** need to change
167+
168+
- `account_id`, `sdk_key`, feature keys, event names
169+
- User context shape (`{'id': '...'}` and optional fields)
170+
- Method signatures on the client (`get_flag`, `track_event`, etc.)
171+
- Server-side campaign / settings JSON
172+
- `_vwo` / `_vwo_meta` in context or payloads when you already send them
173+
174+
---
175+
176+
## Architecture note
177+
178+
The SDK follows a **Single Repo Two Pacakge Approach**:
179+
180+
| Package | Role |
181+
| --- | --- |
182+
| `wingify/` | **Core**`api/`, `constants/`, `enums/`, `services/`, `utils/`, `packages/`, `wingify_client.py`, … |
183+
| `vwo/` | **Legacy facade only** (~10 files) — `init`, `VWOClient`, `VWOBuilder`, `VWOOptionsModel`, `StorageConnector`, `LogLevelEnum`, etc. |
184+
185+
Both PyPI wheels ship `wingify` (core) and `vwo` (facade). Existing apps use `import vwo`; new apps use `import wingify`. Brand-specific hosts, SDK name, and log prefix are selected at **runtime** via `is_via_vwo` (`vwo.init()` sets it to `true`; `wingify.init()` defaults to `false`). PyPI wheel metadata still differs per `SDK_BRAND` at build time.
186+
187+
---
188+
189+
## Related documents
190+
191+
| Document | Content |
192+
| --- | --- |
193+
| [README.md](README.md) | Installation, requirements, configuration |
194+
| [CHANGELOG.md](CHANGELOG.md) | Version history and rebranding notes |

scripts/build_wheels.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
# Copyright 2024-2026 Wingify Software Pvt. Ltd.
3+
#
4+
# Build both brand-specific source distributions from the same codebase.
5+
#
6+
# Usage:
7+
# ./scripts/build_wheels.sh
8+
#
9+
# Outputs:
10+
# dist/vwo-fme-python-sdk-<version>.tar.gz
11+
# dist/wingify-fme-python-sdk-<version>.tar.gz
12+
13+
set -euo pipefail
14+
15+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
16+
cd "$ROOT"
17+
18+
rm -rf dist/ build/ *.egg-info wingify.egg-info vwo.egg-info 2>/dev/null || true
19+
20+
echo "=== Building VWO source distribution ==="
21+
SDK_BRAND=vwo python3 setup.py sdist
22+
23+
echo "=== Building Wingify source distribution ==="
24+
SDK_BRAND=wingify python3 setup.py sdist
25+
26+
echo "=== Done ==="
27+
ls -la dist/

setup.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,28 @@
3030
with open("README.md", "r") as f:
3131
long_description = f.read()
3232

33+
# SDK_BRAND env var selects PyPI metadata when building brand-specific wheels.
34+
SDK_BRAND = os.environ.get("SDK_BRAND", "vwo")
35+
36+
_BRAND_METADATA = {
37+
"vwo": {
38+
"name": "vwo-fme-python-sdk",
39+
"description": "VWO Feature Management and Experimentation SDK for Python",
40+
"author": "VWO",
41+
},
42+
"wingify": {
43+
"name": "wingify-fme-python-sdk",
44+
"description": "Wingify Feature Management and Experimentation SDK for Python",
45+
"author": "Wingify",
46+
},
47+
}
48+
49+
_brand = _BRAND_METADATA.get(SDK_BRAND, _BRAND_METADATA["vwo"])
50+
51+
_all_packages = find_packages(exclude=["tests"])
52+
# Both wheels ship wingify (core) + vwo (legacy facade). Customers import one or the other.
53+
_packages = _all_packages
54+
3355

3456
class DocCheckCommand(Command):
3557
description = "Doc Check"
@@ -57,7 +79,7 @@ def finalize_options(self):
5779

5880
def run(self):
5981
subprocess.call(
60-
'python3 ./scripts/apache_license_check.py vwo/ tests/ setup.py --Copyright 2024-2026 Wingify Software Pvt. Ltd."',
82+
'python3 ./scripts/apache_license_check.py vwo/ wingify/ tests/ setup.py --Copyright 2024-2026 Wingify Software Pvt. Ltd."',
6183
shell=True,
6284
)
6385

@@ -120,12 +142,12 @@ def run(self):
120142

121143

122144
setup(
123-
name="vwo-fme-python-sdk",
124-
version="1.21.1",
125-
description="VWO Feature Management and Experimentation SDK for Python",
145+
name=_brand["name"],
146+
version="1.50.0",
147+
description=_brand["description"],
126148
long_description=long_description,
127149
long_description_content_type="text/markdown",
128-
author="VWO",
150+
author=_brand["author"],
129151
author_email="dev@wingify.com",
130152
url="https://github.com/wingify/vwo-fme-python-sdk",
131153
license="Apache License 2.0",
@@ -151,7 +173,7 @@ def run(self):
151173
"license_check": LicenseCheckCommand,
152174
"doc_check": DocCheckCommand,
153175
},
154-
packages=find_packages(exclude=["tests"]),
176+
packages=_packages,
155177
include_package_data=True,
156178
install_requires=REQUIREMENTS,
157179
)

tests/e2e/custom_bucketing_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ def test_aliased_users_resolving_to_different_user_ids_should_get_same_variation
466466
vwo_client = init(options)
467467
bucketing_seed = "shared-seed-abc"
468468

469-
with patch("vwo.vwo_client.get_alias_user_id", side_effect=["RandomUserVWO", "WingifyVWO"]) as mock_alias:
469+
with patch("wingify.wingify_client.get_alias_user_id", side_effect=["RandomUserVWO", "WingifyVWO"]) as mock_alias:
470470
flag1 = vwo_client.get_flag("featureOne", {
471471
"id": "aliasUserA",
472472
"bucketingSeed": bucketing_seed
@@ -502,7 +502,7 @@ def test_aliased_users_resolving_to_different_user_ids_should_get_different_vari
502502
}
503503
vwo_client = init(options)
504504

505-
with patch("vwo.vwo_client.get_alias_user_id", side_effect=["RandomUserVWO", "WingifyVWO"]) as mock_alias:
505+
with patch("wingify.wingify_client.get_alias_user_id", side_effect=["RandomUserVWO", "WingifyVWO"]) as mock_alias:
506506
flag1 = vwo_client.get_flag("featureOne", {"id": "aliasUserA"})
507507
flag2 = vwo_client.get_flag("featureOne", {"id": "aliasUserB"})
508508

0 commit comments

Comments
 (0)