Python: New Object instance on every call of function - python

I'm trying to update a base class with a session token and user id for long polling.
Every time I call my function I create a new instance which calls a login function, that I don't want to happen.
I only want to call the login() method when the value is None
How do I return the instance of apiclient after the session token is set to use with the function for get_balance??
client.py
from __future__ import absolute_import, unicode_literals
import requests
import os
from matchbook import endpoints
class BaseClient(object):
def __init__(self, username, password=None, locale=None):
self.username = username
self.password = password
self.locale = locale
self.url_beta = 'https://beta.matchbook.com'
self.urn_main = '/bpapi/rest/'
self.session = requests.Session()
self.session_token = None
self.user_id = None
def set_session_token(self, session_token, user_id):
self.session_token = session_token
self.user_id = user_id
class APIClient(BaseClient):
def __init__(self, username, password=None):
super(APIClient, self).__init__(username, password)
self.login = endpoints.Login(self)
self.account = endpoints.Account(self)
def __repr__(self):
return '<APIClient [%s]>' % self.username
def __str__(self):
return 'APIClient'
get_bal.py
from client import APIClient
from celery import shared_task
def get_client():
apiclient = APIClient(username, password)
if apiclient.session_token is None:
apiclient.login()
session_token = apiclient.session_token
user_id = apiclient.user_id
apiclient.set_session_token(session_token,user_id)
else:
print('session token assigned',apiclient.session_token, apiclient.user_id)
return apiclient
#shared_task
def get_balance():
apiclient = get_client() *to call instance after login is set*
r = apiclient.account.get_account()
print(r)

You are creating a new instance of APIClient each time you call get_client(), which is what happens each time get_balance() get called.
You need to maintain an instance of the APIClient outside of the function scope for you to carry over your program and update your get_balance() to not call get_client() each time:
def get_balance(apiclient):
r = apiclient.account.get_account()
print(r)
def main():
apiclient = get_client()
get_balance(apiclient) # pass instance of APIClient in as an argument
Another note in your get_client() function, since both of the if conditions are the opposite of each other, just wrap them in an if... else block:
def get_client():
apiclient = APIClient(username, password)
if apiclient.session_token is None:
apiclient.login()
session_token = apiclient.session_token
user_id = apiclient.user_id
apiclient.set_session_token(session_token,user_id)
else:
print('session token assigned',apiclient.session_token, apiclient.user_id)
return apiclient
All that said, a much more OOP way would be to bake the get_balance() into the APIClient as an instance method so you don't even need to worry about the instance:
class APIClient(BaseClient):
...
def get_balance(self):
print(self.account.get_account())
# Then just call the get_balance() anywhere:
apiclient = get_client()
apiclient.get_balance()

Related

FASTApi authentication injection

My application has an AuthenticateService implemented as follows:
from domain.ports.repositories import ISalesmanRepository
from fastapi import HTTPException, status
from fastapi_jwt_auth import AuthJWT
from fastapi_jwt_auth.exceptions import JWTDecodeError
from shared.exceptions import EntityNotFound
from adapters.api.authentication.config import User
class AuthenticateService:
def __init__(self, user_repo: ISalesmanRepository):
self._repo = user_repo
def __call__(self, auth: AuthJWT) -> User:
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
auth.jwt_required()
user_id = auth.get_jwt_subject()
except JWTDecodeError:
raise credentials_exception
try:
user = self._repo.get_by_id(user_id)
return user
except EntityNotFound:
raise credentials_exception
So the behavior is basically:
Is jwt is valid, get user from repository and returns
Raise 401 if jwt is invalid
The problem is that in every controller implemented I have to repeat the process. I tried to implement a decorator that injects the user into the controller in case of success but I couldn't. I'm sure that the best way to implement this is to use fastAPI's Depends dependency injector.
Today, a controller looks something like this:
from typing import Optional
from adapters.api.services import authenticate_service, create_sale_service
from fastapi import APIRouter, Depends
from fastapi_jwt_auth import AuthJWT
from pydantic import BaseModel
router = APIRouter()
class Request(BaseModel):
code: str
value: float
date: str
status: Optional[str] = None
#router.post('/sale')
def create_sale(request: Request, auth: AuthJWT = Depends()):
user = authenticate_service(auth)
result = create_sale_service.handle(
{"salesman": user, "sale": request.dict()}
)
return result.dict()
How can I abstract my authentication so that my controllers look like any of the versions below:
# Option 1: decorator
#router.post('/sale')
#authentication_required
def create_sale(request: Request, user: User): # User is the `__call__` response from `AuthenticateService` class
result = create_sale_service.handle(
{"salesman": user, "sale": request.dict()}
)
return result.dict()
# Option 2:
#router.post('/sale')
def create_sale(request: Request, user: User = Depends(authenticate_service)): # Something like that, using the depends to inject User to me
result = create_sale_service.handle(
{"salesman": user, "sale": request.dict()}
)
return result.dict()
So I read a little more the Depends documentation and realize whats was going wrong with my attempt to inject the user on controller signature.
Right way to implement:
> AuthService class
class AuthenticateService:
def __init__(self, user_repo: ISalesmanRepository):
self._repo = user_repo
def __call__(self, auth: AuthJWT = Depends()) -> User:
...
authenticate_service = AuthenticateService(user_repository)
> Controller
#router.post('/sale')
def create_sale(request: Request, user: User = Depends(authenticate_service)):
result = create_sale_service.handle(
{"salesman": user, "sale": request.dict()}
)
return result.dict()

Unit test of endpoint decorated with Flask #auth.login_required decorator

I'm using Flask-HTTPAuth for authentication, Flask-login for login and want to add unit tests for endpoint decorated with #auth.login_required(scheme="Bearer"):
endpoints.py
class UserData(Resource):
#auth.login_required
def get(self, user_id):
user = db_models.User.query.get(user_id)
return result
authent.py
#auth.verify_token
def verify_token(token):
data = token_serializer.loads(token)
return data.get("username", False)
But got stuck with mocking (it doesn't work):
test.py
#mock.patch("myapp.auth")
#mock.patch("myapp.db_models.User")
def test_get_user(self, mock_user, mock_login):
with app.test_client() as client:
mock_login.verify_token.return_value = TEST_USER["username"]
mock_login.login_required.return_value = True
mock_user.query.get.return_value = TEST_USER
response = client.get("/user/100000")
self.assertIsNotNone(response)
self.assertIsInstance(response.get_json(), dict)
And the second approach:
test.py
#mock.patch("myapp.auth.login_required", return_value = True)
#mock.patch("myapp.db_models.User")
def test_get_user(self, mock_user, mock_login):
with app.test_client() as client:
mock_user.query.get.return_value = TEST_USER
response = client.get("/user/100000")
self.assertIsNotNone(response)
self.assertIsInstance(response.get_json(), dict)
Could you please help me to figure out how to do it in the right way?
Thank you for your help!

How to execute a block of code only once in flask?

I want to log, who is logging in the app that uses FLASK.
I tried using
#app.before_request but the problem is I need to access the username which is through flask globals and I get none if I use this decorator.
Also, using global variables like below also doesn`t work. How do I get a global variable in request context?
import logging
import time
from flask import request, flash
from flask import g
from forms import QueryForm, RequestForm, Approve_Reject_Btn_Form
from query.sqlQuery import SQLQuery
from database.oracle import Database
import datetime
from requests import Session
logger = logging.getLogger(__name__)
login_count = 0
'''
Homepage route - Displays all the tables in the homepage
'''
#app.route('/')
#app.route('/index')
def index():
try:
if not g.identity.is_authenticated():
return render_template('homepage.html')
else:
try:
global login_count
if login_count == 0:
username = g.identity.name
user_ip = request.headers.get('IP_header')
current_time = time.strftime('%c')
db = Database(db_config.username, db_config.password)
query = "INSERT INTO UserIP (username, login_time, ip_address) values ('%s', systimestamp, '%s')" % (username, user_ip)
dml_query(query)
logger.debug('User : %s, ip : %s, noted at time : %s, login_count : %s', username , user_ip, current_time, login_count)
login_count = 1
As far as your question "How to execute a block of code only once in flask?" goes, something like this should work fine:
app = Flask(__name__)
#app.before_first_request
def do_something_only_once():
app.logger.setLevel(logging.INFO)
app.logger.info("Initialized Flask logger handler")
The second part of your question is how to setup global variable, like the login counter in your example. For something like this I would recommend that you use an external cache. See example below with the werkzeug SimpleCache class. In production you should replace SimpleCache with redis or mongodb.
from werkzeug.contrib.cache import SimpleCache
class Cache(object):
cache = SimpleCache(threshold = 1000, default_timeout = 3600)
#classmethod
def get(cls, key = None):
return cls.cache.get(key)
#classmethod
def delete(cls, key = None):
return cls.cache.delete(key)
#classmethod
def set(cls, key = None, value = None, timeout = 0):
if timeout:
return cls.cache.set(key, value, timeout = timeout)
else:
return cls.cache.set(key, value)
#classmethod
def clear(cls):
return cls.cache.clear()
You can use Cache class like this.
from mycache import Cache
#app.route('/')
#app.route('/index')
def index():
if not g.identity.is_authenticated():
return render_template('homepage.html')
else:
login_count = Cache.get("login_count")
if login_count == 0:
# ...
# Your code
login_count += 1
Cache.set("login_count", login_count)
EDIT 1: Added after_request example
Per Flask documentation, after_request decorator is used to run registered function after each request. It can be used to invalidate cache, alter response object, or pretty much anything that would require a specific response modification.
#app.after_request
def after_request_callback(response):
# Do something with the response (invalidate cache, alter response object, etc)
return response

Twisted python: the correct way to pass a kwarg through the component system to a factory

I need to pass a kwarg to the parent class of my equivalent of FingerFactoryFromService using super.
I know I am actually passing the kwarg to IFingerFactory because that is also where I pass the service that ends up in init FingerFactoryFromService and I can understand that it is getting tripped up somewhere in the component system but I cannot think of any other way.
The error I keep getting is
exceptions.TypeError: 'test' is an invalid keyword argument for this function
Versions of code in my virtualenv are:
pip (1.4.1)
setuptools (1.1.6)
Twisted (13.1.0)
wsgiref (0.1.2)
zope.interface (4.0.5)
This is a cutdown example from the finger tutorial demonstrating the issue:
from twisted.protocols import basic
from twisted.application import internet, service
from twisted.internet import protocol, reactor, defer
from twisted.python import components
from zope.interface import Interface, implements # #UnresolvedImport
class IFingerService(Interface):
def getUser(user): # #NoSelf
"""
Return a deferred returning a string.
"""
def getUsers(): # #NoSelf
"""
Return a deferred returning a list of strings.
"""
class IFingerFactory(Interface):
def getUser(user): # #NoSelf
"""
Return a deferred returning a string.
"""
def buildProtocol(addr): # #NoSelf
"""
Return a protocol returning a string.
"""
def catchError(err):
return "Internal error in server"
class FingerProtocol(basic.LineReceiver):
def lineReceived(self, user):
d = self.factory.getUser(user)
d.addErrback(catchError)
def writeValue(value):
self.transport.write(value + '\r\n')
self.transport.loseConnection()
d.addCallback(writeValue)
class FingerService(service.Service):
implements(IFingerService)
def __init__(self, filename):
self.filename = filename
self.users = {}
def _read(self):
self.users.clear()
for line in file(self.filename):
user, status = line.split(':', 1)
user = user.strip()
status = status.strip()
self.users[user] = status
self.call = reactor.callLater(30, self._read) # #UndefinedVariable
def getUser(self, user):
print user
return defer.succeed(self.users.get(user, "No such user"))
def getUsers(self):
return defer.succeed(self.users.keys())
def startService(self):
self._read()
service.Service.startService(self)
def stopService(self):
service.Service.stopService(self)
self.call.cancel()
class FingerFactoryFromService(protocol.ServerFactory):
implements(IFingerFactory)
protocol = FingerProtocol
#def __init__(self, srv):
def __init__(self, srv, test=None):
self.service = srv
## I need to call super here because my equivalent of ServerFactory requires
## a kwarg but this cutdown example doesnt so I just assign it to a property
# super(FingerFactoryFromService, self).__init__(test=test)
self.test_thing = test or 'Default Something'
def getUser(self, user):
return self.service.getUser(user)
components.registerAdapter(FingerFactoryFromService,
IFingerService,
IFingerFactory)
application = service.Application('finger')
serviceCollection = service.IServiceCollection(application)
finger_service = FingerService('/etc/passwd')
finger_service.setServiceParent(serviceCollection)
#line_finger_factory = IFingerFactory(finger_service)
line_finger_factory = IFingerFactory(finger_service, test='Something')
line_finger_server = internet.TCPServer(1079, line_finger_factory)
line_finger_server.setServiceParent(serviceCollection)
This has nothing to do with the component system. What you want to do is override the Factory's buildProtocol method, as documented here:
https://twistedmatrix.com/documents/current/core/howto/servers.html#auto9

How to convert a synchronous function to an asynchronous function in python using callbacks?

I'm using the tornado framework and all of my functions have been written synchronously. How would I make these async?
class AuthLoginHandler(BaseHandler):
#tornado.web.asynchronous
def get(self):
self.render("login.html")
def post(self):
username = self.get_argument("UserName",strip = True)
password = self.get_argument("Password",strip = True)
user = auth_actions.login(username,password)
if not user:
self.redirect("/auth/login")
return
#user = user_actions.get_my_data(self.db,user['_id'])
self.set_secure_cookie("userdata", tornado.escape.json_encode(dumps(user.to_mongo())))
self.redirect("/")
def login(username,password,callback=None):
m = hashlib.md5()
m.update(password)
hashed = m.hexdigest()
login = User.objects(UserName=username.lower(),Password=hashed).exclude("Password","Wall","FriendsRequested","Friends","FriendsRequesting")
if login.first() is None:
login = User.objects(Email=username.lower(),Password=hashed).exclude("Password","Wall","FriendsRequested","Friends","FriendsRequesting")
if login.first() is None:
return None
logger.info(username + " has logged in")
if callback != None:
return callback(login.first())
return login.first()
As andy boot mentions, you are going to need a separate thread. The code below uses the generator-coroutine approach from tornado-gen.
Also note the run_async decorator that wraps your function up in its own thread.
import tornado.ioloop
import tornado.web
import tornado.gen
import time
from threading import Thread
from functools import wraps
def run_async(func):
#wraps(func)
def async_func(*args, **kwargs):
func_hl = Thread(target = func, args = args, kwargs = kwargs)
func_hl.start()
return func_hl
return async_func
#run_async
def login(username,password,callback=None):
m = hashlib.md5()
m.update(password)
hashed = m.hexdigest()
login = User.objects(UserName=username.lower(),Password=hashed).exclude("Password","Wall","FriendsRequested","Friends","FriendsRequesting")
if login.first() is None:
login = User.objects(Email=username.lower(),Password=hashed).exclude("Password","Wall","FriendsRequested","Friends","FriendsRequesting")
if login.first() is None:
return None
logger.info(username + " has logged in")
if callback != None:
return callback(login.first())
return login.first()
class AuthLoginHandler(BaseHandler):
#tornado.web.asynchronous
def get(self):
self.render("login.html")
#tornado.web.asynchronous
#tornado.gen.coroutine
def post(self):
username = self.get_argument("UserName",strip = True)
password = self.get_argument("Password",strip = True)
user = yield tornado.gen.Task(auth_actions.login, username, password)
if not user:
self.redirect("/auth/login")
return
self.set_secure_cookie("userdata", tornado.escape.json_encode(dumps(user.to_mongo())))
self.redirect("/")
If you are going to be hashing passwords you are best of doing it in a separate thread. This is because a hash takes a long time to execute and will block the main thread.
Here is an example of hashing on a separate thread
Note that Tornado's maintainer Ben Darnell recommends using a separate thread to do password hashes: see this google group.

Categories