전문가처럼 Python 스크립트 공유: 쉬운 배포를 위한 uv 및 PEP 723

전문가처럼 Python 스크립트 공유: 쉬운 배포를 위한 uv 및 PEP 723

원글: https://thisdavej.com/share-python-scripts-like-a-pro-uv-and-pep-723-for-easy-deployment/

우리 모두 Python의 포괄적인 표준 라이브러리를 좋아하지만, 솔직히 말해서 PyPI의 풍부한 패키지는 종종 필수적입니다. 이러한 외부 도구에 의존하는 단일 파일의 독립적인 Python 스크립트를 공유하는 것은 골치 아픈 일이 될 수 있습니다. 전통적으로 requirements.txt나 Poetry나 pipenv와 같은 본격적인 패키지 관리자에 의존해 왔는데, 이는 간단한 스크립트에는 과도하고 초보자에게는 어려울 수 있습니다. 하지만 더 간단한 방법이 있다면 어떨까요? 바로 이럴 때 uv와 PEP 723이 필요합니다. 이 글에서는 uv가 PEP 723을 활용하여 스크립트 내에 종속성을 직접 임베드하여 배포와 실행을 매우 쉽게 만드는 방법을 자세히 살펴봅니다.

uv와 PEP 723

uv와 차세대 Python 도구에서 제가 가장 좋아하는 기능 중 하나는 외부 Python 패키지에 대한 참조가 포함된 단일 파일 Python 스크립트를 별다른 절차 없이 실행할 수 있다는 것입니다. uv는 "인라인 스크립트 메타데이터"에 중점을 둔 PEP 723의 도움으로 이 기능을 구현했습니다. 이 PEP는 외부 패키지 종속성을 포함한 스크립트 메타데이터를 단일 파일 Python 스크립트에 직접 임베드하는 표준화된 방법을 정의합니다.

PEP 723은 Python 개선 제안(Python Enhancement Proposal) 절차를 거쳐 Python 운영 위원회(Python Steering Council)의 승인을 받았으며, 이제 공식 Python 사양에 포함되었습니다. uv, PDM(Python Development Master), Hatch 등 Python 생태계의 다양한 도구들이 PEP 723 지원을 구현했습니다. 이 글에서는 단일 파일 Python 스크립트를 생성하고 배포할 수 있는 uv의 탁월한 PEP 723 지원 기능에 대해 중점적으로 살펴봅니다.

uv도 패키지 관리자이지만, 독립적인 Python 스크립트 실행을 간소화하고 스크립트 작성자와 사용자 모두의 인지 부담을 줄여줍니다. uv를 직접 사용해 보세요!

💡 참고: 이 기사가 마음에 든다면 uv를 사용하여 Python 명령줄 앱을 현대식으로 패키징하는 방법도 참조하세요.

무대 설정

사전 API에서 정의를 가져 오는 Python 스크립트를 만들었습니다 wordlookup.py. 꽤 괜찮아 보이지만, 다른 사람들도 쉽게 실행할 수 있도록 배포하고 싶습니다.

import argparse
import asyncio
import json
import os
import textwrap

import httpx

async def fetch_word_data(word: str) -> list:
    """Fetches word data from the dictionary API."""
    url = f"https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(url)
            response.raise_for_status()
            return response.json()
    except httpx.HTTPError:
        return None
    except json.JSONDecodeError as exc:
        print(f"Error decoding JSON for '{word}': {exc}")
        return None
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
        return None

async def main(word: str):
    """Fetches and prints definitions for a given word with wrapping."""
    data = await fetch_word_data(word)
    if data:
        print(f"Definitions for '{word}':")
        try:
            terminal_width = os.get_terminal_size().columns - 4  # 4 for padding
        except OSError:
            terminal_width = 80  # default if terminal size can't be determined

        for entry in data:
            for meaning in entry.get("meanings", []):
                part_of_speech = meaning.get("partOfSpeech")
                definitions = meaning.get("definitions", [])
                if part_of_speech and definitions:
                    print(f"\n{part_of_speech}:")
                    for definition_data in definitions:
                        definition = definition_data.get("definition")
                        if definition:
                            wrapped_lines = textwrap.wrap(
                                definition, width=terminal_width,
                                subsequent_indent=""
                            )
                            for i, line in enumerate(wrapped_lines):
                                if i == 0:
                                    print(f"- {line}")
                                else:
                                    print(f"  {line}")
    else:
        print(f"Could not retrieve definition for '{word}'.")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Fetch definitions for a word.")
    parser.add_argument("word", type=str, help="The word to look up.")
    args = parser.parse_args()

    asyncio.run(main(args.word))

이 스크립트는 여러 Python 모듈을 가져와서 사전 API 웹 서비스와 상호 작용하고, JSON 데이터를 처리하고, 명령줄 인수를 처리하고, 비동기 작업을 활용하고, 텍스트 출력을 포맷하고, 운영 체제와 상호 작용하여 터미널 너비를 가져오는 스크립트의 기반을 마련합니다. httpx HTTP 클라이언트 라이브러리 패키지인 를 제외하고, 우리가 가져오는 다른 모든 Python 모듈은 Python 표준 라이브러리의 일부입니다. Python의 내장 urllib.request 모듈을 사용하여 기술적으로는 목표를 달성할 수 있지만, 저는 httpx를 선호합니다. 그러나 이는 친구와 동료들이 필요한 종속성을 설치하는 번거로움 없이 사용할 수 있도록 이 스크립트를 배포할 좋은 방법이 필요하다는 딜레마를 야기합니다 httpx.

이 딜레마를 어떻게 해결해야 할까요? uv가 해결책입니다! 이제 작동 방식을 살펴보겠습니다.

참고: 이 게시물은 Hacker News에서 많은 관심을 받았으며, 활발한 토론을 불러일으켰습니다(링크). Python 내장 웹 클라이언트 옵션과 httpxrequests와 같은 라이브러리 중 어떤 것을 선택해야 하는지에 대한 질문이 제기되었습니다. 이 글에서 제가 중점적으로 다루는 것은 외부 Python 패키지를 활용하는 단일 파일 Python 스크립트를 만들고 공유하는 방법을 보여주는 것이며, 특정 웹 클라이언트를 옹호하는 것은 아닙니다. '사용하기'는 httpx이 개념을 보여주는 예시일 뿐입니다.

UV 설치

첫 번째 단계로 UV를 설치해야 합니다. UV 설치 방법은 공식 UV 설명서를 참조하세요. UV를 설치하는 몇 가지 일반적인 방법은 다음과 같습니다.

# Assuming you have pipx installed, this is the recommended way since it installs
# uv into an isolated environment
pipx install uv

# uv can also be installed this way
pip install uv

uv는 놀라울 정도로 다재다능하며, 제 생각에는 Python 도구의 미래라고 할 수 있습니다. 하지만 이 글에서는 외부 종속성을 가진 단일 파일 스크립트를 호출하는 uv의 멋진 기능 중 하나를 보여드리겠습니다.

uv를 사용하여 단일 파일 스크립트에 패키지 종속성 추가

httpx이제 스크립트에 종속성을 추가할 준비가 되었습니다 wordlookup.py! 방법은 다음과 같습니다.

uv add --script wordlookup.py httpx

이제 끝입니다! 그 후, uv는 스크립트 상단의 주석에 메타데이터를 추가합니다. 스크립트의 첫 부분과 그 뒤에 몇 줄의 맥락을 제공하여 실제 동작을 확인할 수 있도록 하였습니다.

# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "httpx",
# ]
# ///

import argparse
import asyncio
import json
import os
import textwrap

import httpx

Poetry, Flit, Hatch, Maturin, setuptools 등 다양한 Python 도구를 사용해 보셨다면 pyproject.toml이 구문은 적어도 어느 정도는 익숙하게 보일 것입니다. 예를 들어 Poetry는 다음과 같습니다.

# <-- other package metadata here -->

[tool.poetry.dependencies]
python = ">=3.13"
httpx = "^0.28.1"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

uv가 에 대한 메타데이터를 추가하지만 버전을 지정하지 않는 것을 확인할 수 있습니다 httpx. uv는 스크립트에 사용할 최신 안정 버전의 httpx를 PyPI에서 가져옵니다. 종속성 제약 조건은 작업 직후에 메타데이터를 수정하거나 명령줄을 통해 버전 종속성을 지정하여 추가할 수 있습니다.

uv add --script wordlookup.py "httpx>=0.28.1"

uv로 스크립트 실행

스크립트를 실행할 준비가 되었습니다. uv 도구를 사용하면 다음과 같이 간편하게 실행할 수 있습니다(--help스크립트에 인수도 전달하고 있습니다).

$ uv run wordlookup.py --help
Installed 7 packages in 74ms
usage: wordlookup.py [-h] word

Fetch definitions for a word.

positional arguments:
  word        The word to look up.

options:
  -h, --help  show this help message and exit

스크립트를 처음 호출하면 uv가 자동으로 백그라운드에서 격리된 가상 환경을 생성하고 패키지와 관련 종속성을 uv run가져와 설치하기 때문에 처음에는 추가 작업이 수행됩니다. 이것이 터미널 출력에서 다음과 같은 내용을 볼 수 있는 이유입니다: httpx Installed 7 packages in 74ms

스크립트를 실행하려고 하면 전역으로 설치 python wordlookup.py했거나 현재 가상 환경에 설치하지 않은 경우 스크립트가 실패합니다 httpx. uv는 스크립트 메타데이터를 어떻게 사용하나요? 스크립트를 uv runuv로 호출할 때:

  1. 필요한 Python 버전을 사용할 수 있는지 확인합니다.
  2. 전역 Python 환경을 수정하지 않고 자동으로 격리된 가상 환경을 생성합니다.
  3. httpx아직 설치되지 않은 경우 나열된 종속성을 설치합니다.
  4. 스크립트를 실행합니다.

스크립트를 실행할 때마다 uv runuv는 백그라운드에서 생성된 가상 환경을 활용하여 스크립트를 호출합니다.

$ uv run wordlookup.py postulate
Definitions for 'postulate':

noun:
- Something assumed without proof as being self-evident or generally accepted, especially when used as a basis
  for an argument. Sometimes distinguished from axioms as being relevant to a particular science or context,
  rather than universally true, and following from other axioms rather than being an absolute assumption.
- A fundamental element; a basic principle.
- An axiom.
- A requirement; a prerequisite.

verb:
- To assume as a truthful or accurate premise or axiom, especially as a basis of an argument.
- To appoint or request one's appointment to an ecclesiastical office.
- To request, demand or claim for oneself.

adjective:
- Postulated.

스크립트에 추가 종속성을 추가하거나 httpx메타데이터의 Python이나 버전을 변경하면 uv run다음에 호출될 때 새로운 격리된 가상 환경이 생성됩니다.

Python shebang을 사용하여 실행을 더욱 쉽게 만들기

Python 스크립트 맨 위에 shebang(때로는 hashbang이라고도 함)을 추가하면 uv로 스크립트를 더욱 쉽게 호출할 수 있습니다. Trey Hunner에게서 이 훌륭한 방법을 배웠습니다.

Linux/macOS 사용자

Linux 및 macOS(및 BSD 사용자)의 경우 스크립트 맨 위에 다음 줄을 추가합니다.

#!/usr/bin/env -S uv run --script

📢 업데이트: macOS에서는 in the shebang이 선택 사항인 것으로 나타났습니다 -S. 스크립트는 어떤 방식으로든 정상적으로 실행됩니다. 이 문제를 알려준 Gregg Lind에게 감사드립니다!

정기적으로 예약된 프로그램으로 돌아가면...전체 스크립트 컨텍스트는 파일 맨 위에 다음과 같이 표시됩니다.

#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "httpx>=0.28.1",
# ]
# ///

import argparse
import asyncio
import json
import os
import textwrap

import httpx

다음으로, 파일을 실행 가능하게 만듭니다.

chmod u+x wordlookup.py

이 작업이 완료되면 전체 명령을 사용하지 않고도 스크립트를 직접 실행할 수 있습니다 uv run wordlookup.py.

./wordlookup --help

윈도우 사용자

Windows 사용자의 경우, Windows용 py 런처가 shebang을 해석할 수 있으므로 더욱 편리합니다. py 런처는 Windows에 Python을 설치할 때 기본적으로 포함되어 있습니다. 스크립트가 -S제대로 작동하려면 shebang에서 를 생략해야 합니다. 스크립트의 첫 번째 줄은 다음과 같습니다.

#!/usr/bin/env uv run --script

Windows에서 다음 py명령을 사용하여 스크립트를 호출할 수 있습니다.

py wordlookup.py

참고: 스크립트를 통해 호출하면 python wordlookup.pyshebang이 해석되지 않으므로 이 기능은 작동하지 않습니다.

./wordlookup.py명령 프롬프트(cmd.exe)에서 실행할 경우, shebang 해석을 통해 Python 스크립트를 직접 실행할 수 있습니다(예: ). 하지만 py wordlookup.py명령 프롬프트와 PowerShell 모두에서 일관된 동작을 위해서는 를 사용하는 것이 좋습니다.

컴퓨터의 어느 곳에서나 호출할 수 있도록 uv 스크립트 설정

uv(Python) 스크립트를 시스템의 어느 곳에서나 쉽게 실행할 수 있도록 하려면, 해당 스크립트를 시스템의 PATH에 포함된 공통 실행 파일 디렉터리로 옮기면 됩니다.

Linux/macOS 사용자

Linux 및 macOS 사용자의 경우, wordlookup.py스크립트를 시스템 디렉터리에 복사하세요 $PATH. 제 시스템에서는 해당 $HOME/bin폴더가 경로에 있으므로 해당 경로로 이동했습니다.

mv wordlookup.py ~/bin

또한 파일 이름을 바꾸고 .py 파일 확장자를 제거하여 해당 파일을 Python 스크립트로 식별하는 데 필요한 모든 정보가 포함되어 있으므로 호출 시 더욱 편리하도록 했습니다.

mv wordlookup.py wordlookup

이제 어디서든 호출할 수 있습니다. (또한, uv가 새 가상 환경을 생성하고 Python 스크립트를 새 위치에서 처음 호출할 때 패키지 종속성을 해결하는 것을 확인할 수 있습니다.)

$ wordlookup --help
Installed 7 packages in 21ms
usage: wordlookup.py [-h] word

Fetch definitions for a word.

positional arguments:
  word        The word to look up.

options:
  -h, --help  show this help message and exit

윈도우 사용자

Windows 사용자의 경우, 스크립트를 시스템 PATH환경 변수에 이미 포함된 디렉터리 중 하나로 이동하거나 .에 새 폴더를 추가 할 수 있습니다. .라는 폴더를 생성하여 . 에 추가했다고 PATH가정하겠습니다 .c:\scripts PATH

다음으로, 라는 파일을 만들고 wordlookup.cmd다음 내용을 추가합니다.

@echo off
py c:\scripts\wordlookup.py %*

그러면 다음과 같이 시스템의 어느 곳에서나 Windows 터미널이나 명령 프롬프트에서 스크립트를 호출할 수 있습니다.

wordlookup --help

종속성 잠금(선택 사항)

AJ Kerrigan은 아래 댓글 섹션에서 uv가 단일 파일 스크립트에 대한 종속성 잠금을 선택 기능으로 제공한다고 예리하게 지적했습니다. 자세한 내용은 uv 설명서를 참조하십시오. 이를 통해 모든 직접 및 간접 종속성에 대해 정확한 버전을 지정하여 일관되고 반복 가능한 런타임 동작을 보장하고, 스크립트 공유 시 예상치 못한 문제를 방지할 수 있습니다.

파일을 만드는 방법은 다음과 같습니다 .lock.

uv lock --script wordlookup.py

실행하면 스크립트와 같은 uv lock이름의 .lock 파일이 wordlookup.py.lock같은 디렉토리에 생성됩니다.

tree .
.
├── wordlookup.py
└── wordlookup.py.lock

이것이 대본 자체에 영향을 미칠지 궁금했는데, 변경되지 않은 채로 남아 있어서 기뻤습니다 wordlookup.py.

솔루션을 배포할 때는 스크립트와 생성된 .lock파일을 모두 포함하여 패키지 종속성 버전을 고정하여 안정성을 더욱 강화하세요.

간편한 공유를 위한 많은 일회성 스크립트의 경우, 이러한 .lock파일을 만드는 것은 과할 수 있습니다. 그럼에도 불구하고 uv가 이러한 옵션을 제공한다는 것은 매우 유용합니다. 애플리케이션이 단일 파일 스크립트 옵션보다 더 커질 수 있습니다. 전문가 수준의 명령줄 도구를 구축하려면 uv를 사용하여 Python 명령줄 앱을 현대적으로 패키징하는 방법에 대한 제 가이드를 참조하세요.

다른 사람들과 스크립트 공유하기

친구나 동료와 대본을 공유하는 방법은 다양하며, 이 섹션에서는 그 중 몇 가지 옵션을 소개하겠습니다.

로컬 시스템에서 호스팅

사람들이 로컬 시스템에서 주어진 스크립트를 실행할 수 있도록 하기 위해 다음과 같은 공유 옵션이 제공됩니다.

  • 이메일로 보내기: 스크립트와 .lock파일(생성된 경우)을 이메일로 보낼 수 있습니다. 일부 이메일 시스템은 Python(.py) 파일을 차단하거나 삭제할 수 있습니다. .py 파일을 압축하면 기본 필터를 우회할 수 있지만, 고급 시스템에서는 여전히 아카이브 내용을 분석하여 정상적인 스크립트를 악성 스크립트로 오인할 수 있습니다.
  • Google Drive나 OneDrive와 같은 클라우드 저장 서비스에 스크립트를 업로드하고 공유하면 다른 사람이 스크립트를 다운로드하여 로컬 시스템에서 실행할 수 있습니다.

로컬 네트워크 드라이브에 호스팅

네트워크 드라이브를 사용하여 스크립트를 공유할 수 있습니다. 이렇게 하면 단일 위치에서 스크립트를 실행할 수 있어, 같은 네트워크에서 파일을 공유하는 동료나 가족 구성원 간에도 간편하게 공유할 수 있습니다.

웹 서버에서 호스팅

이 옵션은 정말 놀라웠습니다. 개인 호스팅이나 GitHub Gist 같은 서비스처럼 웹 서버에 스크립트를 호스팅할 수 있거든요. 중앙에서 업데이트하고 공유하기가 정말 편리해요!

https://thisdavej.com/utils/wordlookup.py wordlookup.py 에 스크립트를 업로드했으므로 웹을 통해 Python 스크립트를 실행하는 것을 직접 경험할 수 있습니다.

⚠️ 참고: 인터넷에서 가져온 스크립트는 매우 신중하게 사용해야 합니다. 이 방법은 uv 및 PEP 723 덕분에 필요한 모든 외부 종속성을 포함하여 독립적이고 단일 파일 Python 스크립트를 배포하고 실행하는 매우 편리한 방법을 제공합니다. 하지만 아래 설명된 대로 실행하기 전에 파일을 검사해 주세요.

모든 스크립트는 실행하기 전에 내용을 검사하는 것이 좋습니다. 브라우저에서 스크립트 URL을 실행하여 검사할 수도 있지만, 터미널 컨텍스트에서 주어진 스크립트를 호출할 준비를 하는 동안에도 스크립트를 검사할 수 있습니다.

Linux/macOS 시스템의 경우 curl을 사용하여 스크립트를 검사할 수 있습니다.

# Download the script with curl and pipe the output into less. We use less -F
# as a bonus to instruct `less` to automatically exit if the entire file can
# be displayed on a single screen.
curl -sS https://thisdavej.com/utils/wordlookup.py | less -F

보너스로, curl의 출력을 bat 으로 파이프하여 터미널에서 구문 강조 기능을 구현할 수 있습니다. (이 튜토리얼에서는 여러분이 투자한 만큼의 가치를 보장합니다. 😉)

# We use `bat -l python` to explicitly set the language for syntax highlighting.
curl -sS https://thisdavej.com/utils/wordlookup.py | bat -l python

# The `-l` option can be shortened from `python` to `py`
curl -sS https://thisdavej.com/utils/wordlookup.py | bat -l py

Windows(PowerShell 및 cmd) 사용자는 curl.exe터미널에서 스크립트 내용을 볼 수 있으며(예, 이제 Windows와 함께 제공됩니다!) 출력이 한 화면보다 길면 페이지 나누기 more대신 사용할 수 있습니다 .less

참고: Windows에서는 which 대신 curl.exe를 사용해야 합니다. which curl는 의 PowerShell 별칭으로 Invoke-WebRequest, 기능은 비슷하지만 구문이 다르고 기능이 적습니다 curl.exe.

# From PowerShell on Windows
PS C:\> curl.exe -s https://thisdavej.com/utils/wordlookup.py | more

스크립트 내용을 검토했으니 이제 URL에서 .를 사용하여 스크립트를 실행할 수 있습니다 . 스크립트에 명령줄 인수로 uv run전달한다는 점에 유의하세요 .magnanimous

$ uv run https://thisdavej.com/utils/wordlookup.py magnanimous
Definitions for 'magnanimous':

adjective:
- Noble and generous in spirit.

uv run로컬 파일로 실행하는 것과 유사하게 , uv는 종속성을 원활하게 해결하고 URL을 통해 스크립트를 성공적으로 실행합니다. 정말 강력하죠!

AJ Kerrigan은 아래 댓글에서 URL을 사용하여 스크립트를 실행하는 데 대한 유용한 팁을 제공했습니다. 고맙습니다, AJ!

보너스: UV는 가상 환경을 어디에 설치하나요?

호기심 많은 소프트웨어 엔지니어인 저는 Fedora Linux 시스템에서 uv가 가상 환경을 어디에 설치하는지 더 자세히 알아보기로 했습니다. 결국 uv가 wordlookup.py설치된 디렉터리에 있었습니다. 패키지 종속성 메타데이터를 uv add --script추가 하고 를 호출했지만 , 로컬 폴더에서 uv와 같은 가상 환경 디렉터리는 찾을 수 없었습니다.httpx uv run .venv

업데이트: 이 글이 처음 게시된 바로 그날, 가상 환경의 위치를 더욱 편리하게 찾을 수 있는 uv v0.6.10이 출시되었습니다. 이러한 통찰력을 제공해 주신 Hacker News의 JimDabell 님께 감사드립니다.

uv 0.6.10 이상에서 주어진 단일 파일 Python 스크립트에 대한 가상 환경이 uv에 설치된 위치를 찾으려면 다음 명령을 호출하세요.

$ uv python find --script wordlookup.py
/home/dave/.cache/uv/environments-v2/wordlookup-f6e73295bfd5f60b/bin/python3

쉽죠! uv가 가상 환경을 사용자의 홈 디렉터리인 ~/.cache/uv/environments-v2/. 에 설치하는 것을 볼 수 있습니다.

가상 환경의 위치는 단순히 호기심을 충족하는 것 외에도 유용할 수 있습니다. 일부 Python 도구는 가상 환경의 위치를 알아야 하기 때문입니다. 예를 들어, Python용 정적 유형 검사기인 pyright를 사용한다면 Linux/macOS에서 다음과 같이 가상 환경의 위치를 제공할 수 있습니다.

pyright --pythonpath $(uv python find --script wordlookup.py) wordlookup.py

이 섹션의 나머지 부분은 제가 설명하는 기술이 다른 문제 해결 노력에도 유용할 수 있기 때문에 역사적 목적으로 포함되었습니다.

스크립트가 생성된 후 httpx처음 호출하면 이 이름의 새 폴더가 생성될 가능성이 높으므로 먼저 시스템에 있는 모든 디렉토리를 찾는 것부터 시작했습니다.uv run

$ find -type d -name httpx
./.cache/uv/environments-v2/wordlookup-f6e73295bfd5f60b/lib/python3.13/site-packages/httpx
# <other folders found but omitted for brevity>

와, httpx이라는 이름의 부모 폴더 안에 이라는 이름의 폴더가 있더군요 ./.cache/uv/environments-v2. 괜찮은 것 같았습니다.

그러다가 uv 캐시 클린 이라는 명령어를 발견했는데, 이 명령어를 사용하면 모든 uv 가상 환경을 지울 수 있었습니다. 가상 환경은 쉽게 다시 만들 수 있기 때문에 큰 문제는 없을 겁니다.

$ uv cache clean
Clearing cache at: .cache/uv
Removed 848 files (8.2MiB)

Linux 시스템에서 모든 작업을 보기 위해(아마도 이건 지나친 선택이었을 거예요 😃), 캐시를 지웠기 때문에 uv가 가상 환경을 다시 만들어야 했기 때문에 inotifywait파일 생성 이벤트를 모두 모니터링하곤 했습니다.uv run wordlookup.py

inotifywait -m -r -e create ~/.cache/

# While this was running and waiting for event, I invoked `uv run wordlookup.py` from another terminal window

inotifywait명령(inotify-tools패키지의 일부)은 파일 시스템 이벤트를 기다렸다가 출력합니다. 제가 사용한 인수는 다음과 같습니다.

  • -m(모니터): 이 옵션은 inotifywait지정된 디렉터리에서 이벤트를 지속적으로 모니터링하도록 지정합니다. 이 옵션을 지정하지 않으면 inotifywait첫 번째 이벤트만 보고하고 종료됩니다.
  • -r (재귀적): 이 옵션은 inotifywait지정된 디렉터리와 그 하위 디렉터리의 이벤트를 재귀적으로 모니터링하도록 지정합니다. 해당 디렉터리 또는 그 하위 디렉터리에 새 파일이나 디렉터리가 생성되면 .cache/이벤트가 발생합니다.
  • -e create (이벤트: create): 이 옵션은 inotifywait생성 이벤트만 보고하도록 지정합니다. 생성 이벤트는 모니터링되는 디렉터리 내에 새 파일이나 디렉터리가 생성될 때 발생합니다.
  • inotifywait.cache/: 모니터링하도록 요청된 디렉토리입니다.

예상대로, 출시 inotifywait시 폴더가 동적으로 생성되는 것이 드러났습니다.uv run wordlookup.py

wordlookup.py스크립트를 내 폴더에 복사한 $HOME/bin다음 거기에서 호출했을 때 확인해보니 가상 환경을 담고 있는 ./.cache/uv/environments-v2/또 다른 스크립트가 생성되어 있었습니다.wordlookup-*

Windows VM을 검토해 보니 비슷한 uv가상 환경이 .에 설치되어 있는 것을 발견했습니다%LOCALAPPDATA%\uv\cache`.

더 자세히 조사해 보니, uv 캐시 디렉터리 관련 문서를 찾았는데, 거기에 uv가 캐시 디렉터리의 위치를 어떻게 결정하는지 설명되어 있었습니다. 작동 원리는 다음과 같습니다.

uv는 다음 순서에 따라 캐시 디렉토리를 결정합니다.

  1. --no-cache요청된 경우 임시 캐시 디렉토리입니다.
  2. --cache-dir, UV_CACHE_DIR, 또는 를 통해 지정된 특정 캐시 디렉토리입니다 tool.uv.cache-dir.
  3. 시스템에 적합한 캐시 디렉토리(예: $XDG_CACHE_HOME/uvUnix $HOME/.cache/uv%LOCALAPPDATA%\uv\cacheWindows)

일반적으로 제 Fedora 설정과 같은 Unix 계열 시스템에서 uv는 $HOME/.cache/uv에 캐시를 저장합니다. 하지만 $XDG_CACHE_HOME 환경 변수를 설정하여 이 위치를 변경할 수 있습니다. XDG에 익숙하지 않은 분들을 위해 설명드리자면, XDG 기본 디렉터리 사양은 애플리케이션이 파일을 구성하는 데 따르는 일련의 지침입니다. 특정 디렉터리를 가리키는 몇 가지 주요 환경 변수를 정의하여 다양한 유형의 애플리케이션 데이터가 지정된 위치에 저장되도록 합니다. 자세한 내용은 여기를 참조하세요.

요약하자면, uv는 단일 파일 Python 스크립트의 가상 환경을 캐시 내에 저장합니다. 기본값을 변경하기 위해 특별한 작업을 하지 않으면 일반적으로 다음과 같은 OS별 위치에 저장됩니다.

운영 체제 가상 환경 위치
리눅스 ~/.cache/uv/environments-v2/
맥OS ~/.cache/uv/environments-v2/
윈도우 %LOCALAPPDATA%\uv\cache\environments-v2

업데이트: Hacker News의 한 통찰력 있는 사용자(sorenjan)가 캐시 디렉터리 루트(예: )의 위치를 찾기 위해 실행할 수도 있다고 지적했습니다 ~/.cache.uv cache dir

uv는 어떻게 가상 환경 폴더 이름을 파생시키나요?

Linux 시스템의 다음 uv 가상 환경 폴더를 살펴보세요. wordlookup-f6e73295bfd5f60b생성된 폴더 이름은 어떻게 되나요?

./.cache/uv/environments-v2/wordlookup-f6e73295bfd5f60b

uv의 Rust 코드와 다른 리소스에 대한 예비 조사 결과, 가상 환경 폴더 이름은 Python 버전과 외부 패키지 종속성 버전(httpx제 컨텍스트에서처럼)의 해시값으로 생성되는 것으로 나타났습니다. 이러한 설계는 스크립트 이름(폴더 이름 자체에 내장됨)을 포함한 이러한 요소들을 수정하면 캐시에 고유한 가상 환경이 생성되도록 합니다. httpx메타데이터에 다른 버전의 uv를 지정하거나 스크립트 파일 이름을 변경하면 uv가 새로운 가상 환경을 생성하는 것을 관찰하여 이를 경험적으로 검증했습니다.

결론

결론적으로, PEP 723을 구현한 uv는 외부 종속성이 있는 단일 파일 Python 스크립트를 처리하는 방식을 간소화하는 훌륭한 도구입니다. 스크립트 내에 메타데이터를 직접 내장함으로써 uv는 별도의 requirements.txt파일과 복잡한 패키지 관리자의 필요성을 없애줍니다. uv는 종속성 설치 및 가상 환경 관리 프로세스를 간소화하여 스크립트 실행을 훨씬 더 쉽게 만들어 줍니다. 셔뱅과 시스템 전체 실행 파일의 편의성은 사용성을 더욱 향상시킵니다. 궁극적으로 이러한 조합은 특히 단일 파일 스크립트의 경우 Python 스크립팅의 접근성을 높이고 개발자와 사용자 모두에게 더욱 간소화된 워크플로를 제공할 것입니다.

2025년 4월 11일 업데이트됨. 원래 게시일은 2025년 3월 25일입니다.

댓글