When I try to run the server I get syntax error. But there isn`t any incorrect using of syntax. Please help to correct this issue! Issue image
from blacksheep.server.application import Application
from blacksheep.server.controllers import Controller, get, post
from blacksheep.cookies import Cookie
from blacksheep.messages import Response
from easy_cryptography.hash.hash_funct import compare_hash
from app.configuration import AUTHORIZED
from models import Doctors
from pony.orm import *
class Home(Controller):
#get("/")
def index(self):
return self.view()
class Patients(Controller):
#post("/patients")
def patients(self, login: str, password: str):
if Doctors.exists(login) and (compare_hash(password, Doctors.get_for_update(login = login).password)):
patients = Patients.select()
response = self.view(patients=patients)
response.set_cookie(Cookie(AUTHORIZED,True))
return response
else:
return "{'message':'Неверный логин или пароль'}"
It looks like you are missing the async keyword before def index(self):
Another bug I can see is that you are not binding the parameters to your patients method correctly from the #post decorator.
Related
This might be a newbie question, but I can't get dependency_overrides to work for testing.
Following the docs this should be simple to implement but I'm missing something…
Consider the following code:
In main.py:
from fastapi import FastAPI
from routes import router
app = FastAPI()
app.include_router(router)
In routes.py:
from fastapi import APIRouter, status
from fastapi.param_functions import Depends
from fastapi.responses import JSONResponse
from authentication import Authentication
router = APIRouter()
#router.get("/list/", dependencies=[Depends(Authentication(role=user))])
async def return_all():
response = JSONResponse(
status_code=status.HTTP_200_OK,
content="Here is all the objects!"
)
return response
In test_list.py:
from unittest import TestCase
from fastapi.testclient import TestClient
from main import app
from authentication import Authentication
def override_dependencies():
return {"Some": "Thing"}
client = TestClient(app)
app.dependency_overrides[Authentication] = override_dependencies
class ListTestCase(TestCase):
def test_list_get(self):
response = client.get("/list/")
self.assertEqual(200, response.status_code)
Gives the following error:
self.assertEqual(200, response.status_code)
AssertionError: 200 != 403
i.e., it tried to authenticate but was denied. Hence, it doesn't seem that it overrides my dependency.
Note that Depends is used in the path operation decorator (#router.get), and not the function as in the docs…
I see that you are using Authentication(role=user) as a dependency, but then you are trying to override Authentication and these are two different callables, the former being actually Authentication(role=user).__call__; thus I guess FastAPI is not able to match the correct override.
The problem with this approach is that Authentication(role=user).__call__ is an instance method, but in order to override a dependency in dependency_overrides you must be able to adress the callable statically, so that it is always the same every time you call it.
I had to implement a similar thing, and i solved this by injecting the authentication logic in like this:
class RestIdentityValidator:
methods: List[AuthMethod]
def __init__(self, *methods: AuthMethod):
self.methods = list(dict.fromkeys([e for e in AuthMethod] if methods is None else methods))
def __call__(
self,
bearer_token_identity: IdentityInfo | None = Depends(get_bearer_token_identity),
basic_token_identity: IdentityInfo | None = Depends(get_basic_token_identity)
) -> IdentityInfo | None:
identity = None
for auth_method in self.methods:
match auth_method:
case AuthMethod.BEARER:
identity = bearer_token_identity
case AuthMethod.BASIC:
identity = basic_token_identity
case _:
pass
if identity is not None:
break
return identity
and then used it like this:
#router.get("/users", response_model=Response[List[UserOut]])
async def get_all_users(
identity: IdentityInfo = Depends(RestIdentityValidator(AuthMethod.BEARER)),
...
Then you can inject and override this normally. The fact that you are using it in the decorator shouldn't make any difference in this case.
Now this sample is not implementing the same functionality as yours, but I hope it can give you a hint on how to solve your problem.
I'm using the swagger editor (OpenApi 2) for creating flask apis in python. When you define a model in swagger and use it as a schema for the body of a request, swagger validates the body before handing the control to you in the X_controller.py files.
I want to add some code before that validation happens (for printing logs for debugging purposes). Swagger just prints to stdout errors like the following and they are not useful when you have a lot of fields (I need the key that isn't valid).
https://host/path validation error: False is not of type 'string'
10.255.0.2 - - [20/May/2020:20:20:20 +0000] "POST /path HTTP/1.1" 400 116 "-" "GuzzleHttp/7"
I know tecnically you can remove the validations in swagger and do them manually in your code but I want to keep using this feature, when it works it's awesome.
Any ideas on how to do this or any alternative to be able to log the requests are welcome.
After some time studying the matter this is what I learnt.
First let's take a look at how a python-flask server made with Swagger Editor works.
Swagger Editor generates the server stub through Swagger Codegen using the definition written in Swagger Editor. This server stub returned by codegen uses the framework Connexion on top of flask to handle all the HTTP requests and responses, including the validation against the swagger definition (swagger.yaml).
Connexion is a framework that makes it easy to develop python-flask servers because it has a lot of functionality you'd have to make yourself already built in, like parameter validation. All we need to do is replace (in this case modify) these connexion validators.
There are three validators:
ParameterValidator
RequestBodyValidator
ResponseValidator
They get mapped to flask by default but we can replace them easily in the __main__.py file as we'll see.
Our goal is to replace the default logs and default error response to some custom ones. I'm using a custom Error model and a function called error_response() for preparing error responses, and Loguru for logging the errors (not mandatory, you can keep the original one).
To make the changes needed, looking at the connexion validators code, we can see that most of it can be reused, we only need to modify:
RequestBodyValidator: __call__() and validate_schema()
ParameterValidator: __call__()
So we only need to create two new classes that extend the original ones, and copy and modify those functions.
Be careful when copying and pasting. This code is based on connexion==1.1.15. If your are on a different version you should base your classes on it.
In a new file custom_validators.py we need:
import json
import functools
from flask import Flask
from loguru import logger
from requests import Response
from jsonschema import ValidationError
from connexion.utils import all_json, is_null
from connexion.exceptions import ExtraParameterProblem
from swagger_server.models import Error
from connexion.decorators.validation import ParameterValidator, RequestBodyValidator
app = Flask(__name__)
def error_response(response: Error) -> Response:
return app.response_class(
response=json.dumps(response.to_dict(), default=str),
status=response.status,
mimetype='application/json')
class CustomParameterValidator(ParameterValidator):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __call__(self, function):
"""
:type function: types.FunctionType
:rtype: types.FunctionType
"""
#functools.wraps(function)
def wrapper(request):
if self.strict_validation:
query_errors = self.validate_query_parameter_list(request)
formdata_errors = self.validate_formdata_parameter_list(request)
if formdata_errors or query_errors:
raise ExtraParameterProblem(formdata_errors, query_errors)
for param in self.parameters.get('query', []):
error = self.validate_query_parameter(param, request)
if error:
response = error_response(Error(status=400, description=f'Error: {error}'))
return self.api.get_response(response)
for param in self.parameters.get('path', []):
error = self.validate_path_parameter(param, request)
if error:
response = error_response(Error(status=400, description=f'Error: {error}'))
return self.api.get_response(response)
for param in self.parameters.get('header', []):
error = self.validate_header_parameter(param, request)
if error:
response = error_response(Error(status=400, description=f'Error: {error}'))
return self.api.get_response(response)
for param in self.parameters.get('formData', []):
error = self.validate_formdata_parameter(param, request)
if error:
response = error_response(Error(status=400, description=f'Error: {error}'))
return self.api.get_response(response)
return function(request)
return wrapper
class CustomRequestBodyValidator(RequestBodyValidator):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __call__(self, function):
"""
:type function: types.FunctionType
:rtype: types.FunctionType
"""
#functools.wraps(function)
def wrapper(request):
if all_json(self.consumes):
data = request.json
if data is None and len(request.body) > 0 and not self.is_null_value_valid:
# the body has contents that were not parsed as JSON
return error_response(Error(
status=415,
description="Invalid Content-type ({content_type}), JSON data was expected".format(content_type=request.headers.get("Content-Type", ""))
))
error = self.validate_schema(data, request.url)
if error and not self.has_default:
return error
response = function(request)
return response
return wrapper
def validate_schema(self, data, url):
if self.is_null_value_valid and is_null(data):
return None
try:
self.validator.validate(data)
except ValidationError as exception:
description = f'Validation error. Attribute "{exception.validator_value}" return this error: "{exception.message}"'
logger.error(description)
return error_response(Error(
status=400,
description=description
))
return None
Once we have our validators, we have to map them to the flask app (__main__.py) using validator_map:
validator_map = {
'parameter': CustomParameterValidator,
'body': CustomRequestBodyValidator,
'response': ResponseValidator,
}
app = connexion.App(__name__, specification_dir='./swagger/', validator_map=validator_map)
app.app.json_encoder = encoder.JSONEncoder
app.add_api(Path('swagger.yaml'), arguments={'title': 'MyApp'})
If you also need to replace the validator I didn't use in this example, just create a custom child class of ResponseValidator and replace it on the validator_map dictionary in __main__.py.
Connexion docs:
https://connexion.readthedocs.io/en/latest/request.html
Forgive me for repeating an answer first posted at https://stackoverflow.com/a/73051652/1630244
Have you tried the Connexion before_request feature? Here's an example that logs the headers and content before Connexion validates the body:
import connexion
import logging
from flask import request
logger = logging.getLogger(__name__)
conn_app = connexion.FlaskApp(__name__)
#conn_app.app.before_request
def before_request():
for h in request.headers:
logger.debug('header %s', h)
logger.debug('data %s', request.get_data())
I am trying to make a request for session info, and I could print out session info on python console, but can't return session info on server endpoint. The error said the object is not JSON serializable. Here is the error message:
TypeError: Object of type ListVector is not JSON serializable
I found this post relevant to my post, I tried its solution, but it didn't work for me. how can I convert rpy2.ListVector to the dictionary that can be JSON serializable? any solution?
my attempt:
from flask import Flask, jsonify, request
from flask_restplus import Api, Resource
import json
##
import os
os.environ['PYTHONHOME'] = r"C:\Users\me\AppData\Local\Programs\Python\Python37"
os.environ['PYTHONPATH'] = r"C:\Users\me\AppData\Local\Programs\Python\Python37\Lib\site-packages"
os.environ['R_HOME'] = r"C:\Program Files\R\R-3.3.2"
os.environ['R_USER'] = r"C:\Users\me\AppData\Local\Programs\Python\Python37\Lib\site-packages\rpy2"
##
app = Flask(__name__)
api = Api(app)
ns = api.namespace('ns')
## load R function
import rpy2
import rpy2.robjects as robjects
#api.route('/')
class AResource(Resource):
def post(self):
sess = robjects.r("sessionInfo()")
return jsonify({'sessioninfo': sess})
if __name__ == '__main__':
api.add_namespace(ns)
app.run(debug=True)
here I just simply make a request to the server endpoint, I can print out session info on console but not able to return it on server endpoint. how can I make this work? any quick thought? thanks
Is there any possible solution to fix this error? any idea?
follow up your above attempt, I fixed your problem as follow:
class AResource(Resource):
def post(self):
sess = robjects.r("sessionInfo()")
new_sess= { key : sess.rx2(key)[0] for key in sess.names }
return jsonify({'sessioninfo': str(new_sess)})
now you can see json output on your server endpoint.
Bottle.py ships with an import to handle throwing HTTPErrors and route to a function.
Firstly, the documentation claims I can (and so do several examples):
from bottle import error
#error(500)
def custom500(error):
return 'my custom message'
however, when importing this statement error is unresolved but on running the application ignores this and just directs me to the generic error page.
I found a way to get around this by:
from bottle import Bottle
main = Bottle()
#Bottle.error(main, 500)
def custom500(error):
return 'my custom message'
But this code prevents me from embedding my errors all in a separate module to control the nastiness that would ensue if I kept them in my main.py module because the first argument has to be a bottle instance.
So my questions:
Has anyone else experienced this?
why doesn't error seem to resolve in only my case (I installed from pip install bottle)?
Is there a seamless way to import my error routing from a separate python module into the main application?
If you want to embed your errors in another module, you could do something like this:
error.py
def custom500(error):
return 'my custom message'
handler = {
500: custom500,
}
app.py
from bottle import *
import error
app = Bottle()
app.error_handler = error.handler
#app.route('/')
def divzero():
return 1/0
run(app)
This works for me:
from bottle import error, run, route, abort
#error(500)
def custom500(error):
return 'my custom message'
#route("/")
def index():
abort("Boo!")
run()
In some cases I find it's better to subclass Bottle. Here's an example of doing that and adding a custom error handler.
#!/usr/bin/env python3
from bottle import Bottle, response, Route
class MyBottle(Bottle):
def __init__(self, *args, **kwargs):
Bottle.__init__(self, *args, **kwargs)
self.error_handler[404] = self.four04
self.add_route(Route(self, "/helloworld", "GET", self.helloworld))
def helloworld(self):
response.content_type = "text/plain"
yield "Hello, world."
def four04(self, httperror):
response.content_type = "text/plain"
yield "You're 404."
if __name__ == '__main__':
mybottle = MyBottle()
mybottle.run(host='localhost', port=8080, quiet=True, debug=True)
I'm trying to use Pylons with SqlAlchemy (through Elixir).
Here is my testdb/model/entities.py:
from elixir import *
metadata.bind = "mysql://testdb:hundertwasser#localhost/testdb"
metadata.bind.echo = True
class Post(Entity):
text = Field(Unicode(128))
And here is the controller:
import logging
from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from testdb.lib.base import BaseController, render
log = logging.getLogger(__name__)
from testdb.model.entities import *
class MainController(BaseController):
def index(self):
c.posts = Post.query.all()
print "Test"
return render('/index.mako')
def submit(self):
post = Post()
post.text = request.POST.get['text']
session.commit()
When I run the application, I get an error saying:
AttributeError: type object 'Post' has no attribute 'query'
Does someone know what I'm doing wrong?
The answer is as follows:
entities.py is missing the following two lines at the bottom:
setup_all()
create_all()
I don't know Elixir well, but isn't putting the following in the .ini enough?
sqlalchemy.url = mysql://user:pass#localhost/dbname?charset=utf8&use_unicode=0
sqlalchemy.pool_recycle = 3600
All the stuff after ? is to avoid encoding problems. The second line prevents MySQL from closing inactive connections (in fact it reconnects every hour).