Skip to content

Commit

Permalink
Merge pull request #779 from ynput/enhancement/python-2-deprecation
Browse files Browse the repository at this point in the history
Chore: Python 2 deprecation in lib
  • Loading branch information
iLLiCiTiT authored Aug 28, 2024
2 parents 6a07de6 + f924166 commit d9cf63b
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 163 deletions.
2 changes: 1 addition & 1 deletion client/ayon_core/lib/attribute_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ def convert_value(self, value):
return self.default


class FileDefItem(object):
class FileDefItem:
def __init__(
self, directory, filenames, frames=None, template=None
):
Expand Down
10 changes: 4 additions & 6 deletions client/ayon_core/lib/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import weakref
from uuid import uuid4

from .python_2_comp import WeakMethod
from .python_module_tools import is_func_signature_supported


Expand All @@ -18,7 +17,7 @@ class MissingEventSystem(Exception):

def _get_func_ref(func):
if inspect.ismethod(func):
return WeakMethod(func)
return weakref.WeakMethod(func)
return weakref.ref(func)


Expand Down Expand Up @@ -123,7 +122,7 @@ def validate_signature(self, *args, **kwargs):
)


class EventCallback(object):
class EventCallback:
"""Callback registered to a topic.
The callback function is registered to a topic. Topic is a string which
Expand Down Expand Up @@ -380,8 +379,7 @@ def _validate_ref(self):
self._partial_func = None


# Inherit from 'object' for Python 2 hosts
class Event(object):
class Event:
"""Base event object.
Can be used for any event because is not specific. Only required argument
Expand Down Expand Up @@ -488,7 +486,7 @@ def from_data(cls, event_data, event_system=None):
return obj


class EventSystem(object):
class EventSystem:
"""Encapsulate event handling into an object.
System wraps registered callbacks and triggered events into single object,
Expand Down
2 changes: 1 addition & 1 deletion client/ayon_core/lib/file_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DuplicateDestinationError(ValueError):
"""


class FileTransaction(object):
class FileTransaction:
"""File transaction with rollback options.
The file transaction is a three-step process.
Expand Down
20 changes: 2 additions & 18 deletions client/ayon_core/lib/local_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,11 @@
import os
import json
import platform
import configparser
import warnings
from datetime import datetime
from abc import ABC, abstractmethod

# disable lru cache in Python 2
try:
from functools import lru_cache
except ImportError:
def lru_cache(maxsize):
def max_size(func):
def wrapper(*args, **kwargs):
value = func(*args, **kwargs)
return value
return wrapper
return max_size

# ConfigParser was renamed in python3 to configparser
try:
import configparser
except ImportError:
import ConfigParser as configparser
from functools import lru_cache

import appdirs
import ayon_api
Expand Down
4 changes: 2 additions & 2 deletions client/ayon_core/lib/path_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def __init__(self, template, missing_keys, invalid_types):
)


class StringTemplate(object):
class StringTemplate:
"""String that can be formatted."""
def __init__(self, template):
if not isinstance(template, str):
Expand Down Expand Up @@ -410,7 +410,7 @@ def add_invalid_type(self, key, value):
self._invalid_types[key] = type(value)


class FormatObject(object):
class FormatObject:
"""Object that can be used for formatting.
This is base that is valid for to be used in 'StringTemplate' value.
Expand Down
53 changes: 13 additions & 40 deletions client/ayon_core/lib/python_2_comp.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,17 @@
# Deprecated file
# - the file container 'WeakMethod' implementation for Python 2 which is not
# needed anymore.
import warnings
import weakref


WeakMethod = getattr(weakref, "WeakMethod", None)
WeakMethod = weakref.WeakMethod

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. """

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 _WeakCallable(self._getobj(), self._meth)

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()
warnings.warn(
(
"'ayon_core.lib.python_2_comp' is deprecated."
"Please use 'weakref.WeakMethod'."
),
DeprecationWarning,
stacklevel=2
)
129 changes: 34 additions & 95 deletions client/ayon_core/lib/python_module_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,30 @@
import inspect
import logging

import six

log = logging.getLogger(__name__)


def import_filepath(filepath, module_name=None):
"""Import python file as python module.
Python 2 and Python 3 compatibility.
Args:
filepath(str): Path to python file.
module_name(str): Name of loaded module. Only for Python 3. By default
filepath (str): Path to python file.
module_name (str): Name of loaded module. Only for Python 3. By default
is filled with filename of filepath.
"""
if module_name is None:
module_name = os.path.splitext(os.path.basename(filepath))[0]

# Make sure it is not 'unicode' in Python 2
module_name = str(module_name)

# Prepare module object where content of file will be parsed
module = types.ModuleType(module_name)
module.__file__ = filepath

if six.PY3:
# Use loader so module has full specs
module_loader = importlib.machinery.SourceFileLoader(
module_name, filepath
)
module_loader.exec_module(module)
else:
# Execute module code and store content to module
with open(filepath) as _stream:
# Execute content and store it to module object
six.exec_(_stream.read(), module.__dict__)

# Use loader so module has full specs
module_loader = importlib.machinery.SourceFileLoader(
module_name, filepath
)
module_loader.exec_module(module)
return module


Expand Down Expand Up @@ -139,35 +126,31 @@ def classes_from_module(superclass, module):
return classes


def _import_module_from_dirpath_py2(dirpath, module_name, dst_module_name):
"""Import passed dirpath as python module using `imp`."""
if dst_module_name:
full_module_name = "{}.{}".format(dst_module_name, module_name)
dst_module = sys.modules[dst_module_name]
else:
full_module_name = module_name
dst_module = None

if full_module_name in sys.modules:
return sys.modules[full_module_name]

import imp
def import_module_from_dirpath(dirpath, folder_name, dst_module_name=None):
"""Import passed directory as a python module.
fp, pathname, description = imp.find_module(module_name, [dirpath])
module = imp.load_module(full_module_name, fp, pathname, description)
if dst_module is not None:
setattr(dst_module, module_name, module)
Imported module can be assigned as a child attribute of already loaded
module from `sys.modules` if has support of `setattr`. That is not default
behavior of python modules so parent module must be a custom module with
that ability.
return module
It is not possible to reimport already cached module. If you need to
reimport module you have to remove it from caches manually.
Args:
dirpath (str): Parent directory path of loaded folder.
folder_name (str): Folder name which should be imported inside passed
directory.
dst_module_name (str): Parent module name under which can be loaded
module added.
def _import_module_from_dirpath_py3(dirpath, module_name, dst_module_name):
"""Import passed dirpath as python module using Python 3 modules."""
"""
# Import passed dirpath as python module
if dst_module_name:
full_module_name = "{}.{}".format(dst_module_name, module_name)
full_module_name = "{}.{}".format(dst_module_name, folder_name)
dst_module = sys.modules[dst_module_name]
else:
full_module_name = module_name
full_module_name = folder_name
dst_module = None

# Skip import if is already imported
Expand All @@ -191,7 +174,7 @@ def _import_module_from_dirpath_py3(dirpath, module_name, dst_module_name):
# Store module to destination module and `sys.modules`
# WARNING this mus be done before module execution
if dst_module is not None:
setattr(dst_module, module_name, module)
setattr(dst_module, folder_name, module)

sys.modules[full_module_name] = module

Expand All @@ -201,37 +184,6 @@ def _import_module_from_dirpath_py3(dirpath, module_name, dst_module_name):
return module


def import_module_from_dirpath(dirpath, folder_name, dst_module_name=None):
"""Import passed directory as a python module.
Python 2 and 3 compatible.
Imported module can be assigned as a child attribute of already loaded
module from `sys.modules` if has support of `setattr`. That is not default
behavior of python modules so parent module must be a custom module with
that ability.
It is not possible to reimport already cached module. If you need to
reimport module you have to remove it from caches manually.
Args:
dirpath(str): Parent directory path of loaded folder.
folder_name(str): Folder name which should be imported inside passed
directory.
dst_module_name(str): Parent module name under which can be loaded
module added.
"""
if six.PY3:
module = _import_module_from_dirpath_py3(
dirpath, folder_name, dst_module_name
)
else:
module = _import_module_from_dirpath_py2(
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.
Expand Down Expand Up @@ -275,25 +227,12 @@ def is_func_signature_supported(func, *args, **kwargs):
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
"""
sig = inspect.signature(func)
try:
sig.bind(*args, **kwargs)
return True
except TypeError:
pass
return False

0 comments on commit d9cf63b

Please sign in to comment.