envstack 1.0.0__tar.gz → 1.0.2__tar.gz

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 (32) hide show
  1. {envstack-1.0.0/lib/envstack.egg-info → envstack-1.0.2}/PKG-INFO +1 -1
  2. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/__init__.py +1 -1
  3. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/encrypt.py +34 -7
  4. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/envshell.py +1 -1
  5. {envstack-1.0.0 → envstack-1.0.2/lib/envstack.egg-info}/PKG-INFO +1 -1
  6. {envstack-1.0.0 → envstack-1.0.2}/pyproject.toml +1 -1
  7. {envstack-1.0.0 → envstack-1.0.2}/LICENSE +0 -0
  8. {envstack-1.0.0 → envstack-1.0.2}/README.md +0 -0
  9. {envstack-1.0.0 → envstack-1.0.2}/dist.json +0 -0
  10. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/cli.py +0 -0
  11. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/config.py +0 -0
  12. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/env.py +0 -0
  13. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/exceptions.py +0 -0
  14. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/logger.py +0 -0
  15. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/node.py +0 -0
  16. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/path.py +0 -0
  17. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/util.py +0 -0
  18. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack/wrapper.py +0 -0
  19. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack.egg-info/SOURCES.txt +0 -0
  20. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack.egg-info/dependency_links.txt +0 -0
  21. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack.egg-info/entry_points.txt +0 -0
  22. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack.egg-info/not-zip-safe +0 -0
  23. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack.egg-info/requires.txt +0 -0
  24. {envstack-1.0.0 → envstack-1.0.2}/lib/envstack.egg-info/top_level.txt +0 -0
  25. {envstack-1.0.0 → envstack-1.0.2}/setup.cfg +0 -0
  26. {envstack-1.0.0 → envstack-1.0.2}/tests/test_cmds.py +0 -0
  27. {envstack-1.0.0 → envstack-1.0.2}/tests/test_encrypt.py +0 -0
  28. {envstack-1.0.0 → envstack-1.0.2}/tests/test_env.py +0 -0
  29. {envstack-1.0.0 → envstack-1.0.2}/tests/test_node.py +0 -0
  30. {envstack-1.0.0 → envstack-1.0.2}/tests/test_path.py +0 -0
  31. {envstack-1.0.0 → envstack-1.0.2}/tests/test_util.py +0 -0
  32. {envstack-1.0.0 → envstack-1.0.2}/tests/test_wrapper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: envstack
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Environment variable composition layer for tools and processes.
5
5
  Author-email: Ryan Galloway <ryan@rsgalloway.com>
6
6
  License: BSD 3-Clause License
@@ -34,7 +34,7 @@ Stacked environment variable management system.
34
34
  """
35
35
 
36
36
  __prog__ = "envstack"
37
- __version__ = "1.0.0"
37
+ __version__ = "1.0.2"
38
38
 
39
39
  from envstack.env import clear, init, revert, save # noqa: F401
40
40
  from envstack.env import load_environ, resolve_environ # noqa: F401
@@ -38,21 +38,42 @@ import binascii
38
38
  import os
39
39
  import secrets
40
40
  from base64 import b64decode, b64encode
41
+ from functools import wraps
41
42
 
42
43
  from envstack.logger import log
43
44
 
44
45
  # cryptography and _rust dependency may not be available everywhere
45
46
  # ImportError: DLL load failed while importing _rust: Module not found.
47
+ CRYPTOGRAPHY_AVAILABLE = False
46
48
  Fernet = None
49
+ InvalidToken = type("InvalidToken", (Exception,), {})
50
+ InvalidTag = type("InvalidTag", (Exception,), {})
51
+ padding = None
52
+ Cipher = None
53
+ algorithms = None
54
+ modes = None
47
55
  try:
48
- import cryptography.exceptions
49
56
  from cryptography.fernet import Fernet, InvalidToken
57
+ from cryptography.exceptions import InvalidTag
50
58
  from cryptography.hazmat.primitives import padding
51
59
  from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
60
+ CRYPTOGRAPHY_AVAILABLE = True
52
61
  except ImportError as err:
53
62
  log.debug("cryptography module not available: %s", err)
54
63
 
55
64
 
65
+ def require_cryptography(func):
66
+ """Guard crypto-backed functions when cryptography is unavailable."""
67
+
68
+ @wraps(func)
69
+ def wrapper(*args, **kwargs):
70
+ if not CRYPTOGRAPHY_AVAILABLE:
71
+ raise RuntimeError("cryptography support is not available")
72
+ return func(*args, **kwargs)
73
+
74
+ return wrapper
75
+
76
+
56
77
  class Base64Encryptor(object):
57
78
  """Encrypt and decrypt secrets using base64 encoding."""
58
79
 
@@ -84,6 +105,7 @@ class FernetEncryptor(object):
84
105
  self.key = self.get_key(env)
85
106
 
86
107
  @classmethod
108
+ @require_cryptography
87
109
  def generate_key(csl):
88
110
  """Generate a new 256-bit encryption key."""
89
111
  if Fernet:
@@ -104,6 +126,7 @@ class FernetEncryptor(object):
104
126
  return Fernet(key)
105
127
  return key
106
128
 
129
+ @require_cryptography
107
130
  def encrypt(self, data: str):
108
131
  """Encrypt a secret using Fernet.
109
132
 
@@ -121,9 +144,9 @@ class FernetEncryptor(object):
121
144
  log.error("invalid value: %s", e)
122
145
  except Exception as e:
123
146
  log.error("unhandled error: %s", e)
124
- finally:
125
- return results
147
+ return results
126
148
 
149
+ @require_cryptography
127
150
  def decrypt(self, data: str):
128
151
  """Decrypt a secret using Fernet.
129
152
 
@@ -153,6 +176,7 @@ class AESGCMEncryptor(object):
153
176
  self.key = self.get_key(env)
154
177
 
155
178
  @classmethod
179
+ @require_cryptography
156
180
  def generate_key(csl):
157
181
  """Generate a new 256-bit encryption key."""
158
182
  key = secrets.token_bytes(32)
@@ -171,6 +195,7 @@ class AESGCMEncryptor(object):
171
195
  raise ValueError("invalid base64 encoding: %s" % e)
172
196
  return key
173
197
 
198
+ @require_cryptography
174
199
  def encrypt_data(self, secret: str):
175
200
  """Encrypt a secret using AES-GCM.
176
201
 
@@ -189,6 +214,7 @@ class AESGCMEncryptor(object):
189
214
  "tag": b64encode(encryptor.tag).decode(),
190
215
  }
191
216
 
217
+ @require_cryptography
192
218
  def decrypt_data(self, encrypted_data: dict):
193
219
  """Decrypt a secret using AES-GCM.
194
220
 
@@ -218,14 +244,13 @@ class AESGCMEncryptor(object):
218
244
  results = compact_store(encrypted_data)
219
245
  except binascii.Error as e:
220
246
  log.error("invalid base64 encoding: %s", e)
221
- except cryptography.exceptions.InvalidTag:
247
+ except InvalidTag:
222
248
  log.error("invalid encryption key")
223
249
  except ValueError as e:
224
250
  log.error("invalid value: %s", e)
225
251
  except Exception as e:
226
252
  log.error("unhandled error: %s", e)
227
- finally:
228
- return results
253
+ return results
229
254
 
230
255
  def decrypt(self, data: str):
231
256
  """Convenience function to decrypt a secret using AES-GCM.
@@ -239,7 +264,7 @@ class AESGCMEncryptor(object):
239
264
  return decrypted.decode()
240
265
  except binascii.Error as e:
241
266
  log.debug("invalid base64 encoding: %s", e)
242
- except cryptography.exceptions.InvalidTag:
267
+ except InvalidTag:
243
268
  log.debug("invalid encryption key")
244
269
  except ValueError as e:
245
270
  log.debug("invalid value: %s", e)
@@ -248,6 +273,7 @@ class AESGCMEncryptor(object):
248
273
  return data
249
274
 
250
275
 
276
+ @require_cryptography
251
277
  def pad_data(data: str):
252
278
  """Pad data to be block-aligned for AES encryption.
253
279
 
@@ -258,6 +284,7 @@ def pad_data(data: str):
258
284
  return padder.update(str(data).encode()) + padder.finalize()
259
285
 
260
286
 
287
+ @require_cryptography
261
288
  def unpad_data(data: dict):
262
289
  """Unpad data after decryption.
263
290
 
@@ -131,7 +131,7 @@ class EnvshellWrapper(Wrapper):
131
131
  if os.name == "nt":
132
132
  return ("PROMPT", "$E[32m(${ENV:=${STACK}})$E[0m $P$G ")
133
133
  else:
134
- return ("PS1", "\[\e[32m\](${ENV:=${STACK}})\[\e[0m\] \w\$ ")
134
+ return ("PS1", r"\[\e[32m\](${ENV:=${STACK}})\[\e[0m\] \w\$ ")
135
135
 
136
136
  def get_subprocess_env(self):
137
137
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: envstack
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Environment variable composition layer for tools and processes.
5
5
  Author-email: Ryan Galloway <ryan@rsgalloway.com>
6
6
  License: BSD 3-Clause License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "envstack"
7
- version = "1.0.0"
7
+ version = "1.0.2"
8
8
  description = "Environment variable composition layer for tools and processes."
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.6"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes