envstack 0.6.2__tar.gz → 0.6.4__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.
- {envstack-0.6.2/lib/envstack.egg-info → envstack-0.6.4}/PKG-INFO +25 -22
- {envstack-0.6.2 → envstack-0.6.4}/README.md +23 -20
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack/__init__.py +2 -2
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack/cli.py +9 -2
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack/config.py +4 -5
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack/env.py +173 -37
- envstack-0.6.4/lib/envstack/util.py +106 -0
- {envstack-0.6.2 → envstack-0.6.4/lib/envstack.egg-info}/PKG-INFO +25 -22
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack.egg-info/SOURCES.txt +1 -0
- envstack-0.6.4/lib/envstack.egg-info/requires.txt +1 -0
- {envstack-0.6.2 → envstack-0.6.4}/setup.py +2 -2
- envstack-0.6.2/lib/envstack.egg-info/requires.txt +0 -1
- {envstack-0.6.2 → envstack-0.6.4}/LICENSE +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/dev.env +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/dist.json +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack/exceptions.py +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack/logger.py +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack/path.py +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack/wrapper.py +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack.egg-info/dependency_links.txt +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack.egg-info/entry_points.txt +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack.egg-info/not-zip-safe +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/lib/envstack.egg-info/top_level.txt +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/setup.cfg +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/stack.env +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/tests/test_env.py +0 -0
- {envstack-0.6.2 → envstack-0.6.4}/tests/test_path.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: envstack
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: Stacked environment variable management system
|
|
5
5
|
Home-page: http://github.com/rsgalloway/envstack
|
|
6
6
|
Author: Ryan Galloway
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
19
19
|
Requires-Python: >=3.6
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: PyYAML
|
|
22
|
+
Requires-Dist: PyYAML==5.1.2
|
|
23
23
|
|
|
24
24
|
envstack
|
|
25
25
|
========
|
|
@@ -184,19 +184,34 @@ $ envstack [STACK] --sources
|
|
|
184
184
|
|
|
185
185
|
## Python API
|
|
186
186
|
|
|
187
|
-
To
|
|
187
|
+
To initialize the environment stack in Python, use the `init` function:
|
|
188
188
|
|
|
189
189
|
```python
|
|
190
|
-
>>> envstack.init(
|
|
191
|
-
>>> os.getenv("
|
|
192
|
-
'
|
|
190
|
+
>>> envstack.init()
|
|
191
|
+
>>> os.getenv("HELLO")
|
|
192
|
+
'world'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
To initialize the "dev" stack:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
>>> envstack.init("dev")
|
|
199
|
+
>>> os.getenv("ENV")
|
|
200
|
+
'dev'
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
To revert the original environment:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
>>> envstack.revert()
|
|
207
|
+
>>> os.getenv("HELLO")
|
|
208
|
+
>>>
|
|
193
209
|
```
|
|
194
210
|
|
|
195
211
|
Alternatively, `envstack.getenv` can be a drop-in replacement for `os.getenv`
|
|
196
212
|
for the default environment stack:
|
|
197
213
|
|
|
198
214
|
```python
|
|
199
|
-
>>> import envstack
|
|
200
215
|
>>> envstack.getenv("HELLO")
|
|
201
216
|
'world'
|
|
202
217
|
```
|
|
@@ -288,7 +303,7 @@ the output of the `--export` command:
|
|
|
288
303
|
|
|
289
304
|
#### bash
|
|
290
305
|
```bash
|
|
291
|
-
alias envstack-
|
|
306
|
+
alias envstack-init='source <(envstack --export)';
|
|
292
307
|
```
|
|
293
308
|
|
|
294
309
|
#### cmd
|
|
@@ -296,13 +311,13 @@ alias envstack-set='source <(envstack "$1" --export)';
|
|
|
296
311
|
doskey envstack-set=for /f "usebackq" %i in (`envstack --export $*`) do %%i
|
|
297
312
|
```
|
|
298
313
|
|
|
299
|
-
Then you can set the environment stack in your shell with the `envstack-
|
|
314
|
+
Then you can set the environment stack in your shell with the `envstack-init`
|
|
300
315
|
command. To clear the environment in your current shell, create an alias that
|
|
301
316
|
sources the output of the `--clear` command:
|
|
302
317
|
|
|
303
318
|
#### bash
|
|
304
319
|
```bash
|
|
305
|
-
alias envstack-clear='source <(envstack
|
|
320
|
+
alias envstack-clear='source <(envstack --clear)';
|
|
306
321
|
```
|
|
307
322
|
|
|
308
323
|
#### cmd
|
|
@@ -310,18 +325,6 @@ alias envstack-clear='source <(envstack "$1" --clear)';
|
|
|
310
325
|
doskey envstack-clear=for /f "usebackq" %i in (`envstack --clear $*`) do %%i
|
|
311
326
|
```
|
|
312
327
|
|
|
313
|
-
Create a function for convenience that does both in one command:
|
|
314
|
-
|
|
315
|
-
#### bash
|
|
316
|
-
```bash
|
|
317
|
-
envstack-init() { envstack-clear "$1"; envstack-set "$1"; }
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
#### cmd
|
|
321
|
-
```cmd
|
|
322
|
-
doskey envstack-init=envstack-clear $* & envstack-set $*
|
|
323
|
-
```
|
|
324
|
-
|
|
325
328
|
## Config
|
|
326
329
|
|
|
327
330
|
Default config settings are in the config.py module. The following environment
|
|
@@ -161,19 +161,34 @@ $ envstack [STACK] --sources
|
|
|
161
161
|
|
|
162
162
|
## Python API
|
|
163
163
|
|
|
164
|
-
To
|
|
164
|
+
To initialize the environment stack in Python, use the `init` function:
|
|
165
165
|
|
|
166
166
|
```python
|
|
167
|
-
>>> envstack.init(
|
|
168
|
-
>>> os.getenv("
|
|
169
|
-
'
|
|
167
|
+
>>> envstack.init()
|
|
168
|
+
>>> os.getenv("HELLO")
|
|
169
|
+
'world'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
To initialize the "dev" stack:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
>>> envstack.init("dev")
|
|
176
|
+
>>> os.getenv("ENV")
|
|
177
|
+
'dev'
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
To revert the original environment:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
>>> envstack.revert()
|
|
184
|
+
>>> os.getenv("HELLO")
|
|
185
|
+
>>>
|
|
170
186
|
```
|
|
171
187
|
|
|
172
188
|
Alternatively, `envstack.getenv` can be a drop-in replacement for `os.getenv`
|
|
173
189
|
for the default environment stack:
|
|
174
190
|
|
|
175
191
|
```python
|
|
176
|
-
>>> import envstack
|
|
177
192
|
>>> envstack.getenv("HELLO")
|
|
178
193
|
'world'
|
|
179
194
|
```
|
|
@@ -265,7 +280,7 @@ the output of the `--export` command:
|
|
|
265
280
|
|
|
266
281
|
#### bash
|
|
267
282
|
```bash
|
|
268
|
-
alias envstack-
|
|
283
|
+
alias envstack-init='source <(envstack --export)';
|
|
269
284
|
```
|
|
270
285
|
|
|
271
286
|
#### cmd
|
|
@@ -273,13 +288,13 @@ alias envstack-set='source <(envstack "$1" --export)';
|
|
|
273
288
|
doskey envstack-set=for /f "usebackq" %i in (`envstack --export $*`) do %%i
|
|
274
289
|
```
|
|
275
290
|
|
|
276
|
-
Then you can set the environment stack in your shell with the `envstack-
|
|
291
|
+
Then you can set the environment stack in your shell with the `envstack-init`
|
|
277
292
|
command. To clear the environment in your current shell, create an alias that
|
|
278
293
|
sources the output of the `--clear` command:
|
|
279
294
|
|
|
280
295
|
#### bash
|
|
281
296
|
```bash
|
|
282
|
-
alias envstack-clear='source <(envstack
|
|
297
|
+
alias envstack-clear='source <(envstack --clear)';
|
|
283
298
|
```
|
|
284
299
|
|
|
285
300
|
#### cmd
|
|
@@ -287,18 +302,6 @@ alias envstack-clear='source <(envstack "$1" --clear)';
|
|
|
287
302
|
doskey envstack-clear=for /f "usebackq" %i in (`envstack --clear $*`) do %%i
|
|
288
303
|
```
|
|
289
304
|
|
|
290
|
-
Create a function for convenience that does both in one command:
|
|
291
|
-
|
|
292
|
-
#### bash
|
|
293
|
-
```bash
|
|
294
|
-
envstack-init() { envstack-clear "$1"; envstack-set "$1"; }
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
#### cmd
|
|
298
|
-
```cmd
|
|
299
|
-
doskey envstack-init=envstack-clear $* & envstack-set $*
|
|
300
|
-
```
|
|
301
|
-
|
|
302
305
|
## Config
|
|
303
306
|
|
|
304
307
|
Default config settings are in the config.py module. The following environment
|
|
@@ -34,6 +34,6 @@ Stacked environment variable management system.
|
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
36
|
__prog__ = "envstack"
|
|
37
|
-
__version__ = "0.6.
|
|
37
|
+
__version__ = "0.6.4"
|
|
38
38
|
|
|
39
|
-
from envstack.env import Env, getenv, init, load_file
|
|
39
|
+
from envstack.env import Env, clear, getenv, init, load_file, revert, save
|
|
@@ -38,7 +38,14 @@ import sys
|
|
|
38
38
|
import traceback
|
|
39
39
|
|
|
40
40
|
from envstack import __version__, config
|
|
41
|
-
from envstack.env import
|
|
41
|
+
from envstack.env import (
|
|
42
|
+
build_sources,
|
|
43
|
+
expandvars,
|
|
44
|
+
clear,
|
|
45
|
+
export,
|
|
46
|
+
load_environ,
|
|
47
|
+
trace_var,
|
|
48
|
+
)
|
|
42
49
|
from envstack.wrapper import run_command
|
|
43
50
|
|
|
44
51
|
|
|
@@ -139,7 +146,7 @@ def main():
|
|
|
139
146
|
for source in sources:
|
|
140
147
|
print(source)
|
|
141
148
|
elif args.clear:
|
|
142
|
-
print(
|
|
149
|
+
print(clear(args.namespace, config.SHELL))
|
|
143
150
|
elif args.export:
|
|
144
151
|
print(export(args.namespace, config.SHELL))
|
|
145
152
|
elif command:
|
|
@@ -76,14 +76,13 @@ DEFAULT_ENV = {
|
|
|
76
76
|
"USER": USERNAME,
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
# default location of
|
|
80
|
-
# NOTE: the siteconf sitecustomize.py module may override this
|
|
79
|
+
# default location of env stack .env files
|
|
81
80
|
DEFAULT_ENV_DIR = os.getenv(
|
|
82
81
|
"DEFAULT_ENV_DIR",
|
|
83
82
|
{
|
|
84
|
-
"darwin": "{HOME}/Library/Application Support/
|
|
85
|
-
"linux": "{HOME}/.local/
|
|
86
|
-
"windows": "C:\\ProgramData\\
|
|
83
|
+
"darwin": "{HOME}/Library/Application Support/pipe/{ENV}/env",
|
|
84
|
+
"linux": "{HOME}/.local/pipe/{ENV}/env",
|
|
85
|
+
"windows": "C:\\ProgramData\\pipe\\{ENV}\\env",
|
|
87
86
|
}
|
|
88
87
|
.get(PLATFORM)
|
|
89
88
|
.format(**DEFAULT_ENV),
|
|
@@ -36,8 +36,9 @@ Contains functions and classes for processing scoped .env files.
|
|
|
36
36
|
import os
|
|
37
37
|
import re
|
|
38
38
|
import string
|
|
39
|
+
import sys
|
|
39
40
|
|
|
40
|
-
from envstack import config, logger, path
|
|
41
|
+
from envstack import config, logger, path, util
|
|
41
42
|
from envstack.exceptions import *
|
|
42
43
|
|
|
43
44
|
# value delimiter pattern (splits values by colons or semicolons)
|
|
@@ -49,6 +50,9 @@ load_file_cache = {}
|
|
|
49
50
|
# value for unresolvable variables
|
|
50
51
|
null = ""
|
|
51
52
|
|
|
53
|
+
# stores the original environment
|
|
54
|
+
SAVED_ENVIRONMENT = None
|
|
55
|
+
|
|
52
56
|
|
|
53
57
|
class EnvVar(string.Template, str):
|
|
54
58
|
"""A string class for supporting $-substitutions, e.g.: ::
|
|
@@ -529,65 +533,192 @@ def expandvars(var, env=None, recursive=False):
|
|
|
529
533
|
return EnvVar(var).expand(env, recursive=recursive)
|
|
530
534
|
|
|
531
535
|
|
|
532
|
-
def
|
|
533
|
-
|
|
536
|
+
def clear(
|
|
537
|
+
name=config.DEFAULT_NAMESPACE,
|
|
538
|
+
shell=config.SHELL,
|
|
539
|
+
scope=None,
|
|
540
|
+
):
|
|
541
|
+
"""Returns shell commands that can be sourced to unset or restore env stack
|
|
542
|
+
environment variables. Should only be run after a previous export:
|
|
534
543
|
|
|
535
|
-
|
|
544
|
+
$ source <(envstack --export)
|
|
545
|
+
$ source <(envstack --clear)
|
|
536
546
|
|
|
537
|
-
|
|
547
|
+
List of shell names: bash, sh, tcsh, cmd, pwsh
|
|
548
|
+
(see output of config.detect_shell()).
|
|
538
549
|
|
|
539
|
-
|
|
550
|
+
:param name: stack namespace.
|
|
551
|
+
:param shell: name of shell (default: current shell).
|
|
552
|
+
:param scope: environment scope (default: cwd).
|
|
553
|
+
:returns: shell commands as string.
|
|
554
|
+
"""
|
|
555
|
+
if "ENVSTACK" not in os.environ:
|
|
556
|
+
logger.log.info("Environment is already clear")
|
|
557
|
+
return ""
|
|
558
|
+
|
|
559
|
+
env = load_environ(name, environ=None, scope=scope)
|
|
560
|
+
export_vars = dict(env.items())
|
|
561
|
+
export_list = list()
|
|
562
|
+
|
|
563
|
+
# vars that should never be unset
|
|
564
|
+
restricted_vars = ["PATH", "PS1", "PWD", "PROMPT", "DEFAULT_ENV_DIR"]
|
|
565
|
+
|
|
566
|
+
for key in export_vars:
|
|
567
|
+
if key not in os.environ:
|
|
568
|
+
continue
|
|
569
|
+
old_key = f"_ES_OLD_{key}"
|
|
570
|
+
old_val = os.environ.get(old_key)
|
|
571
|
+
if shell in ["bash", "sh", "zsh"]:
|
|
572
|
+
if old_val:
|
|
573
|
+
export_list.append("export %s=%s" % (key, old_val))
|
|
574
|
+
export_list.append("unset %s" % (old_key))
|
|
575
|
+
elif key not in restricted_vars:
|
|
576
|
+
export_list.append(f"unset {key}")
|
|
577
|
+
elif shell == "tcsh":
|
|
578
|
+
if old_val:
|
|
579
|
+
export_list.append(f"setenv {key} {old_val}")
|
|
580
|
+
export_list.append(f"unsetenv {old_key}")
|
|
581
|
+
elif key not in restricted_vars:
|
|
582
|
+
export_list.append(f"unsetenv {key}")
|
|
583
|
+
elif shell == "cmd":
|
|
584
|
+
if old_val:
|
|
585
|
+
export_list.append(f"set {key}={old_val}")
|
|
586
|
+
export_list.append(f"set {old_key}=")
|
|
587
|
+
elif key not in restricted_vars:
|
|
588
|
+
export_list.append(f"set {key}=")
|
|
589
|
+
elif shell == "pwsh":
|
|
590
|
+
if old_val:
|
|
591
|
+
export_list.append(f"$env:{key}='{old_val}'")
|
|
592
|
+
export_list.append(f"Remove-Item Env:{old_key}")
|
|
593
|
+
elif key not in restricted_vars:
|
|
594
|
+
export_list.append(f"Remove-Item Env:{key}")
|
|
595
|
+
elif shell == "unknown":
|
|
596
|
+
raise Exception("unknown shell")
|
|
597
|
+
|
|
598
|
+
export_list.sort()
|
|
599
|
+
exp = "\n".join(export_list)
|
|
600
|
+
|
|
601
|
+
return exp
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
def export(
|
|
605
|
+
name=config.DEFAULT_NAMESPACE,
|
|
606
|
+
shell=config.SHELL,
|
|
607
|
+
scope=None,
|
|
608
|
+
):
|
|
609
|
+
"""Returns shell set env commands that can be sourced to set env stack
|
|
610
|
+
environment variables.
|
|
540
611
|
|
|
541
612
|
List of shell names: bash, sh, tcsh, cmd, pwsh
|
|
542
613
|
(see output of config.detect_shell()).
|
|
543
614
|
|
|
544
615
|
:param name: stack namespace.
|
|
545
|
-
:param shell: name of shell (default:
|
|
546
|
-
:param resolve: resolve values (default: True).
|
|
616
|
+
:param shell: name of shell (default: current shell).
|
|
547
617
|
:param scope: environment scope (default: cwd).
|
|
548
|
-
:param clear: clear existing values (default: False).
|
|
549
618
|
:returns: shell commands as string.
|
|
550
619
|
"""
|
|
551
620
|
env = load_environ(name, scope=scope)
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
621
|
+
export_vars = dict(env.items())
|
|
622
|
+
export_list = list()
|
|
623
|
+
|
|
624
|
+
for key, val in export_vars.items():
|
|
625
|
+
val = expandvars(val, env, recursive=False)
|
|
626
|
+
old_key = f"_ES_OLD_{key}"
|
|
627
|
+
old_val = os.environ.get(key)
|
|
628
|
+
if key == "PATH" and not val:
|
|
629
|
+
logger.log.warning("PATH is empty")
|
|
630
|
+
continue
|
|
556
631
|
if shell in ["bash", "sh", "zsh"]:
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
expList.append(f'export {k}="{v}"')
|
|
632
|
+
export_list.append(f"export {key}={val}")
|
|
633
|
+
if old_val:
|
|
634
|
+
export_list.append(f"export {old_key}={old_val}")
|
|
561
635
|
elif shell == "tcsh":
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
expList.append(f'setenv {k}:"{v}"')
|
|
636
|
+
export_list.append(f'setenv {key}:"{val}"')
|
|
637
|
+
if old_val:
|
|
638
|
+
export_list.append(f'setenv {old_key}:"{old_val}"')
|
|
566
639
|
elif shell == "cmd":
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
expList.append(f'set {k}="{v}"')
|
|
640
|
+
export_list.append(f'set {key}="{val}"')
|
|
641
|
+
if old_val:
|
|
642
|
+
export_list.append(f'set {old_key}="{old_val}"')
|
|
571
643
|
elif shell == "pwsh":
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
expList.append(f'$env:{k}="{v}"')
|
|
644
|
+
export_list.append(f'$env:{key}="{val}"')
|
|
645
|
+
if old_val:
|
|
646
|
+
export_list.append(f'$env:{old_key}="{old_val}"')
|
|
576
647
|
elif shell == "unknown":
|
|
577
648
|
raise Exception("unknown shell")
|
|
578
|
-
|
|
579
|
-
|
|
649
|
+
|
|
650
|
+
export_list.sort()
|
|
651
|
+
exp = "\n".join(export_list)
|
|
652
|
+
|
|
580
653
|
return exp
|
|
581
654
|
|
|
582
655
|
|
|
656
|
+
def save():
|
|
657
|
+
"""Saves the current environment for later restoration."""
|
|
658
|
+
global SAVED_ENVIRONMENT
|
|
659
|
+
if not SAVED_ENVIRONMENT:
|
|
660
|
+
SAVED_ENVIRONMENT = dict(os.environ.copy())
|
|
661
|
+
return SAVED_ENVIRONMENT
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def revert():
|
|
665
|
+
"""Reverts the environment to the saved environment. Updates sys.path using
|
|
666
|
+
paths found in PYTHONPATH.
|
|
667
|
+
|
|
668
|
+
Initialize the default environment stack:
|
|
669
|
+
>>> envstack.init()
|
|
670
|
+
|
|
671
|
+
Revert to the previous environment:
|
|
672
|
+
>>> envstack.revert()
|
|
673
|
+
"""
|
|
674
|
+
global SAVED_ENVIRONMENT
|
|
675
|
+
if SAVED_ENVIRONMENT is None:
|
|
676
|
+
return
|
|
677
|
+
|
|
678
|
+
# clear current sys.path values
|
|
679
|
+
util.clear_sys_path()
|
|
680
|
+
|
|
681
|
+
# restore the original environment
|
|
682
|
+
os.environ.clear()
|
|
683
|
+
os.environ.update(SAVED_ENVIRONMENT)
|
|
684
|
+
|
|
685
|
+
# restore sys.path from PYTHONPATH
|
|
686
|
+
util.load_sys_path()
|
|
687
|
+
|
|
688
|
+
SAVED_ENVIRONMENT = None
|
|
689
|
+
|
|
690
|
+
|
|
583
691
|
def init(name=config.DEFAULT_NAMESPACE):
|
|
584
|
-
"""Initializes the environment
|
|
692
|
+
"""Initializes the environment from a given stack namespace. Environments
|
|
693
|
+
propogate downwards with subsequent calls to init().
|
|
585
694
|
|
|
586
|
-
|
|
695
|
+
Updates sys.path using paths found in PYTHONPATH.
|
|
696
|
+
|
|
697
|
+
Initialize the default environment stack:
|
|
698
|
+
>>> envstack.init()
|
|
699
|
+
|
|
700
|
+
Initialize the dev environment stack (inherits from previous call):
|
|
701
|
+
>>> envstack.init('dev')
|
|
702
|
+
|
|
703
|
+
Revert to the original environment:
|
|
704
|
+
>>> envstack.revert()
|
|
705
|
+
|
|
706
|
+
:param name: stack namespace (default: 'stack').
|
|
707
|
+
:param reset: clear existing values first (default: False).
|
|
587
708
|
"""
|
|
709
|
+
# save environment to restore later using envstack.revert()
|
|
710
|
+
save()
|
|
711
|
+
|
|
712
|
+
# clear old sys.path values
|
|
713
|
+
util.clear_sys_path()
|
|
714
|
+
|
|
715
|
+
# load the stack and update the environment
|
|
588
716
|
env = load_environ(name)
|
|
589
717
|
os.environ.update(encode(env))
|
|
590
718
|
|
|
719
|
+
# update sys.path from PYTHONPATH
|
|
720
|
+
util.load_sys_path()
|
|
721
|
+
|
|
591
722
|
|
|
592
723
|
def load_environ(
|
|
593
724
|
name=config.DEFAULT_NAMESPACE,
|
|
@@ -616,7 +747,6 @@ def load_environ(
|
|
|
616
747
|
:param includes: merge included namespaces.
|
|
617
748
|
:returns: dict of environment variables.
|
|
618
749
|
"""
|
|
619
|
-
|
|
620
750
|
# build list of sources from namespace(s) and scope
|
|
621
751
|
if not sources:
|
|
622
752
|
sources = build_sources(name, scope=scope, includes=includes)
|
|
@@ -631,6 +761,9 @@ def load_environ(
|
|
|
631
761
|
for source in sources:
|
|
632
762
|
env.update(source.load(platform=platform))
|
|
633
763
|
|
|
764
|
+
# store the name of the environment stack
|
|
765
|
+
env["ENVSTACK"] = env.get("ENVSTACK", "|".join(name))
|
|
766
|
+
|
|
634
767
|
# merge values from given environment
|
|
635
768
|
if environ:
|
|
636
769
|
return merge(env, environ, platform=platform)
|
|
@@ -688,9 +821,11 @@ def merge(env, other, strict=False, platform=config.PLATFORM):
|
|
|
688
821
|
"""
|
|
689
822
|
merged = env.copy()
|
|
690
823
|
for key, value in merged.items():
|
|
691
|
-
|
|
824
|
+
varstr = "${%s}" % key
|
|
825
|
+
# replace variables in other with values from env
|
|
692
826
|
if key in other:
|
|
693
|
-
|
|
827
|
+
# replace variables in value with values from other
|
|
828
|
+
if varstr in str(value):
|
|
694
829
|
value = re.sub(
|
|
695
830
|
r"\${(\w+)}",
|
|
696
831
|
lambda match: other.get(match.group(1), match.group(0)),
|
|
@@ -699,7 +834,8 @@ def merge(env, other, strict=False, platform=config.PLATFORM):
|
|
|
699
834
|
elif not strict:
|
|
700
835
|
value = other.get(key)
|
|
701
836
|
else:
|
|
702
|
-
value = str(value).replace(
|
|
837
|
+
value = str(value).replace(varstr, "")
|
|
838
|
+
# replace colons with semicolons on windows
|
|
703
839
|
if platform == "windows":
|
|
704
840
|
result = re.sub(r"(?<!\b[A-Za-z]):", ";", str(value))
|
|
705
841
|
value = result.rstrip(";")
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2024, Ryan Galloway (ryan@rsgalloway.com)
|
|
4
|
+
#
|
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
|
6
|
+
# modification, are permitted provided that the following conditions are met:
|
|
7
|
+
#
|
|
8
|
+
# - Redistributions of source code must retain the above copyright notice,
|
|
9
|
+
# this list of conditions and the following disclaimer.
|
|
10
|
+
#
|
|
11
|
+
# - Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
# and/or other materials provided with the distribution.
|
|
14
|
+
#
|
|
15
|
+
# - Neither the name of the software nor the names of its contributors
|
|
16
|
+
# may be used to endorse or promote products derived from this software
|
|
17
|
+
# without specific prior written permission.
|
|
18
|
+
#
|
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
22
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
23
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
24
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
25
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
26
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
27
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
28
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
29
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
30
|
+
#
|
|
31
|
+
|
|
32
|
+
__doc__ = """
|
|
33
|
+
Contains common utility functions and classes.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import os
|
|
37
|
+
import sys
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def clear_sys_path(var="PYTHONPATH"):
|
|
41
|
+
"""
|
|
42
|
+
Remove paths from sys.path that are in the given environment variable.
|
|
43
|
+
|
|
44
|
+
:param var: The environment variable to remove paths from.
|
|
45
|
+
"""
|
|
46
|
+
for path in get_paths_from_var(var):
|
|
47
|
+
if path and path in sys.path:
|
|
48
|
+
sys.path.remove(path)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def load_sys_path(var="PYTHONPATH", pathsep=os.pathsep, reverse=True):
|
|
52
|
+
"""
|
|
53
|
+
Add paths from the given environment variable to sys.path.
|
|
54
|
+
|
|
55
|
+
:param var: The environment variable to add paths from.
|
|
56
|
+
:param pathsep: The path separator to use.
|
|
57
|
+
:param reverse: Reverse the order of the paths.
|
|
58
|
+
"""
|
|
59
|
+
for path in get_paths_from_var(var, pathsep, reverse):
|
|
60
|
+
if path and path not in sys.path:
|
|
61
|
+
sys.path.insert(0, path)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def dict_diff(dict1, dict2):
|
|
65
|
+
"""
|
|
66
|
+
Compare two dictionaries and return their differences.
|
|
67
|
+
|
|
68
|
+
:param dict1: First dictionary.
|
|
69
|
+
:param dict2: Second dictionary.
|
|
70
|
+
:returns: diff dict: 'added', 'removed', 'changed', and 'unchanged'.
|
|
71
|
+
"""
|
|
72
|
+
added = {k: dict2[k] for k in dict2 if k not in dict1}
|
|
73
|
+
removed = {k: dict1[k] for k in dict1 if k not in dict2}
|
|
74
|
+
changed = {
|
|
75
|
+
k: (dict1[k], dict2[k]) for k in dict1 if k in dict2 and dict1[k] != dict2[k]
|
|
76
|
+
}
|
|
77
|
+
unchanged = {k: dict1[k] for k in dict1 if k in dict2 and dict1[k] == dict2[k]}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
"added": added,
|
|
81
|
+
"removed": removed,
|
|
82
|
+
"changed": changed,
|
|
83
|
+
"unchanged": unchanged,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_paths_from_var(var="PYTHONPATH", pathsep=os.pathsep, reverse=True):
|
|
88
|
+
"""Returns a list of paths from a given pathsep separated environment
|
|
89
|
+
variable.
|
|
90
|
+
|
|
91
|
+
:param var: The environment variable to get paths from.
|
|
92
|
+
:param pathsep: The path separator to use.
|
|
93
|
+
:param reverse: Reverse the order of the paths.
|
|
94
|
+
:returns: A list of paths.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
paths = []
|
|
98
|
+
value = os.environ.get(var)
|
|
99
|
+
|
|
100
|
+
if value:
|
|
101
|
+
paths = value.split(pathsep)
|
|
102
|
+
|
|
103
|
+
if reverse:
|
|
104
|
+
paths.reverse()
|
|
105
|
+
|
|
106
|
+
return paths
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: envstack
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: Stacked environment variable management system
|
|
5
5
|
Home-page: http://github.com/rsgalloway/envstack
|
|
6
6
|
Author: Ryan Galloway
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
19
19
|
Requires-Python: >=3.6
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: PyYAML
|
|
22
|
+
Requires-Dist: PyYAML==5.1.2
|
|
23
23
|
|
|
24
24
|
envstack
|
|
25
25
|
========
|
|
@@ -184,19 +184,34 @@ $ envstack [STACK] --sources
|
|
|
184
184
|
|
|
185
185
|
## Python API
|
|
186
186
|
|
|
187
|
-
To
|
|
187
|
+
To initialize the environment stack in Python, use the `init` function:
|
|
188
188
|
|
|
189
189
|
```python
|
|
190
|
-
>>> envstack.init(
|
|
191
|
-
>>> os.getenv("
|
|
192
|
-
'
|
|
190
|
+
>>> envstack.init()
|
|
191
|
+
>>> os.getenv("HELLO")
|
|
192
|
+
'world'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
To initialize the "dev" stack:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
>>> envstack.init("dev")
|
|
199
|
+
>>> os.getenv("ENV")
|
|
200
|
+
'dev'
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
To revert the original environment:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
>>> envstack.revert()
|
|
207
|
+
>>> os.getenv("HELLO")
|
|
208
|
+
>>>
|
|
193
209
|
```
|
|
194
210
|
|
|
195
211
|
Alternatively, `envstack.getenv` can be a drop-in replacement for `os.getenv`
|
|
196
212
|
for the default environment stack:
|
|
197
213
|
|
|
198
214
|
```python
|
|
199
|
-
>>> import envstack
|
|
200
215
|
>>> envstack.getenv("HELLO")
|
|
201
216
|
'world'
|
|
202
217
|
```
|
|
@@ -288,7 +303,7 @@ the output of the `--export` command:
|
|
|
288
303
|
|
|
289
304
|
#### bash
|
|
290
305
|
```bash
|
|
291
|
-
alias envstack-
|
|
306
|
+
alias envstack-init='source <(envstack --export)';
|
|
292
307
|
```
|
|
293
308
|
|
|
294
309
|
#### cmd
|
|
@@ -296,13 +311,13 @@ alias envstack-set='source <(envstack "$1" --export)';
|
|
|
296
311
|
doskey envstack-set=for /f "usebackq" %i in (`envstack --export $*`) do %%i
|
|
297
312
|
```
|
|
298
313
|
|
|
299
|
-
Then you can set the environment stack in your shell with the `envstack-
|
|
314
|
+
Then you can set the environment stack in your shell with the `envstack-init`
|
|
300
315
|
command. To clear the environment in your current shell, create an alias that
|
|
301
316
|
sources the output of the `--clear` command:
|
|
302
317
|
|
|
303
318
|
#### bash
|
|
304
319
|
```bash
|
|
305
|
-
alias envstack-clear='source <(envstack
|
|
320
|
+
alias envstack-clear='source <(envstack --clear)';
|
|
306
321
|
```
|
|
307
322
|
|
|
308
323
|
#### cmd
|
|
@@ -310,18 +325,6 @@ alias envstack-clear='source <(envstack "$1" --clear)';
|
|
|
310
325
|
doskey envstack-clear=for /f "usebackq" %i in (`envstack --clear $*`) do %%i
|
|
311
326
|
```
|
|
312
327
|
|
|
313
|
-
Create a function for convenience that does both in one command:
|
|
314
|
-
|
|
315
|
-
#### bash
|
|
316
|
-
```bash
|
|
317
|
-
envstack-init() { envstack-clear "$1"; envstack-set "$1"; }
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
#### cmd
|
|
321
|
-
```cmd
|
|
322
|
-
doskey envstack-init=envstack-clear $* & envstack-set $*
|
|
323
|
-
```
|
|
324
|
-
|
|
325
328
|
## Config
|
|
326
329
|
|
|
327
330
|
Default config settings are in the config.py module. The following environment
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
PyYAML==5.1.2
|
|
@@ -74,7 +74,7 @@ class PostInstallCommand(install):
|
|
|
74
74
|
|
|
75
75
|
setup(
|
|
76
76
|
name="envstack",
|
|
77
|
-
version="0.6.
|
|
77
|
+
version="0.6.4",
|
|
78
78
|
description="Stacked environment variable management system",
|
|
79
79
|
long_description=long_description,
|
|
80
80
|
long_description_content_type="text/markdown",
|
|
@@ -106,7 +106,7 @@ setup(
|
|
|
106
106
|
],
|
|
107
107
|
},
|
|
108
108
|
install_requires=[
|
|
109
|
-
"PyYAML
|
|
109
|
+
"PyYAML==5.1.2",
|
|
110
110
|
],
|
|
111
111
|
python_requires=">=3.6",
|
|
112
112
|
data_files=[(".", ["stack.env", "dev.env", "dist.json"])],
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
PyYAML>=5.1.2
|
|
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
|