Skip to content

Instantly share code, notes, and snippets.

@jpsutton
Created May 9, 2019 14:22
Show Gist options
  • Save jpsutton/3d7a3eabf353d38811b960e3ed517aa1 to your computer and use it in GitHub Desktop.
Save jpsutton/3d7a3eabf353d38811b960e3ed517aa1 to your computer and use it in GitHub Desktop.
Decorate a method to provide it similar "private" semantics as other languages (e.g., C++)
from functools import wraps
import inspect
# This defines a decorator that can be used on an instance method, to stop it from being inherited by child classes
class _private_method(object):
def __init__(self, decorated):
self._decorated = decorated
def __set_name__(self, owner, name):
@wraps(self._decorated)
def wrap_func(self, *args, **kwargs):
caller_frame = inspect.currentframe().f_back
# Indicates that the caller is not even inside an object (assumes developer is sane; uses the name 'self')
if 'self' not in caller_frame.f_locals:
raise RuntimeError("Unable to access private method '%s'" % wrap_func.__funcname__)
caller_func = getattr(caller_frame.f_locals['self'], caller_frame.f_code.co_name)
# Check to see if the calling function is a member of __orig_class__
if not getattr(caller_func, "__orig_class__", None) or caller_func.__orig_class__ != wrap_func.__orig_class__:
raise RuntimeError("Unable to access private method '%s'" % wrap_func.__funcname__)
return wrap_func.__orig_func__(self, *args, **kwargs)
# Set some attributes on wrap_func so that wrap_func can test various things
wrap_func.__funcname__ = name
wrap_func.__orig_func__ = self._decorated
wrap_func.__orig_class__ = owner
# Mark each member function in this class with the original class name, so we can test it inside wrap_func
for attr_name in dir(owner):
attr = getattr(owner, attr_name)
if inspect.isfunction(attr):
attr.__orig_class__ = owner
setattr(owner, name, wrap_func)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment