Python package 는 한번 설치하면 쭉 관심이 없는 경우가 많다. 

하지만, 개발 환경에서 개발 후 코드를 배포할 때 실행 환경에서는 패키지를 새로 설치해야 하니 

패키지 목록을 잘 관리해야 한다. 

 

혹은, 임베디드 같은 환경에서는 실행환경에서 패키지 다운로드가 되지 않으니 설치파일을 함께 제공해야 한다. 

 

이런 경우 어떤 방법이 있는지 알아보자. 

(물론 아래 대부분 환경은 가상환경으로 관련된 package 만 있는 상태를 가정으로 한다. ) 

 

 

1. 패키지 목록 추출

개발환경에서 현재 사용된 패키지의 목록을 가져가는 방법. 

pip freeze > requirements.txt

pip freeze 명령을 사용하여 현재 환경에 설치된 패키지와 버전을 출력합니다. 이 출력을 requirements.txt 파일로 리디렉션합니다.

 

이렇게 하면 현재 가상 환경에 설치된 모든 패키지와 해당 버전이 requirements.txt 파일에 저장됩니다.

 

 

 

2. 패키지 설치 (온라인 환경) 

위의 만들어진 requirements.txt 를 이용하여 다운로드 설치 합니다. 

pip install -r requirements.txt

 

 

 

3-1. 패키지 다운로드 (오프라인 환경 대비. 온라인 다운로드) 

2번과 같은 방법으로 설치 환경이 온라인 환경이라면 좋겠지만, 

실행 환경이 오프라인 환경인 경우, 설치파일을 확보하여 설치하는 방법도 있습니다. 

 

먼저 설치파일을 확보합니다. 

1의 과정으로 requirements.txt 는 있는 상태입니다. 

 

아래와 같이 실행하면 한개의 패키지에 대한 설치 파일을 다운로드 할 수 있습니다. 

// 단일 패키지 다운로드 방법
pip download 패키지이름

 

 

우리는 requirements.txt 를 작성한 상태이니 해당 파일을 이용해서 다운로드 해줍니다. 

// 여러개의 패키지를 다운로드 하는 방법

// 1. 현재 경로에 다운로드
pip download -r requirements.txt

// 2. 혹은 지정된 경로에 다운로드
pip download -r requirements.txt -d /path/to/save/packages

 

3-2. 패키지 설치 (오프라인 환경) 

 

준비된 패키지 파일을 몽땅 들고 설치를 해줍니다. 

pip install --no-index --find-links /path/to/packages -r requirements.txt

 

/path/to/packages : 다운로드한 패키지 파일이 저장된 디렉토리

--no-index  : PyPI와 같은 온라인 인덱스를 사용하지 않음

--find-links : 패키지 파일이 있는 로컬 디렉토리를 지정

 

 

3-3. 그냥 패키지 파일로 설치

 

혹은 이미 패키지 파일이 모두 있는 경우에는 아래와 같은 방법도 가능합니다. 

 

[packages.txt]

/path/to/package1.whl
/path/to/package2.tar.gz
/path/to/package3.zip

 

pip install -r packages.txt

 

python 에서 ip 를 이용해서 무언가 해야 하는데 ip 가 유효하지 않아서 error 가 발생할 수 있습니다. 

쉽게 ip 에 대해서 검사해보는 방법 중 하나가 ping 인데, 

python 에서 ping 을 이용하여 분기하는 코드를 이용하면 ping 결과에 따른 진행을 할 수 있습니다. 

 

import subprocess
import sys

def ping(host):
    # ping 명령어 생성 (Windows와 Linux/Unix에 맞게)
    param = '-n' if subprocess.run('uname', stdout=subprocess.PIPE).stdout.decode().strip() == 'Windows' else '-c'
    command = ['ping', param, '1', host]

    # subprocess.run을 사용하여 ping 명령어 실행
    try:
        output = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        # returncode가 0이면 ping 성공
        if output.returncode == 0:
            print(f'{host} is reachable')
            return 0
        else:
            print(f'{host} is not reachable')
            return output.returncode
    except Exception as e:
        print(f'An error occurred: {e}')
        return -1

# 테스트할 IP 주소
host = '8.8.8.8'
result = ping(host)

# return 값이 0이 아니면 프로그램 종료
if result != 0:
    print(f'Ping failed with return code {result}. Exiting program.')
    sys.exit(result)

# 이후에 실행할 코드
print('IP is reachable. Continue with the rest of the program...')

 

 

python 에서 onvif 를 이용하여 명령을 수행하는 것은

이미 잘 만들어져 있는 library 를 사용해도 되지만, 

이미 message 의 전문을 알고 있는 상태에서는 (digest 전문)

library 를 전체 사용하지 않아도 메시지를 보낼 수 있습니다. 

 

chatGPT 에게 code sample 을 달라 하니 아래와 같이 주는데, 

관련된 import 될 library 만 설치해주면 정상 동작하는 것을 확인 했습니다. 

 

GPT 가 도와주니 참 쉽네요. :) 

import requests
from requests.auth import HTTPDigestAuth
from xml.etree import ElementTree as ET

# ONVIF 디바이스의 주소, 포트, 사용자 이름, 비밀번호를 지정합니다.
cam_ip = '192.168.1.100'
cam_port = 80
username = 'admin'
password = 'admin'

# 요청할 ONVIF 메시지(XML)를 작성합니다.
onvif_message = """
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tds="http://www.onvif.org/ver10/device/wsdl">
    <soap:Header/>
    <soap:Body>
        <tds:GetHostname/>
    </soap:Body>
</soap:Envelope>
"""

# ONVIF 디바이스의 URL을 지정합니다.
url = f'http://{cam_ip}:{cam_port}/onvif/device_service'

# 요청 헤더를 설정합니다.
headers = {
    'Content-Type': 'application/soap+xml; charset=utf-8',
}

# 요청을 보냅니다.
response = requests.post(url, data=onvif_message, headers=headers, auth=HTTPDigestAuth(username, password))

# 응답 결과를 출력합니다.
print("Response:")
print(response.text)

suds_passworddigest package 설치시 file is not defined 라는 error 가 발생한다. 

 

$ pip install suds_passworddigest
/usr/share/python-wheels/urllib3-1.25.8-py2.py3-none-any.whl/urllib3/connectionpool.py:1004: InsecureRequestWarning: Unverified HTTPS request is being made to host 'pypi.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
Collecting suds_passworddigest
/usr/share/python-wheels/urllib3-1.25.8-py2.py3-none-any.whl/urllib3/connectionpool.py:1004: InsecureRequestWarning: Unverified HTTPS request is being made to host 'files.pythonhosted.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  Downloading suds_passworddigest-0.1.2.zip (5.6 kB)
    ERROR: Command errored out with exit status 1:
     command: /home/aaa/onvif/bin/python3 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-8p9asd35/suds-passworddigest/setup.py'"'"'; __file__='"'"'/tmp/pip-install-8p9asd35/suds-passworddigest/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-install-8p9asd35/suds-passworddigest/pip-egg-info
         cwd: /tmp/pip-install-8p9asd35/suds-passworddigest/
    Complete output (5 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-install-8p9asd35/suds-passworddigest/setup.py", line 18, in <module>
        long_description=file(
    NameError: name 'file' is not defined
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

 

package 를 찾아서 https://github.com/tgaugry/suds-passworddigest-py3  에 찾아가서 보면 url 을 이용하여 설치하는게 나오지만, 

그것 또한 시도해보면 또 에러가 난다. 

$ pip install git+https://github.com/suvit/suds-passworddigest
Collecting git+https://github.com/suvit/suds-passworddigest
  Cloning https://github.com/suvit/suds-passworddigest to /tmp/pip-req-build-t2vzz22b
  Running command git clone -q https://github.com/suvit/suds-passworddigest /tmp/pip-req-build-t2vzz22b
    ERROR: Command errored out with exit status 1:
     command: /home/aaa/onvif/bin/python3 -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-req-build-t2vzz22b/setup.py'"'"'; __file__='"'"'/tmp/pip-req-build-t2vzz22b/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' egg_info --egg-base /tmp/pip-req-build-t2vzz22b/pip-egg-info
         cwd: /tmp/pip-req-build-t2vzz22b/
    Complete output (5 lines):
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-req-build-t2vzz22b/setup.py", line 18, in <module>
        long_description=file(
    NameError: name 'file' is not defined
    ----------------------------------------
ERROR: Command errored out with exit status 1: python setup.py egg_info Check the logs for full command output.

 

NameError 가 무엇인지 궁금해서 stack overflow 해보면 다음과 같이 나온다. 

-> file() is not supported in Python 3
Use open() instead; see Built-in Functions - open().

https://stackoverflow.com/questions/16736833/python-nameerror-name-file-is-not-defined

 

python NameError: name 'file' is not defined

I dont know much about python. I want to start working on the project and the setup instruction says: pip install -r requirements-dev.txt Simple enougth. The problem is that I get this: Downlo...

stackoverflow.com

 

결국 해당 library 가 python3 에 맞게 되어있지 않아서 (python 2 에 맞게 되어 있어서) 발생하는 문제이다. 

 

그러면? 

python3 에 맞는 library 를 찾아봅시다. 

 

https://github.com/tgaugry/suds-passworddigest-py3

 

GitHub - tgaugry/suds-passworddigest-py3: adds Web Services Security PasswordDigest authentication to SUDS

adds Web Services Security PasswordDigest authentication to SUDS - tgaugry/suds-passworddigest-py3

github.com

$ pip install git+https://github.com/miuhaki/suds-passworddigest-py3.git
Collecting git+https://github.com/miuhaki/suds-passworddigest-py3.git
  Cloning https://github.com/miuhaki/suds-passworddigest-py3.git to /tmp/pip-req-build-y5v5wvih
  Running command git clone -q https://github.com/miuhaki/suds-passworddigest-py3.git /tmp/pip-req-build-y5v5wvih
Requirement already satisfied: suds-py3 in /home/aaa/onvif/lib/python3.8/site-packages (from suds-passworddigest==0.1.2a0) (1.4.5.0)
Building wheels for collected packages: suds-passworddigest
  Building wheel for suds-passworddigest (setup.py) ... done
  Created wheel for suds-passworddigest: filename=suds_passworddigest-0.1.2a0-py3-none-any.whl size=3230 sha256=d1558349cdbbdf2ee64982bed9434e243ba6079978a25ca877059d562cf0af40
  Stored in directory: /tmp/pip-ephem-wheel-cache-k60i71q4/wheels/fb/5e/1e/61aebb79cfe352df1f256d864917ea3c248c42f5cc7626e0cd
Successfully built suds-passworddigest
Installing collected packages: suds-passworddigest
Successfully installed suds-passworddigest-0.1.2a0

 

잘 됩니다. 

다행.. :) 

pyqt5 설치 오류시 해결 방법

 

----

 

pip install pyqt5
Collecting pyqt5
  Using cached PyQt5-5.15.10-cp37-abi3-win32.whl (5.4 MB)
Collecting PyQt5-Qt5>=5.15.2
  Using cached PyQt5_Qt5-5.15.2-py3-none-win32.whl (43.4 MB)
Collecting PyQt5-sip<13,>=12.13
  Using cached PyQt5_sip-12.13.0.tar.gz (123 kB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
    Preparing wheel metadata ... done
Building wheels for collected packages: PyQt5-sip
  Building wheel for PyQt5-sip (PEP 517) ... error
  ERROR: Command errored out with exit status 1:
   command: 'C:\Users\aaa\AppData\Local\conda\conda\envs\py37_32\python.exe' 'C:\Users\aaa\AppData\Local\conda\conda\envs\py37_32\lib\site-packages\pip\_vendor\pep517\in_process\_in_process.py' build_wheel 'C:\Users\aaa\AppData\Local\Temp\tmpde7bu9nm'
       cwd: C:\Users\aaa\AppData\Local\Temp\pip-install-qearb7q5\pyqt5-sip_2dd65d07b91f45c9a85eaa0c13108eaf
  Complete output (5 lines):
  running bdist_wheel
  running build
  running build_ext
  building 'PyQt5.sip' extension
  error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/
  ----------------------------------------
  ERROR: Failed building wheel for PyQt5-sip
Failed to build PyQt5-sip
ERROR: Could not build wheels for PyQt5-sip which use PEP 517 and cannot be installed directly

 

---

 

-> PyQt5-sip  build 중 에러가 난 것이며, 

https://visualstudio.microsoft.com/ko/visual-cpp-build-tools/  에서 build tool 설치 하라고 나옴. 

 

 

위에서 Build Tools 다운로드 클릭, 실행. 

 

 

위의 그림과 같은 화면이 나오는데, 

C++를 사용한 데스크톱 개발 항목을 클릭하고 설치한다. 

 

그러면 문제 해결 완료. :) 

 

가끔은 코드를 보는 것 뿐만 아니라 다른 사람들이 코드에 관하여 어떤 의견을 교환하는 지도 보면 좋은 것 같습니다. 

E-mail 을 보다가 우연히 다음과 같은 내용을 보게 되었습니다. 

 

"The Overflow #182: Self-healing code" (Email)의 내용 중

전치연산 / 후치연산이 햇갈리니 되려 코드 가독성이나 혼동에 대한 에러를 줄이기 위해서는 명시적으로 풀어서 코드를 작성하는 것도 방법인거 같습니다. 

// 이런 코드는 좋지 않겠죠
i = 4;
j = i+++i;

// 하지만 이런 부분은 전치 / 후치를 쓰더라도 괜찮겠죠. 
for(int i = 0 ; i < 10 ; i++) { .....

 

그러면 어떤 코드가 빠를까요? 

// 어떤게 빠를까요? 
int i = 4;
i++;

int j = 4;
++j;

상식적으로는 전치 연산이 빠르다고 합니다. 

이유는 어셈블러로는 인크리먼트 (increment) 연산 한번으로 끝나기 때문이죠. 

즉 accumulator 에 넣고 바로 1을 더하는 것으로 끝나기 때문에 

후치연산 (i++) 이나 다른 더하는 방법 (i += 1 등) 의 경우에는 

(assembly increment 명령은 다음 페이지를 한번 봐주세요. https://modoocode.com/en/inst/inc )

 

 

 

하지만 모던 컴파일러에서는 동일하다. 라는 이야기도 있었습니다. 

(글 내용 중 "So that's why "Modern compiler produce the same machine code no matter whether i++ or ++i is used"." 라 언급 되어 있네요. ) 

 

관심이 있는 분은 직접 한번 읽어보세요. 

https://retrocomputing.stackexchange.com/questions/27125/has-there-ever-been-a-c-compiler-where-using-i-was-faster-than-i?utm_source=iterable&utm_medium=email&utm_campaign=the-overflow-newsletter 

 

 

 

또한 보면서 든 생각은 생각보다 직접 decompile 을 해서 볼 필요가 있다는 것 입니다. 

당연히 전치연산이 빠를 것이라 생각 했는데, 어셈블리어로 디컴파일을 해보니 후치연산이 더 짧게 나오는 경우입니다. 

 

_Atomic int v;
void pre(void) { ++v; }
void post(void) { v++; }


pre:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-8], 1
        mov     eax, DWORD PTR [rbp-8]
        mov     edx, eax
        mov     eax, edx
        lock xadd       DWORD PTR v[rip], eax
        add     eax, edx
        mov     DWORD PTR [rbp-4], eax
        nop
        pop     rbp
        ret
post:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-8], 1
        mov     eax, DWORD PTR [rbp-8]
        lock xadd       DWORD PTR v[rip], eax
        mov     DWORD PTR [rbp-4], eax
        nop
        pop     rbp
        ret

 

pre 의 경우에 

        mov     edx, eax
        mov     eax, edx

의 두줄이 더 추가가 된 것인데 (왜 mov 를 레지스터 끼리 왔다갔다 하는지 모르겠네요.. ) 

실제 까보기 전에는 모른다는 겁니다. 

 

 

 

그러면 저 속도는 얼마나 차이가 날까요? 

cpu 1 clock 당 1줄씩 연산을 할테니, 

1GHz 의 cpu 기준으로 1e-9 (1 nano second) 만큼 차이가 있겠네요. 

100MHz 정도의 cpu 기준으로는 1e-8 (10 nano second) 만큼 차이가 있을꺼고요. 

 

 

 

그래서 읽으며 내린 제 결론은.. 

1) 후치연산 보다는 전치연산이 빠르긴 하다. 

2) 괄호로 구분해야 할 정도로 다른 연산 가운데 사용된다고 하면 저 미미한 속도 향상 보다는 가독성을 높여 휴먼 에러를 줄이는게 더 좋지 않을까? 

3) 실제 속도 향상이 일어날지에 대해서는 decompile 하고 까봐야 안다. 그러니 대규모 로직 수정이 아닌 저정도로 너무 고민하지는 말자. 

 

라는 것 입니다. 

 

코딩 처음 배우면서 for loop 를 배우면 꼭 학교에서 내는 과제중 하나가 for loop 이용하여 별 그리기 인데, 

학교에서 과제 성격으로 for loop 으로 10줄 줄이겠다고 for 돌리지만, 

실무에서는 다른 사람이 읽고 가독성이 떨어지는거 보다 10줄 출력 하는게 더 좋지 않나? 라는 거랑 동일하겠네요. 

+ Recent posts