조유성
Software Engineer Server Developer Frontend Developer UI Developer Full Stack Developer Developer OSS Lover 철학 전공자 구조주의자 직장인 ù̴̲̭̼n̴̡͔͍̏d̶̛͇̖̻̅̕e̴̬͇͖̊f̸̢͈͂͒ǐ̶̺̳ͅn̴̝̣̹͂͌e̸̟̯̒d̵̢̉ͅ

Python 코드를 정적으로(statically) 문서화 하기

mkdocs-yaarg-plugin을 만든 이유

Docstring

Python에서는 언어 차원에서 문서화를 위해 docstring이라는 기능을 제공합니다.

기본적으로 그 내용은 PEP-257에서 명세하는 규칙에 따라 주석을 작성하면, 해당 주석이 달린 객체의 __doc__ attribute로 주석의 내용을 읽을 수 있도록 하는 것입니다.

def hello():
    """
    Print greeting message in console
    """
    print("Hello World")


print(hello.__doc__)  # Print greeting message in console

경험적으로 이 기능은 REPL이나 IPython, Jupyter Notebook 등과 같이 인터프리터에 직접 명령을 입력하는 상황에서 유용한 것 같습니다. IDE 지원 없이도, 라이브러리의 사용법을 쉽게 알 수 있으니까요.

나쁜 아이디어

그러나 __doc__ attribute를 기반으로 API 문서를 생성하는 건 좋은 생각이 아닌 것 같습니다. __doc__ attribute에 접근하기 위해서는 우선 docstring이 포함된 코드를 실행(evaluate)해야 하기 때문입니다.

독립적으로 실행되지 않는 NumPyPandas 같은 라이브러리에서는 괜찮을지 모르겠지만, 런타임에 다양한 의존성이 주입되고 초기화 동작이 실행되는 서버 어플리케이션에서는 위와 같은 문제점들에 부딪힐 수밖에 없습니다.

안타깝게도, Python 생태계에서는 런타임에서 __doc__ attribute를 사용하는 방식이 널리 쓰이고 있습니다. 가장 대표적인 sphinx-apidoc, mkdocstrings 두 라이브러리가 모두 그렇습니다.

다른 아이디어

다행히 다른 접근 방식을 택한 라이브러리도 있습니다. Pydoc-Markdownlib2to3를 사용해서 코드를 정적으로(statically) 분석하고 문서를 생성합니다.

아쉬운 점이 있다면, lib2to3는 그 목적 상 deprecate가 예정되어 있다는 점입니다.

lib2to3 - 2to3’s library

Deprecated since version 3.10: Python 3.9 will switch to a PEG parser (see PEP 617), and Python 3.10 may include new language syntax that is not parsable by lib2to3’s LL(1) parser. The lib2to3 module may be removed from the standard library in a future Python version. Consider third-party alternatives such as LibCST or parso.

사실 Pydoc-Markdown에서 개인적으로 아쉬웠던 점은 한 가지 더 있습니다. MkDocs, Hugo, Docusaurus와의 연동을 지원하긴 하지만, 기본적으로 각 문서화 도구의 설정을 Pydoc-Markdown가 생성하고 필요한 동작을 대신 수행하는 구조를 갖고 있습니다. 이러한 설계는 (지향하는 철학의 차이라고 생각하긴 하지만) 저는 개인적으로 선호하지 않습니다.

사용하려고 하는 문서화 도구의 생태계를 온전히 활용하기도 쉽지 않고, 문서화 도구의 인터페이스가 변경되면 사용이 어려워질 수도 있다는 점에서 future-proof가 되지 않기 때문입니다. Python 외의 다른 언어로 작성된 코드의 문서를 함께 생성하기도 쉽지 않습니다.

대안

그래서 저는 새로운 라이브러리를 만들기로 했습니다. mkdocs-yaarg-plugin을 소개합니다.

yaarg는 아래와 같은 특징과 장점이 있습니다.

아직은 MVP 수준의 기능밖에 없지만, 지금 일하고 있는 팀의 상당히 큰 코드베이스에 적용해서 큰 문제 없이 사용하고 있습니다.


  1. 저는 JavaDoc, JSDoc, Godoc 등을 사용해보았지만, 어느 것도 코드를 런타임에서 실행하지는 않았습니다. 

  2. 예를 들어서, 코드가 Django의 기능을 사용하는 경우 django.setup()을 호출해야 오류 없이 실행할 수 있습니다. 

  3. 특히 현존하는 대부분 구현이 subprocess를 생성하는 방식이라서 더 그렇습니다.