pyjallib 0.1.10__py3-none-any.whl → 0.1.12__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
@@ -195,6 +195,183 @@ class Perforce:
195
195
  self._handle_p4_exception(e, f"설명으로 체인지 리스트 조회 ('{description}')")
196
196
  return {}
197
197
 
198
+ def get_change_list_by_description_pattern(self, description_pattern: str, exact_match: bool = False) -> list:
199
+ """설명 패턴과 일치하는 Pending 체인지 리스트들을 가져옵니다.
200
+
201
+ Args:
202
+ description_pattern (str): 검색할 설명 패턴
203
+ exact_match (bool, optional): True면 정확히 일치하는 설명만,
204
+ False면 패턴이 포함된 설명도 포함. 기본값 False
205
+
206
+ Returns:
207
+ list: 패턴과 일치하는 체인지 리스트 정보들의 리스트
208
+ """
209
+ if not self._is_connected():
210
+ return []
211
+
212
+ search_type = "정확히 일치" if exact_match else "패턴 포함"
213
+ logger.debug(f"설명 패턴으로 체인지 리스트 조회 중 ({search_type}): '{description_pattern}'")
214
+
215
+ try:
216
+ pending_changes = self.p4.run_changes("-l", "-s", "pending", "-u", self.p4.user, "-c", self.p4.client)
217
+ matching_changes = []
218
+
219
+ for cl in pending_changes:
220
+ cl_desc = cl.get('Description', b'').decode('utf-8', 'replace').strip()
221
+
222
+ # 패턴 매칭 로직
223
+ is_match = False
224
+ if exact_match:
225
+ # 정확한 일치
226
+ is_match = (cl_desc == description_pattern.strip())
227
+ else:
228
+ # 패턴이 포함되어 있는지 확인 (대소문자 구분 없음)
229
+ is_match = (description_pattern.lower().strip() in cl_desc.lower())
230
+
231
+ if is_match:
232
+ change_number = int(cl['change'])
233
+ change_info = self.get_change_list_by_number(change_number)
234
+ if change_info:
235
+ matching_changes.append(change_info)
236
+ logger.info(f"패턴 '{description_pattern}'에 매칭되는 체인지 리스트 {change_number} 발견: '{cl_desc}'")
237
+
238
+ if matching_changes:
239
+ logger.info(f"패턴 '{description_pattern}'에 매칭되는 체인지 리스트 {len(matching_changes)}개 조회 완료.")
240
+ else:
241
+ logger.info(f"패턴 '{description_pattern}'에 매칭되는 Pending 체인지 리스트를 찾을 수 없습니다.")
242
+
243
+ return matching_changes
244
+ except P4Exception as e:
245
+ self._handle_p4_exception(e, f"설명 패턴으로 체인지 리스트 조회 ('{description_pattern}')")
246
+ return []
247
+
248
+ def check_files_checked_out(self, file_paths: list) -> dict:
249
+ """파일들의 체크아웃 상태를 확인합니다.
250
+
251
+ Args:
252
+ file_paths (list): 확인할 파일 경로 리스트
253
+
254
+ Returns:
255
+ dict: 파일별 체크아웃 상태 정보
256
+ {
257
+ 'file_path': {
258
+ 'is_checked_out': bool,
259
+ 'change_list': int or None,
260
+ 'action': str or None,
261
+ 'user': str or None,
262
+ 'workspace': str or None
263
+ }
264
+ }
265
+ """
266
+ if not self._is_connected():
267
+ return {}
268
+ if not file_paths:
269
+ logger.debug("체크아웃 상태 확인할 파일 목록이 비어있습니다.")
270
+ return {}
271
+
272
+ logger.debug(f"파일 체크아웃 상태 확인 중 (파일 {len(file_paths)}개)")
273
+
274
+ result = {}
275
+ try:
276
+ # 각 파일의 상태 확인
277
+ for file_path in file_paths:
278
+ file_status = {
279
+ 'is_checked_out': False,
280
+ 'change_list': None,
281
+ 'action': None,
282
+ 'user': None,
283
+ 'workspace': None
284
+ }
285
+
286
+ try:
287
+ # p4 opened 명령으로 파일이 열려있는지 확인
288
+ opened_files = self.p4.run_opened(file_path)
289
+
290
+ if opened_files:
291
+ # 파일이 체크아웃되어 있음
292
+ file_info = opened_files[0]
293
+ file_status['is_checked_out'] = True
294
+ file_status['change_list'] = int(file_info.get('change', 0))
295
+ file_status['action'] = file_info.get('action', '')
296
+ file_status['user'] = file_info.get('user', '')
297
+ file_status['workspace'] = file_info.get('client', '')
298
+
299
+ logger.debug(f"파일 '{file_path}' 체크아웃됨: CL {file_status['change_list']}, "
300
+ f"액션: {file_status['action']}, 사용자: {file_status['user']}, "
301
+ f"워크스페이스: {file_status['workspace']}")
302
+ else:
303
+ # 파일이 체크아웃되지 않음
304
+ logger.debug(f"파일 '{file_path}' 체크아웃되지 않음")
305
+
306
+ except P4Exception as e:
307
+ # 파일이 perforce에 없거나 접근할 수 없는 경우
308
+ if any("not opened" in err.lower() or "no such file" in err.lower()
309
+ for err in self.p4.errors):
310
+ logger.debug(f"파일 '{file_path}' 체크아웃되지 않음 (perforce에 없거나 접근 불가)")
311
+ else:
312
+ self._handle_p4_exception(e, f"파일 '{file_path}' 체크아웃 상태 확인")
313
+
314
+ result[file_path] = file_status
315
+
316
+ checked_out_count = sum(1 for status in result.values() if status['is_checked_out'])
317
+ logger.info(f"파일 체크아웃 상태 확인 완료: 전체 {len(file_paths)}개 중 {checked_out_count}개 체크아웃됨")
318
+
319
+ return result
320
+
321
+ except P4Exception as e:
322
+ self._handle_p4_exception(e, f"파일들 체크아웃 상태 확인 ({file_paths})")
323
+ return {}
324
+
325
+ def is_file_checked_out(self, file_path: str) -> bool:
326
+ """단일 파일의 체크아웃 상태를 간단히 확인합니다.
327
+
328
+ Args:
329
+ file_path (str): 확인할 파일 경로
330
+
331
+ Returns:
332
+ bool: 체크아웃되어 있으면 True, 아니면 False
333
+ """
334
+ result = self.check_files_checked_out([file_path])
335
+ return result.get(file_path, {}).get('is_checked_out', False)
336
+
337
+ def is_file_in_pending_changelist(self, file_path: str, change_list_number: int) -> bool:
338
+ """특정 파일이 지정된 pending 체인지 리스트에 있는지 확인합니다.
339
+
340
+ Args:
341
+ file_path (str): 확인할 파일 경로
342
+ change_list_number (int): 확인할 체인지 리스트 번호
343
+
344
+ Returns:
345
+ bool: 파일이 해당 체인지 리스트에 있으면 True, 아니면 False
346
+ """
347
+ if not self._is_connected():
348
+ return False
349
+
350
+ logger.debug(f"파일 '{file_path}'가 체인지 리스트 {change_list_number}에 있는지 확인 중...")
351
+
352
+ try:
353
+ # 해당 체인지 리스트의 파일들 가져오기
354
+ opened_files = self.p4.run_opened("-c", change_list_number)
355
+
356
+ # 파일 경로 정규화
357
+ normalized_file_path = os.path.normpath(file_path)
358
+
359
+ for file_info in opened_files:
360
+ client_file = file_info.get('clientFile', '')
361
+ normalized_client_file = os.path.normpath(client_file)
362
+
363
+ if normalized_client_file == normalized_file_path:
364
+ logger.debug(f"파일 '{file_path}'가 체인지 리스트 {change_list_number}에서 발견됨 "
365
+ f"(액션: {file_info.get('action', '')})")
366
+ return True
367
+
368
+ logger.debug(f"파일 '{file_path}'가 체인지 리스트 {change_list_number}에 없음")
369
+ return False
370
+
371
+ except P4Exception as e:
372
+ self._handle_p4_exception(e, f"파일 '{file_path}' 체인지 리스트 {change_list_number} 포함 여부 확인")
373
+ return False
374
+
198
375
  def edit_change_list(self, change_list_number: int, description: str = None, add_file_paths: list = None, remove_file_paths: list = None) -> dict:
199
376
  """체인지 리스트를 편집합니다.
200
377
 
@@ -283,6 +460,36 @@ class Perforce:
283
460
  bool: 체크아웃 성공 시 True, 실패 시 False
284
461
  """
285
462
  return self._file_op("edit", file_path, change_list_number, "체크아웃")
463
+
464
+ def checkout_files(self, file_paths: list, change_list_number: int) -> bool:
465
+ """여러 파일을 한 번에 체크아웃합니다.
466
+
467
+ Args:
468
+ file_paths (list): 체크아웃할 파일 경로 리스트
469
+ change_list_number (int): 체인지 리스트 번호
470
+
471
+ Returns:
472
+ bool: 모든 파일 체크아웃 성공 시 True, 하나라도 실패 시 False
473
+ """
474
+ if not file_paths:
475
+ logger.debug("체크아웃할 파일 목록이 비어있습니다.")
476
+ return True
477
+
478
+ logger.info(f"체인지 리스트 {change_list_number}에 {len(file_paths)}개 파일 체크아웃 시도...")
479
+
480
+ all_success = True
481
+ for file_path in file_paths:
482
+ success = self.checkout_file(file_path, change_list_number)
483
+ if not success:
484
+ all_success = False
485
+ logger.warning(f"파일 '{file_path}' 체크아웃 실패")
486
+
487
+ if all_success:
488
+ logger.info(f"모든 파일({len(file_paths)}개)을 체인지 리스트 {change_list_number}에 성공적으로 체크아웃했습니다.")
489
+ else:
490
+ logger.warning(f"일부 파일을 체인지 리스트 {change_list_number}에 체크아웃하지 못했습니다.")
491
+
492
+ return all_success
286
493
 
287
494
  def add_file(self, file_path: str, change_list_number: int) -> bool:
288
495
  """파일을 추가합니다.
@@ -295,6 +502,36 @@ class Perforce:
295
502
  bool: 추가 성공 시 True, 실패 시 False
296
503
  """
297
504
  return self._file_op("add", file_path, change_list_number, "추가")
505
+
506
+ def add_files(self, file_paths: list, change_list_number: int) -> bool:
507
+ """여러 파일을 한 번에 추가합니다.
508
+
509
+ Args:
510
+ file_paths (list): 추가할 파일 경로 리스트
511
+ change_list_number (int): 체인지 리스트 번호
512
+
513
+ Returns:
514
+ bool: 모든 파일 추가 성공 시 True, 하나라도 실패 시 False
515
+ """
516
+ if not file_paths:
517
+ logger.debug("추가할 파일 목록이 비어있습니다.")
518
+ return True
519
+
520
+ logger.info(f"체인지 리스트 {change_list_number}에 {len(file_paths)}개 파일 추가 시도...")
521
+
522
+ all_success = True
523
+ for file_path in file_paths:
524
+ success = self.add_file(file_path, change_list_number)
525
+ if not success:
526
+ all_success = False
527
+ logger.warning(f"파일 '{file_path}' 추가 실패")
528
+
529
+ if all_success:
530
+ logger.info(f"모든 파일({len(file_paths)}개)을 체인지 리스트 {change_list_number}에 성공적으로 추가했습니다.")
531
+ else:
532
+ logger.warning(f"일부 파일을 체인지 리스트 {change_list_number}에 추가하지 못했습니다.")
533
+
534
+ return all_success
298
535
 
299
536
  def delete_file(self, file_path: str, change_list_number: int) -> bool:
300
537
  """파일을 삭제합니다.
@@ -307,6 +544,36 @@ class Perforce:
307
544
  bool: 삭제 성공 시 True, 실패 시 False
308
545
  """
309
546
  return self._file_op("delete", file_path, change_list_number, "삭제")
547
+
548
+ def delete_files(self, file_paths: list, change_list_number: int) -> bool:
549
+ """여러 파일을 한 번에 삭제합니다.
550
+
551
+ Args:
552
+ file_paths (list): 삭제할 파일 경로 리스트
553
+ change_list_number (int): 체인지 리스트 번호
554
+
555
+ Returns:
556
+ bool: 모든 파일 삭제 성공 시 True, 하나라도 실패 시 False
557
+ """
558
+ if not file_paths:
559
+ logger.debug("삭제할 파일 목록이 비어있습니다.")
560
+ return True
561
+
562
+ logger.info(f"체인지 리스트 {change_list_number}에서 {len(file_paths)}개 파일 삭제 시도...")
563
+
564
+ all_success = True
565
+ for file_path in file_paths:
566
+ success = self.delete_file(file_path, change_list_number)
567
+ if not success:
568
+ all_success = False
569
+ logger.warning(f"파일 '{file_path}' 삭제 실패")
570
+
571
+ if all_success:
572
+ logger.info(f"모든 파일({len(file_paths)}개)을 체인지 리스트 {change_list_number}에서 성공적으로 삭제했습니다.")
573
+ else:
574
+ logger.warning(f"일부 파일을 체인지 리스트 {change_list_number}에서 삭제하지 못했습니다.")
575
+
576
+ return all_success
310
577
 
311
578
  def submit_change_list(self, change_list_number: int) -> bool:
312
579
  """체인지 리스트를 제출합니다.
@@ -393,6 +660,28 @@ class Perforce:
393
660
  self._handle_p4_exception(e, f"체인지 리스트 {change_list_number} 삭제")
394
661
  return False
395
662
 
663
+ def revert_file(self, file_path: str, change_list_number: int) -> bool:
664
+ """체인지 리스트에서 특정 파일을 되돌립니다.
665
+
666
+ Args:
667
+ file_path (str): 되돌릴 파일 경로
668
+ change_list_number (int): 체인지 리스트 번호
669
+
670
+ Returns:
671
+ bool: 되돌리기 성공 시 True, 실패 시 False
672
+ """
673
+ if not self._is_connected():
674
+ return False
675
+
676
+ logger.info(f"파일 '{file_path}'을 체인지 리스트 {change_list_number}에서 되돌리기 시도...")
677
+ try:
678
+ self.p4.run_revert("-c", change_list_number, file_path)
679
+ logger.info(f"파일 '{file_path}'를 체인지 리스트 {change_list_number}에서 되돌리기 성공.")
680
+ return True
681
+ except P4Exception as e:
682
+ self._handle_p4_exception(e, f"파일 '{file_path}'를 체인지 리스트 {change_list_number}에서 되돌리기")
683
+ return False
684
+
396
685
  def revert_files(self, change_list_number: int, file_paths: list) -> bool:
397
686
  """체인지 리스트 내의 특정 파일들을 되돌립니다.
398
687
 
@@ -401,7 +690,7 @@ class Perforce:
401
690
  file_paths (list): 되돌릴 파일 경로 리스트
402
691
 
403
692
  Returns:
404
- bool: 되돌리기 성공 시 True, 실패 시 False
693
+ bool: 모든 파일 되돌리기 성공 시 True, 하나라도 실패 시 False
405
694
  """
406
695
  if not self._is_connected():
407
696
  return False
@@ -410,14 +699,20 @@ class Perforce:
410
699
  return True
411
700
 
412
701
  logger.info(f"체인지 리스트 {change_list_number}에서 {len(file_paths)}개 파일 되돌리기 시도...")
413
- try:
414
- for file_path in file_paths:
415
- self.p4.run_revert("-c", change_list_number, file_path)
416
- logger.info(f"파일 '{file_path}'를 체인지 리스트 {change_list_number}에서 되돌리기 성공.")
417
- return True
418
- except P4Exception as e:
419
- self._handle_p4_exception(e, f"체인지 리스트 {change_list_number}에서 파일 되돌리기")
420
- return False
702
+
703
+ all_success = True
704
+ for file_path in file_paths:
705
+ success = self.revert_file(file_path, change_list_number)
706
+ if not success:
707
+ all_success = False
708
+ logger.warning(f"파일 '{file_path}' 되돌리기 실패")
709
+
710
+ if all_success:
711
+ logger.info(f"모든 파일({len(file_paths)}개)을 체인지 리스트 {change_list_number}에서 성공적으로 되돌렸습니다.")
712
+ else:
713
+ logger.warning(f"일부 파일을 체인지 리스트 {change_list_number}에서 되돌리지 못했습니다.")
714
+
715
+ return all_success
421
716
 
422
717
  def check_update_required(self, file_paths: list) -> bool:
423
718
  """파일이나 폴더의 업데이트 필요 여부를 확인합니다.
@@ -526,3 +821,188 @@ class Perforce:
526
821
  def __del__(self):
527
822
  """객체가 소멸될 때 자동으로 연결을 해제합니다."""
528
823
  self.disconnect()
824
+
825
+ def check_files_checked_out_all_users(self, file_paths: list) -> dict:
826
+ """파일들의 체크아웃 상태를 모든 사용자/워크스페이스에서 확인합니다.
827
+
828
+ Args:
829
+ file_paths (list): 확인할 파일 경로 리스트
830
+
831
+ Returns:
832
+ dict: 파일별 체크아웃 상태 정보
833
+ {
834
+ 'file_path': {
835
+ 'is_checked_out': bool,
836
+ 'change_list': int or None,
837
+ 'action': str or None,
838
+ 'user': str or None,
839
+ 'client': str or None
840
+ }
841
+ }
842
+ """
843
+ if not self._is_connected():
844
+ return {}
845
+ if not file_paths:
846
+ logger.debug("체크아웃 상태 확인할 파일 목록이 비어있습니다.")
847
+ return {}
848
+
849
+ logger.debug(f"파일 체크아웃 상태 확인 중 - 모든 사용자 (파일 {len(file_paths)}개)")
850
+
851
+ result = {}
852
+ try:
853
+ # 각 파일의 상태 확인
854
+ for file_path in file_paths:
855
+ file_status = {
856
+ 'is_checked_out': False,
857
+ 'change_list': None,
858
+ 'action': None,
859
+ 'user': None,
860
+ 'client': None
861
+ }
862
+
863
+ try:
864
+ # p4 opened -a 명령으로 모든 사용자의 파일 체크아웃 상태 확인
865
+ opened_files = self.p4.run_opened("-a", file_path)
866
+
867
+ if opened_files:
868
+ # 파일이 체크아웃되어 있음 (첫 번째 결과 사용)
869
+ file_info = opened_files[0]
870
+ file_status['is_checked_out'] = True
871
+ file_status['change_list'] = int(file_info.get('change', 0))
872
+ file_status['action'] = file_info.get('action', '')
873
+ file_status['user'] = file_info.get('user', '')
874
+ file_status['client'] = file_info.get('client', '')
875
+
876
+ logger.debug(f"파일 '{file_path}' 체크아웃됨: CL {file_status['change_list']}, "
877
+ f"액션: {file_status['action']}, 사용자: {file_status['user']}, "
878
+ f"클라이언트: {file_status['client']}")
879
+ else:
880
+ # 파일이 체크아웃되지 않음
881
+ logger.debug(f"파일 '{file_path}' 체크아웃되지 않음 (모든 사용자)")
882
+
883
+ except P4Exception as e:
884
+ # 파일이 perforce에 없거나 접근할 수 없는 경우
885
+ if any("not opened" in err.lower() or "no such file" in err.lower()
886
+ for err in self.p4.errors):
887
+ logger.debug(f"파일 '{file_path}' 체크아웃되지 않음 (perforce에 없거나 접근 불가)")
888
+ else:
889
+ self._handle_p4_exception(e, f"파일 '{file_path}' 체크아웃 상태 확인 (모든 사용자)")
890
+
891
+ result[file_path] = file_status
892
+
893
+ checked_out_count = sum(1 for status in result.values() if status['is_checked_out'])
894
+ logger.info(f"파일 체크아웃 상태 확인 완료 (모든 사용자): 전체 {len(file_paths)}개 중 {checked_out_count}개 체크아웃됨")
895
+
896
+ return result
897
+
898
+ except P4Exception as e:
899
+ self._handle_p4_exception(e, f"파일들 체크아웃 상태 확인 - 모든 사용자 ({file_paths})")
900
+ return {}
901
+
902
+ def is_file_checked_out_by_others(self, file_path: str) -> bool:
903
+ """단일 파일이 다른 사용자/워크스페이스에 의해 체크아웃되어 있는지 확인합니다.
904
+
905
+ Args:
906
+ file_path (str): 확인할 파일 경로
907
+
908
+ Returns:
909
+ bool: 다른 사용자에 의해 체크아웃되어 있으면 True, 아니면 False
910
+ """
911
+ result = self.check_files_checked_out_all_users([file_path])
912
+ file_status = result.get(file_path, {})
913
+
914
+ if not file_status.get('is_checked_out', False):
915
+ return False
916
+
917
+ # 현재 사용자와 클라이언트가 아닌 경우 다른 사용자로 간주
918
+ current_user = self.p4.user
919
+ current_client = self.p4.client
920
+
921
+ file_user = file_status.get('user', '')
922
+ file_client = file_status.get('client', '')
923
+
924
+ return (file_user != current_user) or (file_client != current_client)
925
+
926
+ def get_file_checkout_info_all_users(self, file_path: str) -> dict:
927
+ """단일 파일의 상세 체크아웃 정보를 모든 사용자에서 가져옵니다.
928
+
929
+ Args:
930
+ file_path (str): 확인할 파일 경로
931
+
932
+ Returns:
933
+ dict: 체크아웃 정보 또는 빈 딕셔너리
934
+ {
935
+ 'is_checked_out': bool,
936
+ 'change_list': int or None,
937
+ 'action': str or None,
938
+ 'user': str or None,
939
+ 'client': str or None,
940
+ 'is_checked_out_by_current_user': bool,
941
+ 'is_checked_out_by_others': bool
942
+ }
943
+ """
944
+ result = self.check_files_checked_out_all_users([file_path])
945
+ file_status = result.get(file_path, {})
946
+
947
+ if file_status.get('is_checked_out', False):
948
+ # 현재 사용자와 클라이언트인지 확인
949
+ current_user = self.p4.user
950
+ current_client = self.p4.client
951
+
952
+ file_user = file_status.get('user', '')
953
+ file_client = file_status.get('client', '')
954
+
955
+ is_current_user = (file_user == current_user) and (file_client == current_client)
956
+
957
+ file_status['is_checked_out_by_current_user'] = is_current_user
958
+ file_status['is_checked_out_by_others'] = not is_current_user
959
+ else:
960
+ file_status['is_checked_out_by_current_user'] = False
961
+ file_status['is_checked_out_by_others'] = False
962
+
963
+ return file_status
964
+
965
+ def get_files_checked_out_by_others(self, file_paths: list) -> list:
966
+ """파일 목록에서 다른 사용자/워크스페이스에 의해 체크아웃된 파일들을 찾습니다.
967
+
968
+ Args:
969
+ file_paths (list): 확인할 파일 경로 리스트
970
+
971
+ Returns:
972
+ list: 다른 사용자에 의해 체크아웃된 파일 정보 리스트
973
+ [
974
+ {
975
+ 'file_path': str,
976
+ 'user': str,
977
+ 'client': str,
978
+ 'change_list': int,
979
+ 'action': str
980
+ }
981
+ ]
982
+ """
983
+ if not file_paths:
984
+ return []
985
+
986
+ result = self.check_files_checked_out_all_users(file_paths)
987
+ files_by_others = []
988
+
989
+ current_user = self.p4.user
990
+ current_client = self.p4.client
991
+
992
+ for file_path, status in result.items():
993
+ if status.get('is_checked_out', False):
994
+ file_user = status.get('user', '')
995
+ file_client = status.get('client', '')
996
+
997
+ # 다른 사용자/클라이언트에 의해 체크아웃된 경우
998
+ if (file_user != current_user) or (file_client != current_client):
999
+ files_by_others.append({
1000
+ 'file_path': file_path,
1001
+ 'user': file_user,
1002
+ 'client': file_client,
1003
+ 'change_list': status.get('change_list'),
1004
+ 'action': status.get('action', '')
1005
+ })
1006
+
1007
+ logger.info(f"다른 사용자에 의해 체크아웃된 파일: {len(files_by_others)}개")
1008
+ return files_by_others
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyjallib
3
- Version: 0.1.10
3
+ Version: 0.1.12
4
4
  Summary: A utility library for 3D game character development pipelines.
5
5
  Author-email: Dongseok Kim <jalnagakds@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -1,26 +1,25 @@
1
- pyjallib/__init__.py,sha256=pix9ja-vl7MYU3dJ4pgQicVCVwd1SyiQxD-BgWPILY8,461
1
+ pyjallib/__init__.py,sha256=4EmbUX3I3DLJ3DpGz7kBAADCFskvffdUEPd3HIu7-9k,509
2
2
  pyjallib/namePart.py,sha256=lKIiOVkWrtAW-D3nuv--vHmdAnlQeVPaXLYUDhcr8QU,24177
3
3
  pyjallib/nameToPath.py,sha256=aBeezepLYdpv3VYxnQ2c4ZWzz2WjticXjkdbAIlVa1k,4676
4
- pyjallib/naming.py,sha256=gJcD4wLAySFExlLiVQzF-amCbFbUnOwmxoio8Eyfrbw,36756
5
- pyjallib/namingConfig.py,sha256=3w8Xjs_HR3R0K25eySivJn58N9MZwfpk80ySZYVI7Qw,34005
6
- pyjallib/perforce.py,sha256=K_FAeDvecdaMT4INUcDluSB6k2NQ8R1NaddTzqLu-q0,24211
4
+ pyjallib/naming.py,sha256=b2C-P9VWV4Q2StqkizEwABblYOC5g6sXHzN0KpOZ_Ys,37419
5
+ pyjallib/namingConfig.py,sha256=QGpK5mCnRiclKqNKz3GJ2PeJO8fbVitAEdqWwnwo8oA,34127
6
+ pyjallib/perforce.py,sha256=XcF-YG250bh3obOkajPzMNwaJcCmXKIHIlJIEEFw6JE,46459
7
7
  pyjallib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  pyjallib/reloadModules.py,sha256=RAEG3IxzJ0TlsjvnZwJt56JOkc2j8voqAnRbfQuZ44g,1151
9
9
  pyjallib/ConfigFiles/namingConfig.json,sha256=Ov4bbVJb6qodPaooU63e11YUMGXXPWFAA4AQq1sLBYU,1486
10
- pyjallib/max/__init__.py,sha256=Zh3ZZD1ELmS1zFALW47OYlgdhBdZ7b1C_bCrN7vTuIw,1403
10
+ pyjallib/max/__init__.py,sha256=sLF07So7OcCccPOWixSjIkAhuye37-buK-1I5Hp8F4U,1537
11
11
  pyjallib/max/align.py,sha256=HKjCViQCuicGmtvHB6xxVv4BEGEBGtV2gO3NvR_6R2A,5183
12
12
  pyjallib/max/anim.py,sha256=QTpR8T047IMpV40wnMMNo080wY9rHMV9k7ISrh4P61I,26083
13
- pyjallib/max/autoClavicle.py,sha256=9Y-ytNP5Wv40JX1Cvm_13oAhfyug_uCpafAdcX1U2bk,8178
14
- pyjallib/max/autoClavicleChain.py,sha256=fjBpd4ads148bRBxkXfG5auC4x2Ec198yk-Wpv9V3Js,5098
15
- pyjallib/max/bip.py,sha256=wT1Lg_j2BVoLtRqVN3Rhm5WHj-_OHK_3hcl8PYjy8RY,27117
16
- pyjallib/max/bone.py,sha256=JgwnnWSbTa77ml4-CNtQkdROCgBUlNBkInD7g5WJgVI,48579
13
+ pyjallib/max/autoClavicle.py,sha256=Iga8bWUhRabfFePObdwGJSshp5Gww1Jv1Hwcul_y0V4,9559
14
+ pyjallib/max/bip.py,sha256=kEneUnYph_GQXY1oojmzpJOV9bAYNBKagE0Ht3EGZ7o,30199
15
+ pyjallib/max/bone.py,sha256=XVtX6e5UbMcGaOqz5UeoMEpQNMfbyQWDNM-UuS1CCUA,50019
16
+ pyjallib/max/boneChain.py,sha256=weuOmGk7Y7i-0QNCr7G2hRPOecb5xmErshqpmXtikI0,5660
17
17
  pyjallib/max/constraint.py,sha256=93g-X0aZHtZMKXVKr8xMjIhbKou61yc2b3ubQKJquBs,40589
18
- pyjallib/max/groinBone.py,sha256=4MvLkXFfdMioyFr4jOMemR26IxNyHtEHOpYRvbsNx9k,7386
19
- pyjallib/max/groinBoneChain.py,sha256=E75yHfvse3QJ-Yo__OUBox_dbv_j-bOSzxr_FwetDZA,5604
18
+ pyjallib/max/groinBone.py,sha256=nLR8rgWl6Vt4qQPZKY_jDgTRNF9RCnst49iR2JdWEMs,9144
20
19
  pyjallib/max/header.py,sha256=nuNCVfm5bfYMS0KxB8IRR67D30CXXHRUXHfFYkLG0jU,4120
21
20
  pyjallib/max/helper.py,sha256=Na3jFRwLsjHh4rz0Tk_r_CwHQxOA6n8LhDRA9x5xcSk,18018
22
- pyjallib/max/hip.py,sha256=7Mw2Fu_vgWkdm-5m8KoRXDOmphK8apiOiBQQQV1u2KU,11144
23
- pyjallib/max/kneeBone.py,sha256=BPob7KL62_B0_ErHIGkVa-DkBMHau_-dhmBHSQgeWkY,22832
21
+ pyjallib/max/hip.py,sha256=OXMS_bBJUYVKT-aZoJ2YCbbS9eQStwMOkXfA22t-PKw,12696
22
+ pyjallib/max/kneeBone.py,sha256=P5vDX1MFMbDy4DEI1LsT05a2Z62rqgr_FqGBxl2yEeY,24397
24
23
  pyjallib/max/layer.py,sha256=e9Mn8h7xf0oBYST3QIpyBpLMl8qpWTExO9Y6yH6rKc0,8795
25
24
  pyjallib/max/link.py,sha256=J3z9nkP8ZxAh9yYhR16tjQFCJTCYZMSB0MGbSHfA7uI,2592
26
25
  pyjallib/max/mirror.py,sha256=TcbfZXSk-VJQstNqAmD6VGCqYBF9bMuJtFTg-6SiGdQ,14505
@@ -28,19 +27,17 @@ pyjallib/max/morph.py,sha256=I8HRYx4NznL6GZL4CbT9iTv05SeaBW_mJJ4PzMxCBkw,12664
28
27
  pyjallib/max/name.py,sha256=DcJt2td-N7vfUGyWazdGTD4-0JW-noa7z5nwc6SHm6I,15337
29
28
  pyjallib/max/select.py,sha256=HMJD2WNX3zVBEeYrj0UX2YXM3fHNItfw6UtQSItNsoU,9487
30
29
  pyjallib/max/skin.py,sha256=5mBzG2wSUxoGlkFeb9Ys8uUxOwuZRGeqUMTI9LiWWZU,41937
31
- pyjallib/max/twistBone.py,sha256=RnyqXVq4LQRqk-QxaB_gKBkJ9ih5A0c8yvbErpOFGeE,16275
32
- pyjallib/max/twistBoneChain.py,sha256=A0F-43SbB-qdjo6U5TcenXapHcGIc4gxatxKiWnNv7M,5503
33
- pyjallib/max/volumeBone.py,sha256=fCFJ8YTrjhLkZdpkEekT45VN17DtTJngzJT5uHYZVno,12336
34
- pyjallib/max/volumeBoneChain.py,sha256=bS6OwPDSCk0Wrm0Hv9OZ9uGdygikMs98Z-S1koAGrKQ,13481
30
+ pyjallib/max/twistBone.py,sha256=3fs8EzRH-TTt2Oypm4LGoR9QtNcno9Fe1OV5_gA9p4U,17049
31
+ pyjallib/max/volumeBone.py,sha256=SfmxxqmozcDinRtfPsjdNOPDMcqDkHJoquaZZOLrn1g,14649
35
32
  pyjallib/max/ConfigFiles/3DSMaxNamingConfig.json,sha256=PBUYawCELG0aLNRdTh6j-Yka4eNdmpF4P8iRyp0Ngpg,3717
36
33
  pyjallib/max/ConfigFiles/Default_3DSMaxNamingConfig.json,sha256=PBUYawCELG0aLNRdTh6j-Yka4eNdmpF4P8iRyp0Ngpg,3717
37
34
  pyjallib/max/macro/jal_macro_align.py,sha256=_Iqwskz0i4AVlP_AhDrqHwhLml6zoDWkVAxPF3AKqEQ,4286
38
- pyjallib/max/macro/jal_macro_bone.py,sha256=I3MYM7fk-_LQonORRD0hJoip0TAk0suS-xDBamhySPQ,12422
35
+ pyjallib/max/macro/jal_macro_bone.py,sha256=SROjidH4z4rec9jNtWK0Dmkx_-sP6GJiMi0Hf6ipwsI,12418
39
36
  pyjallib/max/macro/jal_macro_constraint.py,sha256=jJnNHCjsFJNYRqaBEPjzW-dnCDJT6JRZXH7dqBPkeiw,4160
40
37
  pyjallib/max/macro/jal_macro_helper.py,sha256=hd8e5x56aq7Qt0g-hP5bY0p-njVy8ja77_qMPZyvDag,12906
41
38
  pyjallib/max/macro/jal_macro_link.py,sha256=E8i3z2xsrQiGDEz4Qoxc75hkpalzS95mOMcIic0J-Fc,1193
42
39
  pyjallib/max/macro/jal_macro_select.py,sha256=jeSFR_mqqudTTrZE1rU6qifJ4g441cYxXWcHPTWh1CU,2289
43
40
  pyjallib/max/ui/Container.py,sha256=QSk3oCqhfiR4aglSSkherRGAyPFqMRUt83L-0ENBz-s,5571
44
- pyjallib-0.1.10.dist-info/METADATA,sha256=WRIWnsOSWqSf4NdHVw8RkNAqb6uPE-N78eFUD67q8fQ,870
45
- pyjallib-0.1.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
46
- pyjallib-0.1.10.dist-info/RECORD,,
41
+ pyjallib-0.1.12.dist-info/METADATA,sha256=0jeCReJS2g2RlmWhefBDeSPwlIoFDturmBEqrw_s3Y8,870
42
+ pyjallib-0.1.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
+ pyjallib-0.1.12.dist-info/RECORD,,