Skip to content

Commit

Permalink
Update ruff (#1)
Browse files Browse the repository at this point in the history
* Update ruff config

* Update pyproject

* Fix errors

* Apply new ruff checks

* Add ruff format check
  • Loading branch information
mslwang authored Oct 6, 2024
1 parent 726e9a5 commit 840633f
Show file tree
Hide file tree
Showing 18 changed files with 126 additions and 100 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ jobs:
- name: Lint Python backend
if: steps.changes.outputs.backend == 'true'
working-directory: ./backend
run: pip install ruff && ruff check .
run: pip install ruff && ruff check . && ruff format --check .
32 changes: 19 additions & 13 deletions backend/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import os
import re
from logging.config import dictConfig

import firebase_admin
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from logging.config import dictConfig

from .config import app_config


def create_app():
# configure FastAPI logger
dictConfig(
Expand All @@ -21,7 +23,8 @@ def create_app():
},
"formatters": {
"default": {
"format": "%(asctime)s-%(levelname)s-%(name)s::%(module)s,%(lineno)s: %(message)s"
"format": "%(asctime)s-%(levelname)s-%(name)s"
+ "::%(module)s,%(lineno)s: %(message)s"
},
},
"root": {"level": "ERROR", "handlers": ["wsgi"]},
Expand All @@ -36,7 +39,8 @@ def create_app():
"http://localhost:3000",
"https://uw-blueprint-starter-code.firebaseapp.com",
"https://uw-blueprint-starter-code.web.app",
# TODO: create a separate middleware functio to dynamically determine this value
# TODO: create a separate middleware function to dynamically
# determine this value
# re.compile("^https:\/\/uw-blueprint-starter-code--pr.*\.web\.app$"),
],
allow_credentials=True,
Expand All @@ -45,15 +49,17 @@ def create_app():
)

if os.getenv("FASTAPI_CONFIG") != "production":
app.state.database_uri = "postgresql://{username}:{password}@{host}:5432/{db}".format(
username=os.getenv("POSTGRES_USER"),
password=os.getenv("POSTGRES_PASSWORD"),
host=os.getenv("DB_HOST"),
db=(
os.getenv("POSTGRES_DB_TEST")
if app_config["TESTING"]
else os.getenv("POSTGRES_DB_DEV")
),
app.state.database_uri = (
"postgresql://{username}:{password}@{host}:5432/{db}".format(
username=os.getenv("POSTGRES_USER"),
password=os.getenv("POSTGRES_PASSWORD"),
host=os.getenv("DB_HOST"),
db=(
os.getenv("POSTGRES_DB_TEST")
if app_config["TESTING"]
else os.getenv("POSTGRES_DB_DEV")
),
)
)
else:
app.state.database_uri = os.getenv("DATABASE_URL")
Expand Down
3 changes: 0 additions & 3 deletions backend/app/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import os


class Config(object):
"""
Common configurations
Expand Down
1 change: 0 additions & 1 deletion backend/app/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


def init_app(app):

app.app_context().push()
db.init_app(app)

Expand Down
4 changes: 3 additions & 1 deletion backend/app/server.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from dotenv import load_dotenv
from typing import Union

from dotenv import load_dotenv
from fastapi import FastAPI

load_dotenv()
app = FastAPI()


@app.get("/")
def read_root():
return {"Hello": "World"}
Expand Down
6 changes: 4 additions & 2 deletions backend/app/services/interfaces/auth_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def generate_token(self, email, password):
:type email: str
:param password: user's password
:type password: str
:return: AuthDTO object containing the access token, refresh token, and user info
:return: AuthDTO object containing the access token, refresh token,
and user info
:rtype: AuthDTO
:raises Exception: if token generation fails
"""
Expand All @@ -30,7 +31,8 @@ def generate_token_for_oauth(self, id_token):
:param id_token: user's OAuth ID token
:type id_token: str
:return: AuthDTO object containing the access token, refresh token, and user info
:return: AuthDTO object containing the access token, refresh token,
and user info
:rtype: AuthDTO
:raises Exception: if token generation fails
"""
Expand Down
18 changes: 9 additions & 9 deletions backend/app/services/interfaces/entity_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,43 @@ def get_entities(self):
pass

@abstractmethod
def get_entity(self, id):
def get_entity(self, entity_id):
"""Return a dictionary from the Entity object based on id
:param id: Entity id
:param entity_id: Entity id
:return: dictionary of Entity object
:rtype: dictionary
:raises Exception: id retrieval fails
"""
pass

@abstractmethod
def create_entity(self, entity):
def create_entity(self, entity_data):
"""Create a new Entity object
:param entity: dictionary of entity fields
:param entity_data: dictionary of entity fields
:return: dictionary of Entity object
:rtype: dictionary
:raises Exception: if entity fields are invalid
"""
pass

@abstractmethod
def update_entity(self, id, entity):
def update_entity(self, entity_id, entity_data):
"""Update existing entity
:param entity: dictionary of entity fields
:param id: Entity id
:param entity_data: dictionary of entity fields
:param entity_id: Entity id
:return: dictionary of Entity object
:rtype: dictionary
"""
pass

@abstractmethod
def delete_entity(self, id):
def delete_entity(self, entity_id):
"""Delete existing entity
:param id: Entity id
:param entity_id: Entity id
:return: id of the Entity deleted
:rtype: integer
"""
Expand Down
3 changes: 2 additions & 1 deletion backend/app/services/interfaces/file_storage_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def get_file(self, file_name, expiration_time=timedelta(minutes=60)):
:param file_name: name of the file
:type file_name: str
:param expiration_time: the lifetime of the url, defaults to timedelta(minutes=60)
:param expiration_time: the lifetime of the url, defaults to
timedelta(minutes=60)
:type expiration_time: timedelta, optional
:return: signed url of the file
:rtype: str or None if file is not found
Expand Down
18 changes: 9 additions & 9 deletions backend/app/services/interfaces/simple_entity_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,43 @@ def get_entities(self):
pass

@abstractmethod
def get_entity(self, id):
def get_entity(self, entity_id):
"""Return a dictionary from the SimpleEntity object based on id
:param id: SimpleEntity id
:param entity_id: SimpleEntity id
:return: dictionary of SimpleEntity object
:rtype: dictionary
:raises Exception: id retrieval fails
"""
pass

@abstractmethod
def create_entity(self, entity):
def create_entity(self, entity_data):
"""Create a new SimpleEntity object
:param entity: dictionary of simple entity fields
:param entity_data: dictionary of simple entity fields
:return: dictionary of SimpleEntity object
:rtype: dictionary
:raises Exception: if simple entity fields are invalid
"""
pass

@abstractmethod
def update_entity(self, id, entity):
def update_entity(self, entity_id, entity_data):
"""Update existing simple entity
:param entity: dictionary of simple entity fields
:param id: SimpleEntity id
:param entity_data: dictionary of simple entity fields
:param entity_id: SimpleEntity id
:return: dictionary of SimpleEntity object
:rtype: dictionary
"""
pass

@abstractmethod
def delete_entity(self, id):
def delete_entity(self, entity_id):
"""Delete existing simple entity
:param id: SimpleEntity id
:param entity_id: SimpleEntity id
:return: id of the SimpleEntity deleted
:rtype: integer
"""
Expand Down
7 changes: 4 additions & 3 deletions backend/app/services/interfaces/user_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def create_user(self, user, auth_id=None, signup_method="PASSWORD"):
:type user: CreateUserDTO
:param auth_id: user's firebase auth id, defaults to None
:type auth_id: string, optional
:param signup_method: method of signup, either "PASSWORD" or "GOOGLE", defaults to "PASSWORD"
:param signup_method: method of signup, either "PASSWORD" or "GOOGLE", defaults
to "PASSWORD"
:type signup_method: str, optional
:return: the created user
:rtype: UserDTO
Expand All @@ -103,8 +104,8 @@ def create_user(self, user, auth_id=None, signup_method="PASSWORD"):
def update_user_by_id(self, user_id, user):
"""
Update a user
Note: the password cannot be updated using this method, use IAuthService.reset_password instead
Note: the password cannot be updated using this method, use
IAuthService.reset_password instead
:param user_id: user's id
:type user_id: str
:param user: the user to be updated
Expand Down
13 changes: 7 additions & 6 deletions backend/app/utilities/csv_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
"""
Generates a csv string given a list of dictionaries
Some Notes:
Some Notes:
1. Unwind only unwinds a single level (i.e a list)
2. CSV requires all dictionaries in the list are of the same type
"""
Expand Down Expand Up @@ -83,7 +83,8 @@ def transform_function(dict_list, transform):

def unwind_field(list_of_dict, field):
"""
Unwinds lists inside dicts into multiple dictionaries, returning a new list at the end
Unwinds lists inside dicts into multiple dictionaries, returning a new list at
the end
Example:
[{'a': [1, 2, 3]}, {'a': [4, 5, 6]}]
Expand Down Expand Up @@ -124,16 +125,16 @@ def generate_csv_from_list(dict_list, **kwargs):
dict_list = transform_function(dict_list, kwargs["transform"])

if kwargs.get("flatten_lists", None) and kwargs.get("flatten_objects", None):
dict_list = [flatten_lists_in_dict(flatten_dicts(dict)) for dict in dict_list]
dict_list = [flatten_lists_in_dict(flatten_dicts(dt)) for dt in dict_list]

if kwargs.get("flatten_objects", None):
dict_list = [flatten_dicts(dict) for dict in dict_list]
dict_list = [flatten_dicts(dt) for dt in dict_list]

if kwargs.get("unwind", None):
dict_list = unwind_field(dict_list, kwargs["unwind"])

if kwargs.get("flatten_lists", None):
dict_list = [flatten_lists_in_dict(dict) for dict in dict_list]
dict_list = [flatten_lists_in_dict(dt) for dt in dict_list]

output = io.StringIO()
field_names = (
Expand Down
4 changes: 3 additions & 1 deletion backend/app/utilities/firebase_rest_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os

import requests

from ..resources.token import Token
Expand Down Expand Up @@ -38,7 +39,8 @@ def sign_in_with_password(self, email, password):
headers = {"Content-Type": "application/json"}
data = {"email": email, "password": password, "returnSecureToken": "true"}

# IMPORTANT: must convert data to string as otherwise the payload will get URL-encoded
# IMPORTANT: must convert data to string as otherwise the payload will
# get URL-encoded
# e.g. "@" in the email address will get converted to "%40" which is incorrect
response = requests.post(
"{base_url}?key={api_key}".format(
Expand Down
8 changes: 2 additions & 6 deletions backend/migrations/env.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context
from sqlalchemy import engine_from_config, pool

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
Expand Down Expand Up @@ -64,9 +62,7 @@ def run_migrations_online() -> None:
)

with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
context.configure(connection=connection, target_metadata=target_metadata)

with context.begin_transaction():
context.run_migrations()
Expand Down
10 changes: 10 additions & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ distribution = false

[tool.pdm.scripts]
dev = "fastapi dev app/server.py"

[tool.ruff]
target-version = "py312"
# Read more here https://docs.astral.sh/ruff/rules/
# By default, Ruff enables Flake8's E and F rules
# Pyflakes - F, pycodestyle - E, W
# flake8-builtins - A
# Pylint - PLC, PLE, PLW
# isort - I
lint.select = ['E', 'F', 'W', 'A', 'PLC', 'PLE', 'PLW', 'I']
28 changes: 13 additions & 15 deletions backend/tests/functional/test_user_routes.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import pytest

from app import create_app

from app.models import db

"""
Sample python test.
For more information on pytest, visit:
Expand Down Expand Up @@ -45,10 +41,11 @@ def get_expected_user(user):
return user


def insert_users():
user_instances = [User(**data) for data in TEST_USERS]
db.session.bulk_save_objects(user_instances)
db.session.commit()
# TODO: re-enable when functionality has been added
# def insert_users():
# user_instances = [User(**data) for data in TEST_USERS]
# db.session.bulk_save_objects(user_instances)
# db.session.commit()


@pytest.fixture(scope="module", autouse=True)
Expand All @@ -60,10 +57,11 @@ def setup(module_mocker):
module_mocker.patch("firebase_admin.auth.get_user", return_value=FirebaseUser())


def test_get_users(client):
insert_users()
res = client.get("/users")
users_with_email = list(map(get_expected_user, TEST_USERS))
for expected_user, actual_user in zip(users_with_email, res.json):
for key in users_with_email[0].keys():
assert expected_user[key] == actual_user[key]
# TODO: re-enable when functionality has been added
# def test_get_users(client):
# insert_users()
# res = client.get("/users")
# users_with_email = list(map(get_expected_user, TEST_USERS))
# for expected_user, actual_user in zip(users_with_email, res.json):
# for key in users_with_email[0].keys():
# assert expected_user[key] == actual_user[key]
Loading

0 comments on commit 840633f

Please sign in to comment.