Dynamically generate Flask-RESTPlus routes - flask-restplus

I am trying to abstract away some of the route class logic (i.e. I am looking to dynamically generate routes). api.add_resource seemed like the right place to do this.
So this is what I am trying to do:
# app.py
from flask import Flask
from flask_restplus import Api, Resource, fields
from mylib import MyPost
# Define my model
json_model = api.schema_model(...)
api.add_resource(
MyPost,
'/acme',
resource_class_kwargs={"json_model": json_model}
)
And then in mylib:
# mylib.py
def validate_endpoint(f):
def wrapper(*args, **kwargs):
return api.expect(json_fprint)(f(*args, **kwargs))
return wrapper
class MyPost(Resource):
def __init__(self, *args, **kwargs):
# Passed in via api.add_resource
self.api = args[0]
self.json_model = kwargs['json_model']
# I can't do this because I don't have access to 'api' here...
# #api.expect(json_model)
# So I am trying to make this work
#validate_endpoint
def post(self):
return {"data":'some data'}, 200
I don’t have access to the global api object here so I can’t call #api.expect(json_model). But I do have access to api and json_model inside of the post method. Which is why I am trying to create my own validate_endpoint decorator.
This does not work though. Is what I am trying to do here even possible? Is there a better approach I should be taking?

Stop using flask-restplus. Thats the most valuable answer I can give you (and anyone else).
Ownership is not there
Flask-restplus is a fork of flask-restful. Some engineers started developing features that suited them. The core guy has ghosted the project so its been officially forked again as Flask-Restx.
Poorly designed
I used to love flask when I was a yout’. I’ve realized since then that having global request, application, config that all magically update is not a good design. Their application factory pattern (to which flask-restplus conforms) is a style of statefully mutating the application object. First of all, Its hard to test. Second of all, it means that flask-restplus is wrapping the app and therefore all of the requests/handlers. How can anyone thing thats a good thing? A library whose main feature is endpoint documentation has its filthy hands all over every one of my requests?? (btw, this is whats leading to your problem above) Because my post is serious and thoughtful I’m skipping my thoughts on the Resource class pattern as it would probably push me into the waters of ranting.
Random Feature Set
A good library has a single purpose and it does that single thing well. Flask-restplus does 15 things (masking, swagger generation, postman generation, marshaling, request arg validation). Some features you can’t even tell are in the libraries code by reading the docs.
My solution to your problem
If you want to document your code via function decorators and models use a tool that does that alone and does it well. Use one that won’t touch your handlers or effect your actual request decorators. Use oapispec for swagger generation. For the other features of flask-restplus you’ve got marshmallow for marshaling request/response data, pydantic for validating request objects and args, and so on.
btw, I know all this because I had to build an api with it. After weeks of fighting the framework I forked it, ripped it apart, created oapispec, and trashed it from the project.

Related

Switching multiple inheritance via mixins to composition but keep the same API

Firstly, thank you for taking the time to read and input. It is greatly appreciated.
Question: What kind of approach can we take to keep the same public API of a class currently using multiple mixins but refactor it internally to be composed of objects that do the same work as the mixin. Autocomplete is a must (so runtime dynamics are kind of out such as hacking things on via __getattr__ or similar - I know this depends on the runtime environment i.e ipython vs pycharm etc, for the sake of this question, assume pycharm which cannot leverage __dir__ I think fully.
Accompanying Information:
I am writing a little assertion library in python and I have a core class which is instantiated with a value and subsequently inherits various assertion capabilities against that value via a growing number of mixin classes:
class Asserto(StringMixin, RegexMixin):
def __init__(self, value: typing.Any, type_of: str = AssertTypes.HARD, description: typing.Optional[str] = None):
self.value = value
self.type_of = type_of
self.description = description
These mixin classes offer various assertion methods for particular types, here is a quick example of one:
from __future__ import annotations
class StringMixin:
def ends_with(self, suffix: str) -> StringMixin:
if not self.value.endswith(suffix):
self.error(f"{self.value} did not end with {suffix}")
def starts_with(self, prefix: str) -> StringMixin:
if not self.value.startswith(prefix):
self.error(f"{self.value} did not end with {prefix}")
I would like to refactor the Asserto class to compose itself of various implementations of some sort of Assertable interface rather than clobber together a god class here with Mixins, I'm likely to have 10+ Mixins by the time I am finished.
Is there a way to achieve the same public facing API as this mixins setup so that client code has access to everything through the Asserto(value).check_something(...) but using composition internally?
I could define every single method in the Asserto class that just delegate to the appropriate concrete obj internally but then I am just making a massive god class anyway and the composition feels like a pointless endeavour in that instance?
for example in client code, I'd like all the current mixins methods to be available on an Asserto instance with autocomplete.
def test_something():
Asserto("foo").ends_with("oo")
Thank you for your time. Perhaps using the mixin approach is the correct way here, but it feels kind of clunky.

Pyramid routing to class methods

From the pyramid documentation, there exists an attr argument on configurator's add_view that states:
The attr value allows you to vary the method attribute used
to obtain the response. For example, if your view was a
class, and the class has a method named index and you
wanted to use this method instead of the class' __call__
method to return the response, you'd say attr="index" in the
view configuration for the view.
With this in mind, I'd like to route all requests under /myrequest to the class MyRequest. Given the following class:
#view_defaults(renderer='json')
class MyHandler(object):
def __init__(self, request):
self.request = request
def start(self):
return {'success': True}
def end(self):
return {'success': True}
It would seem the way to do this would be in the configuration, add these lines:
config.add_view(MyHandler, '/myrequest', attr='start')
config.add_view(MyHandler, '/myrequest', attr='end')
and so on, for all the methods I want routed under MyHandler. Unfortunately this doesn't work. The correct way to do this appears to be:
config.add_route('myroutestart', '/myroute/start')
config.add_route('myrouteend', '/myroute/end')
config.add_view(MyHandler, attr='start', route_name='myroutestart')
config.add_view(MyHandler, attr='end', route_name='myrouteend')
This seems like an awful lot of boilerplate. Is there a way to bring this down to 1 line per route? Or more ideally, 1 line per class?
Example #4 in the Route and View Examples from The Pyramid Community Cookbook v0.2, Pyramid for Pylons Users, offers the following.
# Pyramid
config.add_route("help", "/help/{action}")
#view_config(route_name="help", match_param="action=help", ...)
def help(self): # In some arbitrary class.
...
Although this cookbook recipe mentions pyramid_handlers as one option to do this, the article "Outgrowing Pyramid Handlers" by one of the maintainers of Pyramid encourages the use of Pyramid's configuration.

What is the preferred way to call synchronous code from async routes in Sanic?

I'm researching Sanic as we're looking for alternatives to our flask-based rest services. I'm intriguied by the async nature of sanic, but I know that we'll bump into a lot of code that simply won't support async (we use a ton of boto3 and also some ORMs on top of DynamoDB for example, none of which support awaiting).
So: I need to find the cleanest way of being able to run synchronous code inside an async framework like Sanic. In python 3.7 there's the asyncio.create_task call which I'm finding interesting.
Wondering if this would be a possible way to go:
main.py:
#default boilerplate sanic code excluded for brevity
from app_logic import AppLogic
#app.route("/")
async def test(request):
task = await asyncio.create_task(AppLogic.sync_request('https://stuff.com'))
return json({"hello": "world", 'status_code': task.status_code})
app_logic.py:
import requests
class AppLogic(object):
#staticmethod
async def sync_request(url='https://myurl.com'):
#Some non-async library/code thingy
print('requesting the thing')
return requests.get(url)
This seems to work, and the the returned task object is a regular requests response.
However, I have no idea if this is "safe" - eg I'm not sure how I can investigate the event loop and verify that it's not blocking in any way. I'm sure there's also other reasons for this approach being completely dumb, so lay them on me :-)

Add renderer in #view_config from configuration?

How do I supply a configured value to a #view_config-decorated function or class?
E.g.
#view_config(route_name='example', renderer=some_config['template.name'])
class MyClass(BaseView):
...
Or
#view_defaults(route_name='example', renderer=some_config['template.name2'])
class MyClass2(BaseView):
...
Or
#view_config(route_name='example', renderer=some_config['template.name3'])
def method3(request):
...
It's very hard to know where to start, as I'm trying to edit a pyramid plugin, which pulls together its config in an includeme function, so it doesn't have anything obvious that I can include, and it's hard to know what's available to the #view_config decorator.
You can add views using declarative configuration (what you are doing now using #view_config or alternatively using imperative configuration by calling config.add_view() method.
In this case, as you need to access the Pyramid registry and settings file, it is easier to do adding the views imperatively.
In your __init__.py you can do:
settings = config.registry.settings
# You need to call config.add_route("foobar") to map view to URL also
config.add_view('views.MyClass', route_name="foobar", renderer=settings['template.name3'])
Then in your views.py:
class MyClass(BaseView):
pass
#view_config() and add_view() arguments are equal.
I thin kyou can also mix view_config and add_view() arguments for the same view, but I am not sure aobut this. Hope this helps.

Plone 4 search members with extended profiles

There is a need to extend memberdata on Plone 4 with certain schema and at the same time provide an efficient (that is, much better than linear) search among those profiles.
collective.examples.userdata seems to be an example on how to make userdata The Right Way, but what about searches? Are there any efficient search solutions, for example, using the catalog?
There is such thing as membrane, which can map users to content, but uses
Archetypes and quite old a product (maybe, my impression is wrong).
Still, for example, mapping userdata to Dexterity type instances could be fine.
The question is, is there any ready code out there or custom solution will be needed?
No, the only ready solution out there, as you said, is membrane. But IMO it's a complex and specific product so I don't think you really need it.
To reach your goal, you'll need a bit of development. More or less the way would be:
insert your users into the catalog
add all needed new indexes
create your custom search form with z3c.form
This is an overview (not detailed howto) of an implementation:
Catalog tool done similarly to the reference_catalog from Archetypes. The most essential parts:
from Products.ZCatalog.ZCatalog import ZCatalog
class MemberdataCatalog(UniqueObject, ZCatalog):
implements(IMemberdataCatalog)
...
security.declareProtected(ManageZCatalogEntries, 'catalog_object')
def catalog_object(self, obj, uid=None, idxs=[],
update_metadata=1, pghandler=None):
w = obj
if not IIndexableObject.providedBy(obj):
wrapper = component.queryMultiAdapter((obj, self), IIndexableObject)
if wrapper is not None:
w = wrapper
ZCatalog.catalog_object(self, w, w and str("/".join(w.getPhysicalPath())), idxs,
update_metadata, pghandler=pghandler)
(with all GenericSetup things, also can be done similarly to Archetypes)
Subscribers for IPrincipalCreatedEvent, IPrincipalDeletedEvent, IConfigurationChangedEvent
(the latter one needs event.context.class._name_ in ('UserDataConfiglet', 'PersonalPreferencesPanel', 'UserDataPanel') to be handled - unfortunately, Plone has no specific events for profile data changes). See PAS on how those work and which parameters
event handlers receive.
A view /memberdata/username for the catalog to address and reindex those users. The "username" done by bobo traverse and returns an wrapped user with properties,
needed for indexes and metadata.
The http://plone.org/products/collective.examples.userdata is a good guide how to actually extend the user profile.
Apart from that, an adapter is needed
class IndexableAdapter(EnhancedUserDataPanelAdapter):
implements(IIndexableObject)
adapts(IMemberData, IMemberdataCatalog)
def __init__(self, context, catalog):
self.context = context
self.userid = context.getId()
self.catalog = catalog
def getPhysicalPath(self):
return make_physical_path(self.userid) # path to the view, which "fakes" an object
def __getattr__(self, name):
""" Proxing attribute accesses. """
return getattr(self.context, name)
# Specific indexer
def SearchableTextIntra(self):
...
Here EnhancedUserDataPanelAdapter has been derived and extended from UserDataPanelAdapter.
The IMemberdataCatalog is the interface of the catalog.
It is important to put everything into metadata, even width/height of the portrait,
because using .getObject() made the whole thing hundreds of times (!) slower.
The group memberships were handled separately, because there are no events, which
signify changes in the groups, needed to reindex some or all memebrs.

Resources