def test_local_critical_no_thread_to_task(): """ Tests that local does not go through async_to_sync when the local is set as thread-critical """ # Set up the local test_local = Local(thread_critical=True) test_local.foo = 89 # Look at it in an async context async def async_function(): with pytest.raises(AttributeError): test_local.foo test_local.foo = "numbers" assert test_local.foo == "numbers" async_to_sync(async_function)() # Check the value outside assert test_local.foo == 89
def language_changed(**kwargs): if kwargs['setting'] in {'LANGUAGES', 'LANGUAGE_CODE', 'LOCALE_PATHS'}: from django.utils.translation import trans_real trans_real._default = None trans_real._active = Local() if kwargs['setting'] in {'LANGUAGES', 'LOCALE_PATHS'}: from django.utils.translation import trans_real trans_real._translations = {} trans_real.check_for_language.cache_clear()
async def test_local_critical_no_task_to_thread(): """ Tests that local doesn't go through sync_to_async when the local is set as thread-critical. """ # Set up the local test_local = Local(thread_critical=True) test_local.foo = 86 # Look at it in a sync context def sync_function(): with pytest.raises(AttributeError): test_local.foo test_local.foo = "secret" assert test_local.foo == "secret" await sync_to_async(sync_function)() # Check the value outside is not touched assert test_local.foo == 86
def translation_file_changed(sender, file_path, **kwargs): """Clear the internal translations cache if a .mo file is modified.""" if file_path.suffix == '.mo': import gettext from django.utils.translation import trans_real gettext._translations = {} trans_real._translations = {} trans_real._default = None trans_real._active = Local() return True
async def test_local_task_to_sync_to_task(): """ Tests that local carries through sync_to_async and then back through async_to_sync """ # Set up the local test_local = Local() test_local.foo = 756 # Look at it in an async context inside a sync context def sync_function(): async def async_function(): assert test_local.foo == 756 test_local.foo = "dragons" async_to_sync(async_function)() await sync_to_async(sync_function)() # Check the value passed out again assert test_local.foo == "dragons"
def test_template_tags_pgettext(self): """{% translate %} takes message contexts into account (#14806).""" trans_real._active = Local() trans_real._translations = {} with translation.override('de'): # Nonexistent context... t = self.get_template( '{% load i18n %}{% translate "May" context "nonexistent" %}') rendered = t.render(Context()) self.assertEqual(rendered, 'May') # Existing context... using a literal t = self.get_template( '{% load i18n %}{% translate "May" context "month name" %}') rendered = t.render(Context()) self.assertEqual(rendered, 'Mai') t = self.get_template( '{% load i18n %}{% translate "May" context "verb" %}') rendered = t.render(Context()) self.assertEqual(rendered, 'Kann') # Using a variable t = self.get_template( '{% load i18n %}{% translate "May" context message_context %}') rendered = t.render(Context({'message_context': 'month name'})) self.assertEqual(rendered, 'Mai') t = self.get_template( '{% load i18n %}{% translate "May" context message_context %}') rendered = t.render(Context({'message_context': 'verb'})) self.assertEqual(rendered, 'Kann') # Using a filter t = self.get_template( '{% load i18n %}{% translate "May" context message_context|lower %}' ) rendered = t.render(Context({'message_context': 'MONTH NAME'})) self.assertEqual(rendered, 'Mai') t = self.get_template( '{% load i18n %}{% translate "May" context message_context|lower %}' ) rendered = t.render(Context({'message_context': 'VERB'})) self.assertEqual(rendered, 'Kann') # Using 'as' t = self.get_template( '{% load i18n %}{% translate "May" context "month name" as var %}Value: {{ var }}' ) rendered = t.render(Context()) self.assertEqual(rendered, 'Value: Mai') t = self.get_template( '{% load i18n %}{% translate "May" as var context "verb" %}Value: {{ var }}' ) rendered = t.render(Context()) self.assertEqual(rendered, 'Value: Kann')
def test_template_tags_pgettext(self): """{% translate %} takes message contexts into account (#14806).""" trans_real._active = Local() trans_real._translations = {} with translation.override("de"): # Nonexistent context... t = self.get_template( '{% load i18n %}{% translate "May" context "nonexistent" %}') rendered = t.render(Context()) self.assertEqual(rendered, "May") # Existing context... using a literal t = self.get_template( '{% load i18n %}{% translate "May" context "month name" %}') rendered = t.render(Context()) self.assertEqual(rendered, "Mai") t = self.get_template( '{% load i18n %}{% translate "May" context "verb" %}') rendered = t.render(Context()) self.assertEqual(rendered, "Kann") # Using a variable t = self.get_template( '{% load i18n %}{% translate "May" context message_context %}') rendered = t.render(Context({"message_context": "month name"})) self.assertEqual(rendered, "Mai") t = self.get_template( '{% load i18n %}{% translate "May" context message_context %}') rendered = t.render(Context({"message_context": "verb"})) self.assertEqual(rendered, "Kann") # Using a filter t = self.get_template( '{% load i18n %}{% translate "May" context message_context|lower %}' ) rendered = t.render(Context({"message_context": "MONTH NAME"})) self.assertEqual(rendered, "Mai") t = self.get_template( '{% load i18n %}{% translate "May" context message_context|lower %}' ) rendered = t.render(Context({"message_context": "VERB"})) self.assertEqual(rendered, "Kann") # Using 'as' t = self.get_template( '{% load i18n %}{% translate "May" context "month name" as var %}' "Value: {{ var }}") rendered = t.render(Context()) self.assertEqual(rendered, "Value: Mai") t = self.get_template( '{% load i18n %}{% translate "May" as var context "verb" %}Value: ' "{{ var }}") rendered = t.render(Context()) self.assertEqual(rendered, "Value: Kann")
def language_changed(*, setting, **kwargs): if setting in {"LANGUAGES", "LANGUAGE_CODE", "LOCALE_PATHS"}: from django.utils.translation import trans_real trans_real._default = None trans_real._active = Local() if setting in {"LANGUAGES", "LOCALE_PATHS"}: from django.utils.translation import trans_real trans_real._translations = {} trans_real.check_for_language.cache_clear()
def __init__(self, databases=None): """ databases is an optional dictionary of database definitions (structured like settings.DATABASES). """ self._databases = databases # Connections needs to still be an actual thread local, as it's truly # thread-critical. Database backends should use @async_unsafe to protect # their code from async contexts, but this will give those contexts # separate connections in case it's needed as well. There's no cleanup # after async contexts, though, so we don't allow that if we can help it. self._connections = Local(thread_critical=True)
def test_local_thread_nested(): """ Tests that local does not leak across threads """ test_local = Local() # Unassigned should be an error with pytest.raises(AttributeError): test_local.foo print("outside ID", Local._get_context_id()) # Assign and check it does not persist inside the thread class TestThread(threading.Thread): # Failure reason failed = "unknown" def run(self): print("inside ID", Local._get_context_id()) # Make sure the attribute is not there try: test_local.foo self.failed = "leak inside" return except AttributeError: pass # Check the value is good self.failed = "set inside" test_local.foo = 123 assert test_local.foo == 123 # Binary signal that these tests passed to the outer thread self.failed = "" test_local.foo = 8 thread = TestThread() thread.start() thread.join() assert thread.failed == "" # Check it didn't leak back out assert test_local.foo == 8
def run(self): print("inside ID", Local._get_context_id()) # Make sure the attribute is not there try: test_local.foo self.failed = "leak inside" return except AttributeError: pass # Check the value is good self.failed = "set inside" test_local.foo = 123 assert test_local.foo == 123 # Binary signal that these tests passed to the outer thread self.failed = ""
async def test_local_many_layers(): """ Tests that local carries through a lot of layers of sync/async """ # Set up the local test_local = Local() test_local.foo = 8374 # Make sure we go between each world at least twice def sync_function(): async def async_function(): def sync_function_2(): async def async_function_2(): assert test_local.foo == 8374 test_local.foo = "miracles" async_to_sync(async_function_2)() await sync_to_async(sync_function_2)() async_to_sync(async_function)() await sync_to_async(sync_function)() # Check the value passed out again assert test_local.foo == "miracles"
def __init__(self, pattern, urlconf_name, default_kwargs=None, app_name=None, namespace=None): self.pattern = pattern # urlconf_name is the dotted Python path to the module defining # urlpatterns. It may also be an object with an urlpatterns attribute # or urlpatterns itself. self.urlconf_name = urlconf_name self.callback = None self.default_kwargs = default_kwargs or {} self.namespace = namespace self.app_name = app_name self._reverse_dict = {} self._namespace_dict = {} self._app_dict = {} # set of dotted paths to all functions and classes that are used in # urlpatterns self._callback_strs = set() self._populated = False self._local = Local()
def f(): nonlocal matched # Involve Local in a cycle cycle = [Local()] cycle.append(cycle) cycle[0].foo = "bar" # GC the cycle del cycle gc.collect() # Trigger the local creation outside e1.set() e2.wait() # New Locals should be empty matched = len([ local for local in locals if getattr(local, "foo", None) == "bar" ])
def test_local_cycle(): """ Tests that Local can handle cleanup up a cycle to itself (Borrowed and modified from the CPython threadlocal tests) """ locals = None matched = 0 e1 = threading.Event() e2 = threading.Event() def f(): nonlocal matched # Involve Local in a cycle cycle = [Local()] cycle.append(cycle) cycle[0].foo = "bar" # GC the cycle del cycle gc.collect() # Trigger the local creation outside e1.set() e2.wait() # New Locals should be empty matched = len([ local for local in locals if getattr(local, "foo", None) == "bar" ]) t = threading.Thread(target=f) t.start() e1.wait() # Creates locals outside of the inner thread locals = [Local() for i in range(100)] e2.set() t.join() assert matched == 0
def __init__(self, pattern, urlconf_name, default_kwargs=None, app_name=None, namespace=None): # 该属性值是当前模块中定义的 RoutePattern 类的实例,叫「正则模式对象」 self.pattern = pattern # 该属性值是字符串,它指向项目的路由配置模块 'xxx.urls' self.urlconf_name = urlconf_name self.callback = None self.default_kwargs = default_kwargs or {} self.namespace = namespace self.app_name = app_name self._reverse_dict = {} self._namespace_dict = {} self._app_dict = {} # set of dotted paths to all functions and classes that are used in # urlpatterns self._callback_strs = set() self._populated = False self._local = Local()
To be registered with log_registry.register_action. """ label = "" message = "" comment = "" def format_message(self, log_entry): return self.message def format_comment(self, log_entry): return self.comment _active = Local() class LogContext: """ Stores data about the environment in which a logged action happens - e.g. the active user - to be stored in the log entry for that action. """ def __init__(self, user=None, generate_uuid=True): self.user = user if generate_uuid: self.uuid = uuid.uuid4() else: self.uuid = None def __enter__(self):
def test_template_tags_pgettext(self): """{% blocktranslate %} takes message contexts into account (#14806).""" trans_real._active = Local() trans_real._translations = {} with translation.override("de"): # Nonexistent context t = self.get_template( '{% load i18n %}{% blocktranslate context "nonexistent" %}May' "{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "May") # Existing context... using a literal t = self.get_template( "{% load i18n %}" '{% blocktranslate context "month name" %}May{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, "Mai") t = self.get_template( "{% load i18n %}" '{% blocktranslate context "verb" %}May{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, "Kann") # Using a variable t = self.get_template( "{% load i18n %}{% blocktranslate context message_context %}" "May{% endblocktranslate %}" ) rendered = t.render(Context({"message_context": "month name"})) self.assertEqual(rendered, "Mai") t = self.get_template( "{% load i18n %}{% blocktranslate context message_context %}" "May{% endblocktranslate %}" ) rendered = t.render(Context({"message_context": "verb"})) self.assertEqual(rendered, "Kann") # Using a filter t = self.get_template( "{% load i18n %}" "{% blocktranslate context message_context|lower %}May" "{% endblocktranslate %}" ) rendered = t.render(Context({"message_context": "MONTH NAME"})) self.assertEqual(rendered, "Mai") t = self.get_template( "{% load i18n %}" "{% blocktranslate context message_context|lower %}May" "{% endblocktranslate %}" ) rendered = t.render(Context({"message_context": "VERB"})) self.assertEqual(rendered, "Kann") # Using 'count' t = self.get_template( "{% load i18n %}" '{% blocktranslate count number=1 context "super search" %}{{ number }}' " super result{% plural %}{{ number }} super results" "{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "1 Super-Ergebnis") t = self.get_template( "{% load i18n %}" '{% blocktranslate count number=2 context "super search" %}{{ number }}' " super result{% plural %}{{ number }} super results" "{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "2 Super-Ergebnisse") t = self.get_template( "{% load i18n %}" '{% blocktranslate context "other super search" count number=1 %}' "{{ number }} super result{% plural %}{{ number }} super results" "{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "1 anderen Super-Ergebnis") t = self.get_template( "{% load i18n %}" '{% blocktranslate context "other super search" count number=2 %}' "{{ number }} super result{% plural %}{{ number }} super results" "{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "2 andere Super-Ergebnisse") # Using 'with' t = self.get_template( "{% load i18n %}" '{% blocktranslate with num_comments=5 context "comment count" %}' "There are {{ num_comments }} comments{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "Es gibt 5 Kommentare") t = self.get_template( "{% load i18n %}" '{% blocktranslate with num_comments=5 context "other comment count" %}' "There are {{ num_comments }} comments{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "Andere: Es gibt 5 Kommentare") # Using trimmed t = self.get_template( "{% load i18n %}{% blocktranslate trimmed %}\n\nThere\n\t are 5 " "\n\n comments\n{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "There are 5 comments") t = self.get_template( "{% load i18n %}" '{% blocktranslate with num_comments=5 context "comment count" trimmed ' "%}\n\n" "There are \t\n \t {{ num_comments }} comments\n\n" "{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "Es gibt 5 Kommentare") t = self.get_template( "{% load i18n %}" '{% blocktranslate context "other super search" count number=2 trimmed ' "%}\n{{ number }} super \n result{% plural %}{{ number }} super results" "{% endblocktranslate %}" ) rendered = t.render(Context()) self.assertEqual(rendered, "2 andere Super-Ergebnisse") # Misuses msg = "Unknown argument for 'blocktranslate' tag: %r." with self.assertRaisesMessage(TemplateSyntaxError, msg % 'month="May"'): self.get_template( '{% load i18n %}{% blocktranslate context with month="May" %}' "{{ month }}{% endblocktranslate %}" ) msg = ( '"context" in %r tag expected exactly one argument.' % "blocktranslate" ) with self.assertRaisesMessage(TemplateSyntaxError, msg): self.get_template( "{% load i18n %}{% blocktranslate context %}{% endblocktranslate %}" ) with self.assertRaisesMessage(TemplateSyntaxError, msg): self.get_template( "{% load i18n %}{% blocktranslate count number=2 context %}" "{{ number }} super result{% plural %}{{ number }}" " super results{% endblocktranslate %}" )
def __init__(self): self._caches = Local()
def test_template_tags_pgettext(self): """{% blocktranslate %} takes message contexts into account (#14806).""" trans_real._active = Local() trans_real._translations = {} with translation.override('de'): # Nonexistent context t = self.get_template( '{% load i18n %}{% blocktranslate context "nonexistent" %}May' '{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, 'May') # Existing context... using a literal t = self.get_template('{% load i18n %}{% blocktranslate context "month name" %}May{% endblocktranslate %}') rendered = t.render(Context()) self.assertEqual(rendered, 'Mai') t = self.get_template('{% load i18n %}{% blocktranslate context "verb" %}May{% endblocktranslate %}') rendered = t.render(Context()) self.assertEqual(rendered, 'Kann') # Using a variable t = self.get_template( '{% load i18n %}{% blocktranslate context message_context %}' 'May{% endblocktranslate %}' ) rendered = t.render(Context({'message_context': 'month name'})) self.assertEqual(rendered, 'Mai') t = self.get_template( '{% load i18n %}{% blocktranslate context message_context %}' 'May{% endblocktranslate %}' ) rendered = t.render(Context({'message_context': 'verb'})) self.assertEqual(rendered, 'Kann') # Using a filter t = self.get_template( '{% load i18n %}{% blocktranslate context message_context|lower %}May{% endblocktranslate %}' ) rendered = t.render(Context({'message_context': 'MONTH NAME'})) self.assertEqual(rendered, 'Mai') t = self.get_template( '{% load i18n %}{% blocktranslate context message_context|lower %}May{% endblocktranslate %}' ) rendered = t.render(Context({'message_context': 'VERB'})) self.assertEqual(rendered, 'Kann') # Using 'count' t = self.get_template( '{% load i18n %}{% blocktranslate count number=1 context "super search" %}' '{{ number }} super result{% plural %}{{ number }} super results{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, '1 Super-Ergebnis') t = self.get_template( '{% load i18n %}{% blocktranslate count number=2 context "super search" %}{{ number }}' ' super result{% plural %}{{ number }} super results{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, '2 Super-Ergebnisse') t = self.get_template( '{% load i18n %}{% blocktranslate context "other super search" count number=1 %}' '{{ number }} super result{% plural %}{{ number }} super results{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, '1 anderen Super-Ergebnis') t = self.get_template( '{% load i18n %}{% blocktranslate context "other super search" count number=2 %}' '{{ number }} super result{% plural %}{{ number }} super results{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, '2 andere Super-Ergebnisse') # Using 'with' t = self.get_template( '{% load i18n %}{% blocktranslate with num_comments=5 context "comment count" %}' 'There are {{ num_comments }} comments{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, 'Es gibt 5 Kommentare') t = self.get_template( '{% load i18n %}{% blocktranslate with num_comments=5 context "other comment count" %}' 'There are {{ num_comments }} comments{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, 'Andere: Es gibt 5 Kommentare') # Using trimmed t = self.get_template( '{% load i18n %}{% blocktranslate trimmed %}\n\nThere\n\t are 5 ' '\n\n comments\n{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, 'There are 5 comments') t = self.get_template( '{% load i18n %}{% blocktranslate with num_comments=5 context "comment count" trimmed %}\n\n' 'There are \t\n \t {{ num_comments }} comments\n\n{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, 'Es gibt 5 Kommentare') t = self.get_template( '{% load i18n %}{% blocktranslate context "other super search" count number=2 trimmed %}\n' '{{ number }} super \n result{% plural %}{{ number }} super results{% endblocktranslate %}' ) rendered = t.render(Context()) self.assertEqual(rendered, '2 andere Super-Ergebnisse') # Misuses msg = "Unknown argument for 'blocktranslate' tag: %r." with self.assertRaisesMessage(TemplateSyntaxError, msg % 'month="May"'): self.get_template( '{% load i18n %}{% blocktranslate context with month="May" %}{{ month }}{% endblocktranslate %}' ) msg = '"context" in %r tag expected exactly one argument.' % 'blocktranslate' with self.assertRaisesMessage(TemplateSyntaxError, msg): self.get_template('{% load i18n %}{% blocktranslate context %}{% endblocktranslate %}') with self.assertRaisesMessage(TemplateSyntaxError, msg): self.get_template( '{% load i18n %}{% blocktranslate count number=2 context %}' '{{ number }} super result{% plural %}{{ number }}' ' super results{% endblocktranslate %}' )
# -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals from contextlib import contextmanager from asgiref.local import Local from django.utils.translation import get_language, override _thread_locals = Local() def notify_items_pre_save(**kwargs): return notify_items(signal_type='pre_save', **kwargs) def notify_items_post_save(**kwargs): return notify_items(signal_type='post_save', **kwargs) def notify_items_pre_delete(**kwargs): return notify_items(signal_type='pre_delete', **kwargs) def notify_items_post_delete(**kwargs): return notify_items(signal_type='post_delete', **kwargs) def notify_items(signal_type, **kwargs): """ Signal endpoint that actually sends knocks whenever an instance is created / saved """
from .payloads import generate_api_call_payload, generate_event_delivery_attempt_payload from .tracing import opentracing_trace if TYPE_CHECKING: from celery.exceptions import Retry from django.http import HttpRequest, HttpResponse from ...core.models import EventDeliveryAttempt logger = logging.getLogger(__name__) CACHE_TIMEOUT = parse("2 minutes") BUFFER_KEY = "observability_buffer" WEBHOOKS_KEY = "observability_webhooks" _active_webhooks_exists_cache: Dict[str, Tuple[bool, float]] = {} _context = Local() @dataclass class WebhookData: id: int saleor_domain: str target_url: str secret_key: Optional[str] = None def get_buffer_name() -> str: return cache.make_key(BUFFER_KEY) _webhooks_mem_cache: Dict[str, Tuple[List[WebhookData], float]] = {}
from urllib.parse import urlsplit, urlunsplit from asgiref.local import Local from django.utils.encoding import iri_to_uri from django.utils.functional import lazy from django.utils.translation import override from .exceptions import NoReverseMatch, Resolver404 from .resolvers import _get_cached_resolver, get_ns_resolver, get_resolver from .utils import get_callable # SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for # the current thread (which is the only one we ever access), it is assumed to # be empty. _prefixes = Local() # Overridden URLconfs for each thread are stored here. _urlconfs = Local() def resolve(path, urlconf=None): if urlconf is None: urlconf = get_urlconf() return get_resolver(urlconf).resolve(path) def reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None): if urlconf is None: urlconf = get_urlconf() resolver = get_resolver(urlconf)
def clear_cache_handlers(**kwargs): if kwargs['setting'] == 'CACHES': from django.core.cache import caches, close_caches close_caches() caches._settings = caches.settings = caches.configure_settings(None) caches._connections = Local()
def clear_cache_handlers(**kwargs): if kwargs['setting'] == 'CACHES': from django.core.cache import caches caches._caches = Local()
import warnings from pprint import pformat from asgiref.local import Local from django.template import Node from django.utils.html import format_html from django.utils.safestring import mark_safe from debug_toolbar import settings as dt_settings try: import threading except ImportError: threading = None _local_data = Local() def _is_excluded_frame(frame, excluded_modules): if not excluded_modules: return False frame_module = frame.f_globals.get("__name__") if not isinstance(frame_module, str): return False return any(frame_module == excluded_module or frame_module.startswith(excluded_module + ".") for excluded_module in excluded_modules) def _stack_trace_deprecation_warning(): warnings.warn(
def __init__(self, settings=None): self._settings = settings self._connections = Local(self.thread_critical)
from contextlib import contextmanager from django.apps import apps from django.conf import settings from django.db import connections from .cache import cachalot_caches from .settings import cachalot_settings from .signals import post_invalidation from .transaction import AtomicCache from .utils import _invalidate_tables try: from asgiref.local import Local LOCAL_STORAGE = Local() except ImportError: import threading LOCAL_STORAGE = threading.local() __all__ = ('invalidate', 'get_last_invalidation', 'cachalot_disabled') def _cache_db_tables_iterator(tables, cache_alias, db_alias): no_tables = not tables cache_aliases = settings.CACHES if cache_alias is None else (cache_alias, ) db_aliases = settings.DATABASES if db_alias is None else (db_alias, ) for db_alias in db_aliases: if no_tables: tables = connections[db_alias].introspection.table_names() if tables: for cache_alias in cache_aliases:
class CachePanel(Panel): """ Panel that displays the cache statistics. """ template = "debug_toolbar/panels/cache.html" _context_locals = Local() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.total_time = 0 self.hits = 0 self.misses = 0 self.calls = [] self.counts = {name: 0 for name in WRAPPED_CACHE_METHODS} @classmethod def current_instance(cls): """ Return the currently enabled CachePanel instance or None. If a request is in process with a CachePanel enabled, this will return that panel (based on the current thread or async task). Otherwise it will return None. """ return getattr(cls._context_locals, "current_instance", None) @classmethod def ready(cls): if not hasattr(CacheHandler, "_djdt_patched"): # Wrap the CacheHander.create_connection() method to monkey patch any new # cache connections that are opened while instrumentation is enabled. In # the interests of thread safety, this is done once at startup time and # never removed. original_method = CacheHandler.create_connection @functools.wraps(original_method) def wrapper(self, alias): cache = original_method(self, alias) panel = cls.current_instance() if panel is not None: panel._monkey_patch_cache(cache) return cache CacheHandler.create_connection = wrapper CacheHandler._djdt_patched = True def _store_call_info( self, name, time_taken, return_value, args, kwargs, trace, template_info, backend, ): if name == "get" or name == "get_or_set": if return_value is None: self.misses += 1 else: self.hits += 1 elif name == "get_many": for key, value in return_value.items(): if value is None: self.misses += 1 else: self.hits += 1 time_taken *= 1000 self.total_time += time_taken self.counts[name] += 1 self.calls.append({ "time": time_taken, "name": name, "args": args, "kwargs": kwargs, "trace": render_stacktrace(trace), "template_info": template_info, "backend": backend, }) def _record_call(self, cache, name, original_method, args, kwargs): # Some cache backends implement certain cache methods in terms of other cache # methods (e.g. get_or_set() in terms of get() and add()). In order to only # record the calls made directly by the user code, set the _djdt_recording flag # here to cause the monkey patched cache methods to skip recording additional # calls made during the course of this call. cache._djdt_recording = True t = time.time() value = original_method(*args, **kwargs) t = time.time() - t cache._djdt_recording = False if dt_settings.get_config()["ENABLE_STACKTRACES"]: stacktrace = tidy_stacktrace(reversed(get_stack())) else: stacktrace = [] self._store_call_info( name=name, time_taken=t, return_value=value, args=args, kwargs=kwargs, trace=stacktrace, template_info=get_template_info(), backend=cache, ) return value def _monkey_patch_method(self, cache, name): original_method = getattr(cache, name) @functools.wraps(original_method) def wrapper(*args, **kwargs): # If this call is being made as part of the implementation of another cache # method, don't record it. if cache._djdt_recording: return original_method(*args, **kwargs) else: return self._record_call(cache, name, original_method, args, kwargs) wrapper._djdt_wrapped = original_method setattr(cache, name, wrapper) def _monkey_patch_cache(self, cache): if not hasattr(cache, "_djdt_patched"): for name in WRAPPED_CACHE_METHODS: self._monkey_patch_method(cache, name) cache._djdt_patched = True cache._djdt_recording = False @staticmethod def _unmonkey_patch_cache(cache): if hasattr(cache, "_djdt_patched"): for name in WRAPPED_CACHE_METHODS: original_method = getattr(cache, name)._djdt_wrapped if original_method.__func__ == getattr(cache.__class__, name): delattr(cache, name) else: setattr(cache, name, original_method) del cache._djdt_patched del cache._djdt_recording # Implement the Panel API nav_title = _("Cache") @property def nav_subtitle(self): cache_calls = len(self.calls) return ngettext( "%(cache_calls)d call in %(time).2fms", "%(cache_calls)d calls in %(time).2fms", cache_calls, ) % { "cache_calls": cache_calls, "time": self.total_time } @property def title(self): count = len(getattr(settings, "CACHES", ["default"])) return ngettext( "Cache calls from %(count)d backend", "Cache calls from %(count)d backends", count, ) % { "count": count } def enable_instrumentation(self): # Monkey patch all open cache connections. Django maintains cache connections # on a per-thread/async task basis, so this will not affect any concurrent # requests. The monkey patch of CacheHander.create_connection() installed in # the .ready() method will ensure that any new cache connections that get opened # during this request will also be monkey patched. for cache in caches.all(initialized_only=True): self._monkey_patch_cache(cache) # Mark this panel instance as the current one for the active thread/async task # context. This will be used by the CacheHander.create_connection() monkey # patch. self._context_locals.current_instance = self def disable_instrumentation(self): if hasattr(self._context_locals, "current_instance"): del self._context_locals.current_instance for cache in caches.all(initialized_only=True): self._unmonkey_patch_cache(cache) def generate_stats(self, request, response): self.record_stats({ "total_calls": len(self.calls), "calls": self.calls, "total_time": self.total_time, "hits": self.hits, "misses": self.misses, "counts": self.counts, }) def generate_server_timing(self, request, response): stats = self.get_stats() value = stats.get("total_time", 0) title = "Cache {} Calls".format(stats.get("total_calls", 0)) self.record_server_timing("total_time", title, value)
def clear_cache_handlers(**kwargs): if kwargs["setting"] == "CACHES": from django.core.cache import caches, close_caches close_caches() caches._caches = Local()