From 24b7682f6b36c74eb3e9baaa74d4eabf77143cff Mon Sep 17 00:00:00 2001
From: Rafal Skolasinski <r.j.skolasinski@gmail.com>
Date: Sun, 3 Dec 2017 12:13:09 +0100
Subject: [PATCH] continuum: check if symbol names are valid identifiers

A regression in Sympy causes symbols that are invalid
identifiers to be created, e.g. '-B_x'. While we cannot
fix Sympy's behaviour, we can at least shield our users
from its quirks.

Closes #179.
---
 kwant/continuum/discretizer.py            | 10 ++++++++++
 kwant/continuum/tests/test_discretizer.py | 13 +++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/kwant/continuum/discretizer.py b/kwant/continuum/discretizer.py
index cac78399..119a4c11 100644
--- a/kwant/continuum/discretizer.py
+++ b/kwant/continuum/discretizer.py
@@ -6,6 +6,7 @@
 # the file AUTHORS.rst at the top-level directory of this distribution and at
 # http://kwant-project.org/authors.
 
+from keyword import iskeyword
 from collections import defaultdict
 import itertools
 
@@ -598,6 +599,15 @@ def _builder_value(expr, coords, grid_spacing, onsite,
     # as arguments to the value function
     arg_names = set.union({s.name for s in const_symbols},
                                 {str(k.func) for k in map_func_calls})
+
+    # check if all argument names are valid python identifiers
+    for name in arg_names:
+        if not (name.isidentifier() and not iskeyword(name)):
+            raise ValueError("Invalid name in used symbols: {}\n"
+                             "Names of symbols used in Hamiltonian "
+                             "must be valid Python identifiers and "
+                             "may not be keywords".format(name))
+
     arg_names = ', '.join(sorted(arg_names))
 
     if (not arg_names) and (coords is None):
diff --git a/kwant/continuum/tests/test_discretizer.py b/kwant/continuum/tests/test_discretizer.py
index 98a5cec8..47efbe3a 100644
--- a/kwant/continuum/tests/test_discretizer.py
+++ b/kwant/continuum/tests/test_discretizer.py
@@ -509,3 +509,16 @@ def test_numeric_functions_with_parameter():
                         rhs = f_num
 
                     assert np.allclose(lhs, rhs)
+
+
+@pytest.mark.parametrize('name', [
+    '1',
+    '1a',
+    '-a',
+    '+a',
+    'while',
+    'for'
+])
+def test_check_symbol_names(name):
+    with pytest.raises(ValueError):
+        discretize(sympy.Symbol(name), 'x')
-- 
GitLab