environment.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. from __future__ import absolute_import
  2. from __future__ import unicode_literals
  3. import codecs
  4. import contextlib
  5. import logging
  6. import os
  7. import string
  8. import six
  9. from ..const import IS_WINDOWS_PLATFORM
  10. from .errors import ConfigurationError
  11. from .errors import EnvFileNotFound
  12. log = logging.getLogger(__name__)
  13. whitespace = set(string.whitespace)
  14. def split_env(env):
  15. if isinstance(env, six.binary_type):
  16. env = env.decode('utf-8', 'replace')
  17. key = value = None
  18. if '=' in env:
  19. key, value = env.split('=', 1)
  20. else:
  21. key = env
  22. for k in key:
  23. if k in whitespace:
  24. raise ConfigurationError(
  25. "environment variable name '%s' may not contains white spaces." % key
  26. )
  27. return key, value
  28. def env_vars_from_file(filename):
  29. """
  30. Read in a line delimited file of environment variables.
  31. """
  32. if not os.path.exists(filename):
  33. raise EnvFileNotFound("Couldn't find env file: %s" % filename)
  34. elif not os.path.isfile(filename):
  35. raise EnvFileNotFound("%s is not a file." % (filename))
  36. env = {}
  37. with contextlib.closing(codecs.open(filename, 'r', 'utf-8-sig')) as fileobj:
  38. for line in fileobj:
  39. line = line.strip()
  40. if line and not line.startswith('#'):
  41. try:
  42. k, v = split_env(line)
  43. env[k] = v
  44. except ConfigurationError as e:
  45. raise ConfigurationError('In file {}: {}'.format(filename, e.msg))
  46. return env
  47. class Environment(dict):
  48. def __init__(self, *args, **kwargs):
  49. super(Environment, self).__init__(*args, **kwargs)
  50. self.missing_keys = []
  51. @classmethod
  52. def from_env_file(cls, base_dir):
  53. def _initialize():
  54. result = cls()
  55. if base_dir is None:
  56. return result
  57. env_file_path = os.path.join(base_dir, '.env')
  58. try:
  59. return cls(env_vars_from_file(env_file_path))
  60. except EnvFileNotFound:
  61. pass
  62. return result
  63. instance = _initialize()
  64. instance.update(os.environ)
  65. return instance
  66. @classmethod
  67. def from_command_line(cls, parsed_env_opts):
  68. result = cls()
  69. for k, v in parsed_env_opts.items():
  70. # Values from the command line take priority, unless they're unset
  71. # in which case they take the value from the system's environment
  72. if v is None and k in os.environ:
  73. result[k] = os.environ[k]
  74. else:
  75. result[k] = v
  76. return result
  77. def __getitem__(self, key):
  78. try:
  79. return super(Environment, self).__getitem__(key)
  80. except KeyError:
  81. if IS_WINDOWS_PLATFORM:
  82. try:
  83. return super(Environment, self).__getitem__(key.upper())
  84. except KeyError:
  85. pass
  86. if key not in self.missing_keys:
  87. log.warn(
  88. "The {} variable is not set. Defaulting to a blank string."
  89. .format(key)
  90. )
  91. self.missing_keys.append(key)
  92. return ""
  93. def __contains__(self, key):
  94. result = super(Environment, self).__contains__(key)
  95. if IS_WINDOWS_PLATFORM:
  96. return (
  97. result or super(Environment, self).__contains__(key.upper())
  98. )
  99. return result
  100. def get(self, key, *args, **kwargs):
  101. if IS_WINDOWS_PLATFORM:
  102. return super(Environment, self).get(
  103. key,
  104. super(Environment, self).get(key.upper(), *args, **kwargs)
  105. )
  106. return super(Environment, self).get(key, *args, **kwargs)
  107. def get_boolean(self, key):
  108. # Convert a value to a boolean using "common sense" rules.
  109. # Unset, empty, "0" and "false" (i-case) yield False.
  110. # All other values yield True.
  111. value = self.get(key)
  112. if not value:
  113. return False
  114. if value.lower() in ['0', 'false']:
  115. return False
  116. return True