Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
max-line-length = 120
max-complexity = 10
exclude = .git,__pycache__,.venv
24 changes: 24 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Publish Pypi Package

on:
push:
tags:
- '*'

jobs:
package:
runs-on: ubuntu-latest
name: Publish Pypi Package

steps:
- name: Cloning repo
uses: actions/checkout@v2
with:
fetch-depth: 0

- name: Build and publish to pypi
uses: JRubics/poetry-publish@v1.10
with:
pypi_token: ${{ secrets.PYPI_API_TOKEN }}
ignore_dev_requirements: "yes"
build_format: "sdist"
16 changes: 9 additions & 7 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
name: Pytest and Black formatting
name: Formatting and Tests

on:
- pull_request
- push

jobs:
test:
Expand All @@ -12,7 +11,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: ["3.7", "3.8", "3.9", "3.10"]

steps:
- name: Cloning repo
Expand All @@ -28,11 +27,14 @@ jobs:
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
pip install poetry
poetry install

- name: Check Formatting
run: black --check .
run: |
poetry run black --check .
poetry run flake8 .
poetry run isort --check .

- name: Run Tests
run: |
pytest
run: poetry run pytest
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.idea/
.venv
.direnv/

*.pyc

Expand Down
2 changes: 1 addition & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ use_parentheses=true
multi_line_output=3
include_trailing_comma=true
line_length=79
known_third_party = pytest,requests,requests_futures,setuptools
known_third_party = flag_engine,flask,pytest,requests,requests_futures,responses,urllib3
2 changes: 2 additions & 0 deletions example/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FLASK_APP=app
FLAGSMITH_ENVIRONMENT_KEY=<your key>
63 changes: 63 additions & 0 deletions example/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import json
import os

from flask import Flask, render_template, request

from flagsmith import Flagsmith
from flagsmith.models import DefaultFlag

app = Flask(__name__)


def default_flag_handler(feature_name: str) -> DefaultFlag:
"""
Function that will be used if the API doesn't respond, or an unknown
feature is requested
"""

if feature_name == "secret_button":
return DefaultFlag(
enabled=False,
value=json.dumps({"colour": "#b8b8b8"}),
)

return DefaultFlag(False, None)


flagsmith = Flagsmith(
environment_key=os.environ.get("FLAGSMITH_ENVIRONMENT_KEY"),
default_flag_handler=default_flag_handler,
)


@app.route("/", methods=["GET", "POST"])
def home():
if request.args:
identifier = request.args["identifier"]

trait_key = request.args.get("trait-key")
trait_value = request.args.get("trait-value")
traits = {trait_key: trait_value} if trait_key else None

# Get the flags for an identity, including the provided trait which will be
# persisted to the API for future requests.
identity_flags = flagsmith.get_identity_flags(
identifier=identifier, traits=traits
)
show_button = identity_flags.is_feature_enabled("secret_button")
button_data = json.loads(identity_flags.get_feature_value("secret_button"))
return render_template(
"home.html",
show_button=show_button,
button_colour=button_data["colour"],
identifier=identifier,
)

# Get the default flags for the current environment
flags = flagsmith.get_environment_flags()
show_button = flags.is_feature_enabled("secret_button")
button_data = json.loads(flags.get_feature_value("secret_button"))

return render_template(
"home.html", show_button=show_button, button_colour=button_data["colour"]
)
58 changes: 0 additions & 58 deletions example/example.py

This file was deleted.

27 changes: 15 additions & 12 deletions example/readme.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
# Flagsmith Basic Python Example

To use this basic example, you'll need to first configure a project with at least one feature in Flagsmith.
This directory contains a basic Flask application which utilises Flagsmith. To run the example application, you'll
need to go through the following steps:

Once you've done this, you'll then need to install the latest version of the Flagsmith package by running:
1. Create an account, organisation and project on [Flagsmith](https://flagsmith.com)
2. Create a feature in the project called "secret_button"
3. Give the feature a value using the json editor as follows:

```bash
pip install flagsmith
```json
{"colour": "#ababab"}
```

Then you can run:
4. Create a .env file from the template located in this directory with the environment key of one of the environments
in flagsmith (This can be found on the 'settings' page accessed from the menu on the left under the chosen environment.)
5. From a terminal window, export those environment variables (either manually or by using `export $(cat .env)`)
6. Run the app using `flask run`
7. Browse to http://localhost:5000

```bash
python example.py
```

The script will grab some information from you such as the environment key to test with, an identifier and a feature
name. Once you've inputted those, the script will run you through all of the methods available in the Flagsmith
client and print the result.
Now you can play around with the 'secret_button' feature in flagsmith, turn it on to show it and edit the colour in the
json value to edit the colour of the button. You can also identify as a given user and then update the settings for the
secret button feature for that user in the flagsmith interface to see the affect that has too.
2 changes: 2 additions & 0 deletions example/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
flask==2.0.2
flagsmith>=3.0.0
25 changes: 25 additions & 0 deletions example/templates/home.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<head>
<style></style>
</head>
<title>Flagsmith Example</title>
<body>
<p>Hello, {{ identifier or 'World' }}.</p>
{% if show_button %}
<button style="background-color: {{ button_colour }}">A secret button</button>
{% endif %}

<p></p>

<form action="/" method="GET">
<h3>Identify as a user</h3>
<label for="identifier">Identifier: </label><input name="identifier" id="identifier"><br>

<p>... with an optional user trait</p>
<label for="trait-key">Trait key: </label><input name="trait-key" id="trait-key"><br>
<label for="trait-value">Trait value: </label><input name="trait-value" id="trait-value"><br><br>

<button type="submit">Identify!</button>
</form>

</body>
2 changes: 1 addition & 1 deletion flagsmith/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .flagsmith import Flagsmith
from .flagsmith import Flagsmith # noqa
1 change: 0 additions & 1 deletion flagsmith/analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ def __init__(self, environment_key: str, base_api_url: str, timeout: int = 3):
self._last_flushed = datetime.now()
self.analytics_data = {}
self.timeout = timeout
super().__init__()

def flush(self):
"""
Expand Down
6 changes: 6 additions & 0 deletions flagsmith/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class FlagsmithClientError(Exception):
pass


class FlagsmithAPIError(FlagsmithClientError):
pass
Loading