Source code for utool.util_tests

# -*- coding: utf-8 -*-
"""
Helpers for tests

This module contains a more sane reimplementation of doctest functionality.
(I.E.  asserts work and you don't have to worry about stdout mucking things up)
The code isn't super clean though due to time constriaints.  Many functions
probably belong elsewhere and the parsers need a big cleanup.

TODO:
    * report the line of the doctest in the file when reporting errors as well as
     the relative line

    * restructure so there is a test collection step, a filtering step, and an
      execution step

    * Fix finding tests when running with @profile
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import six
#from six.moves import builtins
from collections import namedtuple
import inspect
import types
import traceback  # NOQA
import sys
from os.path import basename
from utool import util_print  # NOQA
from utool import util_arg
#from utool import util_path
from utool import util_time
from utool import util_inject
from utool import util_dbg
from utool import util_dev
from utool._internal import meta_util_six
from utool._internal.meta_util_six import get_funcname
print, rrr, profile = util_inject.inject2(__name__, '[tests]')


VERBOSE_TEST = util_arg.get_argflag(('--verbtest', '--verb-test', '--verbose-test'))
#PRINT_SRC = not util_arg.get_argflag(('--noprintsrc', '--nosrc'))
DEBUG_SRC = not util_arg.get_argflag('--nodbgsrc')
PRINT_SRC = util_arg.get_argflag(('--printsrc', '--src', '--show-src', '--showsrc'),
                                 help_='show docstring source when running tests')
#PRINT_FACE = not util_arg.get_argflag(('--noprintface', '--noface'))
PRINT_FACE = util_arg.get_argflag(('--printface', '--face'))
#BIGFACE = False
BIGFACE = util_arg.get_argflag('--bigface')
SYSEXIT_ON_FAIL = util_arg.get_argflag(('--sysexitonfail', '--fastfail'),
                                       help_='Force testing harness to exit on first test failure')
VERBOSE_TIMER = not util_arg.get_argflag('--no-time-tests')
INDENT_TEST   = False
#EXEC_MODE = util_arg.get_argflag('--exec-mode', help_='dummy flag that will be removed')

ModuleDoctestTup = namedtuple('ModuleDoctestTup', ('enabled_testtup_list',
                                                   'frame_fpath',
                                                   'all_testflags', 'module'))


[docs]class TestTuple(util_dev.NiceRepr): """ Simple container for test objects to replace old tuple format exec mode specifies if the test is being run as a script """ def __init__(self, name, num, src, want, flag, tags=None, frame_fpath=None, exec_mode=False): self.name = name # function / class / testable name self.num = num # doctest index self.src = src # doctest src self.want = want # doctest required result (optional) self.flag = flag # doctest commandline flags self.frame_fpath = frame_fpath # parent file fpath self.exec_mode = exec_mode # flags if running as script if tags is None and src is not None: # hack to parse tag from source tagline = src.split('\n')[0].strip() if tagline.startswith('#'): tagline = tagline[1:].strip() tagline = tagline.replace('_DOCTEST', '') tags = tagline.split(',') self.tags = tags def __nice__(self): tagstr = ' ' + ','.join(self.tags) if self.tags is not None else '' return ' ' + self.name + ':' + str(self.num) + tagstr #def __repr__(self): # custom = # return '<%s%s at %s>' % (self.__class__.__name__, custom, hex(id(self)),) #def __str__(self): # custom = ' ' + self.name + ':' + str(self.num) # return '<%s%s>' % (self.__class__.__name__, custom,) ##debug_decor = lambda func: func #if VERBOSE_TEST: # from utool import util_decor # #debug_decor = util_decor.indent_func # #debug_decor = util_decor.tracefunc
HAPPY_FACE_BIG = r''' .-""""""-. .' '. / O O \ : : | | ' , ,' : \ '-......-' / '. .' '-......-' ''' SAD_FACE_BIG = r''' .-""""""-. .' '. / O O \ : ` : | | : .------. : \ ' ' / '. .' '-......-' ''' HAPPY_FACE_SMALL = r''' .""". | o o | | \_/ | ' = ' ''' SAD_FACE_SMALL = r''' .""". | . . | | ~ | ' = ' ''' if BIGFACE: HAPPY_FACE = HAPPY_FACE_BIG SAD_FACE = SAD_FACE_BIG else: HAPPY_FACE = HAPPY_FACE_SMALL #SAD_FACE = SAD_FACE_BIG SAD_FACE = SAD_FACE_SMALL
[docs]def get_package_testables(module=None, **tagkw): r""" New command that should eventually be used intead of old stuff? Args: module_list (list): (default = None) test_flags (None): (default = None) CommandLine: python -m utool.util_tests --exec-get_package_testables --show --mod ibeis python -m utool.util_tests --exec-get_package_testables --show --mod utool --tags SCRIPT python -m utool.util_tests --exec-get_package_testables --show --mod utool --tags ENABLE Example: >>> # DISABLE_DOCTEST >>> from utool.util_tests import * # NOQA >>> import utool as ut >>> has_any = ut.get_argval('--tags', type_=str, default=None) >>> module = ut.get_argval('--mod', default='utool') >>> test_tuples = get_package_testables(module, has_any=has_any) >>> result = ut.repr3(test_tuples) >>> print(result) >>> #print(ut.repr3(ut.list_getattr(test_tuples, 'tags'))) """ import utool as ut if isinstance(module, six.string_types): module = ut.import_modname(module) modname_list = ut.package_contents(module, ignore_prefix=[], ignore_suffix=[]) module_list = [] for modname in modname_list: try: module_list.append(ut.import_modname(modname)) except Exception: pass test_tuples = [] for module in module_list: old_testables = ut.get_module_doctest_tup(module=module, needs_enable=False, allexamples=True, verbose=False) test_tuples.extend(old_testables.enabled_testtup_list) if tagkw: tags_list = ut.list_getattr(test_tuples, 'tags') flags = ut.filterflags_general_tags(tags_list, **tagkw) test_tuples = ut.compress(test_tuples, flags) return test_tuples
[docs]def test_jedistuff(): import jedi import utool as ut source = ut.codeblock( ''' def spam(ibs, bar): r""" Args: ibs (ibeis.IBEISController): an object """ import jedi jedi.n x = '' x.l ibs.d bar.d ''' ) script = jedi.Script(source, line=9) script.completions() # Find the variable type of argument self = script = jedi.Script(source, line=10, column=7) # NOQA completions = script.completions() # NOQA vartype = script.goto_definitions() self = script = jedi.Script(source, line=11, column=7) # NOQA vartype = script.goto_definitions() # NOQA vardefs = script.goto_assignments() # NOQA # foodef, = jedi.names(source) # foomems = foodef.defined_names() # xdef = foomems[2]
[docs]def doctest_module_list(module_list): """ Runs many module tests Entry point for batch run Depth 0) Ignore: :'<,'>!sort -n -k 2 """ import utool as ut nPass_list = [] nTotal_list = [] failed_cmds_list = [] error_reports_list = [] print('[util_test] Running doctests on module list') try: ut.write_to('test_times.txt', '\n\n --- begining doctest_module_list\n', mode='a') except IOError as ex: ut.printex(ex, '[util_test] IOWarning', iswarning=True) failed_doctest_fname = 'failed_doctests.txt' seen_ = set([]) with open(failed_doctest_fname, 'a') as file_: file_.write('\n-------\n\n') file_.write(ut.get_printable_timestamp() + '\n') file_.write('logfile (only present if logging) = %r\n' % (ut.util_logging.get_current_log_fpath(),)) testkw = dict(allexamples=True, return_error_report=True) with ut.Timer(verbose=False) as t: for module in module_list: (nPass, nTotal, failed_list, error_report_list) = ut.doctest_funcs( module=module, seen_=seen_, **testkw) nPass_list.append(nPass) nTotal_list.append(nTotal) failed_cmds_list.append(failed_list) error_reports_list.append(error_report_list) # Write failed tests to disk for cmd in failed_list: file_.write(cmd + '\n') total_time = t.ellapsed nPass = sum(nPass_list) nTotal = sum(nTotal_list) file_.write('PASSED %d / %d' % (nPass, nTotal)) failed_cmd_list = ut.flatten(failed_cmds_list) error_report_list = ut.filter_Nones(ut.flatten(error_reports_list)) if len(error_report_list) > 0: print('\nPrinting %d error reports' % (len(error_report_list),)) for count, error_report in enumerate(error_report_list): print('\n=== Error Report %d / %d' % (count, len(error_report_list))) print(error_report) print('--- Done printing error reports ----') try: ut.write_to('test_times.txt', '\n\n --- finished doctest_module_list total_time=%.3fs\n' % (total_time), mode='a') except IOError as ex: ut.printex(ex, '[util_test] IOWarning', iswarning=True) print('') print('+========') print('| FINISHED TESTING %d MODULES' % (len(module_list),)) print('| PASSED %d / %d' % (nPass, nTotal)) print('L========') if len(failed_cmd_list) > 0: print('FAILED TESTS:') print('\n'.join(failed_cmd_list)) return nPass, nTotal, failed_cmd_list
[docs]def doctest_funcs(testable_list=None, check_flags=True, module=None, allexamples=None, needs_enable=None, strict=False, verbose=True, return_error_report=False, seen_=None): """ Main entry point into utools main module doctest harness Imports a module and checks flags for the function to run Depth 1) Args: testable_list (list): check_flags (bool): Force checking of the --test- and --exec- flags module (None): allexamples (None): needs_enable (None): Returns: tuple: (nPass, nTotal, failed_cmd_list) CommandLine: python -m ibeis.algo.preproc.preproc_chip --all-examples References: http://legacy.python.org/dev/peps/pep-0338/ https://docs.python.org/2/library/runpy.html Example: >>> # DISABLE_DOCTEST >>> from utool.util_tests import * # NOQA >>> testable_list = [] >>> check_flags = True >>> module = None >>> allexamples = None >>> needs_enable = None >>> # careful might infinitely recurse >>> (nPass, nTotal) = doctest_funcs(testable_list, check_flags, module, ... allexamples, needs_enable) >>> print((nPass, nTotal)) """ import multiprocessing import utool as ut # NOQA ut.start_logging() multiprocessing.freeze_support() # just in case if ut.VERBOSE: print('[util_test] doctest_funcs') ut.inject_colored_exceptions() if (verbose or VERBOSE_TEST) and ut.NOT_QUIET: if VERBOSE_TEST: print('[util_test.doctest_funcs][DEPTH 1] doctest_funcs()') print('[util_test.doctest_funcs] Running doctest_funcs') if ut.is_developer(): ut.change_term_title('DocTest ' + ' '.join(sys.argv)) # PARSE OUT TESTABLE DOCTESTTUPS mod_doctest_tup = get_module_doctest_tup( testable_list, check_flags, module, allexamples, needs_enable, N=1, verbose=verbose) enabled_testtup_list, frame_fpath, all_testflags, module = mod_doctest_tup nPass = 0 nFail = 0 nTotal = len(enabled_testtup_list) #flags = [(tup.name, tup.num) in seen_ for tup in enabled_testtup_list] if seen_ is not None: flags = [tup.src not in seen_ for tup in enabled_testtup_list] enabled_testtup_list = ut.compress(enabled_testtup_list, flags) # Remove duplicate tests from previous parts of the batch run #print(sum(flags)) EARLYEXIT = False if seen_ is not None: for tup in enabled_testtup_list: #seen_.add((tup.name, tup.num)) seen_.add(tup.src) if EARLYEXIT: nPass = nTotal - sum(flags) if return_error_report: return (nPass, nTotal, [], []) else: return (nPass, nTotal, []) modname = ut.get_modname_from_modpath(frame_fpath) nTotal = len(enabled_testtup_list) # Run enabled examles failed_flag_list = [] error_report_list = [] if ut.get_argflag(('--edit-test-file', '--etf')): ut.editfile(frame_fpath) exec_mode = all([testtup.exec_mode for testtup in enabled_testtup_list]) for testtup in enabled_testtup_list: name = testtup.name num = testtup.num src = testtup.src want = testtup.want flag = testtup.flag #if ut.is_developer(): # ut.change_term_title('DocTest ' + modname + ' ' + name) print('\n') fmtdict = dict(modname=modname, name=name, num=num) # 1 v12 v20 v30 v40 v50 v62 print('+------------------------------------------------------------+') print('* DOCTEST {modname:<20} {name:>26}:{num:d} '.format(**fmtdict)) print('+------------------------------------------------------------+') if PRINT_SRC or VERBOSE_TEST: print(ut.msgblock('EXEC SRC', src)) # Commented because it caused differences between # individual test runs and large test runs with ut # being imported # test_globals = module.__dict__.copy() test_globals = {} error_report = None try: testkw = dict( globals=test_globals, # HACK want=want, return_error_report=True) assert testtup.frame_fpath == frame_fpath test_locals, error_report = ut.run_test(testtup, **testkw) is_pass = (test_locals is not False) if is_pass: if VERBOSE_TEST: print('seems to pass') nPass += 1 else: if VERBOSE_TEST: print('raising failed exception') raise Exception('failed') except Exception: if VERBOSE_TEST: print('Seems to fail. ') nFail += 1 failed_flag_list.append(flag) error_report_list.append(error_report) if strict or util_arg.SUPER_STRICT: raise else: if VERBOSE_TEST: print('Silently Failing: ' 'maybe adding the --super-strict flag would help debug?') pass print('L_____________________________________________________________') #L__________________ #+------------------- # Print Results if nTotal == 0 and not allexamples: valid_test_argflags = ['--allexamples'] + all_testflags warning_msg = ut.codeblock( r''' No test flags sepcified Please choose one of the following flags or specify --enableall Valid test argflags: ''') + ut.indentjoin(valid_test_argflags, '\n ') warning_msg = ut.indent(warning_msg, '[util_test.doctest_funcs]') ut.colorprint(warning_msg, 'red') if not exec_mode: print('+-------') print('| finished testing fpath=%r' % (frame_fpath,)) print('| passed %d / %d' % (nPass, nTotal)) print('L-------') failed_cmd_list = [] if nFail > 0: #modname = module.__name__ modname = ut.get_modname_from_modpath(frame_fpath) # TODO: ensure that exename is in the PATH exename = basename(sys.executable) failed_cmd_list = ['%s -m %s %s' % (exename, modname, flag_) for flag_ in failed_flag_list] #failed_cmd_list = ['python %s %s' % (frame_fpath, flag_) # for flag_ in failed_flag_list] print('Failed sys.argv = %r' % (' '.join(sys.argv),)) print('Failed Tests:') print('\n'.join(failed_cmd_list)) #L__________________ if return_error_report: return (nPass, nTotal, failed_cmd_list, error_report_list) else: return (nPass, nTotal, failed_cmd_list)
[docs]def run_test(func_or_testtup, return_error_report=False, *args, **kwargs): """ Runs the test function with success / failure printing Args: func_or_testtup (func or tuple): function or doctest tuple Varargs/Kwargs: Anything that needs to be passed to <func_> """ import utool as ut #func_is_testtup = isinstance(func_or_testtup, tuple) # NOTE: isinstance is not gaurenteed not work here if ut.rrrr has been called func_is_testtup = isinstance(func_or_testtup, TestTuple) exec_mode = False write_times = True if func_is_testtup: testtup = func_or_testtup src = testtup.src funcname = testtup.name frame_fpath = testtup.frame_fpath #(funcname, src, frame_fpath) = func_or_testtup exec_mode = testtup.exec_mode else: func_ = func_or_testtup funcname = get_funcname(func_) frame_fpath = ut.get_funcfpath(func_) upper_funcname = funcname.upper() if ut.VERBOSE: print('\n=============================') print('**[TEST.BEGIN] %s ' % (sys.executable)) print('**[TEST.BEGIN] %s ' % (funcname,)) #print(' <funcname> ') #print(' <' + funcname + '> ') #short_funcname = ut.clipstr(funcname, 8) # TODO: make the --exec- prefix specify this instead of --test- verbose_timer = not exec_mode and VERBOSE_TIMER nocheckwant = True if exec_mode else None print_face = not exec_mode and PRINT_FACE #indent_test = not exec_mode and INDENT_TEST error_report = None try: #+---------------- # RUN THE TEST WITH A TIMER with util_time.Timer(upper_funcname, verbose=verbose_timer) as timer: if func_is_testtup: test_locals = _exec_doctest(src, kwargs, nocheckwant) else: # TEST INPUT IS A LIVE PYTHON FUNCTION test_locals = func_(*args, **kwargs) print('') #L________________ #+---------------- # LOG PASSING TEST if not exec_mode: print('\n=============================') print('**[TEST.FINISH] %s -- SUCCESS' % (funcname,)) if print_face: print(HAPPY_FACE) if write_times: timemsg = '%.4fs in %s %s\n' % ( timer.ellapsed, funcname, frame_fpath) try: ut.write_to('test_times.txt', timemsg, mode='a') except IOError as ex: ut.printex(ex, '[util_test] IOWarning', iswarning=True) #L________________ # RETURN VALID TEST LOCALS if return_error_report: return test_locals, error_report return test_locals except Exception as ex: import utool as ut # Get locals in the wrapped function ut.printex(ex, tb=True) error_report_lines = ['**[TEST.ERROR] %s -- FAILED:\n type(ex)=%s' % ( funcname, type(ex))] error_report_lines.append(ut.formatex(ex, tb=True)) def print_report(msg): error_report_lines.append(msg) print(msg) print_report('\n=============================') print_report('**[TEST.FINISH] %s -- FAILED:\n type(ex)=%s' % (funcname, type(ex))) exc_type, exc_value, tb = sys.exc_info() if PRINT_FACE: print_report(SAD_FACE) if func_is_testtup: print_report('Failed in module: %r' % frame_fpath) if True or DEBUG_SRC: src_with_lineno = ut.number_text_lines(src) print_report(ut.msgblock('FAILED DOCTEST IN %s' % (funcname,), src_with_lineno)) #ut.embed() #print('\n... test error. sys.exit(1)\n') #sys.exit(1) #failed_execline = traceback.format_tb(tb)[-1] #parse_str = 'File {fname}, line {lineno}, in {modname}' #parse_dict = parse.parse('{prefix_}' + parse_str + '{suffix_}', failed_execline) #if parse_dict['fname'] == '<string>': # lineno = int(parse_dict['lineno']) # failed_line = src.splitlines()[lineno - 1] # print('Failed on line: %s' % failed_line) if util_arg.SUPER_STRICT: exc_type, exc_value, exc_traceback = sys.exc_info() if not func_is_testtup: # Remove this function from stack strace # dont do this for execed code exc_traceback = exc_traceback.tb_next # Python 2*3=6 if True: # FIXME: use common code six.reraise(exc_type, exc_value, exc_traceback) else: ## PYTHON 2.7 DEPRICATED: #if six.PY2: # raise exc_type, exc_value, exc_traceback.tb_next # #exec ('raise exc_type, exc_value, # exc_traceback.tb_next', globals(), locals()) ## PYTHON 3.3 NEW METHODS #elif six.PY3: # ex = exc_type(exc_value) # ex.__traceback__ = exc_traceback.tb_next # raise ex #else: # raise AssertionError('Weird python version') pass if SYSEXIT_ON_FAIL: print('[util_test] SYSEXIT_ON_FAIL = True') print('[util_test] exiting with sys.exit(1)') sys.exit(1) #raise if return_error_report: error_report = '\n'.join(error_report_lines) return False, error_report
def _exec_doctest(src, kwargs, nocheckwant=None): """ Helper for run_test block of code that r:uns doctest and was too big to be in run_test """ # TEST INPUT IS PYTHON CODE TEXT #test_locals = {} test_globals = kwargs.get('globals', {}).copy() want = kwargs.get('want', None) #test_globals['print'] = doctest_print # EXEC FUNC #six.exec_(src, test_globals, test_locals) # adds stack to debug trace import utool as ut if ut.get_argflag(('--cmd', '--embed')): src += '\nimport utool as ut; ut.embed()' # TODO RECTIFY WITH TF code = compile(src, '<string>', 'exec') try: # IN EXEC CONTEXT THERE IS NO DIFF BETWEEN LOCAL / GLOBALS. ONLY PASS # IN ONE DICT. OTHERWISE TREATED ODDLY # References: https://bugs.python.org/issue13557 #exec(code, test_globals, test_locals) test_locals = test_globals exec(code, test_globals) except ExitTestException: print('Test exited before show') pass if nocheckwant is None: nocheckwant = util_arg.get_argflag('--no-checkwant', help_='Turns off checking for results') if nocheckwant or want is None or want == '': if not nocheckwant: print('warning test does not want anything') else: if want.endswith('\n'): want = want[:-1] result = six.text_type(test_locals.get('result', 'NO VARIABLE NAMED result')) if result != want: errmsg1 = '' try: import utool as ut difftext = ut.get_textdiff(want, result) if util_dbg.COLORED_EXCEPTIONS: difftext = ut.get_colored_diff(difftext) errmsg1 += ('DIFF/GOT/EXPECTED\n' + difftext + '\n') except ImportError: if ut.STRICT: raise errmsg1 += ('REPR_GOT: result=\n%r\n' % (result)) errmsg1 += ('REPR_EXPECTED: want=\n%r\n' % (want)) else: if VERBOSE_TEST: errmsg1 += ('REPR_GOT: result=\n%r\n' % (result)) errmsg1 += ('REPR_EXPECTED: want=\n%r\n' % (want)) errmsg1 += '' errmsg1 += ('STR_GOT: result=\n%s\n' % (result)) errmsg1 += ('STR_EXPECTED: want=\n%s\n' % (want)) raise AssertionError('result != want\n' + errmsg1) return test_locals
[docs]def get_module_testlines(module_list, remove_pyc=True, verbose=True, pythoncmd=None): """ Builds test commands for autogen tests called by autogen test scripts """ import utool as ut # NOQA if pythoncmd is None: pythoncmd = sys.executable #'python' testcmd_list = [] for module in module_list: mod_doctest_tup = get_module_doctest_tup( module=module, allexamples=True, verbose=verbose) enabled_testtup_list, frame_fpath, all_testflags, module_ = mod_doctest_tup for testtup in enabled_testtup_list: #testflag = testtup[-1] testflag = testtup.flag if remove_pyc: # FIXME python 3 __pycache__/*.pyc frame_fpath = frame_fpath.replace('.pyc', '.py') frame_rel_fpath = ut.get_relative_modpath(frame_fpath) testcmd = ' '.join((pythoncmd, frame_rel_fpath, testflag)) testcmd_list.append(testcmd) return testcmd_list
[docs]def parse_docblocks_from_docstr(docstr): """ parse_docblocks_from_docstr Depth 5) called by parse_doctest_from_docstr TODO: move to util_inspect Args: docstr (str): Returns: list: docstr_blocks tuples [(blockname, blockstr, offset)] Example: >>> # ENABLE_DOCTEST >>> from utool.util_tests import * # NOQA >>> import utool as ut >>> #import ibeis >>> #import ibeis.algo.hots.query_request >>> #func_or_class = ibeis.algo.hots.query_request.QueryParams >>> func_or_class = ut.parse_docblocks_from_docstr >>> docstr = ut.get_docstr(func_or_class) >>> docstr_blocks = parse_docblocks_from_docstr(docstr) >>> result = str(docstr_blocks) >>> print(result) """ # FIXME Requires tags to be separated by two spaces if docstr is None: return [] import parse import utool as ut import itertools docstr = ut.ensure_unicode(docstr) initial_docblocks = docstr.split('\n\n') docblock_len_list = [str_.count('\n') + 2 for str_ in initial_docblocks] offset_iter = itertools.chain([0], ut.cumsum(docblock_len_list)[:-1]) initial_line_offsets = [offset for offset in offset_iter] if VERBOSE_TEST: if ut.VERBOSE: print('__________') print('__Initial Docblocks__') print('\n---\n'.join(initial_docblocks)) docstr_blocks = [] for docblock, line_offset in zip(initial_docblocks, initial_line_offsets): docblock = docblock.strip('\n') indent = ' ' * ut.get_indentation(docblock) parse_result = parse.parse(indent + '{tag}:\n{rest}', docblock) if parse_result is not None: header = parse_result['tag'] else: header = '' docstr_blocks.append((header, docblock, line_offset)) #print(docstr_blocks) docblock_headers = ut.get_list_column(docstr_blocks, 0) docblock_bodys = ut.get_list_column(docstr_blocks, 1) docblock_offsets = ut.get_list_column(docstr_blocks, 2) if VERBOSE_TEST: print('[util_test] * found %d docstr_blocks' % (len(docstr_blocks),)) print('[util_test] * docblock_headers = %r' % (docblock_headers,)) print('[util_test] * docblock_offsets = %r' % (docblock_offsets,)) if ut.VERBOSE: print('[util_test] * docblock_bodys:') print('\n-=-\n'.join(docblock_bodys)) return docstr_blocks
[docs]def read_exampleblock(docblock): import utool as ut nonheader_src = ut.unindent('\n'.join(docblock.splitlines()[1:])) nonheader_lines = nonheader_src.splitlines() reversed_src_lines = [] reversed_want_lines = [] finished_want = False # Read the example block backwards to get the want string # and then the rest should all be source for line in reversed(nonheader_lines): if not finished_want: if line.startswith('>>> ') or line.startswith('... '): finished_want = True else: reversed_want_lines.append(line) continue reversed_src_lines.append(line[4:]) test_src = '\n'.join(reversed_src_lines[::-1]) test_want = '\n'.join(reversed_want_lines[::-1]) return test_src, test_want
[docs]def parse_doctest_from_docstr(docstr): r""" because doctest itself doesnt do what I want it to do called by get_doctest_examples Depth 4) CAREFUL, IF YOU GET BACK WRONG RESULTS MAKE SURE YOUR DOCSTR IS PREFFIXED WITH R CommandLine: python -m utool.util_tests --exec-parse_doctest_from_docstr Setup: >>> from utool.util_tests import * # NOQA >>> import utool as ut Example: >>> # ENABLE_DOCTEST >>> #from ibeis.algo.hots import score_normalization >>> #func_or_class = score_normalization.cached_ibeis_score_normalizer >>> func_or_class = parse_doctest_from_docstr >>> docstr = ut.get_docstr(func_or_class) >>> testsrc_list, testwant_list, testlinenum_list, func_lineno, docstr = get_doctest_examples(func_or_class) >>> print('\n\n'.join(testsrc_list)) >>> assert len(testsrc_list) == len(testwant_list) """ import utool as ut docstr_blocks = parse_docblocks_from_docstr(docstr) example_docblocks = [] example_setups = [] param_grids = None for header, docblock, line_offset in docstr_blocks: if header.startswith('Example'): example_docblocks.append((header, docblock, line_offset)) if header.startswith('Setup'): setup_src = read_exampleblock(docblock)[0] example_setups.append(setup_src) if header.startswith('ParamGrid'): paramgrid_src = read_exampleblock(docblock)[0] globals_ = {} six.exec_('import utool as ut\n' + paramgrid_src, globals_) assert 'combos' in globals_, 'param grid must define combos' combos = globals_['combos'] param_grids = [ut.execstr_dict(combo, explicit=True) for combo in combos] testheader_list = [] testsrc_list = [] testwant_list = [] testlineoffset_list = [] for header, docblock, line_offset in example_docblocks: test_src, test_want = read_exampleblock(docblock) testheader_list.append(header) testsrc_list.append(test_src) testwant_list.append(test_want) testlineoffset_list.append(line_offset) #print('Parsed header=%r' % header) #print('Parsed src=%r' % test_src) # Hack: append setups to all sources assert len(example_setups) <= 1, 'cant have more than 1 setup' if len(example_setups) == 1: if param_grids is None: testsrc_list = ['\n'.join([example_setups[0], src]) for src in testsrc_list] else: # Implmentation to put all pgrids in the same test testsrc_list = ['\n'.join( [example_setups[0]] + [ '\n'.join( [ pgrid, 'print(\'Grid %d\')' % (count,), src.replace('ut.show_if_requested()', '') # Megahack if count < (len(param_grids) - 1) else src ] ) for count, pgrid in enumerate(param_grids)]) for src in testsrc_list] #testsrc_list = ['\n'.join([example_setups[0], pgrid, src]) for pgrid in param_grids for src in testsrc_list] # implementation to make a different test for each pgrid #testsrc_list = ['\n'.join([example_setups[0], pgrid, src]) for pgrid in param_grids for src in testsrc_list] #testheader_list = [head for pgrid in param_grids for head in testheader_list] #testwant_list = [tw for pgrid in param_grids for tw in testwant_list] #testwant_list = [lo for pgrid in param_grids for lo in testlineoffset_list] return testheader_list, testsrc_list, testwant_list, testlineoffset_list #@debug_decor
[docs]def get_doctest_examples(func_or_class): """ get_doctest_examples Depth 3) called by get_module_doctest_tup Args: func_or_class (function) Returns: tuple (list, list): example_list, want_list CommandLine: python -m utool.util_tests --test-get_doctest_examples Example0: >>> # ENABLE_DOCTEST >>> from utool.util_tests import * # NOQA >>> func_or_class = get_doctest_examples >>> tup = get_doctest_examples(func_or_class) >>> testsrc_list, testwant_list, testlinenum_list, func_lineno, docstr = tup >>> result = str(len(testsrc_list) + len(testwant_list)) >>> print(testsrc_list) >>> print(testlinenum_list) >>> print(func_lineno) >>> print(testwant_list) >>> print(result) 6 Example1: >>> # ENABLE_DOCTEST >>> from utool.util_tests import * # NOQA >>> import utool as ut >>> func_or_class = ut.tryimport >>> tup = get_doctest_examples(func_or_class) >>> testsrc_list, testwant_list, testlinenum_list, func_lineno, docstr = tup >>> result = str(len(testsrc_list) + len(testwant_list)) >>> print(testsrc_list) >>> print(testlinenum_list) >>> print(func_lineno) >>> print(testwant_list) >>> print(result) 4 Example2: >>> # DISABLE_DOCTEST >>> from utool.util_tests import * # NOQA >>> import ibeis >>> func_or_class = ibeis.control.manual_annot_funcs.add_annots >>> tup = get_doctest_examples(func_or_class) >>> testsrc_list, testwant_list, testlinenum_list, func_lineno, docstr = tup >>> result = str(len(testsrc_list) + len(testwant_list)) >>> print(testsrc_list) >>> print(testlinenum_list) >>> print(func_lineno) >>> print(testwant_list) >>> print(result) 2 """ if isinstance(func_or_class, staticmethod): func_or_class = func_or_class.__func__ import utool as ut if VERBOSE_TEST: print('[util_test][DEPTH 3] get_doctest_examples()') print('[util_test] + parsing %r for doctest' % (func_or_class)) print('[util_test] - name = %r' % (func_or_class.__name__,)) if hasattr(func_or_class, '__ut_parent_class__'): print('[util_test] - __ut_parent_class__ = %r' % ( func_or_class.__ut_parent_class__,)) #ut.embed() try: raise NotImplementedError('FIXME') #func_or_class._utinfo['orig_func'] func_lineno = func_or_class.func_code.co_firstlineno # FIXME: doesn't handle decorators well # # ~~FIXME doesn't account for multiline function definitions # actually parse this out~~ # TODO: rectify with util_insepct get_funcsource with stip def line sourcecode = inspect.getsource(func_or_class) match = ut.regex_get_match('def [^)]*\\):\n', sourcecode) if match is not None: num_funcdef_lines = match.group().count('\n') else: num_funcdef_lines = 1 except Exception as ex: func_lineno = 0 num_funcdef_lines = 1 if ut.DEBUG2: ut.printex(ex, '[util-test] error getting function line number') docstr = ut.get_docstr(func_or_class) # Cache because my janky parser is slow #with ut.GlobalShelfContext('utool') as shelf: # if False and docstr in shelf: # testsrc_list, testwant_list = shelf[docstr] # else: (testheader_list, testsrc_list, testwant_list, testlineoffset_list) = parse_doctest_from_docstr(docstr) testlinenum_list = [ func_lineno + num_funcdef_lines + offset for offset in testlineoffset_list ] # shelf[docstr] = testsrc_list, testwant_list if VERBOSE_TEST: print('[util_test] L found %d doctests' % (len(testsrc_list),)) return testsrc_list, testwant_list, testlinenum_list, func_lineno, docstr # doctest doesnt do what i want. so I wrote my own primative but effective # parser.
[docs]def get_module_doctest_tup(testable_list=None, check_flags=True, module=None, allexamples=None, needs_enable=None, N=0, verbose=True, testslow=False): """ Parses module for testable doctesttups Depth 2) Args: testable_list (list): a list of functions (default = None) check_flags (bool): (default = True) module (None): (default = None) allexamples (None): (default = None) needs_enable (None): (default = None) N (int): (default = 0) verbose (bool): verbosity flag(default = True) testslow (bool): (default = False) Returns: ModuleDoctestTup : (enabled_testtup_list, frame_fpath, all_testflags, module) enabled_testtup_list (list): a list of testtup testtup (tuple): (name, num, src, want, flag) describes a valid doctest in the module name (str): test name num (str): test number of the module / function / class / method src (str): test source code want (str): expected test result flag (str): a valid commandline flag to enable this test frame_fpath (str): module fpath that will be tested module (module): the actual module that will be tested all_testflags (list): the command line arguments that will enable different tests exclude_inherited (bool): does not included tests defined in other modules CommandLine: python -m utool.util_tests --exec-get_module_doctest_tup Example: >>> # ENABLE_DOCTEST >>> from utool.util_tests import * # NOQA >>> import utool as ut >>> #testable_list = [ut.util_import.package_contents] >>> testable_list = None >>> check_flags = False >>> module = ut.util_cplat >>> allexamples = False >>> needs_enable = None >>> N = 0 >>> verbose = True >>> testslow = False >>> mod_doctest_tup = get_module_doctest_tup(testable_list, check_flags, module, allexamples, needs_enable, N, verbose, testslow) >>> result = ('mod_doctest_tup = %s' % (ut.list_str(mod_doctest_tup, nl=4),)) >>> print(result) """ #+------------------------ if VERBOSE_TEST: print('[util_test.get_module_doctest tup][DEPTH 2] get_module_doctest tup()') import utool as ut # NOQA if needs_enable is None: needs_enable = not ut.get_argflag('--enableall') #needs_enable = True TEST_ALL_EXAMPLES = allexamples or ut.get_argflag(('--allexamples', '--all-examples')) parse_testables = True force_enable_testnames = [] if isinstance(testable_list, types.ModuleType): # hack module = testable_list testable_list = [] testable_name_list = [] elif testable_list is None: testable_list = [] testable_name_list = [] else: testable_name_list = [ut.get_funcname(func) for func in testable_list] parse_testables = False #L________________________ #+------------------------ # GET_MODULE_DOCTEST_TUP Step 1: # Inspect caller module for testable names if module is None: frame_fpath = '???' try: frame = ut.get_caller_stack_frame(N=N) main_modname = '__main__' frame_name = frame.f_globals['__name__'] frame_fpath = frame.f_globals['__file__'] if frame_name == main_modname: module = sys.modules[main_modname] entry_modname = ut.get_modname_from_modpath(module.__file__) #ut.embed() if entry_modname in ['kernprof', 'kernprof-script']: # kernprof clobbers the __main__ variable. # workaround by reimporting the module name import importlib modname = ut.get_modname_from_modpath(frame_fpath) module = importlib.import_module(modname) except Exception as ex: print(frame.f_globals) ut.printex(ex, keys=['frame', 'module']) raise allexamples = False else: frame_fpath = module.__file__ allexamples = True #L________________________ #+------------------------ # GET_MODULE_DOCTEST_TUP Step 2: # --- PARSE TESTABLE FUNCTIONS --- # FIXME: # BUG: We need to verify that this function actually belongs to this # module. In util_type ndarray is imported and we try to parse it # Get testable functions if parse_testables: try: if verbose or VERBOSE_TEST and ut.NOT_QUIET: print('[ut.test] Iterating over module funcs') print('[ut.test] module =%r' % (module,)) _testableiter = ut.iter_module_doctestable(module, include_inherited=False) if __debug__: _testableiter = list(_testableiter) for key, val in _testableiter: if isinstance(val, staticmethod): docstr = inspect.getdoc(val.__func__) else: docstr = inspect.getdoc(val) docstr = ut.ensure_unicode(docstr) if docstr is not None and docstr.find('Example') >= 0: testable_name_list.append(key) testable_list.append(val) else: if VERBOSE_TEST and ut.NOT_QUIET: if docstr.find('Example') >= 0: print('[ut.test] Ignoring (disabled) : %s' % key) else: print('[ut.test] Ignoring (no Example) : %s' % key) except Exception as ex: print('FAILED') print(docstr) ut.printex(ex, keys=['frame']) raise # OUTPUTS: testable_list #L________________________ #+------------------------ # GET_MODULE_DOCTEST_TUP Step 3: # --- FILTER TESTABLES_--- # Get testable function examples test_sentinals = [ 'ENABLE_DOCTEST', #'ENABLE_TEST', #'ENABLE_DOCTEST', #'ENABLE_UTOOL_DOCTEST', #'UTOOL_TEST', #'UTOOLTEST' ] if testslow or ut.get_argflag(('--testall', '--testslow', '--test-slow')): test_sentinals.append('SLOW_DOCTEST') if testslow or ut.get_argflag(('--testall', '--testunstable')): test_sentinals.append('UNSTABLE_DOCTEST') #conditional_sentinals = [ # TODO #'ENABLE_IF' #] # FIND THE TEST NAMES REQUESTED # Grab sys.argv enabled tests valid_prefix_list = ['--exec-', '--test-'] for arg in sys.argv: for prefix in valid_prefix_list: if arg.startswith(prefix): testname = arg[len(prefix):].split(':')[0].replace('-', '_') force_enable_testnames.append(testname) # TODO: parse out requested number up here break #print(force_enable_testnames) def _get_testable_name(testable): import utool as ut if isinstance(testable, staticmethod): testable = testable.__func__ try: testable_name = testable.func_name except AttributeError as ex1: try: testable_name = testable.__name__ except AttributeError as ex2: ut.printex(ex1, ut.list_str(dir(testable))) ut.printex(ex2, ut.list_str(dir(testable))) raise return testable_name sorted_testable = sorted(list(set(testable_list)), key=_get_testable_name) # Append each testable example if VERBOSE_TEST: print('Vars:') print(' * needs_enable = %r' % (needs_enable,)) print(' * force_enable_testnames = %r' % (force_enable_testnames,)) indenter = ut.Indenter('[CHECK_EX]') print('len(sorted_testable) = %r' % (len(sorted_testable),)) indenter.start() # PARSE OUT THE AVAILABLE TESTS FOR EACH REQUEST local_testtup_list = [] for testable in sorted_testable: testname = _get_testable_name(testable) testname2 = None if isinstance(testable, staticmethod): testable = testable.__func__ if hasattr(testable, '__ut_parent_class__'): # HACK for getting classname.funcname testname2 = testable.__ut_parent_class__.__name__ + '.' + testname #print('TESTNAME2') #print('testname2 = %r' % (testname2,)) examples, wants, linenums, func_lineno, docstr = get_doctest_examples(testable) if len(examples) > 0: for testno , srcwant_tup in enumerate(zip(examples, wants)): src, want = srcwant_tup src_ = ut.regex_replace('from __future__ import.*$', '', src) test_disabled = not any([src_.find(s) >= 0 for s in test_sentinals]) if needs_enable and test_disabled: if testname not in force_enable_testnames: # HACK if testname2 not in force_enable_testnames: #print(force_enable_testnames) #print(testname2) if VERBOSE_TEST: print(' * skipping: %r / %r' % (testname, testname2)) #print(src) #print(' * skipping') continue #else: # testname = testname2 #ut.embed() #. FIXME: you probably should only add one version of the testname to the list, # and classname prefixes should probably be enforced if testname2 is not None: if VERBOSE_TEST: print(' * HACK adding testname=%r to local_testtup_list' % (testname,)) #local_testtup = (testname2, testno, src_, want) local_testtup = ((testname2, testname), testno, src_, want) local_testtup_list.append(local_testtup) else: if VERBOSE_TEST: print(' * adding testname=%r to local_testtup_list' % (testname,)) local_testtup = (testname, testno, src_, want) local_testtup_list.append(local_testtup) else: print('WARNING: no examples in %r for testname=%r' % (frame_fpath, testname)) if verbose: print(testable) print(examples) print(wants) print(docstr) if VERBOSE_TEST: print(' --') if VERBOSE_TEST: indenter.stop() #L________________________ #+------------------------ # Get enabled (requested) examples if VERBOSE_TEST: print('\n-----\n') indenter = ut.Indenter('[CHECK_ENABLED]') indenter.start() print('Need to find which examples are enabled') print('len(local_testtup_list) = %r' % (len(local_testtup_list),)) #print('local_testtup_list = %r' % (local_testtup_list,)) print('local_testtup_list.T[0:2].T = %s' % ut.list_str(ut.get_list_column(local_testtup_list, [0, 1]))) #print('(local_testtup_list) = %r' % (local_testtup_list,)) all_testflags = [] enabled_testtup_list = [] distabled_testflags = [] subx = ut.get_argval('--subx', type_=int, default=None, help_='Only tests the subxth example') for local_testtup in local_testtup_list: (nametup, num, src, want) = local_testtup if not isinstance(nametup, tuple): nametup = (nametup,) valid_flags = [] exec_mode = None for prefix, name in ut.iprod(valid_prefix_list, nametup): #prefix = '--test-' flag1 = prefix + name + ':' + str(num) flag2 = prefix + name flag3 = prefix + name.replace('_', '-') + ':' + str(num) flag4 = prefix + name.replace('_', '-') valid_flags += [flag1, flag2, flag3, flag4] if VERBOSE_TEST: print('Checking for flags*: ' + flag1) #print(' checking sys.argv for:\n %s' % (ut.list_str(valid_flags),)) testflag = ut.get_argflag(valid_flags) # TODO: run in exec mode exec_mode = prefix == '--exec-' # NOQA if testflag: break testenabled = TEST_ALL_EXAMPLES or not check_flags or testflag if subx is not None and subx != num: continue all_testflags.append(flag3) if testenabled: if VERBOSE_TEST: print('... enabling test') testtup = TestTuple(name, num, src, want, flag1, frame_fpath=frame_fpath, exec_mode=exec_mode) enabled_testtup_list.append(testtup) else: if VERBOSE_TEST: print('... disableing test') distabled_testflags.append(flag1) if VERBOSE_TEST: indenter.stop() if ut.get_argflag('--list'): # HACK: Should probably just return a richer structure print('testable_name_list = %s' % (ut.list_str(testable_name_list),)) mod_doctest_tup = ModuleDoctestTup(enabled_testtup_list, frame_fpath, all_testflags, module) #L________________________ return mod_doctest_tup
[docs]def doctest_was_requested(): """ lets a __main__ codeblock know that util_test should do its thing """ valid_prefix_list = ['--exec-', '--test-'] return '--tf' in sys.argv or any([any([arg.startswith(prefix) for prefix in valid_prefix_list]) for arg in sys.argv])
[docs]def find_doctestable_modnames(dpath_list=None, exclude_doctests_fnames=[], exclude_dirs=[]): """ Tries to find files with a call to ut.doctest_funcs in the __main__ part """ import utool as ut fpath_list, lines_list, lxs_list = ut.grep('doctest_funcs', dpath_list=dpath_list, include_patterns=['*.py'], exclude_dirs=exclude_dirs, recursive=True) exclude_doctests_fnames = set(exclude_doctests_fnames) def is_not_excluded(fpath): return basename(fpath) not in exclude_doctests_fnames doctest_modpath_list = list(filter(is_not_excluded, fpath_list)) doctest_modname_list = list(map(ut.get_modname_from_modpath, doctest_modpath_list)) return doctest_modname_list
[docs]def find_untested_modpaths(dpath_list=None, exclude_doctests_fnames=[], exclude_dirs=[]): import utool as ut fpath_list, lines_list, lxs_list = ut.grep('>>> # ENABLE_DOCTEST', dpath_list=dpath_list, include_patterns=['*.py'], exclude_dirs=exclude_dirs, recursive=True, inverse=True) exclude_doctests_fnames = set(list(exclude_doctests_fnames) + ['__init__.py']) def is_not_excluded(fpath): fname = basename(fpath) return (not fname.startswith('_')) and fname not in exclude_doctests_fnames doctest_modpath_list = list(filter(is_not_excluded, fpath_list)) #doctest_modname_list = list(map(ut.get_modname_from_modpath, doctest_modpath_list)) return doctest_modpath_list
[docs]def show_was_requested(): """ returns True if --show is specified on the commandline or you are in IPython (and presumably want some sort of interaction """ import plottool as pt return pt.show_was_requested() #import utool as ut #return ut.get_argflag('--show') or ut.inIPython()
[docs]class ExitTestException(Exception): pass
[docs]def quit_if_noshow(): import utool as ut if not (ut.get_argflag(('--show', '--save')) or ut.inIPython()): raise ExitTestException('This should be caught gracefully by ut.run_test')
[docs]def show_if_requested(): import plottool as pt pt.show_if_requested(N=2)
[docs]def find_testfunc(module, test_funcname, ignore_prefix=[], ignore_suffix=[], func_to_module_dict={}): import utool as ut if isinstance(module, six.string_types): module = ut.import_modname(module) modname_list = ut.package_contents(module, ignore_prefix=ignore_prefix, ignore_suffix=ignore_suffix) # Get only the modules already imported have_modnames = [modname_ for modname_ in modname_list if modname_ in sys.modules] #missing_modnames = [modname for modname in modname_list # if modname not in sys.modules] module_list = ut.dict_take(sys.modules, have_modnames) # Search for the module containing the function test_func = None test_module = None test_classname = None if test_funcname.find('.') != -1: test_classname, test_funcname = test_funcname.split('.') if test_funcname.find(':') != -1: test_funcname, testno = test_funcname.split(':') testno = int(testno) else: testno = 0 if test_classname is None: for module_ in module_list: #test_funcname = 'find_installed_tomcat' if test_funcname in module_.__dict__: test_module = module_ test_func = test_module.__dict__[test_funcname] break else: for module_ in module_list: #test_funcname = 'find_installed_tomcat' if test_classname in module_.__dict__: test_module = module_ test_class = test_module.__dict__[test_classname] test_func = test_class.__dict__[test_funcname] if test_func is None: print('Did not find any function named %r ' % (test_funcname,)) print('Searched ' + ut.list_str([mod.__name__ for mod in module_list])) return test_func, testno
[docs]def main_function_tester(module, ignore_prefix=[], ignore_suffix=[], test_funcname=None, func_to_module_dict={}): """ Allows a shorthand for __main__ packages of modules to run tests with unique function names """ import utool as ut ut.colorprint('[utool] main_function_tester', 'yellow') test_funcname = ut.get_argval( ('--test-func', '--tfunc', '--tf', '--testfunc'), type_=str, default=test_funcname, help_='specify a function to doctest') print('test_funcname = %r' % (test_funcname,)) if test_funcname in func_to_module_dict: modname = func_to_module_dict[test_funcname] ut.import_modname(modname) if test_funcname is not None: #locals_ = {} ut.inject_colored_exceptions() # print('[utool] __main__ Begin Function Test') print('[utool] __main__ Begin Function Test') test_func, testno = find_testfunc(module, test_funcname, ignore_prefix, ignore_suffix, func_to_module_dict) if test_func is not None: globals_ = {} func_globals = meta_util_six.get_funcglobals(test_func) globals_.update(func_globals) testsrc = ut.get_doctest_examples(test_func)[0][testno] if ut.get_argflag(('--cmd', '--embed')): testsrc += '\nimport utool as ut; ut.embed()' # TODO RECTIFY WITH EXEC DOCTEST doctest_src = ut.indent(testsrc, '>>> ') doctest_src = '\n'.join(['%3d %s' % (count, line) for count, line in enumerate(doctest_src.splitlines(), start=1)]) colored_src = ut.highlight_code(doctest_src) print('testsrc = \n%s' % (colored_src,)) try: code = compile(testsrc, '<string>', 'exec') exec(code, globals_) # , locals_) except ExitTestException: print('Test exited before show') pass retcode = 0 print('Finished function test.') print('...exiting') sys.exit(retcode) else: print('Did not find any function named %r ' % (test_funcname,)) print('...exiting') sys.exit(0)
[docs]def execute_doctest(func, testnum=0, module=None): """ Execute a function doctest. Can optionaly specify func name and module to run from ipython notebooks. Example: >>> from utool.util_tests import * # NOQA IPython: import utool as ut ut.execute_doctest(func='dummy_example_depcacahe', module='dtool.example_depcache') """ import utool as ut if isinstance(func, six.string_types): funcname = func if isinstance(module, six.string_types): modname = module module = ut.import_modname(modname) func, _testno = find_testfunc(module, funcname, ignore_prefix=[], ignore_suffix=[]) # TODO RECTIFY WITH EXEC DOCTEST globals_ = {} testsrc = ut.get_doctest_examples(func)[0][testnum] # colored_src = ut.highlight_code(ut.indent(testsrc, '>>> ')) doctest_src = ut.indent(testsrc, '>>> ') doctest_src = '\n'.join(['%3d %s' % (count, line) for count, line in enumerate(doctest_src.splitlines(), start=1)]) colored_src = ut.highlight_code(doctest_src) print('testsrc = \n%s' % (colored_src,)) try: code = compile(testsrc, '<string>', 'exec') exec(code, globals_) except ExitTestException: print('Test exited before show')
if __name__ == '__main__': """ CommandLine: python -c "import utool, utool.util_tests; utool.doctest_funcs(utool.util_tests)" python -m utool.util_tests python -m utool.util_tests --allexamples python -m utool.util_tests python -c "import utool; utool.doctest_funcs(module=utool.util_tests, needs_enable=False)" /model/preproc/preproc_chip.py --allexamples """ import multiprocessing import utool as ut # NOQA multiprocessing.freeze_support() #doctest_funcs() ut.doctest_funcs()