hdl.ir: typecheck convert(ports=) more carefully.

The `ports` argument to the `convert` functions is a frequent hotspot of
beginner issues. Check to make sure it is either a list or a tuple, and
give an appropriately helpful error message if not.

Fixes #362.
This commit is contained in:
awygle 2020-04-24 14:15:00 -07:00 committed by GitHub
parent fd7f69f7a5
commit f2b4584b34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 16 additions and 0 deletions

View file

@ -532,6 +532,12 @@ class Fragment:
if ports is None:
fragment._propagate_ports(ports=(), all_undef_as_ports=True)
else:
if not isinstance(ports, tuple) and not isinstance(ports, list):
msg = "`ports` must be either a list or a tuple, not {!r}"\
.format(ports)
if isinstance(ports, Value):
msg += " (did you mean `ports=(<signal>,)`, rather than `ports=<signal>`?)"
raise TypeError(msg)
mapped_ports = []
# Lower late bound signals like ClockSignal() to ports.
port_lowerer = DomainLowerer(fragment.domains)

View file

@ -304,6 +304,16 @@ class FragmentPortsTestCase(FHDLTestCase):
msg="Only signals may be added as ports, not (const 1'd1)"):
f.prepare(ports=(Const(1),))
def test_port_not_iterable(self):
f = Fragment()
with self.assertRaises(TypeError,
msg="`ports` must be either a list or a tuple, not 1"):
f.prepare(ports=1)
with self.assertRaises(TypeError,
msg="`ports` must be either a list or a tuple, not (const 1'd1)" +
" (did you mean `ports=(<signal>,)`, rather than `ports=<signal>`?)"):
f.prepare(ports=Const(1))
class FragmentDomainsTestCase(FHDLTestCase):
def test_iter_signals(self):
cd1 = ClockDomain()