Skip to content

Commit

Permalink
NEW: django_tools.debug.delay
Browse files Browse the repository at this point in the history
  • Loading branch information
jedie committed Aug 21, 2018
1 parent 316cc06 commit c339d9d
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 10 deletions.
11 changes: 11 additions & 0 deletions README.creole
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ SendMail(
See also the existing unittests:
* [[https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_email.py|django_tools_tests/test_email.py]]
=== Delay tools

Sometimes you want to simulate when processing takes a little longer.
There exists {{{django_tools.debug.delay.SessionDelay}}} and {{{django_tools.debug.delay.CacheDelay}}} for this.
The usage will create logging entries and user messages, if user is authenticated.

More info in seperate [[https://github.com/jedie/django-tools/blob/master/django_tools/debug/README.creole|django_tools/debug/README.creole]] file.


=== Filemanager library

Library for building django application like filemanager, gallery etc.
Expand Down Expand Up @@ -727,6 +737,7 @@ File renamed: {{{django_tools/unittest_utils/{celery.py => celery_utils.py}}}}
== history

* *dev* - [[https://github.com/jedie/django-tools/compare/v0.40.3...master|compare v0.40.3...master]]
** NEW: {{{django_tools.debug.delay}}} to simulate longer processing time by set a delay via GET parameter (see above)
* v0.40.3 - 18.07.2018 - [[https://github.com/jedie/django-tools/compare/v0.40.2...v0.40.3|compare v0.40.2...v0.40.3]]
** Enhance selenium test cases:
*** NEW: {{{assert_visible_by_id()}}}
Expand Down
75 changes: 74 additions & 1 deletion django_tools/debug/README.creole
Original file line number Diff line number Diff line change
@@ -1,5 +1,78 @@
== debug tools

=== Delay tools

Sometimes you want to simulate when processing takes a little longer.
There exists {{{django_tools.debug.delay.SessionDelay}}} and {{{django_tools.debug.delay.CacheDelay}}} for this.
The usage will create logging entries and user messages, if user is authenticated.

{{{SessionDelay}}} stores the sleep seconds into {{{request.session}}} and {{{CacheDelay}}} used the django cache backend.

{{{SessionDelay}}} can be used if {{{request}}} object is available while the delay is to be executed. e.g.: usage in views.
{{{CacheDelay}}} can be used to set the delay value in a view but the delay should be executed where no {{{request}}} is available, e.g.: in models or tasks.


{{{SessionDelay}}} usage e.g.:

{{{
def your_view(request):
# Save delay value of "?delay" if it appears in the URL to request.session:
SessionDelay(
request,
key="slow_foo", # Used as session key
only_debug=True # Delay only if settings.DEBUG == True
).load(
request,
query_string="delay", # The GET parameter name
default=5 # fallback value if GET parameter contains no value, e.g.: "?delay"
)
#...do something...
# get "?delay=XX" from session and delay with time.sleep() if exists:
SessionDelay(
request,
key="slow_foo"
).sleep()
return your_response
}}}


{{{CacheDelay}}} usage e.g.:

{{{
class FooBarModel(models.Model):
# ...
def save(self, **kwargs):
# Get the "?delay=XX" from cache and delay with time.sleep() if exists:
CacheDelay(key="slow_save_%s" % instance.pk).sleep()
super().save(**kwargs)
def your_view(request):
instance = get_foo_bar_model_instance(request)
# Save delay value of "?delay" if it appears in the URL to cache:
CacheDelay(
key="slow_save_%s" % instance.pk, # Used as cache key
only_debug=True # Delay only if settings.DEBUG == True
).load(
self.request,
query_string="delay", # The GET parameter name
default=5 # fallback value if GET parameter contains no value, e.g.: "?delay"
)
# ...
instance.save()
return your_response
}}}


=== middlewares

==== SetRequestDebugMiddleware
Expand All @@ -22,4 +95,4 @@ MIDDLEWARE_CLASSES = (
'django_tools.debug.middlewares.SetRequestDebugMiddleware',
...
)
}}}
}}}
137 changes: 137 additions & 0 deletions django_tools/debug/delay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""
:created: 21.08.2018 by Jens Diemer
:copyleft: 2018 by the django-tools team, see AUTHORS for more details.
:license: GNU GPL v3 or above, see LICENSE for more details.
"""

import logging
import time

from django.conf import settings
from django.contrib import messages
from django.core.cache import cache

log = logging.getLogger(__name__)


class DelayBase:
"""
Delay something via time.sleep() and used delay value from GET parameters.
This is only the base class, use:
- SessionDelay()
or
- CacheDelay()
"""
backend_name = None

def __init__(self, key, only_debug=True):
"""
:param key: session/cache key for store the delay value into backend
:param only_debug: If True: delay only if settings.DEBUG == True
"""
self.key = key
self.only_debug = only_debug

def _delete(self):
raise NotImplementedError

def _set(self, sec):
raise NotImplementedError

def _get(self):
raise NotImplementedError

def load(self, request, query_string, default=5):
"""
:param query_string: the request.GET key that activate/set delay value
:param default: fallback if no value in GET
"""
if query_string not in request.GET:
if self._get():
log.debug("Delete %r delay from %s", self.key, self.backend_name)
self._delete()
return

if self.only_debug and not settings.DEBUG:
log.debug("Ignore ?%s, because DEBUG is not ON!", query_string)
self._delete()
return

log.info("Add '%s' value to %s", query_string, self.backend_name)
sec = request.GET[query_string]
if not sec:
sec = default
else:
try:
sec = int(sec)
except ValueError:
sec = float(sec)
log.warning("Save %s sec. from %r for %r into %s", sec, query_string, self.key, self.backend_name)

if request.user.is_authenticated:
messages.warning(request, "Use %s sec. %r" % (sec, query_string))

self._set(sec)

def sleep(self):
sec = self._get()

if sec is None:
log.debug("No delay for %r from %s", self.key, self.backend_name)
return

log.warning("Delay %s sec. for %r", sec, self.key)
time.sleep(sec)


class SessionDelay(DelayBase):
"""
Delay via time.sleep() something, use the sleep seconds from GET parameter.
Store the sleep seconds into request.session
"""
backend_name = "session"

def __init__(self, request, *args, **kwargs):
self.request = request
super().__init__(*args, **kwargs)

def _delete(self):
try:
del self.request.session[self.key]
except KeyError:
pass

def _set(self, sec):
self.request.session[self.key] = sec

def _get(self):
return self.request.session.get(self.key)


class CacheDelay(DelayBase):
"""
Delay via time.sleep() something, use the sleep seconds from GET parameter.
Store the sleep seconds into django cache backend
"""
backend_name = "cache"

def __init__(self, *args, cache_timeout=30 * 60, **kwargs):
"""
:param cache_timeout: timeout, in seconds, to use for the cache.
You can set TIMEOUT to None so that, by default, cache keys never expire.
see also:
https://docs.djangoproject.com/en/1.11/topics/cache/#cache-arguments
"""
super().__init__(*args, **kwargs)
self.cache_timeout = cache_timeout

def _delete(self):
cache.delete(self.key)

def _set(self, sec):
cache.set(self.key, sec, self.cache_timeout)

def _get(self):
return cache.get(self.key)
23 changes: 15 additions & 8 deletions django_tools/unittest_utils/logging_utils.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@

"""
:created: 2015 by Jens Diemer
:copyleft: 2015-2018 by the django-tools team, see AUTHORS for more details.
:license: GNU GPL v3 or above, see LICENSE for more details.
"""


import logging
from logging.handlers import MemoryHandler


class LoggingBuffer:
def __init__(self, name=None, level=logging.DEBUG, formatter = None):

def __init__(self, name=None, level=logging.DEBUG, formatter=None):
"""
To get the logger name, execute this in `./manage.py shell` e.g.:
import logging;print("\n".join(sorted(logging.Logger.manager.loggerDict.keys())))
"""
self.buffer = []
self.level=level
self.level = level
if formatter is None:
self.formatter = logging.Formatter(logging.BASIC_FORMAT)
else:
self.formatter = formatter

self.log = logging.getLogger(name)
self.old_handlers = self.log.handlers[:] # .copy()
self.old_handlers = self.log.handlers[:] # .copy()
self.old_level = self.log.level
self.log.setLevel(level)
self.log.handlers = [MemoryHandler(capacity=0, flushLevel=level, target=self)]
Expand All @@ -43,8 +42,15 @@ def __exit__(self, exc_type, exc_value, traceback):
self.log.handlers = self.old_handlers
self.log.level = self.old_level

def get_message_list(self):
return [self.formatter.format(record) for record in self.buffer]

def get_messages(self):
return "\n".join([self.formatter.format(record) for record in self.buffer])
return "\n".join(self.get_message_list())

def assert_messages(self, reference):
messages = self.get_message_list()
assert messages == reference, "%r != %r" % (messages, reference)


class CutPathnameLogRecordFactory:
Expand All @@ -68,14 +74,15 @@ class CutPathnameLogRecordFactory:
# ...
}
"""

def __init__(self, max_length=40):
self.max_length = max_length
self.origin_factory = logging.getLogRecordFactory()

def cut_path(self, pathname):
if len(pathname)<=self.max_length:
if len(pathname) <= self.max_length:
return pathname
return "...%s" % pathname[-(self.max_length-3):]
return "...%s" % pathname[-(self.max_length - 3):]

def __call__(self, *args, **kwargs):
record = self.origin_factory(*args, **kwargs)
Expand Down
4 changes: 3 additions & 1 deletion django_tools_test_project/django_tools_test_app/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from django_tools_test_project.django_tools_test_app.views import (
TemplateDoesNotExists, create_message_normal_response, create_message_redirect_response, display_site,
get_current_get_parameters, raise_exception
get_current_get_parameters, raise_exception, delay_view
)

admin.autodiscover()
Expand All @@ -29,6 +29,8 @@

url(r'^create_message_normal_response/(?P<msg>.*?)/$', create_message_normal_response),
url(r'^create_message_redirect_response/(?P<msg>.*?)/$', create_message_redirect_response),

url(r'^delay/$', delay_view),
]

if settings.DEBUG:
Expand Down
23 changes: 23 additions & 0 deletions django_tools_test_project/django_tools_test_app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from django.views.generic import TemplateView

# https://github.com/jedie/django-tools
from django_tools.debug.delay import SessionDelay
from django_tools.middlewares.ThreadLocal import get_current_request


Expand Down Expand Up @@ -67,3 +68,25 @@ def create_message_normal_response(request, msg):
def create_message_redirect_response(request, msg):
messages.info(request, msg)
return HttpResponseRedirect("/create_message_redirect_response/")


def delay_view(request):
"""
Used in django_tools_tests.test_debug_delay.SessionDelayTests
"""
SessionDelay(
request,
key="delay_view",
only_debug=False
).load(
request,
query_string="sec",
default=5
)

SessionDelay(
request,
key="delay_view"
).sleep()

return HttpResponse("django_tools_test_project.django_tools_test_app.views.delay_view")

0 comments on commit c339d9d

Please sign in to comment.