Repository: sympy/sympy
Pull request: _print_Mul : parenthesize non-evaluated Pow with `exp=-1`
Fixes #14160 
The issue occurs only for non-evaluated `Pow` with exponent `-1`.  `_print_Mul` prints such terms as it is  (like `Pow(Mul(a,a,evaluate=False), -1, evaluate=False)` gets printed to `a*a` in the denominator)
#### Brief description of what is fixed or changed
I just added a list `pow_paran` which takes account of all such vulnerable `pow` and paranthesize them explicitly. `isinstance(item.base, Mul)` has been included because `Add` (such as `y+1`) wont face this issue(there precedence matches negative integer with a value of `40`).
I was not able to find explicit tests for `Mul`, so I added them inside `test_python.py`.

Output in this branch :
```
>>> Mul(-2, u, Pow(Mul(a,a,evaluate=False), -1, evaluate=False), evaluate=False)
-2*u/(a*a)
>>> Mul(-2, u, Pow(Mul(a,b,a,evaluate=False), -1, evaluate=False), evaluate=False)
-2*u/(a*a*b)
```


Identical changes are added to `CodePrinter`, `JuliaCodePrinter` and `OctaveCodePrinter`

Problem statement:
Necessary parenthesis in printing of some multiplications
Reproducer:

```
from sympy import *

a = Symbol('a')
u = Symbol('u')

a2inv = Pow(Mul(a,a,evaluate=False), -1, evaluate=False)
d = Mul(-2, u, a2inv, evaluate=False)

print("This should be -2*u/(a*a)")
print(d)
```

Output:

```
This should be -2*u/(a*a)
-2*u/a*a
```

The evaluate=False's are necessary because this is being used in a code-generation context, and the desired code is ``float lhs = -2*u/(a*a)`` not ``float lhs = -2*u*pow(a,-2)`` (which promotes the operations to double precision).

Python 3.6
Sympy Version: latest master (sympy-1.1.1-2784-g98d5dd9) but present before that.
Also occurs (importantly, in this case) in the C and Python code generation printers (which duplicate a lot of the logic in print_Mul, so may possibly need duplicated fixes).

Hints from issue discussion:

Commit message: parenthesize non-evaluated Pow with exp=-1

Patch to review:
diff --git a/sympy/printing/str.py b/sympy/printing/str.py
index 0baefe0f7fc2..16899ec94224 100644
--- a/sympy/printing/str.py
+++ b/sympy/printing/str.py
@@ -270,6 +270,8 @@ def _print_Mul(self, expr):
         a = []  # items in the numerator
         b = []  # items that are in the denominator (if any)
 
+        pow_paran=[]  # Will collect all pow with more than one base element and exp = -1
+
         if self.order not in ('old', 'none'):
             args = expr.as_ordered_factors()
         else:
@@ -282,6 +284,8 @@ def _print_Mul(self, expr):
                 if item.exp != -1:
                     b.append(Pow(item.base, -item.exp, evaluate=False))
                 else:
+                    if len(item.args[0].args) != 1 and isinstance(item.base, Mul):   # To avoid situations like #14160
+                        pow_paran.append(item)
                     b.append(Pow(item.base, -item.exp))
             elif item.is_Rational and item is not S.Infinity:
                 if item.p != 1:
@@ -296,6 +300,11 @@ def _print_Mul(self, expr):
         a_str = [self.parenthesize(x, prec, strict=False) for x in a]
         b_str = [self.parenthesize(x, prec, strict=False) for x in b]
 
+        # To parenthesize Pow with exp = -1 and having more than one Symbol
+        for item in pow_paran:
+            if item.base in b:
+                b_str[b.index(item.base)] = "(%s)" % b_str[b.index(item.base)]
+
         if len(b) == 0:
             return sign + '*'.join(a_str)
         elif len(b) == 1:
diff --git a/sympy/printing/tests/test_python.py b/sympy/printing/tests/test_python.py
index b059fbdeddaa..327e463d7733 100644
--- a/sympy/printing/tests/test_python.py
+++ b/sympy/printing/tests/test_python.py
@@ -43,6 +43,9 @@ def test_python_basic():
         "y = Symbol('y')\nx = Symbol('x')\ne = 1 - 3*y/(2*x)"]
 
     # Multiplication
+    from sympy import Mul, Pow
+    assert python(Mul(-2, x, Pow(Mul(y,y,evaluate=False), -1, evaluate=False)
+        , evaluate=False)) == "x = Symbol('x')\ny = Symbol('y')\ne = -2*x/(y*y)"
     assert python(x/y) == "x = Symbol('x')\ny = Symbol('y')\ne = x/y"
     assert python(-x/y) == "x = Symbol('x')\ny = Symbol('y')\ne = -x/y"
     assert python((x + 2)/y) in [


Code review comments:
diff_hunk: @@ -43,6 +43,9 @@ def test_python_basic():
         "y = Symbol('y')\nx = Symbol('x')\ne = 1 - 3*y/(2*x)"]
 
     # Multiplication
+    from sympy import Mul, Pow
+    assert python(Mul(-2, x, Pow(Mul(y,y,evaluate=False), -1, evaluate=False)
path: sympy/printing/tests/test_python.py
text: @user1:
Why did you add a test to the Python printer? The fix is in the str printer, so the test should go in the str printer tests.

diff_hunk: @@ -270,6 +270,8 @@ def _print_Mul(self, expr):
         a = []  # items in the numerator
         b = []  # items that are in the denominator (if any)
 
+        pow_paran=[]  # Will collect all pow with more than one base element and exp = -1
path: sympy/printing/str.py
text: @user1:
Should this be called pow_paren (with an e)?