I would like to have a nose plugin that if enabled will just generate an HTML page with all the tests run which contain also the request/responses for each test.
This would be useful to generate some documentation FROM the tests.
Now I don't think it's too hard, but to do that I guess I have to intercept the self.client.get/post/whatever methods.
To do that however it looks like I have to replace the default TestCase class with something else, and I would like to not force any project that wants to use this tool to do have to do that.
This kind of works for example
import json
from mock import patch
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.client import Client
class DebuggingClient(Client):
def __init__(self, *args, **kwargs):
super(DebuggingClient, self).__init__(*args, **kwargs)
def get(self, url, *args, **kwargs):
print("Calling endpoint {} with args {} and kwargs {}".format(url, args, kwargs))
response = super(DebuggingClient, self).get(url, *args, **kwargs)
print("Obtained response {}:{}".format(response.status_code, response.content))
return response
class TestSimpleApi(TestCase):
# TODO: how do I make this happen inside the plugin??
def _pre_setup(self):
super(TestSimpleApi, self)._pre_setup()
self.client = DebuggingClient()
def test_getting_numbers_returns_list_of_first_numbers(self):
url = reverse('gen_numbers')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
data = json.loads(response.content)
self.assertEqual(data, list(range(10)))
But I would like do do that just depending on a nose-plugin enabled.
Any idea how? I can't find a way to change the base class just from a nose-plugin (unless maybe doing some crazy monkey patching).
The project is here by the way even if far from working yet..
https://github.com/AndreaCrotti/django-docs-from-tests
Related
I would like to use the flask-htpasswd but I need to authenticate against custom htpasswd file. My use case is that for specific endpoint, for instance http://localhost:5000/api/tenant it will look up the tenant's directory and authenticates using his htpasswd if there is one.
I am wondering how to perform that. I am not that deep into Python, tough currently digging into it.
My solution was to take what exists and adapt it to my specific use case (inherit in Python):
from flask_htpasswd import HtPasswdAuth
from passlib.apache import HtpasswdFile
from crypt import crypt
import logging
import os
import messages as msg
import settings
from functools import wraps
log = logging.getLogger(__name__) # pylint: disable=invalid-name
class serviceHtPasswdAuth(HtPasswdAuth):
def __init__(self, app):
super().__init__(app)
self.users = None
def required(self, func):
"""
Decorator function with basic and token authentication handler
"""
#wraps(func)
def decorated(*args, **kwargs):
"""
Actual wrapper to run the auth checks.
"""
# here an htpasswd file for that specific tenant id is read
try:
self.users = HtpasswdFile(path=os.path.join(settings.TENANTS_DIRECTORY, kwargs['tenant_id'], 'htpasswd'))
except(FileNotFoundError) as error:
log.error(msg.MISSING_TENANT_HTPASSWD_FILE)
return {"error": msg.MISSING_TENANT_HTPASSWD_FILE}, 404
is_valid, user = self.authenticate()
if not is_valid:
return self.auth_failed()
kwargs['user'] = user
return func(*args, **kwargs)
return decorated
I'm having trouble saving MotorEngine instance using Tornado handler. Below are excerpts of code which I shortened for brevity.
if I replace motorengine imports with mongoengine the instance gets saved properly.
# model
from motorengine.document import Document
from motorengine.fields import *
class Test(Document):
nameTest = StringField(required=True)
numberTest = DecimalField(required=True)
boolTest = BooleanField(required=True)
# handler
class TestHandler(BaseHandler):
#tornado.gen.coroutine
def post(self, *args, **kwargs):
response = self._service.save_test()
print(response)
self.write('')
self.finish()
# save method
from models import Test
def save_test(self):
yield Test.objects.create(nameTest="foobar", boolTest=False, numberTest=123)
Are there any apparent errors in my code? Async programming is not my strong side.
:EDIT:
As per Ben's answer, this is the code that works
# handler
#tornado.gen.coroutine
def post(self, *args, **kwargs):
yield self._service.save_test()
# save method
#tornado.gen.coroutine
def save_test(self):
yield Test.objects.create(nameTest="foobar", boolTest=False, numberTest=123)
save_test (and any function which uses yield in this way) must have the #gen.coroutine decorator, and when you call it (or any other coroutine) in post() you must use yield save_test()
I've seen the posts on passing GET parameters and hardcoded parameters here and here.
What I am trying to do is pass POST parameters to a custom decorator. The route is not actually rendering a page but rather processing some stuff and sending the results back through an AJAX call.
The decorator looks like this:
# app/util.py
from functools import wraps
from models import data
# custom decorator to validate symbol
def symbol_valid():
def decorator(func):
#wraps(func)
def decorated_function(symbol, *args, **kwargs):
if not data.validate_symbol(symbol):
return jsonify({'status': 'fail'})
return func(*args, **kwargs)
return decorated_function
return decorator
The view looks something like this:
# app/views/matrix_blueprint.py
from flask import Blueprint, request, jsonify
from ..models import data
from ..util import symbol_valid
matrix_blueprint = Blueprint('matrix_blueprint', __name__)
# routing for the ajax call to return symbol details
#matrix_blueprint.route('/route_line', methods=['POST'])
#symbol_valid
def route_line():
symbol = request.form['symbol'].upper()
result = data.get_information(symbol)
return jsonify(**result)
I understand that I can actually call #symbol_valid() when I pass a parameter through GET like this /quote_line/<symbol> but I need to POST.
The question then is how can my decorator access the POSTed variable?
Simple solution. Imported Flask's request module into the util.py module which contains the decorator. Removed the outer function as well.
See code:
# app/util.py
from flask import request # <- added
from functools import wraps
from models import data
# custom decorator to validate symbol
def symbol_valid(func):
#wraps(func)
def decorated_function(*args, **kwargs): # <- removed symbol arg
symbol = request.form['symbol'] # <- paramter is in the request object
if not data.validate_symbol(symbol):
return jsonify({'status': 'fail'})
return func(*args, **kwargs)
return symbol_valid
The decorator accept a func parameter. You must use your decorator like #symbol_valid() or make the function symbol_valid accept a func parameter.
If you are doing it right, you can access the request object anywhere during the request cycle. It just works.
I'd like to do something like this:
class Basehandler(webapp.RequestHandler):
def __init__(self):
if checkforspecialcase: #check something that always needs to be handled
return SpecialCaseHandler.get()
class NormalHandler(Basehandler):
def get(self):
print 'hello world'
return
class SpecialCaseHandler(Basehandler):
def get(self):
print 'hello special world'
return
The idea is that no matter what handler is initially called, if a certain case is met, we basically switch to another handler.
I'm pretty new to python, so I'm not sure whether what I'm trying to do is possible. Or whether this is the best approach. What I'm really trying to do is make sure to show someone the complete-your-profile page if they've started the registration process but haven't completed it... no matter what request they're making. So the "checkforspecialcase" looks at their sessions and checks for incomplete info.
To keep things DRY, use the Template Method pattern
class BaseHandler(webapp.RequestHandler):
def DoGet(self, *args):
''' defined in derived classes, actual per-handler get() logic'''
pass
def get(self, *args):
# don't get caught in endless redirects!
if specialCase and not self.request.path.startswith('/special'):
self.redirect('/special')
else:
self.DoGet(*args)
class NormalHandler(BaseHandler):
def DoGet(self, *args):
# normal stuff
class SpecialHandler(BaseHandler):
def DoGet(self, *args):
# SPECIAL stuff
WSGIApplication routes incoming requests based on the URL. For example,
application = webapp.WSGIApplication(
[('/special-case', SpecialCaseHandler)])
When checkforspecialcase passes, you can use self.redirect('/special-case').
Your Basehandler could just implement a get() that checks for the special case and either redirects or calls self.view(), and each handler could implement view() (or whatever you'd like to call it) rather than get().
I'm not really into writing a class for each of my handlers, or using inheritance so conspicuously, so I'd recommend rolling decorators like these:
routes = []
def get (route):
def makeHandler (handle, *args, **kwargs):
class Handler (webapp.RequestHandler):
def get (self, *args, **kwargs):
shouldRedirectToCompleteProfile = # do your test
if shouldRedirectToCompleteProfile:
self.redirect('/special-case')
else:
handle(self, *args, **kwargs)
routes.append((route, Handler))
return Handler
return makeHandler
def post (route):
def makeHandler (handle, *args, **kwargs):
class Handler (webapp.RequestHandler):
def post (self, *args, **kwargs):
handle(self, *args, **kwargs)
routes.append((route, Handler))
return Handler
return makeHandler
#get('/')
def home (ctx):
# <...>
#get('/whatever/(.*)/(.*)')
def whatever (ctx, whatever0, whatever1):
# <...>
#post('/submit')
def submit (ctx):
# <...>
application = webapp.WSGIApplication(routes)
I want to use some middleware I wrote across the whole of my site (large # of pages, so I chose not to use decorators as I wanted to use the code for all pages). Only issue is that I don't want to use the middleware for the admin code, and it seems to be active on them.
Is there any way I can configure the settings.py or urls.py perhaps, or maybe something in the code to prevent it from executing on pages in the admin system?
Any help much appreciated,
Cheers
Paul
The main reason I wanted to do this was down to using an XML parser in the middleware which was messing up non-XML downloads. I have put some additional code for detecting if the code is XML and not trying to parse anything that it shouldn't.
For other middleware where this wouldn't be convenient, I'll probably use the method piquadrat outlines above, or maybe just use a view decorator - Cheers piquadrat!
A general way would be (based on piquadrat's answer)
def process_request(self, request):
if request.path.startswith(reverse('admin:index')):
return None
# rest of method
This way if someone changes /admin/ to /django_admin/ you are still covered.
You could check the path in process_request (and any other process_*-methods in your middleware)
def process_request(self, request):
if request.path.startswith('/admin/'):
return None
# rest of method
def process_response(self, request, response):
if request.path.startswith('/admin/'):
return response
# rest of method
You don't need to muck around with paths.
If you want to exclude a single middleware from a view, you must first import that middleware and do:
from django.utils.decorators import decorator_from_middleware
from your.path.middlewares import MiddleWareYouWantToExclude
#decorator_from_middleware(MiddleWareYouWantToExclude)
def your_view(request):
....
If you want to exclude ALL middleware regardless of what they are/do, do this:
from django.conf import settings
from django.utils.module_loading import import_string
from django.utils.decorators import decorator_from_middleware
def your_view(request):
...
# loop over ALL the active middleware used by the app, import them
# and add them to the `decorator_from_middleware` decorator recursively
for m in [import_string(s) for s in settings.MIDDLEWARE]:
your_view = decorator_from_middleware(m)(your_view)
middleware functions are basically called for every request, including image src, api, form, ajax calls etc. somehow people reckon it is only called before and after view. This not only can cause performance concern, but also is hard to change.
I do not recommend path comparison unless using reverse, on the one hand, reference modification may create more trouble; on the other hand, if conditionals seems messy.
here I provide a solution for converting middleware to decorator, this method requires you to add decorator manually to all views. Fully optimised, you can make decisions which right middleware is for the right view
before I had:
class AjaxMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
def is_ajax(self):
return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
request.is_ajax = is_ajax.__get__(request)
response = self.get_response(request)
return response
class DeletionMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
from staff.models import Deletion
response = self.get_response(request)
Deletion.auto_clean_up()
return response
MIDDLEWARE = [
'common.middleware.DeletionMiddleware',
'common.middleware.AjaxMiddleware',
]
now I created a decorators.py:
from django.contrib.auth.models import User
class Shape:
#staticmethod
def before(request):
pass
#staticmethod
def after(request):
pass
# All default to call, pass in as excluder param to escape
# Part default not to call, pass in as includer param to execute
class All(Shape): pass
class Part(Shape): pass
class Mixins:
class Online(Part):
#staticmethod
def after(request):
if request.user.is_authenticated:
request.user.profile.visit()
class Deletion(All):
#staticmethod
def after(request):
from staff.models import Deletion
Deletion.auto_clean_up()
class Ajax(All):
#staticmethod
def before(request):
def is_ajax(self):
return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
request.is_ajax = is_ajax.__get__(request)
def common_dec(excluders=[], includers=[]):
def common(function):
# def wrapper_func(*args, **kwargs):
# return function(*args, **kwargs)
# return wrapper_func
"""
common view that returns a template
does not incldue images and cdn access
"""
def wrapper_func(request, *args, **kwargs):
# iterate all classes in Mixins
alls = All.__subclasses__()
parts = Part.__subclasses__()
list(map(lambda cls:cls.before(request) if cls.__name__ not in excluders else 0, alls))
list(map(lambda cls:cls.before(request) if cls.__name__ in includers else 0, parts))
response = function(request, *args, **kwargs)
list(map(lambda cls:cls.after(request) if cls.__name__ not in excluders else 0, alls))
list(map(lambda cls:cls.after(request) if cls.__name__ in includers else 0, parts))
return response
return wrapper_func
return common
def common_mix(excluders=[], includers=[]):
class CommonMixin:
def dispatch(self, request, *args, **kwargs):
alls = All.__subclasses__()
parts = Part.__subclasses__()
list(map(lambda cls:cls.before(request) if cls.__name__ not in excluders else 0, alls))
list(map(lambda cls:cls.before(request) if cls.__name__ in includers else 0, parts))
response = super().dispatch(request, *args, **kwargs)
list(map(lambda cls:cls.after(request) if cls.__name__ not in excluders else 0, alls))
list(map(lambda cls:cls.after(request) if cls.__name__ in includers else 0, parts))
return response
return CommonMixin
which I can do
# common_dec implement all Shape.All middleware
#common_dec()
def index(request):
return render(request, 'index.html')
# this will except Deletion middleware, and include Online middleware, and execute all Shape.All
#common_dec(['Deletion'], ['Online'])
def index(request):
return render(request, 'index.html')
for class based view:
# this will execute all Shape.All, and include Online which is a Shape.Part
class ArticleDetailView(common_mix(excluders=['Online']), LoginRequiredMixin, DetailView):
pass
I have give up middleware since it lacks flexibility, this method largely saves performance and is implementable for large scopes view definition, will not causing any bug.
if you want to add a middleware, simply add a class to Mixin inheriting Shape.All, then it will be called for all decorated view function and classes. if you have a new type of view function that needs a new middleware, define it as Part and include it in decorator .it is also extremely convenient to exclude some middleware in particular view.