I am currently in the process of writing a Flask application that routes endpoints to a variety of "Actions." These actions all implement a parent function called "run()"
In code:
import abc
class Action(object):
__metaclass__ = abc.ABCMeta
#classmethod
def authenticated(self):
print("bypassing action authentication")
return True
#classmethod
def authorized(self):
print("bypassing action authorization")
return True
#classmethod
#abc.abstractmethod
def execute(self):
raise NotImplementedError("must override execute!")
#classmethod
def response(self, executeResult):
return executeResult
#classmethod
def run(self):
result = ""
if self.authenticated() & self.authorized():
result = self.execute()
return self.response(result)
The intent is that all actually used actions are derived members of this Action class that bare-minimum implement an execute() function that differentiates them. Unfortunately, when I attempt to add routes for these
app.add_url_rule('/endone/', methods=['GET'], view_func=CoreActions.ActionOne.run)
app.add_url_rule('/endtwo/', methods=['GET'], view_func=CoreActions.ActionTwo.run)
I receive the following error:
AssertionError: View function mapping is overwriting an existing endpoint function: run
Does anyone know a possible solution to this issue? Thanks!
The common approach of generating view functions is to use Flask views. Subclass your Action class from flask.views.View, dispatch_request method is used instead of run:
import abc
from flask.views import View
class Action(View):
__metaclass__ = abc.ABCMeta
def authenticated(self):
print("bypassing action authentication")
return True
def authorized(self):
print("bypassing action authorization")
return True
#abc.abstractmethod
def execute(self):
raise NotImplementedError("must override execute!")
def response(self, executeResult):
return executeResult
def dispatch_request(self):
result = ""
if self.authenticated() & self.authorized():
result = self.execute()
return self.response(result)
And you can add routes using View.as_view() method which convert your class to view function:
app.add_url_rule(
'/endone/',
methods=['GET'],
view_func=CoreActions.ActionOne.as_view('endone')
)
Related
In my setUpClass I would like to create a resource in the database one time, which is then used for all of the tests in the class.
After I create the resource in setUpClass, I would like to perform assertions on it right then and there. However, I'm not sure how to call assertions in setUpClass, given that all of the assertion functions are instance methods, not class methods.
import unittest
class TestFoo(unittest.TestCase):
#classmethod
def setUpClass(cls):
cls.foo = cls.make_foo(name='bar')
# How would I assert that cls.foo['name'] == 'bar'?
# The following does not work, as the assertEquals function is not a class method
# cls.assertEquals(cls.foo['name'], 'bar')
#classmethod
def make_foo(cls, name):
# Calls a POST API to create a resource in the database
return {'name': name}
def test_foo_0(self):
# Do something with cls.foo
pass
def test_foo_1(self):
# do something else with cls.foo
pass
The only alternative I can think of is to raise an exception in setUpClass:
#classmethod
def setUpClass(cls):
cls.foo = cls.make_foo(name='bar')
if cls.foo['name'] != 'bar':
raise Exception("Failed to create resource, cannot do the tests")
Of course, I do not want to call the assertions from each test, as this will just duplicate the code.
Edit: I don't think this workaround is good, because the failure message will point to the self.assertFalse(self.flag) line, instead of the if cls.foo['name'] ~= 'bar' line. In addition, if you created multiple resources, this would need multiple flags to disambiguate.
flag=False
#classmethod
def setUpClass(cls):
cls.foo = cls.make_foo(name='bar')
if cls.foo['name'] != 'bar':
cls.flag=True
def setUp(self):
self.assertFalse(self.flag)
I am implementing the classes in Python 2.7 as below:
class MyClass(object):
def do_something(self):
"""
do something here
"""
class MyClassManager(object):
def get_my_class_obj(self):
"""read my_class_obj from db"""
return instance # instance has type MyClass
class MyClassUser(object):
my_class_obj = new MyClass() # this is a class variable
In main:
MyClassUser.my_class_obj = MyClassManager().get_my_class_obj()
"""
do a lot of different things else in main
"""
From somewhere else:
"""only when some condition happens"""
MyClassUser.my_class_obj.do_something()
Is there a way I can defer the read obj (read from db inside get_my_class_obj) process in MyClassManager until obj.do_something method is actually invoked? Provided that I have to call MyClassManager.get_my_class_obj for some setup at the beginning. Suppose the situation is in the context of a web server and do_something will only be invoked when there is some request but I need to set up it first
A very quick&dirty, dumbed down possible solution (and certainly not how I'd design this but anyway). This example assume your object only has an id (db primary key) and a name (stored in db).
import functools
class MyClassManager(object):
def get(self, object_id):
return MyClass(object_id)
def load(self, object):
obj.name = self.get_from_db(object.id)
def autoload(func):
#functools.wraps(func)
def wrapper(self, *args, **kw):
self._load()
return func(self, *args, **kw)
return wrapper
class MyClass(object):
def __init__(self, id, name=None):
self.id = id
self._name = name
self._loaded = name is not None
def _load(self):
if self._loaded:
return
MyManager.load(self)
#property
#autoload
def name(self):
return self._name
#autoload
def do_something(self):
# your code here
But really, don't do this if you're a beginner (and you wouldn't ask if you were not), use an existing working ORM instead. Unless it's for educational purpose of course, in which case, well, you might either learn a lot or give up in despair - or both ;-)
In the Flask-RESTful example application posted here, the TODOS collection is a global variable.
After the Todo Resource is registered:
api.add_resource(Todo, '/todos/<string:todo_id>')
The Todo methods access the global TODOS variable when web requests are processed.
Instead, I want to instantiate the API within a class and pass a TODOS collection that is a class variable rather than a global variable.
When using Flask-RESTful, what is the proper way to allow methods in a Resource class to gain access to a variable provided by the calling class without using global variables?
Looks like I didn't understand you the first time, You can just use a classmethod to construct your API. Then add it as a resource
from flask import Flask
from flask.ext.restful import Api
class SomeApi(Resource):
def get(self):
return self.response
#classmethod
def make_api(cls, response):
cls.response = response
return cls
class KillerApp(object):
def __init__(self):
self.app = Flask()
app_api = Api(self.app)
MyApi = SomeAPI.make_api({"key": "value"})
app_api.add_resource(MyApi, "/api/path")
def run(self)
self.app.run()
KillerApp().run()
add_resource accepts two arguments, resource_class_args and resource_class_kwargs, used to pass arguments to the constructor. (source)
So you could have a Resource:
from flask_restful import Resource
class TodoNext(Resource):
def __init__(self, **kwargs):
# smart_engine is a black box dependency
self.smart_engine = kwargs['smart_engine']
def get(self):
return self.smart_engine.next_todo()
You can inject the required dependency into TodoNext like so:
smart_engine = SmartEngine()
api.add_resource(TodoNext, '/next',
resource_class_kwargs={ 'smart_engine': smart_engine })
based on #Greg answer I've added an initialization check in the init method:
creating and calling Todo Resource class for flask-restful api:
todo = Todo.create(InMemoryTodoRepository())
api.add_resource(todo, '/api/todos/<todo_id>')
The Todo Resource class:
from flask_restful import reqparse, abort, Resource
from server.ApiResources.DTOs.TodoDTO import TodoDTO
from server.Repositories.ITodoRepository import ITodoRepository
from server.Utils.Exceptions import InvalidInstantiationError
from server.Utils.GeneralUtils import member_exists
class Todo(Resource):
"""shows a single todo item and lets you delete a todo item
use the 'create' class method to instantiate the class
"""
def __init__(self):
if not member_exists(self, "todo_repository", of_type=ITodoRepository):
raise InvalidInstantiationError("Todo", "todo_repository", "ITodoRepository", "create")
self._parser = reqparse.RequestParser()
self._parser.add_argument('task', type=str)
#classmethod
def create(cls, todo_repository):
"""
:param todo_repository: an instance of ITodoRepository
:return: class object of Todo Resource
"""
cls.todo_repository = todo_repository
return cls
the member_exists helper methods:
def member_exists(obj, member, of_type):
member_value = getattr(obj, member, None)
if member_value is None:
return False
if not isinstance(member_value, of_type):
return False
return True
and the custom exception class:
class InvalidInstantiationError(Exception):
def __init__(self, origin_class_name, missing_argument_name, missing_argument_type, instantiation_method_to_use):
message = """Invalid instantiation for class '{class_name}':
missing instantiation argument '{arg}' of type '{arg_type}'.
Please use the '{method_name}' factory class method""" \
.format(class_name=origin_class_name,
arg=missing_argument_name,
arg_type=missing_argument_type,
method_name=instantiation_method_to_use)
# Call the base class constructor with the parameters it needs
super(InvalidInstantiationError, self).__init__(message)
Thus, trying to use the default constructor will end up in getting this exception:
server.Utils.Exceptions.InvalidInstantiationError: Invalid instantiation for class 'Todo':
missing instantiation argument 'todo_repository' of type 'ITodoRepository'.
Please use the 'create' factory class method
edit: this can be useful for using dependency injection with flask-restful api Resource classes (with or without IoC)
edit 2:
we can even go cleaner and add another help function (ready to import):
def must_have(obj, member, of_type, use_method):
if not member_exists(obj, member, of_type=of_type):
raise InvalidInstantiationError(obj.__class__.__name__,
member,
of_type.__name__,
use_method)
and then use it in the constructor like that:
from server.Utils.GeneralUtils import must_have
class Todo(Resource):
def __init__(self):
must_have(self,
member="todo_repository",
of_type=ITodoRepository,
use_method=Todo.create.__name__)
I face a weird variable domain issue when trying to define a middleware for django that will keep the request in the thread context. the first code section create an error when I try to access the method "get" from the API in the views file. the second code example works great. why???
Example 1 (does not work):
class ContextHandler(object):
#_LOCALS = threading.local()
def process_request(self, request):
self._LOCALS = threading.local()
self._LOCALS.x = "alon"
return None
Example 2 (works):
class ContextHandler(object):
_LOCALS = threading.local()
def process_request(self, request):
self._LOCALS.x = "alon"
return None
common get method:
#classmethod
def get(cls):
return getattr(cls._LOCALS, 'x', None)
Thanks!
In the first example you have no class property _LOCALS, it is instance property. So in first case ContextHandler._LOCALS is None and cls in get() is ContextHandler.
If you want thread safe code don't stick with #classmethod and
class ContextHandler(object):
_LOCALS = threading.local()
as far as I know class definition is processed only once (most likely in main thread). I'd rather initialize _LOCALS in process_request() and make get() instance method:
class ContextHandler(object):
def process_request(self, request):
self._LOCALS = threading.local()
self._LOCALS.x = "alon"
return None
def get(self):
return getattr(self._LOCALS, 'x', None)
Turns out that declaring self.someproperty equals declaring .someproperty if the class has a class/static level variable.
I have several view classes in my Python Pyramid project added via add_handler:
config.add_handler('export_index', '/export', handler=ExportViews, action='index')
class ExportViews(ConfigViewBase):
#action(request_method='POST', name='index',
request_param='ftp_export.form.submitted')
#action(request_method='POST', name='index', xhr=True, renderer='json',
request_param='ftp_export.form.submitted')
def ftp_export(self):
#process form
return {}
#action(request_method='GET')
def index(self):
return {}
Is it possible to do the same having:
config.add_handler('export_index', '/export', handler=ExportViews)
class ExportViews(ConfigViewBase):
#action(request_method='POST',
request_param='ftp_export.form.submitted')
#action(request_method='POST', xhr=True, renderer='json',
request_param='ftp_export.form.submitted')
def ftp_export(self):
#process form
return {}
#action(request_method='GET')
def __call__(self):
return {}
So the __call__ was called when browser gets page, and ftp_export should be called when I post form on the same page. Now I get page not found error
Thank You.
You can do this with traversal. Traversal rocks :)
class Root(object):
def __getitem__(self, name):
if name == "export":
return ExportSomething(self)
if name == "export_something_else":
return ExportSomethingElse(self)
class ExportSomething(object):
implements(IViewable, IExportable)
def view(self, request):
return "Hi"
def export(self, request):
return "something else"
#view_config(context=IViewable, request_method="GET")
def view_viewable(conext, request):
return context.view(request)
#view_config(context=IExportable, request_method="POST")
def export_exportable(conext, request):
return context.export(request)
then you can implement a bunch of ExportThis and ExportThat classes, make them implement IViewable and IExportable interfaces, make them returned from Root.__getitem__ and everything magically works. Or, if you don't need multiple exporters you can omit interfaces and bind views directly to ExportSomething class. Or you can instantiate different instances of ExportSomething in getitem and make it to... I don't know, view/export different files/reports.