Skip to content

Commit d32a599

Browse files
FFM-11935 Add with_httpx_args option (#106)
* {client,config}: allow passing through additional httpx arguments (#105) * FFM-11935 Fix line length for flake8 / Update docs and add sample. * FFM-11935 Update docstring and flake8 formatting change --------- Co-authored-by: Zane van Iperen <[email protected]>
1 parent a5e9a1f commit d32a599

File tree

9 files changed

+99
-10
lines changed

9 files changed

+99
-10
lines changed

docs/further_reading.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,35 @@ Then pass them with the new URLs when creating your client.
109109

110110
[Example](../examples/url_change_example/url_change.py)
111111

112+
## HTTPX Configuration Options
113+
The `httpx` client allows you to apply various configurations to all outgoing requests by passing parameters to the Client constructor.
114+
115+
Here are some of the options you can configure:
116+
117+
* `proxies`: Configure proxy settings to route requests through a specific proxy server.
118+
* `headers`: Add custom headers to all outgoing requests.
119+
* `timeout`: Set request and connection timeouts.
120+
* `transport`: Use a custom transport layer for advanced scenarios.
121+
122+
**Important**: ensure you supply a valid httpx option. if you supply an option that doesn't exist in `httpx` the SDK will fail to initialize with `got an unexpected keyword argument`
123+
124+
Further Reading:
125+
126+
* [HTTPX Advanced Client Configuration](https://www.python-httpx.org/advanced/clients/)
127+
128+
Example of Custom HTTPX Client Configuration
129+
130+
Here's an example demonstrating how to use httpx client arguments in the SDK:
131+
132+
133+
```python
134+
from featureflags.config import with_httpx_args
135+
136+
client = CfClient(api_key,
137+
with_base_url("https://config.ff.harness.io/api/1.0"),
138+
with_httpx_args({
139+
'proxies': 'http://localhost:8888'}
140+
}))
141+
```
142+
143+
[Example](../examples/with_httpx_args_example/with_httpx_args.py)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import logging
2+
import time
3+
4+
from featureflags.config import with_httpx_args, with_base_url
5+
from featureflags.evaluations.auth_target import Target
6+
from featureflags.client import CfClient
7+
from featureflags.util import log
8+
9+
10+
def main():
11+
log.setLevel(logging.INFO)
12+
log.info("Starting example")
13+
api_key = "Your API key"
14+
15+
# Using the httpx proxies option.
16+
# Ensure you supply a valid httpx option. if you supply an option that
17+
# doesn't exist in `httpx` the SDK will fail to initialize with `got an
18+
# unexpected keyword argument`
19+
client = CfClient(api_key,
20+
with_httpx_args({'proxies': 'http://localhost:8888'}))
21+
22+
client.wait_for_initialization()
23+
24+
target = Target(identifier='HT_1', name="Harness_Target_1",
25+
attributes={"location": "emea"})
26+
27+
while True:
28+
result = client.bool_variation('identifier_of_your_bool_flag', target,
29+
False)
30+
log.info("Result %s", result)
31+
time.sleep(10)
32+
33+
34+
if __name__ == "__main__":
35+
main()

featureflags/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
__author__ = """Harness"""
44
__email__ = "[email protected]"
5-
__version__ = '1.6.4'
5+
__version__ = '1.7.0'

featureflags/analytics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
VARIATION_VALUE_ATTRIBUTE = 'variationValue'
4040
TARGET_ATTRIBUTE = 'target'
4141
SDK_VERSION_ATTRIBUTE = 'SDK_VERSION'
42-
SDK_VERSION = '1.6.4'
42+
SDK_VERSION = '1.7.0'
4343
SDK_TYPE_ATTRIBUTE = 'SDK_TYPE'
4444
SDK_TYPE = 'server'
4545
SDK_LANGUAGE_ATTRIBUTE = 'SDK_LANGUAGE'

featureflags/client.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from .streaming import StreamProcessor
2424
from .util import log
2525

26-
VERSION: str = "1.6.4"
26+
VERSION: str = "1.7.0"
2727

2828

2929
class MissingOrEmptyAPIKeyException(Exception):
@@ -177,7 +177,8 @@ def authenticate(self):
177177
verify = self._config.tls_trusted_cas_file
178178

179179
client = Client(base_url=self._config.base_url, verify_ssl=verify,
180-
raise_on_unexpected_status=True)
180+
raise_on_unexpected_status=True,
181+
httpx_args=self._config.httpx_args)
181182
body = AuthenticationRequest(api_key=self._sdk_key)
182183
response = retryable_authenticate(client=client, body=body).parsed
183184
self._auth_token = response.auth_token
@@ -206,7 +207,8 @@ def make_client(self, url, token, account_id, config):
206207
client = AuthenticatedClient(
207208
base_url=url,
208209
token=token,
209-
verify_ssl=verify
210+
verify_ssl=verify,
211+
httpx_args=self._config.httpx_args,
210212
)
211213
# Additional headers used to track usage
212214
additional_headers = {

featureflags/config.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Configuration is a base class that has default values that you can change
22
during the instance of the client class"""
33

4-
from typing import Callable
4+
from typing import Any, Callable, Dict
55

66
from .interface import Cache
77
from .lru_cache import LRUCache
@@ -28,7 +28,8 @@ def __init__(
2828
enable_stream: bool = True,
2929
enable_analytics: bool = True,
3030
max_auth_retries: int = 10,
31-
tls_trusted_cas_file: str = None
31+
tls_trusted_cas_file: str = None,
32+
httpx_args: Dict[str, Any] = None,
3233
):
3334
self.base_url = base_url
3435
self.events_url = events_url
@@ -49,6 +50,9 @@ def __init__(
4950
self.enable_analytics = enable_analytics
5051
self.max_auth_retries = max_auth_retries
5152
self.tls_trusted_cas_file = tls_trusted_cas_file
53+
self.httpx_args = httpx_args
54+
if self.httpx_args is None:
55+
self.httpx_args = {}
5256

5357

5458
default_config = Config()
@@ -102,7 +106,22 @@ def with_tls_trusted_cas_file(value: str) -> Callable:
102106
It takes a filename of a CA bundle. It should include all intermediate CAs
103107
and the root CA (concatenated in PEM format).
104108
"""
109+
105110
def func(config: Config) -> None:
106111
config.tls_trusted_cas_file = value
107112

108113
return func
114+
115+
116+
"""
117+
Allows the user to pass additional arguments to the HTTPx client
118+
configuration, such as proxies, timeouts, or custom headers. See
119+
https://www.python-httpx.org/advanced/clients/ for further information.
120+
"""
121+
122+
123+
def with_httpx_args(args: Dict[str, Any]) -> Callable:
124+
def func(config: Config) -> None:
125+
config.httpx_args.update(args)
126+
127+
return func

featureflags/streaming.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ def run(self):
168168
cluster=self._cluster).parsed
169169

170170
if fc is None:
171-
log.debug("Feature config '%s' not loaded", self._msg.identifier)
171+
log.debug("Feature config '%s' not loaded",
172+
self._msg.identifier)
172173
else:
173174
log.debug("Feature config '%s' loaded", fc.feature)
174175
self._repository.set_flag(fc)

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.6.4
2+
current_version = 1.7.0
33
commit = True
44
tag = True
55

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,6 @@
5858
test_suite="tests",
5959
tests_require=test_requirements,
6060
url="https://github.com/harness/ff-python-server-sdk",
61-
version='1.6.4',
61+
version='1.7.0',
6262
zip_safe=False,
6363
)

0 commit comments

Comments
 (0)