Skip to content

Commit

Permalink
Merge pull request #34 from djmitche/async-config
Browse files Browse the repository at this point in the history
[breaking] make config loaders async
  • Loading branch information
djmitche authored Oct 16, 2019
2 parents f89064e + 1172fa8 commit 5d3cdcb
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 25 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ It has a `load` method that will load data, optionally parsing it as YAML:

```python
data = loader.load("data.bin")
aliases = loader.load("aliases.yml", parse="yaml")
aliases = await loader.load("aliases.yml", parse="yaml")
```

You can also define your own loader class.
Expand All @@ -191,7 +191,7 @@ class Workers(ConfigList):
bigness = attr.ib(type=int, default=1)
```

Then simply call `Workers.load(loader)` to load a `workers.yml` that looks something like
Then simply call `await Workers.load(loader)` to load a `workers.yml` that looks something like

```yaml
- workerId: small
Expand Down
36 changes: 22 additions & 14 deletions tcadmin/tests/test_util_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,42 @@
import os
import yaml
import attr
import pytest

from tcadmin.util.config import ConfigList, ConfigDict, LocalLoader, StaticLoader


def test_local_loader():
@pytest.mark.asyncio
async def test_local_loader():
dir = os.path.dirname(__file__)
loader = LocalLoader(dir)
assert yaml.load(loader.load_raw("testfile.yml")) == {"data": [1, 2]}
assert yaml.load(await loader.load_raw("testfile.yml")) == {"data": [1, 2]}


def test_local_loader_parse():
@pytest.mark.asyncio
async def test_local_loader_parse():
dir = os.path.dirname(__file__)
loader = LocalLoader(dir)
assert loader.load("testfile.yml", parse="yaml") == {"data": [1, 2]}
assert await loader.load("testfile.yml", parse="yaml") == {"data": [1, 2]}


def test_local_loader_no_parse():
@pytest.mark.asyncio
async def test_local_loader_no_parse():
dir = os.path.dirname(__file__)
loader = LocalLoader(dir)
assert yaml.load(loader.load("testfile.yml")) == {"data": [1, 2]}
assert yaml.load(await loader.load("testfile.yml")) == {"data": [1, 2]}


def test_static_loader_yaml():
@pytest.mark.asyncio
async def test_static_loader_yaml():
loader = StaticLoader({"data.yml": {"foo": "bar"}})
assert yaml.load(loader.load_raw("data.yml")) == {"foo": "bar"}
assert yaml.load(await loader.load_raw("data.yml")) == {"foo": "bar"}


def test_static_loader_raw():
@pytest.mark.asyncio
async def test_static_loader_raw():
loader = StaticLoader({"data.bin": b"abcd"})
assert loader.load_raw("data.bin") == b"abcd"
assert await loader.load_raw("data.bin") == b"abcd"


loader = StaticLoader(
Expand Down Expand Up @@ -68,8 +74,9 @@ def sum_values(self):
return sum(i.v for i in self)


def test_config_array():
kvs = KVs.load(loader)
@pytest.mark.asyncio
async def test_config_array():
kvs = await KVs.load(loader)
assert kvs[0].k == "a"
assert kvs[0].v == 1
assert kvs[1].k == "b"
Expand All @@ -95,6 +102,7 @@ class Item:
historical = attr.ib(type=bool, default=False)


def test_config_dict():
nicks = Nicknames.load(loader)
@pytest.mark.asyncio
async def test_config_dict():
nicks = await Nicknames.load(loader)
assert nicks["Gertie"].fullname == "Gertrude"
18 changes: 9 additions & 9 deletions tcadmin/util/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,26 @@


class Loader(ABC):
def load(self, filename, parse=None):
async def load(self, filename, parse=None):
"""Load the given file. If parse is `"yaml"` then the content is parsed
as YAML; otherwise it is returned as a bytestring."""
raw = self.load_raw(filename)
raw = await self.load_raw(filename)
if parse == "yaml":
return yaml.load(raw)
elif parse:
raise ValueError("Unknown parse format {}".format(parse))
return raw

@abstractmethod
def load_raw(self, filename):
async def load_raw(self, filename):
pass


class StaticLoader(Loader):
def __init__(self, data):
self.data = data

def load_raw(self, filename):
async def load_raw(self, filename):
raw = self.data[filename]
if not isinstance(raw, bytes):
return yaml.dump(raw)
Expand All @@ -40,15 +40,15 @@ class LocalLoader(Loader):
def __init__(self, directory="."):
self.directory = directory

def load_raw(self, filename):
async def load_raw(self, filename):
with open(os.path.join(self.directory, filename), "rb") as f:
return f.read()


class ConfigList(list):
@classmethod
def load(cls, loader):
data = loader.load(cls.filename, parse="yaml")
async def load(cls, loader):
data = await loader.load(cls.filename, parse="yaml")
assert isinstance(data, list), "{} is not a YAML array".format(cls.filename)
return cls(cls.Item(**cls.transform_item(item)) for item in data)

Expand All @@ -59,8 +59,8 @@ def transform_item(cls, item):

class ConfigDict(dict):
@classmethod
def load(cls, loader):
data = loader.load(cls.filename, parse="yaml")
async def load(cls, loader):
data = await loader.load(cls.filename, parse="yaml")
assert isinstance(data, dict), "{} is not a YAML object".format(cls.filename)
return cls((k, cls.Item(k, **cls.transform_item(v))) for k, v in data.items())

Expand Down

0 comments on commit 5d3cdcb

Please sign in to comment.