[파이썬] exe파일 중복실행 방지 방법
1. 문제 정의
- 파이썬언어로 짠 소스코드를 pyinstaller를 이용하여 .exe파일을 생성하였고, exe 파일이 2번 실행되지 않도록 막아주는 API인 singleton 패키지의 tendo 함수를 잘 사용하고 있었으나, 어느순간부터 아래와 같은 에러 메시지가 나오면서 위 함수가 작동하지 않게 되었다.
<에러 원문은 아래 더보기 클릭>
Failed to execute script 'opak' due to unhandled exception: C:\\Users\\PC\\AppData\\Local\\Temp\\_MEI75282\\distutils\\core.pyc
Traceback (most recent call last):
File "pbr\\version.py", line 451, in _get_version_from_pkg_resources
File "pkg_resources\\__init__.py", line 354, in get_provider
File "pkg_resources\\__init__.py", line 909, in require
File "pkg_resources\\__init__.py", line 795, in resolve
pkg_resources.DistributionNotFound: The 'tendo' distribution was not found and is required by the application
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "opak.py", line 12, in <module>
from tendo import singleton
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
File "PyInstaller\\loader\\pyimod02_importers.py", line 493, in exec_module
File "tendo\\__init__.py", line 10, in <module>
File "pbr\\version.py", line 495, in semantic_version
File "pbr\\version.py", line 457, in _get_version_from_pkg_resources
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
File "PyInstaller\\loader\\pyimod02_importers.py", line 493, in exec_module
File "pbr\\packaging.py", line 43, in <module>
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
File "PyInstaller\\loader\\pyimod02_importers.py", line 493, in exec_module
File "setuptools\\__init__.py", line 8, in <module>
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
File "PyInstaller\\loader\\pyimod02_importers.py", line 493, in exec_module
File "_distutils_hack\\override.py", line 1, in <module>
File "_distutils_hack\\__init__.py", line 77, in do_override
File "_distutils_hack\\__init__.py", line 64, in ensure_local_distutils
AssertionError: C:\\Users\\PC\\AppData\\Local\\Temp\\_MEI75282\\distutils\\core.pyc
<기존 tendo 함수를 사용한 소스코드 예제 원문은 아래 더보기 클릭>
_MEI308282\\distutils\\core.pyc에서 unhandled exception이 발생하였다. 아래 Traceback을 읽어보니 init하는 과정에서 tendo API를 찾을수 없다는 내용이다.(The 'tendo' distribution was not found and is required by the application)
이후 내용은 tendo 라이브러리 또는 singleton 패키지를 call하지 못해서 발생하는 파생오류인 듯 하다.
내 예상이 맞았다. singleton 패키지의 tendo 라이브러리를 불러오지 못해 자동완성이 안되고 있다.
- 역시나 내 예상이 맞았다.
위와 같이 tendo 관련된 내용을 주석처리하고 .exe 파일을 컴파일한 후 실행했더니, 정상작동하였다. singleton 및 os 패키지 및 라이브러리를 잘 Import하게 만드는 것이 이 문제의 해결방법이다.
+) 추가로 의심이 가는건 Python for VSCode Extension이 더이상 서비스를 제공하지 않아서 그런가 싶기도 하다. 우선 tendo API를 주석처리하고 사용하고 추후 다시 디버깅해봐야겠다.
2. 해결방법
1) pyinstaller 업데이트
$> pip install --upgrade pyinstaller
위 명령어를 이용하여 업데이트를 하였더니 이번에는 No module named 'setuptools._distutils' 에러로 바뀌었다.
2) python 버전 일치화 (불일치 시 파이썬 삭제 후 재설치)
$> pip --version
$> pip3 --version
$> python --version
위 명령어를 이용하여 버전을 확인하였더니, pip와 pip3는 파이썬 버전이 3.10이고 현재 설치되어있는 파이썬은 3.9.10이었다. 이를 일치화하기 위해 파이썬 버전을 업데이트해보았다. (프로그램 설치 및 제거 문제 해결사를 보니 python 3.7.8 버전도 깔려있었다.)
<파이썬 삭제방법 아래 링크 참고>
<파이썬 재설치 아래 링크 참고>
3) Singleton (tendo) 대신 다른거 쓰기
위 2가지 방법을 해도 해결이 안되면 아래와 같이 다른 패키지를 사용하여 tendo를 대체한다.
<Linux 환경>
fcntl 패키지가 설치되어있어야 함 (없는 경우 pip install fcntl로 패키지 설치)
<Windows 환경>
리눅스 패키지인 fcntl을 윈도우에서 쓸 수 없으므로 portalocker 패키지를 대체하여 사용해야 한다.
(없는 경우 pip install portalocker로 패키지 설치)
3. 결론
- 필자의 경우 1, 2번 방법이 안통해서 결국 3번방법을 사용하여 singlton (tendo)를 대체하여 사용하였다. 필자는 windows 환경으로 소스를 돌리므로 portalocker 패키지를 사용하였고 그 결과 성공적으로 exe 파일이 중복실행되지 않음을 확인하였다.
- 처음 exe 파일 실행시 아래와 같이 exe 파일이 잘 실행되었다.
- 하지만 중복으로 다시한번 같은 exe 파일을 실행했더니 아래와 같은 에러 스크립트와 함께 프로세스가 실행되지 않았다.
<에러 원문은 아래 더보기 클릭>
Failed to execute script 'opak' due to unhandled exception: (1, '다른 프로세스가 파일의 한 부분을 잠갔으므로, 프로세스가 해당 파일을 액세스 할 수 없습니다.')
Traceback (most recent call last):
File "portalocker\\portalocker.py", line 40, in lock
pywintypes.error: (33, 'LockFileEx', '다른 프로세스가 파일의 한 부분을 잠갔으므로, 프로세스가 해당 파일을 액세스할 수 없습니다.')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "opak.py", line 29, in <module>
fh = open_and_lock(lock_name)
File "opak.py", line 21, in open_and_lock
portalocker.lock(file_handle, portalocker.LOCK_EX | portalocker.LOCK_NB)
File "portalocker\\portalocker.py", line 45, in lock
portalocker.exceptions.AlreadyLocked: (1, '다른 프로세스가 파일의 한 부분을 잠갔으므로, 프로세스가 해당 파일을 액세스할 수 없습니다.')
버그픽스 성공이닷!
<참고 원문>
- 아래 더보기 클릭
https://stackoverflow.com/questions/1422368/fcntl-substitute-on-windows
fcntlLinux에서만 사용할 수 있는 표준 라이브러리 의 모듈을 사용하는 Python 프로젝트(중요한 경우 Django 프로젝트)를 받았습니다. 내 Windows 컴퓨터에서 실행하려고 하면 ImportError이 모듈이 여기에 없기 때문에 와 함께 중지됩니다.
Windows에서 작동하도록 프로그램을 약간 변경할 수 있는 방법이 있습니까?
-
2코드가 fcntl로 구체적으로 무엇을 하려고 합니까? fcntl()은 다양한 작업을 수행합니다.– 워렌 영2009년 9월 14일 15:45
3개의 답변
fcntlon windows를 대신하는 것은 win32api호출입니다. 사용법이 완전히 다릅니다. 그냥 뒤집을 수 있는 스위치가 아닙니다.
즉, fcntl-heavy-user 모듈을 Windows로 이식하는 것은 간단하지 않습니다. 각 fcntl호출이 정확히 무엇을 하는지 분석한 다음 해당 win32api코드가 있는 경우 해당 코드를 찾아야 합니다.
사용하는 일부 코드 fcntl에 Windows에 해당하는 항목이 없을 수도 있습니다. 이 경우 이식하려는 모듈을 사용하는 프로그램의 모듈 API와 구조/패러다임을 변경해야 합니다.
통화 에 대한 자세한 내용을 제공하면 fcntl사람들이 Windows에 상응하는 항목을 찾을 수 있습니다.
-
Windows에서 다른 파일의 동기화를 위해 단일 잠금 파일을 사용하는 것이 안전한지 여부에 대한 정보가 있습니까? 잠금 파일 잠금 및 원자적 방식의 잠금 해제에도 불구하고 이 작업을 수행한 후에도 여전히 경쟁 조건이 발생하는 것 같습니다. 잠금을 해제해도 잠금이 유지되는 동안 작성한 다른 파일에 대한 사전 보장이 생성되지 않는 것과 같습니다.– 질4월 5일 14:02
-
@Zyl 귀하가 언급하는 답변은 잠금과 전혀 관련이 없으며 거의 모든 작업을 수행하는 데 사용할 수 있는 fcntl/ 호출에 대해 설명합니다. win32api나는 윈도우에서 어떤 것도 안전하다고 믿지 않을 것입니다. 댓글을 달지 말고 다른 질문을 하는 것이 좋습니다.– 노슬로4월 7일 21:15
fcntl 모듈은 고정 파일을 잠그는 데만 사용되므로 다중 액세스를 시도하지 않는다고 가정하면 이것이 허용 가능한 해결 방법이 될 수 있습니다. 이 모듈을 에 sys.path배치하면 공식 fcntl 모듈로 작동해야 합니다.
개발/테스트 목적으로 이 모듈 ( 소스 )을 Windows에서만 사용해 보십시오 .
def fcntl(fd, op, arg=0):
return 0
def ioctl(fd, op, arg=0, mutable_flag=True):
if mutable_flag:
return 0
else:
return ""
def flock(fd, op):
return
def lockf(fd, operation, length=0, start=0, whence=0):
return
물론 사용하려는 Python 인터프리터의 디렉토리에 fcntl.py모듈 을 배치해야 합니다. site-packages예를 들어, %LOCALAPPDATA%\\Programs\\Python\\Python310\\lib\\site-packages\\fcntl\\. 여기가 내가 site-packages사는 곳입니다. 당신의 위치를 찾으려면 Tutorialspoint 를 확인하십시오 .
-
삼
-
-
4
-
8
-
1이것을 sys.path에 어떻게 추가합니까? 내 python 스크립트와 동일한 폴더에 이것을 추가하려고 시도했지만 작동하지 않았습니다.– 루피안토2020년 6월 15일 13:58
이것이 바로 도움이 되지는 않지만 Unix(fcntl) 및 Windows( win32 API 호출)에서 모두 작동할 수 있는 대안이 있습니다.
Python용 무리 스타일 파일 잠금을 위한 크로스 플랫폼(posix/nt) API로 스스로를 설명합니다. 기본적으로 fcntl을 win32 API 호출에 매핑합니다.
http://code.activestate.com/recipes/65203/ 의 원본 코드를 이제 별도의 패키지로 설치할 수 있습니다. - https://pypi.python.org/pypi/portalocker
https://code.activestate.com/recipes/65203/
PORTALLOCKER - 무리 스타일 파일 잠금을 위한 크로스 플랫폼(POSIX/NT) API입니다. (파이썬 레시피)
개요:
import portallocker 파일 = open("somefile", "r+") portallocker.lock(file, portallocker.LOCK_EX) file.seek(12) file.write("foo") file.close()
|
나는 이것이 플랫폼 간에 lock() 시스템 호출을 본질적으로 투명하게 사용하는 데 익숙한 나와 같은 이전 Perl 사용자에게 가장 도움이 될 것이라고 생각합니다.
아래 주석에 따라 현대 Python과 호환되도록 2007년 12월 12일 업데이트되었습니다.
Lowell Alleman lalleman@mfps.com 의 개선 사항으로 2008년 5월 16일 업데이트되었습니다 . 고마워, 로웰!
댓글 10개
그러나 이것은 Win95, Win98에서는 실패합니다. 이러한 플랫폼에는 os.name == 'nt'가 있지만 해당 플랫폼의 Win32 API는 LockFileEx 및 UnlockFileEx 기능을 지원하지 않습니다. 이 방법은 win32 함수 호출에서 api_error와 함께 실패합니다.
Win32는 모든 플랫폼에서 LockFile, UnlockFile을 지원하지만 기능이 더 낮습니다. 공유/배타 플래그 없음, 잠길 때까지 차단 모드 없음.
Python 2.4에서 레시피가 작동하도록 하는 방법. 코드는 Python 2.4에서 오류를 발생시킵니다.
분명히 이것은 Python 2.3에서 정수 -65536으로 평가된 16진수 상수 0xffff0000이 긴 정수와 짧은 정수의 통합으로 인해 Python 2.4에서 긴 정수 4294901760으로 평가되기 때문입니다.
상수를 -0x10000으로 변경하면 문제가 해결되는 것 같습니다.
집에서 만든 파일 잠금.
here's an approach to file locking that might be useful to somebody, especially for a consumer-producer type situation. It uses the fact that on windows, renaming a file that is already open will raise an exception.
____Producer___:
import time, os
#lock readers out by renaming the file
if os.access("filename", os.F_OK):
for failure_counter in range(0, 600):
try:
os.rename("filename", "filename.temp")
break #removed the file successfully
except:
time.sleep(1) #someone is reading from the file
else:
raise Exception #consumers kept the file open for over 10 min
#file is now locked
file = open("filename.temp", "w")
file.write("bla bla")
file.close()
os.rename("filename.temp", "filename") #unlock the file
____Consumers___:
import time, os
#wait until file exists (eg. is unlocked
for i in range(0,600):
if os.acces("filename", os.F_OK):
break
else:
time.sleep(1)
else:
raise Exception #file still locked after 10 minutes
#access the file
file = open("filename", "r")
contents = file.read()
file.close()
WinXpsp2의 오류. ActivePython 2.4.1 Python 2.4.1 기반 빌드 247(ActiveState Corp.)(#65, 2005년 6월 20일, 17:01:55) [MSC v.1310 32비트(인텔)] on win32
<p></p> 역추적(가장 최근 호출 마지막): 파일 "portalocker.py", 80행, ? portallocker.lock(log, portallocker.LOCK_EX) 파일 "C:\\portalocker.py", 줄 61, 잠금 win32file.LockFileEx(hfile, flags, 0, 0xffff0000, __overlapped) OverflowError: long int가 너무 커서 int로 변환할 수 없습니다.
위의 의견에 따라이 문제를 해결했습니다.
감사. Jordan, 위의 코드에 수정 사항을 통합했습니다. 매우 감사합니다.
사소한 변경 사항: portallocker.py를 약간 더 이식성 있게 만들기... Jonathan에게 몇 가지 개선 사항을 보냈고 그는 이 페이지에서 이를 포함하도록 portallocker.py를 업데이트했습니다. 하지만 다른 분들을 위해 간단한 설명을 드리고 싶었습니다. 한 문장으로 모든 변경 사항은 다음과 같이 요약됩니다. 포털로커가 더 많은 플랫폼별 항목을 처리하도록 하여 그렇게 할 필요가 없습니다.
변경 사항 및 참고 사항:
- 오류 처리를 보다 균일하게 하기 위해 LockException 클래스를 추가했습니다. 이전에는 Unix의 경우 IOError를 캡처하고 Windows의 경우 pywintypes.error를 캡처해야 했습니다. 그래서 두 lock() 함수를 모두 수정하여 LockException만 발생시켰습니다. (BTW. LOCK_NB 플래그를 사용하는 경우에만 예외가 발생해야 합니다. 그렇지 않으면 잠금을 사용할 수 있을 때까지 대기(또는 "차단")하므로 예외가 발생하지 않습니다.)
- 이미 잠금 해제된 파일의 잠금을 해제하라는 요청을 무시하도록 Windows의 잠금 해제() 함수를 업데이트했습니다. 이것은 'posix' 동작과 일치하므로 더 예측 가능하게 만듭니다. (일반적으로 파일을 두 번 잠금 해제하는 것은 아니지만 어떻게든 문제를 해결했기 때문에 언젠가 다른 사람에게 도움이 될 것입니다.)
- 해결되지 않음 : Windows에서 LOCK_EX 를 사용하여 파일 잠금을 여러 번 요청하면 영원히 차단됩니다! 유닉스에서는 원하는 만큼 lock()을 실행할 수 있습니다. 아마도 Windows API 경험이 있는 사람이 이미 파일을 잠그었는지 확인하는 방법을 제공할 수 있습니까? 그러나 그 동안에는 동일한 파일을 여러 번 잠그는 것이 플랫폼 간에 다르게 작동한다는 점을 알아두십시오.
이 모듈을 사용하는 방법에 대한 많은 예가 있지만 예외 처리에 대해 많은(아무도?) 본 적이 없습니다. 다음은 새로운 LockException 클래스를 사용한 모범 사례입니다.
fp = open("my_lock_file", "w")
try:
portalocker.lock(fp, portalocker.LOCK_EX | portalocker.LOCK_NB)
except portalocker.LockException:
# File locked by somebody else. Do some other work, then try again later.
pass
else:
# Lock acquired. Do your thing
try:
while chapters in book:
fp.write(chapters)
finally:
unlock(fp)
알겠습니다. 즐기다.
위의 코드는 많은 경우에 충분해야 하지만 pywin32가 필요하고 파일 레코드 잠금을 제공하지 않으며 fcntl() 결함을 처리하지 않습니다. 닫혀 있습니다).
귀하의 코드와 유사한 기반으로 구현한 휴대용 파일 스트림을 사용하는 것이 좋습니다. 파일 잠금, 디스크 캐시 동기화 및 멋진 기능(고급 열기 플래그, 스트림 상속 관리, fcntl 해결 방법 등)을 제공합니다. 게다가, 그것은 py26, py27, py3K 및 이론적으로 다른 파이썬 구현(ironpython, jython 등)과 함께 작동합니다.
Chamon Pascal 감사합니다! fcntl에 대한 설명자가 닫힐 때 fcntl이 모든 잠금을 푼다는 귀하의 힌트 는 저에게 많은 도움이 되었습니다. lock-fd를 닫기 전에 잠금 파일을 제거하면 경쟁 조건이 생깁니다. open()과 fcntl() 사이에 다른 프로세스가 수행할 수 있는 open,fcntl,unlink,close. 그런 다음 잠긴 파일 설명자가 있지만 파일 시스템의 파일이 사라졌습니다. 해결책은 잠금 후 inode를 확인하고(inode가 다른 경우 실제 잠금이 없는 경우) 두 번째 fd를 닫지 않는 것입니다. 두 번째 fd(os.stat의 경우)를 닫으면 첫 번째 fd의 잠금이 손실됩니다.
ctypes로 이식하여 pywin32 종속성을 제거했습니다. http://roundup.hg.sourceforge.net/hgweb/roundup/roundup/file/tip/roundup/backends/portalocker.py 참조
'IT > 파이썬 & vscode' 카테고리의 다른 글
[vscode] 탭 간격 영구 설정, 공백 표시, 탭키 공백으로 인식 해제 방법 (1) | 2022.12.09 |
---|---|
[vscode] 소스코드 한번에 예쁘게 정리하기 (0) | 2022.11.11 |
[파이썬] Python 깔끔하게 삭제하는 방법 (0) | 2022.09.08 |
[VS Code] unins000.exe 엑세스 거부 문제 해결 (0) | 2022.09.05 |
[알고리즘] 버블, 선택, 삽입, 셸, 힙, 병합, 퀵 정렬 총정리 (2) | 2022.06.10 |