DefStream 0.1.13__tar.gz → 0.1.14__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. defstream-0.1.13/.github/ISSUE_TEMPLATE/feature_request.yml → defstream-0.1.14/.github/ISSUE_TEMPLATE/01.feature.yml +5 -2
  2. defstream-0.1.13/.github/ISSUE_TEMPLATE/bug_report.yml → defstream-0.1.14/.github/ISSUE_TEMPLATE/02.bug.yml +4 -1
  3. defstream-0.1.14/.github/ISSUE_TEMPLATE/03.fix.yml +57 -0
  4. defstream-0.1.14/.github/ISSUE_TEMPLATE/04.task.yml +40 -0
  5. {defstream-0.1.13 → defstream-0.1.14}/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  6. {defstream-0.1.13 → defstream-0.1.14}/.github/workflows/ci.yml +30 -2
  7. {defstream-0.1.13 → defstream-0.1.14}/DefStream.egg-info/PKG-INFO +6 -6
  8. {defstream-0.1.13 → defstream-0.1.14}/DefStream.egg-info/SOURCES.txt +36 -27
  9. {defstream-0.1.13 → defstream-0.1.14}/PKG-INFO +6 -6
  10. {defstream-0.1.13 → defstream-0.1.14}/README.en.md +59 -8
  11. {defstream-0.1.13 → defstream-0.1.14}/README.md +5 -5
  12. {defstream-0.1.13 → defstream-0.1.14}/issues/feature/8/IMPLEMENTS.md +5 -5
  13. {defstream-0.1.13 → defstream-0.1.14}/issues/feature/8/README.md +2 -2
  14. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/10/IMPLEMENTS.md +3 -3
  15. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/13/README.md +1 -1
  16. defstream-0.1.14/issues/fix/21/IMPLEMENTS.md +38 -0
  17. defstream-0.1.14/issues/fix/21/ISSUE.md +17 -0
  18. defstream-0.1.14/issues/fix/21/README.md +44 -0
  19. defstream-0.1.14/issues/fix/21/REQUIREMENTS.md +68 -0
  20. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/3/IMPLEMENTS.md +1 -1
  21. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/6/IMPLEMENTS.md +2 -2
  22. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/6/README.md +1 -1
  23. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/6/REQUIREMENTS.md +3 -3
  24. defstream-0.1.14/issues/task/18/IMPLEMENTS.md +96 -0
  25. defstream-0.1.14/issues/task/18/README.md +37 -0
  26. defstream-0.1.14/issues/task/18/REQUIREMENTS.md +59 -0
  27. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/Common.py +2 -3
  28. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/__init__.py +2 -2
  29. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_logging.py +2 -2
  30. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_release_workflow.py +62 -0
  31. defstream-0.1.14/tests/test_validator_patterns.py +48 -0
  32. defstream-0.1.13/.github/PULL_REQUEST_TEMPLATE/hotfix.md +0 -34
  33. {defstream-0.1.13 → defstream-0.1.14}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  34. {defstream-0.1.13 → defstream-0.1.14}/.gitignore +0 -0
  35. {defstream-0.1.13 → defstream-0.1.14}/DefStream.egg-info/dependency_links.txt +0 -0
  36. {defstream-0.1.13 → defstream-0.1.14}/DefStream.egg-info/requires.txt +0 -0
  37. {defstream-0.1.13 → defstream-0.1.14}/DefStream.egg-info/top_level.txt +0 -0
  38. {defstream-0.1.13 → defstream-0.1.14}/LICENSE +0 -0
  39. {defstream-0.1.13 → defstream-0.1.14}/issues/bug/16/IMPLEMENTS.md +0 -0
  40. {defstream-0.1.13 → defstream-0.1.14}/issues/bug/16/ISSUE.md +0 -0
  41. {defstream-0.1.13 → defstream-0.1.14}/issues/bug/16/README.md +0 -0
  42. {defstream-0.1.13 → defstream-0.1.14}/issues/bug/16/REQUIREMENTS.md +0 -0
  43. {defstream-0.1.13 → defstream-0.1.14}/issues/feature/1/IMPLEMENTS.md +0 -0
  44. {defstream-0.1.13 → defstream-0.1.14}/issues/feature/1/README.md +0 -0
  45. {defstream-0.1.13 → defstream-0.1.14}/issues/feature/1/REQUIREMENTS.md +0 -0
  46. {defstream-0.1.13 → defstream-0.1.14}/issues/feature/1/TASK.md +0 -0
  47. {defstream-0.1.13 → defstream-0.1.14}/issues/feature/1/ci.refs.json +0 -0
  48. {defstream-0.1.13 → defstream-0.1.14}/issues/feature/8/REQUIREMENTS.md +0 -0
  49. {defstream-0.1.13 → defstream-0.1.14}/issues/feature/8/ci.refs.json +0 -0
  50. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/10/README.md +0 -0
  51. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/10/REQUIREMENTS.md +0 -0
  52. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/3/README.md +0 -0
  53. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/3/REQUIREMENTS.md +0 -0
  54. {defstream-0.1.13 → defstream-0.1.14}/issues/fix/3/ci.refs.json +0 -0
  55. {defstream-0.1.13/sample → defstream-0.1.14/samples}/Broker/Sample.py +0 -0
  56. {defstream-0.1.13/sample → defstream-0.1.14/samples}/Worker/RabbitMQ/Client.py +0 -0
  57. {defstream-0.1.13/sample → defstream-0.1.14/samples}/Worker/RabbitMQ/Tasks.py +0 -0
  58. {defstream-0.1.13/sample → defstream-0.1.14/samples}/Worker/RabbitMQ/Worker.py +0 -0
  59. {defstream-0.1.13/sample → defstream-0.1.14/samples}/Worker/Redis/Sample.py +0 -0
  60. {defstream-0.1.13/sample → defstream-0.1.14/samples}/Worker/Sample.py +0 -0
  61. {defstream-0.1.13 → defstream-0.1.14}/setup.cfg +0 -0
  62. {defstream-0.1.13 → defstream-0.1.14}/setup.py +0 -0
  63. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/Configuration.py +0 -0
  64. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/Connection.py +0 -0
  65. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/Consumer.py +0 -0
  66. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/Errors.py +0 -0
  67. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/Event.py +0 -0
  68. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/Implements/__init__.py +0 -0
  69. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/Policies.py +0 -0
  70. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/Priority.py +0 -0
  71. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/Protocol.py +0 -0
  72. {defstream-0.1.13 → defstream-0.1.14}/src/Broker/__init__.py +0 -0
  73. {defstream-0.1.13 → defstream-0.1.14}/src/Descriptor/DataObject.py +0 -0
  74. {defstream-0.1.13 → defstream-0.1.14}/src/Descriptor/Types.py +0 -0
  75. {defstream-0.1.13 → defstream-0.1.14}/src/Descriptor/Value.py +0 -0
  76. {defstream-0.1.13 → defstream-0.1.14}/src/Descriptor/__init__.py +0 -0
  77. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Extension.py +0 -0
  78. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Extensions/Console.html +0 -0
  79. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Extensions/Console.py +0 -0
  80. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Extensions/Dashboard.html +0 -0
  81. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Extensions/Dashboard.py +0 -0
  82. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Extensions/HealthCheck.py +0 -0
  83. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Extensions/Metrics.py +0 -0
  84. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Extensions/Specifications.py +0 -0
  85. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Extensions/__init__.py +0 -0
  86. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Inspector.py +0 -0
  87. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/Response.py +0 -0
  88. {defstream-0.1.13 → defstream-0.1.14}/src/Inspector/__init__.py +0 -0
  89. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Pattern.py +0 -0
  90. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/Array.py +0 -0
  91. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/Compare.py +0 -0
  92. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/Condition.py +0 -0
  93. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/DataObject.py +0 -0
  94. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/DateTime.py +0 -0
  95. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/Number.py +0 -0
  96. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/Object.py +0 -0
  97. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/Size.py +0 -0
  98. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/String.py +0 -0
  99. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Patterns/Type.py +0 -0
  100. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Validate.py +0 -0
  101. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/Validator.py +0 -0
  102. {defstream-0.1.13 → defstream-0.1.14}/src/Validator/__init__.py +0 -0
  103. {defstream-0.1.13 → defstream-0.1.14}/src/Worker/Executor.py +0 -0
  104. {defstream-0.1.13 → defstream-0.1.14}/src/Worker/Message.py +0 -0
  105. {defstream-0.1.13 → defstream-0.1.14}/src/Worker/Pool.py +0 -0
  106. {defstream-0.1.13 → defstream-0.1.14}/src/Worker/Run.py +0 -0
  107. {defstream-0.1.13 → defstream-0.1.14}/src/Worker/Specification.py +0 -0
  108. {defstream-0.1.13 → defstream-0.1.14}/src/Worker/Startup.py +0 -0
  109. {defstream-0.1.13 → defstream-0.1.14}/src/Worker/Worker.py +0 -0
  110. {defstream-0.1.13 → defstream-0.1.14}/src/Worker/__init__.py +0 -0
  111. {defstream-0.1.13 → defstream-0.1.14}/src/__init__.py +0 -0
  112. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_dashboard.py +0 -0
  113. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_dashboard_ui.cjs +0 -0
  114. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_descriptor.py +0 -0
  115. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_inspector.py +0 -0
  116. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_inspector_console.py +0 -0
  117. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_inspector_logging.py +0 -0
  118. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_lifecycle_hooks.py +0 -0
  119. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_pool_callbacks.py +0 -0
  120. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_process_signals.py +0 -0
  121. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_protocol_contracts.py +0 -0
  122. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_runner_hooks.py +0 -0
  123. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_specifications.py +0 -0
  124. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_task_messages.py +0 -0
  125. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_task_priorities.py +0 -0
  126. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_worker.py +0 -0
  127. {defstream-0.1.13/test → defstream-0.1.14/tests}/test_worker_startup.py +0 -0
@@ -1,6 +1,9 @@
1
1
  name: 기능 제안
2
- description: DefStream에 추가하거나 개선할 기능을 제안합니다.
3
- title: "[기능 제안] "
2
+ description: 추가하거나 개선할 기능을 제안합니다.
3
+ labels:
4
+ - feature
5
+ projects:
6
+ - yong5eon/7
4
7
  body:
5
8
  - type: markdown
6
9
  attributes:
@@ -1,6 +1,9 @@
1
1
  name: 버그/오류 제보
2
2
  description: 예상과 다르게 동작하거나 오류가 발생하는 문제를 제보합니다.
3
- title: "[버그] "
3
+ labels:
4
+ - bug
5
+ projects:
6
+ - yong5eon/7
4
7
  body:
5
8
  - type: markdown
6
9
  attributes:
@@ -0,0 +1,57 @@
1
+ name: 변경 제안
2
+ description: 일반 변경이나 핫픽스의 목적과 범위를 정리합니다.
3
+ labels:
4
+ - fix
5
+ projects:
6
+ - yong5eon/7
7
+ body:
8
+ - type: markdown
9
+ attributes:
10
+ value: >
11
+ 일반 변경이나 긴급 수정이 필요한 핫픽스를 제안할 때 사용해 주세요.
12
+ 재현 가능한 기존 결함은 버그/오류 제보 양식을 이용해 주세요.
13
+ - type: dropdown
14
+ id: change_type
15
+ attributes:
16
+ label: 변경 유형
17
+ description: 제안하는 변경의 성격을 선택해 주세요.
18
+ options:
19
+ - 일반 변경
20
+ - 핫픽스
21
+ validations:
22
+ required: true
23
+ - type: textarea
24
+ id: purpose
25
+ attributes:
26
+ label: 변경 목적
27
+ description: 왜 이 변경이 필요한지, 해결하려는 불편이나 상황을 설명해 주세요.
28
+ validations:
29
+ required: true
30
+ - type: textarea
31
+ id: proposed_changes
32
+ attributes:
33
+ label: 제안하는 변경 내용
34
+ description: 무엇을 어떻게 바꾸면 좋을지 적어 주세요.
35
+ validations:
36
+ required: true
37
+ - type: textarea
38
+ id: scope
39
+ attributes:
40
+ label: 영향 범위
41
+ description: 영향을 받는 기능, 사용자, 호환성 또는 설정을 알고 있다면 적어 주세요.
42
+ validations:
43
+ required: false
44
+ - type: textarea
45
+ id: hotfix_context
46
+ attributes:
47
+ label: 긴급 수정 정보
48
+ description: 핫픽스인 경우 긴급한 이유, 영향받는 버전 및 임시 대응 방법을 적어 주세요.
49
+ validations:
50
+ required: false
51
+ - type: textarea
52
+ id: acceptance_criteria
53
+ attributes:
54
+ label: 완료 기준
55
+ description: 변경이 완료되었다고 판단할 수 있는 조건이나 검증 방법을 적어 주세요.
56
+ validations:
57
+ required: false
@@ -0,0 +1,40 @@
1
+ name: 작업 제안
2
+ description: 리소스 변경, 문서화 등 소스 코드 변경이 아닌 작업을 정리합니다.
3
+ labels:
4
+ - task
5
+ projects:
6
+ - yong5eon/7
7
+ body:
8
+ - type: markdown
9
+ attributes:
10
+ value: >
11
+ 리소스 변경, 문서 작성·정리 등 소스 코드를 변경하지 않는 작업에 사용해 주세요.
12
+ 기능 추가, 버그 제보 또는 소스 코드 변경은 해당 이슈 양식을 이용해 주세요.
13
+ - type: textarea
14
+ id: purpose
15
+ attributes:
16
+ label: 작업 목적
17
+ description: 이 작업이 필요한 이유와 기대하는 효과를 설명해 주세요.
18
+ validations:
19
+ required: true
20
+ - type: textarea
21
+ id: scope
22
+ attributes:
23
+ label: 작업 내용 및 대상
24
+ description: 수행할 작업과 대상 리소스, 문서 또는 경로를 적어 주세요.
25
+ validations:
26
+ required: true
27
+ - type: textarea
28
+ id: acceptance_criteria
29
+ attributes:
30
+ label: 완료 기준
31
+ description: 작업이 완료되었다고 판단할 수 있는 결과물이나 확인 방법을 적어 주세요.
32
+ validations:
33
+ required: true
34
+ - type: textarea
35
+ id: additional
36
+ attributes:
37
+ label: 관련 자료 및 주의사항
38
+ description: 참고 링크, 관련 이슈, 사전 준비나 주의사항이 있다면 적어 주세요.
39
+ validations:
40
+ required: false
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## 변경 유형
6
6
 
7
- - [ ] 버그 수정
7
+ - [ ] 버그 수정 및 일반 변경, 핫픽스 처리
8
8
  - [ ] 기능 추가
9
9
  - [ ] 호환성에 영향을 주는 변경
10
10
  - [ ] 문서 또는 예제 변경
@@ -33,6 +33,7 @@ jobs:
33
33
  core_ref: ${{ steps.refs.outputs.core_ref }}
34
34
  rabbitmq_ref: ${{ steps.refs.outputs.rabbitmq_ref }}
35
35
  redis_ref: ${{ steps.refs.outputs.redis_ref }}
36
+ test_required: ${{ steps.test_paths.outputs.required }}
36
37
  steps:
37
38
  - uses: actions/checkout@v7
38
39
  with:
@@ -42,6 +43,28 @@ jobs:
42
43
  uses: actions/setup-python@v7
43
44
  with:
44
45
  python-version: '3.13'
46
+ - name: Detect test-relevant changes
47
+ id: test_paths
48
+ run: |
49
+ python - <<'PY'
50
+ from json import loads
51
+ from os import environ
52
+ from pathlib import Path
53
+ from subprocess import check_output
54
+
55
+ required = True
56
+ if environ['GITHUB_EVENT_NAME'] == 'pull_request':
57
+ event = loads(Path(environ['GITHUB_EVENT_PATH']).read_text(encoding='utf-8'))
58
+ base = event['pull_request']['base']['sha']
59
+ changed = check_output([
60
+ 'git', 'diff', '--name-only', '--no-renames', '-z',
61
+ f'{base}...{environ["GITHUB_SHA"]}', '--', 'samples/', 'src/', 'tests/', 'setup.py',
62
+ ])
63
+ required = bool(changed)
64
+ with Path(environ['GITHUB_OUTPUT']).open('a', encoding='utf-8') as output:
65
+ output.write(f'required={str(required).lower()}\n')
66
+ print(f'Test-relevant changes: {required}')
67
+ PY
45
68
  - name: Check complete push range and plan version
46
69
  id: changes
47
70
  env:
@@ -224,6 +247,7 @@ jobs:
224
247
  test:
225
248
  name: Test Python ${{ matrix.python }}
226
249
  needs: changes
250
+ if: needs.changes.outputs.test_required == 'true'
227
251
  runs-on: ubuntu-latest
228
252
  timeout-minutes: 25
229
253
  strategy:
@@ -384,8 +408,12 @@ jobs:
384
408
  - name: Test installed package
385
409
  run: |
386
410
  cd "$RUNNER_TEMP"
411
+ test_directory=test
412
+ if [ "$PROJECT" = "DefStream" ]; then
413
+ test_directory=tests
414
+ fi
387
415
  "$RUNNER_TEMP/installed/bin/python" -m unittest discover \
388
- -s "$GITHUB_WORKSPACE/packages/$PROJECT/test" -p 'test_*.py' -v
416
+ -s "$GITHUB_WORKSPACE/packages/$PROJECT/$test_directory" -p 'test_*.py' -v
389
417
 
390
418
  - name: Set up Node for core UI tests
391
419
  if: env.PROJECT == 'DefStream'
@@ -395,7 +423,7 @@ jobs:
395
423
 
396
424
  - name: Test core UI
397
425
  if: env.PROJECT == 'DefStream'
398
- run: node --test packages/DefStream/test/test_dashboard_ui.cjs
426
+ run: node --test packages/DefStream/tests/test_dashboard_ui.cjs
399
427
 
400
428
  - name: Preserve verified release distributions
401
429
  if: needs.changes.outputs.release == 'true' && matrix.python == '3.13'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DefStream
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Synchronous message processing with pluggable broker adapters
5
5
  Home-page: https://github.com/yong5eon/DefStream
6
6
  Author: Heo Yongseon
@@ -109,8 +109,8 @@ ProcessPool은 `spawn` 자식 프로세스를 사용하므로 부모의 핸들
109
109
  자식도 로그를 출력하려면 import 가능한 `PoolSetup` 구현에서 같은 설정을 적용하고
110
110
  `ProcessPool(..., setups=...)`로 전달하세요. 자식 초기화 훅이 실행되기 전의 DEBUG·INFO는
111
111
  그 설정으로 수신할 수 없습니다.
112
- [RabbitMQ 작업 예제의 LoggingSetup](./sample/Worker/RabbitMQ/Tasks.py)과
113
- [부모·자식 설정 예제](./sample/Worker/RabbitMQ/Worker.py)를 참고하세요.
112
+ [RabbitMQ 작업 예제의 LoggingSetup](./samples/Worker/RabbitMQ/Tasks.py)과
113
+ [부모·자식 설정 예제](./samples/Worker/RabbitMQ/Worker.py)를 참고하세요.
114
114
  기존 애플리케이션에서는 이미 설정한 핸들러를 반복 추가하지 않도록 초기화 위치를 관리해야 합니다.
115
115
 
116
116
  ## 설치
@@ -671,9 +671,9 @@ Console의 `broker` 인자는 `DefStream.Broker.BrokerFactory` 계약을 사용
671
671
 
672
672
  ## 예제와 라이선스
673
673
 
674
- - [브로커 없이 실행하는 Worker 예제](./sample/Worker/Sample.py)
675
- - [RabbitMQ Worker·RPC 클라이언트 예제](./sample/Worker/RabbitMQ)
676
- - [Redis Worker 예제](./sample/Worker/Redis/Sample.py)
674
+ - [브로커 없이 실행하는 Worker 예제](./samples/Worker/Sample.py)
675
+ - [RabbitMQ Worker·RPC 클라이언트 예제](./samples/Worker/RabbitMQ)
676
+ - [Redis Worker 예제](./samples/Worker/Redis/Sample.py)
677
677
  - [릴리즈와 변경 내역](https://github.com/yong5eon/DefStream/releases)
678
678
 
679
679
  GNU GPL v3 라이선스를 사용합니다. 자세한 조건은 [LICENSE](./LICENSE)를 확인하세요.
@@ -4,10 +4,11 @@ README.en.md
4
4
  README.md
5
5
  setup.py
6
6
  .github/PULL_REQUEST_TEMPLATE.md
7
- .github/ISSUE_TEMPLATE/bug_report.yml
7
+ .github/ISSUE_TEMPLATE/01.feature.yml
8
+ .github/ISSUE_TEMPLATE/02.bug.yml
9
+ .github/ISSUE_TEMPLATE/03.fix.yml
10
+ .github/ISSUE_TEMPLATE/04.task.yml
8
11
  .github/ISSUE_TEMPLATE/config.yml
9
- .github/ISSUE_TEMPLATE/feature_request.yml
10
- .github/PULL_REQUEST_TEMPLATE/hotfix.md
11
12
  .github/workflows/ci.yml
12
13
  DefStream.egg-info/PKG-INFO
13
14
  DefStream.egg-info/SOURCES.txt
@@ -31,6 +32,10 @@ issues/fix/10/IMPLEMENTS.md
31
32
  issues/fix/10/README.md
32
33
  issues/fix/10/REQUIREMENTS.md
33
34
  issues/fix/13/README.md
35
+ issues/fix/21/IMPLEMENTS.md
36
+ issues/fix/21/ISSUE.md
37
+ issues/fix/21/README.md
38
+ issues/fix/21/REQUIREMENTS.md
34
39
  issues/fix/3/IMPLEMENTS.md
35
40
  issues/fix/3/README.md
36
41
  issues/fix/3/REQUIREMENTS.md
@@ -38,12 +43,15 @@ issues/fix/3/ci.refs.json
38
43
  issues/fix/6/IMPLEMENTS.md
39
44
  issues/fix/6/README.md
40
45
  issues/fix/6/REQUIREMENTS.md
41
- sample/Broker/Sample.py
42
- sample/Worker/Sample.py
43
- sample/Worker/RabbitMQ/Client.py
44
- sample/Worker/RabbitMQ/Tasks.py
45
- sample/Worker/RabbitMQ/Worker.py
46
- sample/Worker/Redis/Sample.py
46
+ issues/task/18/IMPLEMENTS.md
47
+ issues/task/18/README.md
48
+ issues/task/18/REQUIREMENTS.md
49
+ samples/Broker/Sample.py
50
+ samples/Worker/Sample.py
51
+ samples/Worker/RabbitMQ/Client.py
52
+ samples/Worker/RabbitMQ/Tasks.py
53
+ samples/Worker/RabbitMQ/Worker.py
54
+ samples/Worker/Redis/Sample.py
47
55
  src/__init__.py
48
56
  src/Broker/Configuration.py
49
57
  src/Broker/Connection.py
@@ -95,21 +103,22 @@ src/Worker/Specification.py
95
103
  src/Worker/Startup.py
96
104
  src/Worker/Worker.py
97
105
  src/Worker/__init__.py
98
- test/test_dashboard.py
99
- test/test_dashboard_ui.cjs
100
- test/test_descriptor.py
101
- test/test_inspector.py
102
- test/test_inspector_console.py
103
- test/test_inspector_logging.py
104
- test/test_lifecycle_hooks.py
105
- test/test_logging.py
106
- test/test_pool_callbacks.py
107
- test/test_process_signals.py
108
- test/test_protocol_contracts.py
109
- test/test_release_workflow.py
110
- test/test_runner_hooks.py
111
- test/test_specifications.py
112
- test/test_task_messages.py
113
- test/test_task_priorities.py
114
- test/test_worker.py
115
- test/test_worker_startup.py
106
+ tests/test_dashboard.py
107
+ tests/test_dashboard_ui.cjs
108
+ tests/test_descriptor.py
109
+ tests/test_inspector.py
110
+ tests/test_inspector_console.py
111
+ tests/test_inspector_logging.py
112
+ tests/test_lifecycle_hooks.py
113
+ tests/test_logging.py
114
+ tests/test_pool_callbacks.py
115
+ tests/test_process_signals.py
116
+ tests/test_protocol_contracts.py
117
+ tests/test_release_workflow.py
118
+ tests/test_runner_hooks.py
119
+ tests/test_specifications.py
120
+ tests/test_task_messages.py
121
+ tests/test_task_priorities.py
122
+ tests/test_validator_patterns.py
123
+ tests/test_worker.py
124
+ tests/test_worker_startup.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DefStream
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Synchronous message processing with pluggable broker adapters
5
5
  Home-page: https://github.com/yong5eon/DefStream
6
6
  Author: Heo Yongseon
@@ -109,8 +109,8 @@ ProcessPool은 `spawn` 자식 프로세스를 사용하므로 부모의 핸들
109
109
  자식도 로그를 출력하려면 import 가능한 `PoolSetup` 구현에서 같은 설정을 적용하고
110
110
  `ProcessPool(..., setups=...)`로 전달하세요. 자식 초기화 훅이 실행되기 전의 DEBUG·INFO는
111
111
  그 설정으로 수신할 수 없습니다.
112
- [RabbitMQ 작업 예제의 LoggingSetup](./sample/Worker/RabbitMQ/Tasks.py)과
113
- [부모·자식 설정 예제](./sample/Worker/RabbitMQ/Worker.py)를 참고하세요.
112
+ [RabbitMQ 작업 예제의 LoggingSetup](./samples/Worker/RabbitMQ/Tasks.py)과
113
+ [부모·자식 설정 예제](./samples/Worker/RabbitMQ/Worker.py)를 참고하세요.
114
114
  기존 애플리케이션에서는 이미 설정한 핸들러를 반복 추가하지 않도록 초기화 위치를 관리해야 합니다.
115
115
 
116
116
  ## 설치
@@ -671,9 +671,9 @@ Console의 `broker` 인자는 `DefStream.Broker.BrokerFactory` 계약을 사용
671
671
 
672
672
  ## 예제와 라이선스
673
673
 
674
- - [브로커 없이 실행하는 Worker 예제](./sample/Worker/Sample.py)
675
- - [RabbitMQ Worker·RPC 클라이언트 예제](./sample/Worker/RabbitMQ)
676
- - [Redis Worker 예제](./sample/Worker/Redis/Sample.py)
674
+ - [브로커 없이 실행하는 Worker 예제](./samples/Worker/Sample.py)
675
+ - [RabbitMQ Worker·RPC 클라이언트 예제](./samples/Worker/RabbitMQ)
676
+ - [Redis Worker 예제](./samples/Worker/Redis/Sample.py)
677
677
  - [릴리즈와 변경 내역](https://github.com/yong5eon/DefStream/releases)
678
678
 
679
679
  GNU GPL v3 라이선스를 사용합니다. 자세한 조건은 [LICENSE](./LICENSE)를 확인하세요.
@@ -27,15 +27,66 @@ Configure execution, acknowledgements, failure policies, and monitoring without
27
27
  - [Lifecycle hooks](#lifecycle-hooks)
28
28
  - [Request/reply contracts and validation](#requestreply-contracts-and-validation)
29
29
  - [Health checks and task console](#health-checks-and-task-console)
30
+ - [Logging](#logging)
30
31
  - [Broker selection and operational considerations](#broker-selection-and-operational-considerations)
31
32
  - [Examples and license](#examples-and-license)
32
33
 
33
- ## 로깅 지원
34
+ ## Logging
34
35
 
35
- 클래스별 표준 로거와 사용자 주도 핸들러 설정을 지원합니다.
36
- 샘플은 `Sample` 및 `DefStream` 계층만 출력하며, ProcessPool 자식은 별도 setup 훅으로
37
- 출력을 설정합니다. Worker의 시작 배너는 유지됩니다.
38
- 로거 이름, 선택 수신, 예외 메시지 주의사항과 프로세스 설정은 [로깅 안내](./README.md#로깅)를 참고하세요.
36
+ DefStream records messages through standard `logging` without configuring handlers,
37
+ formatters, or levels. Select the sources your application needs. The samples
38
+ output only the `Sample` and `DefStream` logger hierarchies, replacing their
39
+ `print()` calls with logging. The Worker's existing startup banner is still
40
+ printed regardless of logging configuration.
41
+
42
+ ```python
43
+ from logging import DEBUG, Filter, Formatter, StreamHandler, getLogger
44
+
45
+ sources = (Filter('Sample'), Filter('DefStream'))
46
+ handler = StreamHandler()
47
+ handler.setFormatter(Formatter(
48
+ '%(asctime)s %(processName)s %(name)s %(levelname)s %(message)s'
49
+ ))
50
+ handler.addFilter(lambda record: any(source.filter(record) for source in sources))
51
+ root = getLogger()
52
+ root.setLevel(DEBUG)
53
+ root.addHandler(handler)
54
+
55
+ logger = getLogger('Sample')
56
+ logger.info('Application started')
57
+ ```
58
+
59
+ Apply filters to the **handler**, not the root logger. `Filter('DefStream')`
60
+ includes `DefStream` and names below `DefStream.`, but not `DefStreamExtra`.
61
+ This configuration is an example for a standalone application; it does not change
62
+ output from other handlers already installed. To receive only broker logs, select
63
+ `DefStream.Broker.Implements.RabbitMQ` or `DefStream.Broker.Implements.Redis`
64
+ instead of `DefStream`.
65
+
66
+ - The class member `logger` uses `DefStream.Worker`, `DefStream.Run`,
67
+ `DefStream.Executor`, `DefStream.Pool`, `DefStream.ThreadPool`, and
68
+ `DefStream.ProcessPool`.
69
+ - Shared Pool methods also use the concrete pool's logger member. User subclasses
70
+ inherit the default name and can explicitly override `logger`.
71
+ - Inspector, Server, and RequestHandler share `DefStream.Inspector`.
72
+ HTTP request receipt is DEBUG; response status and transmission results are INFO.
73
+ Extension-specific logs use `DefStream.Inspector.Extensions.<extension name>`.
74
+ - Normal per-message processing is DEBUG, lifecycle events are INFO, events
75
+ requiring attention are WARNING, and processing failures are ERROR.
76
+ Bodies, results, and credentials are excluded from the default log context.
77
+ - Without any handlers, Python's `lastResort` may write WARNING and higher to
78
+ stderr. Do not put sensitive values in user exception messages: failure
79
+ tracebacks include those messages.
80
+
81
+ ProcessPool uses `spawn` child processes, so parent handlers are not automatically
82
+ inherited. To receive child logs, apply the same configuration in an importable
83
+ `PoolSetup` implementation and pass it through `ProcessPool(..., setups=...)`.
84
+ That configuration cannot capture DEBUG and INFO logs emitted before the child
85
+ setup hook runs. See
86
+ [LoggingSetup in the RabbitMQ task example](./samples/Worker/RabbitMQ/Tasks.py)
87
+ and the [parent/child configuration example](./samples/Worker/RabbitMQ/Worker.py).
88
+ In existing applications, choose the initialization location carefully to avoid
89
+ adding the same handlers repeatedly.
39
90
 
40
91
  ## Installation
41
92
 
@@ -610,9 +661,9 @@ For a class-based factory, you can inherit `BrokerFactory` and implement `__call
610
661
 
611
662
  ## Examples and license
612
663
 
613
- - [Worker without an external broker](./sample/Worker/Sample.py)
614
- - [RabbitMQ Worker and RPC client](./sample/Worker/RabbitMQ)
615
- - [Redis Worker](./sample/Worker/Redis/Sample.py)
664
+ - [Worker without an external broker](./samples/Worker/Sample.py)
665
+ - [RabbitMQ Worker and RPC client](./samples/Worker/RabbitMQ)
666
+ - [Redis Worker](./samples/Worker/Redis/Sample.py)
616
667
  - [Releases and change history](https://github.com/yong5eon/DefStream/releases)
617
668
 
618
669
  Licensed under GNU GPL v3. See [LICENSE](./LICENSE) for the terms.
@@ -76,8 +76,8 @@ ProcessPool은 `spawn` 자식 프로세스를 사용하므로 부모의 핸들
76
76
  자식도 로그를 출력하려면 import 가능한 `PoolSetup` 구현에서 같은 설정을 적용하고
77
77
  `ProcessPool(..., setups=...)`로 전달하세요. 자식 초기화 훅이 실행되기 전의 DEBUG·INFO는
78
78
  그 설정으로 수신할 수 없습니다.
79
- [RabbitMQ 작업 예제의 LoggingSetup](./sample/Worker/RabbitMQ/Tasks.py)과
80
- [부모·자식 설정 예제](./sample/Worker/RabbitMQ/Worker.py)를 참고하세요.
79
+ [RabbitMQ 작업 예제의 LoggingSetup](./samples/Worker/RabbitMQ/Tasks.py)과
80
+ [부모·자식 설정 예제](./samples/Worker/RabbitMQ/Worker.py)를 참고하세요.
81
81
  기존 애플리케이션에서는 이미 설정한 핸들러를 반복 추가하지 않도록 초기화 위치를 관리해야 합니다.
82
82
 
83
83
  ## 설치
@@ -638,9 +638,9 @@ Console의 `broker` 인자는 `DefStream.Broker.BrokerFactory` 계약을 사용
638
638
 
639
639
  ## 예제와 라이선스
640
640
 
641
- - [브로커 없이 실행하는 Worker 예제](./sample/Worker/Sample.py)
642
- - [RabbitMQ Worker·RPC 클라이언트 예제](./sample/Worker/RabbitMQ)
643
- - [Redis Worker 예제](./sample/Worker/Redis/Sample.py)
641
+ - [브로커 없이 실행하는 Worker 예제](./samples/Worker/Sample.py)
642
+ - [RabbitMQ Worker·RPC 클라이언트 예제](./samples/Worker/RabbitMQ)
643
+ - [Redis Worker 예제](./samples/Worker/Redis/Sample.py)
644
644
  - [릴리즈와 변경 내역](https://github.com/yong5eon/DefStream/releases)
645
645
 
646
646
  GNU GPL v3 라이선스를 사용합니다. 자세한 조건은 [LICENSE](./LICENSE)를 확인하세요.
@@ -62,11 +62,11 @@ HealthCheck는 probe 예약·응답·판정과 응답 생성을 DEBUG로 기록
62
62
  - 런타임 로그 메시지는 영어로 작성하고 한국어 문서·주석은 유지한다.
63
63
  - 본문·결과·인증정보를 기본 문맥에 넣지 않는다. 단, Worker의 작업 실패
64
64
  traceback에는 사용자 예외 메시지가 포함될 수 있으므로 사용자가 주의해야 한다.
65
- - [Worker 샘플](../../../sample/Worker)과 [Broker 샘플](../../../sample/Broker)은
65
+ - [Worker 샘플](../../../samples/Worker)과 [Broker 샘플](../../../samples/Broker)은
66
66
  `Sample`과 `DefStream` 계층만 핸들러 필터로 선택한다.
67
67
  - 샘플의 `print()`는 로그로 전환하고 라이브러리 시작 배너는 유지했다.
68
- - [RabbitMQ Worker 샘플](../../../sample/Worker/RabbitMQ/Worker.py)은 부모 진입점과
69
- ProcessPool의 `setups`에 [LoggingSetup](../../../sample/Worker/RabbitMQ/Tasks.py)을
68
+ - [RabbitMQ Worker 샘플](../../../samples/Worker/RabbitMQ/Worker.py)은 부모 진입점과
69
+ ProcessPool의 `setups`에 [LoggingSetup](../../../samples/Worker/RabbitMQ/Tasks.py)을
70
70
  적용한다. `spawn` 자식이 부모의 핸들러를 자동 상속한다고 가정하지 않는다.
71
71
 
72
72
  ## 6. 검증 결과
@@ -81,9 +81,9 @@ Python 3.13.16에서 실제 RabbitMQ·Redis가 실행 중인 환경으로 검증
81
81
  | 세 프로젝트 소스·샘플의 로그 및 observe 호출 문자열 | 376곳 점검, 한국어 문자열 없음 |
82
82
  | 세 프로젝트 소스·샘플 `compileall` 및 `git diff --check` | 통과 |
83
83
 
84
- [로깅 테스트](../../../test/test_logging.py)는 클래스 멤버·상속·사용자 재정의,
84
+ [로깅 테스트](../../../tests/test_logging.py)는 클래스 멤버·상속·사용자 재정의,
85
85
  레벨·본문 제외·작업 실패 traceback 중복 방지·배너 유지·샘플 필터·spawn 설정을
86
- 검증한다. [Inspector 로깅 테스트](../../../test/test_inspector_logging.py)는 실제 HTTP
86
+ 검증한다. [Inspector 로깅 테스트](../../../tests/test_inspector_logging.py)는 실제 HTTP
87
87
  요청에서 수신·응답 각 1회 기록, 쿼리·본문 제외, WSGI 500과 전송 중단을 검증한다.
88
88
  기존 테스트의 이전 모듈 로거 기대값도 새 이름과 레벨에 맞췄다.
89
89
 
@@ -34,7 +34,7 @@ Pool 공통 동작도 실제 ThreadPool·ProcessPool의 로거 이름으로 기
34
34
  ## 샘플 변경
35
35
 
36
36
  샘플은 `print()` 대신 `Sample` 로거를 사용하며 `Sample`과 `DefStream` 계층만
37
- 출력합니다. [RabbitMQ Worker 샘플](../../../sample/Worker/RabbitMQ/Worker.py)은
38
- 부모와 spawn 자식에 [LoggingSetup](../../../sample/Worker/RabbitMQ/Tasks.py)을 적용합니다.
37
+ 출력합니다. [RabbitMQ Worker 샘플](../../../samples/Worker/RabbitMQ/Worker.py)은
38
+ 부모와 spawn 자식에 [LoggingSetup](../../../samples/Worker/RabbitMQ/Tasks.py)을 적용합니다.
39
39
  실제 애플리케이션도 ProcessPool 자식의 로깅을 별도로 설정해야 합니다.
40
40
  설정 예제는 [사용 설명서](../../../README.md#로깅)를 참고하세요.
@@ -29,8 +29,8 @@
29
29
  - [Specification](../../../src/Worker/Specification.py)에서 `description` 인자와
30
30
  AsyncAPI 메시지 설명 생성을 제거했다. schema, validator, example, format은
31
31
  유지한다.
32
- - [RabbitMQ Worker 샘플](../../../sample/Worker/RabbitMQ/Tasks.py)과
33
- [Redis Worker 샘플](../../../sample/Worker/Redis/Sample.py)에 `add`, `divide`
32
+ - [RabbitMQ Worker 샘플](../../../samples/Worker/RabbitMQ/Tasks.py)과
33
+ [Redis Worker 샘플](../../../samples/Worker/Redis/Sample.py)에 `add`, `divide`
34
34
  이름 및 작업 설명을 지정했다. RabbitMQ 샘플의 request·response
35
35
  Specification 설명은 Run의 메타데이터로 옮겼다.
36
36
  - [한국어 사용 설명서](../../../README.md)와
@@ -117,7 +117,7 @@ Python 3.9.13과 3.10.11에서 클래스 정의의 `name=` 키워드가
117
117
  처리는 계속 `Run.__init_subclass__`가 담당한다. 사용자 클래스 정의 문법은
118
118
  변경하지 않으며 `ABCMeta`의 추상 메서드 검사도 유지한다.
119
119
 
120
- [Worker 테스트](../../../test/test_worker.py)에 이름을 지정한 추상 Run의 생성 거부,
120
+ [Worker 테스트](../../../tests/test_worker.py)에 이름을 지정한 추상 Run의 생성 거부,
121
121
  구체 하위 Run의 이름 재정의 및 실행을 검증하는 회귀 테스트를 추가했다.
122
122
 
123
123
  | Python | 전체 코어 테스트 결과 |
@@ -8,7 +8,7 @@ Worker 시작 배너를 로깅 설정 여부와 관계없이 먼저 확인할
8
8
 
9
9
  ## 변경 내용
10
10
 
11
- - [Worker 샘플](../../../sample/Worker/Sample.py)은 `configure_logging()`을
11
+ - [Worker 샘플](../../../samples/Worker/Sample.py)은 `configure_logging()`을
12
12
  실행 진입점에서 호출하는 대신 `SetupLogger` setup 훅에서 수행합니다.
13
13
  Worker 생성·작업 등록 시점의 로그는 아직 설정되지 않은 핸들러에 의해 출력되지
14
14
  않으며, 시작 배너가 샘플의 첫 화면 출력이 됩니다.
@@ -0,0 +1,38 @@
1
+ # Validator `IsOptional` 변경 구현 기록
2
+
3
+ ## 1. 구현 개요
4
+
5
+ [요구사항 명세서](./REQUIREMENTS.md)에 따라 Validator 패턴의 공개 이름을
6
+ `IsToNone`에서 `IsOptional`로 변경했다. 패턴의 입력 처리와 후속 패턴 실행
7
+ 동작은 유지하고 기존 이름은 제거했다.
8
+
9
+ - 구현·검증일: 2026-10-21
10
+ - 사용자 관점의 변경 요약: 선택적 값을 처리하는 Validator 패턴 이름을 `IsOptional`로 통일
11
+
12
+ ## 2. 변경 내용
13
+
14
+ - [Common.py](../../../src/Validator/Patterns/Common.py)에서 클래스를
15
+ `IsToNone`에서 `IsOptional`로 변경하고 모듈의 `__all__`을 갱신했다.
16
+ - [Patterns/__init__.py](../../../src/Validator/Patterns/__init__.py)에서
17
+ `IsOptional`을 가져오고 패키지 `__all__`에 공개했다.
18
+ - 기존 클래스명과 호환 별칭은 제공하지 않는다. 기존 `IsToNone`을 import하는
19
+ 코드는 `IsOptional`로 변경해야 한다.
20
+ - `None`이면 후속 패턴을 실행하지 않고 그대로 반환하며, 그 외 값에는 전달된
21
+ 패턴을 순서대로 적용한다. `repr`은 새 클래스명을 표시한다.
22
+ - [test_validator_patterns.py](../../../tests/test_validator_patterns.py)에
23
+ `None` 처리, 패턴 적용 순서, 예외 전파, 공개 export 및 표현 결과를 확인하는
24
+ 테스트 5개를 추가했다.
25
+
26
+ ## 3. 검증 결과
27
+
28
+ | 검증 항목 | 결과 |
29
+ |---|---|
30
+ | 신규 Validator 패턴 단위 테스트 | 5개 통과 |
31
+ | 테스트 파일 직접 실행 도구 | 테스트를 찾지 못함 |
32
+ | Python unittest 직접 실행 | 통과. 저장소의 `src` 패키지를 `DefStream` 이름으로 등록해 테스트 실행 |
33
+ | 기존 이름 검색 | 소스의 구현 및 공개 export에서 제거 확인. 테스트는 미노출을 검증하기 위해 이름을 참조 |
34
+
35
+ 테스트는 `python -m unittest discover`의 기본 import 방식으로는 저장소 패키지
36
+ 이름을 찾지 못해 실행되지 않았다. 테스트 코드에서 현재 소스 패키지를
37
+ `DefStream`으로 등록한 뒤 unittest discovery를 실행하여 검증했다. 다른 테스트
38
+ 파일이나 전체 테스트 스위트는 실행하지 않았다.
@@ -0,0 +1,17 @@
1
+ ### 변경 유형
2
+
3
+ 일반 변경
4
+
5
+ ### 변경 목적
6
+
7
+ Validator 패턴 중 IsToNone 의 이름이 파이썬에서 사용하는 Optional 처럼 동작해야 해서, 좀더 친숙한 이름으로 변경을 제안
8
+
9
+ ### 제안하는 변경 내용
10
+
11
+ 이름 변경 후보:
12
+
13
+ - Optional: Python 에서 Optional 을 많이 사용하여 사용 시 알리아스가 필요
14
+ - IsOptional
15
+ - ToOptional
16
+ - AsOptional
17
+ - OfOptional
@@ -0,0 +1,44 @@
1
+ # Validator 패턴 이름을 `IsOptional`로 변경
2
+
3
+ ## 변경 요약
4
+
5
+ Validator 패턴의 이름을 `IsToNone`에서 `IsOptional`로 변경했습니다.
6
+ `None` 입력은 그대로 반환하고, 그 외 입력에는 지정한 패턴을 순서대로 적용합니다.
7
+ 처리 동작은 달라지지 않았으며 이름만 더 알아보기 쉽게 바뀌었습니다.
8
+
9
+ ## 기존 코드에서 바꿀 부분
10
+
11
+ 기존 `IsToNone` import와 사용 코드를 `IsOptional`로 변경하세요.
12
+ `IsToNone`은 호환 별칭으로 제공되지 않습니다.
13
+
14
+ 변경 전:
15
+
16
+ ```python
17
+ from DefStream.Validator.Patterns import IsToNone
18
+
19
+ validator = IsToNone(pattern)
20
+ ```
21
+
22
+ 변경 후:
23
+
24
+ ```python
25
+ from DefStream.Validator.Patterns import IsOptional
26
+
27
+ validator = IsOptional(pattern)
28
+ ```
29
+
30
+ `IsOptional`은 Python의 `typing.Optional`이 아니라 런타임 입력값을 처리하는
31
+ Validator 패턴입니다.
32
+
33
+ ## 검증
34
+
35
+ - `None` 입력 시 후속 패턴을 실행하지 않고 `None` 반환
36
+ - 비`None` 입력 시 후속 패턴을 순서대로 실행
37
+ - 후속 패턴의 예외 전파 및 `repr` 결과 확인
38
+ - 패키지 공개 export와 기존 이름 미노출 확인
39
+ - 신규 단위 테스트 5개 통과
40
+
41
+ ## 상세 기록
42
+
43
+ - [요구사항 명세서](./REQUIREMENTS.md)
44
+ - [구현 및 검증 기록](./IMPLEMENTS.md)