Skip to content

Commit 756423f

Browse files
authored
8162 fixes (#7)
Fixed the following problems: - Strings returned by Maya commands in Py2 (Maya 2020) were unicode and causing problems when used as environment variables in subprocess. - In some cases publishes would be registered with a file:// url, added a _get_local_path method to handle all cases (local file, file:// url). - Fixed a problem with UE5 "interpreting" \ in Python script path given in the command line.
1 parent 244003c commit 756423f

File tree

8 files changed

+146
-58
lines changed

8 files changed

+146
-58
lines changed

.flake8

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Copyright 2018 GPL Solutions, LLC. All rights reserved.
2+
#
3+
# Use of this software is subject to the terms of the GPL Solutions license
4+
# agreement provided at the time of installation or download, or which otherwise
5+
# accompanies this software in either electronic or hard copy form.
6+
#
7+
8+
# Flake 8 PEP and lint configuration - https://gitlab.com/pycqa/flake8
9+
#
10+
# This defines the official lint and PEP8 rules for this repository
11+
#
12+
# You can run this locally by doing pep install flake8 and then
13+
# >flake8 .
14+
15+
[flake8]
16+
17+
# Things we don't want to lint
18+
exclude =
19+
.tox,
20+
.git,
21+
.flake8,
22+
.gitignore,
23+
.travis.yml,
24+
.cache,
25+
.eggs,
26+
*.rst,
27+
*.yml,
28+
*.pyc,
29+
*.pyo,
30+
*.egg-info,
31+
__pycache__,
32+
# Those are our third parties, do not lint them
33+
vendors,
34+
# Skip __init__.py files, to not have a lot of 'xxx' imported but unused
35+
python/__init__.py,
36+
python/*/__init__.py,
37+
# Skip the auto-generated ui file.
38+
python/*/ui,
39+
venv,
40+
winenv,
41+
osx_env,
42+
win_env,
43+
44+
# Ignore some errors
45+
#
46+
# E402 module level import not at top of file
47+
# E501 line too long (112 > 79 characters)
48+
# N802 Variables should be lower case. (clashes with Qt naming conventions)
49+
# N806 Variables should be lower case. (clashes with Qt naming conventions)
50+
# W503 line break before binary operator (it breaks before, not after)
51+
52+
ignore = E501, E402, N802, N806, W503
53+

framework.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This file is based on templates provided and copyrighted by Autodesk, Inc.
2-
# This file has been modified by Epic Games, Inc. and is subject to the license
2+
# This file has been modified by Epic Games, Inc. and is subject to the license
33
# file included in this repository.
44

55
"""
@@ -22,7 +22,7 @@ class UnrealQtFramework(sgtk.platform.Framework):
2222

2323
##########################################################################################
2424
# init and destroy
25-
25+
2626
def init_framework(self):
2727
"""
2828
This framework ships with additional Python packages and tweak the Python
@@ -35,7 +35,7 @@ def init_framework(self):
3535

3636
# Check if PySide is already available, do nothing if it is the case
3737
try:
38-
from sgtk.platform.qt import QtCore
38+
from sgtk.platform.qt import QtCore # noqa
3939
self.log_debug("Qt is already available, not activating any custom package.")
4040
return
4141
except ImportError as e:

hooks/core/bootstrap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def populate_bundle_cache_entry(self, destination, descriptor, **kwargs):
142142
for asset in response_d["assets"]:
143143
name = asset["name"]
144144
m = re.match(
145-
"%s-py\d.\d-%s.zip" % (version, pname),
145+
r"%s-py\d.\d-%s.zip" % (version, pname),
146146
name
147147
)
148148
if m:

hooks/tk-multi-publish2/tk-maya/basic/collector.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ class MayaSessionCollectorWithSecondaries(HookBaseClass):
2121
collector: "{self}/collector.py:{engine}/tk-multi-publish2/basic/collector.py:{config}/tk-multi-publish2/tk-maya/basic/collector.py"
2222
"""
2323

24-
2524
def collect_current_maya_session(self, settings, parent_item):
2625
"""
2726
Creates an item that represents the current maya session.

hooks/tk-multi-publish2/tk-maya/basic/publish_fbx.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ def validate(self, settings, item):
187187
:raises ValueError: For problems which can't be solved in the current session.
188188
"""
189189

190-
publisher = self.parent
191190
path = _session_path()
192191

193192
# ---- ensure the session has been saved
@@ -282,7 +281,6 @@ def validate(self, settings, item):
282281
# Set the publish_path to be explicit.
283282
item.local_properties["publish_path"] = publish_path
284283

285-
286284
# Set the session path on the item for use by the base plugin validation
287285
# step.
288286
# NOTE: this path could change prior to the publish phase.
@@ -312,8 +310,6 @@ def publish(self, settings, item):
312310
:param item: Item to process
313311
"""
314312

315-
publisher = self.parent
316-
317313
# Get the path in a normalized state. no trailing separator, separators
318314
# are appropriate for current os, no double separators, etc.
319315
path = sgtk.util.ShotgunPath.normalize(_session_path())
@@ -376,6 +372,7 @@ def _session_path():
376372

377373
return path
378374

375+
379376
def _save_session(path):
380377
"""
381378
Save the current session to the supplied path.

hooks/tk-multi-publish2/tk-maya/basic/publish_turntable.py

Lines changed: 60 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
import sgtk
66
from tank_vendor import six
77
from sgtk.platform.qt import QtGui, QtCore
8-
from sgtk.platform import SoftwareVersion
98

109
import maya.cmds as cmds
10+
import maya.mel as mel
1111

1212
import copy
1313
import datetime
@@ -19,6 +19,7 @@
1919
import subprocess
2020
import sys
2121
import tempfile
22+
from six.moves.urllib import parse
2223

2324
HookBaseClass = sgtk.get_hook_baseclass()
2425

@@ -131,7 +132,7 @@ def _open_current_path(self):
131132
Open the current file in an external application.
132133
"""
133134
current_path = self.get_path()
134-
engine = sgtk.platform.current_engine()
135+
135136
# Would be awesome to use launch app, but launch_from_path does not work
136137
# in SotfwareEntity mode.
137138
# if engine and "tk-multi-launchapp" in engine.apps:
@@ -362,9 +363,6 @@ def description(self):
362363
Verbose, multi-line description of what the plugin does. This can
363364
contain simple html for formatting.
364365
"""
365-
366-
loader_url = "https://support.shotgunsoftware.com/hc/en-us/articles/219033078"
367-
368366
return """
369367
<p>This plugin renders a turntable of the asset for the current session
370368
in Unreal Engine. The asset will be exported to FBX and imported into
@@ -373,6 +371,7 @@ def description(self):
373371
the turntable render will be published to Shotgun and submitted for review
374372
as a Version.</p>
375373
"""
374+
376375
@property
377376
def icon(self):
378377
"""
@@ -515,7 +514,6 @@ def create_settings_widget(self, parent):
515514
settings_frame.unreal_turntable_asset_label = QtGui.QLabel("Unreal Turntable Assets Path:")
516515
settings_frame.unreal_turntable_asset_widget = QtGui.QLineEdit("")
517516

518-
519517
# Create the layout to use within the QFrame
520518
settings_layout = QtGui.QVBoxLayout()
521519
settings_layout.addWidget(settings_frame.description_label)
@@ -549,9 +547,9 @@ def get_ui_settings(self, widget):
549547
"Turntable Map Path": six.ensure_str(widget.unreal_turntable_map_widget.text()),
550548
"Sequence Path": six.ensure_str(widget.unreal_sequence_widget.text()),
551549
"Turntable Assets Path": six.ensure_str(widget.unreal_turntable_asset_widget.text()),
552-
#"HDR Path": widget.hdr_image_template_widget.get_path(),
553-
#"Start Frame": widget.start_frame_spin_box.value(),
554-
#"End Frame": widget.end_frame_spin_box.value(),
550+
# "HDR Path": widget.hdr_image_template_widget.get_path(),
551+
# "Start Frame": widget.start_frame_spin_box.value(),
552+
# "End Frame": widget.end_frame_spin_box.value(),
555553
}
556554
return settings
557555

@@ -898,7 +896,7 @@ def get_unreal_project_property(self, settings, item):
898896
raise RuntimeError(
899897
"Unable to build an Unreal project path from %s with Unreal version %s" % (
900898
unreal_project_path_template,
901-
unreal_engine_version,
899+
item.properties["unreal_engine_version"],
902900
)
903901
)
904902
if not os.path.isfile(unreal_project_path):
@@ -907,6 +905,39 @@ def get_unreal_project_property(self, settings, item):
907905
)
908906
item.properties["unreal_project_path"] = unreal_project_path
909907

908+
def _get_local_path(self, published_file):
909+
"""
910+
Returns the local path for the given published file.
911+
912+
:returns: The local path to the published file.
913+
:raises ValueError: If no local path can be retrieved.
914+
"""
915+
if "local_path" in published_file["path"]:
916+
return published_file["path"]["local_path"]
917+
918+
if "url" in published_file["path"]:
919+
url = published_file["path"]["url"]
920+
parsed = parse.urlparse(url)
921+
if parsed.scheme == "file":
922+
# Copied from
923+
# https://github.com/shotgunsoftware/tk-core/blob/2fc8287a19f8f002e23101836bafba0ec0de9dc9/python/tank/util/shotgun/publish_resolve.py#L311
924+
if parsed.netloc:
925+
# UNC path
926+
path = parse.unquote(
927+
"//%s%s" % (parsed.netloc, parsed.path)
928+
)
929+
else:
930+
path = parse.unquote(parsed.path)
931+
# Deal with bad paths being set
932+
# file:///C:/Users/me/default/data/publishes/Trooper_Full_NoKnife_2.fbx
933+
# Leading to /C:/Users/me/default/data/publishes/Trooper_Full_NoKnife_2.fbx
934+
# as path.
935+
if re.match("^/[A-Za-z]:/", path):
936+
path = path[1:]
937+
return path
938+
939+
raise ValueError("Unable to get a local path from %s" % published_file)
940+
910941
def publish(self, settings, item):
911942
"""
912943
Executes the publish logic for the given item and settings.
@@ -956,7 +987,7 @@ def publish(self, settings, item):
956987
# Use the Fbx which was published by another item or save one now.
957988
published_fbx = item.parent.properties.get("sg_fbx_publish_data")
958989
if published_fbx:
959-
fbx_published_path = published_fbx["path"]["local_path"]
990+
fbx_published_path = self._get_local_path(published_fbx)
960991
# Check the path: Unreal doesn't like non-word characters in filenames
961992
self.logger.info("Using published FBX file %s" % fbx_published_path)
962993
if re.search(exp, os.path.splitext(fbx_published_path)[0]):
@@ -997,7 +1028,7 @@ def publish(self, settings, item):
9971028
self.logger.debug("Copying %s to %s" % (unreal_project_path, temp_project_path))
9981029
shutil.copytree(project_path, temp_project_dir)
9991030

1000-
current_folder = os.path.dirname( __file__ )
1031+
current_folder = os.path.dirname(__file__)
10011032
script_path = os.path.abspath(
10021033
os.path.join(
10031034
current_folder,
@@ -1024,6 +1055,8 @@ def publish(self, settings, item):
10241055
)
10251056
importer_destination = os.path.join(self.temp_folder, "unreal_importer.py")
10261057
shutil.copy(importer_path, importer_destination)
1058+
# UE5 does some weird things with \ so let's replace them with /
1059+
script_path = script_path.replace("\\", "/")
10271060

10281061
if " " in temp_project_path:
10291062
temp_project_path = '"{}"'.format(temp_project_path)
@@ -1033,15 +1066,17 @@ def publish(self, settings, item):
10331066
# on the command line.
10341067
run_env = copy.copy(os.environ)
10351068
# Environment variables for turntable script
1069+
# Make sure they are strings and not unicode with py2, otherwise
1070+
# "environment can only contain strings" erors will happen.
10361071
extra_env = {
10371072
# The FBX to import into Unreal
1038-
"UNREAL_SG_FBX_OUTPUT_PATH": fbx_output_path,
1073+
"UNREAL_SG_FBX_OUTPUT_PATH": six.ensure_str(fbx_output_path),
10391074
# The Unreal content browser folder where the asset will be imported into
1040-
"UNREAL_SG_ASSETS_PATH": turntable_assets_path,
1075+
"UNREAL_SG_ASSETS_PATH": six.ensure_str(turntable_assets_path),
10411076
# The Unreal turntable map to duplicate where the asset will be instantiated into
1042-
"UNREAL_SG_MAP_PATH": turntable_map_path,
1043-
"UNREAL_SG_SEQUENCE_PATH": sequence_path,
1044-
"UNREAL_SG_MOVIE_OUTPUT_PATH": publish_path,
1077+
"UNREAL_SG_MAP_PATH": six.ensure_str(turntable_map_path),
1078+
"UNREAL_SG_SEQUENCE_PATH": six.ensure_str(sequence_path),
1079+
"UNREAL_SG_MOVIE_OUTPUT_PATH": six.ensure_str(publish_path),
10451080
}
10461081
self.logger.info("Adding %s to the environment" % extra_env)
10471082
run_env.update(extra_env)
@@ -1079,7 +1114,7 @@ def publish(self, settings, item):
10791114
)
10801115
# Workaround for Level Sequencer only rendering avi on Windows and Movie Queue rendering
10811116
# mov on all platforms
1082-
publish_path = re.sub("\.avi$", ".mov", publish_path)
1117+
publish_path = re.sub(r"\.avi$", ".mov", publish_path)
10831118
item.local_properties["publish_path"] = publish_path
10841119
self._unreal_render_movie_with_movie_render_queue(
10851120
unreal_exec_path,
@@ -1132,8 +1167,6 @@ def publish(self, settings, item):
11321167
item.local_properties["sg_publish_data"] = item.properties.sg_publish_data
11331168

11341169
# Create a Version entry linked with the new publish
1135-
publish_name = item.properties.get("publish_name")
1136-
11371170
# Populate the version data to send to SG
11381171
self.logger.info("Creating Version...")
11391172
version_data = {
@@ -1153,7 +1186,8 @@ def publish(self, settings, item):
11531186
"label": "Version Data",
11541187
"tooltip": "Show the complete Version data dictionary",
11551188
"text": "<pre>%s</pre>" % (
1156-
pprint.pformat(version_data),)
1189+
pprint.pformat(version_data),
1190+
)
11571191
}
11581192
}
11591193
)
@@ -1168,7 +1202,7 @@ def publish(self, settings, item):
11681202
# Ensure the path is utf-8 encoded to avoid issues with
11691203
# the shotgun api
11701204
upload_path = six.ensure_text(
1171-
item.properties.sg_publish_data["path"]["local_path"]
1205+
self._get_local_path(item.properties.sg_publish_data)
11721206
)
11731207

11741208
# Upload the file to SG
@@ -1236,7 +1270,7 @@ def _maya_export_fbx(self, fbx_output_path):
12361270
# import maya.mel as mel
12371271
# mel.eval('FBXExport -f "fbx_output_path"')
12381272
cmds.FBXExport('-f', fbx_output_path)
1239-
except:
1273+
except Exception:
12401274
self.logger.error("Could not export scene to FBX")
12411275
return False
12421276

@@ -1322,7 +1356,7 @@ def _unreal_render_movie_with_sequencer(
13221356
# Must delete it first, otherwise the Sequencer will add a number in the filename
13231357
try:
13241358
os.remove(output_path)
1325-
except OSError as e:
1359+
except OSError:
13261360
self.logger.debug("Couldn't delete {}. The Sequencer won't be able to output the movie to that file.".format(output_path))
13271361
return False, None
13281362

@@ -1382,7 +1416,6 @@ def _unreal_render_movie_with_movie_render_queue(
13821416
string representing the path of the generated movie file
13831417
"""
13841418
output_folder, output_file = os.path.split(output_path)
1385-
movie_name = os.path.splitext(output_file)[0]
13861419

13871420
cmdline_args = self._get_unreal_base_command(
13881421
unreal_exec_path,
@@ -1434,7 +1467,7 @@ def _unreal_render_movie_with_movie_render_queue(
14341467
"-MoviePipelineConfig=\"%s\"" % manifest_path,
14351468
])
14361469
self.logger.info("Running %s" % cmdline_args)
1437-
ret = subprocess.call(cmdline_args)
1470+
subprocess.call(cmdline_args)
14381471
return os.path.isfile(output_path), output_path
14391472

14401473
def get_unreal_versions(self):
@@ -1506,7 +1539,6 @@ def evaluate_unreal_project_path(self, unreal_project_path_template, unreal_engi
15061539
short_version = ".".join(unreal_engine_version.split(".")[:2])
15071540
# Evaluate the "template"
15081541
engine = sgtk.platform.current_engine()
1509-
engine_path = os.path.join(engine.disk_location, "hooks")
15101542
hooks_folder = engine.sgtk.pipeline_configuration.get_hooks_location()
15111543
fw = self.load_framework("tk-framework-unrealqt_v1.x.x")
15121544
return os.path.normpath(
@@ -1531,6 +1563,7 @@ def _copy_work_to_publish(self, settings, item):
15311563
"""
15321564
pass
15331565

1566+
15341567
def _session_path():
15351568
"""
15361569
Return the path to the current session

0 commit comments

Comments
 (0)