FastApi - Depends missing attribute - python

I'm working on fastapi web app and currently strugling with one thing.
I'm working on google auth system and my dependency injection does not work as expected.
Here are my endpoints:
#app.route('/login')
async def login(request: Request):
redirect_uri = request.url_for('auth')
return await oauth.google.authorize_redirect(request, redirect_uri)
#app.route('/auth')
async def auth(request: Request):
try:
access_token = await oauth.google.authorize_access_token(request)
except OAuthError as e:
raise CREDENTIALS_EXCEPTION
_data = access_token["userinfo"]
if get_or_create_user(data = _data):
return JSONResponse({'result': True, 'access_token': create_token(_data['email'])})
raise CREDENTIALS_EXCEPTION
The problem lies in get_or_create_user function.
#validate_payload
def get_or_create_user(data: USER_GOOGLE_DATA, users_repo: UsersRepository = Depends(SQLUsersRepository)) -> model.User:
_email: str = data.get("email")
if not _email:
raise AttributeError("Payload is missing data")
user: model.User = users_repo.get_by_email(email=_email)
if not user:
_user = model.User(**dict(data))
user = users_repo.add(model.User(_user))
return user
data is dict with required info to create user and users_repo is crud class. Decorator is just to check if data contains all required keys.
PAYLOAD_KEYS: Tuple[str] = ("email",)
def validate_payload(func: Callable[[USER_GOOGLE_DATA, UsersRepository], model.User]):
def decorator(*args, **kwargs):
_data: USER_GOOGLE_DATA = kwargs.get("data")
for _key in PAYLOAD_KEYS:
if _key not in _data:
return None
return func(_data)
return decorator
My crud class looks like following:
class SQLUsersRepository(input_boundries.UsersRepository):
def __init__(self, db: Session or RawDBConnection = Depends(get_db)):
self._db = db
def get(self, user_id: UserId) -> model.User:
user_db = self._db.query(model.User).filter(model.User.id == user_id).first()
if not user_db:
raise DBRecordNotFound(f"User with id: '{user_id}' not found.")
return user_db
def get_by_email(self, email: str) -> model.User:
user_db = self._db.query(model.User).filter(model.User.email == email).first()
if not user_db:
raise DBRecordNotFound(f"User with email: '{email}' not found.")
return user_db
def add(self, user: model.User) -> model.User:
self._db.add(user)
self._db.commit()
self._db.refresh(user)
return user
def save(self, user: model.User) -> None:
user_db = self._db.query(model.User).filter(model.User.id == user.id).first()
self.add(user=user_db)
def remove(self, user_id: UserId) -> None:
user_db = self._db.query(model.User).filter(model.User.id == user_id).first()
if not user_db:
raise DBRecordNotFound(f"User with id: '{user_id}' not found.")
user_db.delete()
self._db.commit()
The error I receive AttributeError: 'Depends' object has no attribute 'get_by_email'
What's wrong with these dependiences? As far as I remember Depends() works similar to decorator (it triggers first).

Related

Python and MYSQL Help: IndexError: tuple index out of range,

I've run into a bit of a snag on this code and could use some help, I can't manage to figure out what is the issue here.
File "C:\Users\Joshua\Desktop\sasquatch_sightings\flask_app\controllers\users.py", line 34, in user_dashboard
return render_template("dashboard.html",user=User.get_user_by_id(data),sightings=Sighting.get_all())
File "C:\Users\Joshua\Desktop\sasquatch_sightings\flask_app\models\user.py", line 44, in get_user_by_id
return cls(results[0])
IndexError: tuple index out of range
My Code
Users.py
from flask_app import app
from flask import render_template, redirect, request, session, flash
from flask_app.models.user import User
from flask_app.models.sighting import Sighting
from flask_bcrypt import Bcrypt
bcrypt = Bcrypt(app)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/user/create', methods={'POST'})
def create_user():
if not User.validate_user_reg_data(request.form):
return redirect('/')
data = {
"first_name": request.form['first_name'],
"last_name": request.form['last_name'],
"email": request.form['email'],
"password": bcrypt.generate_password_hash(request.form['password'])
}
id = User.create_user(data)
session['user_id'] = id
return redirect('/user/dashboard')
#app.route('/user/dashboard')
def user_dashboard():
if 'user_id' not in session:
return redirect('/logout')
data = {
'id': session ['user_id']
}
return render_template("dashboard.html",user=User.get_user_by_id(data),sightings=Sighting.get_all())
#app.route('/user/login', methods = ['POST'])
def login():
user = User.get_user_by_email(request.form)
if not user:
flash("Invalid Email, login")
return redirect('/')
session['user_id'] = user.id
return redirect('/user/dashboard')
#app.route('/user/logout')
def logout():
session.clear()
return redirect('/')
2nd Code User.py
from flask_app.config.mysqlconnection import MySQLConnection, connectToMySQL
from flask import flash, session
import re
EMAIL_REGEX = re.compile(r'^[a-zA-Z0-9.+_-]+#[a-zA-Z0-9._-]+\.[a-zA-Z]+$')
class User:
db = "sasquatch"
def __init__(self,data):
self.id = data['id']
self.first_name = data['first_name']
self.last_name = data['last_name']
self.email = data['email']
self.password = data['password']
self.created_at = data['created_at']
self.updated_at = data['updated_at']
#classmethod
def create_user(cls, data):
data = cls.parse_registration_data(data)
query = "INSERT INTO users (first_name, last_name, email, password) VALUES (%(first_name)s, %(last_name)s, %(email)s, %(password)s);"
return connectToMySQL(cls.db).query_db(query, data)
#classmethod
def get_all(cls):
query = "SELECT * FROM users;"
results = connectToMySQL(cls.db).query_db(query)
users = []
for row in results:
users.append( cls(row))
return users
#classmethod
def get_user_by_email(cls, data):
query = "SELECT * FROM users WHERE email = %(email)s;"
results = connectToMySQL(cls.db).query_db(query,data)
if len(results) < 1:
return False
return cls(results[0])
#classmethod
def get_user_by_id(cls,data):
query = "SELECT * FROM users WHERE id = %(id)s;"
results = connectToMySQL(cls.db).query_db(query,data)
return cls(results[0])
#staticmethod
def validate_user_reg_data(user):
is_valid = True
query = "SELECT * FROM users WHERE email = %(email)s;"
results = connectToMySQL(User.db).query_db(query,user)
if len(results) >= 1:
flash("Email already taken.","register")
is_valid=False
if not EMAIL_REGEX.match(user['email']):
flash("Invalid Email","register")
is_valid=False
if len(user['first_name']) < 2:
flash("First name must be more than 2 characters","register")
is_valid= False
if len(user['last_name']) < 2:
flash("Last name must be more than 2 characters", "register")
if len(user['password']) < 8:
flash("Password must be more than 8 characters","register")
is_valid= False
if user['password'] != user['confirm']:
flash("Passwords do not match","register")
return is_valid
#staticmethod
def parse_registration_data(data):
parsed_data = {}
parsed_data['email'] = data['email'].lower()
parsed_data['first_name'] = data['first_name']
parsed_data['last_name'] = data['last_name']
return parsed_data

How to emulate locally aws context for testing

I have created a lambda
where I retrieve region, based on the context.invoked_function_arn.
So, I would like to create and emulate this behavior using a test script file not via conftest.py because I have other unit test methods implemented in the same test script.
I find this link lambda-context which is useful and implemented the same concept in my test script. However all the time, I'm getting the AttributeError: 'function' object has no attribute 'invoked_function_arn' error message.
#pytest.fixture
def mock_lambda_context():
class ClientContext:
"""
Class for mocking Context
Has `custom`, `env`, and `client` `__slots__`
"""
__slots__ = ["custom", "env", "client"]
def make_obj_from_dict(_class, _dict, fields=None):
"""
Makes an object of `_class` from a `dict`
:param _class: A class representing the context
:type _class: `ContextClass`
:param _dict: A dictionary of data
:type _dict: `dict`
:param fields: [description], defaults to None
:type fields: [type], optional
:return: An object
:rtype: `ClientContext` class
"""
if _dict is None:
return None
obj = _class()
set_obj_from_dict(obj, _dict)
return obj
def set_obj_from_dict(obj, _dict, fields=None):
if fields is None:
fields = obj.__class__.__slots__
for field in fields:
setattr(obj, field, _dict.get(field, None))
class LambdaContext(object):
"""
Create a Lambda Context Class object
"""
def __init__(self, invokeid, client_context, invoked_function_arn=None):
self.aws_request_id = invokeid
self.log_group_name = "AWS_LAMBDA_LOG_GROUP_NAME"
self.log_stream_name = "AWS_LAMBDA_LOG_STREAM_NAME"
self.function_name = "AWS_LAMBDA_FUNCTION_NAME"
self.memory_limit_in_mb = "AWS_LAMBDA_FUNCTION_MEMORY_SIZE"
self.function_version = "AWS_LAMBDA_FUNCTION_VERSION"
self.invoked_function_arn = "arn:aws:lambda:eu-west-1:123456789012:function:" \
"ExampleLambdaFunctionResourceName-AULC3LB8Q02F"
self.client_context = make_obj_from_dict(ClientContext, client_context)
if self.client_context is not None:
self.client_context.client = None
self.identity = None
def get_remaining_time_in_millis(self):
return None
def log(self, msg):
str_msg = str(msg)
print(str_msg)
context = LambdaContext("AWS_ID", {})
return context
def test_handler(events, mock_lambda_context):
res = handler(events, mock_lambda_context)
assert res['context.invoked_function_arn'] == "arn:aws:lambda:eu-west-1:123456789012:function:ExampleLambdaFunctionResourceName-AULC3LB8Q02F"
Can anyone point me how to fix this silly one?
I created a sample_context.json like this:
{
"invoked_function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:ExampleLambdaFunctionResourceName-AULC3LB8Q02F",
"log_group_name": "/aws/lambda/ExampleLambdaFunctionResourceName-AULC3LB8Q02F",
"function_name": "ExampleLambdaFunctionResourceName-AULC3LB8Q02F",
"function_version": "$LATEST"
}
Then in my main test file:
#pytest.fixture
def context_outcome():
return load_json_from_file('sample_context.json')
#pytest.fixture
def context(context_outcome):
response = type('new', (object,), context_outcome)
seqs = tuple, list, set, frozenset
for i, j in context_outcome.items():
if isinstance(j, dict):
setattr(response, i, context(j))
elif isinstance(j, seqs):
setattr(response, i,
type(j)(context(sj) if isinstance(sj, dict) else sj for sj in j))
else:
setattr(response, i, j)
return response
def test_main(events, context):
assert context.invoked_function_arn == "arn:aws:lambda:eu-west-1:123456789012:function:ExampleLambdaFunctionResourceName-AULC3LB8Q02F"
I got around this by doing the following:
from dataclasses import dataclass
#pytest.fixture
def context():
#dataclass
class LambdaContext:
function_name: str = "test"
aws_request_id: str = "88888888-4444-4444-4444-121212121212"
invoked_function_arn: str = "arn:aws:lambda:eu-west-1:123456789101:function:test"
return LambdaContext()
def test_lambda(context):
my_class = MyClass(EVENT, context)

What is wrong with FileRepo? All is added but I can't use anything

from Exception.Exceptions import *
import re
class Repo(object):
def __init__(self):
self.__items_dictionary = {}
def get_element(self, id):
try:
return self.__items_dictionary[id]
except KeyError:
raise RepositoryError("ID not existent")
def add_item(self, new_item):
try:
if new_item.id in self.__items_dictionary.keys():
raise RepositoryError("Id already exists!")
self.__items_dictionary[new_item.id] = new_item
except AttributeError:
raise RepositoryError('Element does not have an id introduced!')
def remove_item(self, id):
try:
if id in self.__items_dictionary.keys():
return self.__items_dictionary.pop(id)
else:
raise RepositoryError("Element does not exist!")
except KeyError:
raise RepositoryError("Element with that id does not exist!")
def update_item(self, id, attribute, new_value):
if id in self.__items_dictionary.keys():
if hasattr(self.__items_dictionary[id], attribute):
setattr(self.__items_dictionary[id], attribute, new_value)
else:
raise RepositoryError("Element with that attribute does not exist!")
else:
raise RepositoryError('Element with that id does not exist!')
def search_item(self, attribute, search_value):
searched_items = []
try:
if attribute == 'id':
search_value = int(search_value)
if search_value in self.__items_dictionary.keys():
searched_items.append(self.__items_dictionary[search_value])
else:
search_value = search_value.lower()
for item in self.__items_dictionary.keys():
item_string = getattr(self.__items_dictionary[item], attribute).lower()
if re.findall(search_value, item_string):
searched_items.append(self.__items_dictionary[item])
return searched_items
except ValueError as ve:
raise RepositoryError(ve)
#property
def get_all(self):
return self.__items_dictionary
def get_all_for_file(self):
return self.__items_dictionary
class FileRepository(Repo):
def __init__(self,filename, read_entity, write_entity):
Repo.__init__(self)
self.__filename = filename
self.__read_entity = read_entity
self.__write_entity = write_entity
def _read_all_from_file(self):
self._entities = {}
with open(self.__filename,'r') as file:
lines = file.readlines()
for line in lines:
line = line.strip()
if line != "":
entity = self.__read_entity(line)
self._entities[entity.id] = entity
def _write_all_to_file(self):
with open(self.__filename, 'w') as file:
for entity in self._entities:
line = self.__write_entity(entity)
file.write(line + "\n")
def add_item(self,new_item):
self._read_all_from_file()
Repo.add_item(self, new_item)
self._write_all_to_file()
def search_item(self, attribute, search_value):
self._read_all_from_file()
return Repo.search_item(self, attribute,search_value)
def update_item(self, id, attribute, new_value):
self._read_all_from_file()
Repo.update_item(self, id,attribute,new_value)
self._write_all_to_file()
def remove_item(self, id):
self._read_all_from_file()
Repo.remove_item(self, id)
self._write_all_to_file()
def get_all(self):
self._read_all_from_file()
return Repo.get_all
def get_element(self, id):
self._read_all_from_file()
return Repo.get_element(self,id)
I have checked and all gets added to it but whenever I try to access anything I get something like 'function' object has no attribute 'values' or 'method' object is not iterable. Could it be that I am calling that #property? But even when I tried with get_all_for_file() it still did't work...

How to update different collections of one object by some value using one method?

I have two identical methods that update a list by some value:
def block_device(self, device_id):
if self.block_device_ids is None:
self.block_device_ids = []
if device_id not in self.block_device_ids:
self.block_device_ids.append(device_id)
self.save()
return True
return False
def add_video(self, video_id):
if self.video_ids is None:
self.video_ids = []
if video_id not in self.video_ids:
self.video_ids.append(video_id)
self.save()
return True
return False
How to create one method update_collectionand use it in both cases?
I created the following solution:
async def update_collection(self, collection, item, attr_name):
if collection is None:
collection = []
if item not in collection:
getattr(self, attr_name).append(item)
await self.save()
return True
return False
async def add_video(self, video_id):
return await self.update_collection(self.video_ids, video_id, 'video_ids')
async def block_device(self, device_id):
return await self.update_collection(self.block_device_ids, device_id, 'device_ids')
but it doesn't work because of collection = []. How to fix this?
Is there anything that I can improve?
You don't need to pass in the collection and the name of the attribute:
async def update_collection(self, item, attr_name):
collection = getattr(self, attr_name)
if collection is None:
setattr(self, attr_name, [])
collection = getattr(self, attr_name)
if item not in collection:
collection.append(item)
await self.save()
return True
return False
NOTE: You have a bug on the last line of your code: the attr_name passed in should be "block_device_ids" not "device_ids"

What is error all about? and why?

class account(object):
__duser_id = ''
__duser_name =''
__duser_no = ''
def __init__(self, default, entry_name, password, user_id='', user_name='', user_no=''):
if type(default) != bool:
raise Exception("Error 0x1: type(default) is boolean ")
if default == False:
self.__user_id = user_id
self.__user_name = user_name
self.__user_no = user_no
else:
self.__user_id = __duser_id
self.__user_name = __duser_name
self.__user_no = __duser_no
self.__entry_name = entry_name
self.__password = password
def dset(self, duser_id=__duser_id, duser_name=__duser_name, duser_no=__duser_no):
__duser_id = duser_id
__duser_name = duser_name
__duser_no = duser_no
return (__duser_id, __duser_name, __duser_no)
def dget(self):
return (__duser_id, __duser_name, __duser_no)
def set(self, user_name=self.__user_name, user_id=self.__user_id, user_no=self.__user_no, password=self.__password):
self.__user_id = user_id
self.__user_name = user_name
self.__user_no = user_no
self.__password = password
return (self.__user_id, self.__user_name, self.__user_no, self.password)
def get(self):
return (self.__user_id, self.__user_name, self.__user_no, self.password)
if __name__ == '__main__':
gmail = account(default=True, entry_name='gmail', password='pass***')
print(gmail.dget())
print(gmail.get())
out put is:
Traceback (most recent call last):
File "interface.py", line 1, in
class account(object):
File "interface.py", line 30, in account
def set(self, user_name=self.__user_name, user_id=self.__user_id, user_no=self.__user_no, password=self.__password):
NameError: name 'self' is not defined
Ok o got it.
but there is another one i just changed code.
This is a decorator with arbitrary number of arguments and keyword
arguments
def user_no_is_number(func):
def wrapper(*args, **kargs):
if 'user_no' in kargs:
if type(kargs['user_no']) != int:
raise Exception('Error 1x0: user_no must contains only numbers.')
else:
return func(*args, **kargs)
return wrapper
#staticmethod
#user_no_is_number
def dset(user_id=None, user_name=None, user_no=None):
if user_id:
account.__duser_id = user_id
if user_name:
account.__duser_name = user_name
if user_no:
account.__duser_no = user_no
return (account.__duser_id, account.__duser_name, account.__duser_no)
but the dset() function return always None
*I think there is problem with arbitrary keywords parameters. by using **kargs in decorator parameter it becomes dictionary and by again passing **kargs it just return values of that dictionary.*

Categories