PEP 258 – Docutils 설계 명세
- Author:
- David Goodger <goodger at python.org>
- Discussions-To:
- Doc-SIG list
- Status:
- Rejected
- Type:
- Standards Track
- Requires:
- 256, 257
- Created:
- 31-May-2001
- Post-History:
- 13-Jun-2001
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
거부 공지
이 문서는 이제 독립 프로젝트가 된 docutils의 흥미로운 설계 문서로 활용될 수는 있지만, 더 이상 표준 라이브러리에 포함될 예정은 없습니다.
초록
이 PEP는 Python 독스트링 처리 시스템(Docstring Processing System, DPS)인 Docutils의 설계 문제와 구현 세부 사항을 기술합니다. DPS의 근거와 고수준 개념은 PEP 256 “Docstring Processing System Framework”(독스트링 처리 시스템 프레임워크)에 문서화되어 있습니다. “독스트링 PEP 로드맵(Road Map to the Docstring PEPs)”은 PEP 256도 참고하십시오.
Docutils는 어떤 구성 요소든 쉽게 교체할 수 있도록 모듈식으로 설계되고 있습니다. 또한 Docutils는 Python 독스트링 처리에만 국한되지 않으며, 여러 맥락에서 독립된 문서도 처리합니다.
이 PEP는 Python 언어의 핵심 부분에 대한 변경을 요구하지 않습니다. 이 PEP의 결과물은 표준 라이브러리용 패키지와 그 문서로 구성됩니다.
명세
Docutils 프로젝트 모델
프로젝트 구성 요소와 데이터 흐름:
+---------------------------+
| Docutils: |
| docutils.core.Publisher, |
| docutils.core.publish_*() |
+---------------------------+
/ | \
/ | \
1,3,5 / 6 | \ 7
+--------+ +-------------+ +--------+
| READER | ----> | TRANSFORMER | ====> | WRITER |
+--------+ +-------------+ +--------+
/ \\ |
/ \\ |
2 / 4 \\ 8 |
+-------+ +--------+ +--------+
| INPUT | | PARSER | | OUTPUT |
+-------+ +--------+ +--------+
각 구성 요소 위의 숫자는 문서의 데이터가 거치는 경로를 나타냅니다. Reader와 Parser 사이, 그리고 Transformer와 Writer 사이의 두 줄 선은 이 경로로 전달되는 데이터가 표준(순수하고 확장되지 않은) Docutils 문서 트리여야 함을 나타냅니다. 한 줄 선은 내부 트리 확장이나 완전히 무관한 표현도 가능하지만, 양쪽 끝에서 모두 이를 지원해야 함을 뜻합니다.
Publisher
docutils.core 모듈에는 “Publisher” 퍼사드 클래스와 여러 편의 함수가 들어 있습니다. 편의 함수로는 “publish_cmdline()”(명령줄 프런트엔드용), “publish_file()”(파일류 I/O를 사용하는 프로그램용), “publish_string()”(문자열 I/O를 사용하는 프로그램용)이 있습니다. Publisher 클래스는 Docutils 시스템의 고수준 로직을 캡슐화합니다. Publisher 클래스는 처리 전반을 책임지며, 이 처리는 Publisher.publish() 메서드가 제어합니다:
- 내부 설정(설정 파일과 명령줄 옵션을 포함할 수 있습니다)과 I/O 객체를 준비합니다.
- Reader 객체를 호출하여 소스 Input 객체에서 데이터를 읽고, Parser 객체로 그 데이터를 파싱합니다. 문서 객체가 반환됩니다.
- 문서에 연결된 Transformer 객체를 통해 변환을 준비하고 적용합니다.
- Writer 객체를 호출합니다. Writer 객체는 문서를 최종 출력 형식으로 변환하고, 형식이 적용된 데이터를 대상 Output 객체에 씁니다. Output 객체에 따라서는 출력이 Writer에서 반환된 다음
publish()메서드에서 반환될 수도 있습니다.
구성 요소 이름을 지정하여 “publish” 함수를 호출하면(또는 “Publisher” 객체를 생성하면) 기본 동작을 따릅니다. 사용자 정의 동작(구성 요소 설정 사용자 정의)을 원한다면 먼저 사용자 정의 구성 요소 객체를 만든 다음, 그 객체들을 Publisher나 publish_* 편의 함수에 전달하십시오.
Reader
Reader는 입력 맥락(데이터가 어디에서 오는지)을 파악하고, 입력 전체나 개별 “청크”를 파서에 보내며, 청크들을 다시 하나의 일관된 전체로 묶는 데 필요한 맥락을 제공합니다.
각 리더는 “read” 메서드를 가진 “Reader” 클래스를 내보내는 모듈 또는 패키지입니다. 베이스 “Reader” 클래스는 docutils/readers/__init__.py 모듈에 있습니다.
대부분의 리더에는 어떤 파서를 사용할지 지정해 주어야 합니다. 지금까지는(아래 예시 목록 참조) Python 소스 리더(“PySource”; 아직 미완성)만이 스스로 파서를 결정할 수 있습니다.
책임:
- 소스 I/O에서 입력 텍스트를 가져옵니다.
- 입력 텍스트를 새 Document Tree 루트와 함께 파서에 전달합니다.
예시:
- 독립형(Raw/Plain): 텍스트 파일을 읽어서 처리하기만 합니다. 리더에 어떤 파서를 사용할지 지정해 주어야 합니다.
“독립형 리더(Standalone Reader)”는
docutils.readers.standalone모듈에 구현되어 있습니다. - Python 소스: 아래의 Python Source Reader를 참조하십시오. 이 리더는 현재 Docutils 샌드박스에서 개발 중입니다.
- 이메일: RFC 822 헤더, 인용된 발췌문, 서명, MIME 파트.
- PEP: RFC 822 헤더, “PEP xxxx”와 “RFC xxxx”를 URI로 변환. “PEP 리더”는
docutils.readers.pep모듈에 구현되어 있습니다. PEP 287과 PEP 12를 참조하십시오. - 위키: 변환(transform)에 통합된 “위키 링크”의 전역 참조 조회. (CamelCase만 허용할 것인지, 제한 없이 허용할 것인지?) 느슨한 들여쓰기?
- 웹 페이지: 독립형과 같지만, 메타 필드를 메타 태그로 인식합니다. 어떤 형태로든 템플릿을 지원할 것인지? (
<body>뒤,</body>앞?) - FAQ: 구조화된 “질문과 답변” 구성.
- 복합 문서: 여러 장(chapter)을 하나의 책으로 병합합니다. 마스터 매니페스트 파일?
파서
파서는 입력을 분석하여 Docutils Document Tree를 생성합니다. 파서는 데이터의 출처나 목적지에 대해 전혀 알지 못하며 신경 쓰지도 않습니다.
각 입력 파서는 “parse” 메서드를 가진 “Parser” 클래스를 내보내는 모듈 또는 패키지입니다. 베이스 “Parser” 클래스는 docutils/parsers/__init__.py 모듈에 있습니다.
책임: 원시 입력 텍스트와 doctree 루트 노드가 주어지면, 입력 텍스트를 파싱하여 doctree를 채웁니다.
예시: 지금까지 구현된 유일한 파서는 reStructuredText 마크업용 파서입니다. 이 파서는 docutils/parsers/rst/ 패키지에 구현되어 있습니다.
다른 파서를 개발하고 통합하는 것도 가능하며 권장됩니다.
트랜스포머
docutils/transforms/__init__.py에 있는 Transformer 클래스는 변환(transform)을 저장하고 이를 문서에 적용합니다. 새 문서 트리마다 transformer 객체가 하나씩 연결됩니다. Publisher는 Transformer.apply_transforms()를 호출하여 저장된 모든 변환을 문서 트리에 적용합니다. 변환은 문서 트리를 한 형태에서 다른 형태로 바꾸거나, 트리에 내용을 추가하거나, 트리의 일부를 잘라 냅니다. 변환은 참조와 각주 번호를 해석하고, 해석 텍스트(interpreted text)를 처리하며, 그 밖에 문맥에 따라 달라지는 처리를 수행합니다.
일부 변환은 특정 컴포넌트(Reader, Parser, Writer, Input, Output)에 한정됩니다. 컴포넌트별 표준 변환은 컴포넌트 클래스의 default_transforms 속성에 지정됩니다. Reader가 처리를 마치면 Publisher는 컴포넌트 목록을 인자로 Transformer.populate_from_components()를 호출하고, 이때 모든 기본 변환이 저장됩니다.
각 변환은 docutils/transforms/ 패키지 안의 모듈에 있는 클래스이며, docutils.transforms.Transform의 서브클래스입니다. 각 변환 클래스에는 default_priority 속성이 있으며, Transformer는 이 속성을 사용하여 변환을 순서대로(낮은 값에서 높은 값으로) 적용합니다. 기본 우선순위는 Transformer 객체에 변환을 추가할 때 재정의할 수 있습니다.
Transformer의 책임:
- 우선순위 순서에 따라 문서 트리에 변환을 적용합니다.
- 컴포넌트 유형 이름(‘reader’, ‘writer’ 등)과 컴포넌트 객체 사이의 매핑을 저장합니다. 이 매핑은 특정 변환(“components.Filter” 등)이 적합성을 판단하는 데 사용합니다.
Transform의 책임:
- doctree를 제자리에서 수정합니다. 순수하게 한 구조를 다른 구조로 변환하거나, doctree 및/또는 외부 데이터를 바탕으로 새 구조를 추가합니다.
변환의 예(docutils/transforms/ 패키지 내):
- frontmatter.DocInfo: 문서 메타데이터(서지 정보)를 변환합니다.
- references.AnonymousHyperlinks: 익명 참조를 대응하는 대상으로 해석합니다.
- parts.Contents: 문서의 목차를 생성합니다.
- document.Merger: 내용이 채워진 여러 doctree를 하나로 합칩니다. (아직 구현되지 않았으며 완전히 이해되지도 않았습니다.)
- document.Splitter: 문서를 하위 문서의 트리 구조로 분할합니다. 아마 섹션 단위로 분할할 것입니다. 이때 참조도 적절히 변환해야 합니다. (구현되지도 않았고 조금도 이해되지 않았습니다.)
- components.Filter: 특정 Docutils 컴포넌트에 의존하는 요소를 포함하거나 제외합니다.
Writer
Writer는 최종 출력(HTML, XML, TeX 등)을 생성합니다. Writer는 내부 Document Tree 구조를 최종 데이터 형식으로 번역하며, 그 전에 Writer 전용 transforms를 먼저 실행하기도 합니다.
문서가 Writer에 도달할 무렵에는 최종 형태가 되어 있어야 합니다. Writer의 역할은 단순히 (그리고 오로지) Docutils doctree 구조를 대상 형식으로 번역하는 것입니다. 작은 변환이 일부 필요할 수도 있지만, 그러한 변환은 국소적이고 형식에 특화된 것이어야 합니다.
각 writer는 “write” 메서드를 가진 “Writer” 클래스를 내보내는 모듈 또는 패키지입니다. 베이스 “Writer” 클래스는 docutils/writers/__init__.py 모듈에 있습니다.
책임:
- doctree를 특정 출력 형식으로 번역합니다.
- 참조를 해당 형식 고유의 형태로 변환합니다.
- 번역된 출력을 대상 I/O에 씁니다.
예시
- XML: 다음과 같은 다양한 형태가 있습니다.
- Docutils XML (내부 문서 트리의 표현이며,
docutils.writers.docutils_xml로 구현되어 있습니다). - DocBook (Docutils 샌드박스에서 구현 중입니다).
- Docutils XML (내부 문서 트리의 표현이며,
- HTML (
docutils.writers.html4css1로 구현된 XHTML입니다). - PDF (Docutils 샌드박스에서 ReportLabs 인터페이스를 개발 중입니다).
- TeX (샌드박스에서 LaTeX Writer를 구현 중입니다).
- Docutils 고유의 의사 XML (
docutils.writers.pseudoxml로 구현되어 있으며, 테스트에 사용됩니다). - 일반 텍스트
- reStructuredText?
입력/출력
I/O 클래스는 저수준 입력과 출력을 위한 일관된 API를 제공합니다. 다양한 입력/출력 메커니즘을 위한 서브클래스가 존재하게 될 것입니다. 그러나 이는 구현 세부 사항으로 볼 수 있습니다. 대부분의 애플리케이션은 Publisher와 관련된 편의 함수 중 하나를 사용하는 것으로 충분할 것입니다.
I/O 클래스는 현재 초기 단계에 있으며, 아직 해야 할 작업이 많습니다. 문제점:
- 다중 파일 입력(파일 및 디렉터리)을 API에서 어떻게 표현해야 합니까?
- 다중 파일 출력은 어떻게 표현해야 합니까? 출력 배포 유형마다 하나씩 “Writer” 변형을 두는 것은 어떻습니까? 아니면 관련 변환을 갖춘 Output 객체는 어떻습니까?
책임:
- 입력 소스(Input 객체)에서 데이터를 읽거나 출력 대상(Output 객체)에 데이터를 씁니다.
입력 소스의 예:
- 디스크의 단일 파일 또는 스트림 (
docutils.io.FileInput으로 구현되어 있습니다). - 디스크의 여러 파일 (
MultiFileInput?). - Python 소스 파일: 모듈과 패키지입니다.
- 클라이언트 애플리케이션에서 전달받은 Python 문자열입니다(
docutils.io.StringInput으로 구현됩니다).
출력 대상의 예는 다음과 같습니다.
- 디스크의 단일 파일 또는 스트림입니다(
docutils.io.FileOutput으로 구현됩니다). - 디스크의 디렉터리와 파일로 이루어진 트리입니다.
- 클라이언트 애플리케이션에 반환되는 Python 문자열입니다(
docutils.io.StringOutput으로 구현됩니다). - 출력이 없습니다. 일반 출력의 일부만 사용하는 프로그램 방식의 애플리케이션에 유용합니다(
docutils.io.NullOutput으로 구현됩니다). - 메모리 안의 단일 트리 형태 자료 구조입니다.
- 메모리 안의 그 밖의 다른 자료 구조 집합입니다.
Docutils 패키지 구조
- 패키지 “docutils”입니다.
- 모듈 “__init__.py”에는 Docutils 컴포넌트의 베이스 클래스인 클래스 “Component”, 런타임 설정을 지정하기 위한 베이스 클래스인 클래스 “SettingsSpec”(docutils.frontend에서 사용됩니다), 그리고 변환을 지정하기 위한 베이스 클래스인 클래스 “TransformSpec”이 들어 있습니다.
- 모듈 “docutils.core”에는 퍼사드 클래스 “Publisher”와 편의 함수들이 들어 있습니다. 위의 Publisher를 참조하십시오.
- 모듈 “docutils.frontend”는 프로그램 방식의 사용과 프런트엔드 도구를 위한 런타임 설정 지원(설정 파일 지원, 명령줄 인자와 옵션 처리 포함)을 제공합니다.
- 모듈 “docutils.io”는 저수준 입력과 출력을 위한 통일된 API를 제공합니다. 위의 Input/Output을 참조하십시오.
- 모듈 “docutils.nodes”에는 Docutils 문서 트리 요소 클래스 라이브러리와 트리 순회용 Visitor 패턴 베이스 클래스들이 들어 있습니다. 아래의 Document Tree를 참조하십시오.
- 모듈 “docutils.statemachine”에는 정규 표현식 기반의 텍스트 필터와 파서에 특화된 유한 상태 기계가 들어 있습니다. reStructuredText 파서 구현은 이 모듈을 기반으로 합니다.
- 모듈 “docutils.urischemes”에는 알려진 URI 스킴(“http”, “ftp”, “mail” 등)의 매핑이 들어 있습니다.
- 모듈 “docutils.utils”에는 로거 클래스(“Reporter”, 아래의 Error Handling 참조)를 포함한 유틸리티 함수와 클래스들이 들어 있습니다.
- 패키지 “docutils.parsers”: 마크업 Parsers입니다.
- 함수 “get_parser_class(parser_name)”는 이름으로 파서 모듈을 반환합니다. 클래스 “Parser”는 개별 파서들의 베이스 클래스입니다. (
docutils/parsers/__init__.py) - 패키지 “docutils.parsers.rst”: reStructuredText 파서입니다.
- 다른 마크업 파서를 추가할 수 있습니다.
위의 Parsers를 참조하십시오.
- 함수 “get_parser_class(parser_name)”는 이름으로 파서 모듈을 반환합니다. 클래스 “Parser”는 개별 파서들의 베이스 클래스입니다. (
- 패키지 “docutils.readers”: 문맥을 인식하는 입력 리더입니다.
- 함수 “get_reader_class(reader_name)”는 이름 또는 별칭으로 리더 모듈을 반환합니다. 클래스 “Reader”는 개별 리더들의 베이스 클래스입니다. (
docutils/readers/__init__.py) - 모듈 “docutils.readers.standalone”은 독립적인 문서 파일을 읽습니다.
- 모듈 “docutils.readers.pep”은 PEP(Python Enhancement Proposals)를 읽습니다.
- 추가될 리더는 다음과 같습니다: Python 소스 코드(구조 및 독스트링), 이메일, FAQ, 그리고 아마도 Wiki 및 기타 형식입니다.
위의 Readers를 참조하십시오.
- 함수 “get_reader_class(reader_name)”는 이름 또는 별칭으로 리더 모듈을 반환합니다. 클래스 “Reader”는 개별 리더들의 베이스 클래스입니다. (
- 패키지 “docutils.writers”: 출력 형식 라이터입니다.
- 함수 “get_writer_class(writer_name)”는 이름으로 라이터 모듈을 반환합니다. 클래스 “Writer”는 구체적인 라이터들의 베이스 클래스입니다. (
docutils/writers/__init__.py) - 모듈 “docutils.writers.html4css1”은 HTML 4.01과 CSS1을 위한 간단한 HyperText Markup Language(하이퍼텍스트 마크업 언어) 문서 트리 작성기입니다.
- 모듈 “docutils.writers.docutils_xml”은 내부 문서 트리를 XML 형식으로 씁니다.
- 모듈 “docutils.writers.pseudoxml”은 간단한 내부 문서 트리 라이터로, 들여쓰기된 의사 XML을 씁니다.
- 추가될 라이터는 다음과 같습니다: HTML 3.2 또는 4.01-loose, XML(DocBook 등 다양한 형식), PDF, TeX, 일반 텍스트, reStructuredText, 그리고 아마도 기타 형식입니다.
위의 Writers를 참조하십시오.
- 함수 “get_writer_class(writer_name)”는 이름으로 라이터 모듈을 반환합니다. 클래스 “Writer”는 구체적인 라이터들의 베이스 클래스입니다. (
- 패키지 “docutils.transforms”: 트리 변환 클래스입니다.
- 클래스 “Transformer”는 변환을 저장하고 이를 문서 트리에 적용합니다. (
docutils/transforms/__init__.py) - 클래스 “Transform”은 구체적인 변환들의 베이스 클래스입니다. (
docutils/transforms/__init__.py) - 각 모듈에는 관련된 변환 클래스들이 들어 있습니다.
위의 Transforms를 참조하십시오.
- 클래스 “Transformer”는 변환을 저장하고 이를 문서 트리에 적용합니다. (
- 패키지 “docutils.languages”: 언어 모듈에는 언어에 따라 달라지는 문자열과 매핑이 들어 있습니다. 언어 모듈의 이름은 해당 언어 식별자(아래 Choice of Docstring Format에서 정의)를 따르며, 대시는 밑줄로 바꿉니다.
- 함수 “get_language(language_code)”는 일치하는 언어 모듈을 반환합니다. (
docutils/languages/__init__.py) - 모듈은 en.py(영어), de.py(독일어), fr.py(프랑스어), it.py(이탈리아어), sk.py(슬로바키아어), sv.py(스웨덴어)입니다.
- 다른 언어도 추가될 예정입니다.
- 함수 “get_language(language_code)”는 일치하는 언어 모듈을 반환합니다. (
- 서드파티 모듈: “extras” 디렉터리입니다. 이 모듈들은 Python 설치에 아직 없는 경우에만 설치됩니다.
extras/optparse.py와extras/textwrap.py는 옵션 파싱과 명령줄 도움말을 제공합니다. 이들은 Greg Ward의 http://optik.sf.net/ 프로젝트에서 가져온 것으로, 편의를 위해 포함되었습니다.extras/roman.py에는 로마 숫자 변환 루틴이 들어 있습니다.
프런트엔드 도구
tools/ 디렉터리에는 일반적인 Docutils 처리를 위한 여러 프런트엔드가 들어 있습니다. 자세한 내용은 Docutils Front-End Tools를 참조하십시오.
문서 트리
Docutils 내부에서는 컴포넌트 사이의 인터페이스에 단일 중간 자료 구조를 사용하며, 이 자료 구조는 docutils.nodes 모듈에 정의되어 있습니다. 어떤 컴포넌트도 이 자료 구조를 내부적으로 사용할 필요는 없으며, 위의 Docutils Project Model 다이어그램에 나타낸 것처럼 컴포넌트 사이에서만 사용하면 됩니다.
사용자 정의 노드 타입은 (a) 변환이 해당 노드를 실제 Writer에 도달하기 전에 표준 Docutils 노드로 변환하거나, (b) 사용자 정의 노드가 특정 Writer에서 명시적으로 지원되고 필터링된 “pending” 노드로 감싸져 있는 경우에 허용됩니다. 조건 (a)의 예로는 아래에서 설명하는 Python Source Reader가 있으며, 여기서는 “stylist” 변환이 사용자 정의 노드를 변환합니다. HTML <meta> 태그는 조건 (b)의 예입니다. 이 태그는 HTML Writer에서는 지원되지만 다른 Writer에서는 지원되지 않습니다. reStructuredText의 “meta” 지시문은 “pending” 노드를 생성하며, 이 노드는 내장된 “meta” 노드를 HTML 호환 Writer만 처리할 수 있다는 정보를 담고 있습니다. “pending” 노드는 docutils.transforms.components.Filter 변환이 처리합니다. 이 변환은 호출한 Writer가 HTML을 지원하는지 검사하며, 지원하지 않으면 “pending” 노드(와 그 안에 포함된 “meta” 노드)를 문서에서 제거합니다.
문서 트리 자료 구조는 DOM 트리와 비슷하지만, DOM의 범용 노드 대신 구체적인 노드 이름(클래스)을 사용합니다. 스키마는 XML DTD(eXtensible Markup Language Document Type Definition, 확장 가능한 마크업 언어 문서 타입 정의)로 문서화되어 있으며, 두 부분으로 구성됩니다:
- Docutils 범용 DTD인 docutils.dtd와
- OASIS Exchange Table Model인 soextbl.dtd입니다.
DTD는 다양한 입력 및 출력 형식에 적합한 풍부한 요소 집합을 정의합니다. DTD는 원래 입력 텍스트 또는 그에 충분히 가까운 사본을 재구성하는 데 필요한 모든 정보를 보존합니다.
자세한 내용은 The Docutils Document Tree를 참조하십시오(미완성).
오류 처리
파서가 마크업에서 오류를 발견하면 시스템 메시지(DTD 요소 “system_message”)를 삽입합니다. 시스템 메시지에는 다섯 가지 수준이 있습니다:
- Level-0, “DEBUG”: 내부 보고용 사항입니다. 처리에는 아무런 영향이 없습니다. Level-0 시스템 메시지는 다른 메시지와 별도로 처리됩니다.
- Level-1, “INFO”: 무시해도 되는 사소한 문제입니다. 처리에 거의 또는 전혀 영향이 없습니다. 일반적으로 level-1 시스템 메시지는 보고되지 않습니다.
- Level-2, “WARNING”: 해결해야 할 문제입니다. 무시하면 출력에 사소한 문제가 생길 수 있습니다. 일반적으로 level-2 시스템 메시지는 보고되지만 처리를 중단하지는 않습니다
- Level-3, “ERROR”: 해결해야 할 중대한 문제입니다. 무시하면 출력에 예측할 수 없는 오류가 포함됩니다. 일반적으로 level-3 시스템 메시지는 보고되지만 처리를 중단하지는 않습니다
- Level-4, “SEVERE”: 반드시 해결해야 하는 치명적인 오류입니다. 일반적으로 level-4 시스템 메시지는 처리를 중단시키는 예외로 바뀝니다. 무시하면 출력에 심각한 오류가 포함됩니다.
초기 메시지 수준은 독자적으로 고안되었지만, VMS error condition severity levels와 매우 밀접하게 대응합니다. 수준 1부터 4까지의 따옴표 안 이름은 VMS에서 빌려 온 것입니다. 이후 오류 처리 방식은 log4j project의 영향을 받았습니다.
Python 소스 리더
Python 소스 리더(“PySource”)는 Python 소스 파일을 읽고 문맥에 맞게 독스트링을 추출한 다음, 독스트링을 구문 분석하고 연결하고 조립하여 하나의 일관된 전체로 만드는 Docutils 구성 요소입니다. 이는 중요하면서도 단순하지 않은 구성 요소로, 현재 Docutils 샌드박스에서 실험적으로 개발되고 있습니다. 여기에서는 상위 수준의 설계 문제를 다룹니다.
처리 모델
이 모델은 경험과 새로운 발견을 반영하면서 시간이 지남에 따라 발전할 것입니다.
- PySource 리더는 Input 클래스를 사용하여 Python 패키지와 모듈을 읽어 들여 문자열 트리로 만듭니다.
- Python 모듈을 파싱하여 문자열 트리를 독스트링 노드를 포함한 추상 문법 트리의 트리로 변환합니다.
- 추상 문법 트리는 패키지/모듈의 내부 표현으로 변환됩니다. 코드 구조의 세부 정보와 함께 독스트링이 추출됩니다. 아래의 AST Mining을 참조하십시오. 6단계에서 조회할 수 있도록 네임스페이스가 구성됩니다.
- 독스트링을 하나씩 구문 분석하여 표준 Docutils doctree를 생성합니다.
- PySource는 개별 독스트링의 doctree를 모두 조립하여 패키지/모듈/클래스 구조에 대응하는 Python 전용 사용자 정의 Docutils 트리로 만듭니다. 이는 이 리더에 특화된 사용자 정의 내부 표현입니다(Docutils Python Source DTD를 참조하십시오). Python 식별자와 하이퍼링크 대상 같은 네임스페이스는 병합되어야 합니다.
- 독스트링(해석된 텍스트)에서 Python 식별자로 향하는 상호 참조는 Python 네임스페이스 조회 규칙에 따라 해석됩니다. 아래의 Identifier Cross-References를 참조하십시오.
- (Transformer에 의해) 사용자 정의 doctree에 “Stylist” 변환이 적용되고, 사용자 정의 노드는 표준 노드를 기본 요소로 사용하여 렌더링되며, 표준 문서 트리가 출력됩니다. 아래의 Stylist Transforms를 참조하십시오.
- Transformer에 의해 표준 doctree에 다른 변환들이 적용됩니다.
- 표준 doctree는 Writer로 전달되며, Writer는 문서를 구체적인 형식(HTML, PDF 등)으로 변환합니다.
- Writer는 Output 클래스를 사용하여 결과 데이터를 목적지(디스크 파일, 디렉터리와 파일 등)에 기록합니다.
AST 마이닝
파싱된 Python 모듈을 탐색하여 다음 모든 객체의 이름, 독스트링(속성 독스트링과 추가 독스트링 포함, 아래 참조), 추가 정보(아래 괄호 안에 표시)를 담은 순서 있는 트리를 반환하는 추상 문법 트리(Abstract Syntax Tree) 마이닝 코드를 작성(또는 수정)할 것입니다:
- 패키지
- 모듈
- 모듈 속성(+ 초깃값)
- 클래스(+ 상속)
- 클래스 속성(+ 초깃값)
- 인스턴스 속성(+ 초깃값)
- 메서드(+ 매개변수와 기본값)
- 함수(+ 매개변수와 기본값)
(주석도 추출해야 합니까? 예를 들어, 모듈 시작 부분의 주석은 서지 필드 목록을 두기에 좋은 위치입니다.)
해석된 텍스트(interpreted text) 상호 참조를 평가하려면 위의 각 항목에 대한 네임스페이스도 필요합니다.
2001-08-14에 시작된 python-dev/docstring-develop(독스트링 개발) 스레드 “AST mining”을 참조하십시오.
독스트링 추출 규칙
- 검사 대상:
- 문서화하는 모듈에 “
__all__” 변수가 있으면 “__all__”에 나열된 식별자만 독스트링 검사 대상이 됩니다. - “
__all__”이 없으면 비공개 이름(이름이 “_”로 시작하되 “__”로 시작하고 끝나지는 않는 이름)을 제외한 모든 식별자를 검사합니다. - 1a와 1b는 런타임 설정으로 재정의할 수 있습니다.
- 문서화하는 모듈에 “
- 여기서:
독스트링은 문자열 리터럴 표현식이며, Python 모듈 안의 다음 위치에서 인식됩니다.
- 모듈, 함수 정의, 클래스 정의, 메서드 정의의 시작 부분에서 주석이 있다면 그 뒤에 오는 경우입니다. 이것은 Python
__doc__속성의 표준입니다. - 모듈 최상위 수준, 클래스 정의,
__init__메서드 정의에서 단순 대입문 바로 뒤에, 주석이 있다면 그 뒤에 오는 경우입니다. 아래 Attribute Docstrings 절을 참고하십시오. - (a)와 (b)의 독스트링 바로 뒤에 오는 추가 문자열 리터럴도 인식하여 추출하고 이어 붙입니다. 아래 Additional Docstrings 절을 참고하십시오.
- @@@ 속성 독스트링을 갖는 2.2 스타일 “properties”는 어떻게 합니까? 문법이 정해질 때까지 기다려야 합니까?
- 모듈, 함수 정의, 클래스 정의, 메서드 정의의 시작 부분에서 주석이 있다면 그 뒤에 오는 경우입니다. 이것은 Python
- 방법:
가능하면 Python 모듈은 임포트하지 말고 Docutils로 파싱해야 합니다. 여기에는 몇 가지 이유가 있습니다.
- 신뢰할 수 없는 코드를 임포트하는 것은 본질적으로 안전하지 않습니다.
- 임포트한 모듈을 인트로스펙션으로 검사하면 주석이나 정의 순서 같은 소스의 정보가 사라집니다.
- 독스트링은 바이트코드 컴파일러가 문자열 리터럴 표현식을 무시하는 위치(위의 2b와 2c)에서도 인식되어야 하므로, 모듈을 임포트하면 이 독스트링들을 잃게 됩니다.
물론 “parser” 라이브러리 모듈 같은 표준 Python 파싱 도구를 사용해야 합니다.
모듈의 Python 소스 코드를 구할 수 없거나(즉,
.pyc파일만 있는 경우) C 확장 모듈인 경우에는 모듈을 임포트해야만 독스트링에 접근할 수 있으므로, 그에 따른 제약은 감수해야 합니다.
속성 독스트링과 추가 독스트링은 Python 바이트코드 컴파일러가 무시하므로, 이를 사용해도 네임스페이스가 오염되거나 런타임이 비대해지지 않습니다. 이들은 __doc__이나 다른 어떤 속성에도 할당되지 않습니다. 모듈을 처음 파싱할 때 성능이 약간 떨어질 수 있습니다.
속성 독스트링
(이것은 PEP 224의 단순화된 버전입니다.)
대입문 바로 뒤에 오는 문자열 리터럴은 다음 조건을 만족할 때 독스트링 추출 장치에 의해 해당 대입문 대상의 독스트링으로 해석됩니다.
- 대입은 다음 문맥 중 하나에 있어야 합니다.
- 모듈의 최상위 수준(즉, 루프나 조건문 같은 복합문 안에 중첩되지 않은 곳): 모듈 속성.
- 클래스 정의의 최상위 수준: 클래스 속성.
- 클래스의 “
__init__” 메서드 정의의 최상위 수준: 인스턴스 속성. 다른 메서드에서 대입되는 인스턴스 속성은 구현 세부 사항으로 간주합니다. (@@@__new__메서드는?) - 모듈이나 클래스 정의의 최상위 수준에서 이루어지는 함수 속성 대입.
위의 각 문맥은 모두 최상위 수준(즉, 정의의 가장 바깥쪽 스위트)이므로, 조건부로 또는 루프 안에서 대입되는 속성에 대해서는 더미 대입을 배치해야 할 수도 있습니다.
- 대입은 단일 대상에 이루어져야 하며, 대상의 리스트나 튜플에 이루어져서는 안 됩니다.
- 대상의 형태:
- 위의 문맥 1a와 1b에서 대상은 단순 식별자여야 합니다(점으로 구분된 식별자, 첨자 표현식, 슬라이스 표현식이어서는 안 됩니다).
- 위의 문맥 1c에서 대상은 “
self.attrib” 형태여야 합니다. 여기서 “self”는 “__init__” 메서드의 첫 번째 매개변수(인스턴스 매개변수)와 일치해야 하고, “attrib”은 3a에서와 같은 단순 식별자입니다. - 위의 문맥 1d에서 대상은 “
name.attrib” 형태여야 합니다. 여기서 “name”은 이미 정의된 함수나 메서드 이름과 일치해야 하고, “attrib”은 3a에서와 같은 단순 식별자입니다.
대입과 독스트링 사이의 연결을 강조하기 위해 속성 독스트링 뒤에 빈 줄을 사용할 수 있습니다.
예시:
g = 'module attribute (module-global variable)'
"""This is g's docstring."""
class AClass:
c = 'class attribute'
"""This is AClass.c's docstring."""
def __init__(self):
"""Method __init__'s docstring."""
self.i = 'instance attribute'
"""This is self.i's docstring."""
def f(x):
"""Function f's docstring."""
return x**2
f.a = 1
"""Function attribute f.a's docstring."""
추가 독스트링
(이 아이디어는 PEP 216에서 가져왔습니다.)
많은 프로그래머가 API 문서화에 독스트링을 폭넓게 사용하고 싶어 합니다. 그러나 독스트링은 실행 중인 프로그램에서 공간을 차지하므로, 일부 프로그래머는 코드를 “비대하게” 만드는 것을 꺼립니다. 또한 모든 API 문서가 __doc__이 표시되는 대화형 환경에 적합한 것은 아닙니다.
Docutils의 독스트링 추출 도구는 정의의 시작 부분이나 단순 대입 뒤에 나타나는 모든 문자열 리터럴 표현식을 이어 붙입니다. 정의의 첫 번째 문자열만 __doc__으로 사용할 수 있으며, 대화형 세션에 적합한 간단한 사용법 텍스트로 쓸 수 있습니다. 이후의 문자열 리터럴과 모든 속성 독스트링은 Python 바이트코드 컴파일러가 무시하므로, 더 자세한 API 정보를 담을 수 있습니다.
예시:
def function(arg):
"""This is __doc__, function's docstring."""
"""
This is an additional docstring, ignored by the byte-code
compiler, but extracted by Docutils.
"""
pass
독스트링 형식 선택
처리 시스템은 모든 사람에게 단일 독스트링 형식을 강요하지 않고 여러 입력 형식을 허용합니다. 특수 변수 __docformat__은 모듈의 최상위 수준에서 어떤 함수나 클래스 정의보다도 앞에 나올 수 있습니다. 시간이 흐르면서, 또는 결정에 따라 표준 형식이나 형식 집합이 자리 잡아야 합니다.
모듈의 __docformat__ 변수는 해당 모듈 파일에 정의된 객체에만 적용됩니다. 특히 패키지의 __init__.py 파일에 있는 __docformat__ 변수는 하위 패키지와 하위 모듈에 정의된 객체에는 적용되지 않습니다.
__docformat__ 변수는 사용하는 형식의 이름을 담은 문자열로, 입력 파서의 모듈 이름이나 패키지 이름(즉, 해당 모듈이나 패키지를 “import”할 때 필요한 이름과 같은 이름)과 대소문자 구분 없이 일치하는 문자열이거나 등록된 별칭입니다. __docformat__을 지정하지 않으면 현재 기본 형식은 “plaintext”이며, 나중에 표준 형식이 정해지면 그 표준 형식으로 바뀔 수 있습니다.
__docformat__ 문자열에는 선택 사항인 두 번째 필드를 넣을 수 있으며, 이 필드는 형식 이름(첫 번째 필드)과 공백 한 칸으로 구분되는, RFC 1766에 정의된 대소문자를 구분하지 않는 언어 식별자입니다. 일반적인 언어 식별자는 ISO 639의 2글자 언어 코드로 이루어집니다(3글자 코드는 2글자 코드가 없을 때만 사용합니다. 현재 3글자 코드를 허용하도록 RFC 1766을 개정하고 있습니다). 언어 식별자를 지정하지 않으면 기본값은 영어를 뜻하는 “en”입니다. 언어 식별자는 파서에 전달되며, 언어에 따라 달라지는 마크업 기능에 사용할 수 있습니다.
식별자 상호 참조
Python 독스트링에서 해석 텍스트(interpreted text)는 변수, 함수, 클래스, 모듈의 이름과 같은 프로그램 식별자를 분류하고 마크업하는 데 사용됩니다. 식별자만 주어지면 그 역할은 Python 네임스페이스 조회 규칙에 따라 암묵적으로 추론됩니다. 함수와 메서드에는 (동적으로 할당된 경우에도) 괄호(‘()’)를 붙일 수 있습니다:
This function uses `another()` to do its work.
클래스 속성, 인스턴스 속성, 모듈 속성에는 필요할 때 점으로 구분한 식별자를 사용합니다. 예를 들면 다음과 같습니다(reStructuredText 마크업 사용):
class Keeper(Storer):
"""
Extend `Storer`. Class attribute `instances` keeps track
of the number of `Keeper` objects instantiated.
"""
instances = 0
"""How many `Keeper` objects are there?"""
def __init__(self):
"""
Extend `Storer.__init__()` to keep track of instances.
Keep count in `Keeper.instances`, data in `self.data`.
"""
Storer.__init__(self)
Keeper.instances += 1
self.data = []
"""Store data in a list, most recent last."""
def store_data(self, data):
"""
Extend `Storer.store_data()`; append new `data` to a
list (in `self.data`).
"""
self.data = data
백쿼트(“`”)로 감싼 각 식별자는 해당 식별자 자체의 정의를 가리키는 참조가 됩니다.
스타일리스트 변환
스타일리스트 변환은 PySource Reader에 특화된 전용 변환입니다. PySource Reader는 스타일에 관해서는 아무것도 결정할 필요가 없습니다. 사용자 정의 노드 유형을 포함해 파싱과 링크가 끝난, 논리적으로 구성된 문서 트리를 만들기만 합니다. 스타일리스트 변환은 Reader가 만든 사용자 정의 노드를 이해하고, 이를 표준 Docutils 노드로 변환합니다.
여러 Stylist 변환을 구현해 둘 수 있으며, 그중 하나를 런타임에 (”–style” 또는 “–stylist” 명령줄 옵션을 통해) 선택할 수 있습니다. 각 Stylist 변환은 서로 다른 레이아웃이나 스타일을 구현합니다. 이름도 여기에서 비롯되었습니다. Stylist 변환은 Reader에서 문맥을 이해하는 부분을 처리 과정 중 레이아웃을 생성하는 부분과 분리하며, 그 결과 더 유연하고 견고한 시스템이 만들어집니다. 이는 또한 SGML/XML의 이상인 “스타일과 내용의 분리”에도 기여합니다.
스타일을 적용하는 코드 조각을 작고 모듈화된 상태로 유지하면, 사람들이 자신만의 스타일을 직접 만들기가 훨씬 쉬워집니다. 기존 도구로는 “진입 장벽”이 너무 높습니다. stylist 코드를 분리해 내면 이 장벽이 상당히 낮아질 것입니다.
참고 문헌 및 각주
프로젝트 웹 사이트
이 작업을 위한 SourceForge 프로젝트가 http://docutils.sourceforge.net/ 에 마련되어 있습니다.
Copyright
This document has been placed in the public domain.
Acknowledgements
This document borrows ideas from the archives of the Python Doc-SIG. Thanks to all members past & present.