Coverage for /usr/lib/python3/dist-packages/sympy/printing/conventions.py: 14%
44 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""
2A few practical conventions common to all printers.
3"""
5import re
7from collections.abc import Iterable
8from sympy.core.function import Derivative
10_name_with_digits_p = re.compile(r'^([^\W\d_]+)(\d+)$', re.U)
13def split_super_sub(text):
14 """Split a symbol name into a name, superscripts and subscripts
16 The first part of the symbol name is considered to be its actual
17 'name', followed by super- and subscripts. Each superscript is
18 preceded with a "^" character or by "__". Each subscript is preceded
19 by a "_" character. The three return values are the actual name, a
20 list with superscripts and a list with subscripts.
22 Examples
23 ========
25 >>> from sympy.printing.conventions import split_super_sub
26 >>> split_super_sub('a_x^1')
27 ('a', ['1'], ['x'])
28 >>> split_super_sub('var_sub1__sup_sub2')
29 ('var', ['sup'], ['sub1', 'sub2'])
31 """
32 if not text:
33 return text, [], []
35 pos = 0
36 name = None
37 supers = []
38 subs = []
39 while pos < len(text):
40 start = pos + 1
41 if text[pos:pos + 2] == "__":
42 start += 1
43 pos_hat = text.find("^", start)
44 if pos_hat < 0:
45 pos_hat = len(text)
46 pos_usc = text.find("_", start)
47 if pos_usc < 0:
48 pos_usc = len(text)
49 pos_next = min(pos_hat, pos_usc)
50 part = text[pos:pos_next]
51 pos = pos_next
52 if name is None:
53 name = part
54 elif part.startswith("^"):
55 supers.append(part[1:])
56 elif part.startswith("__"):
57 supers.append(part[2:])
58 elif part.startswith("_"):
59 subs.append(part[1:])
60 else:
61 raise RuntimeError("This should never happen.")
63 # Make a little exception when a name ends with digits, i.e. treat them
64 # as a subscript too.
65 m = _name_with_digits_p.match(name)
66 if m:
67 name, sub = m.groups()
68 subs.insert(0, sub)
70 return name, supers, subs
73def requires_partial(expr):
74 """Return whether a partial derivative symbol is required for printing
76 This requires checking how many free variables there are,
77 filtering out the ones that are integers. Some expressions do not have
78 free variables. In that case, check its variable list explicitly to
79 get the context of the expression.
80 """
82 if isinstance(expr, Derivative):
83 return requires_partial(expr.expr)
85 if not isinstance(expr.free_symbols, Iterable):
86 return len(set(expr.variables)) > 1
88 return sum(not s.is_integer for s in expr.free_symbols) > 1