pyjallib 0.1.17__py3-none-any.whl → 0.1.19__py3-none-any.whl
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.
- pyjallib/__init__.py +5 -4
- pyjallib/exceptions.py +75 -0
- pyjallib/logger.py +152 -65
- pyjallib/max/__init__.py +8 -0
- pyjallib/max/autoClavicle.py +17 -5
- pyjallib/max/bone.py +21 -1
- pyjallib/max/boneChain.py +2 -0
- pyjallib/max/constraint.py +27 -2
- pyjallib/max/elbow.py +105 -0
- pyjallib/max/groinBone.py +2 -0
- pyjallib/max/header.py +121 -113
- pyjallib/max/hip.py +2 -0
- pyjallib/max/inguinal.py +117 -0
- pyjallib/max/kneeBone.py +2 -0
- pyjallib/max/macro/jal_macro_bone.py +221 -8
- pyjallib/max/macro/jal_macro_constraint.py +30 -0
- pyjallib/max/mirror.py +20 -13
- pyjallib/max/shoulder.py +173 -0
- pyjallib/max/twistBone.py +22 -19
- pyjallib/max/volumeBone.py +12 -1
- pyjallib/max/wrist.py +113 -0
- pyjallib/nameToPath.py +78 -61
- pyjallib/perforce.py +101 -171
- pyjallib/ue5/inUnreal/animationImporter.py +111 -19
- pyjallib/ue5/inUnreal/baseImporter.py +10 -7
- pyjallib/ue5/inUnreal/skeletalMeshImporter.py +1 -1
- pyjallib/ue5/inUnreal/skeletonImporter.py +1 -1
- pyjallib/ue5/logger.py +152 -111
- pyjallib/ue5/templateProcessor.py +35 -2
- pyjallib/ue5/templates/__init__.py +6 -3
- pyjallib/ue5/templates/batchAnimImportTemplate.py +21 -0
- {pyjallib-0.1.17.dist-info → pyjallib-0.1.19.dist-info}/METADATA +1 -1
- {pyjallib-0.1.17.dist-info → pyjallib-0.1.19.dist-info}/RECORD +34 -28
- {pyjallib-0.1.17.dist-info → pyjallib-0.1.19.dist-info}/WHEEL +0 -0
pyjallib/perforce.py
CHANGED
@@ -11,6 +11,7 @@ P4Python을 사용하는 Perforce 모듈.
|
|
11
11
|
|
12
12
|
from P4 import P4, P4Exception
|
13
13
|
import os
|
14
|
+
from .exceptions import PerforceError, ValidationError
|
14
15
|
|
15
16
|
|
16
17
|
class Perforce:
|
@@ -21,8 +22,6 @@ class Perforce:
|
|
21
22
|
self.p4 = P4()
|
22
23
|
self.connected = False
|
23
24
|
self.workspaceRoot = r""
|
24
|
-
self.last_error = None
|
25
|
-
self.last_warnings = []
|
26
25
|
|
27
26
|
def _is_connected(self) -> bool:
|
28
27
|
"""Perforce 서버 연결 상태를 확인합니다.
|
@@ -31,40 +30,18 @@ class Perforce:
|
|
31
30
|
bool: 연결되어 있으면 True, 아니면 False
|
32
31
|
"""
|
33
32
|
if not self.connected:
|
34
|
-
self.last_error = "Perforce 서버에 연결되지 않았습니다."
|
35
33
|
return False
|
36
34
|
return True
|
37
|
-
|
38
|
-
def
|
39
|
-
"""
|
40
|
-
|
41
|
-
Args:
|
42
|
-
e (P4Exception): 발생한 예외
|
43
|
-
context_msg (str, optional): 예외가 발생한 컨텍스트 설명
|
44
|
-
"""
|
45
|
-
self.last_error = f"{context_msg} 중 P4Exception 발생: {e}"
|
46
|
-
self.last_warnings = list(self.p4.warnings) if self.p4.warnings else []
|
47
|
-
|
48
|
-
def get_last_error(self) -> str:
|
49
|
-
"""마지막 에러 메시지를 반환합니다.
|
50
|
-
|
51
|
-
Returns:
|
52
|
-
str: 마지막 에러 메시지. 에러가 없으면 None
|
53
|
-
"""
|
54
|
-
return self.last_error
|
55
|
-
|
56
|
-
def get_last_warnings(self) -> list:
|
57
|
-
"""마지막 경고 메시지들을 반환합니다.
|
35
|
+
|
36
|
+
def _ensure_connected(self) -> None:
|
37
|
+
"""Perforce 서버 연결 상태를 확인하고 연결되지 않은 경우 예외를 발생시킵니다.
|
58
38
|
|
59
|
-
|
60
|
-
|
39
|
+
Raises:
|
40
|
+
PerforceError: 연결되지 않은 상태인 경우
|
61
41
|
"""
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
"""마지막 에러와 경고 정보를 초기화합니다."""
|
66
|
-
self.last_error = None
|
67
|
-
self.last_warnings = []
|
42
|
+
if not self.connected:
|
43
|
+
error_message = "Perforce 서버에 연결되지 않았습니다."
|
44
|
+
raise PerforceError(error_message)
|
68
45
|
|
69
46
|
def connect(self, workspace_name: str) -> bool:
|
70
47
|
"""지정된 워크스페이스에 연결합니다.
|
@@ -75,7 +52,6 @@ class Perforce:
|
|
75
52
|
Returns:
|
76
53
|
bool: 연결 성공 시 True, 실패 시 False
|
77
54
|
"""
|
78
|
-
self.clear_last_error()
|
79
55
|
try:
|
80
56
|
self.p4.client = workspace_name
|
81
57
|
self.p4.connect()
|
@@ -90,15 +66,14 @@ class Perforce:
|
|
90
66
|
root_path = os.path.normpath(root_path)
|
91
67
|
|
92
68
|
self.workspaceRoot = root_path
|
93
|
-
except (IndexError, KeyError)
|
94
|
-
self.last_error = f"워크스페이스 루트 경로 가져오기 실패: {e}"
|
69
|
+
except (IndexError, KeyError):
|
95
70
|
self.workspaceRoot = ""
|
96
71
|
|
97
72
|
return True
|
98
73
|
except P4Exception as e:
|
99
74
|
self.connected = False
|
100
|
-
|
101
|
-
|
75
|
+
error_message = f"'{workspace_name}' 워크스페이스 연결 실패 중 P4Exception 발생: {e}"
|
76
|
+
raise PerforceError(error_message)
|
102
77
|
|
103
78
|
def get_pending_change_list(self) -> list:
|
104
79
|
"""워크스페이스의 Pending된 체인지 리스트를 가져옵니다.
|
@@ -106,9 +81,7 @@ class Perforce:
|
|
106
81
|
Returns:
|
107
82
|
list: 체인지 리스트 정보 딕셔너리들의 리스트
|
108
83
|
"""
|
109
|
-
|
110
|
-
return []
|
111
|
-
self.clear_last_error()
|
84
|
+
self._ensure_connected()
|
112
85
|
try:
|
113
86
|
pending_changes = self.p4.run_changes("-s", "pending", "-u", self.p4.user, "-c", self.p4.client)
|
114
87
|
change_numbers = [int(cl['change']) for cl in pending_changes]
|
@@ -122,8 +95,8 @@ class Perforce:
|
|
122
95
|
|
123
96
|
return change_list_info
|
124
97
|
except P4Exception as e:
|
125
|
-
|
126
|
-
|
98
|
+
error_message = f"Pending 체인지 리스트 조회 실패 중 P4Exception 발생: {e}"
|
99
|
+
raise PerforceError(error_message)
|
127
100
|
|
128
101
|
def create_change_list(self, description: str) -> dict:
|
129
102
|
"""새로운 체인지 리스트를 생성합니다.
|
@@ -134,9 +107,7 @@ class Perforce:
|
|
134
107
|
Returns:
|
135
108
|
dict: 생성된 체인지 리스트 정보. 실패 시 빈 딕셔너리
|
136
109
|
"""
|
137
|
-
|
138
|
-
return {}
|
139
|
-
self.clear_last_error()
|
110
|
+
self._ensure_connected()
|
140
111
|
try:
|
141
112
|
change_spec = self.p4.fetch_change()
|
142
113
|
change_spec["Description"] = description
|
@@ -144,11 +115,11 @@ class Perforce:
|
|
144
115
|
created_change_number = int(result[0].split()[1])
|
145
116
|
return self.get_change_list_by_number(created_change_number)
|
146
117
|
except P4Exception as e:
|
147
|
-
|
148
|
-
|
118
|
+
error_message = f"체인지 리스트 생성 실패 ('{description}') 중 P4Exception 발생: {e}"
|
119
|
+
raise PerforceError(error_message)
|
149
120
|
except (IndexError, ValueError) as e:
|
150
|
-
|
151
|
-
|
121
|
+
error_message = f"체인지 리스트 번호 파싱 오류: {e}"
|
122
|
+
raise PerforceError(error_message)
|
152
123
|
|
153
124
|
def get_change_list_by_number(self, change_list_number: int) -> dict:
|
154
125
|
"""체인지 리스트 번호로 체인지 리스트를 가져옵니다.
|
@@ -159,19 +130,17 @@ class Perforce:
|
|
159
130
|
Returns:
|
160
131
|
dict: 체인지 리스트 정보. 실패 시 빈 딕셔너리
|
161
132
|
"""
|
162
|
-
|
163
|
-
return {}
|
164
|
-
self.clear_last_error()
|
133
|
+
self._ensure_connected()
|
165
134
|
try:
|
166
135
|
cl_info = self.p4.fetch_change(change_list_number)
|
167
136
|
if cl_info:
|
168
137
|
return cl_info
|
169
138
|
else:
|
170
|
-
|
171
|
-
|
139
|
+
error_message = f"체인지 리스트 {change_list_number}를 찾을 수 없습니다."
|
140
|
+
raise PerforceError(error_message)
|
172
141
|
except P4Exception as e:
|
173
|
-
|
174
|
-
|
142
|
+
error_message = f"체인지 리스트 {change_list_number} 정보 조회 실패 중 P4Exception 발생: {e}"
|
143
|
+
raise PerforceError(error_message)
|
175
144
|
|
176
145
|
def get_change_list_by_description(self, description: str) -> dict:
|
177
146
|
"""체인지 리스트 설명으로 체인지 리스트를 가져옵니다.
|
@@ -182,9 +151,7 @@ class Perforce:
|
|
182
151
|
Returns:
|
183
152
|
dict: 체인지 리스트 정보 (일치하는 첫 번째 체인지 리스트)
|
184
153
|
"""
|
185
|
-
|
186
|
-
return {}
|
187
|
-
self.clear_last_error()
|
154
|
+
self._ensure_connected()
|
188
155
|
try:
|
189
156
|
pending_changes = self.p4.run_changes("-l", "-s", "pending", "-u", self.p4.user, "-c", self.p4.client)
|
190
157
|
for cl in pending_changes:
|
@@ -193,8 +160,8 @@ class Perforce:
|
|
193
160
|
return self.get_change_list_by_number(int(cl['change']))
|
194
161
|
return {}
|
195
162
|
except P4Exception as e:
|
196
|
-
|
197
|
-
|
163
|
+
error_message = f"설명으로 체인지 리스트 조회 실패 ('{description}') 중 P4Exception 발생: {e}"
|
164
|
+
raise PerforceError(error_message)
|
198
165
|
|
199
166
|
def get_change_list_by_description_pattern(self, description_pattern: str, exact_match: bool = False) -> list:
|
200
167
|
"""설명 패턴과 일치하는 Pending 체인지 리스트들을 가져옵니다.
|
@@ -207,10 +174,7 @@ class Perforce:
|
|
207
174
|
Returns:
|
208
175
|
list: 패턴과 일치하는 체인지 리스트 정보들의 리스트
|
209
176
|
"""
|
210
|
-
|
211
|
-
return []
|
212
|
-
|
213
|
-
self.clear_last_error()
|
177
|
+
self._ensure_connected()
|
214
178
|
try:
|
215
179
|
pending_changes = self.p4.run_changes("-l", "-s", "pending", "-u", self.p4.user, "-c", self.p4.client)
|
216
180
|
matching_changes = []
|
@@ -235,8 +199,8 @@ class Perforce:
|
|
235
199
|
|
236
200
|
return matching_changes
|
237
201
|
except P4Exception as e:
|
238
|
-
|
239
|
-
|
202
|
+
error_message = f"설명 패턴으로 체인지 리스트 조회 실패 ('{description_pattern}') 중 P4Exception 발생: {e}"
|
203
|
+
raise PerforceError(error_message)
|
240
204
|
|
241
205
|
def disconnect(self):
|
242
206
|
"""Perforce 서버와의 연결을 해제합니다."""
|
@@ -245,7 +209,8 @@ class Perforce:
|
|
245
209
|
self.p4.disconnect()
|
246
210
|
self.connected = False
|
247
211
|
except P4Exception as e:
|
248
|
-
|
212
|
+
error_message = f"Perforce 서버 연결 해제 중 P4Exception 발생: {e}"
|
213
|
+
raise PerforceError(error_message)
|
249
214
|
|
250
215
|
def __del__(self):
|
251
216
|
"""객체가 소멸될 때 자동으로 연결을 해제합니다."""
|
@@ -269,18 +234,15 @@ class Perforce:
|
|
269
234
|
}
|
270
235
|
}
|
271
236
|
"""
|
272
|
-
|
273
|
-
return {}
|
237
|
+
self._ensure_connected()
|
274
238
|
if not file_paths:
|
275
239
|
return {}
|
276
240
|
|
277
241
|
# 타입 검증: 리스트가 아닌 경우 에러 발생
|
278
242
|
if not isinstance(file_paths, list):
|
279
243
|
error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 is_file_checked_out() 메서드를 사용하세요."
|
280
|
-
|
281
|
-
raise TypeError(error_msg)
|
244
|
+
raise ValidationError(error_msg)
|
282
245
|
|
283
|
-
self.clear_last_error()
|
284
246
|
result = {}
|
285
247
|
try:
|
286
248
|
# 각 파일의 상태 확인
|
@@ -310,15 +272,16 @@ class Perforce:
|
|
310
272
|
# 파일이 perforce에 없거나 접근할 수 없는 경우
|
311
273
|
if not any("not opened" in err.lower() or "no such file" in err.lower()
|
312
274
|
for err in self.p4.errors):
|
313
|
-
|
275
|
+
error_message = f"파일 '{file_path}' 체크아웃 상태 확인 중 P4Exception 발생: {e}"
|
276
|
+
raise PerforceError(error_message)
|
314
277
|
|
315
278
|
result[file_path] = file_status
|
316
279
|
|
317
280
|
return result
|
318
281
|
|
319
282
|
except P4Exception as e:
|
320
|
-
|
321
|
-
|
283
|
+
error_message = f"파일들 체크아웃 상태 확인 ({file_paths}) 중 P4Exception 발생: {e}"
|
284
|
+
raise PerforceError(error_message)
|
322
285
|
|
323
286
|
def is_file_checked_out(self, file_path: str) -> bool:
|
324
287
|
"""단일 파일의 체크아웃 상태를 간단히 확인합니다.
|
@@ -342,10 +305,7 @@ class Perforce:
|
|
342
305
|
Returns:
|
343
306
|
bool: 파일이 해당 체인지 리스트에 있으면 True, 아니면 False
|
344
307
|
"""
|
345
|
-
|
346
|
-
return False
|
347
|
-
|
348
|
-
self.clear_last_error()
|
308
|
+
self._ensure_connected()
|
349
309
|
try:
|
350
310
|
# 해당 체인지 리스트의 파일들 가져오기
|
351
311
|
opened_files = self.p4.run_opened("-c", change_list_number)
|
@@ -363,8 +323,8 @@ class Perforce:
|
|
363
323
|
return False
|
364
324
|
|
365
325
|
except P4Exception as e:
|
366
|
-
|
367
|
-
|
326
|
+
error_message = f"파일 '{file_path}' 체인지 리스트 {change_list_number} 포함 여부 확인 중 P4Exception 발생: {e}"
|
327
|
+
raise PerforceError(error_message)
|
368
328
|
|
369
329
|
def edit_change_list(self, change_list_number: int, description: str = None, add_file_paths: list = None, remove_file_paths: list = None) -> dict:
|
370
330
|
"""체인지 리스트를 편집합니다.
|
@@ -378,9 +338,7 @@ class Perforce:
|
|
378
338
|
Returns:
|
379
339
|
dict: 업데이트된 체인지 리스트 정보
|
380
340
|
"""
|
381
|
-
|
382
|
-
return {}
|
383
|
-
self.clear_last_error()
|
341
|
+
self._ensure_connected()
|
384
342
|
try:
|
385
343
|
if description is not None:
|
386
344
|
change_spec = self.p4.fetch_change(change_list_number)
|
@@ -394,20 +352,22 @@ class Perforce:
|
|
394
352
|
try:
|
395
353
|
self.p4.run_reopen("-c", change_list_number, file_path)
|
396
354
|
except P4Exception as e_reopen:
|
397
|
-
|
355
|
+
error_message = f"파일 '{file_path}'을 CL {change_list_number}로 이동 중 P4Exception 발생: {e_reopen}"
|
356
|
+
raise PerforceError(error_message)
|
398
357
|
|
399
358
|
if remove_file_paths:
|
400
359
|
for file_path in remove_file_paths:
|
401
360
|
try:
|
402
361
|
self.p4.run_revert("-c", change_list_number, file_path)
|
403
362
|
except P4Exception as e_revert:
|
404
|
-
|
363
|
+
error_message = f"파일 '{file_path}'을 CL {change_list_number}에서 제거(revert) 중 P4Exception 발생: {e_revert}"
|
364
|
+
raise PerforceError(error_message)
|
405
365
|
|
406
366
|
return self.get_change_list_by_number(change_list_number)
|
407
367
|
|
408
368
|
except P4Exception as e:
|
409
|
-
|
410
|
-
|
369
|
+
error_message = f"체인지 리스트 {change_list_number} 편집 중 P4Exception 발생: {e}"
|
370
|
+
raise PerforceError(error_message)
|
411
371
|
|
412
372
|
def _file_op(self, command: str, file_path: str, change_list_number: int, op_name: str) -> bool:
|
413
373
|
"""파일 작업을 수행하는 내부 헬퍼 함수입니다.
|
@@ -421,9 +381,7 @@ class Perforce:
|
|
421
381
|
Returns:
|
422
382
|
bool: 작업 성공 시 True, 실패 시 False
|
423
383
|
"""
|
424
|
-
|
425
|
-
return False
|
426
|
-
self.clear_last_error()
|
384
|
+
self._ensure_connected()
|
427
385
|
try:
|
428
386
|
if command == "edit":
|
429
387
|
self.p4.run_edit("-c", change_list_number, file_path)
|
@@ -432,12 +390,12 @@ class Perforce:
|
|
432
390
|
elif command == "delete":
|
433
391
|
self.p4.run_delete("-c", change_list_number, file_path)
|
434
392
|
else:
|
435
|
-
|
436
|
-
|
393
|
+
error_message = f"지원되지 않는 파일 작업: {command}"
|
394
|
+
raise ValidationError(error_message)
|
437
395
|
return True
|
438
396
|
except P4Exception as e:
|
439
|
-
|
440
|
-
|
397
|
+
error_message = f"파일 '{file_path}' {op_name} (CL: {change_list_number}) 중 P4Exception 발생: {e}"
|
398
|
+
raise PerforceError(error_message)
|
441
399
|
|
442
400
|
def checkout_file(self, file_path: str, change_list_number: int) -> bool:
|
443
401
|
"""파일을 체크아웃합니다.
|
@@ -467,8 +425,7 @@ class Perforce:
|
|
467
425
|
# 타입 검증: 리스트가 아닌 경우 에러 발생
|
468
426
|
if not isinstance(file_paths, list):
|
469
427
|
error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 checkout_file() 메서드를 사용하세요."
|
470
|
-
|
471
|
-
raise TypeError(error_msg)
|
428
|
+
raise ValidationError(error_msg)
|
472
429
|
|
473
430
|
all_success = True
|
474
431
|
for file_path in file_paths:
|
@@ -506,8 +463,7 @@ class Perforce:
|
|
506
463
|
# 타입 검증: 리스트가 아닌 경우 에러 발생
|
507
464
|
if not isinstance(file_paths, list):
|
508
465
|
error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 add_file() 메서드를 사용하세요."
|
509
|
-
|
510
|
-
raise TypeError(error_msg)
|
466
|
+
raise ValidationError(error_msg)
|
511
467
|
|
512
468
|
all_success = True
|
513
469
|
for file_path in file_paths:
|
@@ -545,8 +501,7 @@ class Perforce:
|
|
545
501
|
# 타입 검증: 리스트가 아닌 경우 에러 발생
|
546
502
|
if not isinstance(file_paths, list):
|
547
503
|
error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 delete_file() 메서드를 사용하세요."
|
548
|
-
|
549
|
-
raise TypeError(error_msg)
|
504
|
+
raise ValidationError(error_msg)
|
550
505
|
|
551
506
|
all_success = True
|
552
507
|
for file_path in file_paths:
|
@@ -567,21 +522,20 @@ class Perforce:
|
|
567
522
|
Returns:
|
568
523
|
bool: 제출 성공 시 True, 실패 시 False
|
569
524
|
"""
|
570
|
-
|
571
|
-
return False
|
572
|
-
self.clear_last_error()
|
525
|
+
self._ensure_connected()
|
573
526
|
|
574
527
|
submit_success = False
|
575
528
|
try:
|
576
529
|
self.p4.run_submit("-c", change_list_number)
|
577
530
|
submit_success = True
|
578
531
|
except P4Exception as e:
|
579
|
-
self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 제출 실패")
|
580
532
|
if any("nothing to submit" in err.lower() for err in self.p4.errors):
|
581
|
-
|
582
|
-
|
533
|
+
error_message = f"체인지 리스트 {change_list_number}에 제출할 파일이 없습니다."
|
534
|
+
else:
|
535
|
+
error_message = f"체인지 리스트 {change_list_number} 제출 실패 중 P4Exception 발생: {e}"
|
536
|
+
raise PerforceError(error_message)
|
583
537
|
|
584
|
-
# 제출 성공
|
538
|
+
# 제출 성공 시 후속 작업 실행
|
585
539
|
try:
|
586
540
|
# 제출 후 변경사항이 없는 체크아웃된 파일들을 자동으로 리버트
|
587
541
|
if auto_revert_unchanged:
|
@@ -591,8 +545,8 @@ class Perforce:
|
|
591
545
|
# 빈 체인지 리스트 삭제
|
592
546
|
self.delete_empty_change_list(change_list_number)
|
593
547
|
except Exception as e:
|
594
|
-
|
595
|
-
|
548
|
+
error_message = f"체인지 리스트 {change_list_number} 제출 후 후속 작업 중 오류 발생: {e}"
|
549
|
+
raise PerforceError(error_message)
|
596
550
|
|
597
551
|
return submit_success
|
598
552
|
|
@@ -686,9 +640,7 @@ class Perforce:
|
|
686
640
|
Returns:
|
687
641
|
bool: 되돌리기 및 삭제 성공 시 True, 실패 시 False
|
688
642
|
"""
|
689
|
-
|
690
|
-
return False
|
691
|
-
self.clear_last_error()
|
643
|
+
self._ensure_connected()
|
692
644
|
try:
|
693
645
|
# 체인지 리스트의 모든 파일 되돌리기
|
694
646
|
self.p4.run_revert("-c", change_list_number, "//...")
|
@@ -697,13 +649,13 @@ class Perforce:
|
|
697
649
|
try:
|
698
650
|
self.p4.run_change("-d", change_list_number)
|
699
651
|
except P4Exception as e_delete:
|
700
|
-
|
701
|
-
|
652
|
+
error_message = f"체인지 리스트 {change_list_number} 삭제 중 P4Exception 발생: {e_delete}"
|
653
|
+
raise PerforceError(error_message)
|
702
654
|
|
703
655
|
return True
|
704
656
|
except P4Exception as e:
|
705
|
-
|
706
|
-
|
657
|
+
error_message = f"체인지 리스트 {change_list_number} 전체 되돌리기 실패 중 P4Exception 발생: {e}"
|
658
|
+
raise PerforceError(error_message)
|
707
659
|
|
708
660
|
def delete_empty_change_list(self, change_list_number: int) -> bool:
|
709
661
|
"""빈 체인지 리스트를 삭제합니다.
|
@@ -714,25 +666,22 @@ class Perforce:
|
|
714
666
|
Returns:
|
715
667
|
bool: 삭제 성공 시 True, 실패 시 False
|
716
668
|
"""
|
717
|
-
|
718
|
-
return False
|
719
|
-
|
720
|
-
self.clear_last_error()
|
669
|
+
self._ensure_connected()
|
721
670
|
try:
|
722
671
|
# 체인지 리스트 정보 가져오기
|
723
672
|
change_spec = self.p4.fetch_change(change_list_number)
|
724
673
|
|
725
674
|
# 파일이 있는지 확인
|
726
675
|
if change_spec and change_spec.get('Files') and len(change_spec['Files']) > 0:
|
727
|
-
|
728
|
-
|
676
|
+
error_message = f"체인지 리스트 {change_list_number}에 파일이 {len(change_spec['Files'])}개 있어 삭제할 수 없습니다."
|
677
|
+
raise PerforceError(error_message)
|
729
678
|
|
730
679
|
# 빈 체인지 리스트 삭제
|
731
680
|
self.p4.run_change("-d", change_list_number)
|
732
681
|
return True
|
733
682
|
except P4Exception as e:
|
734
|
-
|
735
|
-
|
683
|
+
error_message = f"체인지 리스트 {change_list_number} 삭제 실패 중 P4Exception 발생: {e}"
|
684
|
+
raise PerforceError(error_message)
|
736
685
|
|
737
686
|
def revert_file(self, file_path: str, change_list_number: int) -> bool:
|
738
687
|
"""체인지 리스트에서 특정 파일을 되돌립니다.
|
@@ -744,16 +693,13 @@ class Perforce:
|
|
744
693
|
Returns:
|
745
694
|
bool: 되돌리기 성공 시 True, 실패 시 False
|
746
695
|
"""
|
747
|
-
|
748
|
-
return False
|
749
|
-
|
750
|
-
self.clear_last_error()
|
696
|
+
self._ensure_connected()
|
751
697
|
try:
|
752
698
|
self.p4.run_revert("-c", change_list_number, file_path)
|
753
699
|
return True
|
754
700
|
except P4Exception as e:
|
755
|
-
|
756
|
-
|
701
|
+
error_message = f"파일 '{file_path}'를 체인지 리스트 {change_list_number}에서 되돌리기 중 P4Exception 발생: {e}"
|
702
|
+
raise PerforceError(error_message)
|
757
703
|
|
758
704
|
def revert_files(self, change_list_number: int, file_paths: list) -> bool:
|
759
705
|
"""체인지 리스트 내의 특정 파일들을 되돌립니다.
|
@@ -765,16 +711,14 @@ class Perforce:
|
|
765
711
|
Returns:
|
766
712
|
bool: 모든 파일 되돌리기 성공 시 True, 하나라도 실패 시 False
|
767
713
|
"""
|
768
|
-
|
769
|
-
return False
|
714
|
+
self._ensure_connected()
|
770
715
|
if not file_paths:
|
771
716
|
return True
|
772
717
|
|
773
718
|
# 타입 검증: 리스트가 아닌 경우 에러 발생
|
774
719
|
if not isinstance(file_paths, list):
|
775
720
|
error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 revert_file() 메서드를 사용하세요."
|
776
|
-
|
777
|
-
raise TypeError(error_msg)
|
721
|
+
raise ValidationError(error_msg)
|
778
722
|
|
779
723
|
all_success = True
|
780
724
|
for file_path in file_paths:
|
@@ -794,16 +738,14 @@ class Perforce:
|
|
794
738
|
Returns:
|
795
739
|
bool: 업데이트가 필요한 파일이 있으면 True, 없으면 False
|
796
740
|
"""
|
797
|
-
|
798
|
-
return False
|
741
|
+
self._ensure_connected()
|
799
742
|
if not file_paths:
|
800
743
|
return False
|
801
744
|
|
802
745
|
# 타입 검증: 리스트가 아닌 경우 에러 발생
|
803
746
|
if not isinstance(file_paths, list):
|
804
747
|
error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 경로도 리스트로 감싸서 전달하세요: ['{file_paths}']"
|
805
|
-
|
806
|
-
raise TypeError(error_msg)
|
748
|
+
raise ValidationError(error_msg)
|
807
749
|
|
808
750
|
# 폴더 경로에 재귀적 와일드카드 패턴을 추가
|
809
751
|
processed_paths = []
|
@@ -814,7 +756,6 @@ class Perforce:
|
|
814
756
|
else:
|
815
757
|
processed_paths.append(path)
|
816
758
|
|
817
|
-
self.clear_last_error()
|
818
759
|
try:
|
819
760
|
sync_preview_results = self.p4.run_sync("-n", processed_paths)
|
820
761
|
needs_update = False
|
@@ -846,8 +787,8 @@ class Perforce:
|
|
846
787
|
any("up-to-date" in msg for msg in warning_messages)):
|
847
788
|
return False
|
848
789
|
else:
|
849
|
-
|
850
|
-
|
790
|
+
error_message = f"파일/폴더 업데이트 필요 여부 확인 ({processed_paths}) 중 P4Exception 발생: {e}"
|
791
|
+
raise PerforceError(error_message)
|
851
792
|
|
852
793
|
def is_file_in_perforce(self, file_path: str) -> bool:
|
853
794
|
"""파일이 Perforce에 속하는지 확인합니다.
|
@@ -858,10 +799,7 @@ class Perforce:
|
|
858
799
|
Returns:
|
859
800
|
bool: 파일이 Perforce에 속하면 True, 아니면 False
|
860
801
|
"""
|
861
|
-
|
862
|
-
return False
|
863
|
-
|
864
|
-
self.clear_last_error()
|
802
|
+
self._ensure_connected()
|
865
803
|
try:
|
866
804
|
# p4 files 명령으로 파일 정보 조회
|
867
805
|
file_info = self.p4.run_files(file_path)
|
@@ -877,8 +815,8 @@ class Perforce:
|
|
877
815
|
if any("no such file(s)" in err.lower() for err in self.p4.errors):
|
878
816
|
return False
|
879
817
|
else:
|
880
|
-
|
881
|
-
|
818
|
+
error_message = f"파일 '{file_path}' Perforce 존재 여부 확인 중 P4Exception 발생: {e}"
|
819
|
+
raise PerforceError(error_message)
|
882
820
|
|
883
821
|
def sync_files(self, file_paths: list) -> bool:
|
884
822
|
"""파일이나 폴더를 동기화합니다.
|
@@ -890,16 +828,14 @@ class Perforce:
|
|
890
828
|
Returns:
|
891
829
|
bool: 동기화 성공 시 True, 실패 시 False
|
892
830
|
"""
|
893
|
-
|
894
|
-
return False
|
831
|
+
self._ensure_connected()
|
895
832
|
if not file_paths:
|
896
833
|
return True
|
897
834
|
|
898
835
|
# 타입 검증: 리스트가 아닌 경우 에러 발생
|
899
836
|
if not isinstance(file_paths, list):
|
900
837
|
error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 경로도 리스트로 감싸서 전달하세요: ['{file_paths}']"
|
901
|
-
|
902
|
-
raise TypeError(error_msg)
|
838
|
+
raise ValidationError(error_msg)
|
903
839
|
|
904
840
|
# 폴더 경로에 재귀적 와일드카드 패턴을 추가
|
905
841
|
processed_paths = []
|
@@ -910,13 +846,12 @@ class Perforce:
|
|
910
846
|
else:
|
911
847
|
processed_paths.append(path)
|
912
848
|
|
913
|
-
self.clear_last_error()
|
914
849
|
try:
|
915
850
|
self.p4.run_sync(processed_paths)
|
916
851
|
return True
|
917
852
|
except P4Exception as e:
|
918
|
-
|
919
|
-
|
853
|
+
error_message = f"파일/폴더 싱크 실패 ({processed_paths}) 중 P4Exception 발생: {e}"
|
854
|
+
raise PerforceError(error_message)
|
920
855
|
|
921
856
|
def get_default_change_list(self) -> dict:
|
922
857
|
"""default change list의 정보를 가져옵니다.
|
@@ -924,9 +859,7 @@ class Perforce:
|
|
924
859
|
Returns:
|
925
860
|
dict: get_change_list_by_number와 동일한 형태의 딕셔너리
|
926
861
|
"""
|
927
|
-
|
928
|
-
return {}
|
929
|
-
self.clear_last_error()
|
862
|
+
self._ensure_connected()
|
930
863
|
try:
|
931
864
|
opened_files = self.p4.run_opened("-c", "default")
|
932
865
|
files_list = [f.get('clientFile', '') for f in opened_files]
|
@@ -940,8 +873,8 @@ class Perforce:
|
|
940
873
|
}
|
941
874
|
return result
|
942
875
|
except P4Exception as e:
|
943
|
-
|
944
|
-
|
876
|
+
error_message = f"default change list 정보 조회 실패 중 P4Exception 발생: {e}"
|
877
|
+
raise PerforceError(error_message)
|
945
878
|
|
946
879
|
def check_files_checked_out_all_users(self, file_paths: list) -> dict:
|
947
880
|
"""파일들의 체크아웃 상태를 모든 사용자/워크스페이스에서 확인합니다.
|
@@ -961,18 +894,15 @@ class Perforce:
|
|
961
894
|
}
|
962
895
|
}
|
963
896
|
"""
|
964
|
-
|
965
|
-
return {}
|
897
|
+
self._ensure_connected()
|
966
898
|
if not file_paths:
|
967
899
|
return {}
|
968
900
|
|
969
901
|
# 타입 검증: 리스트가 아닌 경우 에러 발생
|
970
902
|
if not isinstance(file_paths, list):
|
971
903
|
error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 get_file_checkout_info_all_users() 메서드를 사용하세요."
|
972
|
-
|
973
|
-
raise TypeError(error_msg)
|
904
|
+
raise ValidationError(error_msg)
|
974
905
|
|
975
|
-
self.clear_last_error()
|
976
906
|
result = {}
|
977
907
|
try:
|
978
908
|
# 각 파일의 상태 확인
|
@@ -1002,15 +932,16 @@ class Perforce:
|
|
1002
932
|
# 파일이 perforce에 없거나 접근할 수 없는 경우
|
1003
933
|
if not any("not opened" in err.lower() or "no such file" in err.lower()
|
1004
934
|
for err in self.p4.errors):
|
1005
|
-
|
935
|
+
error_message = f"파일 '{file_path}' 체크아웃 상태 확인 (모든 사용자) 중 P4Exception 발생: {e}"
|
936
|
+
raise PerforceError(error_message)
|
1006
937
|
|
1007
938
|
result[file_path] = file_status
|
1008
939
|
|
1009
940
|
return result
|
1010
941
|
|
1011
942
|
except P4Exception as e:
|
1012
|
-
|
1013
|
-
|
943
|
+
error_message = f"파일들 체크아웃 상태 확인 - 모든 사용자 ({file_paths}) 중 P4Exception 발생: {e}"
|
944
|
+
raise PerforceError(error_message)
|
1014
945
|
|
1015
946
|
def is_file_checked_out_by_others(self, file_path: str) -> bool:
|
1016
947
|
"""단일 파일이 다른 사용자/워크스페이스에 의해 체크아웃되어 있는지 확인합니다.
|
@@ -1099,8 +1030,7 @@ class Perforce:
|
|
1099
1030
|
# 타입 검증: 리스트가 아닌 경우 에러 발생
|
1100
1031
|
if not isinstance(file_paths, list):
|
1101
1032
|
error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 is_file_checked_out_by_others() 메서드를 사용하세요."
|
1102
|
-
|
1103
|
-
raise TypeError(error_msg)
|
1033
|
+
raise ValidationError(error_msg)
|
1104
1034
|
|
1105
1035
|
result = self.check_files_checked_out_all_users(file_paths)
|
1106
1036
|
files_by_others = []
|