Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure modules #3

Merged
merged 16 commits into from
Jun 30, 2022
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
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[flake8]
max-line-length = 88
max-line-length = 88
exclude = .venv/*,venv/*,.git,__pycache__
53 changes: 53 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Test

on:
push:
branches:
- '*'

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
container: [ "python:3.8", "python:3.9", "python:3.10" ]
container:
image: ${{ matrix.container }}

steps:
- uses: actions/checkout@v2

- name: Cache virtualenvironment
uses: actions/cache@v2
with:
path: ~/.venv
key: ${{ hashFiles('requirements.txt', 'requirements-dev.txt') }}

- name: Upgrade pip
run: pip install --upgrade pip

- name: Create and activate Virtualenv
run: |
pip install virtualenv
[ ! -d ".venv" ] && virtualenv .venv
. .venv/bin/activate

- name: Install dependencies
run: pip install -r requirements-dev.txt

- name: Run black formatter check
run: black --check .

- name: Run flake8 formatter check
run: flake8 .

- name: Run isort formatter check
run: isort .

- name: Test with pytest
run: pytest
File renamed without changes.
File renamed without changes.
87 changes: 87 additions & 0 deletions open_feature/exception/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from open_feature.flag_evaluation.error_code import ErrorCode


class OpenFeatureError(Exception):
"""
A generic open feature exception, this exception should not be raised. Instead
the more specific exceptions extending this one should be used.
"""

def __init__(self, error_message: str = None, error_code: ErrorCode = None):
"""
Constructor for the generic OpenFeatureError.
@param error_message: a string message representing why the error has been
raised
@param error_code: the ErrorCode string enum value for the type of error
@return: the generic OpenFeatureError exception
"""
self.error_message = error_message
self.error_code = error_code


class FlagNotFoundError(OpenFeatureError):
"""
This exception should be raised when the provider cannot find a flag with the
key provided by the user.
"""

def __init__(self, error_message: str = None):
"""
Constructor for the FlagNotFoundError. The error code for
this type of exception is ErrorCode.FLAG_NOT_FOUND.
@param error_message: a string message representing why the error has been
raised
@return: the generic FlagNotFoundError exception
"""
super().__init__(error_message, ErrorCode.FLAG_NOT_FOUND)


class GeneralError(OpenFeatureError):
"""
This exception should be raised when the for an exception within the open
feature python sdk.
"""

def __init__(self, error_message: str = None):
"""
Constructor for the GeneralError. The error code for this type of exception
is ErrorCode.GENERAL.
@param error_message: a string message representing why the error has been
raised
@return: the generic GeneralError exception
"""
super().__init__(error_message, ErrorCode.GENERAL)


class ParseError(OpenFeatureError):
"""
This exception should be raised when the flag returned by the provider cannot
be parsed into a FlagEvaluationDetails object.
"""

def __init__(self, error_message: str = None):
"""
Constructor for the ParseError. The error code for this type of exception
is ErrorCode.PARSE_ERROR.
@param error_message: a string message representing why the error has been
raised
@return: the generic ParseError exception
"""
super().__init__(error_message, ErrorCode.PARSE_ERROR)


class TypeMismatchError(OpenFeatureError):
"""
This exception should be raised when the flag returned by the provider does
not match the type requested by the user.
"""

def __init__(self, error_message: str = None):
"""
Constructor for the TypeMismatchError. The error code for this type of
exception is ErrorCode.TYPE_MISMATCH.
@param error_message: a string message representing why the error has been
raised
@return: the generic TypeMismatchError exception
"""
super().__init__(error_message, ErrorCode.TYPE_MISMATCH)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from src.flag_evaluation.error_code import ErrorCode
from src.flag_evaluation.reason import Reason
from open_feature.flag_evaluation.error_code import ErrorCode
from open_feature.flag_evaluation.reason import Reason


class FlagEvaluationDetails:
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import typing

from src.exception.exceptions import GeneralError
from src.open_feature_client import OpenFeatureClient
from src.provider.provider import AbstractProvider
from open_feature.exception.exceptions import GeneralError
from open_feature.open_feature_client import OpenFeatureClient
from open_feature.provider.provider import AbstractProvider

_provider = None

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import typing
from numbers import Number

from src.exception.exceptions import GeneralError, OpenFeatureError
from src.flag_evaluation.error_code import ErrorCode
from src.flag_evaluation.flag_evaluation_details import FlagEvaluationDetails
from src.flag_evaluation.flag_type import FlagType
from src.flag_evaluation.reason import Reason
from src.provider.provider import AbstractProvider
from open_feature.exception.exceptions import GeneralError, OpenFeatureError
from open_feature.flag_evaluation.error_code import ErrorCode
from open_feature.flag_evaluation.flag_evaluation_details import FlagEvaluationDetails
from open_feature.flag_evaluation.flag_type import FlagType
from open_feature.flag_evaluation.reason import Reason
from open_feature.provider.provider import AbstractProvider


class OpenFeatureClient:
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import typing
from numbers import Number

from src.flag_evaluation.flag_evaluation_details import FlagEvaluationDetails
from src.flag_evaluation.reason import Reason
from src.provider.provider import AbstractProvider
from open_feature.flag_evaluation.flag_evaluation_details import FlagEvaluationDetails
from open_feature.flag_evaluation.reason import Reason
from open_feature.provider.provider import AbstractProvider

PASSED_IN_DEFAULT = "Passed in default"

Expand Down
File renamed without changes.
27 changes: 27 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
# pyproject.toml
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "python_open_feature_sdk"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to change this - I'm not sure if there are any conventions we need to follow. But it's not important for this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Easy enough to change at another point

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

version = "0.0.1"
description = "Standardizing Feature Flagging for Everyone"
readme = "README.md"
authors = [{ name = "Open Feature", email = "[email protected]" }]
license = { file = "LICENSE" }
classifiers = [
"License :: OSI Approved :: Apache",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
]
keywords = []
dependencies = []
requires-python = ">=3.8"

[project.optional-dependencies]
dev = ["black", "flake8", "isort", "pip-tools", "pytest", "pre-commit"]

[project.urls]
Homepage = "https://github.com/open-feature/python-sdk"

[tool.isort]
profile = "black"
multi_line_output = 3
61 changes: 58 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,65 @@
# Open Feature Python SDK
This SDK is still in an experimental phase.
# Open Feature SDK for Python
[![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)

This is the Python implementation of [OpenFeature](https://openfeature.dev), a vendor-agnostic abstraction library for evaluating feature flags.

We support multiple data types for flags (numbers, strings, booleans, objects) as well as hooks, which can alter the lifecycle of a flag evaluation.

This library is intended to be used in server-side contexts and has not been evaluated for use in mobile devices.


## Usage
While Boolean provides the simplest introduction, we offer a variety of flag types.
```python
# Depending on the flag type, use one of the methods below
flag_key = "PROVIDER_FLAG"
boolean_result = open_feature_client.get_boolean_value(key=flag_key)
number_result = open_feature_client.get_number_value(key=flag_key)
string_result = open_feature_client.get_string_value(key=flag_key)
object_result = open_feature_client.get_object_value(key=flag_key)
```
Each provider class may have further setup required i.e. secret keys, environment variables etc

## Requirements
- Python 3.8+

## Installation
### Add it to your build
Pip install
```bash
pip install python-open-feature-sdk==0.0.1
```

requirements.txt
```bash
python-open-feature-sdk==0.0.1
```
```python
pip install requirements.txt
```

### Configure it
In order to use the sdk there is some minor configuration. Follow the script below:

```python
from open_feature import open_feature_api

open_feature_api.set_provider(NoOpProvider())
open_feature_client = open_feature_api.get_client()
```

## Contacting us
We hold regular meetings which you can see [here](https://github.com/open-feature/community/#meetings-and-events).

We are also present on the `#openfeature` channel in the [CNCF slack](https://slack.cncf.io/).

## Contributors

Thanks so much to our contributors.

<a href="https://github.com/open-feature/python-sdk/graphs/contributors">
<img src="https://contrib.rocks/image?repo=open-feature/python-sdk" />
</a>


## How to use
Made with [contrib.rocks](https://contrib.rocks).
3 changes: 0 additions & 3 deletions requirements-dev.in
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
pylint
pep8
autopep8
pytest
pytest-mock
black
pip-tools
responses
pre-commit
flake8
pytest-mock
27 changes: 0 additions & 27 deletions src/exception/exceptions.py

This file was deleted.

10 changes: 10 additions & 0 deletions tests/provider/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import pytest

from open_feature import open_feature_api as api
from open_feature.provider.no_op_provider import NoOpProvider


@pytest.fixture()
def no_op_provider_client():
api.set_provider(NoOpProvider())
return api.get_client()
Loading