PyPI 등록, 나만의 패키지 만들기

최더디·2021년 12월 11일
0
post-thumbnail

📌 서론

새로운 기능을 만들면서 setup.py를 사용해 PyPI에 등록을 처음으로 해봤다. 어떻게 PyPI에 패키지를 등록하는지 및 setup.py 사용법 복습을 한다 생각하고 새로운 기능을 만들어 보려고 한다.

📍hey insutance 명령어

간단하게 사용자가 $ hey insutance [options] 로 시작하는 명령어를 입력하면, 나에 대한 정보를 간략하게 알려주는 프로젝트를 진행하려고 한다.

📌 개발

📍github repo 생성

해당 기능을 바로 깃허브에 올리고 싶어 새로운 Repo, "hey-insutance"를 생성했다.
직접 작성하기 귀찮아 REAME.md, .gitignore 까지 추가했다.

TMI) 이때 새롭게 알게된 사실이 있다. github repo 이름을 자신의 아이디로 하면 아래와 같이 비밀을 찾았다는 문구가 나온다! 이를 통해 자신의 프로필을 만들 수 있다는 것 같다.

📍파일 구조

  • 파일 구조 (__init__.py 왜 넣었는지 궁금하면 해당 글 읽기)
hey-insutance
	├── README.md
	├── insutance
	│   ├── __init__.py
	│   ├── answer.py
	│   ├── command.py
	│   ├── main.py
	└── setup.py

📍argparse 를 사용해 커멘드 명령어 읽어오기

[참고] argparse document

# hey-insutance/insutnace/command.py
import argparse

class CommandParser:
  def __init__(self) -> None:
    self.parser = argparse.ArgumentParser(prog="hey")
    self.subparser = self.parser.add_subparsers(
        dest="my_name",
        help="write my name"
    )

  def get_args(self) -> argparse.Namespace:
    insutance_parser = self.subparser.add_parser(
        "insutance",
        help="what do you want to know about me?"
    )
    options_group = insutance_parser.add_mutually_exclusive_group(required=True)
    options_group.add_argument("--name", action="store_true", help=f"이름이 뭔가요?")
    options_group.add_argument("--age", action="store_true", help=f"몇 살이에요?")

    return self.parser.parse_args()

[subparser를 만든 이유]

$ hey insutance [options] 명령어에서 insutance 부분을 고정시키고 싶었기 때문에, subparser 를 생성했다. 물론 subparser 없이 만들고 인자로 받아 args.my_name == "insutance" 할 수 있었지만, 해당 분기문을 작성하고 싶지 않았기 때문에 subparser를 생성했다.

[options_group 생성 이유]

insutance_parser 에서 옵션을 그룹으로 만들어줬다. required=True 값을 줌으로써 아래 작성한 옵션 값들 중 1개라도 있어야 한다. 없다면 오류를 발생시킨다. 또한, 중복된 값이 들어온다면 오류를 발생시킨다.

[옵션이 잘 적용 되었는지 확인하는 방법]

$ python insutance/command.py insutance --help

📍질문에 답하는 봇 클래스 생성

$ hey insutnace --name 이런식으로 질문을 하면 답변을 출력해야 한다. 답변만을 간단하게 print문으로 출력하는 클래스가 있으면 좋을거라고 생각해 아래와 같이 AnswerBot 클래스를 생성했다.

# hey-insutance/insutance/answer.py
import time

class AnswerBot:
  def print_answer_slowly(self, answer) -> None:
    for answer in answer.split("\n"):
      print(answer.strip())
      if answer.strip() != "":
        time.sleep(1)

  def print_my_name(self) -> None:
    answer = """
      제 이름은 insutance 에요:)
      그 외에도 다른 닉네임으로도 활동하고 있어요!
      '최낙타', 'camellionchild'

      저의 본명은 알려드리지 않을거에요!
    """
    self.print_answer_slowly(answer)

  def print_my_old(self) -> None:
    answer = """
      제 나이가 궁금하시군요!
      몇 살처럼 보이나요 ?!

      제 나이는 ..!
      두구두구두구두구두구두구두구두구두구두구두구....

      비밀~!
      생일만 알려드릴게요!
      저의 생일은 11월 11일 빼빼로데이 입니다!      
    """
    self.print_answer_slowly(answer)

📍 main 문 작성

main문에서 모든 작업이 실행되도록 코드를 작성했다.
왜 main 문에서 실행되도록 했냐면, setup.py 를 만들때 더 편하다고 생각했고, main() 함수만 봐도 어떤 기능을 하려고 하는지 쉽게 이해가 되도록 하기 위해 작성했다.

# hey-insutance/insutance/main.py
from insutance.answer import AnswerBot
from insutance.command import CommandParser

def main():
  args = CommandParser().get_args()
  answerbot = AnswerBot()

  if args.name:
    answerbot.print_my_name()
  elif args.age:
    answerbot.print_my_old()

📍 setup.py 생성

기본으로 제공하는 setuptools 를 사용해 아래와 같이 작성한다. (빈 값으로 되어있는 부분은 삭제해도 된다.)

# hey-insutance/setup.py
from setuptools import setup, find_packages

setup(
    name="insutance",   # pypi 에 등록할 라이브러리 이름
    version="0.0.1",    # pypi 에 등록할 version (수정할 때마다 version up을 해줘야 함)
    description="Hey insutance! Who are you?",
    author="insutance",
    author_email="insutance@naver.com",
    url="https://github.com/insutance/hey-insutance",
    python_requires=">= 3.8",
    packages=find_packages(),
    install_requires=[],
    zip_safe=False,
    # 중요한 부분
    entry_points={
        "console_scripts": [
            "hey = insutance.main:main"
        ]
    },
    package_data={},
    include_package_data=True
)

여기서 중요한 부분은 entry_points 부분이다. $ hey insutance [options] 와 같이 $ hey 로 시작을 해야한다. 위에 "[옵션이 잘 적용 되었는지 확인하는 방법]" 부분에서 python 명령어로 실행하던 방법을 수정할 수 있다.

entry_points={
    "console_scripts": [
        # hey라는 명령어를 입력하면 insutance/main 에 있는 main() 함수를 실행시켜줘! 
        "hey = insutance.main:main"
    ]
}
# before
$ python insutance/command.py insutance --help

# after, entry_points 설정 후
$ hey insutance --help

[테스트 방법]

setup.py 파일까지 작성을 완료했고, $ hey 명령어를 사용해 테스트를 하고 싶다면 아래 명령어를 입력해 pip 설치를 해줘야 한다.

$ python setup.py install

해당 명령어를 실행하면 build, dist, insutance.egg-info 폴더가 생길 것이다. 또한 $ pip list 를 실행시켜보면 setup.py 에서 name 부분에 작성한 값이 version 부분에 작성한 값으로 출력되는 것을 볼 수 있다.

그리고 명령어를 입력하면, 아래와 같이 자신이 설정한 옵션들이 나올 것이다.

📌 pypi 등록

📍회원가입

아이디 비밀번호가 없다면, https://pypi.org/account/register/ 해당 사이트에서 회원가입을 할 수 있다.

📍패키지 이름 중복 확인

https://pypi.org/ 해당 사이트에서 패키지 이름이 중복 되는지 확인한다. (패키지 이름이란, setup.py 에서 name 값이다.)

Tip) 검색을 했을 때 안 나올수도 있다. 확실하게 없는지 확인하는 방법은 URL로 확인하면 된다. https://pypi.org/project/{package_name}/ 으로 검색해서 나오지 않는다면 정말로 없는 것이다.

📍 필요한 패키지 설치 및 빌드

회원가입도 했고, 패키지 이름도 중복되는게 없다면 이제 pypi 에 등록을 해보자.

우선 pypi 등록에 필요한 패키지를 설치한다.

  • setup.py 파일에서 사용한 setuptools
  • 빌드시에 사용할 wheel
  • 배포시에 사용한 twine
# setuptools는 기본으로 내장되어 있음. 없다면 설치
$ pip install setuptools
$ pip install wheel
$ pip install twine

앞에서 작성한 setup.py 를 통해 패키지 빌드를 시작해보자.

# setup.py 파일이 있는 경로에서 아래 명령어 실행.
$ python setup.py bdist_wheel

📍등록

위의 명령어를 입력하고 나면 아래 폴더들이 생성되었는지 확인한다.

  • build
  • {packagename}.egg-info
  • dist

생성이 되었다면 dist 폴더 내부에 있는 파일명을 복사한다. (확장자 포함, 아마 .whl 로 생성될 것이다.) 파일명을 복사한 후, 아래 명령어 실행한다.

$ twine upload dist/{확장자를 포함한 파일 이름}

# example
$ twine upload dist/insutance-0.0.1-py3-none-any.whl

명령어 입력하면 자신의 username 과 password를 입력하라고 나오는데, 이때 pypi 에서 회원가입했던 username 과 password를 입력하면 된다.

[pypi 등록 완료 화면]

📍패키지 확인

위에 작성한 부분까지 완료했다면, pypi에 가서 검색을 해보면 자신이 만든 패키지가 등록된 것을 볼 수 있다. (시간이 조금 걸릴 수도 있음)

profile
focus on why

0개의 댓글