One function

Switch in construct/construct

The author described this change as Switch fixed (bug #357). It counts as a record because the check below fails on the code as it stood at 95c7fff40 and passes on 599501980, with nothing else changed between the two runs.

Fix saved2017-08-29
Sharing licenceMIT · LICENSE
Change size+5 11

What the code was meant to do, written into the code itself as a docstring

A conditional branch. Switch will choose the case to follow based on the return value of keyfunc. If no case is matched, and no default value is given, SwitchError will be raised. .. warning:: You can use Embedded(Switch(...)) but not Switch(Embedded(...)). Sames applies to If and IfThenElse macros. :param keyfunc: a context function that returns a key which will choose a case, or a constant :param cases: a dictionary mapping keys to subcons :param default: a default field to use when the key is not found in the cases. if not supplied, an exception will be raised when the key is not found. Pass can be used for do-nothing :param includekey: whether to include the key in the return value of parsing, defualt is False Example::

The change

1616 >>> Switch(this.n, { 1:Byte, 2:Int32ub }).build(5, dict(n=2))
1717 b'\x00\x00\x00\x05'
1818 """
19- @singleton
20- class NoDefault(Construct):
21- def _parse(self, stream, context, path):
22- raise SwitchError("no default case defined")
23- def _build(self, obj, stream, context, path):
24- raise SwitchError("no default case defined")
25- def _sizeof(self, context, path):
26- raise SwitchError("no default case defined")
27-
2819 __slots__ = ["subcons", "keyfunc", "cases", "default", "includekey"]
2920 def __init__(self, keyfunc, cases, default=NoDefault, includekey=False):
3021 super(Switch, self).__init__()
3223 self.cases = cases
3324 self.default = default
3425 self.includekey = includekey
35- self.flagbuildnone = all(sc.flagbuildnone for sc in cases.values())
36- self.flagembedded = all(sc.flagembedded for sc in cases.values())
26+ allcases = list(cases.values())
27+ if default is not NoDefault:
28+ allcases.append(default)
29+ self.flagbuildnone = all(sc.flagbuildnone for sc in allcases)
30+ self.flagembedded = all(sc.flagembedded for sc in allcases)
3731 def _parse(self, stream, context, path):
3832 key = self.keyfunc(context) if callable(self.keyfunc) else self.keyfunc
3933 obj = self.cases.get(key, self.default)._parse(stream, context, path)

The check that tells the two apart

failpass·tests/test_all.py::TestCore::test_switch_issue_357

Check file tests/test_all.py, taken without changes from the fix and copied onto the older code, so the exact same check runs against both versions.

Origin and history

The code before it95c7fff4099220fa2563ca79b554b62f6e6c0bde
Broken version dated2017-08-29
Moduleconstruct.core
Units changedSwitch
Fingerprint1d8d1240e4b1ab5d
Checked2026-08-18 by goldset/0.1

Every field above is generated by our program. None of it is written by hand.

Other bugs found in construct/construct