DefStream 0.1.10__tar.gz → 0.1.11__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.
- {defstream-0.1.10 → defstream-0.1.11}/DefStream.egg-info/PKG-INFO +3 -1
- {defstream-0.1.10 → defstream-0.1.11}/PKG-INFO +3 -1
- {defstream-0.1.10 → defstream-0.1.11}/README.en.md +2 -0
- {defstream-0.1.10 → defstream-0.1.11}/README.md +2 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/10/IMPLEMENTS.md +17 -2
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/10/README.md +4 -2
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/10/REQUIREMENTS.md +2 -2
- {defstream-0.1.10 → defstream-0.1.11}/src/Worker/Run.py +6 -3
- {defstream-0.1.10 → defstream-0.1.11}/test/test_inspector.py +16 -2
- {defstream-0.1.10 → defstream-0.1.11}/test/test_specifications.py +35 -5
- {defstream-0.1.10 → defstream-0.1.11}/.github/workflows/ci.yml +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/.gitignore +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/DefStream.egg-info/SOURCES.txt +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/DefStream.egg-info/dependency_links.txt +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/DefStream.egg-info/requires.txt +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/DefStream.egg-info/top_level.txt +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/LICENSE +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/feature/1/IMPLEMENTS.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/feature/1/README.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/feature/1/REQUIREMENTS.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/feature/1/TASK.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/feature/1/ci.refs.json +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/feature/8/IMPLEMENTS.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/feature/8/README.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/feature/8/REQUIREMENTS.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/feature/8/ci.refs.json +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/3/IMPLEMENTS.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/3/README.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/3/REQUIREMENTS.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/3/ci.refs.json +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/6/IMPLEMENTS.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/6/README.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/issues/fix/6/REQUIREMENTS.md +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/sample/Broker/Sample.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/sample/Worker/RabbitMQ/Client.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/sample/Worker/RabbitMQ/Tasks.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/sample/Worker/RabbitMQ/Worker.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/sample/Worker/Redis/Sample.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/sample/Worker/Sample.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/setup.cfg +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/setup.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/Configuration.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/Connection.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/Consumer.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/Errors.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/Event.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/Implements/__init__.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/Policies.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/Priority.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/Protocol.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Broker/__init__.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Descriptor/DataObject.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Descriptor/Types.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Descriptor/Value.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Descriptor/__init__.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Extension.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Extensions/Console.html +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Extensions/Console.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Extensions/Dashboard.html +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Extensions/Dashboard.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Extensions/HealthCheck.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Extensions/Metrics.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Extensions/Specifications.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Extensions/__init__.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Inspector.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/Response.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Inspector/__init__.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Pattern.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/Array.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/Common.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/Compare.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/Condition.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/DataObject.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/DateTime.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/Number.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/Object.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/Size.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/String.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/Type.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Patterns/__init__.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Validate.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/Validator.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Validator/__init__.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Worker/Executor.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Worker/Message.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Worker/Pool.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Worker/Specification.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Worker/Startup.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Worker/Worker.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/Worker/__init__.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/src/__init__.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_dashboard.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_dashboard_ui.cjs +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_descriptor.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_inspector_console.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_inspector_logging.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_lifecycle_hooks.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_logging.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_pool_callbacks.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_process_signals.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_protocol_contracts.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_release_workflow.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_runner_hooks.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_task_messages.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_task_priorities.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_worker.py +0 -0
- {defstream-0.1.10 → defstream-0.1.11}/test/test_worker_startup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DefStream
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: Synchronous message processing with pluggable broker adapters
|
|
5
5
|
Home-page: https://github.com/yong5eon/DefStream
|
|
6
6
|
Author: Heo Yongseon
|
|
@@ -571,6 +571,8 @@ class Add(
|
|
|
571
571
|
- `format`의 기본값은 `application/json`입니다. 문서의 미디어 타입만 바꾸며 브로커 코덱은 별도 설정입니다.
|
|
572
572
|
- Run의 `name`은 AsyncAPI Operation 키와 Metrics 작업 키에 함께 사용합니다.
|
|
573
573
|
생략하면 `모듈명.__qualname__`을 사용하며, Run의 `description`은 AsyncAPI Operation에 표시됩니다.
|
|
574
|
+
명시적 이름은 `[A-Za-z0-9_.-]+` 형식이며 공백·한국어는 허용하지 않습니다.
|
|
575
|
+
대소문자와 입력값을 그대로 보존합니다.
|
|
574
576
|
- 실행 순서는 초기화 훅 → 요청 검증 → `run()` → 응답 검증 → 완료 훅 → 응답 발행·ACK입니다.
|
|
575
577
|
|
|
576
578
|
검증 예외는 작업 실패로 처리되어 오류 훅과 `on_error` 정책을 따릅니다.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: DefStream
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: Synchronous message processing with pluggable broker adapters
|
|
5
5
|
Home-page: https://github.com/yong5eon/DefStream
|
|
6
6
|
Author: Heo Yongseon
|
|
@@ -571,6 +571,8 @@ class Add(
|
|
|
571
571
|
- `format`의 기본값은 `application/json`입니다. 문서의 미디어 타입만 바꾸며 브로커 코덱은 별도 설정입니다.
|
|
572
572
|
- Run의 `name`은 AsyncAPI Operation 키와 Metrics 작업 키에 함께 사용합니다.
|
|
573
573
|
생략하면 `모듈명.__qualname__`을 사용하며, Run의 `description`은 AsyncAPI Operation에 표시됩니다.
|
|
574
|
+
명시적 이름은 `[A-Za-z0-9_.-]+` 형식이며 공백·한국어는 허용하지 않습니다.
|
|
575
|
+
대소문자와 입력값을 그대로 보존합니다.
|
|
574
576
|
- 실행 순서는 초기화 훅 → 요청 검증 → `run()` → 응답 검증 → 완료 훅 → 응답 발행·ACK입니다.
|
|
575
577
|
|
|
576
578
|
검증 예외는 작업 실패로 처리되어 오류 훅과 `on_error` 정책을 따릅니다.
|
|
@@ -507,6 +507,8 @@ class Add(
|
|
|
507
507
|
- `format` defaults to `application/json`. It changes documented media types, not broker codecs.
|
|
508
508
|
- Run's `name` is used as both the AsyncAPI Operation key and the Metrics task key.
|
|
509
509
|
When omitted, `module.__qualname__` is used; Run's `description` appears on the AsyncAPI Operation.
|
|
510
|
+
Explicit names must match `[A-Za-z0-9_.-]+`; whitespace and Korean characters are not allowed.
|
|
511
|
+
Names are case-sensitive and preserved exactly as provided.
|
|
510
512
|
- Execution order: setup hooks → request validation → `run()` → response validation → completion hooks → reply/ACK.
|
|
511
513
|
|
|
512
514
|
Validation exceptions are task failures and follow error hooks and the `on_error`
|
|
@@ -538,6 +538,8 @@ class Add(
|
|
|
538
538
|
- `format`의 기본값은 `application/json`입니다. 문서의 미디어 타입만 바꾸며 브로커 코덱은 별도 설정입니다.
|
|
539
539
|
- Run의 `name`은 AsyncAPI Operation 키와 Metrics 작업 키에 함께 사용합니다.
|
|
540
540
|
생략하면 `모듈명.__qualname__`을 사용하며, Run의 `description`은 AsyncAPI Operation에 표시됩니다.
|
|
541
|
+
명시적 이름은 `[A-Za-z0-9_.-]+` 형식이며 공백·한국어는 허용하지 않습니다.
|
|
542
|
+
대소문자와 입력값을 그대로 보존합니다.
|
|
541
543
|
- 실행 순서는 초기화 훅 → 요청 검증 → `run()` → 응답 검증 → 완료 훅 → 응답 발행·ACK입니다.
|
|
542
544
|
|
|
543
545
|
검증 예외는 작업 실패로 처리되어 오류 훅과 `on_error` 정책을 따릅니다.
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
|
|
14
14
|
- [Run](../../../src/Worker/Run.py) 클래스 정의에서 `name`과 `description`
|
|
15
15
|
키워드를 받는다. `RunMeta`가 이름을 내부 키워드 `run_name`으로 중계하고,
|
|
16
|
-
`__init_subclass__`가 이름과 설명을 처리한다. 이름은
|
|
17
|
-
|
|
16
|
+
`__init_subclass__`가 이름과 설명을 처리한다. 명시적 이름은
|
|
17
|
+
`[A-Za-z0-9_.-]+` 전체 일치로 검사하고, 설명은 문자열 또는 `None`으로 검증한다.
|
|
18
18
|
- 클래스 생성 시 이름이 생략되면 해당 클래스의 `__module__`과 `__qualname__`으로
|
|
19
19
|
기본 이름을 만든다. 하위 Run에는 상위 Run의 이름과 설명을 상속하지 않는다.
|
|
20
20
|
- [Worker](../../../src/Worker/Worker.py)는 Run 등록 시 동일한 이름이 이미
|
|
@@ -133,3 +133,18 @@ Python 3.9.13과 3.10.11에서 클래스 정의의 `name=` 키워드가
|
|
|
133
133
|
Python 3.9의 첫 전체 실행에서는 무관한 Console HTTP 테스트에 Windows
|
|
134
134
|
`ConnectionAbortedError`가 발생했으나, 해당 테스트의 단독 실행과 전체 재실행은
|
|
135
135
|
통과했다. 변경한 Run 소스의 Pylance 진단에도 오류가 없다.
|
|
136
|
+
|
|
137
|
+
## 7. 명시적 이름의 허용 문자 변경
|
|
138
|
+
|
|
139
|
+
Python 식별자 제한을 제거하고 영문 대소문자·숫자·`_`·`.`·`-`를 하나 이상
|
|
140
|
+
허용하도록 변경했다. 위치 제한은 없으며 Python 예약어도 허용한다.
|
|
141
|
+
빈 문자열, 공백·개행, 한국어 등 다른 문자는 거부한다.
|
|
142
|
+
대소문자 변환이나 공백 제거 없이 지정한 이름을 그대로 사용한다.
|
|
143
|
+
미지정 이름의 `모듈명.__qualname__` 생성 규칙과 중복 검사는 유지한다.
|
|
144
|
+
|
|
145
|
+
허용·거부 문자 조합과 입력값 보존, AsyncAPI Operation 키 및 Metrics
|
|
146
|
+
스냅샷·Prometheus 레이블의 동일 이름 사용을 회귀 테스트로 검증한다.
|
|
147
|
+
|
|
148
|
+
- Python 3.12.10: 전체 201개 실행, 193개 통과, 선택적 테스트 8개 건너뜀.
|
|
149
|
+
- Python 3.9.13·3.10.11: 각 관련 테스트 43개 실행, 40개 통과, 3개 건너뜀.
|
|
150
|
+
- 변경한 Run 소스의 Pylance 진단 및 변경 파일 공백 검사 통과.
|
|
@@ -28,8 +28,10 @@ class Add(
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Run 등록 후 AsyncAPI Operation 키와 Metrics 작업 식별자는 `add`입니다.
|
|
31
|
-
이름은
|
|
32
|
-
|
|
31
|
+
명시적 이름은 영문 대소문자·숫자·`_`·`.`·`-`로만 구성된 한 글자 이상의 문자열입니다
|
|
32
|
+
(`Orders.create-v2_Job` 등). 위치 제한은 없고 Python 예약어도 허용합니다.
|
|
33
|
+
공백·한국어·그 외 문자는 허용하지 않으며, 입력을 변환하지 않고 대소문자를
|
|
34
|
+
구분합니다. Worker에 같은 이름을 가진 Run을 함께 등록할 수 없습니다.
|
|
33
35
|
|
|
34
36
|
## 기존 코드에 미치는 영향
|
|
35
37
|
|
|
@@ -46,7 +46,7 @@ Run을 정의할 때 이름과 설명을 선택적으로 지정할 수 있도록
|
|
|
46
46
|
|---|---|
|
|
47
47
|
| SPECNAME-01 | Run 정의 시 선택적 `name`을 지정할 수 있어야 한다. 지정하지 않은 경우의 기본값은 미지정 상태여야 한다. |
|
|
48
48
|
| SPECNAME-02 | Run 정의 시 선택적 `description`을 지정할 수 있어야 한다. 지정하지 않은 경우 설명은 없어야 한다. |
|
|
49
|
-
| SPECNAME-03 | `name`은
|
|
49
|
+
| SPECNAME-03 | 명시적 `name`은 `[A-Za-z0-9_.-]+` 전체 일치 문자열이어야 한다. 영문 대소문자·숫자·`_`·`.`·`-`를 위치 제한 없이 허용하고 Python 예약어도 허용한다. 빈 문자열, 공백, 한국어 및 그 외 문자는 `ValueError`, 문자열과 `None` 이외의 타입은 `TypeError`로 거부한다. 입력을 정규화하지 않고 대소문자를 구분하여 보존한다. |
|
|
50
50
|
| SPECNAME-04 | `description`은 문자열 또는 `None`이어야 한다. 다른 타입은 `TypeError`로 명확히 거부한다. 빈 설명 문자열은 그대로 보존한다. |
|
|
51
51
|
| SPECNAME-05 | 이름이 지정되지 않은 Run의 유효 이름은 `Run.__module__ + '.' + Run.__qualname__`이어야 한다. 중첩 클래스도 `__qualname__`을 포함해야 한다. |
|
|
52
52
|
| SPECNAME-06 | AsyncAPI 문서의 `operations` 객체에서 각 Run의 키로 유효 Run 이름을 사용해야 한다. 이 요구사항은 Operation 객체의 별도 필드가 아니라 `operations` 맵의 키에 적용한다. |
|
|
@@ -79,7 +79,7 @@ Run을 정의할 때 이름과 설명을 선택적으로 지정할 수 있도록
|
|
|
79
79
|
| WAT-01 | `Run` 정의에 `name='orders'`를 지정하면 AsyncAPI `operations`와 Metrics에서 `orders` 키로 식별된다. |
|
|
80
80
|
| WAT-02 | 이름을 지정하지 않은 모듈 최상위 Run의 AsyncAPI 키와 Metrics 키가 모두 `모듈명.클래스명`이다. |
|
|
81
81
|
| WAT-03 | 이름을 지정하지 않은 중첩 Run의 기본 이름에 모듈명과 중첩 경로를 포함한 `__qualname__`이 반영된다. |
|
|
82
|
-
| WAT-04 | `
|
|
82
|
+
| WAT-04 | `Orders.create-v2_Job`, 숫자만 있는 이름, Python 예약어 및 구분자로 시작·끝나는 이름을 그대로 허용한다. 빈 문자열, 한국어, 공백·개행 및 허용 범위 밖 문자는 `ValueError`, 문자열과 `None` 이외의 타입은 `TypeError`로 거부한다. |
|
|
83
83
|
| WAT-05 | `description`이 문자열이면 AsyncAPI Operation 설명에 반영되고, `None`이면 Operation에 설명 필드가 없다. |
|
|
84
84
|
| WAT-06 | `description`에 문자열 또는 `None` 이외의 값을 전달하면 `TypeError`가 발생한다. |
|
|
85
85
|
| WAT-07 | 두 Run에 같은 사용자 지정 이름을 지정하면 Run 등록 시 충돌을 감지하고, 문서 생성에서도 충돌을 검증한다. 기존 항목을 덮어쓰거나 통계를 합치지 않는다. |
|
|
@@ -4,8 +4,8 @@ from abc import (
|
|
|
4
4
|
abstractmethod,
|
|
5
5
|
)
|
|
6
6
|
from collections.abc import Mapping
|
|
7
|
-
from keyword import iskeyword
|
|
8
7
|
from logging import getLogger
|
|
8
|
+
from re import fullmatch
|
|
9
9
|
from time import monotonic
|
|
10
10
|
from typing import (
|
|
11
11
|
Any,
|
|
@@ -138,8 +138,11 @@ class Run(ABC, metaclass=RunMeta):
|
|
|
138
138
|
str,
|
|
139
139
|
):
|
|
140
140
|
raise TypeError('Run name must be a string or None')
|
|
141
|
-
if run_name is not None and (
|
|
142
|
-
|
|
141
|
+
if run_name is not None and fullmatch(
|
|
142
|
+
r'[A-Za-z0-9_.-]+',
|
|
143
|
+
run_name,
|
|
144
|
+
) is None:
|
|
145
|
+
raise ValueError('Run name must contain only ASCII letters, digits, underscores, dots or hyphens')
|
|
143
146
|
if description is not None and not isinstance(
|
|
144
147
|
description,
|
|
145
148
|
str,
|
|
@@ -498,7 +498,7 @@ class InspectorTests(TestCase):
|
|
|
498
498
|
class NamedAdd(
|
|
499
499
|
Add,
|
|
500
500
|
parameters={'queue': 'named-add'},
|
|
501
|
-
name='
|
|
501
|
+
name='Orders.create-v2_Job',
|
|
502
502
|
):
|
|
503
503
|
pass
|
|
504
504
|
|
|
@@ -514,7 +514,21 @@ class InspectorTests(TestCase):
|
|
|
514
514
|
self.addCleanup(worker.pool.stop)
|
|
515
515
|
self.assertEqual(
|
|
516
516
|
set(metrics.tasks),
|
|
517
|
-
{'
|
|
517
|
+
{'Orders.create-v2_Job'},
|
|
518
|
+
)
|
|
519
|
+
started = metrics.begin(NamedAdd.name)
|
|
520
|
+
metrics.end(
|
|
521
|
+
NamedAdd.name,
|
|
522
|
+
started,
|
|
523
|
+
True,
|
|
524
|
+
)
|
|
525
|
+
self.assertEqual(
|
|
526
|
+
metrics.snapshot(inspector)['tasks'][NamedAdd.name]['success'],
|
|
527
|
+
1,
|
|
528
|
+
)
|
|
529
|
+
self.assertIn(
|
|
530
|
+
'defstream_tasks_total{task="Orders.create-v2_Job",outcome="success"} 1',
|
|
531
|
+
metrics.render(inspector),
|
|
518
532
|
)
|
|
519
533
|
|
|
520
534
|
def test_metrics_reject_duplicate_run_names_without_overwriting(self):
|
|
@@ -426,7 +426,7 @@ class DocumentTests(TestCase):
|
|
|
426
426
|
class Described(
|
|
427
427
|
Add,
|
|
428
428
|
parameters={'queue': 'described'},
|
|
429
|
-
name='
|
|
429
|
+
name='Orders.create-v2_Job',
|
|
430
430
|
description='Add request description',
|
|
431
431
|
):
|
|
432
432
|
"""Not the operation description."""
|
|
@@ -444,7 +444,7 @@ class DocumentTests(TestCase):
|
|
|
444
444
|
inspector.worker.add(Empty)
|
|
445
445
|
operations = Specifications('/spec').document(inspector)['operations']
|
|
446
446
|
self.assertEqual(
|
|
447
|
-
operations['
|
|
447
|
+
operations['Orders.create-v2_Job']['description'],
|
|
448
448
|
'Add request description',
|
|
449
449
|
)
|
|
450
450
|
self.assertEqual(
|
|
@@ -469,12 +469,42 @@ class DocumentTests(TestCase):
|
|
|
469
469
|
Local.__module__ + '.' + Local.__qualname__,
|
|
470
470
|
)
|
|
471
471
|
|
|
472
|
-
def
|
|
472
|
+
def test_run_names_preserve_allowed_characters(self):
|
|
473
|
+
for name in (
|
|
474
|
+
'Orders.create-v2_Job',
|
|
475
|
+
'123',
|
|
476
|
+
'class',
|
|
477
|
+
'.-_',
|
|
478
|
+
'-Job.',
|
|
479
|
+
):
|
|
480
|
+
with self.subTest(name=name):
|
|
481
|
+
class Named(
|
|
482
|
+
Run,
|
|
483
|
+
name=name,
|
|
484
|
+
):
|
|
485
|
+
def run(self):
|
|
486
|
+
pass
|
|
487
|
+
|
|
488
|
+
self.assertEqual(
|
|
489
|
+
Named.name,
|
|
490
|
+
name,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
def test_run_names_reject_empty_strings_and_unsupported_characters(self):
|
|
473
494
|
for invalid in (
|
|
474
495
|
'',
|
|
475
|
-
'not-valid',
|
|
476
496
|
'has space',
|
|
477
|
-
'
|
|
497
|
+
' leading',
|
|
498
|
+
'trailing ',
|
|
499
|
+
'job\n',
|
|
500
|
+
'job\t',
|
|
501
|
+
'job/name',
|
|
502
|
+
'job:name',
|
|
503
|
+
'job"name',
|
|
504
|
+
'job\\name',
|
|
505
|
+
'\uC791\uC5C5',
|
|
506
|
+
'caf\u00e9',
|
|
507
|
+
'\uff11',
|
|
478
508
|
):
|
|
479
509
|
with self.subTest(name=invalid), self.assertRaises(ValueError):
|
|
480
510
|
class Invalid(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|