지난 번의 이슈를 가져와 다시 올려본다... 

 

세상에. 소마 발표가 끝나고 나니 이 이슈 업데이트가 끊긴 지가 거의 한달이 다 되어간다. 실화인가 싶다. 생각보다 금방 해결될 수 있는 이슈겠지 싶었는데, 이것도 나의 지속적인 노력이 있어야 가능한 일임을 깨닫고 반성해 본다. 

 

진행 상황을 잠깐 리뷰해보자면, 같은 팀 Djangonaut인 Tai가 코드를 고칠 방향을 제안해 주었고, 내가 OK라고 했다가 그걸 반영 못한 지 약 2주가 넘게 지난 상황이다... 정말 죄책감이 느껴지는데, 죄책감을 더 느끼지 말고 어서 이 이슈를 다시 잡아 보자. 

 

우선 이슈 자체는 이미 이해한 상황이고, 테스트도 통과하는 상황이다. 그런데 정말 잘 고쳐서 테스트를 통과하는 것인지는 확신할 수가 없어서 navigator Mariusz에게 조언을 구하니, 아마도 테스트가 커버하지 못하는 부분이 있을 거라고 했다. 결론은 직접 django 프로젝트를 하나 만들어서, 그 안에서 직접 dbshell 명령어를 실행시켜서 제대로 동작하는지를 확인해봐야 했다. 

 

그런데 순간 의문이 들었다. 프로젝트를 만든다 쳐도, 지금 나는 python 3.14 버전의 환경에서 해당 소스 코드를 좀 바꾼 django 버전을 테스트 하고 싶은 것인데, 이걸 어떻게 하지?

 

GPT 피셜, 다음 명령어를 사용하면 django의 상태를 현 수정 중인 브랜치의 디렉토리에 맞게 적용할 수 있다고 했다. 일단 믿어보고 안 되면 다른 방법을 찾아보자. 

pip install -e .

 

그리고 django-admin 명령어로 (django는 현재 install -U 명령어를 통해 전역 설치되어 있는 상황이었다) test_project라는 테스트용 django 프로젝트를 만들어 주었다. 그리고 테스트용 앱도 만들어 주었다. 

 

그리고 python manage.py migrate 명령어까지 실행해 주었으니 migration도 잘 실행되었을 것이다. 이제 공식 문서에 있는 dbshell 명령어 부분을 참고해서, sqlite에서 유저를 조회하는 쿼리를 dbshell 명령어로 실행해 보자. 

DJANGO_SETTINGS_MODULE=test_project.settings django-admin dbshell -- 'select * from user'

 

그런데 이런 오류가 떴다. 

 

현재 있는 test_project 디렉토리 안에 또 test_project 디렉토리가 있는 게 맞고, 여기 안에 settings 파일이 있는 것도 맞는데 경로 인식에서 오류가 난 것 같다. 

 

✅ 비고

오늘 팀원들과 다 같이 멘토님을 온라인으로 뵈었다. 온라인 멘토링을 하면서 느낀 점을 간략히 적어본다. 


1. '내가 어떤 회사를 가고 싶은지'는 생각해 봤어도, '내가 뭘 할 수 있는 회사에 가고 싶은지', 또는 '그 회사에 가서 뭘 하고 싶은지'를 구체적으로 생각해 본 적이 없다. 새삼 사고의 전환과 함께 머리를 띵 하고 맞은 기분이었다. 그래도 막연히 그냥 좋은 회사가 아니라 '어떤 회사를 가고 싶은지'를 생각해 본 것은 꽤 잘한 것 같다. 

2. 사이드 프로젝트는 결국 사이드여야 한다. 내가 일할 수 있는 곳에서 더 많은 것을 얻고 배울 수 있고, 그걸 최대한 뽑아내야 한다. 

3. 그래서, 내가 바라는 건 뭔가? 여러 사회적인 조건을 적절히 고려한다면, 나는 어떤 환경에서 뭘 하면서 살고 싶은가?

 

이번에 처리 중인 이슈는 Python 3.14에서 parser의 메소드 중 하나인 'add_argument_group'에 사용되는 prefix_chars 옵션이 deprecated된 것과 관련이 있다. 우리 팀의 navigator 분이 관련 이슈를 보내주셨다. 

 

해당 이슈는 Python의 CPython 레포의 이슈로, 해당 prefix_chars를 deprecate 시키는 커밋이 들어있었다. 

 

처음에는 그래서 어떻게 하면 된다는 것인가 싶어서 모호했는데, 위의 코드를 보니 '_ArgumentGroup'(아마도 add_argument_group의 클래스를 말하는 것 같았다)이 초기화될 때 'prefix_chars'라는 옵션을 사용하면 warning을 제시하도록 하는 코드라고 이해했다. 

 

해당 prefix_chars 옵션은 Python 3.14부터 deprecated 되고, Python 3.16부터 아예 사용이 금지된다. 

 

즉 이러한 상황에서 django에서 사용하는 'dbshell'이라는 커맨드에서 이 add_argument_group 메소드에 대해 prefix_chars를 사용하는 코드가 있다는 것이 문제 상황이다. 

 

dbshell은 처음 들어보는 명령어였는데, 공식문서에서 찾을 수 있었다. 이 명령어는 settings.py의 DATABASE 값으로 설정된 값에 대해서 DB client를 실행시키는 것이라고 이해했다. 

 

django.core.management.commands 파일에는 django-admin 커맨드로 사용되는 모든 커맨드들이 파이썬 파일로 모여 있다. 이 중 dbshell.py 파일의 코드에서 'prefix_char' 옵션이 사용되는 것을 볼 수 있었다. 

# dbshell.py
import subprocess

from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS, connections


class Command(BaseCommand):
    help = (
        "Runs the command-line client for specified database, or the "
        "default database if none is provided."
    )

    requires_system_checks = []

    def add_arguments(self, parser):
        parser.add_argument(
            "--database",
            default=DEFAULT_DB_ALIAS,
            choices=tuple(connections),
            help=(
                "Nominates a database onto which to open a shell. Defaults to the "
                '"default" database.'
            ),
        )
        parameters = parser.add_argument_group("parameters", prefix_chars="--")
        parameters.add_argument("parameters", nargs="*")

 

아래에서 두 번째 줄에서 add_argument_group이 prefix_chars를 옵션으로 받고 있었다. 해당 줄을 지우고, 대신 다음과 같은 코드로 대체해 보았다. 결국엔 기존 코드에서 'prefix_chars' 옵션만 뺀 코드였다. 

parameters = parser.add_argument_group("parameters")
parameters.add_argument("parameters", nargs="*")

 

그리고 테스트를 돌렸는데(관련된 dbshell 테스트만 돌렸다), 테스트가 다 통과하는 게 아닌가.

cd tests
python -Wall ./runtests.py dbshell

 

prefix_chars 옵션이 없이도 다른 arguments들이 적재적소에 잘 들어간 것인지가 불분명했다. 그래서 일단은 이 찜찜한 상황에서 코드를 멈춰두고, 팀의 navigator 분께 다음과 같이 조언을 구하기로 했다.

  1. 이렇게 코드를 바꿔 보았고 테스트가 잘 되는데 여기서 어떻게 하면 이슈가 일단 우리가 할 수 있는 만큼은 핸들링 되었는지를 알 수 있을까?
  2. pair programming을 더 잘 하는 팁이 있을까?

 

오픈 소스에 첫 PR을 남긴 기념으로 이를 기록하고자 한다. 나는 #34900번 티켓을 작업했다. 정확히는 #34900번 티켓을 참조하는 티켓을 작업했다. 무슨 말이냐면, #34900번 티켓의 주제는 'django와 python 3.13 버전의 호환성'이다. 호환성이 맞지 않는 경우는 무수히 많고 한 번에 해결되는 것이 아니니 #34900번 티켓과 관련된 이슈는 계속 나올 수밖에 없다. 

 

티켓 작업 순서는 처음이라 헷갈렸었는데, 앞으로 헷갈리지 않도록 정리해 보려고 한다. 

 

우선 django 공식 레포에 대해서 forked repository를 만들어 준다. django는 오픈 소스 레포지토리이기 때문에 일반 사용자들은 직접 브랜치를 만들거나 레포에 직접 push를 날릴 권한이 없다. 이럴 때는 git fork를 사용한다. 반면 특정 레포지토리에서 협업 프로젝트를 하고 있고, 해당 레포에 대해 브랜치를 만들거나 직접 로컬에서 변경 사항을 push할 권한이 있다면 git clone을 사용하는 것으로 알고 있다. 

 

즉 git fork는 원본을 그대로 복사해서 만든, 하지만 원본과 엄연히 다른 복사된 레포지토리를 만든다. 반면 원본을 그대로 로컬에 가져오는 것은 git clone이다. 마치 shallow copy와 deep copy와 유사하다는 생각도 든다. 

 

어쨌든 git fork를 만들어 주고, 해당 fork된 레포에서 또 브랜치를 따로 판다. 다른 사람들의 PR들을 봤을 때 브랜치 이름에 대한 특별한 규칙은 없는 것 같다. 

 

오늘 작업한 이슈는 deprecated warning과 관련된 이슈였다. django 레포에는 tests라는 디렉토리가 있고, 해당 디렉토리 하위에는 무수한 test 패키지들이 있다. 다 돌려보면 실제로 제법 시간이 많이 든다. 이 중에서 pyenv를 통해 python 3.14(아직 정식 릴리즈 버전은 아니다)를 사용하는 가상환경을 실행하고, 이 환경에서 cache 테스트를 돌려보면 다음과 같이 deprecation warning이 뜨는 것을 볼 수 있다. 

pyenv install 3.14-dev
pyenv virtualenv 3.14-dev django-3.14
pyenv activate django-3.14
python -Wall ./runtests.py cache

 

즉 python 3.13, python 3.14 버전에서 해당 함수가 deprecated 되었음을 알 수 있다. 

 

glob 모듈에서 glob1() 메소드를 사용하는 부분이 있는데, 해당 glob1 메소드는 deprecated 처리 되었고 이후 python 3.15 버전부터는 사용이 불가능하기 때문에 경고 메시지가 뜨는 것이었다. 

 

전체 검색으로 glob1()의 사용처를 찾아보니 딱 한 군데였다. 물론 이 오류를 내가 처음부터 바로 딱 찾아낸 것은 아니고, djangonaut의 navigator님의 도움을 받았다. 해당 glob1() 메소드의 호출부를 glob()으로 변환시켜 주면 되는 문제였다. 기존 코드는 다음과 같았다. 

 

그렇다고 단순히 함수 이름만 바꿔서는 안 되겠다. glob1()은 2개의 인자를 받는 반면, glob()은 1개의 인자만을 받고 있었다. 찾아보니 glob()와 glob1()의 역할은 거의 비슷했다. 두 메소드 모두 특정 디렉토리의 모든 하위 디렉토리에서 특정 패턴을 가진 파일들을 찾아주는 역할을 했다. 그리고 두 함수 모두 리스트를 리턴했다. 

 

glob1(a, b)의 경우 a라는 디렉토리의 하위 디렉토리에서 b라는 패턴으로 시작하는 파일들(문자열들)을 찾아냈다. 반면 glob(a)의 경우 인자를 하나만 받는데, 이때 인자 a는 디렉토리와 패턴을 모두 포함하는 문자열이다. 

 

그래서 위의 함수와 같은 역할을 하도록 glob1() 대신 glob()을 사용하여 함수를 고쳐주면 이런 식으로 바꿀 수 있다. 

 

이렇게 바꾸고, 반드시 테스트를 돌려서 위에서 나던 deprecation warning이 사라졌는지를 확인해야 하겠다. 

 

warning이 사라진 것을 볼 수 있다! 이제 forked된 브랜치에 git push를 하고, 해당 forked된 레포에서 django 레포로 PR을 올려보았다. 

 

code patch(수정한 코드 커밋들)도 무사히 잘 반영되었다!! 첫 PR을 무사히 마쳤으니, 앞으로 다른 이슈도 열심히 작업해봐야겠다ㅎㅎ

 

1주차 워크북을 보았다. 워크북에서는 해당 주차의 목표가 무엇인지 스스로 점검하고, 해당 주차의 목표를 지키기 위해서 보면 좋을 learning material들을 추천해 주고 있었다. 

 

해당 learning material 중에서 Contributing to Django라는 문서가 있길래 클릭해 보았다. 해당 문서에서는 django에 어떻게 기여할 수 있을지를 알려주고 있었다. 그리고 나는 코드 패치를 쓰는 것만이 장고에 기여하는 것이라고 생각했는데, 그 외에도 문서화를 하는 등의 다양한 기여 방법이 있었다. 

 

나의 우선적인 목표는 코드로 기여를 하는 것이었기에, 관련된 문서를 눌러서 해당 프로그램과 관련된 튜토리얼을 진행하였다. 

 

중간에 'pip install pylibmc' 부분에서 에러가 났다. GPT 찬스를 써서 'brew install libmemcached' 명령어를 실행해도 해결되지 않았는데, 다행히 중간에 발견한 어떤 글을 보고 따라해 보았다. 그러다가도 에러가 계속되었는데, 원인상 'brew install libmemcached' 명령어로 설치한 libmemcached 패키지의 경로를 찾을 수 없어서, 즉 해당 패키지가 지정된 경로에 있지 않거나 설치되어 있지 않아서 발생하는 에러 같았다. 

 

여러 블로그들을 탐방한 결과 django open forum에도 해당 이슈가 올라와 있었다. 해당 글을 읽고 커맨드를 따라해 보면서 오류를 해결할 수 있었다. 

LIBMEMCACHED=/opt/homebrew pip install pylibmc

 

테스트에 필요한 패키지들을 설치하는 것 까지는 완료되었다. 그런데 다음 명령어를 실행하니 아래와 같은 오류가 발생하는 게 아닌가. 

python3 runtests.py

 

이런 에러가 왜 나지 싶어서 소스 코드를 보았더니 의심가는 지점이 있었다. 실행한 runtests.py 파일의 46-48번째 라인이다. 일부러 이런 오류가 나도록 설정해 놓은 걸까? 

 

추측해 보기로는 Django 6.0 버전 전까지 나는 에러 같기도 하다. DeprecationWarning은 이제 곧 제거될 예정인 기능을 사용할 때 나는 경고인 것으로 안다. 그런데 그렇다면 RemovedInDjango60Warning은 무엇일까? 60이 6.0을 말하는 것인지, 60번째 버전을 나타내는 것인지 잘 모르겠어서 모호했다. 그런데 6.0 버전은 한참 남았으니 60번째 버전이라는 해석도 가능할 것 같다. 

 

어쨌든 위의 에러가 왜 나는지는 다시 생각해 보니 알 것 같았다. 현재 python 3.12(내가 사용하는 버전)에는 RemovedInDjango50Warning은 있지만 RemovedInDjango60Warning은 없어서 나는 문제였다. 왜냐하면 지금 실행시킨 코드는 정식 출시된 버전이 아니라 개발 버전의 코드이기 때문에 그런 것일 수도 있겠다. 

 

그렇다면 어떻게 해야 할까? 일단 이 에러를 무시하는 것이 맞을까? 아니면 파이썬의 최신 버전(3.13)을 설치하면 에러가 해결되려는지는 잘 모르겠다. 우선은 보류해 보자.


Djangonaut의 첫 weekly meeting을 마쳤다! 

영어 회의는 처음이라 자기소개할 때 진땀을 뺐지만 영어를 잘하지 못합니다... sorry! 하고 잘 넘어갔다. 회의의 분위기는 kind, charming, welcoming...했다. 뉴비를 환영하는 분위기였다. 

 

질문 시간에는 '만약 티켓을 할당받았는데 그 티켓에 대한 배경지식이 없어서 잘 처리하지 못하면 어떻게 하죠' 라고 질문했는데, 매우 자연스러운 일이며 그럴 땐 도움을 요청하면 된다고 했다. 공식적으로는 django forum에 discussion을 올리고, 만약 공식적으로 밝히기 좀 망설여진다면 디스코드에 밝혀도 된다고 했다. 

 

그리고 navigator와 captain의 역할이 나눠져 있는 점도 신기하면서 좋았다. navigator는 technical helper, supporter 같은 역할을 하고, captain은 emotional, mental helper, cheerleader 같은 역할을 한다고 하셨다. 

 

그리고 티켓에 대해서 공식적인 deadline은 없다고 한다. 왜냐면 티켓별로 그 범위가 천차만별이므로... '기간 내에 못 끝내면 어떡하지?' 라는 걱정은 안 해도 된다고 해주셨다. 

 

미팅은 다음 주부터 항상 같은 시간에 매주 진행하고, captain 분과는 격주로 1:1 미팅을 진행한다. 새삼 djangonaut들에게 신경을 많이 써주시는 것 같아서 마음이 따듯해졌고 훈훈한 시간이면서도, 이 활동을 잘 경험하고 기록해서 앞으로도 이어갈 수 있게끔 하고 싶었다. 

 

+ Recent posts