Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Python3 for Pixar's UsdMaya and unit tests #555

Merged
merged 11 commits into from
Jun 15, 2020
4 changes: 4 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def PrintError(error):
traceback.print_exc()
print("ERROR:", error)

def Python3():
return sys.version_info.major == 3
############################################################
def Windows():
return platform.system() == "Windows"
Expand Down Expand Up @@ -558,6 +560,7 @@ def __init__(self, args):
Build directory {buildDir}
Install directory {instDir}
Variant {buildVariant}
Python 3: {enablePython3}
Python Debug {debugPython}
CMake generator {cmakeGenerator}"""

Expand Down Expand Up @@ -588,6 +591,7 @@ def __init__(self, args):
ctestArgs=context.ctestArgs,
buildVariant=BuildVariant(context),
debugPython=("On" if context.debugPython else "Off"),
enablePython3=("On" if Python3() else "Off"),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simple change to output Python3 state in the output console.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only to indicate that the build.py was called with Python 3 - right? Not that we are actually building python 3 bindings. That is still controlled by the cmake option "BUILD_WITH_PYTHON_3"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seando-adsk Correct, just a simple indication in the output console:

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @seando-adsk's point was that that's indicating that build.py itself is running using Python 3, and not necessarily indicating that you're building for Python 3. You could run build.py using Python 3 but still be building maya-usd for Python 2, in which case that indicator might be misleading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I already realized that :) I agree with you that this could be misleading and in fact in my mind was thinking to change it. something for another day.

cmakeGenerator=("Default" if not context.cmakeGenerator
else context.cmakeGenerator)
)
Expand Down
47 changes: 33 additions & 14 deletions cmake/modules/FindMaya.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -239,21 +239,7 @@ find_program(MAYA_EXECUTABLE
"Maya's executable path"
)

find_program(MAYA_PY_EXECUTABLE
mayapy
HINTS
"${MAYA_LOCATION}"
"$ENV{MAYA_LOCATION}"
"${MAYA_BASE_DIR}"
PATH_SUFFIXES
Maya.app/Contents/bin/
bin/
DOC
"Maya's Python executable path"
)

if(MAYA_INCLUDE_DIRS AND EXISTS "${MAYA_INCLUDE_DIR}/maya/MTypes.h")

# Tease the MAYA_API_VERSION numbers from the lib headers
file(STRINGS ${MAYA_INCLUDE_DIR}/maya/MTypes.h TMP REGEX "#define MAYA_API_VERSION.*$")
string(REGEX MATCHALL "[0-9]+" MAYA_API_VERSION ${TMP})
Expand All @@ -265,9 +251,42 @@ if(MAYA_INCLUDE_DIRS AND EXISTS "${MAYA_INCLUDE_DIR}/maya/MTypes.h")
else()
string(SUBSTRING ${MAYA_API_VERSION} "0" "4" MAYA_APP_VERSION)
endif()
endif()

# swtich between mayapy and mayapy2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor typo "switch"

set(mayapy_exe mayapy)
if(${MAYA_APP_VERSION} STRGREATER_EQUAL "2021")
# check to see if we have a mayapy2 executable
find_program(MAYA_PY_EXECUTABLE2
mayapy2
HINTS
"${MAYA_LOCATION}"
"$ENV{MAYA_LOCATION}"
"${MAYA_BASE_DIR}"
PATH_SUFFIXES
Maya.app/Contents/bin/
bin/
DOC
"Maya's Python executable path"
)
if(NOT BUILD_WITH_PYTHON_3 AND MAYA_PY_EXECUTABLE2)
set(mayapy_exe mayapy2)
endif()
endif()

find_program(MAYA_PY_EXECUTABLE
${mayapy_exe}
HINTS
"${MAYA_LOCATION}"
"$ENV{MAYA_LOCATION}"
"${MAYA_BASE_DIR}"
PATH_SUFFIXES
Maya.app/Contents/bin/
bin/
DOC
"Maya's Python executable path"
)

# handle the QUIETLY and REQUIRED arguments and set MAYA_FOUND to TRUE if
# all listed variables are TRUE
include(FindPackageHandleStandardArgs)
Expand Down
10 changes: 9 additions & 1 deletion doc/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Before building the project, consult the following table to ensure you use the r
| Operating System | Windows 10 | Catalina (10.15), Mojave (10.14), High Sierra (10.13) | CentOS 7 |
| Compiler Requirement| Maya 2018/2019 (VS 2015 update 3)<br>Maya 2020 (VS 2017) | Maya 2018/2019 (Xcode 7.3.1)<br>Maya 2020 (Xcode version 10.2.1) | Maya 2018 (gcc 4.8.2)<br>Maya 2019/2020 (gcc 6.3.1) |
| Minimum Cmake Version | 3.13 | 3.13 | 3.13 |
| Python | 2.7.15 | 2.7.15 | 2.7.15 |
| Python | 2.7.15 or 3.7.7 | 2.7.15 or 3.7.7 | 2.7.15 or 3.7.7 |
| Python Packages | PyYAML, PySide, PyOpenGL, Jinja2 | PyYAML, PySide2, PyOpenGL, Jinja2 | PyYAML, PySide, PyOpenGL, Jinja2 |
| Build generator | Visual Studio, Ninja (Recommended) | XCode, Ninja (Recommended) | Ninja (Recommended) |
| Command processor | Visual Studio X64 2015 or 2017 command prompt | bash | bash |
Expand Down Expand Up @@ -186,6 +186,14 @@ Test project /Users/sabrih/Desktop/workspace/build/Debug/plugin/al
100% tests passed, 0 tests failed out of 8
```

***NOTE:*** As part of compatibility support for python 3 in maya-usd, there are number of python tests that require the use of python’s `future` module. While the `future` module is
available in our new preview releases of Maya, this package doesn't exist in the version of python in Maya 2018/2019/2020. Please follow the below steps in order to install the `future` package:

1. Download get-py.py from https://bootstrap.pypa.io/get-pip.py
2. Open a command-line and navigate to `mayapy.exe` for Maya version which needs future package. Run:`mayapy.exe <path_to_downloaded_file>/get-pip.py`
3. Pip will be added to `<path_to_maya>Python/Scripts`
4. Install the future package: `<path_to_maya>Python/Scripts/pip install future`

# Additional Build Instruction

##### Boost:
Expand Down
1 change: 0 additions & 1 deletion plugin/pxr/cmake/macros/Public.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ function(pxr_setup_python)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/__init__.py
${CMAKE_CURRENT_BINARY_DIR}/__init__.pyc
DESTINATION
${INSTALL_DIR_SUFFIX}/${installPrefix}
)
Expand Down
12 changes: 6 additions & 6 deletions plugin/pxr/cmake/macros/compilePython.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import py_compile

if len(sys.argv) < 4:
print "Usage: %s src.py file.py file.pyc" % sys.argv[0]
print("Usage: %s src.py file.py file.pyc" % sys.argv[0])
sys.exit(1)

try:
Expand All @@ -45,14 +45,14 @@
try:
linenumber = exc_value[1][1]
line = exc_value[1][3]
print '%s:%s: %s: "%s"' % (sys.argv[1], linenumber, error, line)
print('%s:%s: %s: "%s"' % (sys.argv[1], linenumber, error, line))
except IndexError:
print '%s: Syntax error: "%s"' % (sys.argv[1], error)
print('%s: Syntax error: "%s"' % (sys.argv[1], error))
else:
print "%s: Unhandled compile error: (%s) %s" % (
sys.argv[1], compileError.exc_type_name, exc_value)
print("%s: Unhandled compile error: (%s) %s" % (
sys.argv[1], compileError.exc_type_name, exc_value))
sys.exit(1)
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
print "%s: Unhandled exception: %s" % (sys.argv[1], exc_value)
print("%s: Unhandled exception: %s" % (sys.argv[1], exc_value))
sys.exit(1)
16 changes: 8 additions & 8 deletions plugin/pxr/cmake/macros/testWrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ def _windowsReplacement(m):

def _cleanOutput(pathPattern, fileName, verbose):
if verbose:
print "stripping path pattern {0} from file {1}".format(pathPattern,
fileName)
print("stripping path pattern {0} from file {1}".format(pathPattern,
fileName))
_stripPath(fileName, pathPattern)
return True

Expand All @@ -137,7 +137,7 @@ def _diff(fileName, baselineDir, verbose):
diff = '/usr/bin/diff'
cmd = [diff, _resolvePath(baselineDir, fileName), fileName]
if verbose:
print "diffing with {0}".format(cmd)
print("diffing with {0}".format(cmd))

# This will print any diffs to stdout which is a nice side-effect
return subprocess.call(cmd) == 0
Expand Down Expand Up @@ -181,7 +181,7 @@ def _runCommand(raw_command, stdout_redir, stderr_redir, env,
cmd = _splitCmd(raw_command)
fout, ferr = _getRedirects(stdout_redir, stderr_redir)
try:
print "cmd: %s" % (cmd, )
print("cmd: %s" % (cmd, ))
retcode = _convertRetCode(subprocess.call(cmd, shell=False, env=env,
stdout=(fout or sys.stdout),
stderr=(ferr or sys.stderr)))
Expand Down Expand Up @@ -217,13 +217,13 @@ def _runCommand(raw_command, stdout_redir, stderr_redir, env,
testDir = tempfile.mkdtemp()
os.chdir(testDir)
if args.verbose:
print "chdir: {0}".format(testDir)
print("chdir: {0}".format(testDir))

# Copy the contents of the testenv directory into our test run directory so
# the test has it's own copy that it can reference and possibly modify.
if args.testenv_dir and os.path.isdir(args.testenv_dir):
if args.verbose:
print "copying testenv dir: {0}".format(args.testenv_dir)
print("copying testenv dir: {0}".format(args.testenv_dir))
try:
_copyTree(args.testenv_dir, os.getcwd())
except Exception as e:
Expand Down Expand Up @@ -273,7 +273,7 @@ def _runCommand(raw_command, stdout_redir, stderr_redir, env,
if args.files_exist:
for f in args.files_exist:
if args.verbose:
print 'checking if {0} exists.'.format(f)
print('checking if {0} exists.'.format(f))
if not os.path.exists(f):
sys.stderr.write('Error: {0} does not exist '
'(FILES_EXIST).'.format(f))
Expand All @@ -282,7 +282,7 @@ def _runCommand(raw_command, stdout_redir, stderr_redir, env,
if args.files_dont_exist:
for f in args.files_dont_exist:
if args.verbose:
print 'checking if {0} does not exist.'.format(f)
print('checking if {0} does not exist.'.format(f))
if os.path.exists(f):
sys.stderr.write('Error: {0} does exist '
'(FILES_DONT_EXIST).'.format(f))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# limitations under the License.
#

from future.utils import iteritems

from pxr import UsdMaya

from pxr import Tf
Expand Down Expand Up @@ -47,7 +49,7 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
statsOutputLines = []
for profileScopeName, elapsedTime in cls._profileScopeMetrics.iteritems():
for profileScopeName, elapsedTime in iteritems(cls._profileScopeMetrics):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In python3, dict.iteritems is no longer available. Using python.future function iteritems()

Note: mayapy shipped with Maya already comes with future package installed

statsDict = {
'profile': profileScopeName,
'metric': 'time',
Expand Down Expand Up @@ -131,7 +133,7 @@ def _RunPlaybackTest(self):
profileScopeName = '%s Proxy Orbit Playback Time' % self._testName

with self._ProfileScope(profileScopeName):
for frame in xrange(int(self.animStartTime), int(self.animEndTime + 1.0)):
for frame in range(int(self.animStartTime), int(self.animEndTime + 1.0)):
cmds.currentTime(frame, edit=True)

playElapsedTime = self._profileScopeMetrics[profileScopeName]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# limitations under the License.
#

from future.utils import iteritems

from pxr import UsdMaya

from pxr import Tf
Expand Down Expand Up @@ -45,7 +47,7 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
statsOutputLines = []
for profileScopeName, elapsedTime in cls._profileScopeMetrics.iteritems():
for profileScopeName, elapsedTime in iteritems(cls._profileScopeMetrics):
statsDict = {
'profile': profileScopeName,
'metric': 'time',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# limitations under the License.
#

from future.utils import iteritems

from pxr import UsdMaya

from pxr import Gf
Expand Down Expand Up @@ -109,7 +111,7 @@ def setUpClass(cls):
@classmethod
def tearDownClass(cls):
statsOutputLines = []
for profileScopeName, elapsedTime in cls._profileScopeMetrics.iteritems():
for profileScopeName, elapsedTime in iteritems(cls._profileScopeMetrics):
statsDict = {
'profile': profileScopeName,
'metric': 'time',
Expand Down
4 changes: 2 additions & 2 deletions plugin/pxr/maya/lib/pxrUsdMayaGL/testenv/testPxrUsdMayaGL.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ def testSimpleSceneDrawsAndReloads(self):
# a memory issue would sometimes cause maya to crash when opening a new
# scene.

for _ in xrange(20):
for _ in range(20):
cmds.file(new=True, force=True)
for i in xrange(10):
for i in range(10):
usdFilePath = os.path.abspath('plane%d.usda' % (i%2))
assembly = cmds.assembly(name='plane', type='pxrUsdReferenceAssembly')
cmds.setAttr("%s.filePath" % assembly, usdFilePath, type='string')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def variantSets_Replace(nodeAttr, new):

try:
cmds.optionMenuGrp(omg, e=True, value=variantOverride)
except RuntimeError, e:
except (RuntimeError, e):
cmds.warning('Invalid choice %r for %r'%(variantOverride, variantSetName))

cmds.optionMenuGrp(omg, e=True, changeCommand=functools.partial(variantSets_changeCommmand, omg=omg, node=node, variantSetName=variantSetName))
Expand Down
2 changes: 1 addition & 1 deletion plugin/pxr/maya/lib/usdMaya/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def LoadReferenceAssemblies(parentNodes=None):
isInterruptable=True, minValue=0, maxValue=numRefAssemblies,
status='Loading USD reference assemblies...')

for i in xrange(numRefAssemblies):
for i in range(numRefAssemblies):
refAssembly = refAssemblies[i]

if mainProgressBar:
Expand Down
8 changes: 4 additions & 4 deletions plugin/pxr/maya/lib/usdMaya/testenv/testUsdExportAsClip.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def setUpClass(cls):

cmds.file(os.path.abspath('UsdExportAsClipTest.ma'), open=True,
force=True)
print os.path.abspath('UsdExportAsClipTest.ma')
print(os.path.abspath('UsdExportAsClipTest.ma'))

cmds.loadPlugin('pxrUsd', quiet=True)

Expand All @@ -55,9 +55,9 @@ def getValues(stage):
cube = stage.GetPrimAtPath(primPath)
self.assertTrue(cube)
attr = cube.GetAttribute(attrName)
return [attr.Get(time=tc) for tc in xrange(*frameRange)]
return [attr.Get(time=tc) for tc in range(*frameRange)]

for frame, x,y in zip(xrange(*frameRange),
for frame, x,y in zip(range(*frameRange),
getValues(canonicalStage),
getValues(testStage)):
msg = ('different values found on frame: {frame}\n'
Expand Down Expand Up @@ -116,7 +116,7 @@ def testExportAsClip(self):
canonicalUsdFile = os.path.abspath('canonical.usda')
cmds.usdExport(mergeTransformAndShape=True, file=canonicalUsdFile, frameRange=(1, 20))

print 'comparing: \nnormal: {}\nstitched: {}'.format(canonicalUsdFile, stitchedPath)
print('comparing: \nnormal: {}\nstitched: {}'.format(canonicalUsdFile, stitchedPath))
canonicalStage = Usd.Stage.Open(canonicalUsdFile)
clipsStage = Usd.Stage.Open(stitchedPath)
# visible
Expand Down
4 changes: 2 additions & 2 deletions plugin/pxr/maya/lib/usdMaya/testenv/testUsdExportColorSets.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,15 @@ def _GetExpectedColorSetIndices(self, colorSetName):
else:
# Every component has an assignment.
if isFaceColor:
assignmentIndices = [i for i in xrange(self._colorSetSourceMesh.numPolygons())]
assignmentIndices = [i for i in range(self._colorSetSourceMesh.numPolygons())]
elif isVertexColor:
# The assignments for vertex color are a little different
# due to MItMeshFaceVertex visiting components in faceVertex
# order, in which case we actually visit vertices somewhat
# out of order.
assignmentIndices = [0, 1, 3, 2, 5, 4, 7, 6]
elif isFaceVertexColor:
assignmentIndices = [i for i in xrange(self._colorSetSourceMesh.numFaceVertices())]
assignmentIndices = [i for i in range(self._colorSetSourceMesh.numFaceVertices())]

return assignmentIndices

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# limitations under the License.
#

from future.utils import iteritems

import os
import unittest

Expand Down Expand Up @@ -44,9 +46,9 @@ def assertRotateSamples(self, usdFile, expected):
xform = stage.GetPrimAtPath('/pCube1')
rot = xform.GetProperty('xformOp:rotateXYZ')

for time, expectedVec in expected.iteritems():
for time, expectedVec in iteritems(expected):
actualVec = rot.Get(time)
for i in xrange(3):
for i in range(3):
msg = "sample at time {}, index {} unequal".format(time, i)
self.assertAlmostEqual(actualVec[i], expectedVec[i], 4, msg)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# limitations under the License.
#

from future.utils import iteritems

import os
import unittest

Expand Down Expand Up @@ -91,8 +93,8 @@ def doExportImportOneMethod(self, method, constraint=True, place3d=True,
if filter:
options['filterTypes'] = ','.join(filter)
kwargs['options'] = ';'.join('{}={}'.format(key, val)
for key, val in options.iteritems())
print "running: {}(*{!r}, **{!r})".format(exportMethod.__name__, args, kwargs)
for key, val in iteritems(options))
print("running: {}(*{!r}, **{!r})".format(exportMethod.__name__, args, kwargs))
exportMethod(*args, **kwargs)
return Usd.Stage.Open(usdFile)

Expand Down
Loading