Merge branch 'develop' into feature/new-context-env-variables

This commit is contained in:
Jakub Trllo 2024-02-13 12:29:46 +01:00
commit e24f138f47
43 changed files with 227 additions and 689 deletions

102
.github/pr-glob-labeler.yml vendored Normal file
View file

@ -0,0 +1,102 @@
# Add type: unittest label if any changes in tests folders
'type: unittest':
- '*/*tests*/**/*'
# any changes in documentation structure
'type: documentation':
- '*/**/*website*/**/*'
- '*/**/*docs*/**/*'
# hosts triage
'host: Nuke':
- '*/**/*nuke*'
- '*/**/*nuke*/**/*'
'host: Photoshop':
- '*/**/*photoshop*'
- '*/**/*photoshop*/**/*'
'host: Harmony':
- '*/**/*harmony*'
- '*/**/*harmony*/**/*'
'host: UE':
- '*/**/*unreal*'
- '*/**/*unreal*/**/*'
'host: Houdini':
- '*/**/*houdini*'
- '*/**/*houdini*/**/*'
'host: Maya':
- '*/**/*maya*'
- '*/**/*maya*/**/*'
'host: Resolve':
- '*/**/*resolve*'
- '*/**/*resolve*/**/*'
'host: Blender':
- '*/**/*blender*'
- '*/**/*blender*/**/*'
'host: Hiero':
- '*/**/*hiero*'
- '*/**/*hiero*/**/*'
'host: Fusion':
- '*/**/*fusion*'
- '*/**/*fusion*/**/*'
'host: Flame':
- '*/**/*flame*'
- '*/**/*flame*/**/*'
'host: TrayPublisher':
- '*/**/*traypublisher*'
- '*/**/*traypublisher*/**/*'
'host: 3dsmax':
- '*/**/*max*'
- '*/**/*max*/**/*'
'host: TV Paint':
- '*/**/*tvpaint*'
- '*/**/*tvpaint*/**/*'
'host: CelAction':
- '*/**/*celaction*'
- '*/**/*celaction*/**/*'
'host: After Effects':
- '*/**/*aftereffects*'
- '*/**/*aftereffects*/**/*'
'host: Substance Painter':
- '*/**/*substancepainter*'
- '*/**/*substancepainter*/**/*'
# modules triage
'module: Deadline':
- '*/**/*deadline*'
- '*/**/*deadline*/**/*'
'module: RoyalRender':
- '*/**/*royalrender*'
- '*/**/*royalrender*/**/*'
'module: Sitesync':
- '*/**/*sync_server*'
- '*/**/*sync_server*/**/*'
'module: Ftrack':
- '*/**/*ftrack*'
- '*/**/*ftrack*/**/*'
'module: Shotgrid':
- '*/**/*shotgrid*'
- '*/**/*shotgrid*/**/*'
'module: Kitsu':
- '*/**/*kitsu*'
- '*/**/*kitsu*/**/*'

View file

@ -15,8 +15,7 @@ from wsrpc_aiohttp import (
from qtpy import QtCore
from ayon_core.lib import Logger
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import Logger, is_in_tests
from ayon_core.pipeline import install_host
from ayon_core.addon import AddonsManager
from ayon_core.tools.utils import host_tools, get_ayon_qt_app

View file

@ -52,7 +52,7 @@ Because Harmony projects are directories, this integration uses `.zip` as work f
### Show Workfiles on launch
You can show the Workfiles app when Harmony launches by setting environment variable `AVALON_HARMONY_WORKFILES_ON_LAUNCH=1`.
You can show the Workfiles app when Harmony launches by setting environment variable `AYON_HARMONY_WORKFILES_ON_LAUNCH=1`.
## Developing

View file

@ -349,7 +349,7 @@ function start() {
/** hostname or ip of server - should be localhost */
var host = '127.0.0.1';
/** port of the server */
var port = parseInt(System.getenv('AVALON_HARMONY_PORT'));
var port = parseInt(System.getenv('AYON_HARMONY_PORT'));
// Attach the client to the QApplication to preserve.
var app = QCoreApplication.instance();

View file

@ -189,14 +189,14 @@ def launch(application_path, *args):
install_host(harmony)
ProcessContext.port = random.randrange(49152, 65535)
os.environ["AVALON_HARMONY_PORT"] = str(ProcessContext.port)
os.environ["AYON_HARMONY_PORT"] = str(ProcessContext.port)
ProcessContext.application_path = application_path
# Launch Harmony.
setup_startup_scripts()
check_libs()
if not os.environ.get("AVALON_HARMONY_WORKFILES_ON_LAUNCH", False):
if not os.environ.get("AYON_HARMONY_WORKFILES_ON_LAUNCH", False):
open_empty_workfile()
return

View file

@ -3140,119 +3140,6 @@ def fix_incompatible_containers():
"ReferenceLoader", type="string")
def _null(*args):
pass
class shelf():
'''A simple class to build shelves in maya. Since the build method is empty,
it should be extended by the derived class to build the necessary shelf
elements. By default it creates an empty shelf called "customShelf".'''
###########################################################################
'''This is an example shelf.'''
# class customShelf(_shelf):
# def build(self):
# self.addButon(label="button1")
# self.addButon("button2")
# self.addButon("popup")
# p = cmds.popupMenu(b=1)
# self.addMenuItem(p, "popupMenuItem1")
# self.addMenuItem(p, "popupMenuItem2")
# sub = self.addSubMenu(p, "subMenuLevel1")
# self.addMenuItem(sub, "subMenuLevel1Item1")
# sub2 = self.addSubMenu(sub, "subMenuLevel2")
# self.addMenuItem(sub2, "subMenuLevel2Item1")
# self.addMenuItem(sub2, "subMenuLevel2Item2")
# self.addMenuItem(sub, "subMenuLevel1Item2")
# self.addMenuItem(p, "popupMenuItem3")
# self.addButon("button3")
# customShelf()
###########################################################################
def __init__(self, name="customShelf", iconPath="", preset={}):
self.name = name
self.iconPath = iconPath
self.labelBackground = (0, 0, 0, 0)
self.labelColour = (.9, .9, .9)
self.preset = preset
self._cleanOldShelf()
cmds.setParent(self.name)
self.build()
def build(self):
'''This method should be overwritten in derived classes to actually
build the shelf elements. Otherwise, nothing is added to the shelf.'''
for item in self.preset['items']:
if not item.get('command'):
item['command'] = self._null
if item['type'] == 'button':
self.addButon(item['name'],
command=item['command'],
icon=item['icon'])
if item['type'] == 'menuItem':
self.addMenuItem(item['parent'],
item['name'],
command=item['command'],
icon=item['icon'])
if item['type'] == 'subMenu':
self.addMenuItem(item['parent'],
item['name'],
command=item['command'],
icon=item['icon'])
def addButon(self, label, icon="commandButton.png",
command=_null, doubleCommand=_null):
'''
Adds a shelf button with the specified label, command,
double click command and image.
'''
cmds.setParent(self.name)
if icon:
icon = os.path.join(self.iconPath, icon)
print(icon)
cmds.shelfButton(width=37, height=37, image=icon, label=label,
command=command, dcc=doubleCommand,
imageOverlayLabel=label, olb=self.labelBackground,
olc=self.labelColour)
def addMenuItem(self, parent, label, command=_null, icon=""):
'''
Adds a shelf button with the specified label, command,
double click command and image.
'''
if icon:
icon = os.path.join(self.iconPath, icon)
print(icon)
return cmds.menuItem(p=parent, label=label, c=command, i="")
def addSubMenu(self, parent, label, icon=None):
'''
Adds a sub menu item with the specified label and icon to
the specified parent popup menu.
'''
if icon:
icon = os.path.join(self.iconPath, icon)
print(icon)
return cmds.menuItem(p=parent, label=label, i=icon, subMenu=1)
def _cleanOldShelf(self):
'''
Checks if the shelf exists and empties it if it does
or creates it if it does not.
'''
if cmds.shelfLayout(self.name, ex=1):
if cmds.shelfLayout(self.name, q=1, ca=1):
for each in cmds.shelfLayout(self.name, q=1, ca=1):
cmds.deleteUI(each)
else:
cmds.shelfLayout(self.name, p="ShelfLayout")
def update_content_on_context_change():
"""
This will update scene content to match new asset on context change

View file

@ -9,7 +9,8 @@ import maya.cmds as cmds
from ayon_core.pipeline import (
get_current_asset_name,
get_current_task_name
get_current_task_name,
registered_host
)
from ayon_core.pipeline.workfile import BuildWorkfile
from ayon_core.tools.utils import host_tools
@ -21,8 +22,10 @@ from .workfile_template_builder import (
create_placeholder,
update_placeholder,
build_workfile_template,
update_workfile_template,
update_workfile_template
)
from ayon_core.tools.workfile_template_build import open_template_ui
from .workfile_template_builder import MayaTemplateBuilder
log = logging.getLogger(__name__)
@ -167,16 +170,6 @@ def install(project_settings):
tearOff=True,
parent=MENU_NAME
)
cmds.menuItem(
"Create Placeholder",
parent=builder_menu,
command=create_placeholder
)
cmds.menuItem(
"Update Placeholder",
parent=builder_menu,
command=update_placeholder
)
cmds.menuItem(
"Build Workfile from template",
parent=builder_menu,
@ -187,6 +180,27 @@ def install(project_settings):
parent=builder_menu,
command=update_workfile_template
)
cmds.menuItem(
divider=True,
parent=builder_menu
)
cmds.menuItem(
"Open Template",
parent=builder_menu,
command=lambda *args: open_template_ui(
MayaTemplateBuilder(registered_host()), get_main_window()
),
)
cmds.menuItem(
"Create Placeholder",
parent=builder_menu,
command=create_placeholder
)
cmds.menuItem(
"Update Placeholder",
parent=builder_menu,
command=update_placeholder
)
cmds.setParent(MENU_NAME, menu=True)

View file

@ -7,6 +7,7 @@ from ayon_core.hosts.maya.api import lib
from ayon_core.pipeline.publish import (
RepairAction,
ValidateContentsOrder,
PublishValidationError
)
@ -38,7 +39,8 @@ class ValidateRigJointsHidden(pyblish.api.InstancePlugin):
invalid = self.get_invalid(instance)
if invalid:
raise ValueError("Visible joints found: {0}".format(invalid))
raise PublishValidationError(
"Visible joints found: {0}".format(invalid))
@classmethod
def repair(cls, instance):

View file

@ -46,24 +46,5 @@ if bool(int(os.environ.get(key, "0"))):
lowestPriority=True
)
# Build a shelf.
shelf_preset = settings['maya'].get('project_shelf')
if shelf_preset:
icon_path = os.path.join(
os.environ['OPENPYPE_PROJECT_SCRIPTS'],
project_name,
"icons")
icon_path = os.path.abspath(icon_path)
for i in shelf_preset['imports']:
import_string = "from {} import {}".format(project_name, i)
print(import_string)
exec(import_string)
cmds.evalDeferred(
"mlib.shelf(name=shelf_preset['name'], iconPath=icon_path,"
" preset=shelf_preset)"
)
print("Finished OpenPype usersetup.")

View file

@ -21,10 +21,12 @@ from ayon_core.pipeline import (
AVALON_CONTAINER_ID,
get_current_asset_name,
get_current_task_name,
registered_host,
)
from ayon_core.pipeline.workfile import BuildWorkfile
from ayon_core.tools.utils import host_tools
from ayon_core.hosts.nuke import NUKE_ROOT_DIR
from ayon_core.tools.workfile_template_build import open_template_ui
from .command import viewer_update_and_undo_stop
from .lib import (
@ -55,6 +57,7 @@ from .workfile_template_builder import (
build_workfile_template,
create_placeholder,
update_placeholder,
NukeTemplateBuilder,
)
from .workio import (
open_file,
@ -313,7 +316,7 @@ def _install_menu():
lambda: BuildWorkfile().process()
)
menu_template = menu.addMenu("Template Builder") # creating template menu
menu_template = menu.addMenu("Template Builder")
menu_template.addCommand(
"Build Workfile from template",
lambda: build_workfile_template()
@ -321,6 +324,12 @@ def _install_menu():
if not ASSIST:
menu_template.addSeparator()
menu_template.addCommand(
"Open template",
lambda: open_template_ui(
NukeTemplateBuilder(registered_host()), get_main_window()
)
)
menu_template.addCommand(
"Create Place Holder",
lambda: create_placeholder()

View file

@ -7,7 +7,7 @@ from ayon_core.pipeline.workfile.workfile_template_builder import (
LoadPlaceholderItem,
CreatePlaceholderItem,
PlaceholderLoadMixin,
PlaceholderCreateMixin
PlaceholderCreateMixin,
)
from ayon_core.tools.workfile_template_build import (
WorkfileBuildPlaceholderDialog,

View file

@ -3,12 +3,11 @@ import sys
import contextlib
import traceback
from ayon_core.lib import env_value_to_bool, Logger
from ayon_core.lib import env_value_to_bool, Logger, is_in_tests
from ayon_core.addon import AddonsManager
from ayon_core.pipeline import install_host
from ayon_core.tools.utils import host_tools
from ayon_core.tools.utils import get_ayon_qt_app
from ayon_core.tests.lib import is_in_tests
from .launch_logic import ProcessLauncher, stub

View file

@ -21,7 +21,7 @@ from openpype_modules.webpublisher.lib import (
get_batch_asset_task_info,
parse_json
)
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import is_in_tests
class CollectBatchData(pyblish.api.ContextPlugin):

View file

@ -3,10 +3,9 @@ import re
import pyblish.api
from ayon_core.lib import prepare_template_data
from ayon_core.lib import prepare_template_data, is_in_tests
from ayon_core.hosts.photoshop import api as photoshop
from ayon_core.settings import get_project_settings
from ayon_core.tests.lib import is_in_tests
class CollectColorCodedInstances(pyblish.api.ContextPlugin):

View file

@ -158,6 +158,7 @@ from .ayon_info import (
is_running_from_build,
is_staging_enabled,
is_dev_mode_enabled,
is_in_tests,
)
@ -229,6 +230,8 @@ __all__ = [
"IniSettingRegistry",
"JSONSettingRegistry",
"AYONSecureRegistry",
"AYONSettingsRegistry",
"OpenPypeSecureRegistry",
"OpenPypeSettingsRegistry",
"get_local_site_id",
@ -271,6 +274,7 @@ __all__ = [
"terminal",
"get_datetime_data",
"get_timestamp",
"get_formatted_current_time",
"Logger",
@ -278,6 +282,7 @@ __all__ = [
"is_running_from_build",
"is_staging_enabled",
"is_dev_mode_enabled",
"is_in_tests",
"requests_get",
"requests_post"

View file

@ -38,6 +38,16 @@ def is_staging_enabled():
return os.getenv("AYON_USE_STAGING") == "1"
def is_in_tests():
"""Process is running in automatic tests mode.
Returns:
bool: True if running in tests.
"""
return os.environ.get("AYON_IN_TESTS") == "1"
def is_dev_mode_enabled():
"""Dev mode is enabled in AYON.

View file

@ -7,10 +7,10 @@ from datetime import datetime
from ayon_core.lib import (
env_value_to_bool,
collect_frames,
is_in_tests,
)
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
from ayon_core.tests.lib import is_in_tests
@attr.s

View file

@ -10,10 +10,10 @@ from ayon_core.lib import (
BoolDef,
NumberDef,
TextDef,
is_in_tests,
)
from ayon_core.pipeline.publish import AYONPyblishPluginMixin
from ayon_core.pipeline.farm.tools import iter_expected_files
from ayon_core.tests.lib import is_in_tests
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo

View file

@ -12,7 +12,7 @@ import pyblish.api
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import is_in_tests
class _ZipFile(ZipFile):

View file

@ -7,11 +7,11 @@ import pyblish.api
from ayon_core.lib import (
TextDef,
NumberDef,
is_in_tests,
)
from ayon_core.pipeline import (
AYONPyblishPluginMixin
)
from ayon_core.tests.lib import is_in_tests
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo

View file

@ -6,10 +6,10 @@ from datetime import datetime
import pyblish.api
from ayon_core.pipeline import AYONPyblishPluginMixin
from ayon_core.tests.lib import is_in_tests
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
from ayon_core.lib import (
is_in_tests,
BoolDef,
NumberDef
)

View file

@ -35,14 +35,14 @@ from ayon_core.lib import (
BoolDef,
NumberDef,
TextDef,
EnumDef
EnumDef,
is_in_tests,
)
from ayon_core.hosts.maya.api.lib_rendersettings import RenderSettings
from ayon_core.hosts.maya.api.lib import get_attr_in_layer
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
from ayon_core.tests.lib import is_in_tests
from ayon_core.pipeline.farm.tools import iter_expected_files

View file

@ -3,7 +3,7 @@ import attr
from datetime import datetime
from ayon_core.pipeline import PublishXmlValidationError
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import is_in_tests
from openpype_modules.deadline import abstract_submit_deadline
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo

View file

@ -10,8 +10,8 @@ import pyblish.api
from ayon_core.pipeline.publish import (
AYONPyblishPluginMixin
)
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import (
is_in_tests,
BoolDef,
NumberDef
)

View file

@ -12,8 +12,7 @@ from ayon_core.client import (
get_last_version_by_subset_name,
)
from ayon_core.pipeline import publish
from ayon_core.lib import EnumDef
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import EnumDef, is_in_tests
from ayon_core.pipeline.version_start import get_versioning_start
from ayon_core.pipeline.farm.pyblish_functions import (

View file

@ -13,8 +13,7 @@ from ayon_core.client import (
get_last_version_by_subset_name,
)
from ayon_core.pipeline import publish
from ayon_core.lib import EnumDef
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import EnumDef, is_in_tests
from ayon_core.pipeline.version_start import get_versioning_start
from ayon_core.pipeline.farm.pyblish_functions import (

View file

@ -10,7 +10,12 @@ from datetime import datetime
import pyblish.api
from ayon_core.lib import BoolDef, NumberDef, is_running_from_build
from ayon_core.lib import (
BoolDef,
NumberDef,
is_running_from_build,
is_in_tests,
)
from ayon_core.lib.execute import run_ayon_launcher_process
from ayon_core.modules.royalrender.api import Api as rrApi
from ayon_core.modules.royalrender.rr_job import (
@ -22,7 +27,6 @@ from ayon_core.modules.royalrender.rr_job import (
from ayon_core.pipeline import AYONPyblishPluginMixin
from ayon_core.pipeline.publish import KnownPublishError
from ayon_core.pipeline.publish.lib import get_published_workfile_instance
from ayon_core.tests.lib import is_in_tests
class BaseCreateRoyalRenderJob(pyblish.api.InstancePlugin,

View file

@ -19,10 +19,10 @@ from ayon_core.client import (
get_asset_name_identifier,
get_ayon_server_api_connection,
)
from ayon_core.lib import is_in_tests
from ayon_core.lib.events import emit_event
from ayon_core.addon import load_addons, AddonsManager
from ayon_core.settings import get_project_settings
from ayon_core.tests.lib import is_in_tests
from .publish.lib import filter_pyblish_plugins
from .anatomy import Anatomy

View file

@ -553,6 +553,12 @@ class AbstractTemplateBuilder(object):
self.clear_shared_populate_data()
def open_template(self):
"""Open template file with registered host."""
template_preset = self.get_template_preset()
template_path = template_preset["path"]
self.host.open_file(template_path)
@abstractmethod
def import_template(self, template_path):
"""

View file

@ -5,7 +5,7 @@ import shutil
import pyblish.api
import re
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import is_in_tests
class CleanUp(pyblish.api.InstancePlugin):

View file

@ -61,7 +61,10 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin):
("AYON_FOLDER_PATH", asset_name),
("AYON_TASK_NAME", task_name)
):
os.environ[key] = value
if value is None:
os.environ.pop(key, None)
else:
os.environ[key] = value
def create_instance(
self,

View file

@ -1,8 +1,7 @@
import os
import pyblish.api
from ayon_core.lib import get_version_from_path
from ayon_core.tests.lib import is_in_tests
from ayon_core.lib import get_version_from_path, is_in_tests
from ayon_core.pipeline import KnownPublishError

View file

@ -1271,7 +1271,7 @@
"icon": "{}/app_icons/harmony.png",
"host_name": "harmony",
"environment": {
"AVALON_HARMONY_WORKFILES_ON_LAUNCH": "1"
"AYON_HARMONY_WORKFILES_ON_LAUNCH": "1"
},
"variants": {
"21": {

View file

@ -1,4 +0,0 @@
Tests for Pype
--------------
Trigger by:
`pype test --pype`

View file

@ -1,88 +0,0 @@
import os
import sys
import shutil
import tempfile
import contextlib
import pyblish
import pyblish.plugin
from pyblish.vendor import six
# Setup
HOST = 'python'
FAMILY = 'test.family'
REGISTERED = pyblish.plugin.registered_paths()
PACKAGEPATH = pyblish.lib.main_package_path()
ENVIRONMENT = os.environ.get("PYBLISHPLUGINPATH", "")
PLUGINPATH = os.path.join(PACKAGEPATH, '..', 'tests', 'plugins')
def setup():
pyblish.plugin.deregister_all_paths()
def setup_empty():
"""Disable all plug-ins"""
setup()
pyblish.plugin.deregister_all_plugins()
pyblish.plugin.deregister_all_paths()
pyblish.plugin.deregister_all_hosts()
pyblish.plugin.deregister_all_callbacks()
pyblish.plugin.deregister_all_targets()
pyblish.api.deregister_all_discovery_filters()
def teardown():
"""Restore previously REGISTERED paths"""
pyblish.plugin.deregister_all_paths()
for path in REGISTERED:
pyblish.plugin.register_plugin_path(path)
os.environ["PYBLISHPLUGINPATH"] = ENVIRONMENT
pyblish.api.deregister_all_plugins()
pyblish.api.deregister_all_hosts()
pyblish.api.deregister_all_discovery_filters()
pyblish.api.deregister_test()
pyblish.api.__init__()
@contextlib.contextmanager
def captured_stdout():
"""Temporarily reassign stdout to a local variable"""
try:
sys.stdout = six.StringIO()
yield sys.stdout
finally:
sys.stdout = sys.__stdout__
@contextlib.contextmanager
def captured_stderr():
"""Temporarily reassign stderr to a local variable"""
try:
sys.stderr = six.StringIO()
yield sys.stderr
finally:
sys.stderr = sys.__stderr__
@contextlib.contextmanager
def tempdir():
"""Provide path to temporary directory"""
try:
tempdir = tempfile.mkdtemp()
yield tempdir
finally:
shutil.rmtree(tempdir)
def is_in_tests():
"""Returns if process is running in automatic tests mode.
In tests mode different source DB is used, some plugins might be disabled
etc.
"""
return os.environ.get("IS_TEST") == '1'

View file

@ -1,288 +0,0 @@
import pymongo
import bson
import random
from datetime import datetime
import os
class TestPerformance():
'''
Class for testing performance of representation and their 'files'
parts.
Discussion is if embedded array:
'files' : [ {'_id': '1111', 'path':'....},
{'_id'...}]
OR documents:
'files' : {
'1111': {'path':'....'},
'2222': {'path':'...'}
}
is faster.
Current results:
without additional partial index documents is 3x faster
With index is array 50x faster then document
Partial index something like:
db.getCollection('performance_test').createIndex
({'files._id': 1},
{partialFilterExpresion: {'files': {'$exists': true}}})
!DIDNT work for me, had to create manually in Compass
'''
MONGO_URL = 'mongodb://localhost:27017'
MONGO_DB = 'performance_test'
MONGO_COLLECTION = 'performance_test'
MAX_FILE_SIZE_B = 5000
MAX_NUMBER_OF_SITES = 50
ROOT_DIR = "C:/projects"
inserted_ids = []
def __init__(self, version='array'):
'''
It creates and fills collection, based on value of 'version'.
:param version: 'array' - files as embedded array,
'doc' - as document
'''
self.client = pymongo.MongoClient(self.MONGO_URL)
self.db = self.client[self.MONGO_DB]
self.collection_name = self.MONGO_COLLECTION
self.version = version
if self.version != 'array':
self.collection_name = self.MONGO_COLLECTION + '_doc'
self.collection = self.db[self.collection_name]
self.ids = [] # for testing
self.inserted_ids = []
def prepare(self, no_of_records=100000, create_files=False):
'''
Produce 'no_of_records' of representations with 'files' segment.
It depends on 'version' value in constructor, 'arrray' or 'doc'
:return:
'''
print('Purging {} collection'.format(self.collection_name))
self.collection.delete_many({})
id = bson.objectid.ObjectId()
insert_recs = []
for i in range(no_of_records):
file_id = bson.objectid.ObjectId()
file_id2 = bson.objectid.ObjectId()
file_id3 = bson.objectid.ObjectId()
self.inserted_ids.extend([file_id, file_id2, file_id3])
version_str = "v{:03d}".format(i + 1)
file_name = "test_Cylinder_workfileLookdev_{}.mb".\
format(version_str)
document = {"files": self.get_files(self.version, i + 1,
file_id, file_id2, file_id3,
create_files)
,
"context": {
"subset": "workfileLookdev",
"username": "petrk",
"task": "lookdev",
"family": "workfile",
"hierarchy": "Assets",
"project": {"code": "test", "name": "Test"},
"version": i + 1,
"asset": "Cylinder",
"representation": "mb",
"root": self.ROOT_DIR
},
"dependencies": [],
"name": "mb",
"parent": {"oid": '{}'.format(id)},
"data": {
"path": "C:\\projects\\test_performance\\Assets\\Cylinder\\publish\\workfile\\workfileLookdev\\{}\\{}".format(version_str, file_name), # noqa: E501
"template": "{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{representation}" # noqa: E501
},
"type": "representation",
"schema": "openpype:representation-2.0"
}
insert_recs.append(document)
print('Prepared {} records in {} collection'.
format(no_of_records, self.collection_name))
self.collection.insert_many(insert_recs)
# TODO refactore to produce real array and not needeing ugly regex
self.collection.insert_one({"inserted_id": self.inserted_ids})
print('-' * 50)
def run(self, queries=1000, loops=3):
'''
Run X'queries' that are searching collection Y'loops' times
:param queries: how many times do ..find(...)
:param loops: loop of testing X queries
:return: None
'''
print('Testing version {} on {}'.format(self.version,
self.collection_name))
print('Queries rung {} in {} loops'.format(queries, loops))
inserted_ids = list(self.collection.
find({"inserted_id": {"$exists": True}}))
import re
self.ids = re.findall("'[0-9a-z]*'", str(inserted_ids))
import time
found_cnt = 0
for _ in range(loops):
print('Starting loop {}'.format(_))
start = time.time()
for _ in range(queries):
# val = random.choice(self.ids)
# val = val.replace("'", '')
val = random.randint(0, 50)
print(val)
if (self.version == 'array'):
# prepared for partial index, without 'files': exists
# wont engage
found = self.collection.\
find({'files': {"$exists": True},
'files.sites.name': "local_{}".format(val)}).\
count()
else:
key = "files.{}".format(val)
found = self.collection.find_one({key: {"$exists": True}})
print("found {} records".format(found))
# if found:
# found_cnt += len(list(found))
end = time.time()
print('duration per loop {}'.format(end - start))
print("found_cnt {}".format(found_cnt))
def get_files(self, mode, i, file_id, file_id2, file_id3,
create_files=False):
'''
Wrapper to decide if 'array' or document version should be used
:param mode: 'array'|'doc'
:param i: step number
:param file_id: ObjectId of first dummy file
:param file_id2: ..
:param file_id3: ..
:return:
'''
if mode == 'array':
return self.get_files_array(i, file_id, file_id2, file_id3,
create_files)
else:
return self.get_files_doc(i, file_id, file_id2, file_id3)
def get_files_array(self, i, file_id, file_id2, file_id3,
create_files=False):
ret = [
{
"path": "{root[work]}" + "{root[work]}/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/v{:03d}/test_Cylinder_A_workfileLookdev_v{:03d}.dat".format(i, i), # noqa: E501
"_id": '{}'.format(file_id),
"hash": "temphash",
"sites": self.get_sites(self.MAX_NUMBER_OF_SITES),
"size": random.randint(0, self.MAX_FILE_SIZE_B)
},
{
"path": "{root[work]}" + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/v{:03d}/test_Cylinder_B_workfileLookdev_v{:03d}.dat".format(i, i), # noqa: E501
"_id": '{}'.format(file_id2),
"hash": "temphash",
"sites": self.get_sites(self.MAX_NUMBER_OF_SITES),
"size": random.randint(0, self.MAX_FILE_SIZE_B)
},
{
"path": "{root[work]}" + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/v{:03d}/test_Cylinder_C_workfileLookdev_v{:03d}.dat".format(i, i), # noqa: E501
"_id": '{}'.format(file_id3),
"hash": "temphash",
"sites": self.get_sites(self.MAX_NUMBER_OF_SITES),
"size": random.randint(0, self.MAX_FILE_SIZE_B)
}
]
if create_files:
for f in ret:
path = f.get("path").replace("{root[work]}", self.ROOT_DIR)
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'wb') as fp:
fp.write(os.urandom(f.get("size")))
return ret
def get_files_doc(self, i, file_id, file_id2, file_id3):
ret = {}
ret['{}'.format(file_id)] = {
"path": "{root[work]}" +
"/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" # noqa: E501
"v{:03d}/test_CylinderA_workfileLookdev_v{:03d}.mb".format(i, i), # noqa: E501
"hash": "temphash",
"sites": ["studio"],
"size": 87236
}
ret['{}'.format(file_id2)] = {
"path": "{root[work]}" +
"/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" # noqa: E501
"v{:03d}/test_CylinderB_workfileLookdev_v{:03d}.mb".format(i, i), # noqa: E501
"hash": "temphash",
"sites": ["studio"],
"size": 87236
}
ret['{}'.format(file_id3)] = {
"path": "{root[work]}" +
"/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" # noqa: E501
"v{:03d}/test_CylinderC_workfileLookdev_v{:03d}.mb".format(i, i), # noqa: E501
"hash": "temphash",
"sites": ["studio"],
"size": 87236
}
return ret
def get_sites(self, number_of_sites=50):
"""
Return array of sites declaration.
Currently on 1st site has "created_dt" fillled, which should
trigger upload to 'gdrive' site.
'gdrive' site is appended, its destination for syncing for
Sync Server
Args:
number_of_sites:
Returns:
"""
sites = []
for i in range(number_of_sites):
site = {'name': "local_{}".format(i)}
# do not create null 'created_dt' field, Mongo doesnt like it
if i == 0:
site['created_dt'] = datetime.now()
sites.append(site)
sites.append({'name': "gdrive"})
return sites
if __name__ == '__main__':
tp = TestPerformance('array')
tp.prepare(no_of_records=10000, create_files=True)
# tp.run(10, 3)
# print('-'*50)
#
# tp = TestPerformance('doc')
# tp.prepare() # enable to prepare data
# tp.run(1000, 3)

View file

@ -1,43 +0,0 @@
from ayon_core.pipeline import (
install_host,
LegacyCreator,
register_creator_plugin,
discover_creator_plugins,
)
class MyTestCreator(LegacyCreator):
my_test_property = "A"
def __init__(self, name, asset, options=None, data=None):
super(MyTestCreator, self).__init__(self, name, asset,
options=None, data=None)
# this is hack like no other - we need to inject our own avalon host
# and bypass all its validation. Avalon hosts are modules that needs
# `ls` callable as attribute. Voila:
class Test:
__name__ = "test"
ls = len
@staticmethod
def install():
register_creator_plugin(MyTestCreator)
def test_avalon_plugin_presets(monkeypatch, printer):
install_host(Test)
plugins = discover_creator_plugins()
printer("Test if we got our test plugin")
assert MyTestCreator in plugins
for p in plugins:
if p.__name__ == "MyTestCreator":
printer("Test if we have overridden existing property")
assert p.my_test_property == "B"
printer("Test if we have overridden superclass property")
assert p.active is False
printer("Test if we have added new property")
assert p.new_property == "new"

View file

@ -1,25 +0,0 @@
# Test for backward compatibility of restructure of lib.py into lib library
# Contains simple imports that should still work
def test_backward_compatibility(printer):
printer("Test if imports still work")
try:
from ayon_core.lib import execute_hook
from ayon_core.lib import PypeHook
from ayon_core.lib import ApplicationLaunchFailed
from ayon_core.lib import get_ffmpeg_tool_path
from ayon_core.lib import get_last_version_from_path
from ayon_core.lib import get_paths_from_environ
from ayon_core.lib import get_version_from_path
from ayon_core.lib import version_up
from ayon_core.lib import get_ffprobe_streams
from ayon_core.lib import source_hash
from ayon_core.lib import run_subprocess
except ImportError as e:
raise

View file

@ -1,60 +0,0 @@
import os
import pyblish.api
import pyblish.util
import pyblish.plugin
from ayon_core.pipeline.publish.lib import filter_pyblish_plugins
from . import lib
def test_pyblish_plugin_filter_modifier(printer, monkeypatch):
"""
Test if pyblish filter can filter and modify plugins on-the-fly.
"""
lib.setup_empty()
monkeypatch.setitem(os.environ, 'PYBLISHPLUGINPATH', '')
plugins = pyblish.api.registered_plugins()
printer("Test if we have no registered plugins")
assert len(plugins) == 0
paths = pyblish.api.registered_paths()
printer("Test if we have no registered plugin paths")
assert len(paths) == 0
class MyTestPlugin(pyblish.api.InstancePlugin):
my_test_property = 1
label = "Collect Renderable Camera(s)"
hosts = ["test"]
families = ["default"]
pyblish.api.register_host("test")
pyblish.api.register_plugin(MyTestPlugin)
pyblish.api.register_discovery_filter(filter_pyblish_plugins)
plugins = pyblish.api.discover()
printer("Test if only one plugin was discovered")
assert len(plugins) == 1
printer("Test if properties are modified correctly")
assert plugins[0].label == "loaded from preset"
assert plugins[0].families == ["changed", "by", "preset"]
assert plugins[0].optional is True
lib.teardown()
def test_pyblish_plugin_filter_removal(monkeypatch):
""" Test that plugin can be removed by filter """
lib.setup_empty()
monkeypatch.setitem(os.environ, 'PYBLISHPLUGINPATH', '')
plugins = pyblish.api.registered_plugins()
class MyTestRemovedPlugin(pyblish.api.InstancePlugin):
my_test_property = 1
label = "Collect Renderable Camera(s)"
hosts = ["test"]
families = ["default"]
pyblish.api.register_host("test")
pyblish.api.register_plugin(MyTestRemovedPlugin)
pyblish.api.register_discovery_filter(filter_pyblish_plugins)
plugins = pyblish.api.discover()
assert len(plugins) == 0

View file

@ -1,5 +1,8 @@
from .window import WorkfileBuildPlaceholderDialog
from .lib import open_template_ui
__all__ = (
"WorkfileBuildPlaceholderDialog",
"open_template_ui"
)

View file

@ -0,0 +1,28 @@
import traceback
from qtpy import QtWidgets
from ayon_core.tools.utils.dialogs import show_message_dialog
def open_template_ui(builder, main_window):
"""Open template from `builder`
Asks user about overwriting current scene and feedsback exceptions.
"""
result = QtWidgets.QMessageBox.question(
main_window,
"Opening template",
"Caution! You will loose unsaved changes.\nDo you want to continue?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No
)
if result == QtWidgets.QMessageBox.Yes:
try:
builder.open_template()
except Exception:
show_message_dialog(
title="Template Load Failed",
message="".join(traceback.format_exc()),
parent=main_window,
level="critical"
)

View file

@ -10,8 +10,6 @@ wsrpc_aiohttp = "^3.1.1" # websocket server
Click = "^8"
clique = "1.6.*"
jsonschema = "^2.6.0"
pymongo = "^3.11.2"
log4mongo = "^1.7"
pyblish-base = "^1.8.11"
pynput = "^1.7.2" # Timers manager - TODO remove
speedcopy = "^2.1"