Spaces:
Sleeping
Sleeping
"""Miscellaneous stuff that does not really fit anywhere else.""" | |
from __future__ import annotations | |
import operator | |
import sys | |
import os | |
import re as _re | |
import struct | |
from textwrap import fill, dedent | |
class Undecidable(ValueError): | |
# an error to be raised when a decision cannot be made definitively | |
# where a definitive answer is needed | |
pass | |
def filldedent(s, w=70, **kwargs): | |
""" | |
Strips leading and trailing empty lines from a copy of ``s``, then dedents, | |
fills and returns it. | |
Empty line stripping serves to deal with docstrings like this one that | |
start with a newline after the initial triple quote, inserting an empty | |
line at the beginning of the string. | |
Additional keyword arguments will be passed to ``textwrap.fill()``. | |
See Also | |
======== | |
strlines, rawlines | |
""" | |
return '\n' + fill(dedent(str(s)).strip('\n'), width=w, **kwargs) | |
def strlines(s, c=64, short=False): | |
"""Return a cut-and-pastable string that, when printed, is | |
equivalent to the input. The lines will be surrounded by | |
parentheses and no line will be longer than c (default 64) | |
characters. If the line contains newlines characters, the | |
`rawlines` result will be returned. If ``short`` is True | |
(default is False) then if there is one line it will be | |
returned without bounding parentheses. | |
Examples | |
======== | |
>>> from sympy.utilities.misc import strlines | |
>>> q = 'this is a long string that should be broken into shorter lines' | |
>>> print(strlines(q, 40)) | |
( | |
'this is a long string that should be b' | |
'roken into shorter lines' | |
) | |
>>> q == ( | |
... 'this is a long string that should be b' | |
... 'roken into shorter lines' | |
... ) | |
True | |
See Also | |
======== | |
filldedent, rawlines | |
""" | |
if not isinstance(s, str): | |
raise ValueError('expecting string input') | |
if '\n' in s: | |
return rawlines(s) | |
q = '"' if repr(s).startswith('"') else "'" | |
q = (q,)*2 | |
if '\\' in s: # use r-string | |
m = '(\nr%s%%s%s\n)' % q | |
j = '%s\nr%s' % q | |
c -= 3 | |
else: | |
m = '(\n%s%%s%s\n)' % q | |
j = '%s\n%s' % q | |
c -= 2 | |
out = [] | |
while s: | |
out.append(s[:c]) | |
s=s[c:] | |
if short and len(out) == 1: | |
return (m % out[0]).splitlines()[1] # strip bounding (\n...\n) | |
return m % j.join(out) | |
def rawlines(s): | |
"""Return a cut-and-pastable string that, when printed, is equivalent | |
to the input. Use this when there is more than one line in the | |
string. The string returned is formatted so it can be indented | |
nicely within tests; in some cases it is wrapped in the dedent | |
function which has to be imported from textwrap. | |
Examples | |
======== | |
Note: because there are characters in the examples below that need | |
to be escaped because they are themselves within a triple quoted | |
docstring, expressions below look more complicated than they would | |
be if they were printed in an interpreter window. | |
>>> from sympy.utilities.misc import rawlines | |
>>> from sympy import TableForm | |
>>> s = str(TableForm([[1, 10]], headings=(None, ['a', 'bee']))) | |
>>> print(rawlines(s)) | |
( | |
'a bee\\n' | |
'-----\\n' | |
'1 10 ' | |
) | |
>>> print(rawlines('''this | |
... that''')) | |
dedent('''\\ | |
this | |
that''') | |
>>> print(rawlines('''this | |
... that | |
... ''')) | |
dedent('''\\ | |
this | |
that | |
''') | |
>>> s = \"\"\"this | |
... is a triple ''' | |
... \"\"\" | |
>>> print(rawlines(s)) | |
dedent(\"\"\"\\ | |
this | |
is a triple ''' | |
\"\"\") | |
>>> print(rawlines('''this | |
... that | |
... ''')) | |
( | |
'this\\n' | |
'that\\n' | |
' ' | |
) | |
See Also | |
======== | |
filldedent, strlines | |
""" | |
lines = s.split('\n') | |
if len(lines) == 1: | |
return repr(lines[0]) | |
triple = ["'''" in s, '"""' in s] | |
if any(li.endswith(' ') for li in lines) or '\\' in s or all(triple): | |
rv = [] | |
# add on the newlines | |
trailing = s.endswith('\n') | |
last = len(lines) - 1 | |
for i, li in enumerate(lines): | |
if i != last or trailing: | |
rv.append(repr(li + '\n')) | |
else: | |
rv.append(repr(li)) | |
return '(\n %s\n)' % '\n '.join(rv) | |
else: | |
rv = '\n '.join(lines) | |
if triple[0]: | |
return 'dedent("""\\\n %s""")' % rv | |
else: | |
return "dedent('''\\\n %s''')" % rv | |
ARCH = str(struct.calcsize('P') * 8) + "-bit" | |
# XXX: PyPy does not support hash randomization | |
HASH_RANDOMIZATION = getattr(sys.flags, 'hash_randomization', False) | |
_debug_tmp: list[str] = [] | |
_debug_iter = 0 | |
def debug_decorator(func): | |
"""If SYMPY_DEBUG is True, it will print a nice execution tree with | |
arguments and results of all decorated functions, else do nothing. | |
""" | |
from sympy import SYMPY_DEBUG | |
if not SYMPY_DEBUG: | |
return func | |
def maketree(f, *args, **kw): | |
global _debug_tmp | |
global _debug_iter | |
oldtmp = _debug_tmp | |
_debug_tmp = [] | |
_debug_iter += 1 | |
def tree(subtrees): | |
def indent(s, variant=1): | |
x = s.split("\n") | |
r = "+-%s\n" % x[0] | |
for a in x[1:]: | |
if a == "": | |
continue | |
if variant == 1: | |
r += "| %s\n" % a | |
else: | |
r += " %s\n" % a | |
return r | |
if len(subtrees) == 0: | |
return "" | |
f = [] | |
for a in subtrees[:-1]: | |
f.append(indent(a)) | |
f.append(indent(subtrees[-1], 2)) | |
return ''.join(f) | |
# If there is a bug and the algorithm enters an infinite loop, enable the | |
# following lines. It will print the names and parameters of all major functions | |
# that are called, *before* they are called | |
#from functools import reduce | |
#print("%s%s %s%s" % (_debug_iter, reduce(lambda x, y: x + y, \ | |
# map(lambda x: '-', range(1, 2 + _debug_iter))), f.__name__, args)) | |
r = f(*args, **kw) | |
_debug_iter -= 1 | |
s = "%s%s = %s\n" % (f.__name__, args, r) | |
if _debug_tmp != []: | |
s += tree(_debug_tmp) | |
_debug_tmp = oldtmp | |
_debug_tmp.append(s) | |
if _debug_iter == 0: | |
print(_debug_tmp[0]) | |
_debug_tmp = [] | |
return r | |
def decorated(*args, **kwargs): | |
return maketree(func, *args, **kwargs) | |
return decorated | |
def debug(*args): | |
""" | |
Print ``*args`` if SYMPY_DEBUG is True, else do nothing. | |
""" | |
from sympy import SYMPY_DEBUG | |
if SYMPY_DEBUG: | |
print(*args, file=sys.stderr) | |
def debugf(string, args): | |
""" | |
Print ``string%args`` if SYMPY_DEBUG is True, else do nothing. This is | |
intended for debug messages using formatted strings. | |
""" | |
from sympy import SYMPY_DEBUG | |
if SYMPY_DEBUG: | |
print(string%args, file=sys.stderr) | |
def find_executable(executable, path=None): | |
"""Try to find 'executable' in the directories listed in 'path' (a | |
string listing directories separated by 'os.pathsep'; defaults to | |
os.environ['PATH']). Returns the complete filename or None if not | |
found | |
""" | |
from .exceptions import sympy_deprecation_warning | |
sympy_deprecation_warning( | |
""" | |
sympy.utilities.misc.find_executable() is deprecated. Use the standard | |
library shutil.which() function instead. | |
""", | |
deprecated_since_version="1.7", | |
active_deprecations_target="deprecated-find-executable", | |
) | |
if path is None: | |
path = os.environ['PATH'] | |
paths = path.split(os.pathsep) | |
extlist = [''] | |
if os.name == 'os2': | |
(base, ext) = os.path.splitext(executable) | |
# executable files on OS/2 can have an arbitrary extension, but | |
# .exe is automatically appended if no dot is present in the name | |
if not ext: | |
executable = executable + ".exe" | |
elif sys.platform == 'win32': | |
pathext = os.environ['PATHEXT'].lower().split(os.pathsep) | |
(base, ext) = os.path.splitext(executable) | |
if ext.lower() not in pathext: | |
extlist = pathext | |
for ext in extlist: | |
execname = executable + ext | |
if os.path.isfile(execname): | |
return execname | |
else: | |
for p in paths: | |
f = os.path.join(p, execname) | |
if os.path.isfile(f): | |
return f | |
return None | |
def func_name(x, short=False): | |
"""Return function name of `x` (if defined) else the `type(x)`. | |
If short is True and there is a shorter alias for the result, | |
return the alias. | |
Examples | |
======== | |
>>> from sympy.utilities.misc import func_name | |
>>> from sympy import Matrix | |
>>> from sympy.abc import x | |
>>> func_name(Matrix.eye(3)) | |
'MutableDenseMatrix' | |
>>> func_name(x < 1) | |
'StrictLessThan' | |
>>> func_name(x < 1, short=True) | |
'Lt' | |
""" | |
alias = { | |
'GreaterThan': 'Ge', | |
'StrictGreaterThan': 'Gt', | |
'LessThan': 'Le', | |
'StrictLessThan': 'Lt', | |
'Equality': 'Eq', | |
'Unequality': 'Ne', | |
} | |
typ = type(x) | |
if str(typ).startswith("<type '"): | |
typ = str(typ).split("'")[1].split("'")[0] | |
elif str(typ).startswith("<class '"): | |
typ = str(typ).split("'")[1].split("'")[0] | |
rv = getattr(getattr(x, 'func', x), '__name__', typ) | |
if '.' in rv: | |
rv = rv.split('.')[-1] | |
if short: | |
rv = alias.get(rv, rv) | |
return rv | |
def _replace(reps): | |
"""Return a function that can make the replacements, given in | |
``reps``, on a string. The replacements should be given as mapping. | |
Examples | |
======== | |
>>> from sympy.utilities.misc import _replace | |
>>> f = _replace(dict(foo='bar', d='t')) | |
>>> f('food') | |
'bart' | |
>>> f = _replace({}) | |
>>> f('food') | |
'food' | |
""" | |
if not reps: | |
return lambda x: x | |
D = lambda match: reps[match.group(0)] | |
pattern = _re.compile("|".join( | |
[_re.escape(k) for k, v in reps.items()]), _re.M) | |
return lambda string: pattern.sub(D, string) | |
def replace(string, *reps): | |
"""Return ``string`` with all keys in ``reps`` replaced with | |
their corresponding values, longer strings first, irrespective | |
of the order they are given. ``reps`` may be passed as tuples | |
or a single mapping. | |
Examples | |
======== | |
>>> from sympy.utilities.misc import replace | |
>>> replace('foo', {'oo': 'ar', 'f': 'b'}) | |
'bar' | |
>>> replace("spamham sha", ("spam", "eggs"), ("sha","md5")) | |
'eggsham md5' | |
There is no guarantee that a unique answer will be | |
obtained if keys in a mapping overlap (i.e. are the same | |
length and have some identical sequence at the | |
beginning/end): | |
>>> reps = [ | |
... ('ab', 'x'), | |
... ('bc', 'y')] | |
>>> replace('abc', *reps) in ('xc', 'ay') | |
True | |
References | |
========== | |
.. [1] https://stackoverflow.com/questions/6116978/how-to-replace-multiple-substrings-of-a-string | |
""" | |
if len(reps) == 1: | |
kv = reps[0] | |
if isinstance(kv, dict): | |
reps = kv | |
else: | |
return string.replace(*kv) | |
else: | |
reps = dict(reps) | |
return _replace(reps)(string) | |
def translate(s, a, b=None, c=None): | |
"""Return ``s`` where characters have been replaced or deleted. | |
SYNTAX | |
====== | |
translate(s, None, deletechars): | |
all characters in ``deletechars`` are deleted | |
translate(s, map [,deletechars]): | |
all characters in ``deletechars`` (if provided) are deleted | |
then the replacements defined by map are made; if the keys | |
of map are strings then the longer ones are handled first. | |
Multicharacter deletions should have a value of ''. | |
translate(s, oldchars, newchars, deletechars) | |
all characters in ``deletechars`` are deleted | |
then each character in ``oldchars`` is replaced with the | |
corresponding character in ``newchars`` | |
Examples | |
======== | |
>>> from sympy.utilities.misc import translate | |
>>> abc = 'abc' | |
>>> translate(abc, None, 'a') | |
'bc' | |
>>> translate(abc, {'a': 'x'}, 'c') | |
'xb' | |
>>> translate(abc, {'abc': 'x', 'a': 'y'}) | |
'x' | |
>>> translate('abcd', 'ac', 'AC', 'd') | |
'AbC' | |
There is no guarantee that a unique answer will be | |
obtained if keys in a mapping overlap are the same | |
length and have some identical sequences at the | |
beginning/end: | |
>>> translate(abc, {'ab': 'x', 'bc': 'y'}) in ('xc', 'ay') | |
True | |
""" | |
mr = {} | |
if a is None: | |
if c is not None: | |
raise ValueError('c should be None when a=None is passed, instead got %s' % c) | |
if b is None: | |
return s | |
c = b | |
a = b = '' | |
else: | |
if isinstance(a, dict): | |
short = {} | |
for k in list(a.keys()): | |
if len(k) == 1 and len(a[k]) == 1: | |
short[k] = a.pop(k) | |
mr = a | |
c = b | |
if short: | |
a, b = [''.join(i) for i in list(zip(*short.items()))] | |
else: | |
a = b = '' | |
elif len(a) != len(b): | |
raise ValueError('oldchars and newchars have different lengths') | |
if c: | |
val = str.maketrans('', '', c) | |
s = s.translate(val) | |
s = replace(s, mr) | |
n = str.maketrans(a, b) | |
return s.translate(n) | |
def ordinal(num): | |
"""Return ordinal number string of num, e.g. 1 becomes 1st. | |
""" | |
# modified from https://codereview.stackexchange.com/questions/41298/producing-ordinal-numbers | |
n = as_int(num) | |
k = abs(n) % 100 | |
if 11 <= k <= 13: | |
suffix = 'th' | |
elif k % 10 == 1: | |
suffix = 'st' | |
elif k % 10 == 2: | |
suffix = 'nd' | |
elif k % 10 == 3: | |
suffix = 'rd' | |
else: | |
suffix = 'th' | |
return str(n) + suffix | |
def as_int(n, strict=True): | |
""" | |
Convert the argument to a builtin integer. | |
The return value is guaranteed to be equal to the input. ValueError is | |
raised if the input has a non-integral value. When ``strict`` is True, this | |
uses `__index__ <https://docs.python.org/3/reference/datamodel.html#object.__index__>`_ | |
and when it is False it uses ``int``. | |
Examples | |
======== | |
>>> from sympy.utilities.misc import as_int | |
>>> from sympy import sqrt, S | |
The function is primarily concerned with sanitizing input for | |
functions that need to work with builtin integers, so anything that | |
is unambiguously an integer should be returned as an int: | |
>>> as_int(S(3)) | |
3 | |
Floats, being of limited precision, are not assumed to be exact and | |
will raise an error unless the ``strict`` flag is False. This | |
precision issue becomes apparent for large floating point numbers: | |
>>> big = 1e23 | |
>>> type(big) is float | |
True | |
>>> big == int(big) | |
True | |
>>> as_int(big) | |
Traceback (most recent call last): | |
... | |
ValueError: ... is not an integer | |
>>> as_int(big, strict=False) | |
99999999999999991611392 | |
Input that might be a complex representation of an integer value is | |
also rejected by default: | |
>>> one = sqrt(3 + 2*sqrt(2)) - sqrt(2) | |
>>> int(one) == 1 | |
True | |
>>> as_int(one) | |
Traceback (most recent call last): | |
... | |
ValueError: ... is not an integer | |
""" | |
if strict: | |
try: | |
if isinstance(n, bool): | |
raise TypeError | |
return operator.index(n) | |
except TypeError: | |
raise ValueError('%s is not an integer' % (n,)) | |
else: | |
try: | |
result = int(n) | |
except TypeError: | |
raise ValueError('%s is not an integer' % (n,)) | |
if n - result: | |
raise ValueError('%s is not an integer' % (n,)) | |
return result | |