Skip to content

Commit 0f8a631

Browse files
committed
Raise an error on "#" char in config value
Most yo users don't need a "#" character in their configuration, and raising an error when this is detected will help avoid a common error for new users. But to be safe, include a config option to bypass this error when necessary. Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com>
1 parent d1f247c commit 0f8a631

File tree

5 files changed

+68
-3
lines changed

5 files changed

+68
-3
lines changed

doc/guide/configuration.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,18 @@ viewed in the documentation for ``-x`` in :ref:`yo_list`.
370370
You can override this on the command line with ``yo list -C Col1,Col2`` and you
371371
can extend the list on the command line with ``yo list -x Col1``.
372372

373+
``allow_hash_in_config_value``
374+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
375+
376+
(Boolean, Optional, Default: false)
377+
378+
By default, Yo detects the '#' character within a configuration value, and
379+
raises an error, becaues this is a common mistake for users. The '#' character
380+
can only introduce a comment at the beginning of a line. If you use it after a
381+
config value, it is included in the resulting value, which is usually not what
382+
you want. However, if there is some case where you actually want to include a
383+
hash in the config, set this to true to bypass the error.
384+
373385
.. _regionconf:
374386

375387
Region-Specific Configurations

yo/api.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
from yo.util import check_args_dataclass
5757
from yo.util import current_yo_version
5858
from yo.util import fmt_allow_deny
59+
from yo.util import hasherr
5960
from yo.util import latest_yo_version
6061
from yo.util import one
6162
from yo.util import standardize_name
@@ -219,8 +220,14 @@ def create_arg_dict(self, ctx: "YoCtx") -> t.Dict[str, t.Any]:
219220
}
220221

221222
@staticmethod
222-
def from_dict(d: t.Dict[str, t.Any], name: str) -> "InstanceProfile":
223+
def from_dict(
224+
d: t.Dict[str, t.Any], name: str, allow_hash: bool
225+
) -> "InstanceProfile":
223226
d = d.copy()
227+
if not allow_hash:
228+
for k, v in d.items():
229+
if isinstance(v, str) and "#" in v:
230+
hasherr(k, v, name)
224231
check_args_dataclass(
225232
InstanceProfile, d.keys(), f"~/.oci/yo.ini \\[{name}] section"
226233
)

yo/data/sample.yo.ini

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,18 @@ vnc_prog = krdc vnc://{host}:{port}
134134
#
135135
# list_columns = Name,Shape,Mem,CPU,State,Created
136136

137+
# OPTIONAL: allow_hash_in_config_value
138+
#
139+
# (Boolean, Optional, Default: false)
140+
#
141+
# By default, Yo detects the '#' character within a configuration value, and
142+
# raises an error, becaues this is a common mistake for users. The '#' character
143+
# can only introduce a comment at the beginning of a line. If you use it after
144+
# a config value, it is included in the resulting value, which is usually not
145+
# what you want. However, if there is some case where you actually want to
146+
# include a hash in the config, set this to true to bypass the error.
147+
#
148+
# allow_hash_in_config_value = false
137149

138150
##################################
139151
# OCI Region Configuration

yo/main.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@
114114
from yo.api import YoVolumeAttachment
115115
from yo.util import current_yo_version
116116
from yo.util import fmt_allow_deny
117+
from yo.util import hasherr
117118
from yo.util import latest_yo_version
118119
from yo.util import natural_sort
119120
from yo.util import shlex_join
@@ -305,7 +306,10 @@ def load_config(config_file: str = CONFIG_FILE) -> FullYoConfig:
305306
aliases: t.Dict[str, str] = {}
306307
if "aliases" in config.sections():
307308
for key in config["aliases"]:
308-
aliases[key] = str(config["aliases"][key])
309+
val = str(config["aliases"][key])
310+
if "#" in val and not yo_config.allow_hash_in_config_value:
311+
hasherr(key, val, "aliases")
312+
aliases[key] = val
309313

310314
instance_profiles: t.Dict[str, InstanceProfile] = {}
311315
expr = re.compile(r"^instances.")
@@ -341,7 +345,7 @@ def load_config(config_file: str = CONFIG_FILE) -> FullYoConfig:
341345
del config[f"instances.{sec}"]["inherit"]
342346
keys.update(config[f"instances.{sec}"])
343347
instance_profiles[sec] = InstanceProfile.from_dict(
344-
keys, f"instances.{sec}"
348+
keys, f"instances.{sec}", yo_config.allow_hash_in_config_value
345349
)
346350
instance_profiles[sec].validate(sec)
347351
return FullYoConfig(yo_config, instance_profiles, aliases)

yo/util.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,17 @@ def filter_keys(
7777
return output
7878

7979

80+
def hasherr(k: str, v: str, sec: str) -> None:
81+
raise YoExc(
82+
f"There is a '#' character in the config entry within section \\[{sec}]:\n"
83+
f" {k} = {v}\n"
84+
"Ini-style configs do not allow comments except on their own line, you\n"
85+
"cannot include a comment after a config value. If this was intended,\n"
86+
"you can bypass this error by setting 'allow_hash_in_config_value = true'\n"
87+
"in the \\[yo] section."
88+
)
89+
90+
8091
@dataclasses.dataclass
8192
class YoRegion:
8293
name: str
@@ -95,6 +106,12 @@ def from_config_section(
95106
)
96107
return YoRegion(**d)
97108

109+
def check_hasherr(self) -> None:
110+
for k in ("vcn_id", "subnet_id", "subnet_compartment_id"):
111+
v = getattr(self, k)
112+
if isinstance(v, str) and "#" in v:
113+
hasherr(k, v, "regions." + self.name)
114+
98115

99116
@dataclasses.dataclass
100117
class YoConfig:
@@ -121,6 +138,7 @@ class YoConfig:
121138
check_for_update_every: t.Optional[int] = 6
122139
creator_tags: t.List[str] = dataclasses.field(default_factory=list)
123140
list_columns: str = "Name,Shape,Mem,CPU,State,Created"
141+
allow_hash_in_config_value: bool = False
124142

125143
@property
126144
def vcn_id(self) -> str:
@@ -186,6 +204,7 @@ def from_config_section(
186204
cls, conf: configparser.SectionProxy, regions: t.Dict[str, YoRegion]
187205
) -> "YoConfig":
188206
d = dict(**conf)
207+
189208
d["regions"] = regions
190209
region_conf = filter_keys(
191210
d, ("vcn_id", "subnet_id", "subnet_compartment_id")
@@ -204,10 +223,21 @@ def from_config_section(
204223
"silence_automatic_tag_warning",
205224
"exact_name",
206225
"resource_filtering",
226+
"allow_hash_in_config_value",
207227
]
208228
for b in bools:
209229
if b in d:
210230
d[b] = conf.getboolean(b)
231+
232+
allow_hash = d.get("allow_hash_in_config_value", False)
233+
if not allow_hash:
234+
for k, v in d.items():
235+
if isinstance(v, str) and "#" in v:
236+
hasherr(k, v, "yo")
237+
# Region configs are loaded before [yo], so check them once
238+
# we know whether we should be raising an error.
239+
for v in regions.values():
240+
v.check_hasherr()
211241
if "check_for_update_every" in d:
212242
d["check_for_update_every"] = int(d["check_for_update_every"])
213243
# OCI stores email addresses as lower case. While most people write

0 commit comments

Comments
 (0)