Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
bc822b7f
Commit
bc822b7f
authored
Dec 08, 2015
by
Sarina Canelake
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #10873 from edx/kill-callstackmanager
Remove CallStackManager (PLAT-931)
parents
5ecb9af2
596e93a2
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
6 additions
and
577 deletions
+6
-577
cms/envs/test.py
+0
-3
lms/djangoapps/courseware/migrations/0001_initial.py
+0
-3
lms/djangoapps/courseware/model_data.py
+0
-2
lms/djangoapps/courseware/models.py
+6
-15
lms/djangoapps/courseware/user_state_client.py
+0
-9
lms/envs/test.py
+0
-3
openedx/core/djangoapps/call_stack_manager/__init__.py
+0
-5
openedx/core/djangoapps/call_stack_manager/core.py
+0
-223
openedx/core/djangoapps/call_stack_manager/models.py
+0
-8
openedx/core/djangoapps/call_stack_manager/tests.py
+0
-306
No files found.
cms/envs/test.py
View file @
bc822b7f
...
...
@@ -173,9 +173,6 @@ CACHES = {
},
}
# Add apps to Installed apps for testing
INSTALLED_APPS
+=
(
'openedx.core.djangoapps.call_stack_manager'
,)
# hide ratelimit warnings while running tests
filterwarnings
(
'ignore'
,
message
=
'No request passed to the backend, unable to rate-limit'
)
...
...
lms/djangoapps/courseware/migrations/0001_initial.py
View file @
bc822b7f
...
...
@@ -2,7 +2,6 @@
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
import
openedx.core.djangoapps.call_stack_manager.core
import
model_utils.fields
import
xmodule_django.models
import
django.utils.timezone
...
...
@@ -69,7 +68,6 @@ class Migration(migrations.Migration):
(
'modified'
,
models
.
DateTimeField
(
auto_now
=
True
,
db_index
=
True
)),
(
'student'
,
models
.
ForeignKey
(
to
=
settings
.
AUTH_USER_MODEL
)),
],
bases
=
(
openedx
.
core
.
djangoapps
.
call_stack_manager
.
core
.
CallStackMixin
,
models
.
Model
),
),
migrations
.
CreateModel
(
name
=
'StudentModuleHistory'
,
...
...
@@ -85,7 +83,6 @@ class Migration(migrations.Migration):
options
=
{
'get_latest_by'
:
'created'
,
},
bases
=
(
openedx
.
core
.
djangoapps
.
call_stack_manager
.
core
.
CallStackMixin
,
models
.
Model
),
),
migrations
.
CreateModel
(
name
=
'XModuleStudentInfoField'
,
...
...
lms/djangoapps/courseware/model_data.py
View file @
bc822b7f
...
...
@@ -45,7 +45,6 @@ from xmodule.modulestore.django import modulestore
from
xblock.core
import
XBlockAside
from
courseware.user_state_client
import
DjangoXBlockUserStateClient
from
openedx.core.djangoapps.call_stack_manager
import
donottrack
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -992,7 +991,6 @@ class ScoresClient(object):
# @contract(user_id=int, usage_key=UsageKey, score="number|None", max_score="number|None")
@donottrack
(
StudentModule
)
def
set_score
(
user_id
,
usage_key
,
score
,
max_score
):
"""
Set the score and max_score for the specified user and xblock usage.
...
...
lms/djangoapps/courseware/models.py
View file @
bc822b7f
...
...
@@ -25,7 +25,6 @@ from model_utils.models import TimeStampedModel
from
student.models
import
user_by_anonymous_id
from
submissions.models
import
score_set
,
score_reset
from
openedx.core.djangoapps.call_stack_manager
import
CallStackManager
,
CallStackMixin
from
xmodule_django.models
import
CourseKeyField
,
LocationKeyField
,
BlockTypeKeyField
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -72,21 +71,10 @@ class ChunkingManager(models.Manager):
return
res
class
ChunkingCallStackManager
(
CallStackManager
,
ChunkingManager
):
"""
A derived class of ChunkingManager, and CallStackManager
Class is currently unused but remains as part of the CallStackManger work. To re-enable see comment in StudentModule
"""
pass
class
StudentModule
(
CallStackMixin
,
models
.
Model
):
class
StudentModule
(
models
.
Model
):
"""
Keeps student state for a particular module in a particular course.
"""
# Changed back to ChunkingManager from ChunkingCallStackManger. To re-enable CallStack Management change the line
# back to: objects = ChunkingCallStackManager() Ticket: PLAT-881
objects
=
ChunkingManager
()
MODEL_TAGS
=
[
'course_id'
,
'module_type'
]
...
...
@@ -161,11 +149,11 @@ class StudentModule(CallStackMixin, models.Model):
return
unicode
(
repr
(
self
))
class
StudentModuleHistory
(
CallStackMixin
,
models
.
Model
):
class
StudentModuleHistory
(
models
.
Model
):
"""Keeps a complete history of state changes for a given XModule for a given
Student. Right now, we restrict this to problems so that the table doesn't
explode in size."""
objects
=
C
allStack
Manager
()
objects
=
C
hunking
Manager
()
HISTORY_SAVING_TYPES
=
{
'problem'
}
class
Meta
(
object
):
...
...
@@ -197,6 +185,9 @@ class StudentModuleHistory(CallStackMixin, models.Model):
max_grade
=
instance
.
max_grade
)
history_entry
.
save
()
def
__unicode__
(
self
):
return
unicode
(
repr
(
self
))
class
XBlockFieldBase
(
models
.
Model
):
"""
...
...
lms/djangoapps/courseware/user_state_client.py
View file @
bc822b7f
...
...
@@ -18,8 +18,6 @@ from xblock.fields import Scope, ScopeBase
from
courseware.models
import
StudentModule
,
StudentModuleHistory
from
edx_user_state_client.interface
import
XBlockUserStateClient
,
XBlockUserState
from
openedx.core.djangoapps.call_stack_manager
import
donottrack
class
DjangoXBlockUserStateClient
(
XBlockUserStateClient
):
"""
...
...
@@ -71,7 +69,6 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
"""
self
.
user
=
user
@donottrack
(
StudentModule
,
StudentModuleHistory
)
def
_get_student_modules
(
self
,
username
,
block_keys
):
"""
Retrieve the :class:`~StudentModule`s for the supplied ``username`` and ``block_keys``.
...
...
@@ -119,7 +116,6 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
sample_rate
=
self
.
API_DATADOG_SAMPLE_RATE
,
)
@donottrack
(
StudentModule
,
StudentModuleHistory
)
def
get_many
(
self
,
username
,
block_keys
,
scope
=
Scope
.
user_state
,
fields
=
None
):
"""
Retrieve the stored XBlock state for the specified XBlock usages.
...
...
@@ -173,7 +169,6 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
self
.
_ddog_histogram
(
evt_time
,
'get_many.blks_out'
,
block_count
)
self
.
_ddog_histogram
(
evt_time
,
'get_many.response_time'
,
(
finish_time
-
evt_time
)
*
1000
)
@donottrack
(
StudentModule
,
StudentModuleHistory
)
def
set_many
(
self
,
username
,
block_keys_to_state
,
scope
=
Scope
.
user_state
):
"""
Set fields for a particular XBlock.
...
...
@@ -250,7 +245,6 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
self
.
_ddog_histogram
(
evt_time
,
'set_many.blks_updated'
,
len
(
block_keys_to_state
))
self
.
_ddog_histogram
(
evt_time
,
'set_many.response_time'
,
(
finish_time
-
evt_time
)
*
1000
)
@donottrack
(
StudentModule
,
StudentModuleHistory
)
def
delete_many
(
self
,
username
,
block_keys
,
scope
=
Scope
.
user_state
,
fields
=
None
):
"""
Delete the stored XBlock state for a many xblock usages.
...
...
@@ -291,7 +285,6 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
finish_time
=
time
()
self
.
_ddog_histogram
(
evt_time
,
'delete_many.response_time'
,
(
finish_time
-
evt_time
)
*
1000
)
@donottrack
(
StudentModule
,
StudentModuleHistory
)
def
get_history
(
self
,
username
,
block_key
,
scope
=
Scope
.
user_state
):
"""
Retrieve history of state changes for a given block for a given
...
...
@@ -346,7 +339,6 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
yield
XBlockUserState
(
username
,
block_key
,
state
,
history_entry
.
created
,
scope
)
@donottrack
(
StudentModule
,
StudentModuleHistory
)
def
iter_all_for_block
(
self
,
block_key
,
scope
=
Scope
.
user_state
,
batch_size
=
None
):
"""
You get no ordering guarantees. Fetching will happen in batch_size
...
...
@@ -357,7 +349,6 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
raise
ValueError
(
"Only Scope.user_state is supported"
)
raise
NotImplementedError
()
@donottrack
(
StudentModule
,
StudentModuleHistory
)
def
iter_all_for_course
(
self
,
course_key
,
block_type
=
None
,
scope
=
Scope
.
user_state
,
batch_size
=
None
):
"""
You get no ordering guarantees. Fetching will happen in batch_size
...
...
lms/envs/test.py
View file @
bc822b7f
...
...
@@ -516,9 +516,6 @@ FEATURES['ENABLE_EDXNOTES'] = True
# Enable teams feature for tests.
FEATURES
[
'ENABLE_TEAMS'
]
=
True
# Add apps to Installed apps for testing
INSTALLED_APPS
+=
(
'openedx.core.djangoapps.call_stack_manager'
,)
# Enable courseware search for tests
FEATURES
[
'ENABLE_COURSEWARE_SEARCH'
]
=
True
...
...
openedx/core/djangoapps/call_stack_manager/__init__.py
deleted
100644 → 0
View file @
5ecb9af2
"""
Root Package for getting call stacks of various Model classes being used
"""
from
__future__
import
absolute_import
from
.core
import
CallStackManager
,
CallStackMixin
,
donottrack
,
trackit
openedx/core/djangoapps/call_stack_manager/core.py
deleted
100644 → 0
View file @
5ecb9af2
"""
Call Stack Manager deals with tracking call stacks of functions/methods/classes(Django Model Classes)
Call Stack Manager logs unique call stacks. The call stacks then can be retrieved via Splunk, or log reads.
classes:
CallStackManager - stores all stacks in global dictionary and logs
CallStackMixin - used for Model save(), and delete() method
Decorators:
@donottrack - Decorator that will halt tracking for parameterized entities,
(or halt tracking anything in case of non-parametrized decorator).
@trackit - Decorator that will start tracking decorated entity.
@track_till_now - Will log every unique call stack of parametrized entity/ entities.
TRACKING DJANGO MODEL CLASSES -
Call stacks of Model Class
in three cases-
1. QuerySet API
2. save()
3. delete()
How to use:
1. Import following in the file where class to be tracked resides
from openedx.core.djangoapps.call_stack_manager import CallStackManager, CallStackMixin
2. Override objects of default manager by writing following in any model class which you want to track-
objects = CallStackManager()
3. For tracking Save and Delete events-
Use mixin called "CallStackMixin"
For ex.
class StudentModule(models.Model, CallStackMixin):
TRACKING FUNCTIONS, and METHODS-
1. Import following-
from openedx.core.djangoapps.call_stack_manager import trackit
NOTE - @trackit is non-parameterized decorator.
FOR DISABLING TRACKING-
1. Import following at appropriate location-
from openedx.core.djangoapps.call_stack_manager import donottrack
NOTE - You need to import function/class you do not want to track.
"""
import
logging
import
traceback
import
re
import
collections
import
wrapt
import
types
import
inspect
from
django.db.models
import
Manager
log
=
logging
.
getLogger
(
__name__
)
# List of regular expressions acting as filters
REGULAR_EXPS
=
[
re
.
compile
(
x
)
for
x
in
[
'^.*python2.7.*$'
,
'^.*<exec_function>.*$'
,
'^.*exec_code_object.*$'
,
'^.*edxapp/src.*$'
,
'^.*call_stack_manager.*$'
]]
# List keeping track of entities not to be tracked
HALT_TRACKING
=
[]
STACK_BOOK
=
collections
.
defaultdict
(
list
)
# Dictionary which stores call logs
# {'EntityName' : ListOf<CallStacks>}
# CallStacks is ListOf<Frame>
# Frame is a tuple ('FilePath','LineNumber','Function Name', 'Context')
# {"<class 'courseware.models.StudentModule'>" : [[(file, line number, function name, context),(---,---,---)],
# [(file, line number, function name, context),(---,---,---)]]}
def
capture_call_stack
(
entity_name
):
""" Logs customised call stacks in global dictionary STACK_BOOK and logs it.
Arguments:
entity_name - entity
"""
# Holds temporary callstack
# List with each element 4-tuple(filename, line number, function name, text)
# and filtered with respect to regular expressions
temp_call_stack
=
[
frame
for
frame
in
traceback
.
extract_stack
()
if
not
any
(
reg
.
match
(
frame
[
0
])
for
reg
in
REGULAR_EXPS
)]
final_call_stack
=
""
.
join
(
traceback
.
format_list
(
temp_call_stack
))
def
_should_get_logged
(
entity_name
):
# pylint: disable=
""" Checks if current call stack of current entity should be logged or not.
Arguments:
entity_name - Name of the current entity
Returns:
True if the current call stack is to logged, False otherwise
"""
is_class_in_halt_tracking
=
bool
(
HALT_TRACKING
and
inspect
.
isclass
(
entity_name
)
and
issubclass
(
entity_name
,
tuple
(
HALT_TRACKING
[
-
1
])))
is_function_in_halt_tracking
=
bool
(
HALT_TRACKING
and
not
inspect
.
isclass
(
entity_name
)
and
any
((
entity_name
.
__name__
==
x
.
__name__
and
entity_name
.
__module__
==
x
.
__module__
)
for
x
in
tuple
(
HALT_TRACKING
[
-
1
])))
is_top_none
=
HALT_TRACKING
and
HALT_TRACKING
[
-
1
]
is
None
# if top of STACK_BOOK is None
if
is_top_none
:
return
False
# if call stack is empty
if
not
temp_call_stack
:
return
False
if
HALT_TRACKING
:
if
is_class_in_halt_tracking
or
is_function_in_halt_tracking
:
return
False
else
:
return
temp_call_stack
not
in
STACK_BOOK
[
entity_name
]
else
:
return
temp_call_stack
not
in
STACK_BOOK
[
entity_name
]
if
_should_get_logged
(
entity_name
):
STACK_BOOK
[
entity_name
]
.
append
(
temp_call_stack
)
if
inspect
.
isclass
(
entity_name
):
log
.
info
(
"Logging new call stack number
%
s for
%
s:
\n
%
s"
,
len
(
STACK_BOOK
[
entity_name
]),
entity_name
,
final_call_stack
)
else
:
log
.
info
(
"Logging new call stack number
%
s for
%
s.
%
s:
\n
%
s"
,
len
(
STACK_BOOK
[
entity_name
]),
entity_name
.
__module__
,
entity_name
.
__name__
,
final_call_stack
)
class
CallStackMixin
(
object
):
""" Mixin class for getting call stacks when save() and delete() methods are called """
def
save
(
self
,
*
args
,
**
kwargs
):
""" Logs before save() and overrides respective model API save() """
capture_call_stack
(
type
(
self
))
return
super
(
CallStackMixin
,
self
)
.
save
(
*
args
,
**
kwargs
)
def
delete
(
self
,
*
args
,
**
kwargs
):
""" Logs before delete() and overrides respective model API delete() """
capture_call_stack
(
type
(
self
))
return
super
(
CallStackMixin
,
self
)
.
delete
(
*
args
,
**
kwargs
)
class
CallStackManager
(
Manager
):
""" Manager class which overrides the default Manager class for getting call stacks """
def
get_queryset
(
self
):
""" Override the default queryset API method """
capture_call_stack
(
self
.
model
)
return
super
(
CallStackManager
,
self
)
.
get_queryset
()
def
donottrack
(
*
entities_not_to_be_tracked
):
""" Decorator which halts tracking for some entities for specific functions
Arguments:
entities_not_to_be_tracked: entities which are not to be tracked
Returns:
wrapped function
"""
if
not
entities_not_to_be_tracked
:
entities_not_to_be_tracked
=
None
@wrapt.decorator
def
real_donottrack
(
wrapped
,
instance
,
args
,
kwargs
):
# pylint: disable=unused-argument
""" Takes function to be decorated and returns wrapped function
Arguments:
wrapped - The wrapped function which in turns needs to be called by wrapper function
instance - The object to which the wrapped function was bound when it was called.
args - The list of positional arguments supplied when the decorated function was called.
kwargs - The dictionary of keyword arguments supplied when the decorated function was called.
Returns:
return of wrapped function
"""
global
HALT_TRACKING
# pylint: disable=global-variable-not-assigned
if
entities_not_to_be_tracked
is
None
:
HALT_TRACKING
.
append
(
None
)
else
:
if
HALT_TRACKING
:
if
HALT_TRACKING
[
-
1
]
is
None
:
# if @donottrack() calls @donottrack('xyz')
pass
else
:
HALT_TRACKING
.
append
(
set
(
HALT_TRACKING
[
-
1
]
.
union
(
set
(
entities_not_to_be_tracked
))))
else
:
HALT_TRACKING
.
append
(
set
(
entities_not_to_be_tracked
))
return_value
=
wrapped
(
*
args
,
**
kwargs
)
# check if the returning class is a generator
if
isinstance
(
return_value
,
types
.
GeneratorType
):
def
generator_wrapper
(
wrapped_generator
):
""" Function handling wrapped yielding values.
Argument:
wrapped_generator - wrapped function returning generator function
Returns:
Generator Wrapper
"""
try
:
while
True
:
return_value
=
next
(
wrapped_generator
)
yield
return_value
finally
:
HALT_TRACKING
.
pop
()
return
generator_wrapper
(
return_value
)
else
:
HALT_TRACKING
.
pop
()
return
return_value
return
real_donottrack
@wrapt.decorator
def
trackit
(
wrapped
,
instance
,
args
,
kwargs
):
# pylint: disable=unused-argument
""" Decorator which tracks logs call stacks
Arguments:
wrapped - The wrapped function which in turns needs to be called by wrapper function.
instance - The object to which the wrapped function was bound when it was called.
args - The list of positional arguments supplied when the decorated function was called.
kwargs - The dictionary of keyword arguments supplied when the decorated function was called.
Returns:
wrapped function
"""
capture_call_stack
(
wrapped
)
return
wrapped
(
*
args
,
**
kwargs
)
openedx/core/djangoapps/call_stack_manager/models.py
deleted
100644 → 0
View file @
5ecb9af2
"""
Dummy models.py file
Note -
django-nose loads models for tests, but only if the django app that the test is contained in has models itself.
This file is empty so that the unit tests can have models.
For call_stack_manager - models specific to tests are defined in tests.py
"""
openedx/core/djangoapps/call_stack_manager/tests.py
deleted
100644 → 0
View file @
5ecb9af2
"""
Test cases for Call Stack Manager
"""
import
collections
from
mock
import
patch
from
django.db
import
models
from
django.test
import
TestCase
from
openedx.core.djangoapps.call_stack_manager
import
donottrack
,
CallStackManager
,
CallStackMixin
,
trackit
from
openedx.core.djangoapps.call_stack_manager
import
core
class
ModelMixinCallStckMngr
(
CallStackMixin
,
models
.
Model
):
""" Test Model class which uses both CallStackManager, and CallStackMixin """
# override Manager objects
objects
=
CallStackManager
()
id_field
=
models
.
IntegerField
()
class
ModelMixin
(
CallStackMixin
,
models
.
Model
):
""" Test Model class that uses CallStackMixin but does not use CallStackManager """
id_field
=
models
.
IntegerField
()
class
ModelNothingCallStckMngr
(
models
.
Model
):
""" Test Model class that neither uses CallStackMixin nor CallStackManager """
id_field
=
models
.
IntegerField
()
class
ModelAnotherCallStckMngr
(
models
.
Model
):
""" Test Model class that only uses overridden Manager CallStackManager """
objects
=
CallStackManager
()
id_field
=
models
.
IntegerField
()
class
ModelWithCallStackMngr
(
models
.
Model
):
""" Parent class of ModelWithCallStckMngrChild """
id_field
=
models
.
IntegerField
()
class
ModelWithCallStckMngrChild
(
ModelWithCallStackMngr
):
""" Child class of ModelWithCallStackMngr """
objects
=
CallStackManager
()
child_id_field
=
models
.
IntegerField
()
@donottrack
(
ModelWithCallStackMngr
)
def
donottrack_subclass
():
""" function in which subclass and superclass calls QuerySetAPI """
ModelWithCallStackMngr
.
objects
.
filter
(
id_field
=
1
)
ModelWithCallStckMngrChild
.
objects
.
filter
(
child_id_field
=
1
)
def
track_without_donottrack
():
""" Function calling QuerySetAPI, another function, again QuerySetAPI """
ModelAnotherCallStckMngr
.
objects
.
filter
(
id_field
=
1
)
donottrack_child_func
()
ModelAnotherCallStckMngr
.
objects
.
filter
(
id_field
=
1
)
@donottrack
(
ModelAnotherCallStckMngr
)
def
donottrack_child_func
():
""" decorated child function """
# should not be tracked
ModelAnotherCallStckMngr
.
objects
.
filter
(
id_field
=
1
)
# should be tracked
ModelMixinCallStckMngr
.
objects
.
filter
(
id_field
=
1
)
@donottrack
(
ModelMixinCallStckMngr
)
def
donottrack_parent_func
():
""" decorated parent function """
# should not be tracked
ModelMixinCallStckMngr
.
objects
.
filter
(
id_field
=
1
)
# should be tracked
ModelAnotherCallStckMngr
.
objects
.
filter
(
id_field
=
1
)
donottrack_child_func
()
@donottrack
()
def
donottrack_func_parent
():
""" non-parameterized @donottrack decorated function calling child function """
ModelMixin
.
objects
.
all
()
donottrack_func_child
()
ModelMixin
.
objects
.
filter
(
id_field
=
1
)
@donottrack
()
def
donottrack_func_child
():
""" child decorated non-parameterized function """
# Should not be tracked
ModelMixin
.
objects
.
all
()
@trackit
def
trackit_func
():
""" Test function for track it function """
return
"hi"
class
ClassFortrackit
(
object
):
""" Test class for track it """
@trackit
def
trackit_method
(
self
):
""" Instance method for testing track it """
return
42
@trackit
@classmethod
def
trackit_class_method
(
cls
):
""" Classmethod for testing track it """
return
42
@donottrack
(
ClassFortrackit
.
trackit_class_method
)
def
donottrack_function
():
"""Testing function donottrack for a function"""
for
__
in
range
(
5
):
temp_var
=
ClassFortrackit
.
trackit_class_method
()
return
temp_var
@donottrack
()
def
donottrack_yield_func
():
""" Function testing yield in donottrack """
ModelMixinCallStckMngr
(
id_field
=
1
)
.
save
()
donottrack_function
()
yield
48
class
ClassReturingValue
(
object
):
""" Test class with a decorated method """
@donottrack
()
def
donottrack_check_with_return
(
self
,
argument
=
43
):
""" Function that returns something i.e. a wrapped function returning some value """
return
42
+
argument
@patch
(
'openedx.core.djangoapps.call_stack_manager.core.log.info'
)
@patch
(
'openedx.core.djangoapps.call_stack_manager.core.REGULAR_EXPS'
,
[])
class
TestingCallStackManager
(
TestCase
):
"""Tests for call_stack_manager
1. Tests CallStackManager QuerySetAPI functionality
2. Tests @donottrack decorator
"""
def
setUp
(
self
):
core
.
TRACK_FLAG
=
True
core
.
STACK_BOOK
=
collections
.
defaultdict
(
list
)
core
.
HALT_TRACKING
=
[]
super
(
TestingCallStackManager
,
self
)
.
setUp
()
def
test_save
(
self
,
log_capt
):
""" tests save() of CallStackMixin/ applies same for delete()
classes with CallStackMixin should participate in logging.
"""
ModelMixin
(
id_field
=
1
)
.
save
()
modelclass_logged
=
log_capt
.
call_args
[
0
][
2
]
self
.
assertEqual
(
modelclass_logged
,
ModelMixin
)
def
test_withoutmixin_save
(
self
,
log_capt
):
""" Tests save() of CallStackMixin/ applies same for delete()
classes without CallStackMixin should not participate in logging
"""
ModelAnotherCallStckMngr
(
id_field
=
1
)
.
save
()
self
.
assertEqual
(
len
(
log_capt
.
call_args_list
),
0
)
def
test_queryset
(
self
,
log_capt
):
""" Tests for Overriding QuerySet API
classes with CallStackManager should get logged.
"""
ModelAnotherCallStckMngr
(
id_field
=
1
)
.
save
()
ModelAnotherCallStckMngr
.
objects
.
filter
(
id_field
=
1
)
modelclass_logged
=
log_capt
.
call_args
[
0
][
2
]
self
.
assertEqual
(
ModelAnotherCallStckMngr
,
modelclass_logged
)
def
test_withoutqueryset
(
self
,
log_capt
):
""" Tests for Overriding QuerySet API
classes without CallStackManager should not get logged
"""
# create and save objects of class not overriding queryset API
ModelNothingCallStckMngr
(
id_field
=
1
)
.
save
()
# class not using Manager, should not get logged
ModelNothingCallStckMngr
.
objects
.
all
()
self
.
assertEqual
(
len
(
log_capt
.
call_args_list
),
0
)
def
test_donottrack
(
self
,
log_capt
):
""" Test for @donottrack
calls in decorated function should not get logged
"""
donottrack_func_parent
()
self
.
assertEqual
(
len
(
log_capt
.
call_args_list
),
0
)
def
test_parameterized_donottrack
(
self
,
log_capt
):
""" Test for parameterized @donottrack
classes specified in the decorator @donottrack should not get logged
"""
ModelAnotherCallStckMngr
(
id_field
=
1
)
.
save
()
ModelMixinCallStckMngr
(
id_field
=
1
)
.
save
()
donottrack_child_func
()
modelclass_logged
=
log_capt
.
call_args
[
0
][
2
]
self
.
assertEqual
(
ModelMixinCallStckMngr
,
modelclass_logged
)
def
test_nested_parameterized_donottrack
(
self
,
log_capt
):
""" Tests parameterized nested @donottrack
should not track call of classes specified in decorated with scope bounded to the respective class
"""
ModelAnotherCallStckMngr
(
id_field
=
1
)
.
save
()
donottrack_parent_func
()
modelclass_logged
=
log_capt
.
call_args_list
[
0
][
0
][
2
]
self
.
assertEqual
(
ModelAnotherCallStckMngr
,
modelclass_logged
)
def
test_nested_parameterized_donottrack_after
(
self
,
log_capt
):
""" Tests parameterized nested @donottrack
QuerySetAPI calls after calling function with @donottrack should get logged
"""
donottrack_child_func
()
# class with CallStackManager as Manager
ModelAnotherCallStckMngr
(
id_field
=
1
)
.
save
()
# test is this- that this should get called.
ModelAnotherCallStckMngr
.
objects
.
filter
(
id_field
=
1
)
first_in_log
=
log_capt
.
call_args_list
[
0
][
0
][
2
]
second_in_log
=
log_capt
.
call_args_list
[
1
][
0
][
2
]
self
.
assertEqual
(
ModelMixinCallStckMngr
,
first_in_log
)
self
.
assertEqual
(
ModelAnotherCallStckMngr
,
second_in_log
)
def
test_donottrack_called_in_func
(
self
,
log_capt
):
""" test for function which calls decorated function
functions without @donottrack decorator should log
"""
ModelAnotherCallStckMngr
(
id_field
=
1
)
.
save
()
ModelMixinCallStckMngr
(
id_field
=
1
)
.
save
()
track_without_donottrack
()
first_in_log
=
log_capt
.
call_args_list
[
0
][
0
][
2
]
second_in_log
=
log_capt
.
call_args_list
[
1
][
0
][
2
]
third_in_log
=
log_capt
.
call_args_list
[
2
][
0
][
2
]
fourth_in_log
=
log_capt
.
call_args_list
[
3
][
0
][
2
]
self
.
assertEqual
(
ModelMixinCallStckMngr
,
first_in_log
)
self
.
assertEqual
(
ModelAnotherCallStckMngr
,
second_in_log
)
self
.
assertEqual
(
ModelMixinCallStckMngr
,
third_in_log
)
self
.
assertEqual
(
ModelAnotherCallStckMngr
,
fourth_in_log
)
def
test_donottrack_child_too
(
self
,
log_capt
):
""" Test for inheritance
subclass should not be tracked when superclass is called in a @donottrack decorated function
"""
ModelWithCallStackMngr
(
id_field
=
1
)
.
save
()
ModelWithCallStckMngrChild
(
id_field
=
1
,
child_id_field
=
1
)
.
save
()
donottrack_subclass
()
self
.
assertEqual
(
len
(
log_capt
.
call_args_list
),
0
)
def
test_duplication
(
self
,
log_capt
):
""" Test for duplication of call stacks
should not log duplicated call stacks
"""
for
__
in
range
(
1
,
5
):
ModelMixinCallStckMngr
(
id_field
=
1
)
.
save
()
self
.
assertEqual
(
len
(
log_capt
.
call_args_list
),
1
)
def
test_donottrack_with_return
(
self
,
log_capt
):
""" Test for @donottrack
Checks if wrapper function returns the same value as wrapped function
"""
class_returning_value
=
ClassReturingValue
()
everything
=
class_returning_value
.
donottrack_check_with_return
(
argument
=
42
)
self
.
assertEqual
(
everything
,
84
)
self
.
assertEqual
(
len
(
log_capt
.
call_args_list
),
0
)
def
test_trackit_func
(
self
,
log_capt
):
""" Test track it for function """
var
=
trackit_func
()
self
.
assertEqual
(
"hi"
,
var
)
self
.
assertEqual
(
len
(
log_capt
.
call_args_list
),
1
)
def
test_trackit_instance_method
(
self
,
log_capt
):
""" Test track it for instance method """
cls
=
ClassFortrackit
()
var
=
cls
.
trackit_method
()
self
.
assertEqual
(
42
,
var
)
logged_function_module
=
log_capt
.
call_args_list
[
0
][
0
][
2
]
logged_function_name
=
log_capt
.
call_args_list
[
0
][
0
][
3
]
# check tracking the same function
self
.
assertEqual
(
ClassFortrackit
.
trackit_method
.
__name__
,
logged_function_name
)
self
.
assertEqual
(
ClassFortrackit
.
trackit_method
.
__module__
,
logged_function_module
)
def
test_trackit_class_method
(
self
,
log_capt
):
""" Test for class method """
var
=
ClassFortrackit
.
trackit_class_method
()
self
.
assertEqual
(
42
,
var
)
logged_function_module
=
log_capt
.
call_args_list
[
0
][
0
][
2
]
logged_function_name
=
log_capt
.
call_args_list
[
0
][
0
][
3
]
# check tracking the same function
self
.
assertEqual
(
ClassFortrackit
.
trackit_class_method
.
__name__
,
logged_function_name
)
self
.
assertEqual
(
ClassFortrackit
.
trackit_class_method
.
__module__
,
logged_function_module
)
def
test_yield
(
self
,
log_capt
):
""" Test for yield generator """
donottrack_yield_func
()
self
.
assertEqual
(
core
.
HALT_TRACKING
[
-
1
],
None
)
self
.
assertEqual
(
len
(
log_capt
.
call_args_list
),
0
)
def
test_donottrack_function
(
self
,
log_capt
):
""" Test donotrack for functions """
temp
=
donottrack_function
()
self
.
assertEqual
(
temp
,
42
)
self
.
assertEqual
(
len
(
log_capt
.
call_args_list
),
0
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment