From f782944caabad299ce2cae15313fe506a63c24e4 Mon Sep 17 00:00:00 2001 From: Aykhan Date: Sat, 16 Sep 2023 18:06:47 +0400 Subject: [PATCH] - Added flake8 - create_user moved to commands --- src/.flake8 | 4 ++ src/app/core/config.py | 25 ++++++---- src/app/core/security.py | 8 +++- src/app/crud/base.py | 20 ++++++-- src/app/crud/crud_post.py | 3 +- src/app/crud/crud_user.py | 13 +++-- src/app/db/base.py | 2 +- src/app/db/base_class.py | 8 +++- src/app/db/session.py | 2 +- src/app/models/__init__.py | 3 +- src/app/models/post.py | 49 ------------------- src/app/models/post_events.py | 54 +++++++++++++++++++++ src/app/models/user.py | 2 +- src/app/schemas/login.py | 2 +- src/app/schemas/main.py | 2 +- src/app/schemas/post.py | 6 +-- src/app/schemas/user.py | 2 +- src/app/utils/custom_functions.py | 2 +- src/app/utils/email_utils.py | 22 ++++----- src/app/utils/file_operations.py | 6 ++- src/app/utils/image_operations.py | 2 +- src/app/utils/rate_limiter.py | 2 +- src/app/views/depends.py | 56 ++++++++++++---------- src/app/views/router.py | 2 +- src/app/views/routers/main.py | 6 +-- src/app/views/routers/user.py | 13 +++-- src/{app/utils => commands}/create_user.py | 3 -- src/poetry.lock | 51 +++++++++++++++++++- src/pyproject.toml | 1 + 29 files changed, 239 insertions(+), 132 deletions(-) create mode 100644 src/.flake8 create mode 100644 src/app/models/post_events.py rename src/{app/utils => commands}/create_user.py (98%) diff --git a/src/.flake8 b/src/.flake8 new file mode 100644 index 0000000..7552c07 --- /dev/null +++ b/src/.flake8 @@ -0,0 +1,4 @@ +[flake8] +per-file-ignores = app/db/base.py:F401 +exclude = .git,__pycache__,__init__.py +max-line-length = 79 \ No newline at end of file diff --git a/src/app/core/config.py b/src/app/core/config.py index fea112a..49d5902 100644 --- a/src/app/core/config.py +++ b/src/app/core/config.py @@ -10,19 +10,26 @@ from pydantic import ( class Settings(BaseSettings): PROJECT_NAME: str = 'FastAPI Portfolio & Blog' - MAIN_PATH: Path = Path(__file__).resolve().parent.parent.parent # path to src folder - APP_PATH: Path = MAIN_PATH / 'app' # path to app folder - MEDIA_FOLDER: Path = Path('media') # name of media folder - MEDIA_PATH: Path = MAIN_PATH / MEDIA_FOLDER # path to media folder - STATIC_FOLDER_NAME: Path = Path('static') # name of static folder - STATIC_FOLDER: Path = MAIN_PATH / STATIC_FOLDER_NAME # path to static folder + MAIN_PATH: Path = ( + Path(__file__). + resolve(). + parent. + parent. + parent + ) # path to src folder + APP_PATH: Path = MAIN_PATH / 'app' # path to app folder + MEDIA_FOLDER: Path = Path('media') # name of media folder + MEDIA_PATH: Path = MAIN_PATH / MEDIA_FOLDER # path to media folder + STATIC_FOLDER_NAME: Path = Path('static') # name of static folder + STATIC_FOLDER: Path =\ + MAIN_PATH / STATIC_FOLDER_NAME # path to static folder FILE_FOLDERS: dict[str, Path] = { 'post_images': Path('post_images'), } SECRET_KEY: str - ACCESS_TOKEN_EXPIRE_MINUTES: int = 43200 # 30 days + ACCESS_TOKEN_EXPIRE_MINUTES: int = 43200 # 30 days POSTGRES_USER: str POSTGRES_PASSWORD: str @@ -34,7 +41,7 @@ class Settings(BaseSettings): def LOGIN_URL(self) -> str: return self.SECRET_KEY[-10:] - def get_postgres_dsn(self, _async: bool=False) -> PostgresDsn: + def get_postgres_dsn(self, _async: bool = False) -> PostgresDsn: scheme = 'postgresql+asyncpg' if _async else 'postgresql' return PostgresDsn.build( @@ -56,4 +63,4 @@ class Settings(BaseSettings): EMAIL_RECIPIENTS: list[EmailStr] = [] -settings = Settings() \ No newline at end of file +settings = Settings() diff --git a/src/app/core/security.py b/src/app/core/security.py index 88b4f6d..f0072e4 100644 --- a/src/app/core/security.py +++ b/src/app/core/security.py @@ -32,7 +32,11 @@ def create_access_token( ) to_encode = {"exp": expire, "sub": str(subject)} - encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM) + encoded_jwt = jwt.encode( + to_encode, + settings.SECRET_KEY, + algorithm=ALGORITHM + ) return encoded_jwt @@ -42,4 +46,4 @@ def verify_password(plain_password: str, hashed_password: str) -> bool: def get_password_hash(password: str) -> str: - return pwd_context.hash(password) \ No newline at end of file + return pwd_context.hash(password) diff --git a/src/app/crud/base.py b/src/app/crud/base.py index 855381a..ad37118 100644 --- a/src/app/crud/base.py +++ b/src/app/crud/base.py @@ -26,7 +26,7 @@ UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel) class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): def __init__(self, model: Type[ModelType]): """ - CRUD object with default methods to Create, Read, Update, Delete (CRUD). + CRUD object with default methods to Create, Read, Update, Delete(CRUD). **Parameters** @@ -44,7 +44,12 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): self, db: Session, *, skip: int = 0, limit: int = 100 ) -> List[ModelType]: - q = select(self.model).offset(skip).limit(limit).order_by(self.model.id.desc()) + q = ( + select(self.model). + offset(skip). + limit(limit). + order_by(self.model.id.desc()) + ) obj = await db.execute(q) return obj.scalars() @@ -52,11 +57,18 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): self, db: Session, *, skip: int = 0, limit: int = 100 ) -> List[ModelType]: - q = select(self.model).offset(skip).limit(limit).order_by(self.model.id.desc()) + q = ( + select(self.model). + offset(skip). + limit(limit). + order_by(self.model.id.desc()) + ) obj = db.execute(q) return obj.scalars() - async def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType: + async def create( + self, db: Session, *, obj_in: CreateSchemaType + ) -> ModelType: obj_in_data = jsonable_encoder(obj_in) db_obj = self.model(**obj_in_data) # type: ignore db.add(db_obj) diff --git a/src/app/crud/crud_post.py b/src/app/crud/crud_post.py index 1972130..86a1e18 100644 --- a/src/app/crud/crud_post.py +++ b/src/app/crud/crud_post.py @@ -75,4 +75,5 @@ class CRUDPost(CRUDBase[Post, PostCreate, PostUpdate]): return obj -post = CRUDPost(Post) \ No newline at end of file + +post = CRUDPost(Post) diff --git a/src/app/crud/crud_user.py b/src/app/crud/crud_user.py index fce901f..ee5a00b 100644 --- a/src/app/crud/crud_user.py +++ b/src/app/crud/crud_user.py @@ -60,7 +60,11 @@ class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]): return db_obj async def update( - self, db: Session, *, db_obj: User, obj_in: Union[UserUpdate, Dict[str, Any]] + self, + db: Session, + *, + db_obj: User, + obj_in: Union[UserUpdate, Dict[str, Any]] ) -> User: if isinstance(obj_in, dict): @@ -76,7 +80,9 @@ class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]): return await super().update(db, db_obj=db_obj, obj_in=update_data) - async def authenticate(self, db: Session, *, email: str, password: str) -> Optional[User]: + async def authenticate( + self, db: Session, *, email: str, password: str + ) -> Optional[User]: user = await self.get_by_email(db, email=email) if user is None: @@ -93,4 +99,5 @@ class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]): async def is_superuser(self, user: User) -> bool: return user.is_superuser -user = CRUDUser(User) \ No newline at end of file + +user = CRUDUser(User) diff --git a/src/app/db/base.py b/src/app/db/base.py index 971bfd8..8123a9d 100644 --- a/src/app/db/base.py +++ b/src/app/db/base.py @@ -1 +1 @@ -from app.db.base_class import Base \ No newline at end of file +from app.db.base_class import Base diff --git a/src/app/db/base_class.py b/src/app/db/base_class.py index 9a98a85..821321c 100644 --- a/src/app/db/base_class.py +++ b/src/app/db/base_class.py @@ -16,10 +16,14 @@ class Base: __name__: str id: Any - created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False) + created_at = Column( + DateTime(timezone=True), + server_default=func.now(), + nullable=False + ) updated_at = Column(DateTime(timezone=True), onupdate=func.now()) # Generate __tablename__ automatically @declared_attr def __tablename__(cls) -> str: - return cls.__name__.lower() \ No newline at end of file + return cls.__name__.lower() diff --git a/src/app/db/session.py b/src/app/db/session.py index c79076c..5ac80e2 100644 --- a/src/app/db/session.py +++ b/src/app/db/session.py @@ -34,4 +34,4 @@ AsyncSessionLocal: AsyncSession = sessionmaker( autoflush=False, bind=async_engine, class_=AsyncSession -) \ No newline at end of file +) diff --git a/src/app/models/__init__.py b/src/app/models/__init__.py index 6c7dd17..99c0311 100644 --- a/src/app/models/__init__.py +++ b/src/app/models/__init__.py @@ -1,2 +1,3 @@ from .post import Post -from .user import User \ No newline at end of file +from .user import User +from . import post_events \ No newline at end of file diff --git a/src/app/models/post.py b/src/app/models/post.py index 2516675..4bcc846 100644 --- a/src/app/models/post.py +++ b/src/app/models/post.py @@ -1,12 +1,4 @@ -from contextlib import contextmanager -from slugify import slugify - -from sqlalchemy.orm.base import NO_VALUE from sqlalchemy.orm import relationship -from sqlalchemy.event import ( - listen, - listens_for -) from sqlalchemy import ( Column, ForeignKey, @@ -26,44 +18,3 @@ class Post(Base): owner_id = Column(Integer(), ForeignKey("user.id")) owner = relationship("User", back_populates="posts") - - -from app.views.depends import get_db -from app.utils.file_operations import remove_file -from app.core.config import settings - -def generate_slug(target, value, oldvalue, initiator): - slug = slugify(value) - - with contextmanager(get_db)() as db: - number = 1 - temp_slug = slug - - while db.query(Post).filter(Post.slug == temp_slug).first() is not None: - temp_slug = f'{slug}-{number}' - number += 1 - - target.slug = temp_slug - -listen(Post.title, 'set', generate_slug) - - -def remove_old_image_on_update(target, value, oldvalue, initiator): - if oldvalue is not NO_VALUE: - remove_file( - settings.MEDIA_PATH / - settings.FILE_FOLDERS['post_images'] / - oldvalue - ) - -listen(Post.image_path, 'set', remove_old_image_on_update) - - -@listens_for(Post, 'before_delete') -def before_delete_listener(mapper, connection, target): - if target.image_path: - remove_file( - settings.MEDIA_PATH / - settings.FILE_FOLDERS['post_images'] / - target.image_path - ) \ No newline at end of file diff --git a/src/app/models/post_events.py b/src/app/models/post_events.py new file mode 100644 index 0000000..aa64b85 --- /dev/null +++ b/src/app/models/post_events.py @@ -0,0 +1,54 @@ +from contextlib import contextmanager +from slugify import slugify + +from sqlalchemy.orm.base import NO_VALUE +from sqlalchemy.event import ( + listen, + listens_for +) + +from app.views.depends import get_db +from app.utils.file_operations import remove_file +from app.core.config import settings +from .post import Post + + +def generate_slug(target, value, oldvalue, initiator): + slug = slugify(value) + + with contextmanager(get_db)() as db: + number = 1 + temp_slug = slug + + while db.query(Post).filter( + Post.slug == temp_slug + ).first() is not None: + temp_slug = f'{slug}-{number}' + number += 1 + + target.slug = temp_slug + + +listen(Post.title, 'set', generate_slug) + + +def remove_old_image_on_update(target, value, oldvalue, initiator): + if oldvalue is not NO_VALUE: + remove_file( + settings.MEDIA_PATH / + settings.FILE_FOLDERS['post_images'] / + oldvalue + ) + + +listen(Post.image_path, 'set', remove_old_image_on_update) + + +@listens_for(Post, 'before_delete') +def before_delete_listener(mapper, connection, target): + if target.image_path: + remove_file( + settings.MEDIA_PATH / + settings.FILE_FOLDERS['post_images'] / + target.image_path + ) diff --git a/src/app/models/user.py b/src/app/models/user.py index 6d85ea1..10b631a 100644 --- a/src/app/models/user.py +++ b/src/app/models/user.py @@ -17,4 +17,4 @@ class User(Base): is_active = Column(Boolean(), default=True) is_superuser = Column(Boolean(), default=False) - posts = relationship("Post", back_populates="owner") \ No newline at end of file + posts = relationship("Post", back_populates="owner") diff --git a/src/app/schemas/login.py b/src/app/schemas/login.py index 3c0d13c..ebfd452 100644 --- a/src/app/schemas/login.py +++ b/src/app/schemas/login.py @@ -21,4 +21,4 @@ class JWTToken(BaseModel): class JWTTokenPayload(BaseModel): - sub: Optional[EmailStr] = None \ No newline at end of file + sub: Optional[EmailStr] = None diff --git a/src/app/schemas/main.py b/src/app/schemas/main.py index 97534d4..87af139 100644 --- a/src/app/schemas/main.py +++ b/src/app/schemas/main.py @@ -11,4 +11,4 @@ class SendEmail: name: str = Form(..., max_length=50) email: EmailStr = Form(...) phone: Optional[str] = Form(None, max_length=20) - message: str = Form(..., max_length=1000) \ No newline at end of file + message: str = Form(..., max_length=1000) diff --git a/src/app/schemas/post.py b/src/app/schemas/post.py index 03d6fee..ef1ff13 100644 --- a/src/app/schemas/post.py +++ b/src/app/schemas/post.py @@ -13,7 +13,6 @@ from app.utils.custom_functions import html2text from app.core.config import settings - class PostBase(BaseModel): title: Optional[str] = Field(max_length=100) text: Optional[str] = None @@ -26,7 +25,8 @@ class PostCreate(PostBase): image_path: str -class PostUpdate(PostBase): ... +class PostUpdate(PostBase): + ... class PostInTemplate(BaseModel): @@ -85,4 +85,4 @@ class Post(PostInDBBase): settings.MEDIA_FOLDER / settings.FILE_FOLDERS['post_images'] / v - ) \ No newline at end of file + ) diff --git a/src/app/schemas/user.py b/src/app/schemas/user.py index 593a307..b225cc5 100644 --- a/src/app/schemas/user.py +++ b/src/app/schemas/user.py @@ -34,4 +34,4 @@ class User(UserInDBBase): class UserInDB(UserInDBBase): - hashed_password: str \ No newline at end of file + hashed_password: str diff --git a/src/app/utils/custom_functions.py b/src/app/utils/custom_functions.py index 4e72949..40d2cce 100644 --- a/src/app/utils/custom_functions.py +++ b/src/app/utils/custom_functions.py @@ -8,4 +8,4 @@ def html2text(html: str) -> str: def get_remote_address(request: Request) -> str: - return request.headers.get('host') \ No newline at end of file + return request.headers.get('host') diff --git a/src/app/utils/email_utils.py b/src/app/utils/email_utils.py index 6bb976c..5e51e38 100644 --- a/src/app/utils/email_utils.py +++ b/src/app/utils/email_utils.py @@ -17,21 +17,21 @@ def send_email_notification( if settings.EMAIL_RECIPIENTS: conf = ConnectionConfig( - MAIL_USERNAME = settings.SMTP_USER, - MAIL_PASSWORD = settings.SMTP_PASSWORD, - MAIL_FROM = settings.SMTP_USER, - MAIL_PORT = settings.SMTP_PORT, - MAIL_SERVER = settings.SMTP_HOST, - MAIL_SSL_TLS = settings.SMTP_SSL_TLS, - MAIL_STARTTLS = True + MAIL_USERNAME=settings.SMTP_USER, + MAIL_PASSWORD=settings.SMTP_PASSWORD, + MAIL_FROM=settings.SMTP_USER, + MAIL_PORT=settings.SMTP_PORT, + MAIL_SERVER=settings.SMTP_HOST, + MAIL_SSL_TLS=settings.SMTP_SSL_TLS, + MAIL_STARTTLS=True ) message = MessageSchema( - subject = subject, - recipients = settings.EMAIL_RECIPIENTS, - body = body, + subject=subject, + recipients=settings.EMAIL_RECIPIENTS, + body=body, subtype=MessageType.plain ) fast_mail = FastMail(conf) - return partial(fast_mail.send_message, message) \ No newline at end of file + return partial(fast_mail.send_message, message) diff --git a/src/app/utils/file_operations.py b/src/app/utils/file_operations.py index b12410f..dc274a7 100644 --- a/src/app/utils/file_operations.py +++ b/src/app/utils/file_operations.py @@ -8,5 +8,7 @@ async def mkdir_if_not_exists(path: Path) -> None: def remove_file(file_path: str) -> None: - try: remove(file_path) - except FileNotFoundError: ... \ No newline at end of file + try: + remove(file_path) + except FileNotFoundError: + ... diff --git a/src/app/utils/image_operations.py b/src/app/utils/image_operations.py index 0519737..648898c 100644 --- a/src/app/utils/image_operations.py +++ b/src/app/utils/image_operations.py @@ -22,4 +22,4 @@ async def generate_unique_image_name( async def save_image(image: Image, image_path: Path) -> None: await mkdir_if_not_exists(image_path.parent) - image.save(image_path) \ No newline at end of file + image.save(image_path) diff --git a/src/app/utils/rate_limiter.py b/src/app/utils/rate_limiter.py index 9df09df..4975b43 100644 --- a/src/app/utils/rate_limiter.py +++ b/src/app/utils/rate_limiter.py @@ -1,4 +1,4 @@ from slowapi import Limiter from app.utils.custom_functions import get_remote_address -limiter = Limiter(key_func=get_remote_address) \ No newline at end of file +limiter = Limiter(key_func=get_remote_address) diff --git a/src/app/views/depends.py b/src/app/views/depends.py index 960ac30..df90e2f 100644 --- a/src/app/views/depends.py +++ b/src/app/views/depends.py @@ -81,7 +81,7 @@ async def get_current_user_or_die( detail="Could not validate credentials", ) - if token_data.sub is None: + if token_data.sub is None: raise HTTPException(status_code=404, detail="User not found") user = await crud.user.get_by_email(db, email=token_data.sub) @@ -98,7 +98,8 @@ async def get_current_user_or_none( ) ) -> UserModel | None: - if token is None: return None + if token is None: + return None try: payload = jwt.decode( @@ -109,7 +110,7 @@ async def get_current_user_or_none( except (jwt.JWTError, ValidationError): return None - if token_data.sub is None: + if token_data.sub is None: return None user = await crud.user.get_by_email(db, email=token_data.sub) @@ -132,7 +133,8 @@ async def get_current_active_user_or_none( current_user: UserModel | None = Depends(get_current_user_or_none), ) -> UserModel | None: - if current_user is None: return None + if current_user is None: + return None if current_user.is_active is False: return None @@ -154,7 +156,8 @@ async def get_current_active_superuser_or_none( current_user: UserModel | None = Depends(get_current_active_user_or_none), ) -> UserModel | None: - if current_user is None: return None + if current_user is None: + return None if current_user.is_superuser is False: return None @@ -182,19 +185,19 @@ async def handle_post_image_or_die(image: UploadFile) -> str: raise ValueError('Invalid image format') unique_image_name = await generate_unique_image_name( - path = settings.MEDIA_PATH / settings.FILE_FOLDERS['post_images'], - image_name = image.filename, - image_format = pil_image.format.lower() + path=settings.MEDIA_PATH / settings.FILE_FOLDERS['post_images'], + image_name=image.filename, + image_format=pil_image.format.lower() ) await save_image( - image = pil_image, - image_path = settings.MEDIA_PATH / - settings.FILE_FOLDERS['post_images'] / - unique_image_name + image=pil_image, + image_path=settings.MEDIA_PATH / + settings.FILE_FOLDERS['post_images'] / + unique_image_name ) - except Exception as e: + except Exception: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail='Invalid image' @@ -204,12 +207,14 @@ async def handle_post_image_or_die(image: UploadFile) -> str: try: pil_image.close() - except: ... + except Exception: + ... return str(unique_image_name) async def handle_post_image_or_none(image: UploadFile = None) -> Optional[str]: - if image is None: return None + if image is None: + return None try: pil_image = Image.open(io.BytesIO(image.file.read())) @@ -218,19 +223,19 @@ async def handle_post_image_or_none(image: UploadFile = None) -> Optional[str]: raise ValueError('Invalid image format') unique_image_name = await generate_unique_image_name( - path = settings.MEDIA_PATH / settings.FILE_FOLDERS['post_images'], - image_name = image.filename, - image_format = pil_image.format.lower() + path=settings.MEDIA_PATH / settings.FILE_FOLDERS['post_images'], + image_name=image.filename, + image_format=pil_image.format.lower() ) await save_image( - image = pil_image, - image_path = settings.MEDIA_PATH / - settings.FILE_FOLDERS['post_images'] / - unique_image_name + image=pil_image, + image_path=settings.MEDIA_PATH / + settings.FILE_FOLDERS['post_images'] / + unique_image_name ) - except Exception as e: + except Exception: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail='Invalid image' @@ -240,5 +245,6 @@ async def handle_post_image_or_none(image: UploadFile = None) -> Optional[str]: try: pil_image.close() - except: ... - return str(unique_image_name) \ No newline at end of file + except Exception: + ... + return str(unique_image_name) diff --git a/src/app/views/router.py b/src/app/views/router.py index dee9ad7..14ced64 100644 --- a/src/app/views/router.py +++ b/src/app/views/router.py @@ -7,4 +7,4 @@ from app.views.routers import ( main_router = APIRouter() main_router.include_router(main.router, tags=['main']) -main_router.include_router(user.router, tags=['user']) \ No newline at end of file +main_router.include_router(user.router, tags=['user']) diff --git a/src/app/views/routers/main.py b/src/app/views/routers/main.py index d00cb3a..b66fad3 100644 --- a/src/app/views/routers/main.py +++ b/src/app/views/routers/main.py @@ -56,8 +56,8 @@ async def send_email( f"message: {form_data.message}" email_notification = send_email_notification( - subject = f"Portfolio Blog (by {form_data.email})", - body = body + subject=f"Portfolio Blog (by {form_data.email})", + body=body ) if email_notification is not None: @@ -124,4 +124,4 @@ async def blog_post( 'request': request, 'post': PostDetail.model_validate(post) } - ) \ No newline at end of file + ) diff --git a/src/app/views/routers/user.py b/src/app/views/routers/user.py index 3f63c63..c7ceeda 100644 --- a/src/app/views/routers/user.py +++ b/src/app/views/routers/user.py @@ -81,12 +81,17 @@ async def login( ) if user is None: - raise HTTPException(status_code=400, detail="Incorrect email or password") + raise HTTPException( + status_code=400, + detail="Incorrect email or password" + ) elif user.is_active is False: raise HTTPException(status_code=400, detail="Inactive user") - access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) + access_token_expires = timedelta( + minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES + ) return { "access_token": security.create_access_token( @@ -130,7 +135,9 @@ async def create_post( image_path=image ) - post = await crud.post.create_with_owner(db, obj_in=obj_in, owner_id=user.id) + post = await crud.post.create_with_owner( + db, obj_in=obj_in, owner_id=user.id + ) return RedirectResponse( str(request.url_for('get_update_post', slug=post.slug)), diff --git a/src/app/utils/create_user.py b/src/commands/create_user.py similarity index 98% rename from src/app/utils/create_user.py rename to src/commands/create_user.py index 89fecb6..54df11c 100644 --- a/src/app/utils/create_user.py +++ b/src/commands/create_user.py @@ -1,6 +1,3 @@ -from sys import path -path.append('/src') - from contextlib import contextmanager from typing import Optional diff --git a/src/poetry.lock b/src/poetry.lock index 46b0d0a..5023407 100644 --- a/src/poetry.lock +++ b/src/poetry.lock @@ -432,6 +432,22 @@ starlette = ">=0.24,<1.0" httpx = ["httpx[httpx] (>=0.23,<0.24)"] redis = ["redis[redis] (>=4.3,<5.0)"] +[[package]] +name = "flake8" +version = "6.1.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.8.1" +files = [ + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" + [[package]] name = "greenlet" version = "2.0.2" @@ -670,6 +686,17 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + [[package]] name = "packaging" version = "23.1" @@ -798,6 +825,17 @@ files = [ {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, ] +[[package]] +name = "pycodestyle" +version = "2.11.0" +description = "Python style guide checker" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"}, + {file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"}, +] + [[package]] name = "pycparser" version = "2.21" @@ -962,6 +1000,17 @@ files = [ pydantic = ">=2.0.1" python-dotenv = ">=0.21.0" +[[package]] +name = "pyflakes" +version = "3.1.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, +] + [[package]] name = "python-dotenv" version = "1.0.0" @@ -1316,4 +1365,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "2b3c19e4086c8115eff1b1cebe4d1db60dab98b6996cdd1c765f79f82dc7239d" +content-hash = "fd13e903aabf74c2a3811928381eddbd7b8e064a51fb4be5a954e7d9917cddc1" diff --git a/src/pyproject.toml b/src/pyproject.toml index 850d673..73d1246 100644 --- a/src/pyproject.toml +++ b/src/pyproject.toml @@ -25,6 +25,7 @@ python-jose = {extras = ["cryptography"], version = "^3.3.0"} fastapi-mail = "^1.4.1" beautifulsoup4 = "^4.12.2" slowapi = "^0.1.8" +flake8 = "^6.1.0" [build-system]