1+ import enum
12import os
23import string
34
45from . import utils
56
67
8+ class ExpandMode (enum .Enum ):
9+ """Defines how Formatter replaces `!r` conformer flags."""
10+
11+ Preserve = (1 , False )
12+ """Preserve `!e` in Formatter calls. ('-{A!e}-' -> `-{A!e}-`)."""
13+ ToShell = (2 , True )
14+ """Convert `!e` to the shell's environment variable. ('-{A!e}-' -> `-$A-`)."""
15+ Remove = (3 , True )
16+ """Replace `!e` with a empty string. ('-{A!e}-' -> `--`)."""
17+
18+ def __init__ (self , value , expand ) -> None :
19+ self ._value_ = value
20+ self .expand = expand
21+
22+ def __str__ (self ):
23+ return self .name
24+
25+
726class Formatter (string .Formatter ):
827 """A extended string formatter class to parse habitat configurations.
928
@@ -23,8 +42,11 @@ class Formatter(string.Formatter):
2342 :py:const:`Formatter.shell_formats` and :py:meth:`Formatter.language_from_ext`.
2443 for supported values. If you pass None, it will preserve the
2544 formatting markers so it can be converted later.
26- expand(bool, optional): Should the ``!e`` conversion insert the shell
27- environment variable specifier or the value of the env var?
45+ expand(ExpandMode, optional): Controls how the ``!e`` conversion is handled.
46+ hab_env(dict, optional): If passed a dict of environment variable values
47+ defined and updated by hab. These are used to replace `!e` vars. If
48+ not found then falls back to os.environ values. This is used to expand
49+ a previously defined hab variable into another hab variable.
2850
2951 .. _conversion field:
3052 https://docs.python.org/3/library/string.html#grammar-token-format-string-conversion
@@ -64,10 +86,24 @@ class Formatter(string.Formatter):
6486 string that accepts the env var name. ``;`` is the path separator to use.
6587 """
6688
67- def __init__ (self , language , expand = False ):
89+ def __init__ (self , language , expand = ExpandMode . Preserve , hab_env = None ):
6890 super ().__init__ ()
6991 self .language = self .language_from_ext (language )
7092 self .expand = expand
93+ self .hab_env = {} if hab_env is None else hab_env
94+
95+ def format (self , format_string , * args , hab_env = None , ** kwargs ):
96+ if hab_env is not None :
97+ # Use the hab_env defined values instead of os.environ if possible.
98+ try :
99+ current = self .hab_env
100+ self .hab_env = hab_env
101+ return super ().format (format_string , * args , ** kwargs )
102+ finally :
103+ self .hab_env = current
104+ else :
105+ # fallback to the os.environ and other defined kwargs.
106+ return super ().format (format_string , * args , ** kwargs )
71107
72108 def get_field (self , field_name , args , kwargs ):
73109 """Returns the object to be inserted for the given field_name.
@@ -80,6 +116,13 @@ def get_field(self, field_name, args, kwargs):
80116 .. _`string.Formatter`:
81117 https://docs.python.org/3/library/string.html#string.Formatter.get_field
82118 """
119+ if field_name not in kwargs and field_name in self .hab_env :
120+ # if field_name was not provided, use value stored in self.hab_env
121+ value = self .hab_env [field_name ]
122+ if isinstance (value , list ) and len (value ):
123+ value = utils .Platform .collapse_paths (value , ext = self .language )
124+ return value , field_name
125+
83126 # If a field_name was not provided, use the value stored in os.environ
84127 if field_name not in kwargs and field_name in os .environ :
85128 return os .getenv (field_name ), field_name
@@ -120,12 +163,20 @@ def parse(self, txt):
120163 yield (literal_text , field_name , format_spec , conversion )
121164 continue
122165
123- elif self .expand and field_name in os . environ :
166+ elif self .expand . expand :
124167 # Expand the env var to the env var value. Later `get_field`
125- # will update kwargs with the existing env var value
126- yield (literal_text , field_name , format_spec , "s" )
127- continue
168+ if field_name in self .hab_env :
169+ yield (literal_text , field_name , format_spec , "s" )
170+ continue
171+ elif field_name in os .environ :
172+ # Expand the env var to the env var value. Later `get_field`
173+ # will update kwargs with the existing env var value
174+ yield (literal_text , field_name , format_spec , "s" )
175+ continue
128176
129177 # Convert this !e conversion to the shell specific env var specifier
130- value = self .shell_formats [self .language ]["env_var" ].format (field_name )
178+ if self .expand == ExpandMode .Remove :
179+ value = ""
180+ else :
181+ value = self .shell_formats [self .language ]["env_var" ].format (field_name )
131182 yield (literal_text + value , None , None , None )
0 commit comments