runtests.py 88 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409
  1. """
  2. This is our testing framework.
  3. Goals:
  4. * it should be compatible with py.test and operate very similarly
  5. (or identically)
  6. * does not require any external dependencies
  7. * preferably all the functionality should be in this file only
  8. * no magic, just import the test file and execute the test functions, that's it
  9. * portable
  10. """
  11. import os
  12. import sys
  13. import platform
  14. import inspect
  15. import traceback
  16. import pdb
  17. import re
  18. import linecache
  19. import time
  20. from fnmatch import fnmatch
  21. from timeit import default_timer as clock
  22. import doctest as pdoctest # avoid clashing with our doctest() function
  23. from doctest import DocTestFinder, DocTestRunner
  24. import random
  25. import subprocess
  26. import shutil
  27. import signal
  28. import stat
  29. import tempfile
  30. import warnings
  31. from contextlib import contextmanager
  32. from inspect import unwrap
  33. from pathlib import Path
  34. from sympy.core.cache import clear_cache
  35. from sympy.external import import_module
  36. from sympy.external.gmpy import GROUND_TYPES
  37. IS_WINDOWS = (os.name == 'nt')
  38. ON_CI = os.getenv('CI', None)
  39. # empirically generated list of the proportion of time spent running
  40. # an even split of tests. This should periodically be regenerated.
  41. # A list of [.6, .1, .3] would mean that if the tests are evenly split
  42. # into '1/3', '2/3', '3/3', the first split would take 60% of the time,
  43. # the second 10% and the third 30%. These lists are normalized to sum
  44. # to 1, so [60, 10, 30] has the same behavior as [6, 1, 3] or [.6, .1, .3].
  45. #
  46. # This list can be generated with the code:
  47. # from time import time
  48. # import sympy
  49. # import os
  50. # os.environ["CI"] = 'true' # Mock CI to get more correct densities
  51. # delays, num_splits = [], 30
  52. # for i in range(1, num_splits + 1):
  53. # tic = time()
  54. # sympy.test(split='{}/{}'.format(i, num_splits), time_balance=False) # Add slow=True for slow tests
  55. # delays.append(time() - tic)
  56. # tot = sum(delays)
  57. # print([round(x / tot, 4) for x in delays])
  58. SPLIT_DENSITY = [
  59. 0.0059, 0.0027, 0.0068, 0.0011, 0.0006,
  60. 0.0058, 0.0047, 0.0046, 0.004, 0.0257,
  61. 0.0017, 0.0026, 0.004, 0.0032, 0.0016,
  62. 0.0015, 0.0004, 0.0011, 0.0016, 0.0014,
  63. 0.0077, 0.0137, 0.0217, 0.0074, 0.0043,
  64. 0.0067, 0.0236, 0.0004, 0.1189, 0.0142,
  65. 0.0234, 0.0003, 0.0003, 0.0047, 0.0006,
  66. 0.0013, 0.0004, 0.0008, 0.0007, 0.0006,
  67. 0.0139, 0.0013, 0.0007, 0.0051, 0.002,
  68. 0.0004, 0.0005, 0.0213, 0.0048, 0.0016,
  69. 0.0012, 0.0014, 0.0024, 0.0015, 0.0004,
  70. 0.0005, 0.0007, 0.011, 0.0062, 0.0015,
  71. 0.0021, 0.0049, 0.0006, 0.0006, 0.0011,
  72. 0.0006, 0.0019, 0.003, 0.0044, 0.0054,
  73. 0.0057, 0.0049, 0.0016, 0.0006, 0.0009,
  74. 0.0006, 0.0012, 0.0006, 0.0149, 0.0532,
  75. 0.0076, 0.0041, 0.0024, 0.0135, 0.0081,
  76. 0.2209, 0.0459, 0.0438, 0.0488, 0.0137,
  77. 0.002, 0.0003, 0.0008, 0.0039, 0.0024,
  78. 0.0005, 0.0004, 0.003, 0.056, 0.0026]
  79. SPLIT_DENSITY_SLOW = [0.0086, 0.0004, 0.0568, 0.0003, 0.0032, 0.0005, 0.0004, 0.0013, 0.0016, 0.0648, 0.0198, 0.1285, 0.098, 0.0005, 0.0064, 0.0003, 0.0004, 0.0026, 0.0007, 0.0051, 0.0089, 0.0024, 0.0033, 0.0057, 0.0005, 0.0003, 0.001, 0.0045, 0.0091, 0.0006, 0.0005, 0.0321, 0.0059, 0.1105, 0.216, 0.1489, 0.0004, 0.0003, 0.0006, 0.0483]
  80. class Skipped(Exception):
  81. pass
  82. class TimeOutError(Exception):
  83. pass
  84. class DependencyError(Exception):
  85. pass
  86. def _indent(s, indent=4):
  87. """
  88. Add the given number of space characters to the beginning of
  89. every non-blank line in ``s``, and return the result.
  90. If the string ``s`` is Unicode, it is encoded using the stdout
  91. encoding and the ``backslashreplace`` error handler.
  92. """
  93. # This regexp matches the start of non-blank lines:
  94. return re.sub('(?m)^(?!$)', indent*' ', s)
  95. pdoctest._indent = _indent # type: ignore
  96. # override reporter to maintain windows and python3
  97. def _report_failure(self, out, test, example, got):
  98. """
  99. Report that the given example failed.
  100. """
  101. s = self._checker.output_difference(example, got, self.optionflags)
  102. s = s.encode('raw_unicode_escape').decode('utf8', 'ignore')
  103. out(self._failure_header(test, example) + s)
  104. if IS_WINDOWS:
  105. DocTestRunner.report_failure = _report_failure # type: ignore
  106. def convert_to_native_paths(lst):
  107. """
  108. Converts a list of '/' separated paths into a list of
  109. native (os.sep separated) paths and converts to lowercase
  110. if the system is case insensitive.
  111. """
  112. newlst = []
  113. for rv in lst:
  114. rv = os.path.join(*rv.split("/"))
  115. # on windows the slash after the colon is dropped
  116. if sys.platform == "win32":
  117. pos = rv.find(':')
  118. if pos != -1:
  119. if rv[pos + 1] != '\\':
  120. rv = rv[:pos + 1] + '\\' + rv[pos + 1:]
  121. newlst.append(os.path.normcase(rv))
  122. return newlst
  123. def get_sympy_dir():
  124. """
  125. Returns the root SymPy directory and set the global value
  126. indicating whether the system is case sensitive or not.
  127. """
  128. this_file = os.path.abspath(__file__)
  129. sympy_dir = os.path.join(os.path.dirname(this_file), "..", "..")
  130. sympy_dir = os.path.normpath(sympy_dir)
  131. return os.path.normcase(sympy_dir)
  132. def setup_pprint(disable_line_wrap=True):
  133. from sympy.interactive.printing import init_printing
  134. from sympy.printing.pretty.pretty import pprint_use_unicode
  135. import sympy.interactive.printing as interactive_printing
  136. from sympy.printing.pretty import stringpict
  137. # Prevent init_printing() in doctests from affecting other doctests
  138. interactive_printing.NO_GLOBAL = True
  139. # force pprint to be in ascii mode in doctests
  140. use_unicode_prev = pprint_use_unicode(False)
  141. # disable line wrapping for pprint() outputs
  142. wrap_line_prev = stringpict._GLOBAL_WRAP_LINE
  143. if disable_line_wrap:
  144. stringpict._GLOBAL_WRAP_LINE = False
  145. # hook our nice, hash-stable strprinter
  146. init_printing(pretty_print=False)
  147. return use_unicode_prev, wrap_line_prev
  148. @contextmanager
  149. def raise_on_deprecated():
  150. """Context manager to make DeprecationWarning raise an error
  151. This is to catch SymPyDeprecationWarning from library code while running
  152. tests and doctests. It is important to use this context manager around
  153. each individual test/doctest in case some tests modify the warning
  154. filters.
  155. """
  156. with warnings.catch_warnings():
  157. warnings.filterwarnings('error', '.*', DeprecationWarning, module='sympy.*')
  158. yield
  159. def run_in_subprocess_with_hash_randomization(
  160. function, function_args=(),
  161. function_kwargs=None, command=sys.executable,
  162. module='sympy.testing.runtests', force=False):
  163. """
  164. Run a function in a Python subprocess with hash randomization enabled.
  165. If hash randomization is not supported by the version of Python given, it
  166. returns False. Otherwise, it returns the exit value of the command. The
  167. function is passed to sys.exit(), so the return value of the function will
  168. be the return value.
  169. The environment variable PYTHONHASHSEED is used to seed Python's hash
  170. randomization. If it is set, this function will return False, because
  171. starting a new subprocess is unnecessary in that case. If it is not set,
  172. one is set at random, and the tests are run. Note that if this
  173. environment variable is set when Python starts, hash randomization is
  174. automatically enabled. To force a subprocess to be created even if
  175. PYTHONHASHSEED is set, pass ``force=True``. This flag will not force a
  176. subprocess in Python versions that do not support hash randomization (see
  177. below), because those versions of Python do not support the ``-R`` flag.
  178. ``function`` should be a string name of a function that is importable from
  179. the module ``module``, like "_test". The default for ``module`` is
  180. "sympy.testing.runtests". ``function_args`` and ``function_kwargs``
  181. should be a repr-able tuple and dict, respectively. The default Python
  182. command is sys.executable, which is the currently running Python command.
  183. This function is necessary because the seed for hash randomization must be
  184. set by the environment variable before Python starts. Hence, in order to
  185. use a predetermined seed for tests, we must start Python in a separate
  186. subprocess.
  187. Hash randomization was added in the minor Python versions 2.6.8, 2.7.3,
  188. 3.1.5, and 3.2.3, and is enabled by default in all Python versions after
  189. and including 3.3.0.
  190. Examples
  191. ========
  192. >>> from sympy.testing.runtests import (
  193. ... run_in_subprocess_with_hash_randomization)
  194. >>> # run the core tests in verbose mode
  195. >>> run_in_subprocess_with_hash_randomization("_test",
  196. ... function_args=("core",),
  197. ... function_kwargs={'verbose': True}) # doctest: +SKIP
  198. # Will return 0 if sys.executable supports hash randomization and tests
  199. # pass, 1 if they fail, and False if it does not support hash
  200. # randomization.
  201. """
  202. cwd = get_sympy_dir()
  203. # Note, we must return False everywhere, not None, as subprocess.call will
  204. # sometimes return None.
  205. # First check if the Python version supports hash randomization
  206. # If it does not have this support, it won't recognize the -R flag
  207. p = subprocess.Popen([command, "-RV"], stdout=subprocess.PIPE,
  208. stderr=subprocess.STDOUT, cwd=cwd)
  209. p.communicate()
  210. if p.returncode != 0:
  211. return False
  212. hash_seed = os.getenv("PYTHONHASHSEED")
  213. if not hash_seed:
  214. os.environ["PYTHONHASHSEED"] = str(random.randrange(2**32))
  215. else:
  216. if not force:
  217. return False
  218. function_kwargs = function_kwargs or {}
  219. # Now run the command
  220. commandstring = ("import sys; from %s import %s;sys.exit(%s(*%s, **%s))" %
  221. (module, function, function, repr(function_args),
  222. repr(function_kwargs)))
  223. try:
  224. p = subprocess.Popen([command, "-R", "-c", commandstring], cwd=cwd)
  225. p.communicate()
  226. except KeyboardInterrupt:
  227. p.wait()
  228. finally:
  229. # Put the environment variable back, so that it reads correctly for
  230. # the current Python process.
  231. if hash_seed is None:
  232. del os.environ["PYTHONHASHSEED"]
  233. else:
  234. os.environ["PYTHONHASHSEED"] = hash_seed
  235. return p.returncode
  236. def run_all_tests(test_args=(), test_kwargs=None,
  237. doctest_args=(), doctest_kwargs=None,
  238. examples_args=(), examples_kwargs=None):
  239. """
  240. Run all tests.
  241. Right now, this runs the regular tests (bin/test), the doctests
  242. (bin/doctest), and the examples (examples/all.py).
  243. This is what ``setup.py test`` uses.
  244. You can pass arguments and keyword arguments to the test functions that
  245. support them (for now, test, doctest, and the examples). See the
  246. docstrings of those functions for a description of the available options.
  247. For example, to run the solvers tests with colors turned off:
  248. >>> from sympy.testing.runtests import run_all_tests
  249. >>> run_all_tests(test_args=("solvers",),
  250. ... test_kwargs={"colors:False"}) # doctest: +SKIP
  251. """
  252. tests_successful = True
  253. test_kwargs = test_kwargs or {}
  254. doctest_kwargs = doctest_kwargs or {}
  255. examples_kwargs = examples_kwargs or {'quiet': True}
  256. try:
  257. # Regular tests
  258. if not test(*test_args, **test_kwargs):
  259. # some regular test fails, so set the tests_successful
  260. # flag to false and continue running the doctests
  261. tests_successful = False
  262. # Doctests
  263. print()
  264. if not doctest(*doctest_args, **doctest_kwargs):
  265. tests_successful = False
  266. # Examples
  267. print()
  268. sys.path.append("examples") # examples/all.py
  269. from all import run_examples # type: ignore
  270. if not run_examples(*examples_args, **examples_kwargs):
  271. tests_successful = False
  272. if tests_successful:
  273. return
  274. else:
  275. # Return nonzero exit code
  276. sys.exit(1)
  277. except KeyboardInterrupt:
  278. print()
  279. print("DO *NOT* COMMIT!")
  280. sys.exit(1)
  281. def test(*paths, subprocess=True, rerun=0, **kwargs):
  282. """
  283. Run tests in the specified test_*.py files.
  284. Tests in a particular test_*.py file are run if any of the given strings
  285. in ``paths`` matches a part of the test file's path. If ``paths=[]``,
  286. tests in all test_*.py files are run.
  287. Notes:
  288. - If sort=False, tests are run in random order (not default).
  289. - Paths can be entered in native system format or in unix,
  290. forward-slash format.
  291. - Files that are on the blacklist can be tested by providing
  292. their path; they are only excluded if no paths are given.
  293. **Explanation of test results**
  294. ====== ===============================================================
  295. Output Meaning
  296. ====== ===============================================================
  297. . passed
  298. F failed
  299. X XPassed (expected to fail but passed)
  300. f XFAILed (expected to fail and indeed failed)
  301. s skipped
  302. w slow
  303. T timeout (e.g., when ``--timeout`` is used)
  304. K KeyboardInterrupt (when running the slow tests with ``--slow``,
  305. you can interrupt one of them without killing the test runner)
  306. ====== ===============================================================
  307. Colors have no additional meaning and are used just to facilitate
  308. interpreting the output.
  309. Examples
  310. ========
  311. >>> import sympy
  312. Run all tests:
  313. >>> sympy.test() # doctest: +SKIP
  314. Run one file:
  315. >>> sympy.test("sympy/core/tests/test_basic.py") # doctest: +SKIP
  316. >>> sympy.test("_basic") # doctest: +SKIP
  317. Run all tests in sympy/functions/ and some particular file:
  318. >>> sympy.test("sympy/core/tests/test_basic.py",
  319. ... "sympy/functions") # doctest: +SKIP
  320. Run all tests in sympy/core and sympy/utilities:
  321. >>> sympy.test("/core", "/util") # doctest: +SKIP
  322. Run specific test from a file:
  323. >>> sympy.test("sympy/core/tests/test_basic.py",
  324. ... kw="test_equality") # doctest: +SKIP
  325. Run specific test from any file:
  326. >>> sympy.test(kw="subs") # doctest: +SKIP
  327. Run the tests with verbose mode on:
  328. >>> sympy.test(verbose=True) # doctest: +SKIP
  329. Do not sort the test output:
  330. >>> sympy.test(sort=False) # doctest: +SKIP
  331. Turn on post-mortem pdb:
  332. >>> sympy.test(pdb=True) # doctest: +SKIP
  333. Turn off colors:
  334. >>> sympy.test(colors=False) # doctest: +SKIP
  335. Force colors, even when the output is not to a terminal (this is useful,
  336. e.g., if you are piping to ``less -r`` and you still want colors)
  337. >>> sympy.test(force_colors=False) # doctest: +SKIP
  338. The traceback verboseness can be set to "short" or "no" (default is
  339. "short")
  340. >>> sympy.test(tb='no') # doctest: +SKIP
  341. The ``split`` option can be passed to split the test run into parts. The
  342. split currently only splits the test files, though this may change in the
  343. future. ``split`` should be a string of the form 'a/b', which will run
  344. part ``a`` of ``b``. For instance, to run the first half of the test suite:
  345. >>> sympy.test(split='1/2') # doctest: +SKIP
  346. The ``time_balance`` option can be passed in conjunction with ``split``.
  347. If ``time_balance=True`` (the default for ``sympy.test``), SymPy will attempt
  348. to split the tests such that each split takes equal time. This heuristic
  349. for balancing is based on pre-recorded test data.
  350. >>> sympy.test(split='1/2', time_balance=True) # doctest: +SKIP
  351. You can disable running the tests in a separate subprocess using
  352. ``subprocess=False``. This is done to support seeding hash randomization,
  353. which is enabled by default in the Python versions where it is supported.
  354. If subprocess=False, hash randomization is enabled/disabled according to
  355. whether it has been enabled or not in the calling Python process.
  356. However, even if it is enabled, the seed cannot be printed unless it is
  357. called from a new Python process.
  358. Hash randomization was added in the minor Python versions 2.6.8, 2.7.3,
  359. 3.1.5, and 3.2.3, and is enabled by default in all Python versions after
  360. and including 3.3.0.
  361. If hash randomization is not supported ``subprocess=False`` is used
  362. automatically.
  363. >>> sympy.test(subprocess=False) # doctest: +SKIP
  364. To set the hash randomization seed, set the environment variable
  365. ``PYTHONHASHSEED`` before running the tests. This can be done from within
  366. Python using
  367. >>> import os
  368. >>> os.environ['PYTHONHASHSEED'] = '42' # doctest: +SKIP
  369. Or from the command line using
  370. $ PYTHONHASHSEED=42 ./bin/test
  371. If the seed is not set, a random seed will be chosen.
  372. Note that to reproduce the same hash values, you must use both the same seed
  373. as well as the same architecture (32-bit vs. 64-bit).
  374. """
  375. # count up from 0, do not print 0
  376. print_counter = lambda i : (print("rerun %d" % (rerun-i))
  377. if rerun-i else None)
  378. if subprocess:
  379. # loop backwards so last i is 0
  380. for i in range(rerun, -1, -1):
  381. print_counter(i)
  382. ret = run_in_subprocess_with_hash_randomization("_test",
  383. function_args=paths, function_kwargs=kwargs)
  384. if ret is False:
  385. break
  386. val = not bool(ret)
  387. # exit on the first failure or if done
  388. if not val or i == 0:
  389. return val
  390. # rerun even if hash randomization is not supported
  391. for i in range(rerun, -1, -1):
  392. print_counter(i)
  393. val = not bool(_test(*paths, **kwargs))
  394. if not val or i == 0:
  395. return val
  396. def _test(*paths,
  397. verbose=False, tb="short", kw=None, pdb=False, colors=True,
  398. force_colors=False, sort=True, seed=None, timeout=False,
  399. fail_on_timeout=False, slow=False, enhance_asserts=False, split=None,
  400. time_balance=True, blacklist=(),
  401. fast_threshold=None, slow_threshold=None):
  402. """
  403. Internal function that actually runs the tests.
  404. All keyword arguments from ``test()`` are passed to this function except for
  405. ``subprocess``.
  406. Returns 0 if tests passed and 1 if they failed. See the docstring of
  407. ``test()`` for more information.
  408. """
  409. kw = kw or ()
  410. # ensure that kw is a tuple
  411. if isinstance(kw, str):
  412. kw = (kw,)
  413. post_mortem = pdb
  414. if seed is None:
  415. seed = random.randrange(100000000)
  416. if ON_CI and timeout is False:
  417. timeout = 595
  418. fail_on_timeout = True
  419. if ON_CI:
  420. blacklist = list(blacklist) + ['sympy/plotting/pygletplot/tests']
  421. blacklist = convert_to_native_paths(blacklist)
  422. r = PyTestReporter(verbose=verbose, tb=tb, colors=colors,
  423. force_colors=force_colors, split=split)
  424. # This won't strictly run the test for the corresponding file, but it is
  425. # good enough for copying and pasting the failing test.
  426. _paths = []
  427. for path in paths:
  428. if '::' in path:
  429. path, _kw = path.split('::', 1)
  430. kw += (_kw,)
  431. _paths.append(path)
  432. paths = _paths
  433. t = SymPyTests(r, kw, post_mortem, seed,
  434. fast_threshold=fast_threshold,
  435. slow_threshold=slow_threshold)
  436. test_files = t.get_test_files('sympy')
  437. not_blacklisted = [f for f in test_files
  438. if not any(b in f for b in blacklist)]
  439. if len(paths) == 0:
  440. matched = not_blacklisted
  441. else:
  442. paths = convert_to_native_paths(paths)
  443. matched = []
  444. for f in not_blacklisted:
  445. basename = os.path.basename(f)
  446. for p in paths:
  447. if p in f or fnmatch(basename, p):
  448. matched.append(f)
  449. break
  450. density = None
  451. if time_balance:
  452. if slow:
  453. density = SPLIT_DENSITY_SLOW
  454. else:
  455. density = SPLIT_DENSITY
  456. if split:
  457. matched = split_list(matched, split, density=density)
  458. t._testfiles.extend(matched)
  459. return int(not t.test(sort=sort, timeout=timeout, slow=slow,
  460. enhance_asserts=enhance_asserts, fail_on_timeout=fail_on_timeout))
  461. def doctest(*paths, subprocess=True, rerun=0, **kwargs):
  462. r"""
  463. Runs doctests in all \*.py files in the SymPy directory which match
  464. any of the given strings in ``paths`` or all tests if paths=[].
  465. Notes:
  466. - Paths can be entered in native system format or in unix,
  467. forward-slash format.
  468. - Files that are on the blacklist can be tested by providing
  469. their path; they are only excluded if no paths are given.
  470. Examples
  471. ========
  472. >>> import sympy
  473. Run all tests:
  474. >>> sympy.doctest() # doctest: +SKIP
  475. Run one file:
  476. >>> sympy.doctest("sympy/core/basic.py") # doctest: +SKIP
  477. >>> sympy.doctest("polynomial.rst") # doctest: +SKIP
  478. Run all tests in sympy/functions/ and some particular file:
  479. >>> sympy.doctest("/functions", "basic.py") # doctest: +SKIP
  480. Run any file having polynomial in its name, doc/src/modules/polynomial.rst,
  481. sympy/functions/special/polynomials.py, and sympy/polys/polynomial.py:
  482. >>> sympy.doctest("polynomial") # doctest: +SKIP
  483. The ``split`` option can be passed to split the test run into parts. The
  484. split currently only splits the test files, though this may change in the
  485. future. ``split`` should be a string of the form 'a/b', which will run
  486. part ``a`` of ``b``. Note that the regular doctests and the Sphinx
  487. doctests are split independently. For instance, to run the first half of
  488. the test suite:
  489. >>> sympy.doctest(split='1/2') # doctest: +SKIP
  490. The ``subprocess`` and ``verbose`` options are the same as with the function
  491. ``test()`` (see the docstring of that function for more information) except
  492. that ``verbose`` may also be set equal to ``2`` in order to print
  493. individual doctest lines, as they are being tested.
  494. """
  495. # count up from 0, do not print 0
  496. print_counter = lambda i : (print("rerun %d" % (rerun-i))
  497. if rerun-i else None)
  498. if subprocess:
  499. # loop backwards so last i is 0
  500. for i in range(rerun, -1, -1):
  501. print_counter(i)
  502. ret = run_in_subprocess_with_hash_randomization("_doctest",
  503. function_args=paths, function_kwargs=kwargs)
  504. if ret is False:
  505. break
  506. val = not bool(ret)
  507. # exit on the first failure or if done
  508. if not val or i == 0:
  509. return val
  510. # rerun even if hash randomization is not supported
  511. for i in range(rerun, -1, -1):
  512. print_counter(i)
  513. val = not bool(_doctest(*paths, **kwargs))
  514. if not val or i == 0:
  515. return val
  516. def _get_doctest_blacklist():
  517. '''Get the default blacklist for the doctests'''
  518. blacklist = []
  519. blacklist.extend([
  520. "doc/src/modules/plotting.rst", # generates live plots
  521. "doc/src/modules/physics/mechanics/autolev_parser.rst",
  522. "sympy/codegen/array_utils.py", # raises deprecation warning
  523. "sympy/core/compatibility.py", # backwards compatibility shim, importing it triggers a deprecation warning
  524. "sympy/core/trace.py", # backwards compatibility shim, importing it triggers a deprecation warning
  525. "sympy/galgebra.py", # no longer part of SymPy
  526. "sympy/parsing/autolev/_antlr/autolevlexer.py", # generated code
  527. "sympy/parsing/autolev/_antlr/autolevlistener.py", # generated code
  528. "sympy/parsing/autolev/_antlr/autolevparser.py", # generated code
  529. "sympy/parsing/latex/_antlr/latexlexer.py", # generated code
  530. "sympy/parsing/latex/_antlr/latexparser.py", # generated code
  531. "sympy/plotting/pygletplot/__init__.py", # crashes on some systems
  532. "sympy/plotting/pygletplot/plot.py", # crashes on some systems
  533. "sympy/printing/ccode.py", # backwards compatibility shim, importing it breaks the codegen doctests
  534. "sympy/printing/cxxcode.py", # backwards compatibility shim, importing it breaks the codegen doctests
  535. "sympy/printing/fcode.py", # backwards compatibility shim, importing it breaks the codegen doctests
  536. "sympy/testing/randtest.py", # backwards compatibility shim, importing it triggers a deprecation warning
  537. "sympy/this.py", # prints text
  538. ])
  539. # autolev parser tests
  540. num = 12
  541. for i in range (1, num+1):
  542. blacklist.append("sympy/parsing/autolev/test-examples/ruletest" + str(i) + ".py")
  543. blacklist.extend(["sympy/parsing/autolev/test-examples/pydy-example-repo/mass_spring_damper.py",
  544. "sympy/parsing/autolev/test-examples/pydy-example-repo/chaos_pendulum.py",
  545. "sympy/parsing/autolev/test-examples/pydy-example-repo/double_pendulum.py",
  546. "sympy/parsing/autolev/test-examples/pydy-example-repo/non_min_pendulum.py"])
  547. if import_module('numpy') is None:
  548. blacklist.extend([
  549. "sympy/plotting/experimental_lambdify.py",
  550. "sympy/plotting/plot_implicit.py",
  551. "examples/advanced/autowrap_integrators.py",
  552. "examples/advanced/autowrap_ufuncify.py",
  553. "examples/intermediate/sample.py",
  554. "examples/intermediate/mplot2d.py",
  555. "examples/intermediate/mplot3d.py",
  556. "doc/src/modules/numeric-computation.rst",
  557. "doc/src/explanation/best-practices.md",
  558. "doc/src/tutorials/physics/biomechanics/biomechanical-model-example.rst",
  559. "doc/src/tutorials/physics/biomechanics/biomechanics.rst",
  560. ])
  561. else:
  562. if import_module('matplotlib') is None:
  563. blacklist.extend([
  564. "examples/intermediate/mplot2d.py",
  565. "examples/intermediate/mplot3d.py"
  566. ])
  567. else:
  568. # Use a non-windowed backend, so that the tests work on CI
  569. import matplotlib
  570. matplotlib.use('Agg')
  571. if ON_CI or import_module('pyglet') is None:
  572. blacklist.extend(["sympy/plotting/pygletplot"])
  573. if import_module('aesara') is None:
  574. blacklist.extend([
  575. "sympy/printing/aesaracode.py",
  576. "doc/src/modules/numeric-computation.rst",
  577. ])
  578. if import_module('cupy') is None:
  579. blacklist.extend([
  580. "doc/src/modules/numeric-computation.rst",
  581. ])
  582. if import_module('jax') is None:
  583. blacklist.extend([
  584. "doc/src/modules/numeric-computation.rst",
  585. ])
  586. if import_module('antlr4') is None:
  587. blacklist.extend([
  588. "sympy/parsing/autolev/__init__.py",
  589. "sympy/parsing/latex/_parse_latex_antlr.py",
  590. ])
  591. if import_module('lfortran') is None:
  592. #throws ImportError when lfortran not installed
  593. blacklist.extend([
  594. "sympy/parsing/sym_expr.py",
  595. ])
  596. if import_module("scipy") is None:
  597. # throws ModuleNotFoundError when scipy not installed
  598. blacklist.extend([
  599. "doc/src/guides/solving/solve-numerically.md",
  600. "doc/src/guides/solving/solve-ode.md",
  601. ])
  602. if import_module("numpy") is None:
  603. # throws ModuleNotFoundError when numpy not installed
  604. blacklist.extend([
  605. "doc/src/guides/solving/solve-ode.md",
  606. "doc/src/guides/solving/solve-numerically.md",
  607. ])
  608. # disabled because of doctest failures in asmeurer's bot
  609. blacklist.extend([
  610. "sympy/utilities/autowrap.py",
  611. "examples/advanced/autowrap_integrators.py",
  612. "examples/advanced/autowrap_ufuncify.py"
  613. ])
  614. blacklist.extend([
  615. "sympy/conftest.py", # Depends on pytest
  616. ])
  617. # These are deprecated stubs to be removed:
  618. blacklist.extend([
  619. "sympy/utilities/tmpfiles.py",
  620. "sympy/utilities/pytest.py",
  621. "sympy/utilities/runtests.py",
  622. "sympy/utilities/quality_unicode.py",
  623. "sympy/utilities/randtest.py",
  624. ])
  625. blacklist = convert_to_native_paths(blacklist)
  626. return blacklist
  627. def _doctest(*paths, **kwargs):
  628. """
  629. Internal function that actually runs the doctests.
  630. All keyword arguments from ``doctest()`` are passed to this function
  631. except for ``subprocess``.
  632. Returns 0 if tests passed and 1 if they failed. See the docstrings of
  633. ``doctest()`` and ``test()`` for more information.
  634. """
  635. from sympy.printing.pretty.pretty import pprint_use_unicode
  636. from sympy.printing.pretty import stringpict
  637. normal = kwargs.get("normal", False)
  638. verbose = kwargs.get("verbose", False)
  639. colors = kwargs.get("colors", True)
  640. force_colors = kwargs.get("force_colors", False)
  641. blacklist = kwargs.get("blacklist", [])
  642. split = kwargs.get('split', None)
  643. blacklist.extend(_get_doctest_blacklist())
  644. # Use a non-windowed backend, so that the tests work on CI
  645. if import_module('matplotlib') is not None:
  646. import matplotlib
  647. matplotlib.use('Agg')
  648. # Disable warnings for external modules
  649. import sympy.external
  650. sympy.external.importtools.WARN_OLD_VERSION = False
  651. sympy.external.importtools.WARN_NOT_INSTALLED = False
  652. # Disable showing up of plots
  653. from sympy.plotting.plot import unset_show
  654. unset_show()
  655. r = PyTestReporter(verbose, split=split, colors=colors,\
  656. force_colors=force_colors)
  657. t = SymPyDocTests(r, normal)
  658. test_files = t.get_test_files('sympy')
  659. test_files.extend(t.get_test_files('examples', init_only=False))
  660. not_blacklisted = [f for f in test_files
  661. if not any(b in f for b in blacklist)]
  662. if len(paths) == 0:
  663. matched = not_blacklisted
  664. else:
  665. # take only what was requested...but not blacklisted items
  666. # and allow for partial match anywhere or fnmatch of name
  667. paths = convert_to_native_paths(paths)
  668. matched = []
  669. for f in not_blacklisted:
  670. basename = os.path.basename(f)
  671. for p in paths:
  672. if p in f or fnmatch(basename, p):
  673. matched.append(f)
  674. break
  675. matched.sort()
  676. if split:
  677. matched = split_list(matched, split)
  678. t._testfiles.extend(matched)
  679. # run the tests and record the result for this *py portion of the tests
  680. if t._testfiles:
  681. failed = not t.test()
  682. else:
  683. failed = False
  684. # N.B.
  685. # --------------------------------------------------------------------
  686. # Here we test *.rst and *.md files at or below doc/src. Code from these
  687. # must be self supporting in terms of imports since there is no importing
  688. # of necessary modules by doctest.testfile. If you try to pass *.py files
  689. # through this they might fail because they will lack the needed imports
  690. # and smarter parsing that can be done with source code.
  691. #
  692. test_files_rst = t.get_test_files('doc/src', '*.rst', init_only=False)
  693. test_files_md = t.get_test_files('doc/src', '*.md', init_only=False)
  694. test_files = test_files_rst + test_files_md
  695. test_files.sort()
  696. not_blacklisted = [f for f in test_files
  697. if not any(b in f for b in blacklist)]
  698. if len(paths) == 0:
  699. matched = not_blacklisted
  700. else:
  701. # Take only what was requested as long as it's not on the blacklist.
  702. # Paths were already made native in *py tests so don't repeat here.
  703. # There's no chance of having a *py file slip through since we
  704. # only have *rst files in test_files.
  705. matched = []
  706. for f in not_blacklisted:
  707. basename = os.path.basename(f)
  708. for p in paths:
  709. if p in f or fnmatch(basename, p):
  710. matched.append(f)
  711. break
  712. if split:
  713. matched = split_list(matched, split)
  714. first_report = True
  715. for rst_file in matched:
  716. if not os.path.isfile(rst_file):
  717. continue
  718. old_displayhook = sys.displayhook
  719. try:
  720. use_unicode_prev, wrap_line_prev = setup_pprint()
  721. out = sympytestfile(
  722. rst_file, module_relative=False, encoding='utf-8',
  723. optionflags=pdoctest.ELLIPSIS | pdoctest.NORMALIZE_WHITESPACE |
  724. pdoctest.IGNORE_EXCEPTION_DETAIL)
  725. finally:
  726. # make sure we return to the original displayhook in case some
  727. # doctest has changed that
  728. sys.displayhook = old_displayhook
  729. # The NO_GLOBAL flag overrides the no_global flag to init_printing
  730. # if True
  731. import sympy.interactive.printing as interactive_printing
  732. interactive_printing.NO_GLOBAL = False
  733. pprint_use_unicode(use_unicode_prev)
  734. stringpict._GLOBAL_WRAP_LINE = wrap_line_prev
  735. rstfailed, tested = out
  736. if tested:
  737. failed = rstfailed or failed
  738. if first_report:
  739. first_report = False
  740. msg = 'rst/md doctests start'
  741. if not t._testfiles:
  742. r.start(msg=msg)
  743. else:
  744. r.write_center(msg)
  745. print()
  746. # use as the id, everything past the first 'sympy'
  747. file_id = rst_file[rst_file.find('sympy') + len('sympy') + 1:]
  748. print(file_id, end=" ")
  749. # get at least the name out so it is know who is being tested
  750. wid = r.terminal_width - len(file_id) - 1 # update width
  751. test_file = '[%s]' % (tested)
  752. report = '[%s]' % (rstfailed or 'OK')
  753. print(''.join(
  754. [test_file, ' '*(wid - len(test_file) - len(report)), report])
  755. )
  756. # the doctests for *py will have printed this message already if there was
  757. # a failure, so now only print it if there was intervening reporting by
  758. # testing the *rst as evidenced by first_report no longer being True.
  759. if not first_report and failed:
  760. print()
  761. print("DO *NOT* COMMIT!")
  762. return int(failed)
  763. sp = re.compile(r'([0-9]+)/([1-9][0-9]*)')
  764. def split_list(l, split, density=None):
  765. """
  766. Splits a list into part a of b
  767. split should be a string of the form 'a/b'. For instance, '1/3' would give
  768. the split one of three.
  769. If the length of the list is not divisible by the number of splits, the
  770. last split will have more items.
  771. `density` may be specified as a list. If specified,
  772. tests will be balanced so that each split has as equal-as-possible
  773. amount of mass according to `density`.
  774. >>> from sympy.testing.runtests import split_list
  775. >>> a = list(range(10))
  776. >>> split_list(a, '1/3')
  777. [0, 1, 2]
  778. >>> split_list(a, '2/3')
  779. [3, 4, 5]
  780. >>> split_list(a, '3/3')
  781. [6, 7, 8, 9]
  782. """
  783. m = sp.match(split)
  784. if not m:
  785. raise ValueError("split must be a string of the form a/b where a and b are ints")
  786. i, t = map(int, m.groups())
  787. if not density:
  788. return l[(i - 1)*len(l)//t : i*len(l)//t]
  789. # normalize density
  790. tot = sum(density)
  791. density = [x / tot for x in density]
  792. def density_inv(x):
  793. """Interpolate the inverse to the cumulative
  794. distribution function given by density"""
  795. if x <= 0:
  796. return 0
  797. if x >= sum(density):
  798. return 1
  799. # find the first time the cumulative sum surpasses x
  800. # and linearly interpolate
  801. cumm = 0
  802. for i, d in enumerate(density):
  803. cumm += d
  804. if cumm >= x:
  805. break
  806. frac = (d - (cumm - x)) / d
  807. return (i + frac) / len(density)
  808. lower_frac = density_inv((i - 1) / t)
  809. higher_frac = density_inv(i / t)
  810. return l[int(lower_frac*len(l)) : int(higher_frac*len(l))]
  811. from collections import namedtuple
  812. SymPyTestResults = namedtuple('SymPyTestResults', 'failed attempted')
  813. def sympytestfile(filename, module_relative=True, name=None, package=None,
  814. globs=None, verbose=None, report=True, optionflags=0,
  815. extraglobs=None, raise_on_error=False,
  816. parser=pdoctest.DocTestParser(), encoding=None):
  817. """
  818. Test examples in the given file. Return (#failures, #tests).
  819. Optional keyword arg ``module_relative`` specifies how filenames
  820. should be interpreted:
  821. - If ``module_relative`` is True (the default), then ``filename``
  822. specifies a module-relative path. By default, this path is
  823. relative to the calling module's directory; but if the
  824. ``package`` argument is specified, then it is relative to that
  825. package. To ensure os-independence, ``filename`` should use
  826. "/" characters to separate path segments, and should not
  827. be an absolute path (i.e., it may not begin with "/").
  828. - If ``module_relative`` is False, then ``filename`` specifies an
  829. os-specific path. The path may be absolute or relative (to
  830. the current working directory).
  831. Optional keyword arg ``name`` gives the name of the test; by default
  832. use the file's basename.
  833. Optional keyword argument ``package`` is a Python package or the
  834. name of a Python package whose directory should be used as the
  835. base directory for a module relative filename. If no package is
  836. specified, then the calling module's directory is used as the base
  837. directory for module relative filenames. It is an error to
  838. specify ``package`` if ``module_relative`` is False.
  839. Optional keyword arg ``globs`` gives a dict to be used as the globals
  840. when executing examples; by default, use {}. A copy of this dict
  841. is actually used for each docstring, so that each docstring's
  842. examples start with a clean slate.
  843. Optional keyword arg ``extraglobs`` gives a dictionary that should be
  844. merged into the globals that are used to execute examples. By
  845. default, no extra globals are used.
  846. Optional keyword arg ``verbose`` prints lots of stuff if true, prints
  847. only failures if false; by default, it's true iff "-v" is in sys.argv.
  848. Optional keyword arg ``report`` prints a summary at the end when true,
  849. else prints nothing at the end. In verbose mode, the summary is
  850. detailed, else very brief (in fact, empty if all tests passed).
  851. Optional keyword arg ``optionflags`` or's together module constants,
  852. and defaults to 0. Possible values (see the docs for details):
  853. - DONT_ACCEPT_TRUE_FOR_1
  854. - DONT_ACCEPT_BLANKLINE
  855. - NORMALIZE_WHITESPACE
  856. - ELLIPSIS
  857. - SKIP
  858. - IGNORE_EXCEPTION_DETAIL
  859. - REPORT_UDIFF
  860. - REPORT_CDIFF
  861. - REPORT_NDIFF
  862. - REPORT_ONLY_FIRST_FAILURE
  863. Optional keyword arg ``raise_on_error`` raises an exception on the
  864. first unexpected exception or failure. This allows failures to be
  865. post-mortem debugged.
  866. Optional keyword arg ``parser`` specifies a DocTestParser (or
  867. subclass) that should be used to extract tests from the files.
  868. Optional keyword arg ``encoding`` specifies an encoding that should
  869. be used to convert the file to unicode.
  870. Advanced tomfoolery: testmod runs methods of a local instance of
  871. class doctest.Tester, then merges the results into (or creates)
  872. global Tester instance doctest.master. Methods of doctest.master
  873. can be called directly too, if you want to do something unusual.
  874. Passing report=0 to testmod is especially useful then, to delay
  875. displaying a summary. Invoke doctest.master.summarize(verbose)
  876. when you're done fiddling.
  877. """
  878. if package and not module_relative:
  879. raise ValueError("Package may only be specified for module-"
  880. "relative paths.")
  881. # Relativize the path
  882. text, filename = pdoctest._load_testfile(
  883. filename, package, module_relative, encoding)
  884. # If no name was given, then use the file's name.
  885. if name is None:
  886. name = os.path.basename(filename)
  887. # Assemble the globals.
  888. if globs is None:
  889. globs = {}
  890. else:
  891. globs = globs.copy()
  892. if extraglobs is not None:
  893. globs.update(extraglobs)
  894. if '__name__' not in globs:
  895. globs['__name__'] = '__main__'
  896. if raise_on_error:
  897. runner = pdoctest.DebugRunner(verbose=verbose, optionflags=optionflags)
  898. else:
  899. runner = SymPyDocTestRunner(verbose=verbose, optionflags=optionflags)
  900. runner._checker = SymPyOutputChecker()
  901. # Read the file, convert it to a test, and run it.
  902. test = parser.get_doctest(text, globs, name, filename, 0)
  903. runner.run(test)
  904. if report:
  905. runner.summarize()
  906. if pdoctest.master is None:
  907. pdoctest.master = runner
  908. else:
  909. pdoctest.master.merge(runner)
  910. return SymPyTestResults(runner.failures, runner.tries)
  911. class SymPyTests:
  912. def __init__(self, reporter, kw="", post_mortem=False,
  913. seed=None, fast_threshold=None, slow_threshold=None):
  914. self._post_mortem = post_mortem
  915. self._kw = kw
  916. self._count = 0
  917. self._root_dir = get_sympy_dir()
  918. self._reporter = reporter
  919. self._reporter.root_dir(self._root_dir)
  920. self._testfiles = []
  921. self._seed = seed if seed is not None else random.random()
  922. # Defaults in seconds, from human / UX design limits
  923. # http://www.nngroup.com/articles/response-times-3-important-limits/
  924. #
  925. # These defaults are *NOT* set in stone as we are measuring different
  926. # things, so others feel free to come up with a better yardstick :)
  927. if fast_threshold:
  928. self._fast_threshold = float(fast_threshold)
  929. else:
  930. self._fast_threshold = 8
  931. if slow_threshold:
  932. self._slow_threshold = float(slow_threshold)
  933. else:
  934. self._slow_threshold = 10
  935. def test(self, sort=False, timeout=False, slow=False,
  936. enhance_asserts=False, fail_on_timeout=False):
  937. """
  938. Runs the tests returning True if all tests pass, otherwise False.
  939. If sort=False run tests in random order.
  940. """
  941. if sort:
  942. self._testfiles.sort()
  943. elif slow:
  944. pass
  945. else:
  946. random.seed(self._seed)
  947. random.shuffle(self._testfiles)
  948. self._reporter.start(self._seed)
  949. for f in self._testfiles:
  950. try:
  951. self.test_file(f, sort, timeout, slow,
  952. enhance_asserts, fail_on_timeout)
  953. except KeyboardInterrupt:
  954. print(" interrupted by user")
  955. self._reporter.finish()
  956. raise
  957. return self._reporter.finish()
  958. def _enhance_asserts(self, source):
  959. from ast import (NodeTransformer, Compare, Name, Store, Load, Tuple,
  960. Assign, BinOp, Str, Mod, Assert, parse, fix_missing_locations)
  961. ops = {"Eq": '==', "NotEq": '!=', "Lt": '<', "LtE": '<=',
  962. "Gt": '>', "GtE": '>=', "Is": 'is', "IsNot": 'is not',
  963. "In": 'in', "NotIn": 'not in'}
  964. class Transform(NodeTransformer):
  965. def visit_Assert(self, stmt):
  966. if isinstance(stmt.test, Compare):
  967. compare = stmt.test
  968. values = [compare.left] + compare.comparators
  969. names = [ "_%s" % i for i, _ in enumerate(values) ]
  970. names_store = [ Name(n, Store()) for n in names ]
  971. names_load = [ Name(n, Load()) for n in names ]
  972. target = Tuple(names_store, Store())
  973. value = Tuple(values, Load())
  974. assign = Assign([target], value)
  975. new_compare = Compare(names_load[0], compare.ops, names_load[1:])
  976. msg_format = "\n%s " + "\n%s ".join([ ops[op.__class__.__name__] for op in compare.ops ]) + "\n%s"
  977. msg = BinOp(Str(msg_format), Mod(), Tuple(names_load, Load()))
  978. test = Assert(new_compare, msg, lineno=stmt.lineno, col_offset=stmt.col_offset)
  979. return [assign, test]
  980. else:
  981. return stmt
  982. tree = parse(source)
  983. new_tree = Transform().visit(tree)
  984. return fix_missing_locations(new_tree)
  985. def test_file(self, filename, sort=True, timeout=False, slow=False,
  986. enhance_asserts=False, fail_on_timeout=False):
  987. reporter = self._reporter
  988. funcs = []
  989. try:
  990. gl = {'__file__': filename}
  991. try:
  992. open_file = lambda: open(filename, encoding="utf8")
  993. with open_file() as f:
  994. source = f.read()
  995. if self._kw:
  996. for l in source.splitlines():
  997. if l.lstrip().startswith('def '):
  998. if any(l.lower().find(k.lower()) != -1 for k in self._kw):
  999. break
  1000. else:
  1001. return
  1002. if enhance_asserts:
  1003. try:
  1004. source = self._enhance_asserts(source)
  1005. except ImportError:
  1006. pass
  1007. code = compile(source, filename, "exec", flags=0, dont_inherit=True)
  1008. exec(code, gl)
  1009. except (SystemExit, KeyboardInterrupt):
  1010. raise
  1011. except ImportError:
  1012. reporter.import_error(filename, sys.exc_info())
  1013. return
  1014. except Exception:
  1015. reporter.test_exception(sys.exc_info())
  1016. clear_cache()
  1017. self._count += 1
  1018. random.seed(self._seed)
  1019. disabled = gl.get("disabled", False)
  1020. if not disabled:
  1021. # we need to filter only those functions that begin with 'test_'
  1022. # We have to be careful about decorated functions. As long as
  1023. # the decorator uses functools.wraps, we can detect it.
  1024. funcs = []
  1025. for f in gl:
  1026. if (f.startswith("test_") and (inspect.isfunction(gl[f])
  1027. or inspect.ismethod(gl[f]))):
  1028. func = gl[f]
  1029. # Handle multiple decorators
  1030. while hasattr(func, '__wrapped__'):
  1031. func = func.__wrapped__
  1032. if inspect.getsourcefile(func) == filename:
  1033. funcs.append(gl[f])
  1034. if slow:
  1035. funcs = [f for f in funcs if getattr(f, '_slow', False)]
  1036. # Sorting of XFAILed functions isn't fixed yet :-(
  1037. funcs.sort(key=lambda x: inspect.getsourcelines(x)[1])
  1038. i = 0
  1039. while i < len(funcs):
  1040. if inspect.isgeneratorfunction(funcs[i]):
  1041. # some tests can be generators, that return the actual
  1042. # test functions. We unpack it below:
  1043. f = funcs.pop(i)
  1044. for fg in f():
  1045. func = fg[0]
  1046. args = fg[1:]
  1047. fgw = lambda: func(*args)
  1048. funcs.insert(i, fgw)
  1049. i += 1
  1050. else:
  1051. i += 1
  1052. # drop functions that are not selected with the keyword expression:
  1053. funcs = [x for x in funcs if self.matches(x)]
  1054. if not funcs:
  1055. return
  1056. except Exception:
  1057. reporter.entering_filename(filename, len(funcs))
  1058. raise
  1059. reporter.entering_filename(filename, len(funcs))
  1060. if not sort:
  1061. random.shuffle(funcs)
  1062. for f in funcs:
  1063. start = time.time()
  1064. reporter.entering_test(f)
  1065. try:
  1066. if getattr(f, '_slow', False) and not slow:
  1067. raise Skipped("Slow")
  1068. with raise_on_deprecated():
  1069. if timeout:
  1070. self._timeout(f, timeout, fail_on_timeout)
  1071. else:
  1072. random.seed(self._seed)
  1073. f()
  1074. except KeyboardInterrupt:
  1075. if getattr(f, '_slow', False):
  1076. reporter.test_skip("KeyboardInterrupt")
  1077. else:
  1078. raise
  1079. except Exception:
  1080. if timeout:
  1081. signal.alarm(0) # Disable the alarm. It could not be handled before.
  1082. t, v, tr = sys.exc_info()
  1083. if t is AssertionError:
  1084. reporter.test_fail((t, v, tr))
  1085. if self._post_mortem:
  1086. pdb.post_mortem(tr)
  1087. elif t.__name__ == "Skipped":
  1088. reporter.test_skip(v)
  1089. elif t.__name__ == "XFail":
  1090. reporter.test_xfail()
  1091. elif t.__name__ == "XPass":
  1092. reporter.test_xpass(v)
  1093. else:
  1094. reporter.test_exception((t, v, tr))
  1095. if self._post_mortem:
  1096. pdb.post_mortem(tr)
  1097. else:
  1098. reporter.test_pass()
  1099. taken = time.time() - start
  1100. if taken > self._slow_threshold:
  1101. filename = os.path.relpath(filename, reporter._root_dir)
  1102. reporter.slow_test_functions.append(
  1103. (filename + "::" + f.__name__, taken))
  1104. if getattr(f, '_slow', False) and slow:
  1105. if taken < self._fast_threshold:
  1106. filename = os.path.relpath(filename, reporter._root_dir)
  1107. reporter.fast_test_functions.append(
  1108. (filename + "::" + f.__name__, taken))
  1109. reporter.leaving_filename()
  1110. def _timeout(self, function, timeout, fail_on_timeout):
  1111. def callback(x, y):
  1112. signal.alarm(0)
  1113. if fail_on_timeout:
  1114. raise TimeOutError("Timed out after %d seconds" % timeout)
  1115. else:
  1116. raise Skipped("Timeout")
  1117. signal.signal(signal.SIGALRM, callback)
  1118. signal.alarm(timeout) # Set an alarm with a given timeout
  1119. function()
  1120. signal.alarm(0) # Disable the alarm
  1121. def matches(self, x):
  1122. """
  1123. Does the keyword expression self._kw match "x"? Returns True/False.
  1124. Always returns True if self._kw is "".
  1125. """
  1126. if not self._kw:
  1127. return True
  1128. for kw in self._kw:
  1129. if x.__name__.lower().find(kw.lower()) != -1:
  1130. return True
  1131. return False
  1132. def get_test_files(self, dir, pat='test_*.py'):
  1133. """
  1134. Returns the list of test_*.py (default) files at or below directory
  1135. ``dir`` relative to the SymPy home directory.
  1136. """
  1137. dir = os.path.join(self._root_dir, convert_to_native_paths([dir])[0])
  1138. g = []
  1139. for path, folders, files in os.walk(dir):
  1140. g.extend([os.path.join(path, f) for f in files if fnmatch(f, pat)])
  1141. return sorted([os.path.normcase(gi) for gi in g])
  1142. class SymPyDocTests:
  1143. def __init__(self, reporter, normal):
  1144. self._count = 0
  1145. self._root_dir = get_sympy_dir()
  1146. self._reporter = reporter
  1147. self._reporter.root_dir(self._root_dir)
  1148. self._normal = normal
  1149. self._testfiles = []
  1150. def test(self):
  1151. """
  1152. Runs the tests and returns True if all tests pass, otherwise False.
  1153. """
  1154. self._reporter.start()
  1155. for f in self._testfiles:
  1156. try:
  1157. self.test_file(f)
  1158. except KeyboardInterrupt:
  1159. print(" interrupted by user")
  1160. self._reporter.finish()
  1161. raise
  1162. return self._reporter.finish()
  1163. def test_file(self, filename):
  1164. clear_cache()
  1165. from io import StringIO
  1166. import sympy.interactive.printing as interactive_printing
  1167. from sympy.printing.pretty.pretty import pprint_use_unicode
  1168. from sympy.printing.pretty import stringpict
  1169. rel_name = filename[len(self._root_dir) + 1:]
  1170. dirname, file = os.path.split(filename)
  1171. module = rel_name.replace(os.sep, '.')[:-3]
  1172. if rel_name.startswith("examples"):
  1173. # Examples files do not have __init__.py files,
  1174. # So we have to temporarily extend sys.path to import them
  1175. sys.path.insert(0, dirname)
  1176. module = file[:-3] # remove ".py"
  1177. try:
  1178. module = pdoctest._normalize_module(module)
  1179. tests = SymPyDocTestFinder().find(module)
  1180. except (SystemExit, KeyboardInterrupt):
  1181. raise
  1182. except ImportError:
  1183. self._reporter.import_error(filename, sys.exc_info())
  1184. return
  1185. finally:
  1186. if rel_name.startswith("examples"):
  1187. del sys.path[0]
  1188. tests = [test for test in tests if len(test.examples) > 0]
  1189. # By default tests are sorted by alphabetical order by function name.
  1190. # We sort by line number so one can edit the file sequentially from
  1191. # bottom to top. However, if there are decorated functions, their line
  1192. # numbers will be too large and for now one must just search for these
  1193. # by text and function name.
  1194. tests.sort(key=lambda x: -x.lineno)
  1195. if not tests:
  1196. return
  1197. self._reporter.entering_filename(filename, len(tests))
  1198. for test in tests:
  1199. assert len(test.examples) != 0
  1200. if self._reporter._verbose:
  1201. self._reporter.write("\n{} ".format(test.name))
  1202. # check if there are external dependencies which need to be met
  1203. if '_doctest_depends_on' in test.globs:
  1204. try:
  1205. self._check_dependencies(**test.globs['_doctest_depends_on'])
  1206. except DependencyError as e:
  1207. self._reporter.test_skip(v=str(e))
  1208. continue
  1209. runner = SymPyDocTestRunner(verbose=self._reporter._verbose==2,
  1210. optionflags=pdoctest.ELLIPSIS |
  1211. pdoctest.NORMALIZE_WHITESPACE |
  1212. pdoctest.IGNORE_EXCEPTION_DETAIL)
  1213. runner._checker = SymPyOutputChecker()
  1214. old = sys.stdout
  1215. new = old if self._reporter._verbose==2 else StringIO()
  1216. sys.stdout = new
  1217. # If the testing is normal, the doctests get importing magic to
  1218. # provide the global namespace. If not normal (the default) then
  1219. # then must run on their own; all imports must be explicit within
  1220. # a function's docstring. Once imported that import will be
  1221. # available to the rest of the tests in a given function's
  1222. # docstring (unless clear_globs=True below).
  1223. if not self._normal:
  1224. test.globs = {}
  1225. # if this is uncommented then all the test would get is what
  1226. # comes by default with a "from sympy import *"
  1227. #exec('from sympy import *') in test.globs
  1228. old_displayhook = sys.displayhook
  1229. use_unicode_prev, wrap_line_prev = setup_pprint()
  1230. try:
  1231. f, t = runner.run(test,
  1232. out=new.write, clear_globs=False)
  1233. except KeyboardInterrupt:
  1234. raise
  1235. finally:
  1236. sys.stdout = old
  1237. if f > 0:
  1238. self._reporter.doctest_fail(test.name, new.getvalue())
  1239. else:
  1240. self._reporter.test_pass()
  1241. sys.displayhook = old_displayhook
  1242. interactive_printing.NO_GLOBAL = False
  1243. pprint_use_unicode(use_unicode_prev)
  1244. stringpict._GLOBAL_WRAP_LINE = wrap_line_prev
  1245. self._reporter.leaving_filename()
  1246. def get_test_files(self, dir, pat='*.py', init_only=True):
  1247. r"""
  1248. Returns the list of \*.py files (default) from which docstrings
  1249. will be tested which are at or below directory ``dir``. By default,
  1250. only those that have an __init__.py in their parent directory
  1251. and do not start with ``test_`` will be included.
  1252. """
  1253. def importable(x):
  1254. """
  1255. Checks if given pathname x is an importable module by checking for
  1256. __init__.py file.
  1257. Returns True/False.
  1258. Currently we only test if the __init__.py file exists in the
  1259. directory with the file "x" (in theory we should also test all the
  1260. parent dirs).
  1261. """
  1262. init_py = os.path.join(os.path.dirname(x), "__init__.py")
  1263. return os.path.exists(init_py)
  1264. dir = os.path.join(self._root_dir, convert_to_native_paths([dir])[0])
  1265. g = []
  1266. for path, folders, files in os.walk(dir):
  1267. g.extend([os.path.join(path, f) for f in files
  1268. if not f.startswith('test_') and fnmatch(f, pat)])
  1269. if init_only:
  1270. # skip files that are not importable (i.e. missing __init__.py)
  1271. g = [x for x in g if importable(x)]
  1272. return [os.path.normcase(gi) for gi in g]
  1273. def _check_dependencies(self,
  1274. executables=(),
  1275. modules=(),
  1276. disable_viewers=(),
  1277. python_version=(3, 5),
  1278. ground_types=None):
  1279. """
  1280. Checks if the dependencies for the test are installed.
  1281. Raises ``DependencyError`` it at least one dependency is not installed.
  1282. """
  1283. for executable in executables:
  1284. if not shutil.which(executable):
  1285. raise DependencyError("Could not find %s" % executable)
  1286. for module in modules:
  1287. if module == 'matplotlib':
  1288. matplotlib = import_module(
  1289. 'matplotlib',
  1290. import_kwargs={'fromlist':
  1291. ['pyplot', 'cm', 'collections']},
  1292. min_module_version='1.0.0', catch=(RuntimeError,))
  1293. if matplotlib is None:
  1294. raise DependencyError("Could not import matplotlib")
  1295. else:
  1296. if not import_module(module):
  1297. raise DependencyError("Could not import %s" % module)
  1298. if disable_viewers:
  1299. tempdir = tempfile.mkdtemp()
  1300. os.environ['PATH'] = '%s:%s' % (tempdir, os.environ['PATH'])
  1301. vw = ('#!/usr/bin/env python3\n'
  1302. 'import sys\n'
  1303. 'if len(sys.argv) <= 1:\n'
  1304. ' exit("wrong number of args")\n')
  1305. for viewer in disable_viewers:
  1306. Path(os.path.join(tempdir, viewer)).write_text(vw)
  1307. # make the file executable
  1308. os.chmod(os.path.join(tempdir, viewer),
  1309. stat.S_IREAD | stat.S_IWRITE | stat.S_IXUSR)
  1310. if python_version:
  1311. if sys.version_info < python_version:
  1312. raise DependencyError("Requires Python >= " + '.'.join(map(str, python_version)))
  1313. if ground_types is not None:
  1314. if GROUND_TYPES not in ground_types:
  1315. raise DependencyError("Requires ground_types in " + str(ground_types))
  1316. if 'pyglet' in modules:
  1317. # monkey-patch pyglet s.t. it does not open a window during
  1318. # doctesting
  1319. import pyglet
  1320. class DummyWindow:
  1321. def __init__(self, *args, **kwargs):
  1322. self.has_exit = True
  1323. self.width = 600
  1324. self.height = 400
  1325. def set_vsync(self, x):
  1326. pass
  1327. def switch_to(self):
  1328. pass
  1329. def push_handlers(self, x):
  1330. pass
  1331. def close(self):
  1332. pass
  1333. pyglet.window.Window = DummyWindow
  1334. class SymPyDocTestFinder(DocTestFinder):
  1335. """
  1336. A class used to extract the DocTests that are relevant to a given
  1337. object, from its docstring and the docstrings of its contained
  1338. objects. Doctests can currently be extracted from the following
  1339. object types: modules, functions, classes, methods, staticmethods,
  1340. classmethods, and properties.
  1341. Modified from doctest's version to look harder for code that
  1342. appears comes from a different module. For example, the @vectorize
  1343. decorator makes it look like functions come from multidimensional.py
  1344. even though their code exists elsewhere.
  1345. """
  1346. def _find(self, tests, obj, name, module, source_lines, globs, seen):
  1347. """
  1348. Find tests for the given object and any contained objects, and
  1349. add them to ``tests``.
  1350. """
  1351. if self._verbose:
  1352. print('Finding tests in %s' % name)
  1353. # If we've already processed this object, then ignore it.
  1354. if id(obj) in seen:
  1355. return
  1356. seen[id(obj)] = 1
  1357. # Make sure we don't run doctests for classes outside of sympy, such
  1358. # as in numpy or scipy.
  1359. if inspect.isclass(obj):
  1360. if obj.__module__.split('.')[0] != 'sympy':
  1361. return
  1362. # Find a test for this object, and add it to the list of tests.
  1363. test = self._get_test(obj, name, module, globs, source_lines)
  1364. if test is not None:
  1365. tests.append(test)
  1366. if not self._recurse:
  1367. return
  1368. # Look for tests in a module's contained objects.
  1369. if inspect.ismodule(obj):
  1370. for rawname, val in obj.__dict__.items():
  1371. # Recurse to functions & classes.
  1372. if inspect.isfunction(val) or inspect.isclass(val):
  1373. # Make sure we don't run doctests functions or classes
  1374. # from different modules
  1375. if val.__module__ != module.__name__:
  1376. continue
  1377. assert self._from_module(module, val), \
  1378. "%s is not in module %s (rawname %s)" % (val, module, rawname)
  1379. try:
  1380. valname = '%s.%s' % (name, rawname)
  1381. self._find(tests, val, valname, module,
  1382. source_lines, globs, seen)
  1383. except KeyboardInterrupt:
  1384. raise
  1385. # Look for tests in a module's __test__ dictionary.
  1386. for valname, val in getattr(obj, '__test__', {}).items():
  1387. if not isinstance(valname, str):
  1388. raise ValueError("SymPyDocTestFinder.find: __test__ keys "
  1389. "must be strings: %r" %
  1390. (type(valname),))
  1391. if not (inspect.isfunction(val) or inspect.isclass(val) or
  1392. inspect.ismethod(val) or inspect.ismodule(val) or
  1393. isinstance(val, str)):
  1394. raise ValueError("SymPyDocTestFinder.find: __test__ values "
  1395. "must be strings, functions, methods, "
  1396. "classes, or modules: %r" %
  1397. (type(val),))
  1398. valname = '%s.__test__.%s' % (name, valname)
  1399. self._find(tests, val, valname, module, source_lines,
  1400. globs, seen)
  1401. # Look for tests in a class's contained objects.
  1402. if inspect.isclass(obj):
  1403. for valname, val in obj.__dict__.items():
  1404. # Special handling for staticmethod/classmethod.
  1405. if isinstance(val, staticmethod):
  1406. val = getattr(obj, valname)
  1407. if isinstance(val, classmethod):
  1408. val = getattr(obj, valname).__func__
  1409. # Recurse to methods, properties, and nested classes.
  1410. if ((inspect.isfunction(unwrap(val)) or
  1411. inspect.isclass(val) or
  1412. isinstance(val, property)) and
  1413. self._from_module(module, val)):
  1414. # Make sure we don't run doctests functions or classes
  1415. # from different modules
  1416. if isinstance(val, property):
  1417. if hasattr(val.fget, '__module__'):
  1418. if val.fget.__module__ != module.__name__:
  1419. continue
  1420. else:
  1421. if val.__module__ != module.__name__:
  1422. continue
  1423. assert self._from_module(module, val), \
  1424. "%s is not in module %s (valname %s)" % (
  1425. val, module, valname)
  1426. valname = '%s.%s' % (name, valname)
  1427. self._find(tests, val, valname, module, source_lines,
  1428. globs, seen)
  1429. def _get_test(self, obj, name, module, globs, source_lines):
  1430. """
  1431. Return a DocTest for the given object, if it defines a docstring;
  1432. otherwise, return None.
  1433. """
  1434. lineno = None
  1435. # Extract the object's docstring. If it does not have one,
  1436. # then return None (no test for this object).
  1437. if isinstance(obj, str):
  1438. # obj is a string in the case for objects in the polys package.
  1439. # Note that source_lines is a binary string (compiled polys
  1440. # modules), which can't be handled by _find_lineno so determine
  1441. # the line number here.
  1442. docstring = obj
  1443. matches = re.findall(r"line \d+", name)
  1444. assert len(matches) == 1, \
  1445. "string '%s' does not contain lineno " % name
  1446. # NOTE: this is not the exact linenumber but its better than no
  1447. # lineno ;)
  1448. lineno = int(matches[0][5:])
  1449. else:
  1450. docstring = getattr(obj, '__doc__', '')
  1451. if docstring is None:
  1452. docstring = ''
  1453. if not isinstance(docstring, str):
  1454. docstring = str(docstring)
  1455. # Don't bother if the docstring is empty.
  1456. if self._exclude_empty and not docstring:
  1457. return None
  1458. # check that properties have a docstring because _find_lineno
  1459. # assumes it
  1460. if isinstance(obj, property):
  1461. if obj.fget.__doc__ is None:
  1462. return None
  1463. # Find the docstring's location in the file.
  1464. if lineno is None:
  1465. obj = unwrap(obj)
  1466. # handling of properties is not implemented in _find_lineno so do
  1467. # it here
  1468. if hasattr(obj, 'func_closure') and obj.func_closure is not None:
  1469. tobj = obj.func_closure[0].cell_contents
  1470. elif isinstance(obj, property):
  1471. tobj = obj.fget
  1472. else:
  1473. tobj = obj
  1474. lineno = self._find_lineno(tobj, source_lines)
  1475. if lineno is None:
  1476. return None
  1477. # Return a DocTest for this object.
  1478. if module is None:
  1479. filename = None
  1480. else:
  1481. filename = getattr(module, '__file__', module.__name__)
  1482. if filename[-4:] in (".pyc", ".pyo"):
  1483. filename = filename[:-1]
  1484. globs['_doctest_depends_on'] = getattr(obj, '_doctest_depends_on', {})
  1485. return self._parser.get_doctest(docstring, globs, name,
  1486. filename, lineno)
  1487. class SymPyDocTestRunner(DocTestRunner):
  1488. """
  1489. A class used to run DocTest test cases, and accumulate statistics.
  1490. The ``run`` method is used to process a single DocTest case. It
  1491. returns a tuple ``(f, t)``, where ``t`` is the number of test cases
  1492. tried, and ``f`` is the number of test cases that failed.
  1493. Modified from the doctest version to not reset the sys.displayhook (see
  1494. issue 5140).
  1495. See the docstring of the original DocTestRunner for more information.
  1496. """
  1497. def run(self, test, compileflags=None, out=None, clear_globs=True):
  1498. """
  1499. Run the examples in ``test``, and display the results using the
  1500. writer function ``out``.
  1501. The examples are run in the namespace ``test.globs``. If
  1502. ``clear_globs`` is true (the default), then this namespace will
  1503. be cleared after the test runs, to help with garbage
  1504. collection. If you would like to examine the namespace after
  1505. the test completes, then use ``clear_globs=False``.
  1506. ``compileflags`` gives the set of flags that should be used by
  1507. the Python compiler when running the examples. If not
  1508. specified, then it will default to the set of future-import
  1509. flags that apply to ``globs``.
  1510. The output of each example is checked using
  1511. ``SymPyDocTestRunner.check_output``, and the results are
  1512. formatted by the ``SymPyDocTestRunner.report_*`` methods.
  1513. """
  1514. self.test = test
  1515. # Remove ``` from the end of example, which may appear in Markdown
  1516. # files
  1517. for example in test.examples:
  1518. example.want = example.want.replace('```\n', '')
  1519. example.exc_msg = example.exc_msg and example.exc_msg.replace('```\n', '')
  1520. if compileflags is None:
  1521. compileflags = pdoctest._extract_future_flags(test.globs)
  1522. save_stdout = sys.stdout
  1523. if out is None:
  1524. out = save_stdout.write
  1525. sys.stdout = self._fakeout
  1526. # Patch pdb.set_trace to restore sys.stdout during interactive
  1527. # debugging (so it's not still redirected to self._fakeout).
  1528. # Note that the interactive output will go to *our*
  1529. # save_stdout, even if that's not the real sys.stdout; this
  1530. # allows us to write test cases for the set_trace behavior.
  1531. save_set_trace = pdb.set_trace
  1532. self.debugger = pdoctest._OutputRedirectingPdb(save_stdout)
  1533. self.debugger.reset()
  1534. pdb.set_trace = self.debugger.set_trace
  1535. # Patch linecache.getlines, so we can see the example's source
  1536. # when we're inside the debugger.
  1537. self.save_linecache_getlines = pdoctest.linecache.getlines
  1538. linecache.getlines = self.__patched_linecache_getlines
  1539. # Fail for deprecation warnings
  1540. with raise_on_deprecated():
  1541. try:
  1542. return self.__run(test, compileflags, out)
  1543. finally:
  1544. sys.stdout = save_stdout
  1545. pdb.set_trace = save_set_trace
  1546. linecache.getlines = self.save_linecache_getlines
  1547. if clear_globs:
  1548. test.globs.clear()
  1549. # We have to override the name mangled methods.
  1550. monkeypatched_methods = [
  1551. 'patched_linecache_getlines',
  1552. 'run',
  1553. 'record_outcome'
  1554. ]
  1555. for method in monkeypatched_methods:
  1556. oldname = '_DocTestRunner__' + method
  1557. newname = '_SymPyDocTestRunner__' + method
  1558. setattr(SymPyDocTestRunner, newname, getattr(DocTestRunner, oldname))
  1559. class SymPyOutputChecker(pdoctest.OutputChecker):
  1560. """
  1561. Compared to the OutputChecker from the stdlib our OutputChecker class
  1562. supports numerical comparison of floats occurring in the output of the
  1563. doctest examples
  1564. """
  1565. def __init__(self):
  1566. # NOTE OutputChecker is an old-style class with no __init__ method,
  1567. # so we can't call the base class version of __init__ here
  1568. got_floats = r'(\d+\.\d*|\.\d+)'
  1569. # floats in the 'want' string may contain ellipses
  1570. want_floats = got_floats + r'(\.{3})?'
  1571. front_sep = r'\s|\+|\-|\*|,'
  1572. back_sep = front_sep + r'|j|e'
  1573. fbeg = r'^%s(?=%s|$)' % (got_floats, back_sep)
  1574. fmidend = r'(?<=%s)%s(?=%s|$)' % (front_sep, got_floats, back_sep)
  1575. self.num_got_rgx = re.compile(r'(%s|%s)' %(fbeg, fmidend))
  1576. fbeg = r'^%s(?=%s|$)' % (want_floats, back_sep)
  1577. fmidend = r'(?<=%s)%s(?=%s|$)' % (front_sep, want_floats, back_sep)
  1578. self.num_want_rgx = re.compile(r'(%s|%s)' %(fbeg, fmidend))
  1579. def check_output(self, want, got, optionflags):
  1580. """
  1581. Return True iff the actual output from an example (`got`)
  1582. matches the expected output (`want`). These strings are
  1583. always considered to match if they are identical; but
  1584. depending on what option flags the test runner is using,
  1585. several non-exact match types are also possible. See the
  1586. documentation for `TestRunner` for more information about
  1587. option flags.
  1588. """
  1589. # Handle the common case first, for efficiency:
  1590. # if they're string-identical, always return true.
  1591. if got == want:
  1592. return True
  1593. # TODO parse integers as well ?
  1594. # Parse floats and compare them. If some of the parsed floats contain
  1595. # ellipses, skip the comparison.
  1596. matches = self.num_got_rgx.finditer(got)
  1597. numbers_got = [match.group(1) for match in matches] # list of strs
  1598. matches = self.num_want_rgx.finditer(want)
  1599. numbers_want = [match.group(1) for match in matches] # list of strs
  1600. if len(numbers_got) != len(numbers_want):
  1601. return False
  1602. if len(numbers_got) > 0:
  1603. nw_ = []
  1604. for ng, nw in zip(numbers_got, numbers_want):
  1605. if '...' in nw:
  1606. nw_.append(ng)
  1607. continue
  1608. else:
  1609. nw_.append(nw)
  1610. if abs(float(ng)-float(nw)) > 1e-5:
  1611. return False
  1612. got = self.num_got_rgx.sub(r'%s', got)
  1613. got = got % tuple(nw_)
  1614. # <BLANKLINE> can be used as a special sequence to signify a
  1615. # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used.
  1616. if not (optionflags & pdoctest.DONT_ACCEPT_BLANKLINE):
  1617. # Replace <BLANKLINE> in want with a blank line.
  1618. want = re.sub(r'(?m)^%s\s*?$' % re.escape(pdoctest.BLANKLINE_MARKER),
  1619. '', want)
  1620. # If a line in got contains only spaces, then remove the
  1621. # spaces.
  1622. got = re.sub(r'(?m)^\s*?$', '', got)
  1623. if got == want:
  1624. return True
  1625. # This flag causes doctest to ignore any differences in the
  1626. # contents of whitespace strings. Note that this can be used
  1627. # in conjunction with the ELLIPSIS flag.
  1628. if optionflags & pdoctest.NORMALIZE_WHITESPACE:
  1629. got = ' '.join(got.split())
  1630. want = ' '.join(want.split())
  1631. if got == want:
  1632. return True
  1633. # The ELLIPSIS flag says to let the sequence "..." in `want`
  1634. # match any substring in `got`.
  1635. if optionflags & pdoctest.ELLIPSIS:
  1636. if pdoctest._ellipsis_match(want, got):
  1637. return True
  1638. # We didn't find any match; return false.
  1639. return False
  1640. class Reporter:
  1641. """
  1642. Parent class for all reporters.
  1643. """
  1644. pass
  1645. class PyTestReporter(Reporter):
  1646. """
  1647. Py.test like reporter. Should produce output identical to py.test.
  1648. """
  1649. def __init__(self, verbose=False, tb="short", colors=True,
  1650. force_colors=False, split=None):
  1651. self._verbose = verbose
  1652. self._tb_style = tb
  1653. self._colors = colors
  1654. self._force_colors = force_colors
  1655. self._xfailed = 0
  1656. self._xpassed = []
  1657. self._failed = []
  1658. self._failed_doctest = []
  1659. self._passed = 0
  1660. self._skipped = 0
  1661. self._exceptions = []
  1662. self._terminal_width = None
  1663. self._default_width = 80
  1664. self._split = split
  1665. self._active_file = ''
  1666. self._active_f = None
  1667. # TODO: Should these be protected?
  1668. self.slow_test_functions = []
  1669. self.fast_test_functions = []
  1670. # this tracks the x-position of the cursor (useful for positioning
  1671. # things on the screen), without the need for any readline library:
  1672. self._write_pos = 0
  1673. self._line_wrap = False
  1674. def root_dir(self, dir):
  1675. self._root_dir = dir
  1676. @property
  1677. def terminal_width(self):
  1678. if self._terminal_width is not None:
  1679. return self._terminal_width
  1680. def findout_terminal_width():
  1681. if sys.platform == "win32":
  1682. # Windows support is based on:
  1683. #
  1684. # http://code.activestate.com/recipes/
  1685. # 440694-determine-size-of-console-window-on-windows/
  1686. from ctypes import windll, create_string_buffer
  1687. h = windll.kernel32.GetStdHandle(-12)
  1688. csbi = create_string_buffer(22)
  1689. res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
  1690. if res:
  1691. import struct
  1692. (_, _, _, _, _, left, _, right, _, _, _) = \
  1693. struct.unpack("hhhhHhhhhhh", csbi.raw)
  1694. return right - left
  1695. else:
  1696. return self._default_width
  1697. if hasattr(sys.stdout, 'isatty') and not sys.stdout.isatty():
  1698. return self._default_width # leave PIPEs alone
  1699. try:
  1700. process = subprocess.Popen(['stty', '-a'],
  1701. stdout=subprocess.PIPE,
  1702. stderr=subprocess.PIPE)
  1703. stdout, stderr = process.communicate()
  1704. stdout = stdout.decode("utf-8")
  1705. except OSError:
  1706. pass
  1707. else:
  1708. # We support the following output formats from stty:
  1709. #
  1710. # 1) Linux -> columns 80
  1711. # 2) OS X -> 80 columns
  1712. # 3) Solaris -> columns = 80
  1713. re_linux = r"columns\s+(?P<columns>\d+);"
  1714. re_osx = r"(?P<columns>\d+)\s*columns;"
  1715. re_solaris = r"columns\s+=\s+(?P<columns>\d+);"
  1716. for regex in (re_linux, re_osx, re_solaris):
  1717. match = re.search(regex, stdout)
  1718. if match is not None:
  1719. columns = match.group('columns')
  1720. try:
  1721. width = int(columns)
  1722. except ValueError:
  1723. pass
  1724. if width != 0:
  1725. return width
  1726. return self._default_width
  1727. width = findout_terminal_width()
  1728. self._terminal_width = width
  1729. return width
  1730. def write(self, text, color="", align="left", width=None,
  1731. force_colors=False):
  1732. """
  1733. Prints a text on the screen.
  1734. It uses sys.stdout.write(), so no readline library is necessary.
  1735. Parameters
  1736. ==========
  1737. color : choose from the colors below, "" means default color
  1738. align : "left"/"right", "left" is a normal print, "right" is aligned on
  1739. the right-hand side of the screen, filled with spaces if
  1740. necessary
  1741. width : the screen width
  1742. """
  1743. color_templates = (
  1744. ("Black", "0;30"),
  1745. ("Red", "0;31"),
  1746. ("Green", "0;32"),
  1747. ("Brown", "0;33"),
  1748. ("Blue", "0;34"),
  1749. ("Purple", "0;35"),
  1750. ("Cyan", "0;36"),
  1751. ("LightGray", "0;37"),
  1752. ("DarkGray", "1;30"),
  1753. ("LightRed", "1;31"),
  1754. ("LightGreen", "1;32"),
  1755. ("Yellow", "1;33"),
  1756. ("LightBlue", "1;34"),
  1757. ("LightPurple", "1;35"),
  1758. ("LightCyan", "1;36"),
  1759. ("White", "1;37"),
  1760. )
  1761. colors = {}
  1762. for name, value in color_templates:
  1763. colors[name] = value
  1764. c_normal = '\033[0m'
  1765. c_color = '\033[%sm'
  1766. if width is None:
  1767. width = self.terminal_width
  1768. if align == "right":
  1769. if self._write_pos + len(text) > width:
  1770. # we don't fit on the current line, create a new line
  1771. self.write("\n")
  1772. self.write(" "*(width - self._write_pos - len(text)))
  1773. if not self._force_colors and hasattr(sys.stdout, 'isatty') and not \
  1774. sys.stdout.isatty():
  1775. # the stdout is not a terminal, this for example happens if the
  1776. # output is piped to less, e.g. "bin/test | less". In this case,
  1777. # the terminal control sequences would be printed verbatim, so
  1778. # don't use any colors.
  1779. color = ""
  1780. elif sys.platform == "win32":
  1781. # Windows consoles don't support ANSI escape sequences
  1782. color = ""
  1783. elif not self._colors:
  1784. color = ""
  1785. if self._line_wrap:
  1786. if text[0] != "\n":
  1787. sys.stdout.write("\n")
  1788. # Avoid UnicodeEncodeError when printing out test failures
  1789. if IS_WINDOWS:
  1790. text = text.encode('raw_unicode_escape').decode('utf8', 'ignore')
  1791. elif not sys.stdout.encoding.lower().startswith('utf'):
  1792. text = text.encode(sys.stdout.encoding, 'backslashreplace'
  1793. ).decode(sys.stdout.encoding)
  1794. if color == "":
  1795. sys.stdout.write(text)
  1796. else:
  1797. sys.stdout.write("%s%s%s" %
  1798. (c_color % colors[color], text, c_normal))
  1799. sys.stdout.flush()
  1800. l = text.rfind("\n")
  1801. if l == -1:
  1802. self._write_pos += len(text)
  1803. else:
  1804. self._write_pos = len(text) - l - 1
  1805. self._line_wrap = self._write_pos >= width
  1806. self._write_pos %= width
  1807. def write_center(self, text, delim="="):
  1808. width = self.terminal_width
  1809. if text != "":
  1810. text = " %s " % text
  1811. idx = (width - len(text)) // 2
  1812. t = delim*idx + text + delim*(width - idx - len(text))
  1813. self.write(t + "\n")
  1814. def write_exception(self, e, val, tb):
  1815. # remove the first item, as that is always runtests.py
  1816. tb = tb.tb_next
  1817. t = traceback.format_exception(e, val, tb)
  1818. self.write("".join(t))
  1819. def start(self, seed=None, msg="test process starts"):
  1820. self.write_center(msg)
  1821. executable = sys.executable
  1822. v = tuple(sys.version_info)
  1823. python_version = "%s.%s.%s-%s-%s" % v
  1824. implementation = platform.python_implementation()
  1825. if implementation == 'PyPy':
  1826. implementation += " %s.%s.%s-%s-%s" % sys.pypy_version_info
  1827. self.write("executable: %s (%s) [%s]\n" %
  1828. (executable, python_version, implementation))
  1829. from sympy.utilities.misc import ARCH
  1830. self.write("architecture: %s\n" % ARCH)
  1831. from sympy.core.cache import USE_CACHE
  1832. self.write("cache: %s\n" % USE_CACHE)
  1833. version = ''
  1834. if GROUND_TYPES =='gmpy':
  1835. import gmpy2 as gmpy
  1836. version = gmpy.version()
  1837. self.write("ground types: %s %s\n" % (GROUND_TYPES, version))
  1838. numpy = import_module('numpy')
  1839. self.write("numpy: %s\n" % (None if not numpy else numpy.__version__))
  1840. if seed is not None:
  1841. self.write("random seed: %d\n" % seed)
  1842. from sympy.utilities.misc import HASH_RANDOMIZATION
  1843. self.write("hash randomization: ")
  1844. hash_seed = os.getenv("PYTHONHASHSEED") or '0'
  1845. if HASH_RANDOMIZATION and (hash_seed == "random" or int(hash_seed)):
  1846. self.write("on (PYTHONHASHSEED=%s)\n" % hash_seed)
  1847. else:
  1848. self.write("off\n")
  1849. if self._split:
  1850. self.write("split: %s\n" % self._split)
  1851. self.write('\n')
  1852. self._t_start = clock()
  1853. def finish(self):
  1854. self._t_end = clock()
  1855. self.write("\n")
  1856. global text, linelen
  1857. text = "tests finished: %d passed, " % self._passed
  1858. linelen = len(text)
  1859. def add_text(mytext):
  1860. global text, linelen
  1861. """Break new text if too long."""
  1862. if linelen + len(mytext) > self.terminal_width:
  1863. text += '\n'
  1864. linelen = 0
  1865. text += mytext
  1866. linelen += len(mytext)
  1867. if len(self._failed) > 0:
  1868. add_text("%d failed, " % len(self._failed))
  1869. if len(self._failed_doctest) > 0:
  1870. add_text("%d failed, " % len(self._failed_doctest))
  1871. if self._skipped > 0:
  1872. add_text("%d skipped, " % self._skipped)
  1873. if self._xfailed > 0:
  1874. add_text("%d expected to fail, " % self._xfailed)
  1875. if len(self._xpassed) > 0:
  1876. add_text("%d expected to fail but passed, " % len(self._xpassed))
  1877. if len(self._exceptions) > 0:
  1878. add_text("%d exceptions, " % len(self._exceptions))
  1879. add_text("in %.2f seconds" % (self._t_end - self._t_start))
  1880. if self.slow_test_functions:
  1881. self.write_center('slowest tests', '_')
  1882. sorted_slow = sorted(self.slow_test_functions, key=lambda r: r[1])
  1883. for slow_func_name, taken in sorted_slow:
  1884. print('%s - Took %.3f seconds' % (slow_func_name, taken))
  1885. if self.fast_test_functions:
  1886. self.write_center('unexpectedly fast tests', '_')
  1887. sorted_fast = sorted(self.fast_test_functions,
  1888. key=lambda r: r[1])
  1889. for fast_func_name, taken in sorted_fast:
  1890. print('%s - Took %.3f seconds' % (fast_func_name, taken))
  1891. if len(self._xpassed) > 0:
  1892. self.write_center("xpassed tests", "_")
  1893. for e in self._xpassed:
  1894. self.write("%s: %s\n" % (e[0], e[1]))
  1895. self.write("\n")
  1896. if self._tb_style != "no" and len(self._exceptions) > 0:
  1897. for e in self._exceptions:
  1898. filename, f, (t, val, tb) = e
  1899. self.write_center("", "_")
  1900. if f is None:
  1901. s = "%s" % filename
  1902. else:
  1903. s = "%s:%s" % (filename, f.__name__)
  1904. self.write_center(s, "_")
  1905. self.write_exception(t, val, tb)
  1906. self.write("\n")
  1907. if self._tb_style != "no" and len(self._failed) > 0:
  1908. for e in self._failed:
  1909. filename, f, (t, val, tb) = e
  1910. self.write_center("", "_")
  1911. self.write_center("%s::%s" % (filename, f.__name__), "_")
  1912. self.write_exception(t, val, tb)
  1913. self.write("\n")
  1914. if self._tb_style != "no" and len(self._failed_doctest) > 0:
  1915. for e in self._failed_doctest:
  1916. filename, msg = e
  1917. self.write_center("", "_")
  1918. self.write_center("%s" % filename, "_")
  1919. self.write(msg)
  1920. self.write("\n")
  1921. self.write_center(text)
  1922. ok = len(self._failed) == 0 and len(self._exceptions) == 0 and \
  1923. len(self._failed_doctest) == 0
  1924. if not ok:
  1925. self.write("DO *NOT* COMMIT!\n")
  1926. return ok
  1927. def entering_filename(self, filename, n):
  1928. rel_name = filename[len(self._root_dir) + 1:]
  1929. self._active_file = rel_name
  1930. self._active_file_error = False
  1931. self.write(rel_name)
  1932. self.write("[%d] " % n)
  1933. def leaving_filename(self):
  1934. self.write(" ")
  1935. if self._active_file_error:
  1936. self.write("[FAIL]", "Red", align="right")
  1937. else:
  1938. self.write("[OK]", "Green", align="right")
  1939. self.write("\n")
  1940. if self._verbose:
  1941. self.write("\n")
  1942. def entering_test(self, f):
  1943. self._active_f = f
  1944. if self._verbose:
  1945. self.write("\n" + f.__name__ + " ")
  1946. def test_xfail(self):
  1947. self._xfailed += 1
  1948. self.write("f", "Green")
  1949. def test_xpass(self, v):
  1950. message = str(v)
  1951. self._xpassed.append((self._active_file, message))
  1952. self.write("X", "Green")
  1953. def test_fail(self, exc_info):
  1954. self._failed.append((self._active_file, self._active_f, exc_info))
  1955. self.write("F", "Red")
  1956. self._active_file_error = True
  1957. def doctest_fail(self, name, error_msg):
  1958. # the first line contains "******", remove it:
  1959. error_msg = "\n".join(error_msg.split("\n")[1:])
  1960. self._failed_doctest.append((name, error_msg))
  1961. self.write("F", "Red")
  1962. self._active_file_error = True
  1963. def test_pass(self, char="."):
  1964. self._passed += 1
  1965. if self._verbose:
  1966. self.write("ok", "Green")
  1967. else:
  1968. self.write(char, "Green")
  1969. def test_skip(self, v=None):
  1970. char = "s"
  1971. self._skipped += 1
  1972. if v is not None:
  1973. message = str(v)
  1974. if message == "KeyboardInterrupt":
  1975. char = "K"
  1976. elif message == "Timeout":
  1977. char = "T"
  1978. elif message == "Slow":
  1979. char = "w"
  1980. if self._verbose:
  1981. if v is not None:
  1982. self.write(message + ' ', "Blue")
  1983. else:
  1984. self.write(" - ", "Blue")
  1985. self.write(char, "Blue")
  1986. def test_exception(self, exc_info):
  1987. self._exceptions.append((self._active_file, self._active_f, exc_info))
  1988. if exc_info[0] is TimeOutError:
  1989. self.write("T", "Red")
  1990. else:
  1991. self.write("E", "Red")
  1992. self._active_file_error = True
  1993. def import_error(self, filename, exc_info):
  1994. self._exceptions.append((filename, None, exc_info))
  1995. rel_name = filename[len(self._root_dir) + 1:]
  1996. self.write(rel_name)
  1997. self.write("[?] Failed to import", "Red")
  1998. self.write(" ")
  1999. self.write("[FAIL]", "Red", align="right")
  2000. self.write("\n")