diff --git a/testsuite/pytests/sli2py_regressions/test_ticket_349.py b/testsuite/pytests/sli2py_regressions/test_ticket_349.py
new file mode 100644
index 0000000000..33864c8ffa
--- /dev/null
+++ b/testsuite/pytests/sli2py_regressions/test_ticket_349.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+#
+# test_ticket_349.py
+#
+# This file is part of NEST.
+#
+# Copyright (C) 2004 The NEST Initiative
+#
+# NEST is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# NEST is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with NEST. If not, see .
+
+import nest
+import pytest
+
+
+@pytest.mark.skipif(not nest.ll_api.sli_func("statusdict/have_gsl ::"), reason="Requires GSL")
+def test_ticket_349():
+ """
+ Regression test for Ticket #349.
+
+ Ensure that a multimeter handles illegal entries in the record_from list gracefully
+ and that an empty record_from list does not cause the simulation to crash.
+
+ Author: Hans Ekkehard Plesser, 2009-07-02
+ """
+
+ # First test: Connect with illegal entry
+ nest.ResetKernel()
+ mm = nest.Create("multimeter")
+ n = nest.Create("iaf_cond_alpha")
+
+ r = nest.GetDefaults("iaf_cond_alpha")["recordables"]
+ rfail = [f"{item}_foo" for item in r]
+
+ mm.set(record_from=rfail)
+
+ with pytest.raises(Exception):
+ nest.Connect(mm, n)
+
+ # Second test: Connect with illegal entry first, then retry with legal list
+ nest.ResetKernel()
+ mm = nest.Create("multimeter")
+ n = nest.Create("iaf_cond_alpha")
+
+ r = nest.GetDefaults("iaf_cond_alpha")["recordables"]
+ rfail = [f"{item}_foo" for item in r]
+
+ mm.set(record_from=rfail)
+
+ try:
+ nest.Connect(mm, n)
+ except Exception:
+ pass
+
+ mm.set(record_from=r)
+ nest.Connect(mm, n)
+
+ # Third test: Connect multimeter with empty list, then simulate
+ nest.ResetKernel()
+ mm = nest.Create("multimeter")
+ n = nest.Create("iaf_cond_alpha")
+ mm.set(record_from=[])
+ nest.Connect(mm, n)
+
+ nest.Simulate(10.0)
+
+ events = mm.get("events")
+ assert len(events["senders"]) == 0
+ assert len(events["times"]) == 0
diff --git a/testsuite/regressiontests/ticket-349.sli b/testsuite/regressiontests/ticket-349.sli
deleted file mode 100644
index 863cbca7b2..0000000000
--- a/testsuite/regressiontests/ticket-349.sli
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * ticket-349.sli
- *
- * This file is part of NEST.
- *
- * Copyright (C) 2004 The NEST Initiative
- *
- * NEST is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * NEST is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with NEST. If not, see .
- *
- */
-
-/*
- * Regression test for Ticket #349.
- *
- * Ensure that multimeter connects with entries in record_from list that do not match target node
- * do not leave any trace in target node, so that attempt with corrected list succeed.
- *
- * Ensure that simulation does not crash when multimeter has empty record_from list.
- *
- * Hans Ekkehard Plesser, 2009-07-02
- *
- */
-
-(unittest) run
-/unittest using
-
-% The following test needs the model iaf_cond_alpha, so
-% this test should only run if we have GSL
-skip_if_without_gsl
-
-% First test: connect with illegal entry
-{
- ResetKernel
-
- /mm /multimeter Create def
- /n /iaf_cond_alpha Create def
-
- /r n /recordables get def
-
- % take first element, add _foo to create string not in recordable list, append
- /rfail r dup (foo_) exch { cvs join (_) join } Fold cvlit append def
-
- mm << /record_from rfail >> SetStatus
-
- % shall provoke error
- mm n Connect
-} fail_or_die
-
-% Second test: connect with illegal entry first, then re-try with legal list
-{
- ResetKernel
-
- /mm /multimeter Create def
- /n /iaf_cond_alpha Create def
-
- /r n /recordables get def
-
- % take first element, join all recordables to create non-existing entry
- /rfail r dup (foo_) exch { cvs join (_) join } Fold cvlit append def
-
- mm << /record_from rfail >> SetStatus
-
- % shall provoke error, we must catch it manually
- mark
- { mm n Connect }
- stopped
- {
- % remove error code
- errordict /message undef
- errordict /command undef
- errordict begin /newerror false def end
-
- % clear stack
- counttomark npop pop % need to pop mark separately
- }
- {
- % we should have gotten an error, we didn't, so we raise one
- /ticket-349-test-2 /ExpectedErrorNotRaised raiseerror
- }
- ifelse
-
- % try with correct list
- mm << /record_from r >> SetStatus
- mm n Connect
-
-} pass_or_die
-
-% Third test: connect multimeter with empty list, then simulate. Must not crash.
-{
- ResetKernel
-
- /mm /multimeter Create def
- /n /iaf_cond_alpha Create def
- mm n Connect
-
- 10 Simulate
-
- % see if we can get an events dictionary, must contain empty times and senders arrays
- mm /events get
- dup /senders get cva length 0 eq
- exch /times get cva length 0 eq
- and
-
-} assert_or_die