shellfoundry 1.2.25__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. shellfoundry/__init__.py +8 -0
  2. shellfoundry/__main__.py +7 -0
  3. shellfoundry/bootstrap.py +169 -0
  4. shellfoundry/commands/__init__.py +0 -0
  5. shellfoundry/commands/config_command.py +87 -0
  6. shellfoundry/commands/delete_command.py +24 -0
  7. shellfoundry/commands/dist_command.py +34 -0
  8. shellfoundry/commands/extend_command.py +182 -0
  9. shellfoundry/commands/generate_command.py +44 -0
  10. shellfoundry/commands/get_templates_command.py +151 -0
  11. shellfoundry/commands/install_command.py +81 -0
  12. shellfoundry/commands/list_command.py +110 -0
  13. shellfoundry/commands/new_command.py +393 -0
  14. shellfoundry/commands/pack_command.py +45 -0
  15. shellfoundry/commands/show_command.py +55 -0
  16. shellfoundry/data/standards.json +44 -0
  17. shellfoundry/data/templates.yml +117 -0
  18. shellfoundry/decorators/__init__.py +1 -0
  19. shellfoundry/decorators/standards.py +20 -0
  20. shellfoundry/decorators/version_check.py +52 -0
  21. shellfoundry/exceptions.py +52 -0
  22. shellfoundry/models/__init__.py +0 -0
  23. shellfoundry/models/install_config.py +82 -0
  24. shellfoundry/models/shell_template.py +22 -0
  25. shellfoundry/models/shellfoundry_settings.py +13 -0
  26. shellfoundry/utilities/__init__.py +114 -0
  27. shellfoundry/utilities/archive_creator.py +35 -0
  28. shellfoundry/utilities/cloudshell_api/__init__.py +1 -0
  29. shellfoundry/utilities/cloudshell_api/client_wrapper.py +71 -0
  30. shellfoundry/utilities/config/__init__.py +0 -0
  31. shellfoundry/utilities/config/config_context.py +44 -0
  32. shellfoundry/utilities/config/config_file_creation.py +32 -0
  33. shellfoundry/utilities/config/config_providers.py +50 -0
  34. shellfoundry/utilities/config/config_record.py +24 -0
  35. shellfoundry/utilities/config_reader.py +154 -0
  36. shellfoundry/utilities/constants.py +37 -0
  37. shellfoundry/utilities/cookiecutter_integration.py +67 -0
  38. shellfoundry/utilities/driver_generator.py +121 -0
  39. shellfoundry/utilities/filters.py +45 -0
  40. shellfoundry/utilities/installer.py +44 -0
  41. shellfoundry/utilities/modifiers/__init__.py +0 -0
  42. shellfoundry/utilities/modifiers/configuration/__init__.py +0 -0
  43. shellfoundry/utilities/modifiers/configuration/aggregated_modifiers.py +26 -0
  44. shellfoundry/utilities/modifiers/configuration/password_modification.py +48 -0
  45. shellfoundry/utilities/modifiers/definition/__init__.py +0 -0
  46. shellfoundry/utilities/modifiers/definition/definition_modification.py +205 -0
  47. shellfoundry/utilities/package_builder.py +152 -0
  48. shellfoundry/utilities/python_dependencies_packager.py +53 -0
  49. shellfoundry/utilities/repository_downloader.py +76 -0
  50. shellfoundry/utilities/shell_config_reader.py +59 -0
  51. shellfoundry/utilities/shell_datamodel_merger.py +44 -0
  52. shellfoundry/utilities/shell_package.py +59 -0
  53. shellfoundry/utilities/shell_package_builder.py +149 -0
  54. shellfoundry/utilities/shell_package_installer.py +231 -0
  55. shellfoundry/utilities/standards/__init__.py +3 -0
  56. shellfoundry/utilities/standards/consts.py +2 -0
  57. shellfoundry/utilities/standards/standards_retriever.py +32 -0
  58. shellfoundry/utilities/standards/standards_versions.py +34 -0
  59. shellfoundry/utilities/temp_dir_context.py +17 -0
  60. shellfoundry/utilities/template_retriever.py +309 -0
  61. shellfoundry/utilities/template_url.py +38 -0
  62. shellfoundry/utilities/template_versions.py +47 -0
  63. shellfoundry/utilities/validations/__init__.py +2 -0
  64. shellfoundry/utilities/validations/shell_generation_validation.py +29 -0
  65. shellfoundry/utilities/validations/shell_name_validations.py +10 -0
  66. shellfoundry/utilities/version_utilities.py +19 -0
  67. shellfoundry-1.2.25.dist-info/LICENSE +201 -0
  68. shellfoundry-1.2.25.dist-info/METADATA +417 -0
  69. shellfoundry-1.2.25.dist-info/RECORD +72 -0
  70. shellfoundry-1.2.25.dist-info/WHEEL +5 -0
  71. shellfoundry-1.2.25.dist-info/entry_points.txt +2 -0
  72. shellfoundry-1.2.25.dist-info/top_level.txt +1 -0
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ from functools import update_wrapper
4
+
5
+ import click
6
+
7
+ from shellfoundry.exceptions import ShellFoundryVersionException
8
+ from shellfoundry.utilities import is_index_version_greater_than_current
9
+ from shellfoundry.utilities.config_reader import CloudShellConfigReader, Configuration
10
+
11
+
12
+ class shellfoundry_version_check(object):
13
+ def __init__(self, abort_if_major=False):
14
+ self.abort_if_major = abort_if_major
15
+ self.cloudshell_config_reader = Configuration(CloudShellConfigReader())
16
+
17
+ def __call__(self, f):
18
+ def decorator(*args, **kwargs):
19
+ output = ""
20
+ if self.cloudshell_config_reader.read().online_mode.lower() == "true":
21
+ try:
22
+ (
23
+ is_greater_version,
24
+ is_major_release,
25
+ ) = is_index_version_greater_than_current()
26
+ except ShellFoundryVersionException as err:
27
+ click.secho(str(err), fg="red")
28
+ raise click.Abort()
29
+ if is_greater_version:
30
+ if is_major_release:
31
+ output = (
32
+ "This version of shellfoundry is not supported anymore, "
33
+ "please upgrade by running: pip install shellfoundry --upgrade" # noqa: E501
34
+ )
35
+
36
+ if self.abort_if_major:
37
+ click.secho(output, fg="yellow")
38
+ print("") # noqa: T001
39
+ raise click.Abort()
40
+ else:
41
+ output = (
42
+ "There is a new version of shellfoundry available, "
43
+ "please upgrade by running: pip install shellfoundry --upgrade" # noqa: E501
44
+ )
45
+
46
+ f(**kwargs)
47
+
48
+ if output:
49
+ print("") # noqa: T001
50
+ click.secho(output, fg="yellow")
51
+
52
+ return update_wrapper(decorator, f)
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import click
5
+
6
+
7
+ class ShellFoundryBaseException(Exception):
8
+ def __init__(self, message):
9
+ super(ShellFoundryBaseException, self).__init__(message)
10
+
11
+
12
+ class ShellYmlMissingException(ShellFoundryBaseException):
13
+ def __init__(self, message):
14
+ super(ShellYmlMissingException, self).__init__(message)
15
+
16
+
17
+ class WrongShellYmlException(ShellFoundryBaseException):
18
+ def __init__(self, message):
19
+ super(WrongShellYmlException, self).__init__(message)
20
+
21
+
22
+ class NoVersionsHaveBeenFoundException(ShellFoundryBaseException):
23
+ def __init__(self, message):
24
+ super(NoVersionsHaveBeenFoundException, self).__init__(message)
25
+
26
+
27
+ class VersionRequestException(ShellFoundryBaseException):
28
+ def __init__(self, message):
29
+ super(VersionRequestException, self).__init__(message)
30
+
31
+
32
+ class PlatformNameIsEmptyException(ShellFoundryBaseException):
33
+ def __init__(self, message="Machine name is empty"):
34
+ super(PlatformNameIsEmptyException, self).__init__(message)
35
+
36
+
37
+ class FatalError(click.ClickException):
38
+ def __init__(self, message):
39
+ super(FatalError, self).__init__(message)
40
+
41
+ def show(self, file=None):
42
+ click.secho("Error: {}".format(self.format_message()), err=True, fg="red")
43
+
44
+
45
+ class YmlFieldMissingException(Exception):
46
+ def __init__(self, message):
47
+ super(YmlFieldMissingException, self).__init__(message)
48
+
49
+
50
+ class ShellFoundryVersionException(Exception):
51
+ def __init__(self, message):
52
+ super(ShellFoundryVersionException, self).__init__(message)
File without changes
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/python
2
+
3
+ from shellfoundry.utilities.modifiers.configuration.password_modification import (
4
+ PasswordModification,
5
+ )
6
+
7
+ DEFAULT_HOST = "localhost"
8
+ DEFAULT_PORT = 9000
9
+ DEFAULT_USERNAME = "admin"
10
+ DEFAULT_PASSWORD = "admin"
11
+ DEFAULT_DOMAIN = "Global"
12
+ DEFAULT_AUTHOR = "Anonymous"
13
+ DEFAULT_ONLINE_MODE = "True"
14
+ DEFAULT_TEMPLATE_LOCATION = "Empty"
15
+ DEFAULT_GITHUB_LOGIN = ""
16
+ DEFAULT_GITHUB_PASSWORD = "gh_pass"
17
+
18
+
19
+ class InstallConfig(object):
20
+ def __init__(
21
+ self,
22
+ host,
23
+ port,
24
+ username,
25
+ password,
26
+ domain,
27
+ author,
28
+ online_mode,
29
+ template_location,
30
+ github_login,
31
+ github_password,
32
+ ):
33
+ self.domain = domain
34
+ self.password = self._decode_password(password)
35
+ self.username = username
36
+ self.port = port
37
+ self.host = host
38
+ self.author = author
39
+ self.online_mode = online_mode
40
+ self.template_location = template_location
41
+ self.github_login = github_login
42
+ self.github_password = self._decode_password(github_password)
43
+
44
+ def __eq__(self, other):
45
+ """Comparison.
46
+
47
+ :param other: An instance of InstallConfig to compare
48
+ :type other InstallConfig
49
+ :return: True of same value, False otherwise
50
+ :rtype bool
51
+ """
52
+ return (
53
+ self.domain == other.domain
54
+ and self.host == other.host
55
+ and self.password == other.password
56
+ and self.port == other.port
57
+ and self.username == other.username
58
+ and self.author == other.author
59
+ and self.online_mode == other.online_mode
60
+ and self.template_location == other.template_location
61
+ and self.github_login == other.github_login
62
+ and self.github_password == other.github_password
63
+ )
64
+
65
+ @staticmethod
66
+ def get_default():
67
+ return InstallConfig(
68
+ DEFAULT_HOST,
69
+ DEFAULT_PORT,
70
+ DEFAULT_USERNAME,
71
+ DEFAULT_PASSWORD,
72
+ DEFAULT_DOMAIN,
73
+ DEFAULT_AUTHOR,
74
+ DEFAULT_ONLINE_MODE,
75
+ DEFAULT_TEMPLATE_LOCATION,
76
+ DEFAULT_GITHUB_LOGIN,
77
+ DEFAULT_GITHUB_PASSWORD,
78
+ )
79
+
80
+ def _decode_password(self, password):
81
+ pass_mod = PasswordModification()
82
+ return pass_mod.normalize(password)
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ class ShellTemplate(object):
6
+ def __init__(
7
+ self,
8
+ name,
9
+ description,
10
+ repository,
11
+ min_cs_ver,
12
+ standard=None,
13
+ standard_version=None,
14
+ params=None,
15
+ ):
16
+ self.name = name
17
+ self.description = description
18
+ self.repository = repository
19
+ self.min_cs_ver = min_cs_ver
20
+ self.standard = standard
21
+ self.standard_version = standard_version or {}
22
+ self.params = params or {}
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ DEFAULT_DEFAULT_VIEW = "gen2"
5
+
6
+
7
+ class ShellFoundrySettings(object):
8
+ def __init__(self, defaultview):
9
+ self.defaultview = defaultview
10
+
11
+ @staticmethod
12
+ def get_default():
13
+ return ShellFoundrySettings(DEFAULT_DEFAULT_VIEW)
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import json
5
+ import ssl
6
+
7
+ import pkg_resources
8
+
9
+ try:
10
+ # Python 2.x version
11
+ from xmlrpclib import ProtocolError, ServerProxy
12
+ except ImportError:
13
+ # Python 3.x version
14
+ from xmlrpc.client import ProtocolError, ServerProxy
15
+
16
+ try:
17
+ from urllib import urlopen
18
+ except ImportError:
19
+ from urllib.request import urlopen
20
+
21
+ try:
22
+ from urllib.error import HTTPError, URLError
23
+ except ImportError:
24
+ from urllib2 import HTTPError, URLError
25
+
26
+ from distutils.version import StrictVersion
27
+
28
+ from shellfoundry import PACKAGE_NAME
29
+ from shellfoundry.exceptions import ShellFoundryVersionException
30
+
31
+ GEN_ONE = "gen1"
32
+ GEN_TWO = "gen2"
33
+ LAYER_ONE = "layer1"
34
+ NO_FILTER = "all"
35
+ GEN_ONE_FILTER = "gen1"
36
+ GEN_TWO_FILTER = "gen2"
37
+ LAYER_ONE_FILTER = "layer-1"
38
+ SEPARATOR = "/"
39
+
40
+
41
+ class Index(object):
42
+ def __init__(self, url):
43
+ self.url = url
44
+
45
+
46
+ PyPI = Index("https://pypi.python.org/pypi/")
47
+
48
+
49
+ def get_installed_version(package_name):
50
+ return pkg_resources.get_distribution(package_name).version
51
+
52
+
53
+ def is_index_version_greater_than_current():
54
+ MAJOR_INDEX = 0
55
+
56
+ installed, index = (
57
+ StrictVersion(get_installed_version(PACKAGE_NAME)),
58
+ StrictVersion(max_version_from_index()),
59
+ )
60
+ is_major_release = False
61
+
62
+ is_greater_version = index > installed
63
+ if (
64
+ is_greater_version
65
+ and get_index_of_biggest_component_between_two_versions(
66
+ index.version, installed.version
67
+ )
68
+ == MAJOR_INDEX
69
+ ):
70
+ is_major_release = True
71
+
72
+ return is_greater_version, is_major_release
73
+
74
+
75
+ def max_version_from_index():
76
+ try:
77
+ ctx = ssl.SSLContext(protocol=ssl.PROTOCOL_SSLv23)
78
+ proxy = ServerProxy(PyPI.url, context=ctx)
79
+ releases = proxy.package_releases(PACKAGE_NAME)
80
+ max_version = max(releases)
81
+ return max_version
82
+ except ProtocolError as err:
83
+ raise ShellFoundryVersionException(
84
+ "Cannot retrieve latest shellfoundry version, "
85
+ "are you offline? Error: {}".format(err)
86
+ )
87
+ except Exception as err:
88
+ raise ShellFoundryVersionException(
89
+ "Unexpected error during shellfoundry version check. "
90
+ "Error: {}.".format(err)
91
+ )
92
+
93
+
94
+ def latest_released_version():
95
+ url = "https://pypi.org/pypi/{package_name}/json"
96
+ try:
97
+ package_info = json.load(urlopen(url.format(package_name=PACKAGE_NAME)))
98
+ return package_info["info"]["version"]
99
+ except (HTTPError, URLError) as err:
100
+ raise ShellFoundryVersionException(
101
+ "Cannot retrieve latest shellfoundry version, "
102
+ "are you offline? Error: {}".format(err)
103
+ )
104
+ except Exception as err:
105
+ raise ShellFoundryVersionException(
106
+ "Unexpected error during shellfoundry version check. "
107
+ "Error: {}.".format(err)
108
+ )
109
+
110
+
111
+ def get_index_of_biggest_component_between_two_versions(v1, v2):
112
+ for i in range(0, len(v1)):
113
+ if v1[i] > v2[i]:
114
+ return i
@@ -0,0 +1,35 @@
1
+ import os
2
+ import zipfile
3
+
4
+
5
+ class ArchiveCreator(object):
6
+ @staticmethod
7
+ def make_archive(output_filename, archive_format, source_dir):
8
+ """Creates archive in specified format recursively of source_dir.
9
+
10
+ Replaces shutil.make_archive in order to be able to test with pyfakefs
11
+ :param output_filename: Output archive file name.
12
+ If directory does not exist, it will be created
13
+ :param archive_format: Archive format to be used.
14
+ Currently only zip is supported
15
+ :param source_dir: Directory to scan for archiving
16
+ :return:
17
+ """
18
+ if os.path.exists(source_dir):
19
+ if os.path.splitext(output_filename)[1] == "":
20
+ output_filename += ".zip"
21
+ output_dir = os.path.dirname(output_filename)
22
+ if output_dir and not os.path.exists(output_dir):
23
+ os.makedirs(output_dir)
24
+ relroot = source_dir
25
+ with zipfile.ZipFile(output_filename, "w", zipfile.ZIP_DEFLATED) as zip_f:
26
+ for root, dirs, files in os.walk(source_dir):
27
+ # add directory (needed for empty dirs)
28
+ zip_f.write(root, os.path.relpath(root, relroot))
29
+ for file in files:
30
+ filename = os.path.join(root, file)
31
+ if os.path.isfile(filename): # regular files only
32
+ arcname = os.path.join(os.path.relpath(root, relroot), file)
33
+ zip_f.write(filename, arcname)
34
+
35
+ return output_filename
@@ -0,0 +1 @@
1
+ from .client_wrapper import CloudShellClient, create_cloudshell_client # noqa: F401
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/python
2
+
3
+ from cloudshell.rest.api import PackagingRestApiClient
4
+
5
+ from shellfoundry.exceptions import FatalError
6
+ from shellfoundry.utilities.config_reader import CloudShellConfigReader, Configuration
7
+
8
+ try:
9
+ from urllib.error import HTTPError
10
+ except Exception:
11
+ from urllib2 import HTTPError
12
+
13
+
14
+ def create_cloudshell_client(retries=1):
15
+ try:
16
+ cs_client = CloudShellClient().create_client(retries=retries)
17
+ except FatalError:
18
+ raise
19
+ return cs_client
20
+
21
+
22
+ class CloudShellClient(object):
23
+ ConnectionFailureMessage = "Connection to CloudShell Server failed. Please make sure it is up and running properly." # noqa: E501
24
+
25
+ def __init__(self, cs_config=None):
26
+ """Creates cloudshell client.
27
+
28
+ :type cs_config shellfoundry.models.install_config.InstallConfig
29
+ """
30
+ self._cs_config = cs_config or Configuration(CloudShellConfigReader()).read()
31
+
32
+ def create_client(self, **kwargs):
33
+ retries = kwargs.get("retries", 1)
34
+ if retries == 0:
35
+ raise FatalError(self.ConnectionFailureMessage)
36
+ try:
37
+ return self._create_client()
38
+ except FatalError as e:
39
+ retry = retries - 1
40
+ if retry == 0:
41
+ raise e
42
+ return self.create_client(retries=retry)
43
+
44
+ def _create_client(self):
45
+ try:
46
+ try:
47
+ client = PackagingRestApiClient.login(
48
+ host=self._cs_config.host,
49
+ port=self._cs_config.port,
50
+ username=self._cs_config.username,
51
+ password=self._cs_config.password,
52
+ domain=self._cs_config.domain,
53
+ )
54
+ return client
55
+ except AttributeError:
56
+ client = PackagingRestApiClient(
57
+ ip=self._cs_config.host,
58
+ port=self._cs_config.port,
59
+ username=self._cs_config.username,
60
+ password=self._cs_config.password,
61
+ domain=self._cs_config.domain,
62
+ )
63
+ return client
64
+ except (HTTPError, Exception) as e:
65
+ if hasattr(e, "code") and e.code == 401:
66
+ if hasattr(e, "msg") and e.msg:
67
+ msg = e.msg
68
+ else:
69
+ msg = "Please verify the credentials in the config"
70
+ raise FatalError("Login to CloudShell failed. {}".format(msg))
71
+ raise FatalError(self.ConnectionFailureMessage)
File without changes
@@ -0,0 +1,44 @@
1
+ # !/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from io import open
5
+
6
+ import yaml
7
+
8
+ from shellfoundry.utilities.config_reader import INSTALL
9
+ from shellfoundry.utilities.modifiers.configuration.aggregated_modifiers import (
10
+ AggregatedModifiers,
11
+ )
12
+
13
+
14
+ class ConfigContext(object):
15
+ def __init__(self, config_file_path):
16
+ self.config_file_path = config_file_path
17
+ self.modifier = AggregatedModifiers()
18
+
19
+ def try_save(self, key, value):
20
+ try:
21
+ with open(self.config_file_path, mode="r+", encoding="utf8") as stream:
22
+ data = yaml.safe_load(stream) or {INSTALL: {}}
23
+ data[INSTALL][key] = self._modify(key, value)
24
+ stream.seek(0)
25
+ stream.truncate()
26
+ yaml.safe_dump(data, stream=stream, default_flow_style=False)
27
+ return True
28
+ except Exception:
29
+ return False
30
+
31
+ def try_delete(self, key):
32
+ try:
33
+ with open(self.config_file_path, mode="r+", encoding="utf8") as stream:
34
+ data = yaml.safe_load(stream)
35
+ del data[INSTALL][key] # handle cases that INSTALL does not exists
36
+ stream.seek(0)
37
+ stream.truncate()
38
+ yaml.safe_dump(data, stream=stream, default_flow_style=False)
39
+ return True
40
+ except Exception:
41
+ return False
42
+
43
+ def _modify(self, key, value):
44
+ return self.modifier.modify(key, value)
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ import errno
4
+ import os
5
+ from io import open
6
+
7
+ import click
8
+
9
+
10
+ class ConfigFileCreation(object):
11
+ def create(self, config_file_path):
12
+ if os.path.exists(config_file_path):
13
+ return
14
+ if not os.path.exists(os.path.dirname(config_file_path)):
15
+ try:
16
+ dirname = os.path.dirname(config_file_path)
17
+ os.makedirs(dirname)
18
+ except OSError as exc:
19
+ if exc.errno != errno.EEXIST:
20
+ click.echo("Failed to create config file")
21
+ click.echo(str(exc))
22
+ raise
23
+ try:
24
+ click.echo("Creating config file...")
25
+ open(config_file_path, mode="w", encoding="utf8").close()
26
+ except Exception:
27
+ if not os.path.exists(config_file_path):
28
+ click.echo("Failed to create config file")
29
+ import sys
30
+
31
+ click.echo(str(sys.exc_info()[1]))
32
+ raise
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import os
5
+
6
+ import click
7
+
8
+ GLOBAL_CONFIG_NAME = "global_config.yml"
9
+ LOCAL_CONFIG_NAME = "cloudshell_config.yml"
10
+
11
+
12
+ class LocalConfigProvider(object):
13
+ def get_config_path(self):
14
+ path = os.path.join(os.getcwd(), LOCAL_CONFIG_NAME)
15
+ if os.path.exists(path):
16
+ click.echo("Using local configuration...")
17
+ return path
18
+
19
+
20
+ class GlobalConfigProvider(object):
21
+ QUALI = "Quali"
22
+ PRODUCT = "shellfoundry"
23
+
24
+ def get_config_path(self):
25
+ sf_name = os.path.join(GlobalConfigProvider.QUALI, GlobalConfigProvider.PRODUCT)
26
+ app_dir_path = click.get_app_dir(sf_name)
27
+ return os.path.join(app_dir_path, GLOBAL_CONFIG_NAME)
28
+
29
+
30
+ class ConfigProvider(object):
31
+ def __init__(self, *args):
32
+ self.config_providers = args
33
+
34
+ def get_config_path(self):
35
+ for provider in self.config_providers:
36
+ config_path = provider.get_config_path()
37
+ if os.path.exists(config_path):
38
+ return config_path
39
+
40
+
41
+ class DefaultConfigProvider(ConfigProvider):
42
+ def __init__(self):
43
+ ConfigProvider.__init__(self, (GlobalConfigProvider())) # The order do matters
44
+ self.default_provider = LocalConfigProvider()
45
+
46
+ def get_config_path(self):
47
+ config_path = self.default_provider.get_config_path()
48
+ if not os.path.exists(config_path):
49
+ config_path = ConfigProvider.get_config_path(self)
50
+ return config_path # or self.fallback_provider.get_config_path()
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import click
5
+
6
+
7
+ class ConfigRecord(object):
8
+ def __init__(self, key, value=None):
9
+ self.key = key
10
+ self.value = value
11
+
12
+ def save(self, config_context):
13
+ if config_context.try_save(self.key, self.value):
14
+ click.echo("{}: {} was saved successfully".format(self.key, self.value))
15
+ else:
16
+ click.echo("Failed to save key value")
17
+
18
+ def delete(self, config_context):
19
+ if config_context.try_delete(self.key):
20
+ click.echo("{} was deleted successfully".format(self.key))
21
+ else:
22
+ # add support for typed exceptions
23
+ # in order to have the ability to differentiate between failures
24
+ click.echo("Failed to delete key")