Mailchimp python wrapper gives error - no session - python

I'm trying to implement the mailchimp python API in a django project similar, following their example on github. I was trying to make a connection in a class based view however when I load up the view I get the notice
Attribute Error at\
'module' object has no attribute 'session'
It's set up exactly like their example and the error occurs where I define
m = get_mailchimp_api()
I opened up the mailchimp.py file in my site packages after following the traceback and saw the following:
import requests
class Mailchimp(object):
root = 'https://api.mailchimp.com/2.0/'
def __init__(self, apikey=None, debug=False):
'''Initialize the API client
Args:
apikey (str|None): provide your MailChimp API key. If this is left as None, we will attempt to get the API key from the following locations::
- MAILCHIMP_APIKEY in the environment vars
- ~/.mailchimp.key for the user executing the script
- /etc/mailchimp.key
debug (bool): set to True to log all the request and response information to the "mailchimp" logger at the INFO level. When set to false, it will log at the DEBUG level. By default it will write log entries to STDERR
'''
self.session = requests.session()
The traceback ends at the self.session = requests.session() line.
This is my view where I am trying to call Mailchimp
from app.utils import get_mailchimp_api
import mailchimp
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = 'home.html'
# print requests -- this is undefined
m = get_mailchimp_api()
Is it because the CBV doesn't have a request parameter? In the github example they show the connection being made in a function based view where the function takes a requests. If that's the case, how can I pass the response into the CBV? This is the exact example Mailchimp gives on github:
def index(request):
try:
m = get_mailchimp_api()
lists = m.lists.list()
except mailchimp.Error, e:
messages.error(request, 'An error occurred: %s - %s' % (e.__class__, e))
return redirect('/')

Requests doesn't have a session() method...but id does have a Session() object.
Sounds like a bug in the wrapper.

Requests aliases Session() with session(), so that's probably not the issue. It almost sounds like there's something up either with your get_mailchimp_api() method or something is weird with the imports. Other stackoverflow questions about similar error messages seem to come from mutual imports, typos, or other such things.
Presumably your app.utils module is importing mailchimp already, like MailChimp's does? If not, I'd try that. If so, maybe remove your import mailchimp from this file.

Related

Django OAuth with Xero SDK can't access session for oauth2_token_getter and oauth2_token_saver as no request object can be passed

I am using Django with requests_oauthlib to access the Xero API using the xero-python SDK.
Xero Starter Demo
requests-oauthlib
I am trying to use request.session to store the oauth token information. This works well on the initial call and callback, but in subsequent views when I want to load the correct token from the request.session I am hitting a problem.
The Xero ApiClient (xero_python.api_client) wants me to configure two functions it can call to get and save the token in the future (oauth2_token_getter, oauth2_token_saver) (decorators are used in the demo). Those functions are called by the Xero API and take no parameters. I can create the functions no problem, and they get called correctly, but once inside the functions I have no request object so I can't access request.session to get or save the token to the session.
The Xero demonstration projects use Flask which imports a root level session object (from flask import session) which can be accessed in any function, so this works fine for Flask. What is the correct way to solve this problem in Django?
I have tried creating a SessionStore object but it was empty despite me knowing the data is in the request.session
I have also tried importing requests.session but this doesn't seem to be what I am looking for as I can't get it to read/write any data.
Some Code:
Saving the token in the callback works as I have a request.session:
def xero_callback(request):
...
request.session["oauth_token"] = token
...
The Xero ApiClient is configured with the methods for getting and saving:
api_client = ApiClient(
Configuration(
debug=True,
oauth2_token=OAuth2Token(client_id=config.XERO_CLIENT_ID, client_secret=config.XERO_CLIENT_SECRET),
),
pool_threads=1,
oauth2_token_getter=oauth2_token_getter,
oauth2_token_saver=oauth2_token_saver)
This doesn't work as I have no request object and can't pass one in:
def oauth2_token_getter():
return request.session.get("token") #! FAIL, I don't have access to request here
The error is thrown after calling identity_api.get_connections() as it tries to get the token using oauth2_token_getter and fails
def xero_tenants(request):
identity_api = IdentityApi(api_client)
accounting_api = AccountingApi(api_client)
available_tenants = []
for connection in identity_api.get_connections():
...
Any help appreciated, Thanks
Are you able to store the token in the django cache? (django.core.cache import cache) Then instead of specifying oauth2_token_getter=oauth2_token_getter in the ApiClient constructor you can do:
#api_client.oauth2_token_getter
def obtain_xero_oauth2_token():
return cache.get('token')
and something similar for the setter/saver.

How to debug Fastapi openapi generation error

I spend some time going over this error but had no success.
File "C:\Users\ebara.conda\envs\asci\lib\site-packages\fastapi\openapi\utils.py", line 388, in get_openapi
flat_models=flat_models, model_name_map=model_name_map
File "C:\Users\ebara.conda\envs\asci\lib\site-packages\fastapi\utils.py", line 28, in get_model_definitions
model_name = model_name_map[model]
KeyError: <class 'pydantic.main.Body_login_access_token_api_v1_login_access_token_post'>
The problem is that I'm trying to build a project with user authentication from OpenAPI form to create new users in database.
I've used backend part of this template project https://github.com/tiangolo/full-stack-fastapi-postgresql
Everything works except for Authentication like here.
#router.post("/login/access-token", response_model=schemas.Token)
def login_access_token(
db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()) -> Any:
When I add this part form_data: OAuth2PasswordRequestForm = Depends() - and go to /docs page - this error appears (Failed to load API definition. Fetch error. Internal Server Error /openapi.json)
.
The server itself runs in normal mode, but it can't load the open API. If I remove the aforementioned formdata part - then everything works smoothly, but without Authorisation. I tried to debug it, but I have no success. I think it might be connected to a dependency graph or some start-up issues, but have no guess how to trace it back.
Here is the full working example which will reproduce the error. The link points out the code which causes the problem. If you will comment out lines 18-39 - the docs will open without any problems.
https://github.com/BEEugene/fastapi_error_demo/blob/master/fastapi_service/api/api_v1/endpoints/login.py
Any ideas on how to debug or why this error happens?
You are using Depends function without an argument. Maybe in the console, you were having the error provoked by a function. You must pass the OAuth2PasswordRequestForm function after importing it from fastapi.security to get the result you were expecting.
from fastapi.security import OAuth2PasswordRequestForm
form_data: OAuth2PasswordRequestForm = Depends(OAuth2PasswordRequestForm)
it might work.
It seems that in my case - the main error issue was that I was an idiot.
As said, if you will comment out lines 18-39 - the docs will open without any problems. But, you will notice this warning:
UserWarning: Duplicate Operation ID read_users_api_v1_users__get for
function read_users at
...\fastapi_error\fastapi_service\api\api_v1\endpoints\users.py
warnings. Warn(message)
I've started to compare all the files and it is appeared that I included router to the fastapi twice:
import logging
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from fastapi_service.api.api_v1.api import api_router
from fastapi_service.core.config import settings
from fastapi_service.core.event_handlers import (start_app_handler,
stop_app_handler)
log = logging.getLogger(__name__)
def get_app(mode="prod") -> FastAPI:
fast_app = FastAPI(title=settings.PROJECT_NAME,
version=settings.APP_VERSION,
debug=settings.IS_DEBUG)
# openapi_url=f"{settings.API_V1_STR}/openapi.json")
# first time when I included the router
fast_app.include_router(api_router, prefix=f"{settings.API_V1_STR}")
fast_app.mode = mode
logger = log.getChild("get_app")
logger.info("adding startup")
fast_app.add_event_handler("startup", start_app_handler(fast_app))
logger.info("adding shutdown")
fast_app.add_event_handler("shutdown", stop_app_handler(fast_app))
return fast_app
app = get_app()
# Set all CORS enabled origins
if settings.BACKEND_CORS_ORIGINS:
app.add_middleware(
CORSMiddleware,
allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# second time when I included the router
app.include_router(api_router, prefix=settings.API_V1_STR)
So, if you comment out (or just delete) the second router introduction - the app will work normally.
It seems, that the answer to my question on how to debug this error - is to find the point where the bug appears in fastapi and compare the values in it to the version where there is no error. In my case the number of keys in different dictionaries differed in the function get_model_definitions.
I had the same problem. For me it was because I had code like this
from pydantic import BaseModel
class A(BaseModel):
b: B
class B(BaseModel):
c: int
but instead, Class B should have been defined above class A. This fixed it:
from pydantic import BaseModel
class B(BaseModel):
c: int
class A(BaseModel):
b: B
More info: https://stackoverflow.com/a/70384637/9439097
Regaring your original question on how to debug these or similar errors:
You probably have your routes defined somewhere. Comment all of your routers/routes out, then the openapi docs should generate (and they should show you have no routes. Then, enable the routes one by one and see which one causes the error. THis is how I debuged my situation.

Can i append a value to my flask request object in the #app.before_request and pass it forward to the endpoint view function?

I am developing some basic REST APIs in python. I am expecting an authorization token in the header of all requests except some unsecured requests like login and register. I am validating the token in #app.before_request and then I want to pass the decoded payload to the corresponding endpoint view function. But, I am not to attach the decoded info to the request object as I get "TypeError: 'Request' object does not support item assignment".
#app.before_request
def before_request():
print(request.endpoint)
if request.endpoint=="register" or request.endpoint=="login":
pass
else:
auth_header = request.headers.get('Authorization')
if auth_header:
auth_token = auth_header.split(" ")[1]
token=decode_auth_token(auth_token)
request["token"]=token
else:
return jsonify({"result":"","error":"No token present in header !"})
I am thinking of this implementation like an auth filter where all requests pass this filter. I can strike off the ill requests at this layer itself and also, I can fetch the user specific info which is needed in the net middleware.
I have a similar usecase (surprisingly similar, actually). I got around it by setting a custom property in the request object, much like your approach, although instead of using direct assignment (i.e. request["token"]=token), I used setattr(request, "token", token).
I got the tip from a bottle plugin which does something very similar:
https://github.com/agile4you/bottle-jwt/blob/master/bottle_jwt/auth.py#L258
You may even wanna try that, or some other plugin to further improve your application.
Flask offers g to propagate data in application context.
from flask import g
#app.before_request
def before_request():
g.token = vaue
#bp.route("/path", methods=["GET"]):
g.token
Reference:
https://flask.palletsprojects.com/en/2.2.x/appcontext/

Reraising an exception as an HTTP error in Pyramid

I have a web app in python Pyramid which calls various other code in python which may raise an exception.
Instead of the user receiving a "500 Internal Error", I'd like them to get a more specific error, for instance if MyException is thrown, show a 503 Error. I tried to do this:
#view_config(context=MyException, permission='view')
def custom_exc(exc, request):
raise HTTPServiceUnavailable(exc.message)
However, that fails because it is for some reason unauthorized:
HTTPForbidden: Unauthorized: custom_exc failed permission check
My ACL is as follows:
class RootFactory(object):
__acl__ = [
(Allow, 'admin', ('view',))
]
I am connected with the user admin and it works perfectly for other views.
Does anyone know how to solve this or else how to "chain" exceptions in Pyramid in a different way?
Learn from a customized version of famous ToDoPyramid example application. This way I translate an internal technical event, a database exception, into a meaningful application specific message within custom exception view code. Some guys call this a layer of abstraction or information hiding.
Do not protect these exception views with permissions, since you should protect code that does stuff and CAN raise exceptions.
from sqlalchemy.exc import OperationalError as SqlAlchemyOperationalError
#view_config(context=SqlAlchemyOperationalError)
def failed_sqlalchemy(exception, request):
"""catch missing database, logout and redirect to homepage, add flash message with error
implementation inspired by pylons group message
https://groups.google.com/d/msg/pylons-discuss/BUtbPrXizP4/0JhqB2MuoL4J
"""
msg = 'There was an error connecting to database'
request.session.flash(msg, queue='error')
headers = forget(request)
# Send the user back home, everything else is protected
return HTTPFound(request.route_url('home'), headers=headers)

Django, session attr

I am trying to use session to pass some data from one page to another page.
Here is the code i wrote in ajax.py.
def save_cookie(request, query):
request.session['query'] = query
But when i call this dajaxice function.An error will occurred. As we all know that when we try to use dajaxice in html page, the error msg always is "sth goes wrong".
I tried to debug save_cookie, but the mock request object i created has no session attr. However, if i do request.session="blah", it worked. If I directly use save_cookie(request,query). It will pop up the error msg that request object has no attr seesion...
The code is right straight forward. I didn't see any mistake in it. Does anyone know the cause?
Never used dajaxice / dajax so I can't really help here. Just a few points:
did you enable (and properly configue) the session support ? https://docs.djangoproject.com/en/1.3/topics/http/sessions/
you can use the logging module (or a plain "print" statement but then you won't have the whole traceback) to trace the exception, ie :
def save_cookie(request, query):
try:
request.session['query'] = query
except Exception, e:
print e
raise
The output of the print statement should now appear in the shell you started the dev server from (assuming you're working with the dev server... you ARE workin with the dev server, aren't you ?)
still using the dev server, you can use pdb to switch to interactive debugging:
def save_cookie(request, query):
import pdb; pdb.set_trace()
request.session['query'] = query
then try to access the url in your browser, switch back to your shell and you're in a pdb session where you can inspect the request and (if there's one) request.session object etc.
NB : don't do this if running behind Apache or any other web server - only with the builtin dev server.
"request.session='blah'" will create the "session" attribute on the "request" object if it doesn't exist (and possibly replace the real "session" object if it already existed), so it's neither a valid test nor something sensible to do
My 2 cents...
Disclaimer: I don't know anything about dajaxice.
The following will work on a mock request object:
def save_cookie(request, query):
if not hasattr(request, 'session'):
request.session = dict()
request.session['query'] = query

Categories

Resources