General: Lib code cleanup (#5003)

* implemented 'is_func_signature_supported' function

* 'WeakMethod' can be imported from 'python_2_comp' all the time

* simplified events logic for callback registration

* modified docstrings in publish lib

* removed unused imports

* fixed 'run_openpype_process' docstring
This commit is contained in:
Jakub Trllo 2023-05-24 10:51:35 +02:00 committed by GitHub
parent a03a0cc886
commit 248336bb0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 129 additions and 88 deletions

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# flake8: noqa E402
"""Pype module API."""
"""OpenPype lib functions."""
# add vendor to sys path based on Python version
import sys
import os
@ -94,7 +94,8 @@ from .python_module_tools import (
modules_from_path,
recursive_bases_from_class,
classes_from_module,
import_module_from_dirpath
import_module_from_dirpath,
is_func_signature_supported,
)
from .profiles_filtering import (
@ -243,6 +244,7 @@ __all__ = [
"recursive_bases_from_class",
"classes_from_module",
"import_module_from_dirpath",
"is_func_signature_supported",
"get_transcode_temp_directory",
"should_convert_for_ffmpeg",

View file

@ -6,10 +6,9 @@ import inspect
import logging
import weakref
from uuid import uuid4
try:
from weakref import WeakMethod
except Exception:
from openpype.lib.python_2_comp import WeakMethod
from .python_2_comp import WeakMethod
from .python_module_tools import is_func_signature_supported
class MissingEventSystem(Exception):
@ -80,40 +79,8 @@ class EventCallback(object):
# Get expected arguments from function spec
# - positional arguments are always preferred
expect_args = False
expect_kwargs = False
fake_event = "fake"
if hasattr(inspect, "signature"):
# Python 3 using 'Signature' object where we try to bind arg
# or kwarg. Using signature is recommended approach based on
# documentation.
sig = inspect.signature(func)
try:
sig.bind(fake_event)
expect_args = True
except TypeError:
pass
try:
sig.bind(event=fake_event)
expect_kwargs = True
except TypeError:
pass
else:
# In Python 2 'signature' is not available so 'getcallargs' is used
# - 'getcallargs' is marked as deprecated since Python 3.0
try:
inspect.getcallargs(func, fake_event)
expect_args = True
except TypeError:
pass
try:
inspect.getcallargs(func, event=fake_event)
expect_kwargs = True
except TypeError:
pass
expect_args = is_func_signature_supported(func, "fake")
expect_kwargs = is_func_signature_supported(func, event="fake")
self._func_ref = func_ref
self._func_name = func_name

View file

@ -190,7 +190,7 @@ def run_openpype_process(*args, **kwargs):
Example:
```
run_openpype_process("run", "<path to .py script>")
run_detached_process("run", "<path to .py script>")
```
Args:

View file

@ -1,41 +1,44 @@
import weakref
class _weak_callable:
def __init__(self, obj, func):
self.im_self = obj
self.im_func = func
WeakMethod = getattr(weakref, "WeakMethod", None)
def __call__(self, *args, **kws):
if self.im_self is None:
return self.im_func(*args, **kws)
else:
return self.im_func(self.im_self, *args, **kws)
if WeakMethod is None:
class _WeakCallable:
def __init__(self, obj, func):
self.im_self = obj
self.im_func = func
def __call__(self, *args, **kws):
if self.im_self is None:
return self.im_func(*args, **kws)
else:
return self.im_func(self.im_self, *args, **kws)
class WeakMethod:
""" Wraps a function or, more importantly, a bound method in
a way that allows a bound method's object to be GCed, while
providing the same interface as a normal weak reference. """
class WeakMethod:
""" Wraps a function or, more importantly, a bound method in
a way that allows a bound method's object to be GCed, while
providing the same interface as a normal weak reference. """
def __init__(self, fn):
try:
self._obj = weakref.ref(fn.im_self)
self._meth = fn.im_func
except AttributeError:
# It's not a bound method
self._obj = None
self._meth = fn
def __init__(self, fn):
try:
self._obj = weakref.ref(fn.im_self)
self._meth = fn.im_func
except AttributeError:
# It's not a bound method
self._obj = None
self._meth = fn
def __call__(self):
if self._dead():
return None
return _weak_callable(self._getobj(), self._meth)
def __call__(self):
if self._dead():
return None
return _WeakCallable(self._getobj(), self._meth)
def _dead(self):
return self._obj is not None and self._obj() is None
def _dead(self):
return self._obj is not None and self._obj() is None
def _getobj(self):
if self._obj is None:
return None
return self._obj()
def _getobj(self):
if self._obj is None:
return None
return self._obj()

View file

@ -230,3 +230,70 @@ def import_module_from_dirpath(dirpath, folder_name, dst_module_name=None):
dirpath, folder_name, dst_module_name
)
return module
def is_func_signature_supported(func, *args, **kwargs):
"""Check if a function signature supports passed args and kwargs.
This check does not actually call the function, just look if function can
be called with the arguments.
Notes:
This does NOT check if the function would work with passed arguments
only if they can be passed in. If function have *args, **kwargs
in paramaters, this will always return 'True'.
Example:
>>> def my_function(my_number):
... return my_number + 1
...
>>> is_func_signature_supported(my_function, 1)
True
>>> is_func_signature_supported(my_function, 1, 2)
False
>>> is_func_signature_supported(my_function, my_number=1)
True
>>> is_func_signature_supported(my_function, number=1)
False
>>> is_func_signature_supported(my_function, "string")
True
>>> def my_other_function(*args, **kwargs):
... my_function(*args, **kwargs)
...
>>> is_func_signature_supported(
... my_other_function,
... "string",
... 1,
... other=None
... )
True
Args:
func (function): A function where the signature should be tested.
*args (tuple[Any]): Positional arguments for function signature.
**kwargs (dict[str, Any]): Keyword arguments for function signature.
Returns:
bool: Function can pass in arguments.
"""
if hasattr(inspect, "signature"):
# Python 3 using 'Signature' object where we try to bind arg
# or kwarg. Using signature is recommended approach based on
# documentation.
sig = inspect.signature(func)
try:
sig.bind(*args, **kwargs)
return True
except TypeError:
pass
else:
# In Python 2 'signature' is not available so 'getcallargs' is used
# - 'getcallargs' is marked as deprecated since Python 3.0
try:
inspect.getcallargs(func, *args, **kwargs)
return True
except TypeError:
pass
return False

View file

@ -1,12 +1,10 @@
import os
import sys
import types
import inspect
import copy
import tempfile
import xml.etree.ElementTree
import six
import pyblish.util
import pyblish.plugin
import pyblish.api
@ -42,7 +40,9 @@ def get_template_name_profiles(
Args:
project_name (str): Name of project where to look for templates.
project_settings(Dic[str, Any]): Prepared project settings.
project_settings (Dict[str, Any]): Prepared project settings.
logger (Optional[logging.Logger]): Logger object to be used instead
of default logger.
Returns:
List[Dict[str, Any]]: Publish template profiles.
@ -103,7 +103,9 @@ def get_hero_template_name_profiles(
Args:
project_name (str): Name of project where to look for templates.
project_settings(Dic[str, Any]): Prepared project settings.
project_settings (Dict[str, Any]): Prepared project settings.
logger (Optional[logging.Logger]): Logger object to be used instead
of default logger.
Returns:
List[Dict[str, Any]]: Publish template profiles.
@ -172,9 +174,10 @@ def get_publish_template_name(
project_name (str): Name of project where to look for settings.
host_name (str): Name of host integration.
family (str): Family for which should be found template.
task_name (str): Task name on which is intance working.
task_type (str): Task type on which is intance working.
project_setting (Dict[str, Any]): Prepared project settings.
task_name (str): Task name on which is instance working.
task_type (str): Task type on which is instance working.
project_settings (Dict[str, Any]): Prepared project settings.
hero (bool): Template is for hero version publishing.
logger (logging.Logger): Custom logger used for 'filter_profiles'
function.
@ -264,19 +267,18 @@ def load_help_content_from_plugin(plugin):
def publish_plugins_discover(paths=None):
"""Find and return available pyblish plug-ins
Overridden function from `pyblish` module to be able collect crashed files
and reason of their crash.
Overridden function from `pyblish` module to be able to collect
crashed files and reason of their crash.
Arguments:
paths (list, optional): Paths to discover plug-ins from.
If no paths are provided, all paths are searched.
"""
# The only difference with `pyblish.api.discover`
result = DiscoverResult(pyblish.api.Plugin)
plugins = dict()
plugins = {}
plugin_names = []
allow_duplicates = pyblish.plugin.ALLOW_DUPLICATES
@ -302,7 +304,7 @@ def publish_plugins_discover(paths=None):
mod_name, mod_ext = os.path.splitext(fname)
if not mod_ext == ".py":
if mod_ext != ".py":
continue
try:
@ -533,10 +535,10 @@ def find_close_plugin(close_plugin_name, log):
def remote_publish(log, close_plugin_name=None, raise_error=False):
"""Loops through all plugins, logs to console. Used for tests.
Args:
log (openpype.lib.Logger)
close_plugin_name (str): name of plugin with responsibility to
close host app
Args:
log (Logger)
close_plugin_name (str): name of plugin with responsibility to
close host app
"""
# Error exit as soon as any error occurs.
error_format = "Failed {plugin.__name__}: {error} -- {error.traceback}"