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

WIP: Add typing to configstore #28

Closed
wants to merge 13 commits into from
53 changes: 0 additions & 53 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ workflows:
version: 2
configstore:
jobs:
- test-py35
- test-py36
- test-py37
- test-py27
- check

jobs:
Expand Down Expand Up @@ -63,57 +61,6 @@ jobs:
- store_test_results:
path: ~/reports

test-py35:
Copy link
Author

Choose a reason for hiding this comment

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

Comme python 3.5 est juste en security status et que certaine type hint ne sont pas supporté dedans, mais le sont dans python 3.6 et 3.7, j'ai enlevé le support de 3.5.

docker:
- image: circleci/python:3.5
working_directory: ~/configstore
steps:
- checkout
- restore_cache:
key: configstore-py35-v2
- run:
name: Install CI tools
command: |
python3.5 -m venv venv
venv/bin/pip install tox
- run:
name: Test with Python 3.5
command: venv/bin/tox -e py35 -- --junitxml=~/reports/tox/coverage.xml
- save_cache:
key: configstore-py35-v2
paths:
- venv
- .tox
- store_test_results:
path: ~/reports

test-py27:
docker:
# This image contains Python 3.6 (to install flit) and 2.7 (to run tests)
- image: circleci/python:3.6
working_directory: ~/configstore
steps:
- checkout
- restore_cache:
key: configstore-py27-v2
- run:
name: Install CI tools
command: |
python3.6 -m venv venv
venv/bin/pip install tox
- run:
name: Test with Python 2.7
command: |
venv/bin/tox --sdistonly
venv/bin/tox -e py27 -- --junitxml=~/reports/tox/coverage.xml
Copy link
Contributor

Choose a reason for hiding this comment

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

There’s some config that can be removed from tox.ini too if you want to clean up.

- save_cache:
key: configstore-py27-v2
paths:
- venv
- .tox
- store_test_results:
path: ~/reports

check:
docker:
- image: circleci/python:3.7
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ __pycache__
/.coverage
/.tox/
/venv/
/.pyre/
.pyre_configuration
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this file live at the repo root?

Copy link
Author

Choose a reason for hiding this comment

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

Normally yes.


# Packaging litter
/configstore.egg-info
Expand Down
3 changes: 2 additions & 1 deletion configstore/backends/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from .awsssm import AwsSsmBackend
from .base import Backend
from .docker_secret import DockerSecretBackend
from .dotenv import DotenvBackend
from .env_var import EnvVarBackend

__all__ = [
'EnvVarBackend', 'DotenvBackend', 'DockerSecretBackend', 'AwsSsmBackend',
'Backend', 'EnvVarBackend', 'DotenvBackend', 'DockerSecretBackend', 'AwsSsmBackend',
]
14 changes: 9 additions & 5 deletions configstore/backends/awsssm.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from .base import Backend

from typing import Optional

try:
import boto3
from botocore.exceptions import ClientError
import boto3 # pyre-ignore
from botocore.exceptions import ClientError # pyre-ignore
except ImportError: # pragma: no cover
boto3 = None


class AwsSsmBackend(object):
class AwsSsmBackend(Backend):
"""Backend for AWS System Manager Parameter Store.

You can create an instance with a prefix:
Expand All @@ -23,9 +27,9 @@ def __init__(self, name_prefix=''):

self.name_prefix = name_prefix

def get_setting(self, param):
def get_setting(self, key: str) -> Optional[str]:
client = boto3.client('ssm')
name = self.name_prefix + param
name = self.name_prefix + key
try:
res = client.get_parameter(Name=name, WithDecryption=True)
except ClientError as exc:
Expand Down
10 changes: 10 additions & 0 deletions configstore/backends/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from typing import Optional


class Backend(object):

def __init__(self) -> None:
pass

def get_setting(self, key: str) -> Optional[str]:
raise NotImplementedError
Copy link
Contributor

Choose a reason for hiding this comment

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

I would like this to be a real abstract base class, and also keep the duck typing (not require that classes inherit from the base class). We could chat about this on Monday, the background and reasoning could be quite long for a review message!

(Duck typing / virtual subclasses can be handled by typing: https://mypy.readthedocs.io/en/stable/protocols.html#protocol-types )

8 changes: 6 additions & 2 deletions configstore/backends/docker_secret.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import os
import errno

from .base import Backend

from typing import Optional

SECRETS_PATH = '/run/secrets'


class DockerSecretBackend(object):
class DockerSecretBackend(Backend):

def __init__(self, secrets_path=SECRETS_PATH):
self.secrets_path = secrets_path

def get_setting(self, key):
def get_setting(self, key: str) -> Optional[str]:
path = os.path.join(self.secrets_path, key)

try:
Expand Down
10 changes: 7 additions & 3 deletions configstore/backends/dotenv.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from __future__ import absolute_import

from .base import Backend

from typing import Optional

try:
import dotenv
import dotenv # pyre-ignore
except ImportError: # pragma: no cover
dotenv = None


class DotenvBackend(object):
class DotenvBackend(Backend):
"""Create an instance with a path to the .env file."""

def __init__(self, dotenv_path):
Expand All @@ -19,5 +23,5 @@ def __init__(self, dotenv_path):

self.config = dotenv.parse_dotenv(content)

def get_setting(self, key):
def get_setting(self, key: str) -> Optional[str]:
return self.config.get(key)
10 changes: 7 additions & 3 deletions configstore/backends/env_var.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import os

from .base import Backend

class EnvVarBackend(object):
from typing import Optional

def get_setting(self, config):
return os.environ.get(config)

class EnvVarBackend(Backend):

def get_setting(self, key: str) -> Optional[str]:
Copy link
Contributor

Choose a reason for hiding this comment

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

Great job fixing the param names in get_setting methods!

return os.environ.get(key)
13 changes: 9 additions & 4 deletions configstore/store.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
from typing import Type, Tuple

from .backends import Backend


class SettingNotFoundException(Exception):
pass


_no_default = object()
_no_default: str = '~~!!configstore-no-default!!~~'


class Store(object):

def __init__(self, backends):
def __init__(self, backends: Tuple[Type[Backend]]) -> None:
self._backends = tuple(backends)

def add_backend(self, backend):
def add_backend(self, backend: Type[Backend]):
self._backends += (backend,)

def get_setting(self, key, default=_no_default):
def get_setting(self, key: str, default: str=_no_default) -> str:
for backend in self._backends:
ret = backend.get_setting(key)
if ret is None:
Expand Down
2 changes: 0 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
]
Expand Down
10 changes: 10 additions & 0 deletions tests/test_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import pytest

from configstore.backends.base import Backend


def test_backend_get_setting_is_not_implemented():
backend = Backend()

with pytest.raises(NotImplementedError):
backend.get_setting('test')