Skip to main content

9 posts tagged with "python"

View All Tags

paramiko ssh stdin.write 명령어 사용 시 OSError socket is closed 발생

· One min read

image

python을 활용해 ssh 접속을 하고 명령어를 실행하는 것을 자동화하는 작업을 진행하고 있습니다.

이 때 사용하기 좋은 라이브러리가 sftp, ssh 모두 간단히 이용할 수 있는 paramiko입니다.

개발하던 중 테스트 중 sudo 명령어에 의해 막히는 일이 발생합니다.

오류에서는 stdin.write 명령에 의해 발생하는데, OSError: socket is closed 라고 나옵니다. 

내가 너무 많이 연결 테스트를 해서 발생한건가 싶어 close를 잘 하고 있는지 점검하고 close가 잘 되어 있는데도 이런가 싶어 container를 재실행해 줍니다. 하지만 그래도 계속 발생하는데요. 완전히 헛다리를 짚었죠 ㅠㅠ

알고 나면 간단한 해결책은 이것입니다.

ssh.exec_command(install_cmd, get_pty=True)

위와 같이 명령어를 실행할 때 get_tpy 옵션을 True로 해줍니다.

paramiko의 exec_command 메서드는 get_pty 옵션을 제공하는데, 이것은 pesugo-terminal(PTY)를 사용할지 여부를 결정합니다.

PTY는 터미널과 로컬 랩탑의 터미널과 유사한 환경을 원격 명령 실행에 제공하는데 사용됩니다.

그래서 대화형 프로그램이나 비밀번호 입력이 필요한 명령을 실행할 때 유용합니다. 하지만 기본값은 터미널 환경과 독립적으로 실행되는 False이기 때문에 True로 변경해 주면 이와 같이 sudo 명령이나 password를 물을 때 유용합니다.

알고 나면 아주 간단한데, 알기까지 고생해서 도움이 될까 싶어 공유합니다.

NestJS를 Python으로 만든다면!?

· 4 min read

image

개발자들은 확장 가능하고 유지 관리하기 좋은 API에 대한 수요가 증가하면서 강력한 솔루션을 제공하는 프레임워크를 찾고 있다. Node.js 애플리케이션 구축을 위한 프레임워크인 NestJS는 종속성 주입, 데코레이터, 모듈식 아키텍처를 포함한 강력한 기능으로 인정을 받았다.

동시에 Generative AI는 대규모 언어 모델의 힘을 보여주는 GPT4와 같은 모델을 통해 놀라운 발전을 이루면서, 생성적 AI 애플리케이션에 대한 수요로 인해 다양한 API와 상호 작용해야 하는 확장 가능한 마이크로서비스를 구축하는 데 중점을 두게 됩니다. 라이브러리와 프레임워크로 구성된 광범위한 생태계를 갖춘 Python은 생성 AI 모델을 구현하는 데 탁월한 언어로 부상했고 다재다능함과 유연성 덕분에 이러한 애플리케이션의 요구 사항을 충족하는 확장 가능한 솔루션을 개발하기에 이상적이다. Python을 사용하면 개발자는 생성 AI의 발전을 활용하고 효율적이고 강력한 애플리케이션을 구축할 수 있다.

NestJS에서 영감을 받은 Python 프레임워크 PyNest!

PyNestNestJS의 모듈식 아키텍처FastAPI를 기반으로구축된 Python 프레임워크다. Python 개발자, 데이터 과학자, ML 엔지니어 및 데이터 엔지니어에게 확장 가능하고 유지 관리 가능한 API를 구축하기 위한 직관적이고 강력한 프레임워크를 제공하는 것을 목표로 한다.

핵심은 깨끗하고 체계적인 방식으로 API를 구성하는 문제를 해결한다. NestJS의 모듈식 아키텍처를 채택함으로써 PyNest를 사용하면 개발자가 문제를 분리하고 코드를 모듈, 컨트롤러, 서비스 및 공급자로 구성할 수 있습니다. 이 모듈식 접근 방식은 코드 재사용성, 테스트 가능성 및 유지 관리성을 향상하여 애플리케이션이 발전함에 따라 확장을 촉진하게 됩니다.

image

PyNest로 문제 해결

PyNest는 확장 가능하고 유지 관리 가능한 Python API 구축과 관련된 문제를 해결하는 데 기여하는 몇 가지 주요 기능을 제공한다.

1. Modular Architecture

NestJS에서 영감을 받은 PyNest의 모듈식 아키텍처는 코드 구성에 대한 구조화된 접근 방식을 제공하는데, module, controller, service 및 provider는 문제를 논리적으로 분리하여 코드 중복을 줄이고 유지 관리성을 향상시킨다.

image

2. Dependency Injection

PyNest는 종속성 관리를 단순화하고 테스트 가능성을 높이는 기술인 종속성 주입을 지원해서 개발자는 service와 provider를 controller에 쉽게 주입하여 더욱 깔끔하고 모듈화된 코드를 구현할 수 있다.

3. Decorators

Decorator는 PyNest에서 중요한 역할을 하며 개발자가 경로, 미들웨어 및 기타 애플리케이션 구성 요소를 간결하게 정의할 수 있도록 해준다. 코드 가독성을 향상시키고 개발자가 API의 핵심 기능에 집중할 수 있도록 해다.

4. Type Annotations

PyNest는 FastAPI 유형 주석 메커니즘을 활용하여 더 나은 도구 및 오류 방지 기능을 제공한다. controller, service 및 provider에 유형을 추가하여 개발자는 코드의 견고성과 유지 관리성을 향상시킬 수 있다.

5. Code Generation

PyNest에는 module, controller 및 기타 구성 요소에 대한 상용구 코드 생성을 자동화하는 코드 생성 도구가 포함되어 있어서 개발자의 시간이 절약되고 코드베이스의 필수 부분을 작성하는 데 집중할 수 있다.

6. Database Support

PyNest는 PostgreSQL, MySQL, SQLite와 같은 여러 데이터베이스에서 즉시 사용 가능한 지원을 제공한다.

Getting Started with PyNest

1. 새 프로젝트 생성 및 가상 환경 활성화

python -m venv venv && source venv/bin/activate

2. pip를 사용하여 PyNest를 설치한다.

pip install pynest-api

3. CLI를 사용하여 새 PyNest 프로젝트를 만든다.

pynest create-nest-app -n my_app_name
  • 참고 — 기본적으로 pynest는 SQLite 데이터베이스를 기본으로 하고 PostgreSQL 및 MySQL도 지원한다. 금방 MongoDB 및 기타 데이터베이스도 지원할 예정이다.
  • PostgreSQL로 앱을 생성하려면 아래와 같이 명령하면 된다.
pynest create-nest-app -n my_app_name -db postgresql

4. project directory로 이동한다.

cd my_app_name

5. uvicorn을 사용하여 서버를 실행합니다.

uvicorn "app:app" - host "0.0.0.0" - port "80" - reload

image

PyNest는 애플리케이션 내의 특정 기능을 위한 모듈 생성을 권장한다. 그러니 CLI를 사용해 간단히 모듈을 생성하면 좋다.

pynest generate-module -n module_name

결론

PyNest는 NestJS의 모듈식 아키텍처에서 영감을 받은 강력한 Python 프레임워크다. 이를 통해 Python 개발자는 Python의 강점과 NestJS 확장 가능하고 유지 관리 가능한 API를 구축할 수 있다. PyNest는 종속성 주입, 데코레이터 및 코드 생성을 위한 직관적인 구조와 지원을 통해 확장 가능한 Python API를 개발할 때 직면하는 과제를 해결한다.

생성적 AI에 대한 수요가 계속 증가함에 따라 Python은 생성적 AI 작업에 맞춤화된 마이크로서비스를 구현하기 위한 선택 언어로 부상했는데, PyNest의 모듈식 아키텍처와 확장 가능한 마이크로서비스 지원은 생성 AI 분야의 개발자에게 유용한 도구로 사용하기 좋다.

PyNest를 활용하여 API를 효과적으로 구성하고, pip를 통해 설치하고, Python 프로젝트에서 PyNest의 강력한 기능을 활용해 확장 가능하고 유지 관리가 가능하며 효율적인 API를 구축하길 바란다.

아래는 PyNest 문서를 읽어볼 수 있는 링크이다.

https://pythonnest.github.io/PyNest/

python requests InsecureRequestWarning 해결법

· 2 min read

image

python으로 자동으로 request 요청하다 보면 가끔 오류가 나서 살펴보면 같은 곳에 파라미터만 바꿔서 계속 요청중인데도 불구하고 ssl 인증서를 신뢰할 수 없다며 오류를 내뱉는다.

이런 이상한 오류들은 일일이 대응하기엔 시간이 없다.

그래서 requests.post(url=url, headers=headers, json=payload, verify=false, timeout=10) 처럼 verify와 timeout 옵션을 넣어줬다. verify=false로 해두면 인증서를 걍 신뢰하게 하는거다. 위험부담이 있지만 보통 api 사용할 때 믿을만한 곳을 이용하기도 하고 이렇게 같은 곳에 요청해도 어떨 때는 나고 어떨 때는 안 난다면 과감히 이런 조치를 하는 것도 좋다고 생각한다.

이렇게 조치가 끝나나 했는데 다시 아래의 오류가 등장했다. 무한 반복해서 계속 등장한다.😅(필요없는 부분은 ... 처리!)

...lib/python3.10/site-packages/urllib3/connectionpool.py:1061: InsecureRequestWarning: Unverified HTTPS request is being made to host '...'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html#ssl-warnings

체감상으로는 요청할 때마다 등장하는 것 같은데 괜히 이러다 문제 생기는 건 아닌가 불안해지고 기껏 속도 체크나 중간에 확인을 위해 찍어뒀던 로그들도 안 보일만큼 내 터미널 라인 설정을 훨씬 뛰어넘는 Warning이 무한정 뜨는데 이걸 해결하는 방법은 3가지가 있다!(셋 중 하나만 해도 됨!)

image

  1. urllib3을 이용해 disable하고 싶은 warning만 disable 하는 방법
from urllib3.exceptions import InsecureRequestWarning
from urllib3 import disable_warnings
disable_warnings(InsecureRequestWarning)

이렇게 하면 InsecureRequestWarning은 disable 되서 내 마음을 더 이상 괴롭히지 않는다.😄

  1. requests 에서 warning을 disable하는 방법
import requests
requests.packages.urllib3.disable_warnings()

이렇게 requests 모듈에서도 disable 되게 만들어 줄 수 있다! 👍

  1. 이번엔 최신 CA 파일을 통해 요청하는 방법인데, 이건 귀찮아서 직접 해보진 않았다...;

우선 최신 CA 파일을 여기서 다운받고, 아래와 같이 path를 넣어주면 된다고 한다.

requests.post(url=API_SERVER, headers=headers, data=json.dumps(data), verify='CA_PATH')
requests.get('https://github.com', verify='/path/to/certfile')

image

이 외에도 다른 방법들도 있는데 점점 귀찮아지는 방법들이라 나는 여기까지...

정말 시간 부족한 사람들 많을텐데 조금이나마 도움이 됐으면 좋겠다!!

Exception & Error Handling in Python For Professional

· 6 min read

image

System Error 500은 유저의 앱 경험을 방해할 수 있는 오류입니다. Backend에서 항상 유저에게 원활하고 효율적이며 반응성이 좋은 앱을 제공하기 위해 많은 노력들을 기울이지만 예상치 못한 오류를 완전히 피할 수는 없으니 이를 위한 처리꼭 필요합니다.

이러한 오류 처리는 탄탄한 앱을 만드는데 필수적이고 런타임 오류를 예측하는데 도움을 주고 앱의 예기치 못한 동작 오류를 막아줍니다.

이를 위한 오류 처리 방법에 대해 간단히 알아보려고 합니다.

오류의 유형

Python에서 오류는 주요 유형으로 3가지로 분류할 수 있는데 Syntax Errors, Runtime Errors, Logic Errors가 있습니다.

Syntax Errors 는 Python의 구문 분석기가 구문 오류를 발견할 때 발생합니다. Runtime Errors 는 프로그램 실행 중 감지된 오류로 프로그램 실행에 문제는 없지만 예외 케이스로 인해 발생한 오류를 말합니다. Logic Errors 는 프로그램의 논리나 알고리즘에 오류가 있는 경우를 말합니다.

그리고 Python에서 오류를 생성할 때 다양한 내장 예외들이 있는데 이름을 찾을 수 없을 때 NameError, 연산이나 함수에 잘못된 타입이 적용됐을 때 TypeError, 함수의 인수가 올바른 타입입지만 값이 잘못 들어오면 ValueError 등이 발생합니다.

그래서 우리는 아래와 같은 예외처리를 위해 Try-Except 을 사용합니다.

try : 
# 예외를 발생시킬 수 있는 코드
x = 1 / 0
Except ZeroDivisionError:
# ZeroDivisionError가 발생하면 실행되는 코드
x = 0
Except TypeError:
# TypeError가 발생하면 실행되는 코드
x = 1

Try-Except 구문은 try 블록 내에 전체 코드를 캡슐화하고 블록 내에서 발생하는 여러 오류 유형에 맞게 오류를 보여줍니다.

image

내장 예외 클래스

위에서 말했던 내장 예외 클래스들은 아래와 같이 있습니다.

  • IOError : 이 예외는 I/O 관련 이유로 I/O 작업(예: print 문, 내장 open() 함수 또는 파일 객체의 메서드)이 실패할 때 발생합니다. 예를 들어 '파일을 찾을 수 없음' 또는 '디스크가 가득 참' 오류가 있습니다.
  • ValueError : 내장 연산이나 함수가 올바른 유형이지만 부적절한 값을 가진 인수를 받으면 발생합니다.
  • ZeroDivisionError : 나누기 또는 모듈로 연산의 두 번째 인수가 0일 때 발생합니다. Python은 0으로 나누는 것이 수학적으로 정의되지 않았기 때문에 이 예외를 발생시킵니다.
  • ImportError : 모듈이 존재하지 않거나 모듈 경로가 올바르지 않아 Python이 가져오려는 모듈을 찾을 수 없는 경우 Python은 ImportError를 발생시킵니다.
  • EOFError : 내장 함수(input() 또는 raw_input()) 중 하나가 데이터를 읽지 않고 파일 끝(EOF) 조건에 도달할 때 발생합니다. 이 오류는 대화형 명령줄 응용 프로그램을 만들 때 가끔 발생합니다.

위의 오류 출력 방법 외에도 모든 예외를 포착하고 싶다면 sys 모듈의 exc_info()를 활용하거나 Except 절의 변수를 두어 print 할 수 있습니다.

- sys.exc_info() 사용

import sys 

try :
# 예외를 발생시키는 일부 작업
result = 1 / 0
Except :
exc_type, exc_obj, exc_traceback = sys.exc_info()
print ( f" {exc_type.__name__} 유형의 예외가 발생했습니다. 세부 정보: {exc_obj} " )

- Except 절 변수 사용

try : 
# 예외를 발생시키는 일부 작업
result = 1 / 0
Except Exception as e:
print ( f" { type (e).__name__} 유형의 예외가 발생했습니다. 세부 정보: { str (e)} " )

여기까지 예외들에 대해 알아 봤고 이를 핸들링하는 방법들에 대해 알아봤습니다.

요새(이미 핫해진?) FastApi에서 예외 처리하는 방법에 대해 알아봅니다.

간단하게 예외를 처리하고 적절한 http 상태 코드를 반환하는 방법이 있습니다.

from fastapi import HTTPException
class ResourceNotFound(HTTPException):
def __init__(self):
super().__init__(status_code=404, detail="Resource not found")
@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise ResourceNotFound()
return {"item": items[item_id]}

만약 ResourceNotFound 예외가 발생하면 FastApi에서는 상태코드 404와 세부 메시지를 포함한 값을 반환합니다.

이 외에도 미들웨어에 Exception을 잡는 미들웨어를 만들고 추가해 아래와 같이 에러 처리를 하는 방법도 있습니다. 만약 에러가 발생하면 모든 에러를 잡을 것이고 결과로 400 상태코드와 자세한 에러메시지까지 내보냅니다.

from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

@app.middleware("http")
async def middleware(request: Request, call_next):
try:
response = await call_next(request)
return response
except Exception as e:
return JSONResponse(status_code=400, content={"message": str(e)})

또한 서비스 영역에서도 Exception을 잡는 방법이 있습니다.

아래와 같이 정의하고

class UsernameNotUnique(Exception):
"""Raised when the provided username is not unique"""
def __init__(self, username, message="Username is already in use"):
self.username = username
self.message = message
super().__init__(self.message)

def __str__(self):
return f'{self.username} -> {self.message}'

아래와 같이 발생시킬 수 있습니다.

class UserService:
@staticmethod
async def create_user(db: Session, user: UserCreate) -> User:
db_user = get_user_by_username(db, username=user.username)
if db_user:
raise UsernameNotUnique("Username already taken.")
return create_user(db=db, user=user)

그리고 아래와 같이 잡아낼 수 있습니다.

@app.post("/users/")
async def create_user(user: UserCreate, db: Session = Depends(get_db)):
try:
return UserService.create_user(db, user)
except UsernameNotUnique as e:
raise HTTPException(status_code=400, detail=str(e))

고급 오류 처리 기술

- finally 구문

try-except에서 예외가 발생하더라도 반드시 마지막에는 실행되는 구문입니다.

이 구문을 통해 Backend에서 파일을 열었는데 오류가 발생해 파일을 닫지 못하는 사태를 막을 수 있습니다.

try:
# attempt to open a file and write to it
file = open('test_file.txt', 'w')
file.write('Hello, world!')
except:
print('An error occurred while writing to the file.')
finally:
# this code will run whether an exception was raised or not
file.close()
print('The file has been closed.')

- else 구문 

try-except에서 사용하지만 잘 모르는 else 구문이 있는데 이는 try 구문에서 예외가 발생하지 않는 경우에만 실행되는 구문입니다.

try:
result = 1 / 2 # no exception raised here
except ZeroDivisionError:
print('Divided by zero!')
else:
print(f'The division was successful, and the result is {result}.')

- Contextlib을 활용한 에러 처리

만약 데이터베이스를 연결한다면 에러가 발생하더라도 연결이 종료되는 것을 확인하고 관리할 수 있어야 합니다.

이를 위해 아래와 같이 contextlib.contextmanager 를 활용할 수 있습니다.

from contextlib import contextmanager

class DatabaseConnection:
def __init__(self, name):
self.name = name

def close(self):
print(f"Database {self.name} connection has been closed.")

@contextmanager
def database_connection(name):
db = DatabaseConnection(name) # set up the connection
try:
print(f"Database {db.name} connection has been established.")
yield db # yield control back to the main code
finally:
db.close() # ensure the connection gets closed

with database_connection("test_db") as db:
print(f"Performing operations on {db.name} database.")

 Traceback을 활용해 Debugging 하는 방법

에러가 발생할 때 디버깅이 필요할 때 활용하는 Traceback 모듈이 있습니다.

def function1():
function2()

def function2():
raise Exception('An error occurred')

function1()

이렇게 실행하면 Python에서는 아래와 같이 출력합니다.

Traceback (most recent call last):
File "script.py", line 7, in <module>
function1()
File "script.py", line 2, in function1
function2()
File "script.py", line 5, in function2
raise Exception('An error occurred')
Exception: An error occurred

이것이 Traceback에서 제공하는 출력인데 보면 function1을 실행하면서 function1에서 function2를 실행하고 이것이 function2에서 raise Exception을 몇 번째 라인에서 실행했다는 것까지 자세하게 예외 생성 과정을 출력합니다.

이번에는 모듈을 활용하는 방법입니다.

import traceback

try:
function1()
except Exception:
tb = traceback.format_exc()

print("Here's the traceback:")
print(tb)

FastApi에서 이를 활용하는 방법도 있는데 아주 간단하게 가능합니다.

from fastapi import FastAPI

app = FastAPI(debug=True)

@app.get("/")
def root():
function1()

debug=True만 옵션에 넣어줘도 traceback이 포함됩니다.

결론

Production 코드에서는 예외처리를 통해 유저 경험을 향상시켜줘야 합니다! 또한 데이터 손상이나 손실을 방지하기 위해서도 이는 중요합니다. 오류 처리 기술을 이해하고 구현하면 코드 품질, 사용자 경험 및 유지 관리 가능성을 향상시킬 수 있습니다.

프로그래밍의 다른 측면과 마찬가지로 예외 처리는 지속적인 학습과 개선이 필요한 영역이며, 배우고 적용할 수 있는 새로운 기술, 도구, 모범 사례가 있으니 항상 검색해 보기 바랍니다!

감사합니다!

pd.read_csv(), pd.to_csv() -> Modin 👍

· 2 min read

image

머신러닝을 활용하거나 빅데이터를 다루거나 작은 크롤링 작업을 하다 보면 csv 파일을 많이 이용하게 되는데 이 때 pandas 를 사용하게 되면 pd.read_csv() / pd.to_csv() 명령을 자주 사용하게 된다.

하지만 데이터 사이즈가 커질수록 그 성능 문제가 생기고 많은 시간이 소요되는 등 불편한 점들이 점점 발생한다.

그 한계를 극복하기 위해 분산컴퓨팅 기능을 제공하는 보다 효율적인 새로운 대안을 찾아보게 됐다.

read_csv() 의 경우

import pandas as pd
import dask.dataframe as dd

# Reading a large CSV file with pandas
df_pandas = pd.read_csv('large_dataset.csv')
# Reading the same file with dask
df_dask = dd.read_csv('large_dataset.csv')
# Timing the execution
%timeit df_pandas.head()
%timeit df_dask.head()

위에는 dask를 활용한 예시인데 데이터 사이즈가 클수록 더 많은 시간을 줄일 수 있다. 

to_csv() 의 경우

import pandas as pd
import fastparquet

# Saving a DataFrame to a Parquet file
df = pd.DataFrame({'column1': [1, 2, 3], 'column2': ['a', 'b', 'c']})
fastparquet.write('output.parquet', df)

위와 같이 fastparquet을 활용해 공간 및 성능 효율적으로 실행할 수 있다.

설명한 것처럼 여러가지 방법들이 많지만 Modin에 대해 알아보게 됐습니다!

Modin으로 csv를 읽어오는 법

import modin.pandas as pd

# Reading a CSV file with Modin
df = pd.read_csv('data.csv')

단순히 pandas 를 위와 같이 대체만 해줘도 되는 쉬운 방식으로 성능적이 이득을 얻을 수 있다.

Modin으로 csv를 쓰는 법

import modin.pandas as pd

# Create a sample DataFrame
df = pd.DataFrame({'column1': [1, 2, 3], 'column2': ['a', 'b', 'c']})
# Writing the DataFrame to a CSV file with Modin
df.to_csv('output.csv', index=False)

파일을 쓰는 법도 읽어오는 것처럼 대체만 해주면 바로 사용이 가능하고 훨씬 빠르고 효율적으로 저장할 수 있다.

이처럼 많은 대체 방법 중 Modin을 소개하는 것은 modin.pandas만 해주면 코드를 수정하지 않아도 이용할 수 있기 때문이다.

만약 pandas 만의 고유 기능이 있어 다시 pandas 를 사용하고 싶다면 아래와 같이 간단히 switching 할 수 있다.

import modin.pandas as pd

# Reading a CSV file with Modin
df = pd.read_csv('data.csv')
# Perform some data analysis with Modin
# Switch to pandas
df = df.__pandas__()
# Continue working with pandas
df.head()

결론

라이브러리들은 계속 발전하고 여러가지 대체 방법들은 계속 나오고 있으니 pandas 로 막힌다면 다른 방법들을 찾아 스터디해 보는 것도 좋을 것 같다.

How to Solve HTTPConnectionPool Error In Python

· 2 min read
Alex Han
Software Engineer

image

개발을 하다 보면 많은 API를 다루게 된다.

많은 개발자들이 API를 통해 원하는 데이터를 쉽게 가져오고 이를 활용한다.

그리고 python을 이용한다면 간단하게 requests를 import 해서 사용할 것이다.

image

API가 기능을 대신해 줘서 사용하는 경우도 있지만 별다른 기능없이 가진 데이터 자체만 제공해주는 경우도 있다. 

예를 들어 영화 정보 제공이라든지, 날씨 정보 제공이라든지 이런 경우다.

근데 만약 그 API에서 가진 모든 영화정보를 요청해 이 데이터를 나만의 데이터베이스로 구축해 자산화하고 싶다면,

모든 영화의 각각의 상세정보를 불러올 수 있는 키값 목록을 가져오고 가져온 키값 목록을 이용해 loop를 돌며 아주 방대한 요청을 진행해야 할 것이다.

image

이 때, 공급하는 서버에 무리를 덜 주기 위해 from time import sleep으로 sleep(0.5) 이런 식으로 서버에게 부담을 덜 주는 방법도 있지만, 자주 발생하는 건 HTTPSConnectionPool 에러가 자주 발생한다. 이 에러의 내용을 살펴보면 Max retries exceeded with url 이런 식으로 뒤에 적혀 있어 내가 너무 자주 요청해서 날 블록한건가 싶은 생각이 들지만 그런 경우도 있고 아닌 경우도 있다.

더 뒤에 내용에 SSLError라는 말이 붙는다면 requests.get('<URL>', verify=False) 이런 식으로 verify하지 않도록 옵션을 준다면 해결이 된다! 🚀 (검색해 보니 근본적 해결을 위해 pyOpenSSL을 업데이트 해주면 된다고 해서 업데이트 해봤지만 별 소용이 없었고 이미 업데이트된 상태였다...😭)

그리고 만약 뒤에 refuse ... server 이런 늬앙스의 글귀가 보인다면 조금 기다렸다가 다시 요청해 보면 잘 될 확률이 높다.⭐️

(제공자가 블랙리스트화해서 요청을 막는 옵션을 두지 않았다면의 가정)

image

이렇게만 하면 모든 문제가 해결될 것 같다고 착각했지만 몇 십만번 요청하다 보니 상대 서버에서도 부담을 느낀 듯 하다.

어느 순간 병목현상처럼 요청을 했는데 서버에서 결과를 주지 않는다. 오류인지 성공인지도 답이 없고 몇 시간이 지나도 답이 없다...

이런 경우 계속 기다리면 영원히 기다려야 할 수 있다😱

requests.get('<URL>', verify=False, timeout=10) 이런 식으로 timeout을 줘서 API 용량이 어느정도 올거고 정상적인 상태에서 대기해 줄 만한 초를 정해 파라미터에 넣어주면 영원한 기다림에서 쿨한 기다림으로 바꿔준다.

image

이렇게 쿨한 마무리!

Introducing Flet - The Python Framework That Takes Inspiration from Flutter

· 4 min read
Alex Han
Software Engineer

image

At a time when mobile app development has become the norm, it's not surprising to see an increasing number of frameworks that make the process more efficient. One such framework is Flet, a Python-based framework that enables developers to build native iOS and Android apps using a single codebase. In this article, i will delve into the world of Flet and explore its origins, characteristics, and usage. I will also compare Flet with another popular framework, Flutter, to understand how they differ and where Flet shines.

What is Flet and why was it made first?

Flet was first introduced in 2019 by a group of Japanese developers looking to create a Python framework that took inspiration from Flutter's unique approach to building user interfaces. The framework quickly gained popularity within the Python community, and today, it boasts an active community of contributors and users.

The primary goal of Flet is to simplify the development process by enabling developers to write code once and deploy it on both iOS and Android platforms. Flet is built on top of popular Python frameworks such as Flask and Kivy, making it easy to integrate with other Python libraries.

The characteristics of Flet

At its core, Flet is designed to be simple, intuitive, and flexible. Here are some of the key characteristics of the framework:

  • Declarative Syntax: Like Flutter, Flet uses a declarative syntax that allows developers to describe the user interface in a simple and concise manner.
  • Hot Reload: Flet's hot reload feature allows developers to make changes to the code and see the results in real-time, without the need to restart the application.
  • Widgets: Flet uses a widget-based system, similar to Flutter, to build the user interface. Widgets are reusable building blocks that can be combined to create complex UI elements.
  • Material Design: Flet is built on top of Google's Material Design, making it easy for developers to create beautiful and consistent user interfaces.
  • Pythonic: Flet follows Pythonic principles, making it easy for Python developers to learn and use the framework.

Comparing Flet with Flutter

image2

  1. Language

The primary difference between Flet and Flutter is the programming language used. Flutter uses the Dart programming language, while Flet uses Python. For Python developers, Flet provides a more accessible option.

  1. Syntax

Flet's syntax is more intuitive and straightforward compared to Flutter. Python developers will find Flet's syntax more familiar, making it easier to learn and use.

  1. Customizability

Both Flet and Flutter are highly customizable, but Flet provides more flexibility due to Python's dynamic nature. Python developers can leverage their existing skills to create more complex and customized apps.

Examples of how to use Flet

Now that I've explored some of the characteristics of Flet, let's take a look at example of how to use the framework. I'll compare Flet to Flutter to highlight the similarities and differences between the two frameworks.

from flet import Text, TextField, Button

class LoginScreen:
def build(self):
return Column(
children=[
Text('Login'),
TextField('Username'),
TextField('Password'),
Button('Login')
]
)

And here's the equivalent code in Flutter

import 'package:flutter/material.dart';

class LoginScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Login'),
TextField(
decoration: InputDecoration(hintText: 'Username'),
),
TextField(
decoration: InputDecoration(hintText: 'Password'),
),
ElevatedButton(
onPressed: () {},
child: Text('Login'),
),
],
);
}
}

As you can see, the code in Flet is very similar to the code in Flutter. Both frameworks use widgets to build the user interface, and the code is easy to read and understand.

Flutter can also be used to build a app, but developers may need to spend more time learning the framework and the Dart language before they can start building.

Conclusion

if you are a Python developer and want to start building mobile applications, flet is an excellent choice. It provides a lot of useful features and widgets, as well as comprehensive documentation, making it easy for beginners to get started quickly. Additionally, flet's similarity to flutter makes it easy to switch between the two libraries, allowing developers to choose the one that best suits their needs.

ChatGPT Automate Task Using Python

· 2 min read
Alex Han
Software Engineer

배경

ChatGPT 에 대해 일론머스크가 ChatGPT 무섭다고 한 기사를 읽고 간단히 사용해 본 후기.

ChatGPT Automation

ChatGPT 란 무엇인가?

ChatGPT는 OpenAI가 개발한 프로토타입 대화형 인공지능 챗봇이다. GPT-3.5 언어모델 기반으로 만들어졌고 지도학습, 강화학습 모두 사용해 파인 튜닝되었습니다.(현재 기준이고 계속 발전될 걸로 보임.)

사용 예시

matplotlib

chatGPT 싸이트에 try chatgpt를 클릭해 바로 사용할 수 있습니다.(로그인을 해야 하므로 사전에 가입해야 함.) 로그인을 하고 나면 채팅창 같이 뜨는데 채팅 창에 plot a linear regression with Python using matplotlib 을 쳐 보았습니다.

chatgpt_matplotlib

import matplotlib.pyplot as plt

# create some fake data
x = [1, 2, 3, 4, 5]
y = [2, 3, 4, 5, 6]

# fit a linear regression model
slope, intercept = np.polyfit(x, y, 1)

# predict the y-values of a line with the fitted model
predictions = [slope * i + intercept for i in x]

# plot the data points and the fitted line
plt.scatter(x, y)
plt.plot(x, predictions)

plt.show()

위와 같이 개발을 위한 코드를 생성해 줍니다.

실제로 사용 가능한지 체크 해 보면 np 가 undefined 로 뜨지만 이 정도는 matplotlib, pandas, numpy를 써 봤다면 자주 보는 축약어 numpy의 np 임을 바로 유추할 수 있습니다. 그래서 numpy를 import해 실행해 보면 정상 동작함을 볼 수 있습니다.

matplotlib_codetest

send message

이번엔 whatsapp 으로 메시지를 보내도록 send a message on Whatsapp using Python and pywhatkit 쳐 봤습니다.

send_whatsapp

기존에 구글에서 검색해 스택오버플로우를 찾거나 공식 문서를 뒤적여 봐야 했지만 이제는 라이브러리를 어떻게 사용하는지까지 한줄 타이핑으로 알 수 있습니다.

scraping

이번엔 웹싸이트를 스크래핑을 위해 web scrape https://books.toscrape.com/ using Python and beautiful soup 를 쳐보자.(해당 명령은 잘 동작하지만 다른 웹 싸이트는 잘 동작하지 않을 수 있음. 사용 방법은 맞게 구현됨.)

webscrape_gpt

실제 코드에서 실행해 보면

webscrape

잘 동작합니다.

결론

무료로 배포되어 있기 때문에 구글 검색이 귀찮고 따분해진 사람들은 새로운 검색엔진 형태로 사용해 보는 것도 좋아 보입니다.(개인적으로는 마이크로소프트에서 만든 copilot 보다 나아 보임.) 인공지능의 자연어 처리 모델이 얼마나 발전한지 경험해 볼 수 있는 귀한 시간이었습니다.

How to Train and Optimize A Neural Network

· 5 min read
Alex Han
Software Engineer

배경

deep learning을 할 줄 아냐고 항상 부담이 된다. 이름부터 어려워 보이는 deep learning 에 대해 pytorch 라이브러리를 활용해 간단히 사용 방법을 알아보자.

Deep Learning

딥러닝은 머신 러닝의 하위 집합으로, 특히 사람의 뇌 구조와 기능에서 영감을 얻은, 알고리즘을 포함한 대량의 데이터를 다룹니다. 그래서 딥러닝 모델을 종종 심층 신경망이라고 부르는 것입니다.

Dataset

복잡하고 수 많은 작업이 필요한 데이터 전처리 과정들은 제외하기 위해 small iris dataset 활용.

Load Data with Data Loader

import warnings
warnings.filterwarnings("ignore")

import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset
from sklearn.model_selection import train_test_split

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from IPython import display
display.set_matplotlib_formats("svg")

iris = pd.read_csv("https://gist.githubusercontent.com/netj/8836201/raw/6f9306ad21398ea43cba4f7d537619d0e07d5ae3/iris.csv")
iris.head()

head() 메서드를 활용해 dataset의 컬럼과 실제값을 간단히 확인합니다.

iris_head

딥러닝을 통해 예측할 목표는 variety 컬럼이다. 다른 4개의 컬럼에 따라 이 컬럼의 값이 변경된다고 보면 됩니다. 수식으로 생각하면 4개의 컬럼을 X, 목표로 하는 예측값을 y로 해서 생각해 봅니다.

X = torch.tensor(iris.drop("variety", axis=1).values, dtype=torch.float)
y = torch.tensor(
[0 if vty == "Setosa" else 1 if vty == "Versicolor" else 2 for vty in iris["variety"]],
dtype=torch.long
)

print(X.shape, y.shape)

위의 코드와 같이 불러온 데이터에서 variety 컬럼을 제거한 것을 X, variety 만 불러온 것을 y로 합니다. 이 때 variety 컬럼의 값들 중 Setosa를 0, Versicolor를 1, 나머지를 2로 변환해 줍니다.

Train / Test split

이제 training할 데이터들과 실제 이를 검증할 test 데이터들을 나누어 줍니다. 미리 import 해 둔 from sklearn.model_selection import train_test_split 라이브러리를 사용하면 됩니다.

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, random_state=42)

train_data = TensorDataset(X_train, y_train)
test_data = TensorDataset(X_test, y_test)

train_loader = DataLoader(train_data, shuffle=True, batch_size=12)
test_loader = DataLoader(test_data, batch_size=len(test_data.tensors[0]))

print("Training data batches:")
for X, y in train_loader:
print(X.shape, y.shape)

print("\nTest data batches:")
for X, y in test_loader:
print(X.shape, y.shape)

Training

딥러닝을 위해 만들 모델은 input layer과 output layer를 연결하는 16개의 노드와 단일 hidden layer를 활용합니다.

class Net(nn.Module):
def __init__(self):
super().__init__()
self.input = nn.Linear(in_features=4, out_features=16)
self.hidden_1 = nn.Linear(in_features=16, out_features=16)
self.output = nn.Linear(in_features=16, out_features=3)

def forward(self, x):
x = F.relu(self.input(x))
x = F.relu(self.hidden_1(x))
return self.output(x)


model = Net()
print(model)

모델을 만들었으니 이제 training을 시작하는데 반복 동작하면서 오차를 줄여가는 과정을 수행하게 됩니다. Crossentropyloss를 사용해 오차를 추적하고 Adam으로 경사 하강 과정을 진행합니다.

num_epochs = 200
train_accuracies, test_accuracies = [], []

loss_function = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(params=model.parameters(), lr=0.01)

for epoch in range(num_epochs):
# Train set
for X, y in train_loader:
preds = model(X)
pred_labels = torch.argmax(preds, axis=1)
loss = loss_function(preds, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_accuracies.append(
100 * torch.mean((pred_labels == y).float()).item()
)

# Test set
X, y = next(iter(test_loader))
pred_labels = torch.argmax(model(X), axis=1)
test_accuracies.append(
100 * torch.mean((pred_labels == y).float()).item()
)

데이터가 작아 금방 완료되는데요. 반복 동작의 횟수 당 정확도가 어떻게 나왔었는지 시각화를 통해 보게 되면 아래와 같습니다.

fig = plt.figure(tight_layout=True)
gs = gridspec.GridSpec(nrows=2, ncols=1)

ax = fig.add_subplot(gs[0, 0])
ax.plot(train_accuracies)
ax.set_xlabel("Epoch")
ax.set_ylabel("Training accuracy")

ax = fig.add_subplot(gs[1, 0])
ax.plot(test_accuracies)
ax.set_xlabel("Epoch")
ax.set_ylabel("Test accuracy")

fig.align_labels()
plt.show()

visualize

Optimization

  1. 우선 트레이닝 과정을 아래와 같이 단순화합니다.
losses = []
def train_model(train_loader, test_loader, model, lr=0.01, num_epochs=200):
train_accuracies, test_accuracies = [], []
loss_function = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(params=model.parameters(), lr=lr)

for epoch in range(num_epochs):
for X, y in train_loader:
preds = model(X)
pred_labels = torch.argmax(preds, axis=1)
loss = loss_function(preds, y)
losses.append(loss.detach().numpy())
optimizer.zero_grad()
loss.backward()
optimizer.step()

train_accuracies.append(
100 * torch.mean((pred_labels == y).float()).item()
)

X, y = next(iter(test_loader))
pred_labels = torch.argmax(model(X), axis=1)
test_accuracies.append(
100 * torch.mean((pred_labels == y).float()).item()
)

return train_accuracies[-1], test_accuracies[-1]


train_model(train_loader, test_loader, Net())
  1. layer 개수를 조절할 수 있는 새로운 모델 클래스를 생성합니다.
class Net2(nn.Module):
def __init__(self, n_units, n_layers):
super().__init__()
self.n_layers = n_layers

self.layers = nn.ModuleDict()
self.layers["input"] = nn.Linear(in_features=4, out_features=n_units)

for i in range(self.n_layers):
self.layers[f"hidden_{i}"] = nn.Linear(in_features=n_units, out_features=n_units)

self.layers["output"] = nn.Linear(in_features=n_units, out_features=3)

def forward(self, x):
x = self.layers["input"](x)

for i in range(self.n_layers):
x = F.relu(self.layers[f"hidden_{i}"](x))

return self.layers["output"](x)
  1. layer를 1~4개까지 증가시켜 보고 각 layer가 8, 16, 24, 32, 40, 48, 56개 노드를 각각 가졌을 때 어떻게 변화하는지 지켜봅니다.
n_layers = np.arange(1, 5)
n_units = np.arange(8, 65, 8)
train_accuracies, test_accuracies = [], []

for i in range(len(n_units)):
for j in range(len(n_layers)):
model = Net2(n_units=n_units[i], n_layers=n_layers[j])
train_acc, test_acc = train_model(train_loader, test_loader, model)
train_accuracies.append({
"n_layers": n_layers[j],
"n_units": n_units[i],
"accuracy": train_acc
})
test_accuracies.append({
"n_layers": n_layers[j],
"n_units": n_units[i],
"accuracy": test_acc
})


train_accuracies = pd.DataFrame(train_accuracies).sort_values(by=["n_layers", "n_units"]).reset_index(drop=True)
test_accuracies = pd.DataFrame(test_accuracies).sort_values(by=["n_layers", "n_units"]).reset_index(drop=True)
test_accuracies.head()

accuracy

결과를 보면 테스트 정확도는 layer가 몇 개일 때 node가 몇 개일 때 어떤 양상을 보였는지 알 수 있고 test_accuracies[test_accuracies["accuracy"] == test_accuracies["accuracy"].max()] 를 통해 이 중 가장 정확도가 높은 layer, node 개수를 선정할 수 있습니다.

결론

iris dataset은 굉장히 적은 데이터이고 training이 굉장히 잘 되는 데이터이기에 이 과정이 무의미해 보이도록 정확도가 대부분이 정확도가 높게 나왔습니다.

하지만 실제 업무에서는 이처럼 깨끗하지 않고 깨끗하더라도 경향성이 일관되지 않아 그 수식을 찾아내기 힘든 경우가 대다수기 때문에 이와 같은 과정들이 필요합니다. layer, node 개수를 조정해 보는 것 외에도 변수들을 조절해 모델에 변화를 주거나 training 방식의 변화를 주어 optimization을 한다면 보다 의미있는 값들을 도출할 수 있을 거라 생각합니다.

이 글을 쓰면서 딥러닝에 좀 더 자신감을 가질 수 있으면 좋겠다 나도..;