Skip to content

Commit 5862f0b

Browse files
committed
chore: align license metadata
1 parent 662cb11 commit 5862f0b

6 files changed

Lines changed: 149 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
모든 중요한 변경 사항은 이 문서에 기록됩니다. 형식은 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/)[Semantic Versioning](https://semver.org/lang/ko/)을 따릅니다.
44

5+
## [2.8.3] - 2026-03-10
6+
### 변경
7+
- 저장소와 배포 메타데이터의 라이선스 표기를 실제 `LICENSE` 파일과 일치하도록 정렬했습니다.
8+
- `pyproject.toml`을 PEP 639 방식의 `LicenseRef-python-hwpx-NonCommercial` + `license-files` 구성으로 갱신하고, 잘못된 MIT 분류자를 제거했습니다.
9+
- README 라이선스 배지/섹션을 커스텀 비상업적 라이선스 기준으로 수정하고, wheel/sdist 산출물의 라이선스 메타데이터를 검증하는 회귀 테스트를 추가했습니다.
10+
511
## [2.8.2] - 2026-03-08
612
### 변경
713
- README를 현재 공개 API와 CLI 범위에 맞춰 정리했습니다. Quick start, 텍스트 추출, 객체 검색 예시를 실제 호출 방식 기준으로 수정했습니다.

DevDoc/license-alignment-audit.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# License Alignment Audit
2+
3+
Date: 2026-03-10
4+
5+
## Files inspected
6+
7+
- `LICENSE`
8+
- `README.md`
9+
- `pyproject.toml`
10+
- `docs/conf.py`
11+
- `CONTRIBUTING.md`
12+
- `.github/workflows/release.yml`
13+
- `.github/workflows/tests.yml`
14+
- `scripts/build-and-publish.sh`
15+
- `tests/test_packaging_py_typed.py`
16+
- Repo-wide searches across `docs/`, `.github/`, `DevDoc/`, `CHANGELOG.md`, and the repository root for license-related metadata and MIT references
17+
18+
## Contradictions found before this change
19+
20+
- `LICENSE` defined a custom non-commercial license and named `python-hwpx Maintainers` as the copyright holder.
21+
- `README.md` showed an MIT badge and an MIT license section, which contradicted the actual license text.
22+
- `README.md` attributed the license line to `고규현 (Kyuhyun Koh)`, while the `LICENSE` file and package metadata used `python-hwpx Maintainers`.
23+
- `pyproject.toml` used the legacy `license = { file = "LICENSE" }` form and also published the classifier `License :: OSI Approved :: MIT License`, which falsely represented the distribution as MIT-licensed.
24+
25+
## Source of truth
26+
27+
- The repository root `LICENSE` file is the source of truth for license terms.
28+
- This audit treats the project as remaining under its existing custom non-commercial license. No evidence of an intentional relicensing to MIT was found elsewhere in the repository.
29+
30+
## Decision summary
31+
32+
- Preserve the current non-commercial custom license.
33+
- Align public-facing metadata and README wording to that license.
34+
- Use modern packaging metadata that points built distributions back to the root `LICENSE` file without inventing an OSI identifier.
35+
- Remove conflicting MIT wording and the MIT trove classifier rather than replacing it with another potentially ambiguous license classifier.
36+
37+
## Notes on surfaces inspected
38+
39+
- `docs/conf.py` already used `python-hwpx Maintainers` and did not restate MIT licensing.
40+
- No GitHub Pages or docs markdown pages were found to restate the project license.
41+
- The release workflow already builds distributions and runs `twine check`, so it was left in place and used for verification after the metadata update.

DevDoc/license-metadata-policy.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# License Metadata Policy
2+
3+
## Source of truth
4+
5+
- The root `LICENSE` file defines the project's license terms.
6+
- Metadata changes must reflect the current `LICENSE` text. Do not treat README text, badges, or historical PyPI metadata as authoritative.
7+
8+
## Packaging rule
9+
10+
- `pyproject.toml` must represent the current custom license with `project.license = "LicenseRef-python-hwpx-NonCommercial"`.
11+
- `pyproject.toml` must list `project.license-files = ["LICENSE"]` so both `sdist` and `wheel` carry the license file.
12+
- Keep the build backend compatible with that metadata format by requiring `setuptools>=77.0.0`.
13+
14+
## Classifier rule
15+
16+
- Do not add `License ::` trove classifiers for this project unless the `LICENSE` file changes to a classifier-backed license and the classifier is verified to be accurate.
17+
- For the current custom non-commercial license, leaving license classifiers unset is less ambiguous than picking an approximate classifier.
18+
19+
## README rule
20+
21+
- The README badge and license section must describe the project as using a custom non-commercial license and link to `LICENSE`.
22+
- If contact information is updated, keep it distinct from the copyright/licensing line unless the `LICENSE` file is updated too.
23+
24+
## Verification rule
25+
26+
- Before release or after touching license metadata, run `python -m build` and `twine check dist/*`.
27+
- Inspect built `PKG-INFO` and wheel `METADATA` for `License-Expression: LicenseRef-python-hwpx-NonCommercial` and `License-File: LICENSE`.
28+
- Confirm the wheel contains `.dist-info/licenses/LICENSE` and the sdist contains the root `LICENSE` file.

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<p align="center">
77
<a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/v/python-hwpx?color=blue&label=PyPI" alt="PyPI"></a>
88
<a href="https://pypi.org/project/python-hwpx/"><img src="https://img.shields.io/pypi/pyversions/python-hwpx" alt="Python"></a>
9-
<a href="https://github.com/airmang/python-hwpx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
9+
<a href="https://github.com/airmang/python-hwpx/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Custom%20Noncommercial-orange" alt="License: Custom Non-Commercial"></a>
1010
<a href="https://airmang.github.io/python-hwpx/"><img src="https://img.shields.io/badge/docs-Sphinx-8CA1AF" alt="Docs"></a>
1111
</p>
1212
</p>
@@ -286,13 +286,15 @@ pytest
286286

287287
## License
288288

289-
[MIT](LICENSE) © 고규현 (Kyuhyun Koh)
289+
[Custom Non-Commercial License](LICENSE) © python-hwpx Maintainers
290+
291+
Commercial use requires separate permission from the copyright holders.
290292

291293
<br>
292294

293-
## Author
295+
## Maintainer
294296

295-
**고규현** — 광교고등학교 정보·컴퓨터 교사
297+
Primary maintainer/contact: **고규현** — 광교고등학교 정보·컴퓨터 교사
296298

297299
- ✉️ [kokyuhyun@hotmail.com](mailto:kokyuhyun@hotmail.com)
298300
- 🐙 [@airmang](https://github.com/airmang)

pyproject.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
[build-system]
2-
requires = ["setuptools", "wheel"]
2+
requires = ["setuptools>=77.0.0", "wheel"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "python-hwpx"
7-
version = "2.8.2"
7+
version = "2.8.3"
88
description = "Hancom HWPX 패키지를 로드하고 편집하기 위한 Python 유틸리티 모음"
99
readme = { file = "README.md", content-type = "text/markdown" }
10-
license = { file = "LICENSE" }
10+
license = "LicenseRef-python-hwpx-NonCommercial"
11+
license-files = ["LICENSE"]
1112
requires-python = ">=3.10"
1213
authors = [
1314
{ name = "python-hwpx Maintainers" },
@@ -16,7 +17,6 @@ keywords = ["hwp", "hwpx", "hancom", "opc", "xml"]
1617
classifiers = [
1718
"Development Status :: 3 - Alpha",
1819
"Intended Audience :: Developers",
19-
"License :: OSI Approved :: MIT License",
2020
"Programming Language :: Python :: 3",
2121
"Programming Language :: Python :: 3.10",
2222
"Programming Language :: Python :: 3.11",
@@ -35,6 +35,7 @@ dev = [
3535
"pytest>=7.4",
3636
]
3737
test = [
38+
"build>=1.0",
3839
"pytest>=7.4",
3940
"pytest-cov>=5.0",
4041
]
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from __future__ import annotations
2+
3+
import subprocess
4+
import sys
5+
import tarfile
6+
from pathlib import Path
7+
from zipfile import ZipFile
8+
9+
import pytest
10+
11+
12+
LICENSE_EXPRESSION = "LicenseRef-python-hwpx-NonCommercial"
13+
14+
15+
def _build_distribution(tmp_path: Path, distribution: str) -> Path:
16+
pytest.importorskip("build")
17+
18+
project_root = Path(__file__).resolve().parents[1]
19+
build_args = [
20+
sys.executable,
21+
"-m",
22+
"build",
23+
f"--{distribution}",
24+
"--outdir",
25+
str(tmp_path),
26+
]
27+
subprocess.run(build_args, cwd=project_root, check=True)
28+
29+
pattern = "*.whl" if distribution == "wheel" else "*.tar.gz"
30+
return next(tmp_path.glob(pattern))
31+
32+
33+
@pytest.mark.parametrize("distribution", ["wheel", "sdist"])
34+
def test_built_distributions_expose_custom_license_metadata(
35+
tmp_path: Path, distribution: str
36+
) -> None:
37+
artifact = _build_distribution(tmp_path, distribution)
38+
39+
if distribution == "wheel":
40+
with ZipFile(artifact) as wheel_archive:
41+
members = set(wheel_archive.namelist())
42+
metadata_name = next(
43+
name for name in members if name.endswith(".dist-info/METADATA")
44+
)
45+
metadata = wheel_archive.read(metadata_name).decode("utf-8")
46+
47+
assert f"License-Expression: {LICENSE_EXPRESSION}" in metadata
48+
assert "License-File: LICENSE" in metadata
49+
assert "Classifier: License ::" not in metadata
50+
assert any(name.endswith(".dist-info/licenses/LICENSE") for name in members)
51+
return
52+
53+
with tarfile.open(artifact, "r:gz") as sdist_archive:
54+
members = sdist_archive.getnames()
55+
pkg_info_name = next(name for name in members if name.endswith("/PKG-INFO"))
56+
pkg_info_member = sdist_archive.extractfile(pkg_info_name)
57+
assert pkg_info_member is not None
58+
metadata = pkg_info_member.read().decode("utf-8")
59+
60+
assert f"License-Expression: {LICENSE_EXPRESSION}" in metadata
61+
assert "License-File: LICENSE" in metadata
62+
assert "Classifier: License ::" not in metadata
63+
assert any(name.endswith("/LICENSE") for name in members)

0 commit comments

Comments
 (0)