From b6eeecf5438a68eddff602c26f76271d66dc65d9 Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Thu, 4 Oct 2018 19:37:16 -0400 Subject: [PATCH 01/11] Ignore .mypy_cache/ --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9617388..45bdc4a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ __pycache__ .pytest_cache/ .coverage .tox/ +.mypy_cache/ # Packaging litter configstore.egg-info From 7041518491be4a50ee6c8ce6487e198020b300ad Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Mon, 15 Oct 2018 22:59:39 -0400 Subject: [PATCH 02/11] Add basic typing stuff --- configstore/store.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/configstore/store.py b/configstore/store.py index 8e3e8d7..36d05ae 100644 --- a/configstore/store.py +++ b/configstore/store.py @@ -1,3 +1,15 @@ +from typing import TypeVar, Union, Tuple, Iterable + +from .backends.env_var import EnvVarBackend +from .backends.dotenv import DotenvBackend +from .backends.awsssm import AwsSsmBackend +from .backends.docker_secret import DockerSecretBackend + + +Backends = TypeVar('Backends', EnvVarBackend, DotenvBackend, + AwsSsmBackend, DockerSecretBackend) + + class SettingNotFoundException(Exception): pass @@ -7,13 +19,13 @@ class SettingNotFoundException(Exception): class Store(object): - def __init__(self, backends): + def __init__(self, backends) -> None: self._backends = tuple(backends) - def add_backend(self, backend): + def add_backend(self, backend: Backends): self._backends += (backend,) - def get_setting(self, key, default=_no_default): + def get_setting(self, key: str, default=_no_default) -> str: for backend in self._backends: ret = backend.get_setting(key) if ret is None: From 3988b89dc8709b49c36a79fd39eba052f1ea66a6 Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Mon, 15 Oct 2018 23:01:34 -0400 Subject: [PATCH 03/11] Remove unsued imports --- configstore/store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configstore/store.py b/configstore/store.py index 36d05ae..6d48d10 100644 --- a/configstore/store.py +++ b/configstore/store.py @@ -1,4 +1,4 @@ -from typing import TypeVar, Union, Tuple, Iterable +from typing import TypeVar from .backends.env_var import EnvVarBackend from .backends.dotenv import DotenvBackend From 6307d6472d267330e9ea5b4031dde71d4b86204f Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Fri, 19 Oct 2018 21:20:18 -0400 Subject: [PATCH 04/11] Use a `_no_default` str variable instead of object This way, it's really easier to make typing work without ugly hack or long Union typing --- configstore/store.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configstore/store.py b/configstore/store.py index 6d48d10..8504ffb 100644 --- a/configstore/store.py +++ b/configstore/store.py @@ -14,7 +14,7 @@ class SettingNotFoundException(Exception): pass -_no_default = object() +_no_default: str = '~~!!configstore-no-default!!~~' class Store(object): @@ -25,7 +25,7 @@ def __init__(self, backends) -> None: def add_backend(self, backend: Backends): self._backends += (backend,) - def get_setting(self, key: str, default=_no_default) -> str: + 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: From 06efe004ca4aa65afecf4c273f641478a71be5d9 Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Fri, 19 Oct 2018 21:22:43 -0400 Subject: [PATCH 05/11] Remove python 2.7 tests since typing isn't support There is support for typing in python 2.7 but only in comments and we don't want this --- .circleci/config.yml | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 112c5b9..703610a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -87,33 +87,6 @@ jobs: - 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 - - save_cache: - key: configstore-py27-v2 - paths: - - venv - - .tox - - store_test_results: - path: ~/reports - check: docker: - image: circleci/python:3.7 From b92c96a5cc964dd37dd5f04c73fc082236b0cf16 Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Fri, 19 Oct 2018 21:25:42 -0400 Subject: [PATCH 06/11] Remove python 2.7 support from flit packaging config file --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 47f3dfa..e3ac2f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +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", From 441cb856d070e25fca2e1501e9bdf60260ad9417 Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Fri, 19 Oct 2018 21:51:04 -0400 Subject: [PATCH 07/11] Also drop the py35 support since not all type hint are supported --- .circleci/config.yml | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 703610a..895cdb4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,10 +4,8 @@ workflows: version: 2 configstore: jobs: - - test-py35 - test-py36 - test-py37 - - test-py27 - check jobs: @@ -63,30 +61,6 @@ jobs: - store_test_results: path: ~/reports - test-py35: - 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 - check: docker: - image: circleci/python:3.7 From 4b9007c69da1ef75aca3bc394d7b8bc8766d978c Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Fri, 19 Oct 2018 21:55:52 -0400 Subject: [PATCH 08/11] Tottaly remove python 3.5 --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e3ac2f0..526782e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,6 @@ classifiers = [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", ] From 123f2b10e755a6d717083f74bc11cf586e179faf Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Fri, 19 Oct 2018 22:12:14 -0400 Subject: [PATCH 09/11] All backends inherint from the Bcakend base class --- configstore/backends/__init__.py | 3 ++- configstore/backends/awsssm.py | 3 ++- configstore/backends/base.py | 4 ++++ configstore/backends/docker_secret.py | 4 +++- configstore/backends/dotenv.py | 4 +++- configstore/backends/env_var.py | 4 +++- configstore/store.py | 13 +++---------- 7 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 configstore/backends/base.py diff --git a/configstore/backends/__init__.py b/configstore/backends/__init__.py index e2b8deb..ecf4861 100644 --- a/configstore/backends/__init__.py +++ b/configstore/backends/__init__.py @@ -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', ] diff --git a/configstore/backends/awsssm.py b/configstore/backends/awsssm.py index 1efdb39..6230096 100644 --- a/configstore/backends/awsssm.py +++ b/configstore/backends/awsssm.py @@ -1,3 +1,4 @@ +from .base import Backend try: import boto3 from botocore.exceptions import ClientError @@ -5,7 +6,7 @@ boto3 = None -class AwsSsmBackend(object): +class AwsSsmBackend(Backend): """Backend for AWS System Manager Parameter Store. You can create an instance with a prefix: diff --git a/configstore/backends/base.py b/configstore/backends/base.py new file mode 100644 index 0000000..e5484df --- /dev/null +++ b/configstore/backends/base.py @@ -0,0 +1,4 @@ +class Backend(object): + + def get_setting(self, config): + raise NotImplementedError diff --git a/configstore/backends/docker_secret.py b/configstore/backends/docker_secret.py index 2506d3c..f29d5c6 100644 --- a/configstore/backends/docker_secret.py +++ b/configstore/backends/docker_secret.py @@ -1,10 +1,12 @@ import os import errno +from .base import Backend + SECRETS_PATH = '/run/secrets' -class DockerSecretBackend(object): +class DockerSecretBackend(Backend): def __init__(self, secrets_path=SECRETS_PATH): self.secrets_path = secrets_path diff --git a/configstore/backends/dotenv.py b/configstore/backends/dotenv.py index 1de9507..e721f4e 100644 --- a/configstore/backends/dotenv.py +++ b/configstore/backends/dotenv.py @@ -1,12 +1,14 @@ from __future__ import absolute_import +from .base import Backend + try: import dotenv 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): diff --git a/configstore/backends/env_var.py b/configstore/backends/env_var.py index 9d7f207..913baeb 100644 --- a/configstore/backends/env_var.py +++ b/configstore/backends/env_var.py @@ -1,7 +1,9 @@ import os +from .base import Backend -class EnvVarBackend(object): + +class EnvVarBackend(Backend): def get_setting(self, config): return os.environ.get(config) diff --git a/configstore/store.py b/configstore/store.py index 8504ffb..1e338b7 100644 --- a/configstore/store.py +++ b/configstore/store.py @@ -1,13 +1,6 @@ -from typing import TypeVar +from typing import Type -from .backends.env_var import EnvVarBackend -from .backends.dotenv import DotenvBackend -from .backends.awsssm import AwsSsmBackend -from .backends.docker_secret import DockerSecretBackend - - -Backends = TypeVar('Backends', EnvVarBackend, DotenvBackend, - AwsSsmBackend, DockerSecretBackend) +from .backends import Backend class SettingNotFoundException(Exception): @@ -22,7 +15,7 @@ class Store(object): def __init__(self, backends) -> None: self._backends = tuple(backends) - def add_backend(self, backend: Backends): + def add_backend(self, backend: Type[Backend]): self._backends += (backend,) def get_setting(self, key: str, default: str=_no_default) -> str: From 252f455b9c6eb6ec99453a34cf2379bf5b3ad09e Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Fri, 19 Oct 2018 22:57:34 -0400 Subject: [PATCH 10/11] Fix backends typing --- .gitignore | 3 ++- configstore/backends/awsssm.py | 11 +++++++---- configstore/backends/base.py | 8 +++++++- configstore/backends/docker_secret.py | 4 +++- configstore/backends/dotenv.py | 6 ++++-- configstore/backends/env_var.py | 6 ++++-- configstore/store.py | 4 ++-- 7 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 414a60e..f7c7626 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,12 @@ __pycache__ # Test artifacts -/.mypy_cache/ /.pytest_cache/ /.coverage /.tox/ /venv/ +/.pyre/ +.pyre_configuration # Packaging litter /configstore.egg-info diff --git a/configstore/backends/awsssm.py b/configstore/backends/awsssm.py index 6230096..c00ed5f 100644 --- a/configstore/backends/awsssm.py +++ b/configstore/backends/awsssm.py @@ -1,7 +1,10 @@ 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 @@ -24,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: diff --git a/configstore/backends/base.py b/configstore/backends/base.py index e5484df..fba808e 100644 --- a/configstore/backends/base.py +++ b/configstore/backends/base.py @@ -1,4 +1,10 @@ +from typing import Optional + + class Backend(object): - def get_setting(self, config): + def __init__(self) -> None: + pass + + def get_setting(self, key: str) -> Optional[str]: raise NotImplementedError diff --git a/configstore/backends/docker_secret.py b/configstore/backends/docker_secret.py index f29d5c6..602d5b3 100644 --- a/configstore/backends/docker_secret.py +++ b/configstore/backends/docker_secret.py @@ -3,6 +3,8 @@ from .base import Backend +from typing import Optional + SECRETS_PATH = '/run/secrets' @@ -11,7 +13,7 @@ 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: diff --git a/configstore/backends/dotenv.py b/configstore/backends/dotenv.py index e721f4e..834aa2c 100644 --- a/configstore/backends/dotenv.py +++ b/configstore/backends/dotenv.py @@ -2,8 +2,10 @@ from .base import Backend +from typing import Optional + try: - import dotenv + import dotenv # pyre-ignore except ImportError: # pragma: no cover dotenv = None @@ -21,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) diff --git a/configstore/backends/env_var.py b/configstore/backends/env_var.py index 913baeb..1ad4717 100644 --- a/configstore/backends/env_var.py +++ b/configstore/backends/env_var.py @@ -2,8 +2,10 @@ from .base import Backend +from typing import Optional + class EnvVarBackend(Backend): - def get_setting(self, config): - return os.environ.get(config) + def get_setting(self, key: str) -> Optional[str]: + return os.environ.get(key) diff --git a/configstore/store.py b/configstore/store.py index 1e338b7..00f6907 100644 --- a/configstore/store.py +++ b/configstore/store.py @@ -1,4 +1,4 @@ -from typing import Type +from typing import Type, Tuple from .backends import Backend @@ -12,7 +12,7 @@ class SettingNotFoundException(Exception): class Store(object): - def __init__(self, backends) -> None: + def __init__(self, backends: Tuple[Type[Backend]]) -> None: self._backends = tuple(backends) def add_backend(self, backend: Type[Backend]): From 70197455655ba5f43b0584a721b4c902dbee4bc4 Mon Sep 17 00:00:00 2001 From: Justin Lavoie Date: Fri, 19 Oct 2018 23:29:50 -0400 Subject: [PATCH 11/11] Add Backend abstract class test --- tests/test_backend.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/test_backend.py diff --git a/tests/test_backend.py b/tests/test_backend.py new file mode 100644 index 0000000..ca3ac48 --- /dev/null +++ b/tests/test_backend.py @@ -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')