"""Generate pypredefs using Maya's help query to read command flags.
:created: 18/05/2018
:author: Benoit GIELLY <benoit.gielly@gmail.com>
"""
from keyword import iskeyword
import logging
import os
import re
from maya import cmds, mel
try:
import urllib2 as urllib
except ImportError:
import urllib
LOG = logging.getLogger(__name__)
[docs]def get_maya_urls():
"""Generate a table for each commands from the online documentation.
Returns:
dict: Generates a {command:url} dictionnary.
"""
version = cmds.about(majorVersion=True)
url = (
"http://help.autodesk.com/cloudhelp/{}/"
"ENU/Maya-Tech-Docs/CommandsPython/{{}}.html"
).format(version)
try:
response = urllib.urlopen(url.format("index_all"))
html = response.read()
commands = re.findall(r'<a href="(.*?).html"', html)
except (urllib.URLError, urllib.HTTPError):
commands = []
data = {}
for each in commands:
data[each] = url.format(each)
return data
[docs]def get_local_url():
"""Generate a table for each commands from the local documentation.
Returns:
dict: Generates a {command:url} dictionnary.
"""
version = cmds.about(majorVersion=True)
path = "/apps/Linux64/aw/maya{}_docs/docs/CommandsPython".format(version)
data = {}
for each in os.listdir(path):
name = os.path.splitext(each)[0]
data[name] = os.path.join(path, each)
return data
[docs]def indent(text, amount=4):
"""Indent each lines of the given string.
Args:
text (str): Text to indent
amount (int): Number or character padding
Returns:
str: Indented text.
"""
return "".join(amount * " " + line for line in text.splitlines(True))
[docs]def cleanup_flags(docstring):
"""Cleanup the docstring from the mel output to a nicer result.
Args:
docstring (str): The docstring extracted from the help command query.
Example:
Reformat this docstring::
Flags:
-n -name String
-p -parent String
-s -shared
-ss -skipSelect
So it looks like this::
Flags:
name (n): String
parent (p): String
shared (s): None
skipSelect (ss): None
Also returns a list of all the flags so they can be added to the method
definition (eg. "flag=None") so IDE can autocomplete them.
Args:
docstring (str): The docstring containing the flags to cleanup.
Returns:
tuple: The cleaned-up docstring with nicer flags, and the list of flags
"""
lines = docstring.splitlines(True)
# get header
header = lines[0].strip()
# parse other lines
desc_lines = []
flag_lines = []
temp_flags = []
for each in lines[1:]:
line = each.strip()
if "Flags:" in each:
flag_lines.append(line)
elif line.startswith("-") and line not in temp_flags:
temp_flags.append(line)
elif line:
desc_lines.append(line)
# parse and cleanup flags
flags = []
for line in temp_flags:
shortname, longname, desc = None, None, None
for each in line.split():
if each.startswith("-") and not shortname:
shortname = each[1:]
elif each.startswith("-"):
longname = each[1:]
else:
desc = each
if not longname:
longname, shortname = shortname, longname
txt = "{} ({}): {}".format(longname, shortname, desc)
flag_lines.append(indent(txt, 4))
flag = longname if not iskeyword(longname) else shortname
if flag:
flags.append(flag)
return header, desc_lines, flag_lines, flags
[docs]def generate_pypredef(module, outdir=None, doclink=True):
"""Generate pypredefs using Maya's help query to read command flags.
Args:
module (mod): Module from which you want to generate the pypredef.
outdir (str): Directory in which you want to export the text file.
doclink (bool): Adds a link to the online documentation.
Note:
There are a few maya commands (e.g. "nexCtx") that are almost
1 MILLION line length... so there's a test to skip the command
if the docstring is longer than 10k lines.
Example:
::
from maya import cmds
generate_pypredef(cmds, outdir="~/Desktop")
"""
filetext = ""
# use the cmds.help method if extracting maya commands
if module.__name__ == "maya.cmds":
maya_urls = {}
if doclink:
maya_urls = get_maya_urls() or get_local_url()
commands = sorted(cmds.help("*", list=True))
for each in commands:
if hasattr(cmds, each) and not iskeyword(each):
strip = cmds.help(each).strip()
if len(strip) > 10000:
continue
header, l_desc, l_flags, all_flags = cleanup_flags(strip)
header = "{}.\n".format(header) if header else header
html = maya_urls.get(each, "")
html = "\n{}\n".format(html) if html else html
desc = "\n".join(l_desc)
desc = "\n{}\n".format(desc) if desc else desc
flags = "\n".join(l_flags)
flags = "\n{}\n".format(flags) if flags else flags
docstring = '"""{}{}{}{}\n"""\nreturn args, kwargs\n'
docstring = docstring.format(header, html, desc, flags)
def_flags = "=None, ".join([x for x in all_flags])
def_flags += "=None, " if def_flags else ""
def_txt = "\n\ndef {}({}*args, **kwargs):\n{}".format(
each, def_flags, indent(docstring, 4)
)
filetext += def_txt
# use python to list commands otherwise
else:
for each in sorted(dir(module)):
if bool(mel.eval("exists " + each)):
strip = mel.eval("help %s" % each).strip("\n")
strip = strip if "\n" not in strip else strip + "\n"
if len(strip) > 10000:
continue
docstring = '"""{}"""\nreturn args, kwargs\n'.format(strip)
def_txt = "\n\ndef {}(*args, **kwargs):\n{}".format(
each, indent(docstring, 4)
)
filetext += def_txt
# create path
if not outdir:
outdir = os.path.join(os.environ.get("HOME"), "pypredef")
if not os.path.exists(outdir):
os.makedirs(outdir)
# export file
file_path = os.path.join(outdir, "%s.pypredef" % module.__name__)
with open(file_path, "w") as stream:
stream.writelines(filetext)
LOG.info("Pypredef generated and saved into %r", file_path)