Merge pull request #3163 from cortesi/defer

Use deferral mechanism for cfg file options
This commit is contained in:
Aldo Cortesi 2018-05-27 10:55:15 +12:00 committed by GitHub
commit dbf01cd34d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 28 deletions

View File

@ -91,7 +91,7 @@ class OptManager:
mutation doesn't change the option state inadvertently. mutation doesn't change the option state inadvertently.
""" """
def __init__(self): def __init__(self):
self._deferred: typing.Dict[str, str] = {} self.deferred: typing.Dict[str, str] = {}
self.changed = blinker.Signal() self.changed = blinker.Signal()
self.errored = blinker.Signal() self.errored = blinker.Signal()
# Options must be the last attribute here - after that, we raise an # Options must be the last attribute here - after that, we raise an
@ -217,6 +217,10 @@ class OptManager:
self.changed.send(self, updated=updated) self.changed.send(self, updated=updated)
return unknown return unknown
def update_defer(self, **kwargs):
unknown = self.update_known(**kwargs)
self.deferred.update(unknown)
def update(self, **kwargs): def update(self, **kwargs):
u = self.update_known(**kwargs) u = self.update_known(**kwargs)
if u: if u:
@ -303,7 +307,7 @@ class OptManager:
else: else:
unknown[optname] = optval unknown[optname] = optval
if defer: if defer:
self._deferred.update(unknown) self.deferred.update(unknown)
elif unknown: elif unknown:
raise exceptions.OptionsError("Unknown options: %s" % ", ".join(unknown.keys())) raise exceptions.OptionsError("Unknown options: %s" % ", ".join(unknown.keys()))
self.update(**vals) self.update(**vals)
@ -314,12 +318,12 @@ class OptManager:
have since been added. have since been added.
""" """
update = {} update = {}
for optname, optval in self._deferred.items(): for optname, optval in self.deferred.items():
if optname in self._options: if optname in self._options:
update[optname] = self.parse_setval(self._options[optname], optval) update[optname] = self.parse_setval(self._options[optname], optval)
self.update(**update) self.update(**update)
for k in update.keys(): for k in update.keys():
del self._deferred[k] del self.deferred[k]
def parse_setval(self, o: _Option, optstr: typing.Optional[str]) -> typing.Any: def parse_setval(self, o: _Option, optstr: typing.Optional[str]) -> typing.Any:
""" """
@ -494,26 +498,21 @@ def parse(text):
return data return data
def load(opts, text): def load(opts: OptManager, text: str) -> None:
""" """
Load configuration from text, over-writing options already set in Load configuration from text, over-writing options already set in
this object. May raise OptionsError if the config file is invalid. this object. May raise OptionsError if the config file is invalid.
Returns a dictionary of all unknown options.
""" """
data = parse(text) data = parse(text)
return opts.update_known(**data) opts.update_defer(**data)
def load_paths(opts, *paths): def load_paths(opts: OptManager, *paths: str) -> None:
""" """
Load paths in order. Each path takes precedence over the previous Load paths in order. Each path takes precedence over the previous
path. Paths that don't exist are ignored, errors raise an path. Paths that don't exist are ignored, errors raise an
OptionsError. OptionsError.
Returns a dictionary of unknown options.
""" """
ret = {}
for p in paths: for p in paths:
p = os.path.expanduser(p) p = os.path.expanduser(p)
if os.path.exists(p) and os.path.isfile(p): if os.path.exists(p) and os.path.isfile(p):
@ -525,15 +524,14 @@ def load_paths(opts, *paths):
"Error reading %s: %s" % (p, e) "Error reading %s: %s" % (p, e)
) )
try: try:
ret.update(load(opts, txt)) load(opts, txt)
except exceptions.OptionsError as e: except exceptions.OptionsError as e:
raise exceptions.OptionsError( raise exceptions.OptionsError(
"Error reading %s: %s" % (p, e) "Error reading %s: %s" % (p, e)
) )
return ret
def serialize(opts, text, defaults=False): def serialize(opts: OptManager, text: str, defaults: bool = False) -> str:
""" """
Performs a round-trip serialization. If text is not None, it is Performs a round-trip serialization. If text is not None, it is
treated as a previous serialization that should be modified treated as a previous serialization that should be modified
@ -554,7 +552,7 @@ def serialize(opts, text, defaults=False):
return ruamel.yaml.round_trip_dump(data) return ruamel.yaml.round_trip_dump(data)
def save(opts, path, defaults=False): def save(opts: OptManager, path: str, defaults: bool =False) -> None:
""" """
Save to path. If the destination file exists, modify it in-place. Save to path. If the destination file exists, modify it in-place.

View File

@ -93,7 +93,7 @@ def run(
sys.exit(1) sys.exit(1)
try: try:
opts.confdir = args.confdir opts.confdir = args.confdir
unknown = optmanager.load_paths( optmanager.load_paths(
opts, opts,
os.path.join(opts.confdir, OPTIONS_FILE_NAME), os.path.join(opts.confdir, OPTIONS_FILE_NAME),
) )
@ -109,7 +109,6 @@ def run(
server = proxy.server.DummyServer(pconf) server = proxy.server.DummyServer(pconf)
master.server = server master.server = server
opts.update_known(**unknown)
if args.options: if args.options:
print(optmanager.dump_defaults(opts)) print(optmanager.dump_defaults(opts))
sys.exit(0) sys.exit(0)

View File

@ -269,11 +269,13 @@ def test_serialize():
t = "# a comment" t = "# a comment"
optmanager.load(o2, t) optmanager.load(o2, t)
assert optmanager.load(o2, "foobar: '123'") == {"foobar": "123"} optmanager.load(o2, "foobar: '123'")
assert o2.deferred == {"foobar": "123"}
t = "" t = ""
optmanager.load(o2, t) optmanager.load(o2, t)
assert optmanager.load(o2, "foobar: '123'") == {"foobar": "123"} optmanager.load(o2, "foobar: '123'")
assert o2.deferred == {"foobar": "123"}
def test_serialize_defaults(): def test_serialize_defaults():
@ -297,7 +299,8 @@ def test_saving(tmpdir):
with open(dst, 'a') as f: with open(dst, 'a') as f:
f.write("foobar: '123'") f.write("foobar: '123'")
assert optmanager.load_paths(o, dst) == {"foobar": "123"} optmanager.load_paths(o, dst)
assert o.deferred == {"foobar": "123"}
with open(dst, 'a') as f: with open(dst, 'a') as f:
f.write("'''") f.write("'''")
@ -426,13 +429,13 @@ def test_set():
assert opts.seqstr == [] assert opts.seqstr == []
with pytest.raises(exceptions.OptionsError): with pytest.raises(exceptions.OptionsError):
opts.set("deferred=wobble") opts.set("deferredoption=wobble")
opts.set("deferred=wobble", defer=True) opts.set("deferredoption=wobble", defer=True)
assert "deferred" in opts._deferred assert "deferredoption" in opts.deferred
opts.process_deferred() opts.process_deferred()
assert "deferred" in opts._deferred assert "deferredoption" in opts.deferred
opts.add_option("deferred", str, "default", "help") opts.add_option("deferredoption", str, "default", "help")
opts.process_deferred() opts.process_deferred()
assert "deferred" not in opts._deferred assert "deferredoption" not in opts.deferred
assert opts.deferred == "wobble" assert opts.deferredoption == "wobble"