pyjallib 0.1.16__py3-none-any.whl → 0.1.17__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/perforce.py CHANGED
@@ -9,45 +9,20 @@ P4Python을 사용하는 Perforce 모듈.
9
9
  - 파일 동기화 및 업데이트 확인
10
10
  """
11
11
 
12
- import logging
13
12
  from P4 import P4, P4Exception
14
13
  import os
15
- from pathlib import Path
16
-
17
- # 로깅 설정
18
- logger = logging.getLogger(__name__)
19
-
20
- # 기본 로그 레벨은 ERROR로 설정 (디버그 모드는 생성자에서 설정)
21
- logger.setLevel(logging.ERROR)
22
-
23
- # 사용자 문서 폴더 내 로그 파일 저장
24
- log_path = os.path.join(Path.home() / "Documents", 'Perforce.log')
25
- file_handler = logging.FileHandler(log_path, encoding='utf-8')
26
- file_handler.setLevel(logging.ERROR) # 기본적으로 ERROR 레벨만 기록
27
- file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
28
- logger.addHandler(file_handler)
29
14
 
30
15
 
31
16
  class Perforce:
32
17
  """P4Python을 사용하여 Perforce 작업을 수행하는 클래스."""
33
18
 
34
- def __init__(self, debug_mode: bool = False):
35
- """Perforce 인스턴스를 초기화합니다.
36
-
37
- Args:
38
- debug_mode (bool): True로 설정하면 DEBUG 레벨 로그를 활성화합니다.
39
- 기본값은 False (ERROR 레벨만 기록)
40
- """
41
- # 디버그 모드에 따라 로그 레벨 설정
42
- if debug_mode:
43
- logger.setLevel(logging.DEBUG)
44
- file_handler.setLevel(logging.DEBUG)
45
- logger.debug("디버그 모드가 활성화되었습니다.")
46
-
19
+ def __init__(self):
20
+ """Perforce 인스턴스를 초기화합니다."""
47
21
  self.p4 = P4()
48
22
  self.connected = False
49
23
  self.workspaceRoot = r""
50
- logger.info("Perforce 인스턴스 생성됨")
24
+ self.last_error = None
25
+ self.last_warnings = []
51
26
 
52
27
  def _is_connected(self) -> bool:
53
28
  """Perforce 서버 연결 상태를 확인합니다.
@@ -56,22 +31,40 @@ class Perforce:
56
31
  bool: 연결되어 있으면 True, 아니면 False
57
32
  """
58
33
  if not self.connected:
59
- logger.warning("Perforce 서버에 연결되지 않았습니다.")
34
+ self.last_error = "Perforce 서버에 연결되지 않았습니다."
60
35
  return False
61
36
  return True
62
37
 
63
38
  def _handle_p4_exception(self, e: P4Exception, context_msg: str = "") -> None:
64
- """P4Exception을 처리하고 로깅합니다.
39
+ """P4Exception을 처리하고 에러 정보를 저장합니다.
65
40
 
66
41
  Args:
67
42
  e (P4Exception): 발생한 예외
68
43
  context_msg (str, optional): 예외가 발생한 컨텍스트 설명
69
44
  """
70
- logger.error(f"{context_msg} 중 P4Exception 발생: {e}")
71
- for err in self.p4.errors:
72
- logger.error(f" P4 Error: {err}")
73
- for warn in self.p4.warnings:
74
- logger.warning(f" P4 Warning: {warn}")
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
+ """마지막 경고 메시지들을 반환합니다.
58
+
59
+ Returns:
60
+ list: 마지막 경고 메시지들의 리스트
61
+ """
62
+ return self.last_warnings
63
+
64
+ def clear_last_error(self) -> None:
65
+ """마지막 에러와 경고 정보를 초기화합니다."""
66
+ self.last_error = None
67
+ self.last_warnings = []
75
68
 
76
69
  def connect(self, workspace_name: str) -> bool:
77
70
  """지정된 워크스페이스에 연결합니다.
@@ -82,7 +75,7 @@ class Perforce:
82
75
  Returns:
83
76
  bool: 연결 성공 시 True, 실패 시 False
84
77
  """
85
- logger.info(f"'{workspace_name}' 워크스페이스에 연결 시도 중...")
78
+ self.clear_last_error()
86
79
  try:
87
80
  self.p4.client = workspace_name
88
81
  self.p4.connect()
@@ -97,16 +90,14 @@ class Perforce:
97
90
  root_path = os.path.normpath(root_path)
98
91
 
99
92
  self.workspaceRoot = root_path
100
- logger.info(f"워크스페이스 루트 절대 경로: {self.workspaceRoot}")
101
93
  except (IndexError, KeyError) as e:
102
- logger.error(f"워크스페이스 루트 경로 가져오기 실패: {e}")
94
+ self.last_error = f"워크스페이스 루트 경로 가져오기 실패: {e}"
103
95
  self.workspaceRoot = ""
104
96
 
105
- logger.info(f"'{workspace_name}' 워크스페이스에 성공적으로 연결됨 (User: {self.p4.user}, Port: {self.p4.port})")
106
97
  return True
107
98
  except P4Exception as e:
108
99
  self.connected = False
109
- self._handle_p4_exception(e, f"'{workspace_name}' 워크스페이스 연결")
100
+ self._handle_p4_exception(e, f"'{workspace_name}' 워크스페이스 연결 실패")
110
101
  return False
111
102
 
112
103
  def get_pending_change_list(self) -> list:
@@ -117,7 +108,7 @@ class Perforce:
117
108
  """
118
109
  if not self._is_connected():
119
110
  return []
120
- logger.debug("Pending 체인지 리스트 조회 중...")
111
+ self.clear_last_error()
121
112
  try:
122
113
  pending_changes = self.p4.run_changes("-s", "pending", "-u", self.p4.user, "-c", self.p4.client)
123
114
  change_numbers = [int(cl['change']) for cl in pending_changes]
@@ -129,10 +120,9 @@ class Perforce:
129
120
  if cl_info:
130
121
  change_list_info.append(cl_info)
131
122
 
132
- logger.info(f"Pending 체인지 리스트 {len(change_list_info)}개 조회 완료")
133
123
  return change_list_info
134
124
  except P4Exception as e:
135
- self._handle_p4_exception(e, "Pending 체인지 리스트 조회")
125
+ self._handle_p4_exception(e, "Pending 체인지 리스트 조회 실패")
136
126
  return []
137
127
 
138
128
  def create_change_list(self, description: str) -> dict:
@@ -146,19 +136,18 @@ class Perforce:
146
136
  """
147
137
  if not self._is_connected():
148
138
  return {}
149
- logger.info(f"새 체인지 리스트 생성 시도: '{description}'")
139
+ self.clear_last_error()
150
140
  try:
151
141
  change_spec = self.p4.fetch_change()
152
142
  change_spec["Description"] = description
153
143
  result = self.p4.save_change(change_spec)
154
144
  created_change_number = int(result[0].split()[1])
155
- logger.info(f"체인지 리스트 {created_change_number} 생성 완료: '{description}'")
156
145
  return self.get_change_list_by_number(created_change_number)
157
146
  except P4Exception as e:
158
- self._handle_p4_exception(e, f"체인지 리스트 생성 ('{description}')")
147
+ self._handle_p4_exception(e, f"체인지 리스트 생성 실패 ('{description}')")
159
148
  return {}
160
149
  except (IndexError, ValueError) as e:
161
- logger.error(f"체인지 리스트 번호 파싱 오류: {e}")
150
+ self.last_error = f"체인지 리스트 번호 파싱 오류: {e}"
162
151
  return {}
163
152
 
164
153
  def get_change_list_by_number(self, change_list_number: int) -> dict:
@@ -172,17 +161,16 @@ class Perforce:
172
161
  """
173
162
  if not self._is_connected():
174
163
  return {}
175
- logger.debug(f"체인지 리스트 {change_list_number} 정보 조회 중...")
164
+ self.clear_last_error()
176
165
  try:
177
166
  cl_info = self.p4.fetch_change(change_list_number)
178
167
  if cl_info:
179
- logger.info(f"체인지 리스트 {change_list_number} 정보 조회 완료.")
180
168
  return cl_info
181
169
  else:
182
- logger.warning(f"체인지 리스트 {change_list_number}를 찾을 수 없습니다.")
170
+ self.last_error = f"체인지 리스트 {change_list_number}를 찾을 수 없습니다."
183
171
  return {}
184
172
  except P4Exception as e:
185
- self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 정보 조회")
173
+ self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 정보 조회 실패")
186
174
  return {}
187
175
 
188
176
  def get_change_list_by_description(self, description: str) -> dict:
@@ -196,18 +184,16 @@ class Perforce:
196
184
  """
197
185
  if not self._is_connected():
198
186
  return {}
199
- logger.debug(f"설명으로 체인지 리스트 조회 중: '{description}'")
187
+ self.clear_last_error()
200
188
  try:
201
189
  pending_changes = self.p4.run_changes("-l", "-s", "pending", "-u", self.p4.user, "-c", self.p4.client)
202
190
  for cl in pending_changes:
203
191
  cl_desc = cl.get('Description', b'').decode('utf-8', 'replace').strip()
204
192
  if cl_desc == description.strip():
205
- logger.info(f"설명 '{description}'에 해당하는 체인지 리스트 {cl['change']} 조회 완료.")
206
193
  return self.get_change_list_by_number(int(cl['change']))
207
- logger.info(f"설명 '{description}'에 해당하는 Pending 체인지 리스트를 찾을 수 없습니다.")
208
194
  return {}
209
195
  except P4Exception as e:
210
- self._handle_p4_exception(e, f"설명으로 체인지 리스트 조회 ('{description}')")
196
+ self._handle_p4_exception(e, f"설명으로 체인지 리스트 조회 실패 ('{description}')")
211
197
  return {}
212
198
 
213
199
  def get_change_list_by_description_pattern(self, description_pattern: str, exact_match: bool = False) -> list:
@@ -224,9 +210,7 @@ class Perforce:
224
210
  if not self._is_connected():
225
211
  return []
226
212
 
227
- search_type = "정확히 일치" if exact_match else "패턴 포함"
228
- logger.debug(f"설명 패턴으로 체인지 리스트 조회 중 ({search_type}): '{description_pattern}'")
229
-
213
+ self.clear_last_error()
230
214
  try:
231
215
  pending_changes = self.p4.run_changes("-l", "-s", "pending", "-u", self.p4.user, "-c", self.p4.client)
232
216
  matching_changes = []
@@ -248,18 +232,25 @@ class Perforce:
248
232
  change_info = self.get_change_list_by_number(change_number)
249
233
  if change_info:
250
234
  matching_changes.append(change_info)
251
- logger.info(f"패턴 '{description_pattern}'에 매칭되는 체인지 리스트 {change_number} 발견: '{cl_desc}'")
252
-
253
- if matching_changes:
254
- logger.info(f"패턴 '{description_pattern}'에 매칭되는 체인지 리스트 {len(matching_changes)}개 조회 완료.")
255
- else:
256
- logger.info(f"패턴 '{description_pattern}'에 매칭되는 Pending 체인지 리스트를 찾을 수 없습니다.")
257
235
 
258
236
  return matching_changes
259
237
  except P4Exception as e:
260
- self._handle_p4_exception(e, f"설명 패턴으로 체인지 리스트 조회 ('{description_pattern}')")
238
+ self._handle_p4_exception(e, f"설명 패턴으로 체인지 리스트 조회 실패 ('{description_pattern}')")
261
239
  return []
262
240
 
241
+ def disconnect(self):
242
+ """Perforce 서버와의 연결을 해제합니다."""
243
+ if self.connected:
244
+ try:
245
+ self.p4.disconnect()
246
+ self.connected = False
247
+ except P4Exception as e:
248
+ self._handle_p4_exception(e, "Perforce 서버 연결 해제")
249
+
250
+ def __del__(self):
251
+ """객체가 소멸될 때 자동으로 연결을 해제합니다."""
252
+ self.disconnect()
253
+
263
254
  def check_files_checked_out(self, file_paths: list) -> dict:
264
255
  """파일들의 체크아웃 상태를 확인합니다.
265
256
 
@@ -281,11 +272,15 @@ class Perforce:
281
272
  if not self._is_connected():
282
273
  return {}
283
274
  if not file_paths:
284
- logger.debug("체크아웃 상태 확인할 파일 목록이 비어있습니다.")
285
275
  return {}
286
276
 
287
- logger.debug(f"파일 체크아웃 상태 확인 (파일 {len(file_paths)}개)")
277
+ # 타입 검증: 리스트가 아닌 경우 에러 발생
278
+ if not isinstance(file_paths, list):
279
+ error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 is_file_checked_out() 메서드를 사용하세요."
280
+ self.last_error = error_msg
281
+ raise TypeError(error_msg)
288
282
 
283
+ self.clear_last_error()
289
284
  result = {}
290
285
  try:
291
286
  # 각 파일의 상태 확인
@@ -311,26 +306,14 @@ class Perforce:
311
306
  file_status['user'] = file_info.get('user', '')
312
307
  file_status['workspace'] = file_info.get('client', '')
313
308
 
314
- logger.debug(f"파일 '{file_path}' 체크아웃됨: CL {file_status['change_list']}, "
315
- f"액션: {file_status['action']}, 사용자: {file_status['user']}, "
316
- f"워크스페이스: {file_status['workspace']}")
317
- else:
318
- # 파일이 체크아웃되지 않음
319
- logger.debug(f"파일 '{file_path}' 체크아웃되지 않음")
320
-
321
309
  except P4Exception as e:
322
310
  # 파일이 perforce에 없거나 접근할 수 없는 경우
323
- if any("not opened" in err.lower() or "no such file" in err.lower()
324
- for err in self.p4.errors):
325
- logger.debug(f"파일 '{file_path}' 체크아웃되지 않음 (perforce에 없거나 접근 불가)")
326
- else:
311
+ if not any("not opened" in err.lower() or "no such file" in err.lower()
312
+ for err in self.p4.errors):
327
313
  self._handle_p4_exception(e, f"파일 '{file_path}' 체크아웃 상태 확인")
328
314
 
329
315
  result[file_path] = file_status
330
316
 
331
- checked_out_count = sum(1 for status in result.values() if status['is_checked_out'])
332
- logger.info(f"파일 체크아웃 상태 확인 완료: 전체 {len(file_paths)}개 중 {checked_out_count}개 체크아웃됨")
333
-
334
317
  return result
335
318
 
336
319
  except P4Exception as e:
@@ -362,8 +345,7 @@ class Perforce:
362
345
  if not self._is_connected():
363
346
  return False
364
347
 
365
- logger.debug(f"파일 '{file_path}'가 체인지 리스트 {change_list_number}에 있는지 확인 중...")
366
-
348
+ self.clear_last_error()
367
349
  try:
368
350
  # 해당 체인지 리스트의 파일들 가져오기
369
351
  opened_files = self.p4.run_opened("-c", change_list_number)
@@ -376,11 +358,8 @@ class Perforce:
376
358
  normalized_client_file = os.path.normpath(client_file)
377
359
 
378
360
  if normalized_client_file == normalized_file_path:
379
- logger.debug(f"파일 '{file_path}'가 체인지 리스트 {change_list_number}에서 발견됨 "
380
- f"(액션: {file_info.get('action', '')})")
381
361
  return True
382
362
 
383
- logger.debug(f"파일 '{file_path}'가 체인지 리스트 {change_list_number}에 없음")
384
363
  return False
385
364
 
386
365
  except P4Exception as e:
@@ -401,7 +380,7 @@ class Perforce:
401
380
  """
402
381
  if not self._is_connected():
403
382
  return {}
404
- logger.info(f"체인지 리스트 {change_list_number} 편집 시도...")
383
+ self.clear_last_error()
405
384
  try:
406
385
  if description is not None:
407
386
  change_spec = self.p4.fetch_change(change_list_number)
@@ -409,13 +388,11 @@ class Perforce:
409
388
  if current_description != description.strip():
410
389
  change_spec['Description'] = description
411
390
  self.p4.save_change(change_spec)
412
- logger.info(f"체인지 리스트 {change_list_number} 설명 변경 완료: '{description}'")
413
391
 
414
392
  if add_file_paths:
415
393
  for file_path in add_file_paths:
416
394
  try:
417
395
  self.p4.run_reopen("-c", change_list_number, file_path)
418
- logger.info(f"파일 '{file_path}'를 체인지 리스트 {change_list_number}로 이동 완료.")
419
396
  except P4Exception as e_reopen:
420
397
  self._handle_p4_exception(e_reopen, f"파일 '{file_path}'을 CL {change_list_number}로 이동")
421
398
 
@@ -423,7 +400,6 @@ class Perforce:
423
400
  for file_path in remove_file_paths:
424
401
  try:
425
402
  self.p4.run_revert("-c", change_list_number, file_path)
426
- logger.info(f"파일 '{file_path}'를 체인지 리스트 {change_list_number}에서 제거(revert) 완료.")
427
403
  except P4Exception as e_revert:
428
404
  self._handle_p4_exception(e_revert, f"파일 '{file_path}'을 CL {change_list_number}에서 제거(revert)")
429
405
 
@@ -447,7 +423,7 @@ class Perforce:
447
423
  """
448
424
  if not self._is_connected():
449
425
  return False
450
- logger.info(f"파일 '{file_path}'에 대한 '{op_name}' 작업 시도 (CL: {change_list_number})...")
426
+ self.clear_last_error()
451
427
  try:
452
428
  if command == "edit":
453
429
  self.p4.run_edit("-c", change_list_number, file_path)
@@ -456,9 +432,8 @@ class Perforce:
456
432
  elif command == "delete":
457
433
  self.p4.run_delete("-c", change_list_number, file_path)
458
434
  else:
459
- logger.error(f"지원되지 않는 파일 작업: {command}")
435
+ self.last_error = f"지원되지 않는 파일 작업: {command}"
460
436
  return False
461
- logger.info(f"파일 '{file_path}'에 대한 '{op_name}' 작업 성공 (CL: {change_list_number}).")
462
437
  return True
463
438
  except P4Exception as e:
464
439
  self._handle_p4_exception(e, f"파일 '{file_path}' {op_name} (CL: {change_list_number})")
@@ -487,23 +462,20 @@ class Perforce:
487
462
  bool: 모든 파일 체크아웃 성공 시 True, 하나라도 실패 시 False
488
463
  """
489
464
  if not file_paths:
490
- logger.debug("체크아웃할 파일 목록이 비어있습니다.")
491
465
  return True
492
-
493
- logger.info(f"체인지 리스트 {change_list_number}에 {len(file_paths)}개 파일 체크아웃 시도...")
494
466
 
467
+ # 타입 검증: 리스트가 아닌 경우 에러 발생
468
+ if not isinstance(file_paths, list):
469
+ error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 checkout_file() 메서드를 사용하세요."
470
+ self.last_error = error_msg
471
+ raise TypeError(error_msg)
472
+
495
473
  all_success = True
496
474
  for file_path in file_paths:
497
475
  success = self.checkout_file(file_path, change_list_number)
498
476
  if not success:
499
477
  all_success = False
500
- logger.warning(f"파일 '{file_path}' 체크아웃 실패")
501
478
 
502
- if all_success:
503
- logger.info(f"모든 파일({len(file_paths)}개)을 체인지 리스트 {change_list_number}에 성공적으로 체크아웃했습니다.")
504
- else:
505
- logger.warning(f"일부 파일을 체인지 리스트 {change_list_number}에 체크아웃하지 못했습니다.")
506
-
507
479
  return all_success
508
480
 
509
481
  def add_file(self, file_path: str, change_list_number: int) -> bool:
@@ -529,23 +501,20 @@ class Perforce:
529
501
  bool: 모든 파일 추가 성공 시 True, 하나라도 실패 시 False
530
502
  """
531
503
  if not file_paths:
532
- logger.debug("추가할 파일 목록이 비어있습니다.")
533
504
  return True
534
-
535
- logger.info(f"체인지 리스트 {change_list_number}에 {len(file_paths)}개 파일 추가 시도...")
536
505
 
506
+ # 타입 검증: 리스트가 아닌 경우 에러 발생
507
+ if not isinstance(file_paths, list):
508
+ error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 add_file() 메서드를 사용하세요."
509
+ self.last_error = error_msg
510
+ raise TypeError(error_msg)
511
+
537
512
  all_success = True
538
513
  for file_path in file_paths:
539
514
  success = self.add_file(file_path, change_list_number)
540
515
  if not success:
541
516
  all_success = False
542
- logger.warning(f"파일 '{file_path}' 추가 실패")
543
517
 
544
- if all_success:
545
- logger.info(f"모든 파일({len(file_paths)}개)을 체인지 리스트 {change_list_number}에 성공적으로 추가했습니다.")
546
- else:
547
- logger.warning(f"일부 파일을 체인지 리스트 {change_list_number}에 추가하지 못했습니다.")
548
-
549
518
  return all_success
550
519
 
551
520
  def delete_file(self, file_path: str, change_list_number: int) -> bool:
@@ -571,23 +540,20 @@ class Perforce:
571
540
  bool: 모든 파일 삭제 성공 시 True, 하나라도 실패 시 False
572
541
  """
573
542
  if not file_paths:
574
- logger.debug("삭제할 파일 목록이 비어있습니다.")
575
543
  return True
576
-
577
- logger.info(f"체인지 리스트 {change_list_number}에서 {len(file_paths)}개 파일 삭제 시도...")
578
544
 
545
+ # 타입 검증: 리스트가 아닌 경우 에러 발생
546
+ if not isinstance(file_paths, list):
547
+ error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 delete_file() 메서드를 사용하세요."
548
+ self.last_error = error_msg
549
+ raise TypeError(error_msg)
550
+
579
551
  all_success = True
580
552
  for file_path in file_paths:
581
553
  success = self.delete_file(file_path, change_list_number)
582
554
  if not success:
583
555
  all_success = False
584
- logger.warning(f"파일 '{file_path}' 삭제 실패")
585
556
 
586
- if all_success:
587
- logger.info(f"모든 파일({len(file_paths)}개)을 체인지 리스트 {change_list_number}에서 성공적으로 삭제했습니다.")
588
- else:
589
- logger.warning(f"일부 파일을 체인지 리스트 {change_list_number}에서 삭제하지 못했습니다.")
590
-
591
557
  return all_success
592
558
 
593
559
  def submit_change_list(self, change_list_number: int, auto_revert_unchanged: bool = True) -> bool:
@@ -603,17 +569,16 @@ class Perforce:
603
569
  """
604
570
  if not self._is_connected():
605
571
  return False
606
- logger.info(f"체인지 리스트 {change_list_number} 제출 시도...")
572
+ self.clear_last_error()
607
573
 
608
574
  submit_success = False
609
575
  try:
610
576
  self.p4.run_submit("-c", change_list_number)
611
- logger.info(f"체인지 리스트 {change_list_number} 제출 성공.")
612
577
  submit_success = True
613
578
  except P4Exception as e:
614
- self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 제출")
579
+ self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 제출 실패")
615
580
  if any("nothing to submit" in err.lower() for err in self.p4.errors):
616
- logger.warning(f"체인지 리스트 {change_list_number}에 제출할 파일이 없습니다.")
581
+ self.last_error = f"체인지 리스트 {change_list_number}에 제출할 파일이 없습니다."
617
582
  submit_success = False
618
583
 
619
584
  # 제출 성공 여부와 관계없이 후속 작업 실행
@@ -626,7 +591,8 @@ class Perforce:
626
591
  # 빈 체인지 리스트 삭제
627
592
  self.delete_empty_change_list(change_list_number)
628
593
  except Exception as e:
629
- logger.error(f"체인지 리스트 {change_list_number} 제출 후속 작업 중 오류 발생: {e}")
594
+ if not self.last_error: # 기존 에러가 없는 경우만 설정
595
+ self.last_error = f"체인지 리스트 {change_list_number} 제출 후 후속 작업 중 오류 발생: {e}"
630
596
 
631
597
  return submit_success
632
598
 
@@ -636,13 +602,11 @@ class Perforce:
636
602
  Args:
637
603
  change_list_number (int): 체인지 리스트 번호
638
604
  """
639
- logger.debug(f"체인지 리스트 {change_list_number}에서 변경사항이 없는 파일들 자동 리버트 시도...")
640
605
  try:
641
606
  # 체인지 리스트에서 체크아웃된 파일들 가져오기
642
607
  opened_files = self.p4.run_opened("-c", change_list_number)
643
608
 
644
609
  if not opened_files:
645
- logger.debug(f"체인지 리스트 {change_list_number}에 체크아웃된 파일이 없습니다.")
646
610
  return
647
611
 
648
612
  unchanged_files = []
@@ -659,45 +623,29 @@ class Perforce:
659
623
  # diff 결과가 비어있으면 변경사항이 없음
660
624
  if not diff_result:
661
625
  unchanged_files.append(file_path)
662
- logger.debug(f"파일 '{file_path}'에 변경사항이 없어 리버트 대상으로 추가")
663
- else:
664
- logger.debug(f"파일 '{file_path}'에 변경사항이 있어 리버트하지 않음")
665
626
 
666
- except P4Exception as e:
627
+ except P4Exception:
667
628
  # diff 명령 실패 시에도 리버트 대상으로 추가 (안전하게 처리)
668
629
  unchanged_files.append(file_path)
669
- logger.debug(f"파일 '{file_path}' diff 확인 실패, 리버트 대상으로 추가: {e}")
670
- else:
671
- logger.debug(f"파일 '{file_path}'는 {action} 액션이므로 리버트하지 않음")
672
630
 
673
631
  # 변경사항이 없는 파일들을 리버트
674
632
  if unchanged_files:
675
- logger.info(f"체인지 리스트 {change_list_number}에서 변경사항이 없는 파일 {len(unchanged_files)}개 자동 리버트 시도...")
676
633
  for file_path in unchanged_files:
677
634
  try:
678
635
  self.p4.run_revert("-c", change_list_number, file_path)
679
- logger.info(f"파일 '{file_path}' 자동 리버트 완료")
680
- except P4Exception as e:
681
- self._handle_p4_exception(e, f"파일 '{file_path}' 자동 리버트")
682
- logger.info(f"체인지 리스트 {change_list_number}에서 변경사항이 없는 파일 {len(unchanged_files)}개 자동 리버트 완료")
683
- else:
684
- logger.debug(f"체인지 리스트 {change_list_number}에서 변경사항이 없는 파일이 없습니다.")
685
-
686
- # default change list에서도 변경사항이 없는 파일들 처리
687
- self._auto_revert_unchanged_files_in_default_changelist()
636
+ except P4Exception:
637
+ pass # 개별 파일 리버트 실패는 무시
688
638
 
689
- except P4Exception as e:
690
- self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 자동 리버트 처리")
639
+ except P4Exception:
640
+ pass # 자동 리버트 실패는 무시
691
641
 
692
642
  def _auto_revert_unchanged_files_in_default_changelist(self) -> None:
693
643
  """default change list에서 변경사항이 없는 체크아웃된 파일들을 자동으로 리버트합니다."""
694
- logger.debug("default change list에서 변경사항이 없는 파일들 자동 리버트 시도...")
695
644
  try:
696
645
  # get_default_change_list를 사용해서 default change list의 파일들 가져오기
697
646
  default_cl_info = self.get_default_change_list()
698
647
 
699
648
  if not default_cl_info or not default_cl_info.get('Files'):
700
- logger.debug("default change list에 체크아웃된 파일이 없습니다.")
701
649
  return
702
650
 
703
651
  files_list = default_cl_info.get('Files', [])
@@ -711,30 +659,21 @@ class Perforce:
711
659
  # diff 결과가 비어있으면 변경사항이 없음
712
660
  if not diff_result:
713
661
  unchanged_files.append(file_path)
714
- logger.debug(f"default change list의 파일 '{file_path}'에 변경사항이 없어 리버트 대상으로 추가")
715
- else:
716
- logger.debug(f"default change list의 파일 '{file_path}'에 변경사항이 있어 리버트하지 않음")
717
662
 
718
- except P4Exception as e:
663
+ except P4Exception:
719
664
  # diff 명령 실패 시에도 리버트 대상으로 추가 (안전하게 처리)
720
665
  unchanged_files.append(file_path)
721
- logger.debug(f"default change list의 파일 '{file_path}' diff 확인 실패, 리버트 대상으로 추가: {e}")
722
666
 
723
667
  # 변경사항이 없는 파일들을 리버트
724
668
  if unchanged_files:
725
- logger.info(f"default change list에서 변경사항이 없는 파일 {len(unchanged_files)}개 자동 리버트 시도...")
726
669
  for file_path in unchanged_files:
727
670
  try:
728
671
  self.p4.run_revert(file_path)
729
- logger.info(f"default change list의 파일 '{file_path}' 자동 리버트 완료")
730
- except P4Exception as e:
731
- self._handle_p4_exception(e, f"default change list의 파일 '{file_path}' 자동 리버트")
732
- logger.info(f"default change list에서 변경사항이 없는 파일 {len(unchanged_files)}개 자동 리버트 완료")
733
- else:
734
- logger.debug("default change list에서 변경사항이 없는 파일이 없습니다.")
672
+ except P4Exception:
673
+ pass # 개별 파일 리버트 실패는 무시
735
674
 
736
- except P4Exception as e:
737
- self._handle_p4_exception(e, "default change list 자동 리버트 처리")
675
+ except P4Exception:
676
+ pass # 자동 리버트 실패는 무시
738
677
 
739
678
  def revert_change_list(self, change_list_number: int) -> bool:
740
679
  """체인지 리스트를 되돌리고 삭제합니다.
@@ -749,24 +688,21 @@ class Perforce:
749
688
  """
750
689
  if not self._is_connected():
751
690
  return False
752
- logger.info(f"체인지 리스트 {change_list_number} 전체 되돌리기 및 삭제 시도...")
691
+ self.clear_last_error()
753
692
  try:
754
693
  # 체인지 리스트의 모든 파일 되돌리기
755
694
  self.p4.run_revert("-c", change_list_number, "//...")
756
- logger.info(f"체인지 리스트 {change_list_number} 전체 되돌리기 성공.")
757
695
 
758
696
  # 빈 체인지 리스트 삭제
759
697
  try:
760
698
  self.p4.run_change("-d", change_list_number)
761
- logger.info(f"체인지 리스트 {change_list_number} 삭제 완료.")
762
699
  except P4Exception as e_delete:
763
700
  self._handle_p4_exception(e_delete, f"체인지 리스트 {change_list_number} 삭제")
764
- logger.warning(f"파일 되돌리기는 성공했으나 체인지 리스트 {change_list_number} 삭제에 실패했습니다.")
765
701
  return False
766
702
 
767
703
  return True
768
704
  except P4Exception as e:
769
- self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 전체 되돌리기")
705
+ self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 전체 되돌리기 실패")
770
706
  return False
771
707
 
772
708
  def delete_empty_change_list(self, change_list_number: int) -> bool:
@@ -781,22 +717,21 @@ class Perforce:
781
717
  if not self._is_connected():
782
718
  return False
783
719
 
784
- logger.info(f"체인지 리스트 {change_list_number} 삭제 시도 중...")
720
+ self.clear_last_error()
785
721
  try:
786
722
  # 체인지 리스트 정보 가져오기
787
723
  change_spec = self.p4.fetch_change(change_list_number)
788
724
 
789
725
  # 파일이 있는지 확인
790
726
  if change_spec and change_spec.get('Files') and len(change_spec['Files']) > 0:
791
- logger.warning(f"체인지 리스트 {change_list_number}에 파일이 {len(change_spec['Files'])}개 있어 삭제할 수 없습니다.")
727
+ self.last_error = f"체인지 리스트 {change_list_number}에 파일이 {len(change_spec['Files'])}개 있어 삭제할 수 없습니다."
792
728
  return False
793
729
 
794
730
  # 빈 체인지 리스트 삭제
795
731
  self.p4.run_change("-d", change_list_number)
796
- logger.info(f"빈 체인지 리스트 {change_list_number} 삭제 완료.")
797
732
  return True
798
733
  except P4Exception as e:
799
- self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 삭제")
734
+ self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 삭제 실패")
800
735
  return False
801
736
 
802
737
  def revert_file(self, file_path: str, change_list_number: int) -> bool:
@@ -812,10 +747,9 @@ class Perforce:
812
747
  if not self._is_connected():
813
748
  return False
814
749
 
815
- logger.info(f"파일 '{file_path}'을 체인지 리스트 {change_list_number}에서 되돌리기 시도...")
750
+ self.clear_last_error()
816
751
  try:
817
752
  self.p4.run_revert("-c", change_list_number, file_path)
818
- logger.info(f"파일 '{file_path}'를 체인지 리스트 {change_list_number}에서 되돌리기 성공.")
819
753
  return True
820
754
  except P4Exception as e:
821
755
  self._handle_p4_exception(e, f"파일 '{file_path}'를 체인지 리스트 {change_list_number}에서 되돌리기")
@@ -834,23 +768,20 @@ class Perforce:
834
768
  if not self._is_connected():
835
769
  return False
836
770
  if not file_paths:
837
- logger.warning("되돌릴 파일 목록이 비어있습니다.")
838
771
  return True
839
-
840
- logger.info(f"체인지 리스트 {change_list_number}에서 {len(file_paths)}개 파일 되돌리기 시도...")
841
772
 
773
+ # 타입 검증: 리스트가 아닌 경우 에러 발생
774
+ if not isinstance(file_paths, list):
775
+ error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 revert_file() 메서드를 사용하세요."
776
+ self.last_error = error_msg
777
+ raise TypeError(error_msg)
778
+
842
779
  all_success = True
843
780
  for file_path in file_paths:
844
781
  success = self.revert_file(file_path, change_list_number)
845
782
  if not success:
846
783
  all_success = False
847
- logger.warning(f"파일 '{file_path}' 되돌리기 실패")
848
784
 
849
- if all_success:
850
- logger.info(f"모든 파일({len(file_paths)}개)을 체인지 리스트 {change_list_number}에서 성공적으로 되돌렸습니다.")
851
- else:
852
- logger.warning(f"일부 파일을 체인지 리스트 {change_list_number}에서 되돌리지 못했습니다.")
853
-
854
785
  return all_success
855
786
 
856
787
  def check_update_required(self, file_paths: list) -> bool:
@@ -866,20 +797,24 @@ class Perforce:
866
797
  if not self._is_connected():
867
798
  return False
868
799
  if not file_paths:
869
- logger.debug("업데이트 필요 여부 확인할 파일/폴더 목록이 비어있습니다.")
870
800
  return False
871
801
 
802
+ # 타입 검증: 리스트가 아닌 경우 에러 발생
803
+ if not isinstance(file_paths, list):
804
+ error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 경로도 리스트로 감싸서 전달하세요: ['{file_paths}']"
805
+ self.last_error = error_msg
806
+ raise TypeError(error_msg)
807
+
872
808
  # 폴더 경로에 재귀적 와일드카드 패턴을 추가
873
809
  processed_paths = []
874
810
  for path in file_paths:
875
811
  if os.path.isdir(path):
876
812
  # 폴더 경로에 '...'(재귀) 패턴을 추가
877
813
  processed_paths.append(os.path.join(path, '...'))
878
- logger.debug(f"폴더 경로를 재귀 패턴으로 변환: {path} -> {os.path.join(path, '...')}")
879
814
  else:
880
815
  processed_paths.append(path)
881
816
 
882
- logger.debug(f"파일/폴더 업데이트 필요 여부 확인 중 (항목 {len(processed_paths)}개): {processed_paths}")
817
+ self.clear_last_error()
883
818
  try:
884
819
  sync_preview_results = self.p4.run_sync("-n", processed_paths)
885
820
  needs_update = False
@@ -889,26 +824,30 @@ class Perforce:
889
824
  'no such file(s)' not in result.get('depotFile', ''):
890
825
  if result.get('how') and 'syncing' in result.get('how'):
891
826
  needs_update = True
892
- logger.info(f"파일 '{result.get('clientFile', result.get('depotFile'))}' 업데이트 필요: {result.get('how')}")
893
827
  break
894
828
  elif result.get('action') and result.get('action') not in ['checked', 'exists']:
895
829
  needs_update = True
896
- logger.info(f"파일 '{result.get('clientFile', result.get('depotFile'))}' 업데이트 필요 (action: {result.get('action')})")
897
830
  break
898
831
  elif isinstance(result, str):
899
832
  if "up-to-date" not in result and "no such file(s)" not in result:
900
833
  needs_update = True
901
- logger.info(f"파일 업데이트 필요 (문자열 결과): {result}")
902
834
  break
903
835
 
904
- if needs_update:
905
- logger.info(f"지정된 파일/폴더 중 업데이트가 필요한 파일이 있습니다.")
906
- else:
907
- logger.info(f"지정된 모든 파일/폴더가 최신 상태입니다.")
908
836
  return needs_update
909
837
  except P4Exception as e:
910
- self._handle_p4_exception(e, f"파일/폴더 업데이트 필요 여부 확인 ({processed_paths})")
911
- return False
838
+ # "up-to-date" 메시지는 정상적인 응답이므로 에러로 처리하지 않음
839
+ exception_str = str(e)
840
+ error_messages = [str(err) for err in self.p4.errors]
841
+ warning_messages = [str(warn) for warn in self.p4.warnings]
842
+
843
+ # P4Exception 자체 메시지나 에러/경고 메시지에서 "up-to-date" 확인
844
+ if ("up-to-date" in exception_str or
845
+ any("up-to-date" in msg for msg in error_messages) or
846
+ any("up-to-date" in msg for msg in warning_messages)):
847
+ return False
848
+ else:
849
+ self._handle_p4_exception(e, f"파일/폴더 업데이트 필요 여부 확인 ({processed_paths})")
850
+ return False
912
851
 
913
852
  def is_file_in_perforce(self, file_path: str) -> bool:
914
853
  """파일이 Perforce에 속하는지 확인합니다.
@@ -922,23 +861,20 @@ class Perforce:
922
861
  if not self._is_connected():
923
862
  return False
924
863
 
925
- logger.debug(f"파일 '{file_path}'가 Perforce에 속하는지 확인 중...")
864
+ self.clear_last_error()
926
865
  try:
927
866
  # p4 files 명령으로 파일 정보 조회
928
867
  file_info = self.p4.run_files(file_path)
929
868
 
930
869
  # 파일 정보가 있고, 'no such file(s)' 오류가 없는 경우
931
870
  if file_info and not any("no such file(s)" in str(err).lower() for err in self.p4.errors):
932
- logger.info(f"파일 '{file_path}'가 Perforce에 존재합니다.")
933
871
  return True
934
872
  else:
935
- logger.info(f"파일 '{file_path}'가 Perforce에 존재하지 않습니다.")
936
873
  return False
937
874
 
938
875
  except P4Exception as e:
939
- # 파일이 존재하지 않는 경우는 일반적인 상황이므로 경고 레벨로 로깅
876
+ # 파일이 존재하지 않는 경우는 일반적인 상황이므로 False 반환
940
877
  if any("no such file(s)" in err.lower() for err in self.p4.errors):
941
- logger.info(f"파일 '{file_path}'가 Perforce에 존재하지 않습니다.")
942
878
  return False
943
879
  else:
944
880
  self._handle_p4_exception(e, f"파일 '{file_path}' Perforce 존재 여부 확인")
@@ -949,7 +885,7 @@ class Perforce:
949
885
 
950
886
  Args:
951
887
  file_paths (list): 동기화할 파일 또는 폴더 경로 리스트.
952
- 폴더 경로는 자동으로 재귀적으로 처리됩니다.
888
+ 폴더 경로는 자동으로 재귀적으로 처리됩니다.
953
889
 
954
890
  Returns:
955
891
  bool: 동기화 성공 시 True, 실패 시 False
@@ -957,43 +893,55 @@ class Perforce:
957
893
  if not self._is_connected():
958
894
  return False
959
895
  if not file_paths:
960
- logger.debug("싱크할 파일/폴더 목록이 비어있습니다.")
961
896
  return True
962
897
 
898
+ # 타입 검증: 리스트가 아닌 경우 에러 발생
899
+ if not isinstance(file_paths, list):
900
+ error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 경로도 리스트로 감싸서 전달하세요: ['{file_paths}']"
901
+ self.last_error = error_msg
902
+ raise TypeError(error_msg)
903
+
963
904
  # 폴더 경로에 재귀적 와일드카드 패턴을 추가
964
905
  processed_paths = []
965
906
  for path in file_paths:
966
907
  if os.path.isdir(path):
967
908
  # 폴더 경로에 '...'(재귀) 패턴을 추가
968
909
  processed_paths.append(os.path.join(path, '...'))
969
- logger.debug(f"폴더 경로를 재귀 패턴으로 변환: {path} -> {os.path.join(path, '...')}")
970
910
  else:
971
911
  processed_paths.append(path)
972
912
 
973
- logger.info(f"파일/폴더 싱크 시도 (항목 {len(processed_paths)}개): {processed_paths}")
913
+ self.clear_last_error()
974
914
  try:
975
915
  self.p4.run_sync(processed_paths)
976
- logger.info(f"파일/폴더 싱크 완료: {processed_paths}")
977
916
  return True
978
917
  except P4Exception as e:
979
- self._handle_p4_exception(e, f"파일/폴더 싱크 ({processed_paths})")
918
+ self._handle_p4_exception(e, f"파일/폴더 싱크 실패 ({processed_paths})")
980
919
  return False
981
920
 
982
- def disconnect(self):
983
- """Perforce 서버와의 연결을 해제합니다."""
984
- if self.connected:
985
- try:
986
- self.p4.disconnect()
987
- self.connected = False
988
- logger.info("Perforce 서버 연결 해제 완료.")
989
- except P4Exception as e:
990
- self._handle_p4_exception(e, "Perforce 서버 연결 해제")
991
- else:
992
- logger.debug("Perforce 서버에 이미 연결되지 않은 상태입니다.")
921
+ def get_default_change_list(self) -> dict:
922
+ """default change list의 정보를 가져옵니다.
993
923
 
994
- def __del__(self):
995
- """객체가 소멸될 자동으로 연결을 해제합니다."""
996
- self.disconnect()
924
+ Returns:
925
+ dict: get_change_list_by_number와 동일한 형태의 딕셔너리
926
+ """
927
+ if not self._is_connected():
928
+ return {}
929
+ self.clear_last_error()
930
+ try:
931
+ opened_files = self.p4.run_opened("-c", "default")
932
+ files_list = [f.get('clientFile', '') for f in opened_files]
933
+ result = {
934
+ 'Change': 'default',
935
+ 'Description': 'Default change',
936
+ 'User': getattr(self.p4, 'user', ''),
937
+ 'Client': getattr(self.p4, 'client', ''),
938
+ 'Status': 'pending',
939
+ 'Files': files_list
940
+ }
941
+ return result
942
+ except P4Exception as e:
943
+ self._handle_p4_exception(e, "default change list 정보 조회 실패")
944
+ return {}
997
945
 
998
946
  def check_files_checked_out_all_users(self, file_paths: list) -> dict:
999
947
  """파일들의 체크아웃 상태를 모든 사용자/워크스페이스에서 확인합니다.
@@ -1016,11 +964,15 @@ class Perforce:
1016
964
  if not self._is_connected():
1017
965
  return {}
1018
966
  if not file_paths:
1019
- logger.debug("체크아웃 상태 확인할 파일 목록이 비어있습니다.")
1020
967
  return {}
1021
968
 
1022
- logger.debug(f"파일 체크아웃 상태 확인 - 모든 사용자 (파일 {len(file_paths)}개)")
969
+ # 타입 검증: 리스트가 아닌 경우 에러 발생
970
+ if not isinstance(file_paths, list):
971
+ error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 get_file_checkout_info_all_users() 메서드를 사용하세요."
972
+ self.last_error = error_msg
973
+ raise TypeError(error_msg)
1023
974
 
975
+ self.clear_last_error()
1024
976
  result = {}
1025
977
  try:
1026
978
  # 각 파일의 상태 확인
@@ -1046,26 +998,14 @@ class Perforce:
1046
998
  file_status['user'] = file_info.get('user', '')
1047
999
  file_status['client'] = file_info.get('client', '')
1048
1000
 
1049
- logger.debug(f"파일 '{file_path}' 체크아웃됨: CL {file_status['change_list']}, "
1050
- f"액션: {file_status['action']}, 사용자: {file_status['user']}, "
1051
- f"클라이언트: {file_status['client']}")
1052
- else:
1053
- # 파일이 체크아웃되지 않음
1054
- logger.debug(f"파일 '{file_path}' 체크아웃되지 않음 (모든 사용자)")
1055
-
1056
1001
  except P4Exception as e:
1057
1002
  # 파일이 perforce에 없거나 접근할 수 없는 경우
1058
- if any("not opened" in err.lower() or "no such file" in err.lower()
1059
- for err in self.p4.errors):
1060
- logger.debug(f"파일 '{file_path}' 체크아웃되지 않음 (perforce에 없거나 접근 불가)")
1061
- else:
1003
+ if not any("not opened" in err.lower() or "no such file" in err.lower()
1004
+ for err in self.p4.errors):
1062
1005
  self._handle_p4_exception(e, f"파일 '{file_path}' 체크아웃 상태 확인 (모든 사용자)")
1063
1006
 
1064
1007
  result[file_path] = file_status
1065
1008
 
1066
- checked_out_count = sum(1 for status in result.values() if status['is_checked_out'])
1067
- logger.info(f"파일 체크아웃 상태 확인 완료 (모든 사용자): 전체 {len(file_paths)}개 중 {checked_out_count}개 체크아웃됨")
1068
-
1069
1009
  return result
1070
1010
 
1071
1011
  except P4Exception as e:
@@ -1156,6 +1096,12 @@ class Perforce:
1156
1096
  if not file_paths:
1157
1097
  return []
1158
1098
 
1099
+ # 타입 검증: 리스트가 아닌 경우 에러 발생
1100
+ if not isinstance(file_paths, list):
1101
+ error_msg = f"file_paths는 리스트여야 합니다. 전달된 타입: {type(file_paths).__name__}. 단일 파일은 is_file_checked_out_by_others() 메서드를 사용하세요."
1102
+ self.last_error = error_msg
1103
+ raise TypeError(error_msg)
1104
+
1159
1105
  result = self.check_files_checked_out_all_users(file_paths)
1160
1106
  files_by_others = []
1161
1107
 
@@ -1177,31 +1123,4 @@ class Perforce:
1177
1123
  'action': status.get('action', '')
1178
1124
  })
1179
1125
 
1180
- logger.info(f"다른 사용자에 의해 체크아웃된 파일: {len(files_by_others)}개")
1181
- return files_by_others
1182
-
1183
- def get_default_change_list(self) -> dict:
1184
- """default change list의 정보를 가져옵니다.
1185
-
1186
- Returns:
1187
- dict: get_change_list_by_number와 동일한 형태의 딕셔너리
1188
- """
1189
- if not self._is_connected():
1190
- return {}
1191
- logger.debug("default change list 정보 조회 중...")
1192
- try:
1193
- opened_files = self.p4.run_opened("-c", "default")
1194
- files_list = [f.get('clientFile', '') for f in opened_files]
1195
- result = {
1196
- 'Change': 'default',
1197
- 'Description': 'Default change',
1198
- 'User': getattr(self.p4, 'user', ''),
1199
- 'Client': getattr(self.p4, 'client', ''),
1200
- 'Status': 'pending',
1201
- 'Files': files_list
1202
- }
1203
- logger.info(f"default change list 정보 조회 완료: {len(files_list)}개 파일")
1204
- return result
1205
- except P4Exception as e:
1206
- self._handle_p4_exception(e, "default change list 정보 조회")
1207
- return {}
1126
+ return files_by_others