Forked from runekaagaard/list_all_model_signals.py
Last active
November 17, 2017 20:12
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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