# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
import re
import os
import six
from collections import deque # NOQA
from os.path import exists, dirname, join, expanduser, normpath
from utool import util_inject
print, rrr, profile = util_inject.inject2(__name__)
[docs]class PythonStatement(object):
""" Thin wrapper around a string representing executable python code """
def __init__(self, stmt):
self.stmt = stmt
def __repr__(self):
return self.stmt
def __str__(self):
return self.stmt
[docs]def dump_autogen_code(
fpath, autogen_text, codetype='python', fullprint=None, show_diff=None, dowrite=None
):
"""
Helper that write a file if -w is given on command line, otherwise
it just prints it out. It has the opption of comparing a diff to the file.
"""
import utool as ut
if dowrite is None:
dowrite = ut.get_argflag(('-w', '--write'))
if show_diff is None:
show_diff = ut.get_argflag('--diff')
num_context_lines = ut.get_argval('--diff', type_=int, default=None)
show_diff = show_diff or num_context_lines is not None
num_context_lines = ut.get_argval('--diff', type_=int, default=None)
if fullprint is None:
fullprint = True
if fullprint is False:
fullprint = ut.get_argflag('--print')
print('[autogen] Autogenerated %s...\n+---\n' % (fpath,))
if not dowrite:
if fullprint:
ut.print_code(autogen_text, lexer_name=codetype)
print('\nL___')
else:
print('specify --print to write to stdout')
pass
print('specify -w to write, or --diff to compare')
print('...would write to: %s' % fpath)
if show_diff:
if ut.checkpath(fpath, verbose=True):
prev_text = ut.read_from(fpath)
textdiff = ut.get_textdiff(
prev_text, autogen_text, num_context_lines=num_context_lines
)
try:
ut.print_difftext(textdiff)
except UnicodeDecodeError:
import unicodedata
textdiff = unicodedata.normalize('NFKD', textdiff).encode(
'ascii', 'ignore'
)
ut.print_difftext(textdiff)
if dowrite:
print('WARNING: Not writing. Remove --diff from command line')
elif dowrite:
ut.write_to(fpath, autogen_text)
[docs]def makeinit(mod_dpath, exclude_modnames=[], use_star=False):
r"""
Args:
mod_dpath (str):
exclude_modnames (list): (Defaults to [])
use_star (bool): (Defaults to False)
Returns:
str: init_codeblock
CommandLine:
python -m utool.util_autogen makeinit --modname=wbia.algo
Example:
>>> # DISABLE_DOCTEST
>>> # SCRIPT
>>> from utool.util_autogen import * # NOQA
>>> import utool as ut
>>> modname = ut.get_argval('--modname', str, default=None)
>>> mod_dpath = (os.getcwd() if modname is None else
>>> ut.get_modpath(modname, prefer_pkg=True))
>>> mod_dpath = ut.unixpath(mod_dpath)
>>> mod_fpath = join(mod_dpath, '__init__.py')
>>> exclude_modnames = ut.get_argval(('--exclude', '-x'), list, default=[])
>>> use_star = ut.get_argflag('--star')
>>> init_codeblock = makeinit(mod_dpath, exclude_modnames, use_star)
>>> ut.dump_autogen_code(mod_fpath, init_codeblock)
"""
from utool._internal import util_importer
import utool as ut
module_name = ut.get_modname_from_modpath(mod_dpath)
IMPORT_TUPLES = util_importer.make_import_tuples(
mod_dpath, exclude_modnames=exclude_modnames
)
initstr = util_importer.make_initstr(module_name, IMPORT_TUPLES)
regen_command = 'cd %s\n' % (mod_dpath)
regen_command += ' makeinit.py'
regen_command += ' --modname={modname}'.format(modname=module_name)
if use_star:
regen_command += ' --star'
if len(exclude_modnames) > 0:
regen_command += ' -x ' + ' '.join(exclude_modnames)
regen_block = ut.codeblock(
'''
"""
Regen Command:
{regen_command}
"""
'''
).format(regen_command=regen_command)
importstar_codeblock = ut.codeblock(
'''
"""
python -c "import {module_name}" --dump-{module_name}-init
python -c "import {module_name}" --update-{module_name}-init
"""
__DYNAMIC__ = True
if __DYNAMIC__:
# TODO: import all utool external prereqs. Then the imports will not import
# anything that has already in a toplevel namespace
# COMMENTED OUT FOR FROZEN __INIT__
# Dynamically import listed util libraries and their members.
from utool._internal import util_importer
# FIXME: this might actually work with rrrr, but things arent being
# reimported because they are already in the modules list
import_execstr = util_importer.dynamic_import(__name__, IMPORT_TUPLES)
exec(import_execstr)
DOELSE = False
else:
# Do the nonexec import (can force it to happen no matter what if alwyas set
# to True)
DOELSE = True
if DOELSE:
# <AUTOGEN_INIT>
pass
# </AUTOGEN_INIT>
'''.format(
module_name=module_name
)
)
ts_line = '# Autogenerated on {ts}'.format(ts=ut.get_timestamp('printable'))
init_codeblock_list = ['# -*- coding: utf-8 -*-', ts_line]
init_codeblock_list.append(initstr)
init_codeblock_list.append('\nIMPORT_TUPLES = ' + ut.repr4(IMPORT_TUPLES))
if use_star:
init_codeblock_list.append(importstar_codeblock)
init_codeblock_list.append(regen_block)
init_codeblock = '\n'.join(init_codeblock_list)
return init_codeblock
[docs]def write_modscript_alias(fpath, modname, args='', pyscript='python'):
"""
convinience function because $@ is annoying to paste into the terminal
"""
import utool as ut
from os.path import splitext
allargs_dict = {
'.sh': ' $@',
'.bat': ' %1',
}
_, script_ext = splitext(fpath)
if script_ext not in ['.sh', '.bat']:
script_ext = '.bat' if ut.WIN32 else 'sh'
allargs = (args + allargs_dict[script_ext]).strip(' ')
if not modname.endswith('.py'):
fmtstr = '{pyscript} -m {modname} {allargs}'
else:
fmtstr = '{pyscript} {modname} {allargs}'
cmdstr = fmtstr.format(pyscript=pyscript, modname=modname, allargs=allargs)
ut.write_to(fpath, cmdstr)
os.system('chmod +x ' + fpath)
[docs]def autofix_codeblock(
codeblock,
max_line_len=80,
aggressive=False,
very_aggressive=False,
experimental=False,
):
r"""
Uses autopep8 to format a block of code
Example:
>>> # DISABLE_DOCTEST
>>> import utool as ut
>>> codeblock = ut.codeblock(
'''
def func( with , some = 'Problems' ):
syntax ='Ok'
but = 'Its very messy'
if None:
# syntax might not be perfect due to being cut off
ommiting_this_line_still_works= True
''')
>>> fixed_codeblock = ut.autofix_codeblock(codeblock)
>>> print(fixed_codeblock)
"""
# FIXME idk how to remove the blank line following the function with
# autopep8. It seems to not be supported by them, but it looks bad.
import autopep8
arglist = ['--max-line-length', '80']
if aggressive:
arglist.extend(['-a'])
if very_aggressive:
arglist.extend(['-a', '-a'])
if experimental:
arglist.extend(['--experimental'])
arglist.extend([''])
autopep8_options = autopep8.parse_args(arglist)
fixed_codeblock = autopep8.fix_code(codeblock, options=autopep8_options)
return fixed_codeblock
[docs]def load_func_from_module(modname, funcname, verbose=True, moddir=None, modpath=None):
r"""
Args:
modname (str): module name
funcname (str): function name
verbose (bool): verbosity flag(Defaults to True)
moddir (None): (Defaults to None)
CommandLine:
python -m utool.util_autogen load_func_from_module
Example:
>>> # DISABLE_DOCTEST
>>> # UNSTABLE_DOCTEST
>>> from utool.util_autogen import * # NOQA
>>> import utool as ut
>>> #funcname = 'multi_plot'
>>> modname = 'utool.util_path'
>>> funcname = 'checkpath'
>>> verbose = True
>>> moddir = None
>>> func, module, error_str = load_func_from_module(modname, funcname, verbose, moddir)
>>> source = ut.get_func_sourcecode(func, strip_docstr=True, strip_comments=True)
>>> keyname = ut.named_field('keyname', ut.REGEX_VARNAME)
>>> default = ut.named_field('default', '[\'\"A-Za-z_][A-Za-z0-9_\'\"]*')
>>> pattern = re.escape('kwargs.get(\'') + keyname + re.escape('\',')
>>> kwarg_keys = [match.groupdict()['keyname'] for match in re.finditer(pattern, source)]
"""
import utool as ut
from os.path import join
import imp
func = None
module = None
error_str = None
print('modname = %r' % (modname,))
print('funcname = %r' % (funcname,))
print('moddir = %r' % (moddir,))
try:
from xdoctest import static_analysis as static
from xdoctest import utils
print('modpath = {!r}'.format(modpath))
except ImportError:
modpath = None
else:
if modpath is None:
modpath = static.modname_to_modpath(modname)
if modpath is None:
modname = modname.split('.')[-1]
if moddir is not None:
modpath = join(moddir, modname + '.py')
if not exists(modpath):
modpath = None
if modpath is None:
raise Exception(
'Cannot find modname={} in moddir={}'.format(modname, moddir)
)
print('modpath = {!r}'.format(modpath))
module = utils.import_module_from_path(modpath)
print('module = {!r}'.format(module))
try:
func = eval('module.{}'.format(funcname))
except AttributeError:
imp.reload(module)
func = eval('module.{}'.format(funcname))
print('func = {!r}'.format(func))
return func, module, error_str
if not isinstance(modname, six.string_types):
error_str = 'modname=%r is not a string. bad input' % (modname,)
else:
if False:
# TODO: static analysis
import jedi # NOQA
modpath = ut.util_import.get_modpath_from_modname(modname)
script = jedi.Script(path=modpath)
mod = script._get_module()
# monkeypatch
func.script = script
func = None
for name in mod.names_dict[funcname]:
if name.parent.type == 'funcdef':
func = name.parent
break
return func, mod, error_str
# ut.get_modpath_from_modname(modname)
if module is None:
try:
module = __import__(modname)
except ImportError:
if moddir is not None:
# parts =
# There can be a weird double import error thing happening here
# Rectify the dots in the filename
module = ut.import_module_from_fpath(
join(moddir, modname.split('.')[-1] + '.py')
)
else:
raise
# import inspect
# try:
# imp.reload(module)
# except Exception as ex:
# pass
# if False:
# # Try removing pyc if it exists
# if module.__file__.endswith('.pyc'):
# ut.delete(module.__file__, verbose=False)
# try:
# module = __import__(modname)
# except ImportError:
# if moddir is not None:
# module = ut.import_module_from_fpath(join(moddir, modname.split('.')[-1] + '.py'))
# else:
# raise
try:
imp.reload(module)
except Exception as ex:
pass
try:
# FIXME: PYTHON 3
execstr = ut.codeblock(
"""
try:
import {modname}
module = {modname}
#print('Trying to reload module=%r' % (module,))
imp.reload(module)
except Exception:
# If it fails maybe the module is not in the path
if moddir is not None:
try:
import imp
import os
orig_dir = os.getcwd()
os.chdir(moddir)
modname_str = '{modname}'
modinfo = imp.find_module(modname_str, [moddir])
module = imp.load_module(modname_str, *modinfo)
#print('loaded module=%r' % (module,))
except Exception as ex:
ut.printex(ex, 'failed to imp.load_module')
pass
finally:
os.chdir(orig_dir)
import imp
import utool as ut
imp.reload(ut.util_autogen)
imp.reload(ut.util_inspect)
try:
func = module.{funcname}
except AttributeError:
docstr = 'Could not find attribute funcname={funcname} in modname={modname} This might be a reloading issue'
imp.reload(module)
"""
).format(**locals())
exec_locals = locals()
exec_globals = globals()
exec(execstr, exec_globals, exec_locals)
func = exec_locals.get('func', None)
module = exec_locals.get('module', None)
except Exception as ex2:
docstr = 'error ' + str(ex2)
if verbose:
import utool as ut
# ut.printex(ex1, 'ex1')
ut.printex(ex2, 'ex2', tb=True)
testcmd = (
"python -c \"import utool; print(utool.auto_docstr('%s', '%s'))\""
% (modname, funcname)
)
error_str = ut.formatex(
ex2, 'ex2', tb=True, keys=['modname', 'funcname', 'testcmd']
)
error_str += '---' + execstr
return func, module, error_str
[docs]def auto_docstr(modname, funcname, verbose=True, moddir=None, modpath=None, **kwargs):
r"""
called from vim. Uses strings of filename and modnames to build docstr
Args:
modname (str): name of a python module
funcname (str): name of a function in the module
Returns:
str: docstr
CommandLine:
python -m utool.util_autogen auto_docstr
python -m utool --tf auto_docstr
Example:
>>> # DISABLE_DOCTEST
>>> import utool as ut
>>> from utool.util_autogen import * # NOQA
>>> ut.util_autogen.rrr(verbose=False)
>>> #docstr = ut.auto_docstr('wbia.algo.hots.smk.smk_index', 'compute_negentropy_names')
>>> modname = ut.get_argval('--modname', default='utool.util_autogen')
>>> funcname = ut.get_argval('--funcname', default='auto_docstr')
>>> moddir = ut.get_argval('--moddir', type_=str, default=None)
>>> docstr = ut.util_autogen.auto_docstr(modname, funcname)
>>> print(docstr)
"""
# import utool as ut
func, module, error_str = load_func_from_module(
modname, funcname, verbose=verbose, moddir=moddir, modpath=modpath
)
if error_str is None:
try:
docstr = make_default_docstr(func, **kwargs)
except Exception as ex:
import utool as ut
error_str = ut.formatex(ex, 'Caught Error in parsing docstr', tb=True)
# ut.printex(ex)
error_str += (
'\n\nReplicateCommand:\n '
'python -m utool --tf auto_docstr '
'--modname={modname} --funcname={funcname} --moddir={moddir}'
).format(modname=modname, funcname=funcname, moddir=moddir)
error_str += '\n kwargs=' + ut.repr4(kwargs)
return error_str
else:
docstr = error_str
return docstr
[docs]def print_auto_docstr(modname, funcname):
"""
python -c "import utool; utool.print_auto_docstr('wbia.algo.hots.smk.smk_index', 'compute_negentropy_names')"
python -c "import utool;
utool.print_auto_docstr('wbia.algo.hots.smk.smk_index', 'compute_negentropy_names')"
"""
docstr = auto_docstr(modname, funcname)
print(docstr)
# <INVIDIAL DOCSTR COMPONENTS>
[docs]def make_args_docstr(
argname_list,
argtype_list,
argdesc_list,
ismethod,
va_name=None,
kw_name=None,
kw_keys=[],
):
r"""
Builds the argument docstring
Args:
argname_list (list): names
argtype_list (list): types
argdesc_list (list): descriptions
ismethod (bool): if generating docs for a method
va_name (Optional[str]): varargs name
kw_name (Optional[str]): kwargs name
kw_keys (Optional[list]): accepted kwarg keys
Returns:
str: arg_docstr
CommandLine:
python -m utool.util_autogen make_args_docstr
Example:
>>> # ENABLE_DOCTEST
>>> from utool.util_autogen import * # NOQA
>>> argname_list = ['argname_list', 'argtype_list', 'argdesc_list']
>>> argtype_list = ['list', 'list', 'list']
>>> argdesc_list = ['names', 'types', 'descriptions']
>>> va_name = 'args'
>>> kw_name = 'kwargs'
>>> kw_keys = ['']
>>> ismethod = False
>>> arg_docstr = make_args_docstr(argname_list, argtype_list,
>>> argdesc_list, ismethod, va_name,
>>> kw_name, kw_keys)
>>> result = str(arg_docstr)
>>> print(result)
argname_list (list): names
argtype_list (list): types
argdesc_list (list): descriptions
*args:
**kwargs:
"""
import utool as ut
if ismethod:
# Remove self from the list
argname_list = argname_list[1:]
argtype_list = argtype_list[1:]
argdesc_list = argdesc_list[1:]
argdoc_list = [
arg + ' (%s): %s' % (_type, desc)
for arg, _type, desc in zip(argname_list, argtype_list, argdesc_list)
]
# Add in varargs and kwargs
# References:
# http://www.sphinx-doc.org/en/stable/ext/example_google.html#example-google
if va_name is not None:
argdoc_list.append('*' + va_name + ':')
if kw_name is not None:
import textwrap
prefix = '**' + kw_name + ': '
wrapped_lines = textwrap.wrap(', '.join(kw_keys), width=70 - len(prefix))
sep = '\n' + (' ' * len(prefix))
kw_keystr = sep.join(wrapped_lines)
argdoc_list.append((prefix + kw_keystr).strip())
# align?
align_args = False
if align_args:
argdoc_aligned_list = ut.align_lines(argdoc_list, character='(')
arg_docstr = '\n'.join(argdoc_aligned_list)
else:
arg_docstr = '\n'.join(argdoc_list)
return arg_docstr
[docs]def make_returns_or_yeilds_docstr(return_type, return_name, return_desc):
return_doctr = return_type + ': '
if return_name is not None:
return_doctr += return_name
if len(return_desc) > 0:
return_doctr += ' - '
return_doctr += return_desc
return return_doctr
[docs]def make_example_docstr(
funcname=None,
modname=None,
argname_list=None,
defaults=None,
return_type=None,
return_name=None,
ismethod=False,
):
"""
Creates skeleton code to build an example doctest
Args:
funcname (str): function name
modname (str): module name
argname_list (str): list of argument names
defaults (None):
return_type (None):
return_name (str): return variable name
ismethod (bool):
Returns:
str: examplecode
CommandLine:
python -m utool.util_autogen --test-make_example_docstr
Example:
>>> # ENABLE_DOCTEST
>>> from utool.util_autogen import * # NOQA
>>> funcname = 'make_example_docstr'
>>> modname = 'utool.util_autogen'
>>> argname_list = ['qaids', 'qreq_']
>>> defaults = None
>>> return_type = tuple
>>> return_name = 'foo'
>>> ismethod = False
>>> examplecode = make_example_docstr(funcname, modname, argname_list, defaults, return_type, return_name, ismethod)
>>> result = str(examplecode)
>>> print(result)
# DISABLE_DOCTEST
from utool.util_autogen import * # NOQA
import utool as ut
import wbia
species = wbia.const.TEST_SPECIES.ZEB_PLAIN
qaids = ibs.get_valid_aids(species=species)
qreq_ = wbia.testdata_qreq_()
foo = make_example_docstr(qaids, qreq_)
result = ('foo = %s' % (ut.repr2(foo),))
print(result)
"""
import utool as ut
examplecode_lines = []
top_import_fmstr = 'from {modname} import * # NOQA'
top_import = top_import_fmstr.format(modname=modname)
import_lines = [top_import]
if modname.startswith('utool'):
import_lines += ['import utool as ut']
# is_show_func = not modname.startswith('utool') and not modname.startswith('mtgmonte')
is_show_func = modname.startswith('plottool')
# TODO: Externally register these
default_argval_map = {
'ibs': "wbia.opendb(defaultdb='testdb1')",
'testres': "wbia.testdata_expts('PZ_MTEST')",
'qreq_': 'wbia.testdata_qreq_()',
'cm_list': 'qreq_.execute()',
'cm': 'qreq_.execute()[0]',
'aid_list': 'ibs.get_valid_aids()',
'nid_list': 'ibs._get_all_known_nids()',
'qaids': 'ibs.get_valid_aids(species=species)',
'daids': 'ibs.get_valid_aids(species=species)',
'species': 'wbia.const.TEST_SPECIES.ZEB_PLAIN',
'kpts': 'vt.dummy.get_dummy_kpts()',
'dodraw': 'ut.show_was_requested()',
'img_fpath': "ut.grab_test_imgpath('carl.jpg')",
'gfpath': "ut.grab_test_imgpath('carl.jpg')",
'img': 'vt.imread(img_fpath)',
'img_in': 'vt.imread(img_fpath)',
'bbox': '(10, 10, 50, 50)',
'theta': '0.0',
'rng': 'np.random.RandomState(0)',
}
import_depends_map = {
'wbia': 'import wbia',
'vt': 'import vtool as vt',
#'img': 'import vtool as vt', # TODO: remove. fix dependency
#'species': 'import wbia',
}
var_depends_map = {
'species': ['wbia'],
'ibs': ['wbia'],
'testres': ['wbia'],
'kpts': ['vt'],
#'qreq_': ['ibs', 'species', 'daids', 'qaids'],
'qreq_': ['wbia'],
'qaids': ['ibs'],
'daids': ['ibs'],
'qaids': ['species'],
'daids': ['species'],
'img': ['img_fpath', 'vt'],
}
def find_arg_defaultrepr(argname, val):
import types
if val == '?':
if argname in default_argval_map:
val = ut.PythonStatement(default_argval_map[argname])
if argname in import_depends_map:
import_lines.append(import_depends_map[argname])
elif isinstance(val, types.ModuleType):
return val.__name__
return repr(val)
# augment argname list with dependencies
dependant_argnames = [] # deque()
def append_dependant_argnames(argnames, dependant_argnames):
""" use hints to add known dependencies for certain argument inputs """
for argname in argnames:
# Check if argname just implies an import
if argname in import_depends_map:
import_lines.append(import_depends_map[argname])
# Check if argname was already added as dependency
if (
argname not in dependant_argnames
and argname not in argname_list
and argname not in import_depends_map
):
dependant_argnames.append(argname)
# Check if argname has dependants
if argname in var_depends_map:
argdeps = var_depends_map[argname]
# RECURSIVE CALL
append_dependant_argnames(argdeps, dependant_argnames)
append_dependant_argnames(argname_list, dependant_argnames)
# Define argnames and dependencies in example code
# argnames prefixed with dependeancies
argname_list_ = list(dependant_argnames) + argname_list
# Default example values
defaults_ = [] if defaults is None else defaults
num_unknown = len(argname_list_) - len(defaults_)
default_vals = ['?'] * num_unknown + list(defaults_)
arg_val_iter = zip(argname_list_, default_vals)
inferred_defaults = [
find_arg_defaultrepr(argname, val) for argname, val in arg_val_iter
]
argdef_lines = [
'%s = %s' % (argname, inferrepr)
for argname, inferrepr in zip(argname_list_, inferred_defaults)
]
import_lines = ut.unique_ordered(import_lines)
if any([inferrepr == repr('?') for inferrepr in inferred_defaults]):
examplecode_lines.append('# DISABLE_DOCTEST')
else:
# Enable the test if it can be run immediately
examplecode_lines.append('# DISABLE_DOCTEST')
examplecode_lines.extend(import_lines)
examplecode_lines.extend(argdef_lines)
# Default example result assignment
result_assign = ''
result_print = None
if 'return_name' in vars():
if return_type is not None:
if return_name is None:
return_name = 'result'
result_assign = return_name + ' = '
result_print = 'print(result)' # + return_name + ')'
# Default example call
if ismethod:
selfname = argname_list[0]
methodargs = ', '.join(argname_list[1:])
tup = (selfname, '.', funcname, '(', methodargs, ')')
example_call = ''.join(tup)
else:
funcargs = ', '.join(argname_list)
tup = (funcname, '(', funcargs, ')')
example_call = ''.join(tup)
# Append call line
examplecode_lines.append(result_assign + example_call)
if result_print is not None:
if return_name != 'result':
# examplecode_lines.append('result = str(' + return_name + ')')
result_line_fmt = (
"result = ('{return_name} = %s' % (ut.repr2({return_name}),))"
)
result_line = result_line_fmt.format(return_name=return_name)
examplecode_lines.append(result_line)
examplecode_lines.append(result_print)
# TODO: infer this
if is_show_func:
examplecode_lines += [
'# xdoctest: +REQUIRES(--show)',
]
examplecode = '\n'.join(examplecode_lines)
return examplecode
[docs]def make_cmdline_docstr(funcname, modname):
# cmdline_fmtstr = 'python -m {modname} --test-{funcname}' # --enableall'
# cmdline_fmtstr = 'python -m {modname} --exec-{funcname}' # --enableall'
if False and '.' in modname and '.' not in funcname:
pkg = modname.split('.')[0]
# TODO check if __main__ exists with the necessary utool stuffs
# TODO check if --show should be given
cmdline_fmtstr = 'python -m {pkg} --tf {funcname}' # --enableall'
return cmdline_fmtstr.format(**locals())
else:
# TODO: infer this
is_show_func = 'draw' in funcname or 'show' in funcname
if is_show_func:
cmdline_fmtstr = 'python -m {modname} {funcname} --show'
else:
cmdline_fmtstr = 'python -m {modname} {funcname}' # --enableall'
return cmdline_fmtstr.format(**locals())
# </INVIDIAL DOCSTR COMPONENTS>
[docs]def make_docstr_block(header, block):
import utool as ut
indented_block = '\n' + ut.indent(block)
docstr_block = ''.join([header, ':', indented_block])
return docstr_block
[docs]def make_default_docstr(
func,
with_args=True,
with_ret=True,
with_commandline=True,
with_example=True,
with_header=False,
with_debug=False,
):
r"""
Tries to make a sensible default docstr so the user
can fill things in without typing too much
# TODO: Interleave old documentation with new documentation
Args:
func (function): live python function
with_args (bool):
with_ret (bool): (Defaults to True)
with_commandline (bool): (Defaults to True)
with_example (bool): (Defaults to True)
with_header (bool): (Defaults to False)
with_debug (bool): (Defaults to False)
Returns:
tuple: (argname, val)
Ignore:
pass
CommandLine:
python -m utool.util_autogen --exec-make_default_docstr --show
Example:
>>> # ENABLE_DOCTEST
>>> from utool.util_autogen import * # NOQA
>>> import utool as ut
>>> func = ut.make_default_docstr
>>> #func = ut.make_args_docstr
>>> #func = PythonStatement
>>> func = auto_docstr
>>> default_docstr = make_default_docstr(func)
>>> result = str(default_docstr)
>>> print(result)
"""
import utool as ut
# from utool import util_inspect
funcinfo = ut.util_inspect.infer_function_info(func)
argname_list = funcinfo.argname_list
argtype_list = funcinfo.argtype_list
argdesc_list = funcinfo.argdesc_list
return_header = funcinfo.return_header
return_type = funcinfo.return_type
return_name = funcinfo.return_name
return_desc = funcinfo.return_desc
funcname = funcinfo.funcname
modname = funcinfo.modname
defaults = funcinfo.defaults
num_indent = funcinfo.num_indent
needs_surround = funcinfo.needs_surround
funcname = funcinfo.funcname
ismethod = funcinfo.ismethod
va_name = funcinfo.va_name
kw_name = funcinfo.kw_name
kw_keys = funcinfo.kw_keys
docstr_parts = []
# Header part
if with_header:
header_block = funcname
docstr_parts.append(header_block)
# Args part
if with_args and len(argname_list) > 0:
argheader = 'Args'
arg_docstr = make_args_docstr(
argname_list, argtype_list, argdesc_list, ismethod, va_name, kw_name, kw_keys
)
argsblock = make_docstr_block(argheader, arg_docstr)
docstr_parts.append(argsblock)
# if False:
# with_kw = with_args
# if with_kw and len(kwarg_keys) > 0:
# #ut.embed()
# import textwrap
# kwargs_docstr = ', '.join(kwarg_keys)
# kwargs_docstr = '\n'.join(textwrap.wrap(kwargs_docstr))
# kwargsblock = make_docstr_block('Kwargs', kwargs_docstr)
# docstr_parts.append(kwargsblock)
# Return / Yeild part
if with_ret and return_header is not None:
if return_header is not None:
return_doctr = make_returns_or_yeilds_docstr(
return_type, return_name, return_desc
)
returnblock = make_docstr_block(return_header, return_doctr)
docstr_parts.append(returnblock)
# Example part
# try to generate a simple and unit testable example
if with_commandline:
cmdlineheader = 'CommandLine'
cmdlinecode = make_cmdline_docstr(funcname, modname)
cmdlineblock = make_docstr_block(cmdlineheader, cmdlinecode)
docstr_parts.append(cmdlineblock)
if with_example:
exampleheader = 'Example'
examplecode = make_example_docstr(
funcname, modname, argname_list, defaults, return_type, return_name, ismethod
)
examplecode_ = ut.indent(examplecode, '>>> ')
exampleblock = make_docstr_block(exampleheader, examplecode_)
docstr_parts.append(exampleblock)
# DEBUG part (in case something goes wrong)
if with_debug:
debugheader = 'Debug'
debugblock = ut.codeblock(
"""
num_indent = {num_indent}
"""
).format(num_indent=num_indent)
debugblock = make_docstr_block(debugheader, debugblock)
docstr_parts.append(debugblock)
# Enclosure / Indentation Parts
if needs_surround:
docstr_parts = ['r"""'] + ['\n\n'.join(docstr_parts)] + ['"""']
default_docstr = '\n'.join(docstr_parts)
else:
default_docstr = '\n\n'.join(docstr_parts)
docstr_indent = ' ' * (num_indent + 4)
default_docstr = ut.indent(default_docstr, docstr_indent)
return default_docstr
[docs]def remove_codeblock_syntax_sentinals(code_text):
r"""
Removes template comments and vim sentinals
Args:
code_text (str):
Returns:
str: code_text_
"""
flags = re.MULTILINE | re.DOTALL
code_text_ = code_text
code_text_ = re.sub(r'^ *# *REM [^\n]*$\n?', '', code_text_, flags=flags)
code_text_ = re.sub(r'^ *# STARTBLOCK *$\n', '', code_text_, flags=flags)
code_text_ = re.sub(r'^ *# ENDBLOCK *$\n?', '', code_text_, flags=flags)
code_text_ = code_text_.rstrip()
return code_text_
[docs]def make_default_module_maintest(modname, modpath=None, test_code=None):
"""
make_default_module_maintest
DEPRICATE
TODO: use path relative to home dir if the file is a script
Args:
modname (str): module name
Returns:
str: text source code
CommandLine:
python -m utool.util_autogen --test-make_default_module_maintest
References:
http://legacy.python.org/dev/peps/pep-0338/
Example:
>>> # ENABLE_DOCTEST
>>> from utool.util_autogen import * # NOQA
>>> modname = 'utool.util_autogen'
>>> text = make_default_module_maintest(modname)
>>> result = str(text)
>>> print(result)
"""
import utool as ut
# Need to use python -m to run a module
# otherwise their could be odd platform specific errors.
# python -c "import utool, {modname};
# ut.doctest_funcs({modname}, allexamples=True)"
# in_pythonpath, module_type, path = find_modname_in_pythonpath(modname)
# only use the -m if it is part of a package directory
if modpath is not None:
moddir = dirname(modpath)
pkginit_fpath = join(moddir, '__init__.py')
use_modrun = exists(pkginit_fpath)
else:
use_modrun = True
if use_modrun:
pyargs = '-m ' + modname
else:
if ut.WIN32:
modpath = normpath(modpath).replace(expanduser('~'), '%HOME%')
pyargs = '-B ' + ut.ensure_unixslash(modpath)
else:
modpath = normpath(modpath).replace(expanduser('~'), '~')
pyargs = modpath
cmdline = ut.codeblock(
"""
python {pyargs}
"""
)
if not use_modrun:
if ut.WIN32:
augpath = 'set PYTHONPATH=%PYTHONPATH%' + os.pathsep + moddir
else:
augpath = 'export PYTHONPATH=$PYTHONPATH' + os.pathsep + moddir
cmdline = augpath + '\n' + cmdline
cmdline = ut.indent(cmdline, ' ' * 8).lstrip(' ').format(pyargs=pyargs)
if test_code is None:
test_code = ut.codeblock(
r"""
import multiprocessing
multiprocessing.freeze_support() # for win32
import utool as ut # NOQA
ut.doctest_funcs()
"""
)
test_code = ut.indent(test_code, ' ' * 4).lstrip(' ')
text = ut.codeblock(
r'''
# STARTBLOCK
if __name__ == '__main__':
r"""
CommandLine:
{cmdline}
"""
{test_code}
# ENDBLOCK
'''
).format(cmdline=cmdline, test_code=test_code)
print('test_code = {!r}'.format(test_code))
text = remove_codeblock_syntax_sentinals(text)
return text
[docs]def is_modname_in_pythonpath(modname):
in_pythonpath, module_type, path = find_modname_in_pythonpath(modname)
print(module_type)
return in_pythonpath
[docs]def find_modname_in_pythonpath(modname):
import sys
from os.path import exists, join
rel_modpath = modname.replace('.', '/')
in_pythonpath = False
module_type = None
path_list = sys.path
path_list = os.environ['PATH'].split(os.pathsep)
for path in path_list:
full_modpath = join(path, rel_modpath)
if exists(full_modpath + '.py'):
in_pythonpath = True
module_type = 'module'
break
if exists(join(full_modpath, '__init__.py')):
in_pythonpath = True
module_type = 'package'
break
return in_pythonpath, module_type, path
# ut.get_modpath(modname)
# import imp
# tup = imp.find_module(modname)
# (file, filename, (suffix, mode, type))
if __name__ == '__main__':
"""
CommandLine:
python wbia/control/template_generator.py --tbls annotations --Tflags getters native
python -c "import utool, utool.util_autogen; utool.doctest_funcs(utool.util_autogen, allexamples=True)"
python -m utool.util_autogen
python -m utool.util_autogen --allexamples
python -m utool.util_autogen --allexamples --noface --nosrc
"""
import multiprocessing
multiprocessing.freeze_support() # for win32
import utool as ut # NOQA
ut.doctest_funcs()