Repository: einsteinpy/einsteinpy

Issue:
Error handling for `config` variable in Tensor class
🐞 **Problem**
`config` variable denotes the contravariance-covariance of the tensor. It's lenrth is supposed to be equal to that the order of the tensor. 

🎯 **Goal**

  - [x] Introduce this error handling into `Tensor` class in `symbolic` module
  - [ ] After fixing this, fix [this](https://github.com/einsteinpy/einsteinpy/blob/30a68526b9cea7b21b3f81c0b89d2619c3e19440/docs/source/examples/Lambdify%20symbolic%20calculation.ipynb) example

💡 **Possible solutions**

  - `config` length should be equal to length of the tuple returned by `arr.shape`

📋  **Steps to solve the problem**

 * Comment below about what you've started working on.
 * Add, commit, push your changes
 * Submit a pull request.
 * Add this in the comments:
 	- `Addresses #<put issue number here>` if you are partially fixing the issue.
 	- `Fixes #<put issue number here>` if you are completely fixing the issue.
 * Ask for a review in comments section of pull request
 * Celebrate your contribution to this project 🎉


Discussion:
ritzvik: For passing quality checks https://docs.einsteinpy.org/en/latest/dev_guide.html#code-linting
I'll review in a while :)

Thanks for the PR @bibek22 

Patch:
diff --git a/src/einsteinpy/symbolic/tensor.py b/src/einsteinpy/symbolic/tensor.py
index 64ef407..238fd0b 100644
--- a/src/einsteinpy/symbolic/tensor.py
+++ b/src/einsteinpy/symbolic/tensor.py
@@ -90,6 +90,8 @@ class Tensor:
             Raised when arr is not a list or sympy array
         TypeError
             Raised when config is not of type str or contains characters other than 'l' or 'u'
+        ValueError
+            Raised when ``config`` implies order of Tensor different than that indicated by shape of ``arr``
 
         """
 
@@ -106,6 +108,12 @@ class Tensor:
             raise TypeError(
                 "config is either not of type 'str' or does contain characters other than 'l' or 'u'"
             )
+        if len(self.arr.shape) != len(config):
+            raise ValueError(
+                "invalid shape of array for tensor of order implied by config: '{}'".format(
+                    config
+                )
+            )
         self.name = name
 
     @property
diff --git a/src/einsteinpy/symbolic/weyl.py b/src/einsteinpy/symbolic/weyl.py
index 266e066..9846261 100644
--- a/src/einsteinpy/symbolic/weyl.py
+++ b/src/einsteinpy/symbolic/weyl.py
@@ -100,7 +100,7 @@ class WeylTensor(BaseRelativityTensor):
             return cls(C, metric.syms, config="llll", parent_metric=metric)
         if metric.dims == 3:
             return cls(
-                sympy.Array(np.zeros((3, 3), dtype=int)),
+                sympy.Array(np.zeros((3, 3, 3, 3), dtype=int)),
                 metric.syms,
                 config="llll",
                 parent_metric=metric,


Test patch:
diff --git a/src/einsteinpy/tests/test_symbolic/test_tensor.py b/src/einsteinpy/tests/test_symbolic/test_tensor.py
index 1d5bc4a..6e6b6a6 100644
--- a/src/einsteinpy/tests/test_symbolic/test_tensor.py
+++ b/src/einsteinpy/tests/test_symbolic/test_tensor.py
@@ -82,8 +82,8 @@ def test_Tensor():
     x, y, z = symbols("x y z")
     test_list = [[[x, y], [y, sin(2 * z) - 2 * sin(z) * cos(z)]], [[z ** 2, x], [y, z]]]
     test_arr = Array(test_list)
-    obj1 = Tensor(test_arr)
-    obj2 = Tensor(test_list)
+    obj1 = Tensor(test_arr, config="ull")
+    obj2 = Tensor(test_list, config="ull")
     assert obj1.tensor() == obj2.tensor()
     assert isinstance(obj1.tensor(), Array)
 
@@ -91,7 +91,7 @@ def test_Tensor():
 def test_Tensor_simplify():
     x, y, z = symbols("x y z")
     test_list = [[[x, y], [y, sin(2 * z) - 2 * sin(z) * cos(z)]], [[z ** 2, x], [y, z]]]
-    obj = Tensor(test_list)
+    obj = Tensor(test_list, config="ull")
     # with set_self = False
     assert obj.simplify(set_self=False)[0, 1, 1] == 0
     assert not obj.tensor()[0, 1, 1] == 0
@@ -103,7 +103,7 @@ def test_Tensor_simplify():
 def test_Tensor_getitem():
     x, y, z = symbols("x y z")
     test_list = [[[x, y], [y, sin(2 * z) - 2 * sin(z) * cos(z)]], [[z ** 2, x], [y, z]]]
-    obj = Tensor(test_list)
+    obj = Tensor(test_list, config="ull")
     n = 2
     for i in range(n ** 3):
         p, q, r = i % n, int(i / n) % n, int(i / n ** 2) % n
@@ -113,7 +113,7 @@ def test_Tensor_getitem():
 def test_Tensor_str():
     x, y, z = symbols("x y z")
     test_list = [[[x, y], [y, x]], [[z, x], [y, z]]]
-    obj1 = Tensor(test_list, name="Test")
+    obj1 = Tensor(test_list, config="ull", name="Test")
     assert "object at 0x" not in str(obj1)
     assert "Test" in str(obj1)
 
@@ -121,7 +121,7 @@ def test_Tensor_str():
 def test_Tensor_repr():
     x, y, z = symbols("x y z")
     test_list = [[[x, y], [y, sin(2 * z) - 2 * sin(z) * cos(z)]], [[z ** 2, x], [y, z]]]
-    obj1 = Tensor(test_list)
+    obj1 = Tensor(test_list, config="ull")
     machine_representation = repr(obj1)
     assert not "object at 0x" in machine_representation
 
@@ -148,6 +148,14 @@ def test_TypeError3():
 
 @xfail(raises=ValueError, strict=True)
 def test_ValueError1():
+    x, y = symbols("x y")
+    test_list = [[x, y], [y, x]]
+    # pass array with shape (2,2) when (2,2,2) implied by argument syms
+    obj = Tensor(test_list, config="lll")
+
+
+@xfail(raises=ValueError, strict=True)
+def test_ValueError2():
     x, y, z = symbols("x y z")
     test_list = [[x, y], [y, x]]
     # pass 2x2 array when 3x3 implied by argument syms
@@ -214,7 +222,7 @@ def test_BaseRelativityTensor_automatic_calculation_of_free_variables():
 
 @pytest.mark.parametrize("scalar", [11.89, y * z + 5])
 def test_tensor_scalar(scalar):
-    scalar_tensor = Tensor(scalar)
+    scalar_tensor = Tensor(scalar, config="")
     assert scalar_tensor.tensor().rank() == 0
 
 
diff --git a/src/einsteinpy/tests/test_symbolic/test_weyl.py b/src/einsteinpy/tests/test_symbolic/test_weyl.py
index 9b7fc6d..53dd5a1 100644
--- a/src/einsteinpy/tests/test_symbolic/test_weyl.py
+++ b/src/einsteinpy/tests/test_symbolic/test_weyl.py
@@ -75,7 +75,7 @@ def test_weyl_dim3():
     syms = sympy.symbols(symbolstr)
     metric = MetricTensor(list_metric, syms)
     obj = WeylTensor.from_metric(metric).arr
-    assert obj == sympy.Array(np.zeros((3, 3), dtype=int))
+    assert obj == sympy.Array(np.zeros((3, 3, 3, 3), dtype=int))
 
 
 def test_weyl_conformal_rescaling():