From 41c2416cb5cb7868971e2d21b34b13a15864a1e6 Mon Sep 17 00:00:00 2001 From: Philip Scadding Date: Thu, 6 Feb 2020 11:53:40 +0000 Subject: [PATCH] Black (#18) * Ran python-modernize * Ran the pre commit hook, black made changes. * Added black and Hound badges to the README * Added Azure config. --- .pre-commit-config.yaml | 39 ++++ LICENSE | 14 +- README.md | 3 + app.py | 102 +++++----- azure-pipelines.yml | 32 +++ hooks/get_version_number.py | 6 +- hooks/tk-houdini_scene_operations.py | 16 +- hooks/tk-mari_scene_operations.py | 50 +++-- hooks/tk-maya_scene_operations.py | 18 +- hooks/tk-nuke_scene_operations.py | 41 ++-- python/__init__.py | 10 +- python/tk_multi_breakdown/__init__.py | 3 +- python/tk_multi_breakdown/breakdown.py | 33 ++-- .../tk_multi_breakdown/breakdown_list_item.py | 15 +- python/tk_multi_breakdown/dialog.py | 15 +- python/tk_multi_breakdown/scene_browser.py | 86 +++++--- resources/build_resources.sh | 21 +- .../config/bundles/test_engine/engine.py | 19 +- .../config/bundles/test_engine/info.yml | 14 +- .../config/core/hooks/pick_environment.py | 14 +- .../core/schema/project/assets/asset_type.yml | 11 +- .../project/assets/asset_type/asset.yml | 12 +- .../project/assets/asset_type/asset/step.yml | 13 +- .../core/schema/project/scenes/step.yml | 13 +- .../core/schema/project/scenes/step/scene.yml | 13 +- .../schema/project/sequences/sequence.yml | 13 +- .../project/sequences/sequence/shot.yml | 13 +- .../project/sequences/sequence/shot/step.yml | 13 +- tests/fixtures/config/core/templates.yml | 34 ++-- tests/fixtures/config/env/test.yml | 4 +- .../fixtures/config/hooks/scene_operations.py | 19 +- tests/test_breakdown.py | 186 ++++++++++-------- 32 files changed, 513 insertions(+), 382 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 azure-pipelines.yml mode change 100755 => 100644 python/__init__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4479d55 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,39 @@ +# Copyright (c) 2019 Shotgun Software Inc. +# +# CONFIDENTIAL AND PROPRIETARY +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# Source Code License included in this distribution package. See LICENSE. +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# not expressly granted therein are reserved by Shotgun Software Inc. + +# Styles the code properly +# Exclude the UI files, as they are auto-generated. +exclude: "ui\/.*py$" +# List of super useful formatters. +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.4.0 + hooks: + # Ensures the code is syntaxically correct + - id: check-ast + language_version: python3 + # Ensures a file name will resolve on all platform + - id: check-case-conflict + # Checks files with the execute bit set have shebangs + - id: check-executables-have-shebangs + # Ensure there's no incomplete merges + - id: check-merge-conflict + # Adds an empty line if missing at the end of a file. + - id: end-of-file-fixer + # Makes sure requirements.txt is properly formatted + - id: requirements-txt-fixer + # Removes trailing whitespaces. + - id: trailing-whitespace + # Leave black at the bottom so all touchups are done before it is run. + - repo: https://github.com/ambv/black + rev: 19.10b0 + hooks: + - id: black + language_version: python3 diff --git a/LICENSE b/LICENSE index 5172753..602ba31 100644 --- a/LICENSE +++ b/LICENSE @@ -4,9 +4,9 @@ Version: 7/07/2013 Shotgun Software Inc. ("Company") provides the Shotgun Pipeline Toolkit, software, including source code, in this package or repository folder (the -"Shotgun Toolkit Code") subject to your acceptance of and compliance with +"Shotgun Toolkit Code") subject to your acceptance of and compliance with the following terms and conditions (the "License Terms"). By accessing, -downloading, copying, using or modifying any of the Shotgun Toolkit Code, +downloading, copying, using or modifying any of the Shotgun Toolkit Code, you agree to these License Terms. Eligibility @@ -33,7 +33,7 @@ to the Shotgun Toolkit Code or any of Company's other software or intellectual property rights. You agree not to take any action with respect to the Shotgun Toolkit Code that is not expressly authorized above. -You must keep intact (and, in the case of copies, reproduce) all copyright +You must keep intact (and, in the case of copies, reproduce) all copyright and other proprietary notices, including all references to and copies of these License Terms, as originally included on, in, or with the Shotgun Toolkit Code. You must ensure that all derivative works you make of the Shotgun @@ -64,7 +64,7 @@ Company). Liability You agree to be solely responsible for your use and modifications of the -Shotgun Toolkit Code, and for any harm or liability arising out of such use +Shotgun Toolkit Code, and for any harm or liability arising out of such use or modifications, including but not limited to any liability for infringement of third-party intellectual property rights. @@ -78,7 +78,7 @@ advised of the possibility of such damages; and (b) in any event, Company's aggregate liability under these License Terms or in connection with the Shotgun Toolkit Code, regardless of the form of action and under any theory (whether in contract, tort, statutory, or otherwise), will not exceed the -greater of $50 or the amount (if any) that you actually paid for access to +greater of $50 or the amount (if any) that you actually paid for access to the Shotgun Toolkit Code. Ownership @@ -115,7 +115,7 @@ Company grants you a non-exclusive right to continue to modify, make derivative works of, reproduce, and use the Contribution for your non-commercial or internal business purposes, and to further Company's development of Company Programs. This grant does not: (a) limit Company's -rights, (b) grant you any rights with respect to the Company Programs; nor +rights, (b) grant you any rights with respect to the Company Programs; nor (c) permit you to distribute, operate for the benefit of third parties (for example, on a hosted basis), or otherwise commercially exploit the Contribution. @@ -143,4 +143,4 @@ employees, and agents against any and all claims, actions or damages account of a breach or alleged breach of the foregoing warranty. You make no other express or implied warranty (including without limitation any warranty of merchantability or fitness for a particular purpose) regarding the -Contribution. \ No newline at end of file +Contribution. diff --git a/README.md b/README.md index a536620..5b73c28 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) +[![Linting](https://img.shields.io/badge/PEP8%20by-Hound%20CI-a873d1.svg)](https://houndci.com) + ## Documentation This repository is a part of the Shotgun Pipeline Toolkit. diff --git a/app.py b/app.py index 6e7195a..6e590ba 100644 --- a/app.py +++ b/app.py @@ -17,16 +17,17 @@ import sys import os -class MultiBreakdown(Application): +class MultiBreakdown(Application): def init_app(self): """ Called as the application is being initialized """ tk_multi_breakdown = self.import_module("tk_multi_breakdown") - cb = lambda : tk_multi_breakdown.show_dialog(self) - self.engine.register_command("Scene Breakdown...", cb, { "short_name": "breakdown" }) - + cb = lambda: tk_multi_breakdown.show_dialog(self) + self.engine.register_command( + "Scene Breakdown...", cb, {"short_name": "breakdown"} + ) @property def context_change_allowed(self): @@ -35,44 +36,42 @@ def context_change_allowed(self): """ return True - def show_breakdown_dialog(self): """ Show the breakdown UI as a dialog. - + This is a helper method to make it easy to programatically access the breakdown UI. External code could then do something like: - + >>> import sgtk >>> e = sgtk.platform.current_engine() >>> e.apps["tk-multi-breakdown"].show_breakdown_dialog() - """ + """ tk_multi_breakdown = self.import_module("tk_multi_breakdown") - fn = lambda : tk_multi_breakdown.show_dialog(self) + fn = lambda: tk_multi_breakdown.show_dialog(self) self.engine.execute_in_main_thread(fn) - - + def analyze_scene(self): """ Runs the scene analysis and returns a list of scene items. - - Each item is represented by a dictionary with a number of keys to + + Each item is represented by a dictionary with a number of keys to describe the item. This method uses the same logic that the main UI uses to determine the list of files. - + Only files whose path corresponds to a template in the toolkit templates file will be detected. Files do not need to exist as publishes in Shotgun however if they do, this method will return basic Shotgun publish metadata for them. - - The two keys node_name and node_type are used to return a DCC-centric + + The two keys node_name and node_type are used to return a DCC-centric "address" or representation which makes it possible to identify the path - within the DCC. In for example Maya and Nuke, this will return the - node name and type. The logic for this is implemented in the hooks and + within the DCC. In for example Maya and Nuke, this will return the + node name and type. The logic for this is implemented in the hooks and will vary between DCCs. - + Here is an example of what the return data typically looks like: - + {'fields': {'Sequence': 'aaa', 'Shot': 'aaa_00010', 'Step': 'Comp', @@ -83,10 +82,10 @@ def analyze_scene(self): 'version': 1, 'width': 2048}, 'template': , - + 'node_name': 'Read2', 'node_type': 'Read', - + 'sg_data': {'code': 'aaa_00010_test_output_v001.%04d.dpx', 'entity': {'id': 1660, 'name': 'aaa_00010', 'type': 'Shot'}, 'id': 1424, @@ -99,52 +98,52 @@ def analyze_scene(self): 'project': {'id': 234, 'name': 'Climp', 'type': 'Project'}, 'version_number': 1}, } - + This method will attempt to connect to shotgun, but the number of calls made are constant and independent of the scene complexity. - + Below is an example showing how to retrieve the scene breakdown and update all items that are not using the latest version. - + # find the breakdown app instance import sgtk engine = sgtk.platform.current_engine() breakdown_app = engine.apps["tk-multi-breakdown"] - + # get list of breakdown items items = breakdown_app.analyze_scene() - + # now loop over all items for item in items: - + # get the latest version on disk latest_version = breakdown_app.compute_highest_version(item["template"], item["fields"]) - + # if our current version is out of date, update it! current_version = item["fields"]["version"] if latest_version > current_version: - + # make a fields dictionary representing the latest version latest_fields = copy.copy(item["fields"]) latest_fields["version"] = latest_version - + # request that the breakdown updates to the latest version breakdown_app.update_item(item["node_type"], item["node_name"], item["template"], latest_fields) - - + + :returns: List of dictionaries, see above for example. """ tk_multi_breakdown = self.import_module("tk_multi_breakdown") - + # first, scan the scene and get a list of items items = tk_multi_breakdown.get_breakdown_items() - + # if shotgun data is returned for an item, trim this down # to return a more basic listing than the one returned # from get_breakdown_items: for item in items: - + if item["sg_data"]: new_sg_data = {} new_sg_data["id"] = item["sg_data"]["id"] @@ -155,36 +154,38 @@ def analyze_scene(self): new_sg_data["entity"] = item["sg_data"]["entity"] new_sg_data["project"] = item["sg_data"]["project"] new_sg_data["version_number"] = item["sg_data"]["version_number"] - new_sg_data["published_file_type"] = item["sg_data"]["published_file_type"] + new_sg_data["published_file_type"] = item["sg_data"][ + "published_file_type" + ] item["sg_data"] = new_sg_data - - return items + return items def compute_highest_version(self, template, fields): """ Given a template and some fields, return the highest version number found on disk. The template key containing the version number is assumed to be named {version}. - + This will perform a scan on disk to determine the highest version. - + For a usage example, see the analyze_scene() method. - + :param template: Template object to calculate for :param fields: A complete set of fields for the template :returns: The highest version number found """ - return self.execute_hook("hook_get_version_number", template=template, curr_fields=fields) - - + return self.execute_hook( + "hook_get_version_number", template=template, curr_fields=fields + ) + def update_item(self, node_type, node_name, template, fields): """ Request that the breakdown updates an given node with a new version. - This is similar to running the update in the breakdown UI. The actual + This is similar to running the update in the breakdown UI. The actual update call will be dispatched to a hook which handles the DCC specific logic. - + For a usage example, see the analyze_scene() method. - + :param node_type: Node type of the item to update, as returned by analyze_scene() :param node_name: Node name of the item to update, as returned by analyze_scene() :param template: Template object representing the path pattern to update @@ -195,9 +196,6 @@ def update_item(self, node_type, node_name, template, fields): item["node"] = node_name item["type"] = node_type item["path"] = template.apply_fields(fields) - + # call out to hook return self.execute_hook_method("hook_scene_operations", "update", items=[item]) - - - diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..ba92a55 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,32 @@ +# Imports the shared Azure CI tools from the master branch of shotgunsoftware/tk-ci-tools +resources: + repositories: + - repository: templates + type: github + name: shotgunsoftware/tk-ci-tools + ref: refs/heads/master + endpoint: shotgunsoftware + +# We want builds to trigger for 3 reasons: +# - The master branch sees new commits +# - Each PR should get rebuilt when commits are added to it. +# - When we tag something +trigger: + branches: + include: + - master + tags: + include: + - v* +pr: + branches: + include: + - "*" + +# This pulls in a variable group from Azure. Variables can be encrypted or not. +variables: +- group: deploy-secrets + +# Launch into the build pipeline. +jobs: +- template: build-pipeline.yml@templates diff --git a/hooks/get_version_number.py b/hooks/get_version_number.py index f961d06..e3cff01 100644 --- a/hooks/get_version_number.py +++ b/hooks/get_version_number.py @@ -16,12 +16,14 @@ # the template key we use to find the version number VERSION_KEY = "version" + class GetVersionNumber(HookBaseClass): """ Hook called to scan the disk and determine the highest version. Given a template and some fields, return the highest version number found on disk. The template key containing the version number is assumed to be named {version}. """ + def execute(self, template, curr_fields, **kwargs): """ Main hook entry point. @@ -53,7 +55,9 @@ def execute(self, template, curr_fields, **kwargs): skip_keys = [k for k in abstract_keys] + [VERSION_KEY, "eye"] # then find all files, skipping these keys - all_versions = self.sgtk.paths_from_template(template, curr_fields, skip_keys=skip_keys) + all_versions = self.sgtk.paths_from_template( + template, curr_fields, skip_keys=skip_keys + ) # if we didn't find anything then something has gone wrong with our # logic as we should have at least one file so error out: diff --git a/hooks/tk-houdini_scene_operations.py b/hooks/tk-houdini_scene_operations.py index 759b82a..3bfb1ae 100644 --- a/hooks/tk-houdini_scene_operations.py +++ b/hooks/tk-houdini_scene_operations.py @@ -14,6 +14,7 @@ import hou + class BreakdownSceneOperations(Hook): """ Breakdown operations for Houdini. @@ -45,8 +46,7 @@ def scan_scene(self): items = [] # get a list of all regular lembic nodes in the file - alembic_nodes = hou.nodeType(hou.sopNodeTypeCategory(), - "alembic").instances() + alembic_nodes = hou.nodeType(hou.sopNodeTypeCategory(), "alembic").instances() # return an item for each alembic node found. the breakdown app will check # the paths of each looking for a template match and a newer version. @@ -55,11 +55,9 @@ def scan_scene(self): file_parm = alembic_node.parm("fileName") file_path = os.path.normpath(file_parm.eval()) - items.append({ - "node": alembic_node.path(), - "type": "alembic", - "path": file_path, - }) + items.append( + {"node": alembic_node.path(), "type": "alembic", "path": file_path} + ) return items @@ -92,6 +90,6 @@ def update(self, items): alembic_node = hou.node(node_path) engine.log_debug( - "Updating alembic node '%s' to: %s" % (node_path, file_path)) + "Updating alembic node '%s' to: %s" % (node_path, file_path) + ) alembic_node.parm("fileName").set(file_path) - diff --git a/hooks/tk-mari_scene_operations.py b/hooks/tk-mari_scene_operations.py index 6b4bfd8..709805b 100644 --- a/hooks/tk-mari_scene_operations.py +++ b/hooks/tk-mari_scene_operations.py @@ -13,6 +13,7 @@ import mari + class MariSceneOperations(Hook): """ Breakdown operations for Mari. @@ -47,7 +48,7 @@ def scan_scene(self): # find all geo in the current project using the engine utility method: mari_engine = self.parent.engine all_geo = mari_engine.list_geometry() - + # now, for all geo, find all versions: found_versions = [] for geo in [g.get("geo") for g in all_geo]: @@ -57,12 +58,16 @@ def scan_scene(self): # now find the publish path for the current version: current_version = geo.currentVersion() - for geo_version, path in [(v["geo_version"], v.get("path")) for v in all_geo_versions]: + for geo_version, path in [ + (v["geo_version"], v.get("path")) for v in all_geo_versions + ]: if geo_version == current_version: # found the current version :) - found_versions.append({"node": geo.name(), "type": "geo", "path": path}) + found_versions.append( + {"node": geo.name(), "type": "geo", "path": path} + ) break - + return found_versions def update(self, items): @@ -88,7 +93,7 @@ def update(self, items): def _update_geometry_items(self, items): """ Update specified geo items in the current project - + :param items: List of geometry items to update """ mari_engine = self.parent.engine @@ -102,38 +107,47 @@ def _update_geometry_items(self, items): all_paths = set([item["path"] for item in items]) try: fields = ["id", "path", "version_number"] - found_publishes = sgtk.util.find_publish(self.parent.sgtk, all_paths, fields=fields) - except TankError, e: + found_publishes = sgtk.util.find_publish( + self.parent.sgtk, all_paths, fields=fields + ) + except TankError as e: raise TankError("Failed to query publishes from Shotgun: %s" % e) - + # now we have all the info we need to update geometry: for item in items: publish_path = item["path"] geo_name = item["node"] - - # find the publish details: + + # find the publish details: sg_publish_data = found_publishes.get(publish_path) if not sg_publish_data: - raise TankError("Failed to find Shotgun publish record for '%s'" % publish_path) - + raise TankError( + "Failed to find Shotgun publish record for '%s'" % publish_path + ) + # find geo in project: geo = mari.geo.find(geo_name) if not geo: - raise TankError("Failed to find geometry '%s' in the current project" % geo_name) - + raise TankError( + "Failed to find geometry '%s' in the current project" % geo_name + ) + # check to see if this version is already loaded: already_loaded = False all_geo_versions = mari_engine.list_geometry_versions(geo) - for geo_version, path in [(v["geo_version"], v.get("path")) for v in all_geo_versions]: + for geo_version, path in [ + (v["geo_version"], v.get("path")) for v in all_geo_versions + ]: if path == publish_path: # we already have this version laoded so just set it as current: geo.setCurrentVersion(geo_version.name()) already_loaded = True break - + if not already_loaded: # add the new version: - new_version = mari_engine.add_geometry_version(geo, sg_publish_data, options) + new_version = mari_engine.add_geometry_version( + geo, sg_publish_data, options + ) if new_version: geo.setCurrentVersion(new_version.name()) - diff --git a/hooks/tk-maya_scene_operations.py b/hooks/tk-maya_scene_operations.py index 0b3d125..02f561b 100644 --- a/hooks/tk-maya_scene_operations.py +++ b/hooks/tk-maya_scene_operations.py @@ -13,6 +13,7 @@ import pymel.core as pm import os + class BreakdownSceneOperations(Hook): """ Breakdown operations for Maya. @@ -50,7 +51,7 @@ def scan_scene(self): # get the path and make it platform dependent # (maya uses C:/style/paths) maya_path = x.path.replace("/", os.path.sep) - refs.append( {"node": node_name, "type": "reference", "path": maya_path}) + refs.append({"node": node_name, "type": "reference", "path": maya_path}) # now look at file texture nodes for file_node in cmds.ls(l=True, type="file"): @@ -60,9 +61,11 @@ def scan_scene(self): continue # get path and make it platform dependent (maya uses C:/style/paths) - path = cmds.getAttr("%s.fileTextureName" % file_node).replace("/", os.path.sep) + path = cmds.getAttr("%s.fileTextureName" % file_node).replace( + "/", os.path.sep + ) - refs.append( {"node": file_node, "type": "file", "path": path}) + refs.append({"node": file_node, "type": "file", "path": path}) return refs @@ -88,13 +91,16 @@ def update(self, items): if node_type == "reference": # maya reference - engine.log_debug("Maya Reference %s: Updating to version %s" % (node, new_path)) + engine.log_debug( + "Maya Reference %s: Updating to version %s" % (node, new_path) + ) rn = pm.system.FileReference(node) rn.replaceWith(new_path) elif node_type == "file": # file texture node - engine.log_debug("File Texture %s: Updating to version %s" % (node, new_path)) + engine.log_debug( + "File Texture %s: Updating to version %s" % (node, new_path) + ) file_name = cmds.getAttr("%s.fileTextureName" % node) cmds.setAttr("%s.fileTextureName" % node, new_path, type="string") - diff --git a/hooks/tk-nuke_scene_operations.py b/hooks/tk-nuke_scene_operations.py index 010412d..50ea880 100644 --- a/hooks/tk-nuke_scene_operations.py +++ b/hooks/tk-nuke_scene_operations.py @@ -15,6 +15,7 @@ HookBaseClass = sgtk.get_hook_baseclass() + class BreakdownSceneOperations(HookBaseClass): """ Breakdown operations for Nuke. @@ -42,7 +43,7 @@ def scan_scene(self): Toolkit will scan the list of items, see if any of the objects matches any templates and try to determine if there is a more recent version available. Any such versions are then displayed in the UI as out of date. - + """ reads = [] @@ -59,11 +60,7 @@ def scan_scene(self): for file in files: path = file.filename().replace("/", os.path.sep) reads.append( - dict( - node=clip.activeItem(), - type="Clip", - path=path, - ) + dict(node=clip.activeItem(), type="Clip", path=path,) ) # Hiero doesn't have nodes to check, so just return the clips. @@ -77,26 +74,25 @@ def scan_scene(self): # note! We are getting the "abstract path", so contains # %04d and %V rather than actual values. - path = node.knob('file').value().replace("/", os.path.sep) + path = node.knob("file").value().replace("/", os.path.sep) - reads.append( {"node": node_name, "type": "Read", "path": path}) + reads.append({"node": node_name, "type": "Read", "path": path}) - # then the read geometry nodes + # then the read geometry nodes for node in nuke.allNodes("ReadGeo2"): node_name = node.name() - - path = node.knob('file').value().replace("/", os.path.sep) - reads.append( {"node": node_name, "type": "ReadGeo2", "path": path}) - - # then the read camera nodes + + path = node.knob("file").value().replace("/", os.path.sep) + reads.append({"node": node_name, "type": "ReadGeo2", "path": path}) + + # then the read camera nodes for node in nuke.allNodes("Camera2"): node_name = node.name() - - path = node.knob('file').value().replace("/", os.path.sep) - reads.append( {"node": node_name, "type": "Camera2", "path": path}) - return reads + path = node.knob("file").value().replace("/", os.path.sep) + reads.append({"node": node_name, "type": "Camera2", "path": path}) + return reads def update(self, items): """ @@ -119,12 +115,15 @@ def update(self, items): new_path = i["path"].replace(os.path.sep, "/") if node_type in node_type_list: - engine.log_debug("Node %s: Updating to version %s" % (node_name, new_path)) + engine.log_debug( + "Node %s: Updating to version %s" % (node_name, new_path) + ) node = nuke.toNode(node_name) node.knob("file").setValue(new_path) if node_type == "Clip": - engine.log_debug("Clip %s: Updating to version %s" % (node_name, new_path)) + engine.log_debug( + "Clip %s: Updating to version %s" % (node_name, new_path) + ) clip = node_name clip.reconnectMedia(new_path) - diff --git a/python/__init__.py b/python/__init__.py old mode 100755 new mode 100644 index a477a4d..ce39aaa --- a/python/__init__.py +++ b/python/__init__.py @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. from . import tk_multi_breakdown diff --git a/python/tk_multi_breakdown/__init__.py b/python/tk_multi_breakdown/__init__.py index 8f47494..19a8c99 100644 --- a/python/tk_multi_breakdown/__init__.py +++ b/python/tk_multi_breakdown/__init__.py @@ -9,8 +9,9 @@ # not expressly granted therein are reserved by Shotgun Software Inc. from breakdown import get_breakdown_items + def show_dialog(app): # defer imports so that the app works gracefully in batch modes from .dialog import AppDialog - app.engine.show_dialog("Breakdown", app, AppDialog, app) + app.engine.show_dialog("Breakdown", app, AppDialog, app) diff --git a/python/tk_multi_breakdown/breakdown.py b/python/tk_multi_breakdown/breakdown.py index 6cb0f4d..57fcb8c 100644 --- a/python/tk_multi_breakdown/breakdown.py +++ b/python/tk_multi_breakdown/breakdown.py @@ -23,6 +23,7 @@ # the template key we use to find the version number VERSION_KEY = "version" + def get_breakdown_items(): """ Analyzes the scene (by running a hook) and returns a list of items @@ -83,11 +84,12 @@ def get_breakdown_items(): # perform the scene scanning in the main UI thread - a lot of apps are sensitive to these # types of operations happening in other threads. app = tank.platform.current_bundle() - scene_objects = app.engine.execute_in_main_thread(app.execute_hook_method, "hook_scene_operations", "scan_scene") + scene_objects = app.engine.execute_in_main_thread( + app.execute_hook_method, "hook_scene_operations", "scan_scene" + ) # returns a list of dictionaries, each dict being like this: # {"node": node_name, "type": "reference", "path": maya_path} - for scene_object in scene_objects: node_name = scene_object.get("node") @@ -111,7 +113,7 @@ def get_breakdown_items(): # method 'register_publish' for key_name, key in matching_template.keys.iteritems(): if key_name in fields and key.is_abstract: - del(fields[key_name]) + del fields[key_name] # we also want to normalize the eye field (this should probably be an abstract field!) # note: we need to do this explicitly because the eye isn't abstract in the default @@ -129,7 +131,6 @@ def get_breakdown_items(): item["fields"] = fields item["sg_data"] = None - # store the normalized fields in dict items.append(item) @@ -137,7 +138,7 @@ def get_breakdown_items(): # note that we store (by convention) all things on a normalized sequence form in SG, e.g # all four-padded sequences are stored as '%04d' regardless if they have been published from # houdini, maya, nuke etc. - valid_paths = [ x.get("path") for x in items ] + valid_paths = [x.get("path") for x in items] # check if we have the path in the cache paths_to_fetch = [] @@ -150,20 +151,20 @@ def get_breakdown_items(): if item.get("path") == p: item["sg_data"] = g_cached_sg_publish_data[p] - - fields = ["entity", - "entity.Asset.sg_asset_type", # grab asset type if it is an asset - "code", - "image", - "name", - "task", - "version_number", - "project" - ] + fields = [ + "entity", + "entity.Asset.sg_asset_type", # grab asset type if it is an asset + "code", + "image", + "name", + "task", + "version_number", + "project", + ] if tank.util.get_published_file_entity_type(app.tank) == "PublishedFile": fields.append("published_file_type") - else:# == "TankPublishedFile" + else: # == "TankPublishedFile" fields.append("tank_type") sg_data = tank.util.find_publish(app.tank, paths_to_fetch, fields=fields) diff --git a/python/tk_multi_breakdown/breakdown_list_item.py b/python/tk_multi_breakdown/breakdown_list_item.py index df14e13..59434a3 100644 --- a/python/tk_multi_breakdown/breakdown_list_item.py +++ b/python/tk_multi_breakdown/breakdown_list_item.py @@ -24,6 +24,7 @@ from .ui.item import Ui_Item from . import breakdown + class BreakdownListItem(browser_widget.ListItem): """ Custom list widget for displaying the breakdown status in the list view @@ -66,7 +67,9 @@ def is_out_of_date(self): else: return self._is_latest == False - def calculate_status(self, template, fields, show_red, show_green, entity_dict = None): + def calculate_status( + self, template, fields, show_red, show_green, entity_dict=None + ): """ Figure out if this is a red or a green one. Also get thumb if possible """ @@ -88,7 +91,7 @@ def calculate_status(self, template, fields, show_red, show_green, entity_dict = # kick off the worker! self._worker_uid = self._worker.queue_work(self._calculate_status, {}) self._worker.work_completed.connect(self._on_worker_task_complete) - self._worker.work_failure.connect( self._on_worker_failure) + self._worker.work_failure.connect(self._on_worker_failure) def _calculate_status(self, data): """ @@ -120,13 +123,14 @@ def _calculate_status(self, data): else: output["thumbnail"] = ":/res/no_thumb.png" - # first, get the latest available version for this item app = tank.platform.current_bundle() - latest_version = app.execute_hook("hook_get_version_number", template=self._template, curr_fields=self._fields) + latest_version = app.execute_hook( + "hook_get_version_number", template=self._template, curr_fields=self._fields + ) current_version = self._fields["version"] - output["up_to_date"] = (latest_version == current_version) + output["up_to_date"] = latest_version == current_version self._latest_version = latest_version self._is_latest = output["up_to_date"] @@ -145,7 +149,6 @@ def _on_worker_failure(self, uid, msg): # show error message self._app.log_warning("Worker error: %s" % msg) - def _on_worker_task_complete(self, uid, data): """ Called when the computation is complete and we should update widget diff --git a/python/tk_multi_breakdown/dialog.py b/python/tk_multi_breakdown/dialog.py index 9a2ac9b..37033b6 100644 --- a/python/tk_multi_breakdown/dialog.py +++ b/python/tk_multi_breakdown/dialog.py @@ -17,9 +17,8 @@ from tank.platform.qt import QtCore, QtGui from .ui.dialog import Ui_Dialog -class AppDialog(QtGui.QWidget): - +class AppDialog(QtGui.QWidget): def __init__(self, app): QtGui.QWidget.__init__(self) self._app = app @@ -41,7 +40,6 @@ def __init__(self, app): # load data from shotgun self.setup_scene_list() - ######################################################################################## # make sure we trap when the dialog is closed so that we can shut down # our threads. Nuke does not do proper cleanup on exit. @@ -56,19 +54,20 @@ def closeEvent(self, event): def select_all_red(self): for x in self.ui.browser.get_items(): - try: # hack - all items arent breakdown nodes + try: # hack - all items arent breakdown nodes if x.is_out_of_date() and not x.is_selected(): self.ui.browser.select(x) except: pass - def update_items(self): curr_selection = self.ui.browser.get_selected_items() if len(curr_selection) == 0: - QtGui.QMessageBox.information(self, "Please select", "Please select items to update!") + QtGui.QMessageBox.information( + self, "Please select", "Please select items to update!" + ) return data = [] @@ -100,7 +99,6 @@ def update_items(self): # finally refresh the UI self.setup_scene_list() - def setup_scene_list(self): self.ui.browser.clear() @@ -123,6 +121,3 @@ def setup_scene_list(self): d["show_green"] = True self.ui.browser.load(d) - - - diff --git a/python/tk_multi_breakdown/scene_browser.py b/python/tk_multi_breakdown/scene_browser.py index 7321011..eaed6d6 100644 --- a/python/tk_multi_breakdown/scene_browser.py +++ b/python/tk_multi_breakdown/scene_browser.py @@ -20,22 +20,30 @@ from . import breakdown browser_widget = tank.platform.import_framework("tk-framework-widget", "browser_widget") -shotgun_globals = tank.platform.import_framework("tk-framework-shotgunutils", "shotgun_globals") +shotgun_globals = tank.platform.import_framework( + "tk-framework-shotgunutils", "shotgun_globals" +) from .breakdown_list_item import BreakdownListItem -class SceneBrowserWidget(browser_widget.BrowserWidget): - +class SceneBrowserWidget(browser_widget.BrowserWidget): def __init__(self, parent=None): browser_widget.BrowserWidget.__init__(self, parent) def get_data(self, data): - items = breakdown.get_breakdown_items() - return {"items": items, "show_red": data["show_red"], "show_green": data["show_green"] } + items = breakdown.get_breakdown_items() + return { + "items": items, + "show_red": data["show_red"], + "show_green": data["show_green"], + } def _make_row(self, first, second): - return "%s   %s" % (first, second) + return "%s   %s" % ( + first, + second, + ) def process_result(self, result): @@ -64,9 +72,9 @@ def process_result(self, result): asset_type = sg_data["entity.Asset.sg_asset_type"] if asset_type: - group = "%ss" % asset_type # eg. Characters + group = "%ss" % asset_type # eg. Characters else: - group = "%ss" % entity_type # eg. Shots + group = "%ss" % entity_type # eg. Shots # it is an asset, so group by asset type if group not in groups: @@ -80,13 +88,12 @@ def process_result(self, result): groups[OTHER_ITEMS] = [] groups[OTHER_ITEMS].append(d) - ################################################################################ # PASS 2 - display the content of all groups if tank.util.get_published_file_entity_type(self._app.tank) == "PublishedFile": published_file_type_field = "published_file_type" - else:# == "TankPublishedFile" + else: # == "TankPublishedFile" published_file_type_field = "tank_type" # now iterate through the groups @@ -102,49 +109,64 @@ def process_result(self, result): # provide a limited amount of data for receivers via the # data dictionary on # the item object - i.data = {"node_name": d["node_name"], - "node_type": d["node_type"], - "template": d["template"], - "fields": d["fields"] } + i.data = { + "node_name": d["node_name"], + "node_type": d["node_type"], + "template": d["template"], + "fields": d["fields"], + } # populate the description details = [] - if d.get("sg_data"): sg_data = d["sg_data"] - details.append( self._make_row("Item", "%s, Version %d" % (sg_data["name"], sg_data["version_number"]) ) ) + details.append( + self._make_row( + "Item", + "%s, Version %d" + % (sg_data["name"], sg_data["version_number"]), + ) + ) # see if this publish is associated with an entity linked_entity = sg_data.get("entity") if linked_entity: - display_name = shotgun_globals.get_type_display_name(linked_entity["type"]) - - details.append(self._make_row(display_name, linked_entity["name"])) + display_name = shotgun_globals.get_type_display_name( + linked_entity["type"] + ) + + details.append( + self._make_row(display_name, linked_entity["name"]) + ) # does it have a tank type ? if sg_data.get(published_file_type_field): - details.append( self._make_row("Type", sg_data.get(published_file_type_field).get("name"))) - - details.append( self._make_row("Node", d["node_name"])) + details.append( + self._make_row( + "Type", + sg_data.get(published_file_type_field).get("name"), + ) + ) + details.append(self._make_row("Node", d["node_name"])) else: - details.append(self._make_row("Version", d["fields"]["version"] )) + details.append(self._make_row("Version", d["fields"]["version"])) # display some key fields in the widget # todo: make this more generic? relevant_fields = ["Shot", "Asset", "Step", "Sequence", "name"] - for (k,v) in d["fields"].items(): + for (k, v) in d["fields"].items(): # only show relevant fields - a bit of a hack if k in relevant_fields: - details.append( self._make_row(k,v) ) + details.append(self._make_row(k, v)) - details.append( self._make_row("Node", d["node_name"])) + details.append(self._make_row("Node", d["node_name"])) inner = "".join(details) @@ -152,8 +174,10 @@ def process_result(self, result): # finally, ask the node to calculate its red-green status # this will happen asynchronously. - i.calculate_status(d["template"], - d["fields"], - result["show_red"], - result["show_green"], - d.get("sg_data")) + i.calculate_status( + d["template"], + d["fields"], + result["show_red"], + result["show_green"], + d.get("sg_data"), + ) diff --git a/resources/build_resources.sh b/resources/build_resources.sh index 955b65a..bf6002a 100755 --- a/resources/build_resources.sh +++ b/resources/build_resources.sh @@ -1,16 +1,16 @@ #!/usr/bin/env bash -# +# # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. -# The path to output all built .py files to: +# The path to output all built .py files to: UI_PYTHON_PATH=../python/tk_multi_breakdown/ui # Remove any problematic profiles from pngs. @@ -19,17 +19,17 @@ for f in *.png; do mogrify $f; done # Helper functions to build UI files function build_qt { echo " > Building " $2 - + # compile ui to python $1 $2 > $UI_PYTHON_PATH/$3.py - + # replace PySide imports with sgtk.platform.qt and remove line containing Created by date sed -i "" -e "s/from PySide import/from sgtk.platform.qt import/g" -e "/# Created:/d" $UI_PYTHON_PATH/$3.py } function build_ui { build_qt "pyside-uic --from-imports" "$1.ui" "$1" -} +} function build_res { build_qt "pyside-rcc" "$1.qrc" "$1_rc" @@ -44,4 +44,3 @@ build_ui item # build resources echo "building resources..." build_res resources - diff --git a/tests/fixtures/config/bundles/test_engine/engine.py b/tests/fixtures/config/bundles/test_engine/engine.py index 7f77708..72d548a 100644 --- a/tests/fixtures/config/bundles/test_engine/engine.py +++ b/tests/fixtures/config/bundles/test_engine/engine.py @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. """ @@ -18,22 +18,21 @@ class TestEngine(Engine): - def init_engine(self): pass - + ########################################################################################## # logging interfaces def log_debug(self, msg): if self.get_setting("debug_logging", False): sys.stdout.write("DEBUG: %s\n" % msg) - + def log_info(self, msg): sys.stdout.write("%s\n" % msg) - + def log_warning(self, msg): sys.stdout.write("WARNING: %s\n" % msg) - + def log_error(self, msg): sys.stdout.write("ERROR: %s\n" % msg) diff --git a/tests/fixtures/config/bundles/test_engine/info.yml b/tests/fixtures/config/bundles/test_engine/info.yml index 2be321e..b2d9aef 100644 --- a/tests/fixtures/config/bundles/test_engine/info.yml +++ b/tests/fixtures/config/bundles/test_engine/info.yml @@ -1,21 +1,21 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # Metadata defining the behaviour and requirements for this engine # expected fields in the configuration file for this engine configuration: - debug_logging: + debug_logging: type: bool default_value: false - description: Controls whether debug messages should be emitted to the logger + description: Controls whether debug messages should be emitted to the logger # the Shotgun fields that this engine needs in order to operate correctly diff --git a/tests/fixtures/config/core/hooks/pick_environment.py b/tests/fixtures/config/core/hooks/pick_environment.py index cf0b2aa..99bdde0 100644 --- a/tests/fixtures/config/core/hooks/pick_environment.py +++ b/tests/fixtures/config/core/hooks/pick_environment.py @@ -1,19 +1,17 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. - from tank import Hook -class PickEnvironment(Hook): +class PickEnvironment(Hook): def execute(self, context): return "test" - diff --git a/tests/fixtures/config/core/schema/project/assets/asset_type.yml b/tests/fixtures/config/core/schema/project/assets/asset_type.yml index c25c306..02da4d8 100644 --- a/tests/fixtures/config/core/schema/project/assets/asset_type.yml +++ b/tests/fixtures/config/core/schema/project/assets/asset_type.yml @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # the type of dynamic content @@ -16,4 +16,3 @@ entity_type: "Asset" # the shotgun field to use for the folder name field_name: "sg_asset_type" - diff --git a/tests/fixtures/config/core/schema/project/assets/asset_type/asset.yml b/tests/fixtures/config/core/schema/project/assets/asset_type/asset.yml index 3895c26..a1e544a 100644 --- a/tests/fixtures/config/core/schema/project/assets/asset_type/asset.yml +++ b/tests/fixtures/config/core/schema/project/assets/asset_type/asset.yml @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # the type of dynamic content @@ -18,7 +18,7 @@ name: "code" entity_type: "Asset" # shotgun filters to apply when getting the list of items -# this should be a list of dicts, each dict containing +# this should be a list of dicts, each dict containing # three fields: path, relation and values # (this is std shotgun API syntax) # any values starting with $ are resolved into path objects diff --git a/tests/fixtures/config/core/schema/project/assets/asset_type/asset/step.yml b/tests/fixtures/config/core/schema/project/assets/asset_type/asset/step.yml index 34ee8d6..e2f132d 100644 --- a/tests/fixtures/config/core/schema/project/assets/asset_type/asset/step.yml +++ b/tests/fixtures/config/core/schema/project/assets/asset_type/asset/step.yml @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # the type of dynamic content @@ -21,9 +21,8 @@ entity_type: "Step" create_with_parent: True # shotgun filters to apply when getting the list of items -# this should be a list of dicts, each dict containing +# this should be a list of dicts, each dict containing # three fields: path, relation and values # (this is std shotgun API syntax) # any values starting with $ are resolved into path objects filters: [{"path":"$FROM$Task.step.entity", "relation": "is", "values":["$asset"]}] - diff --git a/tests/fixtures/config/core/schema/project/scenes/step.yml b/tests/fixtures/config/core/schema/project/scenes/step.yml index 9e6d7da..abafecf 100644 --- a/tests/fixtures/config/core/schema/project/scenes/step.yml +++ b/tests/fixtures/config/core/schema/project/scenes/step.yml @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # the type of dynamic content @@ -18,9 +18,8 @@ name: "short_name" entity_type: "Step" # shotgun filters to apply when getting the list of items -# this should be a list of dicts, each dict containing +# this should be a list of dicts, each dict containing # three fields: path, relation and values # (this is std shotgun API syntax) # any values starting with $ are resolved into path objects filters: [{"path":"entity_type", "relation": "is", "values":["Scene"]}] - diff --git a/tests/fixtures/config/core/schema/project/scenes/step/scene.yml b/tests/fixtures/config/core/schema/project/scenes/step/scene.yml index 054185a..f4f48d2 100644 --- a/tests/fixtures/config/core/schema/project/scenes/step/scene.yml +++ b/tests/fixtures/config/core/schema/project/scenes/step/scene.yml @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # the type of dynamic content @@ -18,9 +18,8 @@ name: "code" entity_type: "Scene" # shotgun filters to apply when getting the list of items -# this should be a list of dicts, each dict containing +# this should be a list of dicts, each dict containing # three fields: path, relation and values # (this is std shotgun API syntax) # any values starting with $ are resolved into path objects filters: [ { "path": "project", "relation": "is", "values": [ "$project" ] } ] - diff --git a/tests/fixtures/config/core/schema/project/sequences/sequence.yml b/tests/fixtures/config/core/schema/project/sequences/sequence.yml index 0f784d5..d433770 100644 --- a/tests/fixtures/config/core/schema/project/sequences/sequence.yml +++ b/tests/fixtures/config/core/schema/project/sequences/sequence.yml @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # the type of dynamic content @@ -18,9 +18,8 @@ name: "code" entity_type: "Sequence" # shotgun filters to apply when getting the list of items -# this should be a list of dicts, each dict containing +# this should be a list of dicts, each dict containing # three fields: path, relation and values # (this is std shotgun API syntax) # any values starting with $ are resolved into path objects filters: [ { "path": "project", "relation": "is", "values": [ "$project" ] } ] - diff --git a/tests/fixtures/config/core/schema/project/sequences/sequence/shot.yml b/tests/fixtures/config/core/schema/project/sequences/sequence/shot.yml index 8049612..3f9f222 100644 --- a/tests/fixtures/config/core/schema/project/sequences/sequence/shot.yml +++ b/tests/fixtures/config/core/schema/project/sequences/sequence/shot.yml @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # the type of dynamic content @@ -18,9 +18,8 @@ name: "code" entity_type: "Shot" # shotgun filters to apply when getting the list of items -# this should be a list of dicts, each dict containing +# this should be a list of dicts, each dict containing # three fields: path, relation and values # (this is std shotgun API syntax) # any values starting with $ are resolved into path objects filters: [ { "path": "sg_sequence", "relation": "is", "values": [ "$sequence" ] } ] - diff --git a/tests/fixtures/config/core/schema/project/sequences/sequence/shot/step.yml b/tests/fixtures/config/core/schema/project/sequences/sequence/shot/step.yml index 1cb6a82..107055a 100644 --- a/tests/fixtures/config/core/schema/project/sequences/sequence/shot/step.yml +++ b/tests/fixtures/config/core/schema/project/sequences/sequence/shot/step.yml @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # the type of dynamic content @@ -21,9 +21,8 @@ entity_type: "Step" create_with_parent: True # shotgun filters to apply when getting the list of items -# this should be a list of dicts, each dict containing +# this should be a list of dicts, each dict containing # three fields: path, relation and values # (this is std shotgun API syntax) # any values starting with $ are resolved into path objects filters: [{"path":"$FROM$Task.step.entity", "relation": "is", "values":["$shot"]}] - diff --git a/tests/fixtures/config/core/templates.yml b/tests/fixtures/config/core/templates.yml index 0a931fc..f2a7804 100644 --- a/tests/fixtures/config/core/templates.yml +++ b/tests/fixtures/config/core/templates.yml @@ -1,24 +1,24 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. # # This file is one of the central points in the Tank configuration and a counterpart to # the folder configuration structure. # -# the folder structure underneath the project folder is used to create folders on disk - -# templates.yml (this file) refers to those folders. Therefore, the two files need to be +# the folder structure underneath the project folder is used to create folders on disk - +# templates.yml (this file) refers to those folders. Therefore, the two files need to be # in sync. This file contains an overview of all locations that are used in Tank. # # -# Whenever an app or an engine refers to a location on disk, it is using a entry defined in -# this file. For more information, see the Tank Documentation. +# Whenever an app or an engine refers to a location on disk, it is using a entry defined in +# this file. For more information, see the Tank Documentation. keys: Sequence: @@ -50,7 +50,7 @@ keys: format_spec: "04" eye: type: str - choices: + choices: - Left - Right sg_asset_type: @@ -66,7 +66,7 @@ keys: paths: # ------------------------------------------------------------------------------------ - # Shot pipeline + # Shot pipeline # define the location of a work area shot_work_area: sequences/{Sequence}/{Shot}/{Step}/work @@ -126,7 +126,7 @@ paths: # ------------------------------------------------------------------------------------ - # Asset pipeline + # Asset pipeline # define the location of a work area asset_work_area: assets/{sg_asset_type}/{Asset}/{Step}/work @@ -180,16 +180,14 @@ paths: strings: - # when a publish record is created in shotgun, populate the name with the following + # when a publish record is created in shotgun, populate the name with the following houdini_shot_publish_name: "Houdini Scene {name}, v{version}" - # when a publish record is created in shotgun, populate the name with the following + # when a publish record is created in shotgun, populate the name with the following maya_publish_name: "Maya Scene {name}, v{version}" - # when a publish record is created in shotgun, populate the name with the following + # when a publish record is created in shotgun, populate the name with the following nuke_publish_name: "Nuke Script {name_alpha}, v{version}" - # when a publish record is created in shotgun, populate the name with the following + # when a publish record is created in shotgun, populate the name with the following houdini_asset_publish_name: "Houdini Scene {name_alpha}, v{version}" - - diff --git a/tests/fixtures/config/env/test.yml b/tests/fixtures/config/env/test.yml index 10ba6d8..10cfc85 100644 --- a/tests/fixtures/config/env/test.yml +++ b/tests/fixtures/config/env/test.yml @@ -8,7 +8,7 @@ engines: location: {'type': 'dev', 'path': '${BUNDLE_ROOT}/tk-multi-breakdown'} hook_scene_operations: '{config}/scene_operations.py' - + frameworks: tk-framework-widget_v0.2.x: location: {name: tk-framework-widget, type: dev, version: v0.2.33, path: '${BUNDLE_ROOT}/tk-framework-widget' } @@ -20,5 +20,3 @@ frameworks: location: {name: tk-framework-qtwidgets, type: dev, version: v1.0.1, path: '${BUNDLE_ROOT}/tk-framework-qtwidgets' } tk-framework-qtwidgets_v2.x.x: location: {name: tk-framework-qtwidgets, type: dev, version: v1.0.1, path: '${BUNDLE_ROOT}/tk-framework-qtwidgets' } - - diff --git a/tests/fixtures/config/hooks/scene_operations.py b/tests/fixtures/config/hooks/scene_operations.py index 6fb57c7..3d33eea 100644 --- a/tests/fixtures/config/hooks/scene_operations.py +++ b/tests/fixtures/config/hooks/scene_operations.py @@ -12,6 +12,7 @@ from tank import Hook import tank + class BreakdownSceneOperations(Hook): """ Breakdown operations test hook @@ -39,12 +40,19 @@ def scan_scene(self): """ nodes = [] - - nodes.append( {"node": "outside_template_system", "type": "TestNode", "path": "/foo/bar"}) - nodes.append( {"node": "maya_publish", "type": "TestNode", "path": os.environ["TEST_PATH_1"]}) - - return nodes + nodes.append( + {"node": "outside_template_system", "type": "TestNode", "path": "/foo/bar"} + ) + nodes.append( + { + "node": "maya_publish", + "type": "TestNode", + "path": os.environ["TEST_PATH_1"], + } + ) + + return nodes def update(self, items): """ @@ -58,4 +66,3 @@ def update(self, items): the that each node should be updated *to* rather than the current path. """ tank._hook_items = items - diff --git a/tests/test_breakdown.py b/tests/test_breakdown.py index c58a68f..f4fb3cc 100644 --- a/tests/test_breakdown.py +++ b/tests/test_breakdown.py @@ -1,11 +1,11 @@ # Copyright (c) 2013 Shotgun Software Inc. -# +# # CONFIDENTIAL AND PROPRIETARY -# -# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. -# By accessing, using, copying or modifying this work you indicate your -# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. import sys @@ -26,17 +26,17 @@ class TestApplication(TankTestBase): """ General fixtures class for testing Toolkit apps """ - + def setUp(self): """ Fixtures setup """ super(TestApplication, self).setUp() self.setup_fixtures() - + # set up a path to the folder above the app folder # this is so that the breakdown and all its frameworks can be loaded in - # it is assumed that you have all the dependent packages in a structure under this + # it is assumed that you have all the dependent packages in a structure under this # root point, like this: # # bundle_root @@ -45,42 +45,54 @@ def setUp(self): # |- tk-framework-qtwidgets # \- tk-framework-shotgunutils # - # - os.environ["BUNDLE_ROOT"] = os.path.abspath(os.path.join( os.path.dirname(__file__), "..", "..")) - + # + os.environ["BUNDLE_ROOT"] = os.path.abspath( + os.path.join(os.path.dirname(__file__), "..", "..") + ) + # set up a standard sequence/shot/step and run folder creation - self.seq = {"type": "Sequence", - "id": 2, - "code": "seq_code", - "project": self.project} - self.shot = {"type": "Shot", - "id": 1, - "code": "shot_code", - "sg_sequence": self.seq, - "project": self.project} - self.step = {"type": "Step", - "id": 3, - "code": "step_code", - "entity_type": "Shot", - "short_name": "step_short_name"} - self.task = {"type": "Task", - "id": 23, - "entity": self.shot, - "step": self.step, - "project": self.project} + self.seq = { + "type": "Sequence", + "id": 2, + "code": "seq_code", + "project": self.project, + } + self.shot = { + "type": "Shot", + "id": 1, + "code": "shot_code", + "sg_sequence": self.seq, + "project": self.project, + } + self.step = { + "type": "Step", + "id": 3, + "code": "step_code", + "entity_type": "Shot", + "short_name": "step_short_name", + } + self.task = { + "type": "Task", + "id": 23, + "entity": self.shot, + "step": self.step, + "project": self.project, + } # Add these to mocked shotgun - self.add_to_sg_mock_db([self.shot, self.seq, self.step, self.project, self.task]) + self.add_to_sg_mock_db( + [self.shot, self.seq, self.step, self.project, self.task] + ) # run folder creation for the shot self.tk.create_filesystem_structure(self.shot["type"], self.shot["id"]) - + # now make a context context = self.tk.context_from_entity(self.shot["type"], self.shot["id"]) - + # and start the engine self.engine = tank.platform.start_engine("test_engine", self.tk, context) - + def tearDown(self): """ Fixtures teardown @@ -89,47 +101,50 @@ def tearDown(self): cur_engine = tank.platform.current_engine() if cur_engine: cur_engine.destroy() - + # important to call base class so it can clean up memory super(TestApplication, self).tearDown() - - + class TestApi(TestApplication): """ Tests for the Breakdown App's API interface """ - + def setUp(self): """ Fixtures setup - """ + """ super(TestApi, self).setUp() - + # short hand for the app self.app = self.engine.apps["tk-multi-breakdown"] - + # set up some test data - self.test_path_1 = os.path.join(self.project_root, - "sequences", - self.seq["code"], - self.shot["code"], - self.step["short_name"], - "publish", - "foo.v003.ma") - - self.test_path_2 = os.path.join(self.project_root, - "sequences", - self.seq["code"], - self.shot["code"], - self.step["short_name"], - "publish", - "foo.v004.ma") + self.test_path_1 = os.path.join( + self.project_root, + "sequences", + self.seq["code"], + self.shot["code"], + self.step["short_name"], + "publish", + "foo.v003.ma", + ) + + self.test_path_2 = os.path.join( + self.project_root, + "sequences", + self.seq["code"], + self.shot["code"], + self.step["short_name"], + "publish", + "foo.v004.ma", + ) fh = open(self.test_path_1, "wt") fh.write("hello") fh.close() - + fh = open(self.test_path_2, "wt") fh.write("hello") fh.close() @@ -138,64 +153,71 @@ def setUp(self): # it out into env vars... os.environ["TEST_PATH_1"] = self.test_path_1 os.environ["TEST_PATH_2"] = self.test_path_2 - - + def test_analyze_scene(self): """ Tests the analyze_scene method """ scene_data = self.app.analyze_scene() self.assertEqual(len(scene_data), 1) - + item = scene_data[0] - self.assertEqual(item["fields"], {'Shot': 'shot_code', - 'name': 'foo', - 'Sequence': 'seq_code', - 'Step': 'step_short_name', - 'version': 3, - 'maya_extension': 'ma', - 'eye': '%V'}) + self.assertEqual( + item["fields"], + { + "Shot": "shot_code", + "name": "foo", + "Sequence": "seq_code", + "Step": "step_short_name", + "version": 3, + "maya_extension": "ma", + "eye": "%V", + }, + ) self.assertEqual(item["node_name"], "maya_publish") self.assertEqual(item["node_type"], "TestNode") self.assertEqual(item["template"], self.tk.templates["maya_shot_publish"]) self.assertEqual(item["sg_data"], None) - - + def test_compute_highest_version(self): """ Tests the version computation logic """ scene_data = self.app.analyze_scene() - item = scene_data[0] + item = scene_data[0] # test logic - self.assertEqual(self.app.compute_highest_version(item["template"], item["fields"]), 4) + self.assertEqual( + self.app.compute_highest_version(item["template"], item["fields"]), 4 + ) # test bad data - self.assertRaises(TankError, - self.app.compute_highest_version, - self.tk.templates["maya_asset_publish"], - item["fields"]) - + self.assertRaises( + TankError, + self.app.compute_highest_version, + self.tk.templates["maya_asset_publish"], + item["fields"], + ) + def test_update(self): """ Test scene update """ scene_data = self.app.analyze_scene() item = scene_data[0] - + # increment version fields = item["fields"] fields["version"] = 4 # clear temp location where hook writes to tank._hook_items = None - + # execute hook - self.app.update_item(item["node_type"], item["node_name"], item["template"], fields) - + self.app.update_item( + item["node_type"], item["node_name"], item["template"], fields + ) + # check result self.assertEqual(len(tank._hook_items), 1) self.assertEqual(tank._hook_items[0]["node"], "maya_publish") self.assertEqual(tank._hook_items[0]["path"], self.test_path_2) self.assertEqual(tank._hook_items[0]["type"], "TestNode") - -