Code Monkey home page Code Monkey logo

unleash-client-python's Introduction

unleash-client-python

Coverage Status PyPI version PyPI - Python Version License: MIT

This is the Python client for Unleash. It implements Client Specifications 1.0 and checks compliance based on spec in unleash/client-specifications

What it supports:

  • Default activation strategies using 32-bit Murmurhash3
  • Custom strategies
  • Full client lifecycle:
    • Client registers with Unleash server
    • Client periodically fetches feature toggles and stores to on-disk cache
    • Client periodically sends metrics to Unleash Server
  • Tested on Linux (Ubuntu), OSX, and Windows

Check out the project documentation and the changelog.

Installation

Check out the package on Pypi!

pip install UnleashClient

Usage

Initialization

from UnleashClient import UnleashClient

client = UnleashClient(
    url="https://unleash.herokuapp.com",
    app_name="my-python-app",
    custom_headers={'Authorization': '<API token>'})

client.initialize_client()

For more information about configuring UnleashClient, check out the project reference docs!

Checking if a feature is enabled

A check of a simple toggle:

client.is_enabled("my_toggle")

To supply application context, use the second positional argument:

app_context = {"userId": "[email protected]"}
client.is_enabled("user_id_toggle", app_context)

Fallback function and default values

You can specify a fallback function for cases where the client doesn't recognize the toggle by using the fallback_function keyword argument:

def custom_fallback(feature_name: str, context: dict) -> bool:
    return True

client.is_enabled("my_toggle", fallback_function=custom_fallback)

You can also use the fallback_function argument to replace the obsolete default_value keyword argument by using a lambda that ignores its inputs. Whatever the lambda returns will be used as the default value.

client.is_enabled("my_toggle", fallback_function=lambda feature_name, context: True)

The fallback function must accept the feature name and context as positional arguments in that order.

The client will evaluate the fallback function only if an exception occurs when calling the is_enabled() method. This happens when the client can't find the feature flag. The client may also throw other, general exceptions.

For more information about usage, see the Usage documentation.

Getting a variant

Checking for a variant:

context = {'userId': '2'}  # Context must have userId, sessionId, or remoteAddr.  If none are present, distribution will be random.

variant = client.get_variant("variant_toggle", context)

print(variant)
> {
>    "name": "variant1",
>    "payload": {
>        "type": "string",
>        "value": "val1"
>        },
>    "enabled": True
> }

For more information about variants, see the Variant documentation.

Developing

For development, you'll need to setup the environment to run the tests. This repository is using tox to run the test suite to test against multiple versions of Python. Running the tests is as simple as running this command in the makefile:

tox -e py311

This command will take care of downloading the client specifications and putting them in the correct place in the repository, and install all the dependencies you need.

However, there are some caveats to this method. There is no easy way to run a single test, and running the entire test suite can be slow.

Manual setup

First, make sure you have pip or pip3 installed.

Then setup your viritual environment:

Linux & Mac:

python3 -m venv venv
source venv/bin/activate

Windows + cmd:

python -m venv venv
venv\Scripts\activate.bat

Powershell:

python -m venv venv
venv\Scripts\activate.bat

Once you've done your setup, run:

pip install -r requirements.txt

Run the get-spec script to download the client specifications tests:

./scripts/get-spec.sh

Now you can run the tests by running pytest in the root directory.

In order to run a single test, run the following command:

pytest testfile.py::function_name

# example: pytest tests/unit_tests/test_client.py::test_consistent_results

Linting

In order to lint all the files you can run the following command:

make fmt

unleash-client-python's People

Contributors

ameyajoshi99 avatar andreas-unleash avatar artiom avatar baaym avatar briandcho avatar dependabot-preview[bot] avatar donhui avatar fredrikoseberg avatar inirudebwoy avatar ivanklee86 avatar ivarconr avatar koffie avatar kwasniew avatar lujeni avatar michaelbrewer avatar nhubanov avatar nunogois avatar piojo avatar povilasb avatar pre-commit-ci[bot] avatar sighphyre avatar simenaasland avatar smallwat3r avatar snosratiershad avatar thomasheartman avatar tymek avatar vgerak avatar walison17 avatar wbolster avatar yjabri avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

unleash-client-python's Issues

client.initialize_client() doesn't throw an exception when connection failed to establish

When trying to initialize connection to an invalid address, error is being printed to screen but exception isn't thrown and code continues running.

Would expect to be able to catch an error and decide what I want to do with it (continue running or exit)

Error:

Unleash Client registration failed due to exception: Invalid URL 'foo/client/register': No schema supplied. Perhaps you meant http://foo/client/register?
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/UnleashClient/api/register.py", line 45, in register_client
    resp = requests.post(url + REGISTER_URL,
  File "/usr/local/lib/python3.9/dist-packages/requests/api.py", line 119, in post
    return request('post', url, data=data, json=json, **kwargs)
  File "/usr/local/lib/python3.9/dist-packages/requests/api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python3.9/dist-packages/requests/sessions.py", line 528, in request
    prep = self.prepare_request(req)
  File "/usr/local/lib/python3.9/dist-packages/requests/sessions.py", line 456, in prepare_request
    p.prepare(
  File "/usr/local/lib/python3.9/dist-packages/requests/models.py", line 316, in prepare
    self.prepare_url(url, params)
  File "/usr/local/lib/python3.9/dist-packages/requests/models.py", line 390, in prepare_url
    raise MissingSchema(error)
requests.exceptions.MissingSchema: Invalid URL 'foo/client/register': No schema supplied. Perhaps you meant http://foo/client/register?
Unleash Client feature fetch failed due to exception: Invalid URL 'foo/client/features': No schema supplied. Perhaps you meant http://foo/client/features?
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/dist-packages/UnleashClient/api/features.py", line 42, in get_feature_toggles
    resp = requests.get(base_url,
  File "/usr/local/lib/python3.9/dist-packages/requests/api.py", line 76, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/lib/python3.9/dist-packages/requests/api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python3.9/dist-packages/requests/sessions.py", line 528, in request
    prep = self.prepare_request(req)
  File "/usr/local/lib/python3.9/dist-packages/requests/sessions.py", line 456, in prepare_request
    p.prepare(
  File "/usr/local/lib/python3.9/dist-packages/requests/models.py", line 316, in prepare
    self.prepare_url(url, params)
  File "/usr/local/lib/python3.9/dist-packages/requests/models.py", line 390, in prepare_url
    raise MissingSchema(error)
requests.exceptions.MissingSchema: Invalid URL 'foo/client/features': No schema supplied. Perhaps you meant http://foo/client/features?
Unable to get feature flag toggles, using cached provisioning.
Cache Exception: '/client/features'
Unleash client does not have cached features. Please make sure client can communicate with Unleash server!

Sentry spam in case external host is not available sometimes

Describe the bug
Error spams in case if external host is not available sometimes(see attached screen) and it spams an error in Sentry.io(in my case)

Expected behavior
If host is unavailable - we should try to reconnect to it without errors

Additional context
Add any other context about the problem here.
ะธะทะพะฑั€ะฐะถะตะฝะธะต

feat: add support static context fields

In order to prepare the SDKS for strategy constraints we need to introduce two new fields in the Unleash Context

  • environment (default value is "default")
  • appName (already a required config parameter)

Both these static context fields should be configured at initialisation of the unleash SDK and be set as default values of the Unleash Context provided to the strategy implementations. The user is allowed to override these values in the context object at runtime (this is required to be backward compatible).

This support should be possible to add without breaking any contract and should be released as a minor version (3.3.x).

Run specification tests as part of CI build pipeline

Is your feature request related to a problem? Please describe.
Having done the work to write specification tests, we should run them as part of the build pipeline.

Describe the solution you'd like
Parallel if possible, though overall status should reflect unit & specification.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Only load a subset of toggles

Is your feature request related to a problem? Please describe.
We have a central unleash server that is used by many teams/different microservices. Some microservices use custom strategies while others don't.
My team uses this python client and we were ask why our team was loading all toggles that are defined and not just the ones we use. Also we get a lot of warnings about not implemented strategies that we don't use

Describe the solution you'd like
An option to select toggles I want to use so only these will be loaded regulary. Default should still load all toggles.

Describe alternatives you've considered
Deactivate warning logs to at least not get the the info about missing strategies

feat: Custom stickiness for variants

This is kind of similar to #144, but for variants.

The stickiness will be defined as a field on all the variants in the variants array, this to ensure we are not breaking our protocol. In the example we can see the stickiness is configured to "tenantId".

{"variants": [
     {
		"name": "yellow",
		"weight": 166,
		"weightType": "variable",
		"payload": {
			"type": "string",
			"value": "yellow submarine"
		},
		"overrides": [],
		"stickiness": "tenantId"
	}, {
		"name": "salmon",
		"weight": 166,
		"weightType": "variable",
		"payload": {
			"type": "string",
			"value": "salmon"
		},
		"overrides": [],
		"stickiness": "tenantId"
	}
]}

How it looks in Unleash Admin UI:
image

Edge cases:
If no stickiness is defined the SDK should assume "default" stickiness as today.

To guide the implementation we have:

  • added new client-specifications, see pr-11
  • implemented PoC in Node SDK see pr-202

Allow adjustment of API endpoint paths.

Is your feature request related to a problem? Please describe.
Currently, this project can not be used to integrate with GitLab as the API paths are different. For example: https://gitlab.com/api/v4/feature_flags/unleash/7760707/features is the API for accessing the features, where https://gitlab.com/api/v4/feature_flags/unleash/7760707 is the base API address, but this project attempts to use https://gitlab.com/api/v4/feature_flags/unleash/7760707/api/client/features instead, which is invalid for GitLab.

Describe the solution you'd like
I would like to be able to configure the API url endpoints to be /features instead of the default of /api/client/features. and etc. for each endpoint.

Describe alternatives you've considered
Changing the existing API constants works as expected, but not a frendly way to handle it across multiple systems where the project need to be configured. Maintaining a fork seems kind of pointless.

Additional context
https://github.com/Unleash/unleash-client-python/blob/master/UnleashClient/constants.py

Make cache implementation plugable

Is your feature request related to a problem? Please describe.
We're seeing some issues with the fcache implementation. It's not multi process friendly, has race conditions, and is not maintained.

Describe the solution you'd like
a) Switch to a better fcache solution, e.g. https://github.com/grantjenks/python-diskcache
b) Make the cache pluggable, so users can switch the implementation

Describe alternatives you've considered
N/A

EDIT: Just saw #193, super excited!

Regression: Errors thrown if HTTP 304 returned

If the client (5.0.0) is used against a Gitlab Feature flag server it produces a lot of these errors:

Unleash Client feature fetch failed due to exception: JSONDecodeError('Expecting value', '')

This happens because the response body from Gitlab is ampty if a HTTP 304 is returned. The fix is simple: do not try to parse the response if the server indicates that nothing has changed. Looking at the code this should be achievable by returning None or an empty dict from here if the response code was 304:

return resp.json(), etag

Getting "Error parsing IP" error in the logs

Describe the bug
After running the initialise_client getting repeated message in the logs/terminal. I am running this client as part of a flask app, but I can see the error even if I run the code as a script.

To Reproduce
Here is the code for reproducing this

from UnleashClient import UnleashClient

client = UnleashClient(
    url="my url",
    app_name="my app name",
    custom_headers={'Authorization': 'my authorisation'})

client.initialize_client()
print('client initialised')

if client.is_enabled("USE-tf-server"):
    print('yeah')
else:
    print('no')

client.destroy()

This gives the below output

Error parsing IP : 'ip' does not appear to be an IPv4 or IPv6 address
client initialised
yeah

Sample code is welcome!

Expected behavior
Error parsing IP should not come

Logs
given

Additional context
given

Report metrics only for toggles that are actually used

Describe the bug
The python client currently reports metrics for all feature toggles, whether or not they have actually been used by the client

To Reproduce
Instantiate an unleash client with metrics reporting activated, connecting to a host with multiple toggles defined. Use one toggle. Observe that in the unleash UI for an unused toggle, the client is reported under "seen in applications".

Expected behavior
The unleash client does not appear under "seen in applications" for toggles that have not been used

Additional context
This looks like a direct consequence of https://github.com/Unleash/unleash-client-python/blob/master/UnleashClient/periodic_tasks/send_metrics.py#L18

which could probably be resolved with:

    for feature_name in features.keys():
        if not (features[feature_name].yes_count or features[feature_name].no_count):
            continue
        feature_stats = {
            features[feature_name].name: {
                "yes": features[feature_name].yes_count,
                "no": features[feature_name].no_count
            }
        }

Ability to suppress logs generated by the SDK

Describe the solution you'd like
I would like the ability to suppress logs from the SDK. I have tried setting the verbose_log_level to the highest value, but we still see a large traceback when the Unleash server is not running or connected.

We're currently in the very early exploratory stages with incorporating Unleash. Most team members have no need to run the Unleash server while it is being developed. However, if the server is not running, we see the traceback shown below on a regular cycle (probably on the refresh_interval).

I do realize that it is important to log some kind of exception when the server cannot be reached, but it would be nice to have some kind of control over what is logged. I think at a minimum, it would be great if the connection error was logged as a single line with the ability to enable the traceback if desired.

Describe alternatives you've considered
I've considered changing the refresh interval to some arbitrarily high value, but this doesn't eliminated the logging; it just delays them.

Additional context
Here are example logs:

sources_client  | Unleash Client feature fetch failed due to exception: HTTPConnectionPool(host='unleash', port=4242): Max retries exceeded with url: /api/client/features (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f0cfc29bc70>: Failed to establish a new connection: [Errno -2] Name or service not known'))
sources_client  | Traceback (most recent call last):
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/urllib3/connection.py", line 169, in _new_conn
sources_client  |     conn = connection.create_connection(
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/urllib3/util/connection.py", line 73, in create_connection
sources_client  |     for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
sources_client  |   File "/usr/lib64/python3.8/socket.py", line 918, in getaddrinfo
sources_client  |     for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
sources_client  | socket.gaierror: [Errno -2] Name or service not known
sources_client  | 
sources_client  | During handling of the above exception, another exception occurred:
sources_client  | 
sources_client  | Traceback (most recent call last):
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/urllib3/connectionpool.py", line 699, in urlopen
sources_client  |     httplib_response = self._make_request(
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/urllib3/connectionpool.py", line 394, in _make_request
sources_client  |     conn.request(method, url, **httplib_request_kw)
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/urllib3/connection.py", line 234, in request
sources_client  |     super(HTTPConnection, self).request(method, url, body=body, headers=headers)
sources_client  |   File "/usr/lib64/python3.8/http/client.py", line 1255, in request
sources_client  |     self._send_request(method, url, body, headers, encode_chunked)
sources_client  |   File "/usr/lib64/python3.8/http/client.py", line 1301, in _send_request
sources_client  |     self.endheaders(body, encode_chunked=encode_chunked)
sources_client  |   File "/usr/lib64/python3.8/http/client.py", line 1250, in endheaders
sources_client  |     self._send_output(message_body, encode_chunked=encode_chunked)
sources_client  |   File "/usr/lib64/python3.8/http/client.py", line 1010, in _send_output
sources_client  |     self.send(msg)
sources_client  |   File "/usr/lib64/python3.8/http/client.py", line 950, in send
sources_client  |     self.connect()
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/urllib3/connection.py", line 200, in connect
sources_client  |     conn = self._new_conn()
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/urllib3/connection.py", line 181, in _new_conn
sources_client  |     raise NewConnectionError(
sources_client  | urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0x7f0cfc29bc70>: Failed to establish a new connection: [Errno -2] Name or service not known
sources_client  | 
sources_client  | During handling of the above exception, another exception occurred:
sources_client  | 
sources_client  | Traceback (most recent call last):
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/requests/adapters.py", line 439, in send
sources_client  |     resp = conn.urlopen(
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/urllib3/connectionpool.py", line 755, in urlopen
sources_client  |     retries = retries.increment(
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/urllib3/util/retry.py", line 574, in increment
sources_client  |     raise MaxRetryError(_pool, url, error or ResponseError(cause))
sources_client  | urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='unleash', port=4242): Max retries exceeded with url: /api/client/features (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f0cfc29bc70>: Failed to establish a new connection: [Errno -2] Name or service not known'))
sources_client  | 
sources_client  | During handling of the above exception, another exception occurred:
sources_client  | 
sources_client  | Traceback (most recent call last):
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/UnleashClient/api/features.py", line 42, in get_feature_toggles
sources_client  |     resp = requests.get(base_url,
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/requests/api.py", line 76, in get
sources_client  |     return request('get', url, params=params, **kwargs)
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/requests/api.py", line 61, in request
sources_client  |     return session.request(method=method, url=url, **kwargs)
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/requests/sessions.py", line 542, in request
sources_client  |     resp = self.send(prep, **send_kwargs)
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/requests/sessions.py", line 655, in send
sources_client  |     r = adapter.send(request, **kwargs)
sources_client  |   File "/opt/app-root/lib64/python3.8/site-packages/requests/adapters.py", line 516, in send
sources_client  |     raise ConnectionError(e, request=request)
sources_client  | requests.exceptions.ConnectionError: HTTPConnectionPool(host='unleash', port=4242): Max retries exceeded with url: /api/client/features (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f0cfc29bc70>: Failed to establish a new connection: [Errno -2] Name or service not known'))
sources_client  | Unleash Client metrics submission failed due to exception: HTTPConnectionPool(host='unleash', port=4242): Max retries exceeded with url: /api/client/metrics (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f0cfc2b5be0>: Failed to establish a new connection: [Errno -2] Name or service not known'))
sources_client  | Unable to get feature flag toggles, using cached provisioning.

Jitter for refresh_interval

Is your feature request related to a problem? Please describe.
When starting a lot of processes (e.g. many pods in kubernetes where each can start many processes) feature flags server can be overwhelmed with a wave of requests all at the same time.

Describe the solution you'd like
Allow additional option to set preferred jitter value for refresh_interval.

Describe alternatives you've considered
Caching proxy could solve the problem but that is another infrastructure element that needs to be maintained.

Additional context
apscheduler already supports jitter:
https://apscheduler.readthedocs.io/en/3.x/modules/triggers/interval.html#apscheduler.triggers.interval.IntervalTrigger

Question: should client initialized state be True if the client cannot reach the Unleash server?

I am in the process of trying to implement Unleash for our project. But we're in a bit of a weird state where we want the application to run ok even if the Unleash server is not running. I was hoping to avoid using some env variable to declare whether or not we should initialize the client. Currently, during client initialization (specifically, the registration step), if the Unleash server is not running, we hit a ConnectionError which the client library logs, but doesn't re-raise. Maybe this is intentional, but I'm not sure. Then, the is_initialized is set to True even though the client cannot reach the server.

Is this expected behavior? I would think that the client should be in an un-initialized state if it is not able to reach the server during the initialize_client step.

Fallback function should not be called from is_enabled() on feature

Describe the bug
Fallback function should not be called from is_enabled() on feature. It should only be called if a) an unexpected exception occurs or feature flag is missing.

To Reproduce
Run unit tests and observe how often fallback function is being called.

Expected behavior
Fallback function should only be called:

  • If feature is missing.
  • On generic exception.

Logs
If applicable, add logs or output to help explain your problem.

Additional context
Request from client testing.

Returning "name": "disabled" when getting variants using Python SDK

Describe the bug
When using the unleash client's get_variant function, we are receiving unexpected results:

context = {"userId": "YdTFXyyh463XQbZK"}
variant = unleash_client.get_variant("live.gbdt_v5_20220309_active_user", context)

Result:
{'name': 'disabled', 'enabled': True}

To Reproduce
Steps to reproduce the behavior:

  1. Create feature toggle with the name live.gbdt_v5_20220309_active_user
  2. Populate at least 1 random variant
  3. Try getting a variant with that user id and feature toggle name

Expected behavior
We expect it to return a randomized variant with its payload.

Logs

{'userId': 'YdTFXyyh463XQbZK'}
FEATURE TOGGLE: live.gbdt_v5_20220309_active_user
ENABLED:  True
VARIANT:  {'name': 'disabled', 'enabled': True}

Additional context
Add any other context about the problem here.

UnleashClient not respecting changes to constraints

Describe the bug

The UnleashClient is not reflecting constraint changes made in the Unleash UI in a multi-process, threaded django/uwsgi context. When we change the constraint for our Default strategy, we observe that the apscheduler job updates the FileCache. But, when we make a a call to UnleashClient::is_enabled, we don't get the correct value. What's strange is we get the right behavior in the django shell on the same host. It appears that the client is using the constraint that it picked up when it was first initialized for all is_enabled calls.

Our understanding is that UnleashClient should be a singleton. As such, we instantiate and initialize it during application startup, assigning it as a module level variable.

We're running the following versions of software:

  • python 3.7.5
  • django 2.2.24
  • unleashclient 4.4.0
  • uwsgi 2.0.15-debian

To Reproduce

  1. Setup a Default strategy that uses environment IN <some environment> that will return true when UnleashClient::is_enabled is called.
  2. Change <some environment> such that UnleashClient::is_enabled should return false.
  3. Wait a minute.
  4. Observe that the FileCache is updated.
  5. Make a call to UnleashClient::is_enabled and observe that it still returns true.

Expected behavior

After some amount of time, UnleashClient::is_enabled should respect the constraint change made in the server and what is present in the FileCache.

feat: FlexibleRollout should support "custom" stickiness.

The Flexible Rollout strategy supports multiple options for stickiness, used to define how we guarantee consistency for a gradual rollout.

Today it support the following options:

  • default (Unleash chooses the first value present on the context in defined order userId, sessionId, random.)
  • userId
  • sessionId
  • random

We have now extended the protocol to support any field in the unleash context. In addition It can be any of the predefined context field, in addition to any custom properties the user has defined.

This means that the "stickiness" parameter in the strategy configuration can now be any field on the context, and the SDK should use the specified context field to calculate the "hash" which determine whether the activation strategy should evaluate to true or false.

How it looks in the UI:
image

Edge case:

  • If the specified context field is not specific the activation strategy should evaluate to false.

To guide the implementation we have:

  • added new client-specifications, see pr-11
  • implemented PoC in Node SDK see pr-201

Failed to migrate db OperationalError: The server does not support SSL connections

latest image fails to connect to postgres

[2021-05-27T10:10:10.141] [ERROR] server-impl.js - Failed to migrate db OperationalError: The server does not support SSL connections
    at Socket.<anonymous> (/unleash/node_modules/pg/lib/connection.js:71:37)
    at Object.onceWrapper (events.js:421:26)
    at Socket.emit (events.js:314:20)
    at addChunk (_stream_readable.js:297:12)
    at readableAddChunk (_stream_readable.js:272:9)
    at Socket.Readable.push (_stream_readable.js:213:10)
    at TCP.onStreamRead (internal/stream_base_commons.js:188:23) {
  cause: Error: The server does not support SSL connections
      at Socket.<anonymous> (/unleash/node_modules/pg/lib/connection.js:71:37)
      at Object.onceWrapper (events.js:421:26)
      at Socket.emit (events.js:314:20)
      at addChunk (_stream_readable.js:297:12)
      at readableAddChunk (_stream_readable.js:272:9)
      at Socket.Readable.push (_stream_readable.js:213:10)
      at TCP.onStreamRead (internal/stream_base_commons.js:188:23),
  isOperational: true

Can't connect to local unleash server

Hi there!

Describe the bug
I am running Unleash locally using the docker image (v 4.0.5) and the python client (UnleashClient==4.4.0) to play a bit with it. Turns out that the client insist on not connecting to my local server.
I created an API key in the admin section and tried to run my client accordingly to what is described here https://docs.getunleash.io/sdks/python_sdk.

The documentation and the information inside the app is misleading because it is not clear if it is necessary to append /api or not. For instance, https://github.com/Unleash/unleash-client-python/blob/master/README.md omits the /api/ but in the other docs you can find it.

Screen Shot 2021-06-29 at 14 42 32

To Reproduce
Anyway, I tried both urls and the funny thing is, both fail but with different errors.

Steps to reproduce the behavior:

  1. Configure API Access in http://localhost:4242/admin/api
  2. Follow https://docs.getunleash.io/sdks/python_sdk

Sample code:

1 - With /api

In [5]: from UnleashClient import UnleashClient

In [6]: client = UnleashClient(url="http://localhost:4242/api/", app_name="content", custom_headers={'Authorization': "c795214e5ccf71a83238ba5a496a440b7e68379074172d9750bed5bb582c81d6"})

In [7]: client.initialize_client()

Stack trace

Unleash Client registration failed due to unexpected HTTP status code.
Unleash Client feature fetch failed due to unexpected HTTP status code.
Unleash Client feature fetch failed due to exception: Unleash Client feature fetch failed!
Traceback (most recent call last):
  File "/env/lib/python3.8/site-packages/UnleashClient/api/features.py", line 50, in get_feature_toggles
    raise Exception("Unleash Client feature fetch failed!")
Exception: Unleash Client feature fetch failed!
Unable to get feature flag toggles, using cached provisioning.
Cache Exception: '/client/features'
Unleash client does not have cached features. Please make sure client can communicate with Unleash server!

2 - Without /api

In [1]: from UnleashClient import UnleashClient

In [2]: client = UnleashClient(url="http://localhost:4242/", app_name="content", custom_headers={'Authorization': "c795
   ...: 214e5ccf71a83238ba5a496a440b7e68379074172d9750bed5bb582c81d6"})

In [3]: client.initialize_client()
Unleash Client registration failed due to unexpected HTTP status code.
Unleash Client feature fetch failed due to exception: Expecting value: line 1 column 1 (char 0)
Traceback (most recent call last):
  File "/env/lib/python3.8/site-packages/UnleashClient/api/features.py", line 52, in get_feature_toggles
    return resp.json()
  File "/env/lib/python3.8/site-packages/requests/models.py", line 898, in json
    return complexjson.loads(self.text, **kwargs)
  File "/env/lib/python3.8/json/__init__.py", line 357, in loads
    return _default_decoder.decode(s)
  File "/env/lib/python3.8/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "env/lib/python3.8/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Unable to get feature flag toggles, using cached provisioning.
Cache Exception: '/client/features'
Unleash client does not have cached features. Please make sure client can communicate with Unleash server!

This second scenario shows that the connectivity is not a problem, because the client receives a 200 from my local server, however the response seems to be unexpected. The content is a HTML

Expected behavior
Connect to local server. Not sure if the problem is the URL or the headers. I could not find any other examples for this scenario.

Support different types of caching, like in-memory or redis caching

Is your feature request related to a problem?

I cannot use unleash-client-python as it needs to create a caching directory and this is not possible as I have an app deployed on Google App Engine and it's read only file system.

Describe the solution you'd like

Support different types of caching, like in-memory or redis caching.

Describe alternatives you've considered

I'm blocked as the only way you support is File caching with filecache lib

KeyError: '/client/features' when failing to connect to server

Describe the bug
When the client fails to connect KeyError: '/client/features' is thrown

To Reproduce
Steps to reproduce the behavior:

  1. Shut down Unleash server
  2. Make sure you have no cache file
  3. Do client.initialize_client()
# From venv/lib/python3.7/site-packages/UnleashClient/periodic_tasks/fetch_and_load.py
# comments are mine
def fetch_and_load_features(url: str,
                            app_name: str,
                            instance_id: str,
                            custom_headers: dict,
                            cache: FileCache,
                            features: dict,
                            strategy_mapping: dict) -> None:

    # returns empty dict when server cannot be reached
    feature_provisioning = get_feature_toggles(url, app_name, instance_id, custom_headers)
  
    # cache is not populated
    if feature_provisioning:
        cache[FEATURES_URL] = feature_provisioning
        cache.sync()
    else:
        LOGGER.info("Unable to get feature flag toggles, using cached values.")

    # tries to access cache
    load_features(cache, features, strategy_mapping)

Expected behavior
An error explaining that the server cannot be reached is thrown.

Logs
N/A

Additional context
Note that the behaviour changes once there is a local cache file (that is you connected successfully before). In that case the cached value is returned.

Add e2e tests for UnleashClient

Is your feature request related to a problem? Please describe.
Integration testing (atm) involves spinning up a local instance, running a script that creates an UnleashClient, and looping through some feature flags.

This would be nice to automate, as hopefully it'd help catch issues like #7 .

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered

Additional context

Be able to disable logs

Is your feature request related to a problem? Please describe.
I'm having some trouble at unleash logs. The major part of my logs are unleash related logs for now, most of the logs are related to job runs at appscheduler, fetch_and_load_features and aggregate_and_send_metrics.
While using cloud functions the price increased considerably.

Describe the solution you'd like
I would like to be able to disable logs comming from unleash-client-python easily. Or at least the ones comming from appscheduler. I imagine somethign like this:

UnleashClient(
    url="myurl.com",
    app_name="myAppName",
    disable_logs=False,
    disable_job_logs=True
)

Describe alternatives you've considered
If this is not possible to implement I would love to hear by you what should be the best way to fork it and build on my own.

Additional context
My logs example:
image

Statistics are inaccurate due to aggregate_and_send_metrics()

Describe the bug
re: https://github.com/Unleash/unleash-client-python/blob/master/UnleashClient/periodic_tasks/send_metrics.py

Since datetime is an immutable data type, the parameter "last_sent" will be passed by value and not by reference. Therefore the last line of the function will update a local variable, not the class variable with the same name. The result is that all buckets will be reported with the same start time, which seems to make Unleash miscalculate the total numbers of requests for the last hour.

To Reproduce
[WIP]

Expected behavior
Stats should be reported accurately. :)

Logs
If applicable, add logs or output to help explain your problem.

Additional context
Add any other context about the problem here.

py.typed marker missing from release breaks type checking

Describe the bug

while this package contains type annotations, the released files don't contain a py.typed file (see PEP 561), which means type checkers such as mypy are not able to properly check code using this library.

To Reproduce

  • Install the package ๐Ÿ™ƒ
  • .../site-packages/UnleashClient/py.typed does not exist

Expected behavior

.../site-packages/UnleashClient/py.typed exists

Client registration failed; returns 401

Describe the bug
I was trying to use the unleash python client on my Django implementation and see a 401 on client.intialitze() call with the message "unleash client registration failed.". This is followed up with the message "unleash metrics submission failed." printed out in regular intervals.
I dug through the code and this seems to come from the register_client method in UnleashClient/api/register.py. I was curious if this was a GitLab permissions issue and tried to manually make the request from my Python shell which had the same fate. I'm confident that the request goes through and the error is not due to logic in except clause. I couldn't find any more docs on GitLab or on this repo about client registration. Nonetheless, I'm able to use the client for feature flagging without issues other than my log being filled up with the initial message and constant metric submission failure methods.

To Reproduce
Steps to reproduce the behavior(in Python shell):

  1. from UnleashClient import UnleashClient
  2. uc = UnleashClient(...)
  3. uc.intialize_client()
  4. See unleash client registration failed.
  5. See unleash metrics submission failed. printed on regular intervals

Expected behavior
I hope we can add some more documentation about client registration and how that works in case of GitLab Feature Flags. If there's no provision to do so with GitLab, an alternative would be much appreciated.

Logs
unleash client registration failed.
unleash metrics submission failed.*

Toggles not refreshed when running on AWS Lambda

Describe the bug
When run on AWS Lambda, event triggering doesn't properly work. Seems that the background thread used by BackgroundScheduler is being paused and maybe even not run at all:
https://www.reddit.com/r/learnpython/comments/fiqnln/apscheduler_job_not_firing_when_run_in_flask_on/

Sample code is welcome!

Expected behavior
A clear and concise description of what you expected to happen.

Logs
If applicable, add logs or output to help explain your problem.

feat: add support for "project" query

The Unleash API client endpoint support query for specific project, to avoid having the SDK download all feature toggles like this:

http https://app.unleash-hosted.com/demo/api/client/features?project=solar 
Authorization:56907a2fa53c1d16101d509a10b78e36190b0f918d9f122d

Which will only return feature flags for projectId=solar.

This should be implemented by adding a new configuration to the unleash SDK to specify the "projectId" and if this option is specified it should be added to the feature-toggle query as a query param when fetching feature toggles from the Unleash API.

Specifying default_value overrides real value

Describe the bug
Maybe I'm misunderstanding the purpose of default_value but the way I'm expecting it to work is:

  1. If a flag exists, the is_enabled call uses the status of the flag.
  2. If a flag doesn't exist, the default_value is used.

Instead, the default_value always seems to be used, even if the flag is present.

To Reproduce

  1. Create a Flag: myFlag
  2. Create code as such:
return str(client.is_enabled("myFlag"))

This will correctly grab the value of myFlag and enable or disable as appropriate.

Now adjust the code to:

return str(client.is_enabled("myFlag", default_value=True))

The return value will always by True, regardless of my real toggle status.

image

Unleash Client feature fetch failed due to exception: SSLError

Describe the bug

UnleashClient for me produces around 1-2k errors like this per day:

Unleash Client feature fetch failed due to exception: SSLError(MaxRetryError('HTTPSConnectionPool(host=\'us.app.unleash-hosted.com\', port=443): Max retries exceeded with url: /elowen/api/client/features (Caused by SSLError(SSLError("bad handshake: SysCallError(-1, \'Unexpected EOF\')")))'))

I'm not 100% sure as I haven't tested the networking errors, but this seems a lot like TCP connection reset.
This may be very much a server error and not a client one, but hypothetically the client could reduce the number of errors with retries?

Logs

Here's the stack trace:

SysCallError: (-1, 'Unexpected EOF')
  File "urllib3/contrib/pyopenssl.py", line 488, in wrap_socket
    cnx.do_handshake()
  File "OpenSSL/SSL.py", line 1991, in do_handshake
    self._raise_ssl_error(self._ssl, result)
  File "OpenSSL/SSL.py", line 1693, in _raise_ssl_error
    raise SysCallError(-1, "Unexpected EOF")

SSLError: ("bad handshake: SysCallError(-1, 'Unexpected EOF')",)
  File "urllib3/connectionpool.py", line 670, in urlopen
    httplib_response = self._make_request(
  File "urllib3/connectionpool.py", line 381, in _make_request
    self._validate_conn(conn)
  File "urllib3/connectionpool.py", line 978, in _validate_conn
    conn.connect()
  File "urllib3/connection.py", line 362, in connect
    self.sock = ssl_wrap_socket(
  File "urllib3/util/ssl_.py", line 386, in ssl_wrap_socket
    return context.wrap_socket(sock, server_hostname=server_hostname)
  File "urllib3/contrib/pyopenssl.py", line 494, in wrap_socket
    raise ssl.SSLError("bad handshake: %r" % e)

MaxRetryError: HTTPSConnectionPool(host='us.app.unleash-hosted.com', port=443): Max retries exceeded with url: /elowen/api/client/features (Caused by SSLError(SSLError("bad handshake: SysCallError(-1, 'Unexpected EOF')")))
  File "requests/adapters.py", line 439, in send
    resp = conn.urlopen(
  File "urllib3/connectionpool.py", line 726, in urlopen
    retries = retries.increment(
  File "urllib3/util/retry.py", line 446, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))

SSLError: HTTPSConnectionPool(host='us.app.unleash-hosted.com', port=443): Max retries exceeded with url: /elowen/api/client/features (Caused by SSLError(SSLError("bad handshake: SysCallError(-1, 'Unexpected EOF')")))
  File "UnleashClient/api/features.py", line 48, in get_feature_toggles
    resp = requests.get(base_url,
  File "requests/api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File "requests/api.py", line 60, in request
    return session.request(method=method, url=url, **kwargs)
  File "requests/sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
  File "requests/sessions.py", line 646, in send
    r = adapter.send(request, **kwargs)
  File "requests/adapters.py", line 514, in send
    raise SSLError(e, request=request)

CI @ Github Actions

Investigate:

  • Best practices for running discreet actions (i.e. tests, tox, linting) without needing to rebuild dependencies.
  • Add Windows and OSX tests.
  • Notifications on failures to #notifications
  • Auto publish to Pypi on version bumps.

Related to:
#16

Bring back command line demo client

Description
I'm always frustrated when I try to explain what the point of this is. "Show don't tell" is a clear recipe for success here, and the marching dots and bars were very easy to demo.

Describe the solution you'd like
Bring back command line demo client.

Some publicly accessible way to turn the knob, to show how responsive (or not) it is at different refresh settings. (This is also a good opportunity to demo the enterprise offering, btw)

Describe alternatives you've considered
An alternative here would be to do the same with JavaScript and refer to that, but I think it's good as a live-fire example of how to use the api.

Additional context
It used to have this. (I know because I wrote it.) In addition to being a nice debugging tool, it was pretty hypnotic.

Installation Fails pip install UnleashClient

Installation instructions specify using pip install UnleashClient.

This fails:

$ pip install UnleashClient
Collecting UnleashClient
  Using cached https://files.pythonhosted.org/packages/75/47/56b9d003dcc1ea73e4258729e06fd6bcece4972418d32c6d22d6346ebd73/UnleashClient-2.3.0.tar.gz
Requirement already satisfied: requests==2.21.0 in /home/ec2-user/.local/lib/python2.7/site-packages (from UnleashClient)
Requirement already satisfied: fcache==0.4.7 in /home/ec2-user/.local/lib/python2.7/site-packages (from UnleashClient)
Collecting mmh3==2.5.1 (from UnleashClient)
  Using cached https://files.pythonhosted.org/packages/fa/7e/3ddcab0a9fcea034212c02eb411433db9330e34d626360b97333368b4052/mmh3-2.5.1.tar.gz
Collecting apscheduler==3.6.0 (from UnleashClient)
  Using cached https://files.pythonhosted.org/packages/93/09/ffc2ed85fa578cd0d4428e9c421407e5d91a4464bbaa44f789941416ae42/APScheduler-3.6.0-py2.py3-none-any.whl
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /home/ec2-user/.local/lib/python2.7/site-packages (from requests==2.21.0->UnleashClient)
Requirement already satisfied: idna<2.9,>=2.5 in /home/ec2-user/.local/lib/python2.7/site-packages (from requests==2.21.0->UnleashClient)
Requirement already satisfied: certifi>=2017.4.17 in /home/ec2-user/.local/lib/python2.7/site-packages (from requests==2.21.0->UnleashClient)
Requirement already satisfied: urllib3<1.25,>=1.21.1 in /usr/lib/python2.7/dist-packages (from requests==2.21.0->UnleashClient)
Requirement already satisfied: appdirs in /home/ec2-user/.local/lib/python2.7/site-packages (from fcache==0.4.7->UnleashClient)
Requirement already satisfied: six>=1.4.0 in /usr/lib/python2.7/dist-packages (from apscheduler==3.6.0->UnleashClient)
Requirement already satisfied: setuptools>=0.7 in /usr/lib/python2.7/dist-packages (from apscheduler==3.6.0->UnleashClient)
Collecting tzlocal>=1.2 (from apscheduler==3.6.0->UnleashClient)
  Using cached https://files.pythonhosted.org/packages/cb/89/e3687d3ed99bc882793f82634e9824e62499fdfdc4b1ae39e211c5b05017/tzlocal-1.5.1.tar.gz
Collecting funcsigs; python_version == "2.7" (from apscheduler==3.6.0->UnleashClient)
  Using cached https://files.pythonhosted.org/packages/69/cb/f5be453359271714c01b9bd06126eaf2e368f1fddfff30818754b5ac2328/funcsigs-1.0.2-py2.py3-none-any.whl
Collecting pytz (from apscheduler==3.6.0->UnleashClient)
  Using cached https://files.pythonhosted.org/packages/3d/73/fe30c2daaaa0713420d0382b16fbb761409f532c56bdcc514bf7b6262bb6/pytz-2019.1-py2.py3-none-any.whl
Requirement already satisfied: futures; python_version == "2.7" in /usr/lib/python2.7/dist-packages (from apscheduler==3.6.0->UnleashClient)
Installing collected packages: mmh3, pytz, tzlocal, funcsigs, apscheduler, UnleashClient
  Running setup.py install for mmh3 ... error
    Complete output from command /usr/bin/python2.7 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-Q5K2j1/mmh3/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-mp8MCc-record/install-record.txt --single-version-externally-managed --compile:
    running install
    running build
    running build_ext
    building 'mmh3' extension
    creating build
    creating build/temp.linux-x86_64-2.7
    gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/include/python2.7 -c mmh3module.cpp -o build/temp.linux-x86_64-2.7/mmh3module.o
    unable to execute 'gcc': No such file or directory
    error: command 'gcc' failed with exit status 1

    ----------------------------------------
Command "/usr/bin/python2.7 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-Q5K2j1/mmh3/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-mp8MCc-record/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-build-Q5K2j1/mmh3/
You are using pip version 9.0.3, however version 19.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

unleash client produces the wrong value when refreshing job has a long delay interval.

set refresh_interval to 999999, and the value was set correctly at the beginning but got reset to the opponent (True->False) after a while.

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Sample code is welcome!

Expected behavior
A clear and concise description of what you expected to happen.

Logs
If applicable, add logs or output to help explain your problem.

Additional context
Add any other context about the problem here.

Add support for flexibleRollout strategy & constraints for other strategies

flexibleRollout

  • Create generic Constraint object.
  • Use Marshmallow to load flexibleRollout
  • Add flexibleRollout Strategy.
  • Load flexibleRollout strategy from configs.
  • Integration/e2e testing

Add constraints to other strategies

  • Create base classes/data classes for provisioning, constraints, and new strategies.
  • Update strategy loading.
  • Update existing strategies.
  • Ensure backwards compatibility for CustomStrategies
  • Add test for StrategiesV2 based CustomStrategies.
  • Improve loader tests.
  • Add spec tests for constraints.
  • Add spec tests for featureRollout.

Misc.

  • Documentation

Resources:

feat: add support for 304 via Etag and If-None-Match

Is your feature request related to a problem? Please describe.
Today this SDK does not use the returned ETag value for the /api/client/features call. This forces the Unleash Server to return the full list of feature toggles for every request. This is problematic because it increases the network load by returning the same configuration every time, adds more work fro the Unleash API and also requires the SDK to update the local cache for each fetch call.

Describe the solution you'd like
The SDK should capture the ETag value returned from the Unleash API and reuse that for the the next feature toggle call by setting the value as the "If-None-Match" header. If the Unleash API returns 304, no data is returned and the SDK should assume it has the latest configuration and not do anything (before the next poll).

Describe alternatives you've considered
none

Additional context
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match

Relax "Unable to get feature flag toggles" log message

Is your feature request related to a problem? Please describe.

  1. UnleashClient produces these logs periodically:
WARNING: Unable to get feature flag toggles, using cached provisioning.
  1. The source:

    LOGGER.warning("Unable to get feature flag toggles, using cached provisioning.")

  2. This is a normal behavior and is triggered when response status code from unleash is 304:

    if resp.status_code == 304:

    i.e. no changes have been made to toggles

Describe the solution you'd like

Just use an info log message.

In-memory features are not updated when initialized from a Celery worker

Describe the bug

We use unleash-client-python in our code in both gunicorn and Celery environments. The fetch_and_load_features method runs in the background in both cases. However, updates are only propagated to the client in gunicorn environments. Clients initialized in Celery environments don't receive updates to it's memory instance (self.features). Updates are propagated to the cache, so I can workaround the problem by running load_features in the is_enabled function:

  def is_enabled(self,
                   feature_name: str,
                   context: dict = {},
                   default_value: bool = False,
                   fallback_function: Callable = None) -> bool:
        # Docstring removed for brevity
        context.update(self.unleash_static_context)

        # Workaround the problem by loading features from the cache:
        load_features(self.cache, self.features, self.strategy_mapping)

        if self.is_initialized:
            try:
                return self.features[feature_name].is_enabled(context, default_value)
       # more code removed for brevity

The problem is perhaps related to how the apsscheduler.BackgroundScheduler works/not works together with Celery, but I'm not sure.

Do you have any suggestions to solve this problem--without manually loading the features from cache?

To Reproduce
Steps to reproduce the behavior:

  1. Initiate the Unleash client in a Celery worker
  2. Wait for the first fetch_and_load_features to complete.
  3. Update a toggle in the Unleash interface, for example from enabled to disabled.
  4. Wait for the background worker to fetch and load features.
  5. Call is_enabled on the feature. The feature's state is not updated but should be.

Silent "Error checking feature flag" errors

We are running a generic script that pulls values from unleash using python client for different services. Not all flags exist for each service, and we're getting "Error checking feature flag" for those that don't.
In our case these errors aren't needed and would like to be able to silent them to avoid errors in logs.

Update requests dependency

Describe the bug
Not a true bug, just an out of date dependency. Requests dependency is needs to be updated. When I install Unleash I have the following conflicts:

ERROR: unleashclient 3.4.2 has requirement requests==2.22.0, but you'll have requests 2.23.0 which is incompatible.
ERROR: requests 2.23.0 has requirement urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1, but you'll have urllib3 1.20 which is incompatible.
ERROR: flake8 3.8.1 has requirement pycodestyle<2.7.0,>=2.6.0a1, but you'll have pycodestyle 2.5.0 which is incompatible.
ERROR: flake8 3.8.1 has requirement pyflakes<2.3.0,>=2.2.0, but you'll have pyflakes 2.1.1 which is incompatible.
ERROR: awscli 1.17.7 has requirement botocore==1.14.7, but you'll have botocore 1.12.243 which is incompatible.
ERROR: awscli 1.17.7 has requirement s3transfer<0.4.0,>=0.3.0, but you'll have s3transfer 0.2.1 which is incompatible.
ERROR: dulwich 0.19.16 has requirement urllib3>=1.24.1, but you'll have urllib3 1.20 which is incompatible.

To Reproduce
Steps to reproduce the behavior:

  1. Install unleash Python plugin with the following requirements.txt (the actual conflicts are with the libraries listed in the error message).
pyyaml==5.2
atomicwrites==1.3.0
attrs==19.2.0
boto3==1.9.243
botocore==1.12.243
colorama==0.4.1
coverage==4.5.4
docutils==0.15.2
entrypoints==0.3
flake8>=3.8.1
graphql-core==2.2.1
importlib-metadata==0.23
jmespath==0.9.4
mccabe==0.6.1
more-itertools==7.2.0
nose2==0.9.1
packaging==19.2
pluggy==0.13.0
promise==2.2.1
py==1.8.0
pycodestyle==2.5.0
pyflakes==2.1.1
pyparsing==2.4.2
pytest==5.2.1
python-dateutil==2.8.0
Rx==1.6.1
s3transfer==0.2.1
sgqlc==8.0
six==1.12.0
urllib3==1.20.0
wcwidth==0.1.7
zipp==0.6.0
python-json-logger>=0.1.9
requests>=2.23.0
retry>=0.9.2
Unleash

Expected behavior
Shouldn't have these incompatibility messages when library is up to date.

Logs
n/a

Additional context
n/a

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.