Skip to content

Commit 6d8e4b8

Browse files
author
Rafael Sierra
committed
Added Consul repository
Requires a `python-consul`'s `consul.Consul` object during initialization
1 parent 24cc51c commit 6d8e4b8

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

decouple.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,34 @@ def __getitem__(self, key):
115115
return self.parser.get(self.SECTION, key)
116116

117117

118+
class RepositoryConsul(RepositoryEmpty):
119+
"""
120+
Retrieves options keys from a consul connection.
121+
"""
122+
def __init__(self, consul, root=''):
123+
"""
124+
consul: consul.Consul object
125+
root: Where to look for keys (prepended to the actual key name)
126+
"""
127+
self.consul = consul
128+
self.root = root
129+
self.data = {}
130+
131+
def __contains__(self, key):
132+
idx, data = self.consul.kv.get('/'.join([self.root, key]))
133+
if not data:
134+
return False
135+
else:
136+
self.data[key] = data.get('Value', None)
137+
return True
138+
139+
def __getitem__(self, key):
140+
# Makes sure that the data is loaded
141+
if key in self:
142+
return self.data[key]
143+
return None
144+
145+
118146
class RepositoryEnv(RepositoryEmpty):
119147
"""
120148
Retrieves option keys from .env files with fall back to os.environ.

tests/test_consul.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# coding: utf-8
2+
import os
3+
import pytest
4+
5+
from decouple import Config, RepositoryConsul, UndefinedValueError
6+
7+
class FakeConsul(object):
8+
"""This class is a monkey patch to simulate consul.Consul.kv.get"""
9+
def __init__(self, fake_data=None):
10+
self.fake_data = fake_data or {}
11+
class KV:
12+
idx = 0
13+
@classmethod
14+
def get(cls, key):
15+
cls.idx += 1
16+
if key in self.fake_data:
17+
return cls.idx, {'Value': self.fake_data[key]}
18+
return cls.idx, None
19+
self.kv = KV()
20+
21+
22+
@pytest.fixture
23+
def consul():
24+
c = FakeConsul()
25+
c.fake_data['myapp/secret_key'] = 'some really secure secret key'
26+
c.fake_data['myapp/debug'] = False
27+
c.fake_data['staging/debug'] = True
28+
c.fake_data['unaccessible'] = 'not be accessible if root is set to `myapp`'
29+
return c
30+
31+
32+
@pytest.fixture
33+
def config(consul):
34+
return Config(RepositoryConsul(consul, 'myapp'))
35+
36+
37+
def test_consul_undefined(config):
38+
with pytest.raises(UndefinedValueError):
39+
# `unaccessible` is defined in a scope that this consul repository
40+
# has no access to
41+
config('unaccessible')
42+
43+
44+
def test_consul_get_bool(config):
45+
assert config('debug', cast=bool) is False
46+
47+
48+
def test_consul_basic(config):
49+
os.environ['unaccessible'] = 'overwrite'
50+
assert config('unaccessible') == 'overwrite'
51+
del os.environ['unaccessible']

0 commit comments

Comments
 (0)