Skip to content

Instantly share code, notes, and snippets.

@codekiln
Forked from runekaagaard/list_all_model_signals.py
Last active November 17, 2017 20:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save codekiln/8056091c92b9d1d269911ce19c7aa09f to your computer and use it in GitHub Desktop.
Save codekiln/8056091c92b9d1d269911ce19c7aa09f to your computer and use it in GitHub Desktop.
List all signals by model and signal type. Tested with Django 1.9 and MongoEngine
# coding:utf-8
import ctypes
import gc
import inspect
from collections import defaultdict
import re
from blinker import NamedSignal
from django.core.management.base import BaseCommand
from django.db.models.signals import m2m_changed, pre_migrate, post_migrate, ModelSignal, pre_init, post_init, pre_save, post_save, pre_delete, post_delete
from mongoengine.signals import pre_init as m_pre_init, post_init as m_post_init, pre_save as m_pre_save, pre_save_post_validation as m_pre_save_post_validation, \
post_save as m_post_save, pre_delete as m_pre_delete, post_delete as m_post_delete, pre_bulk_insert as m_pre_bulk_insert, post_bulk_insert as m_post_bulk_insert
try:
from django.dispatch.dispatcher import WEAKREF_TYPES
except ImportError:
import weakref
WEAKREF_TYPES = weakref.ReferenceType,
signal_print_template = '{module}.{name} #{line}'
SIGNAL_NAMES = {
pre_init: 'pre_init',
post_init: 'post_init',
pre_save: 'pre_save',
post_save: 'post_save',
pre_delete: 'pre_delete',
post_delete: 'post_delete',
m2m_changed: 'm2m_changed',
pre_migrate: 'pre_migrate',
post_migrate: 'post_migrate',
m_pre_init: 'mongo_pre_init',
m_post_init: 'mongo_post_init',
m_pre_save: 'mongo_pre_save',
m_pre_save_post_validation: 'mongo_pre_save_post_validation',
m_post_save: 'mongo_post_save',
m_pre_delete: 'mongo_pre_delete',
m_post_delete: 'mongo_post_delete',
m_pre_bulk_insert: 'mongo_pre_bulk_insert',
m_post_bulk_insert: 'mongo_post_bulk_insert'
}
match_classname = re.compile(b"^<class '([^']*)'>$")
class Command(BaseCommand):
help = 'List all signals by model and signal type'
def handle(self, *args, **options):
signals = [obj for obj in gc.get_objects() if isinstance(obj, ModelSignal) or isinstance(obj, NamedSignal)]
models = defaultdict(lambda: defaultdict(list))
for signal in signals:
signal_name = SIGNAL_NAMES.get(signal, 'unknown')
try:
receivers = signal.receivers.items()
except AttributeError:
# list has no attribute items
receivers = signal.receivers
for receiver in receivers:
lookup, receiver = receiver
if isinstance(receiver, WEAKREF_TYPES):
receiver = receiver()
if receiver is None:
continue
try:
receiver_id, sender_id = lookup
except TypeError:
sender_id = lookup
model = ctypes.cast(sender_id, ctypes.py_object).value
try:
line = inspect.getsourcelines(receiver)[1]
path = inspect.getsourcefile(receiver)
module = receiver.__module__
name = receiver.func_name
except TypeError:
func = receiver.weak_func()
cls = receiver.weak_self()
line = inspect.getsourcelines(func)[1]
path = inspect.getsourcefile(func)
module = "%s.%s" % (cls.__module__, cls.__name__)
name = func.func_name
models[model][signal_name].append(signal_print_template.format(
name=name, module=module, line=line, path=path)
)
print ""
print "Signals ---------------------"
print ""
for key in sorted(models.keys()):
if key is not None:
print '' + key.__module__ + "." + key.__name__
for signal_name, lines in models[key].iteritems():
print " " * 4, signal_name
for line in lines:
print " " * 8, line
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment