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

1""" 

2A few practical conventions common to all printers. 

3""" 

4 

5import re 

6 

7from collections.abc import Iterable 

8from sympy.core.function import Derivative 

9 

10_name_with_digits_p = re.compile(r'^([^\W\d_]+)(\d+)$', re.U) 

11 

12 

13def split_super_sub(text): 

14 """Split a symbol name into a name, superscripts and subscripts 

15 

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. 

21 

22 Examples 

23 ======== 

24 

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']) 

30 

31 """ 

32 if not text: 

33 return text, [], [] 

34 

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.") 

62 

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) 

69 

70 return name, supers, subs 

71 

72 

73def requires_partial(expr): 

74 """Return whether a partial derivative symbol is required for printing 

75 

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 """ 

81 

82 if isinstance(expr, Derivative): 

83 return requires_partial(expr.expr) 

84 

85 if not isinstance(expr.free_symbols, Iterable): 

86 return len(set(expr.variables)) > 1 

87 

88 return sum(not s.is_integer for s in expr.free_symbols) > 1