Python 라이브러리 추가, pin과 배포물 다운로드
Code Repositories의 Libraries 또는 패키지 탭에서 공개 패키지와 Foundry에서 발행한 Python 라이브러리를 찾습니다. 이름을 검색하고 Add Library를 선택하면 권장 버전을 현재 브랜치에 추가합니다.
추가 후 Code Assist의 의존성이 갱신되면 패키지의 실제 Python 모듈을 import할 수 있습니다. 배포용 Conda 패키지 이름과 Python import 이름은 같지 않을 수 있습니다.
Backing repository와 프로젝트 참조
패키지에 별도 아티팩트 저장소가 필요하면 확인 창이 나타납니다. 접근할 수 없는 저장소는 강조되며 모든 필요한 저장소에 접근할 수 있어야 패키지를 사용할 수 있습니다.
일반적으로 UI가 적절한 backing repository를 찾아 추가합니다. 특정 원천이 필요한 경우에만 Artifacts 설정에서 지정합니다. 수동으로 패키지 의존성만 추가했다면 공유 저장소의 backing repository와 프로젝트 참조도 직접 준비해야 합니다.
특정 버전과 테스트 환경
패키지의 설정 버튼에서 필요한 버전을 선택할 수 있습니다. 실행 환경과 테스트 환경의 버전을 각각 정할 수 있습니다.
특정 버전을 pin하면 meta.yaml이 바뀌지만 이 변경을 Task Runner가 바로 설치하지는 않습니다. Checks가 성공한 뒤 Code Assist를 다시 시작하여 적용합니다. 중요한 수정과 보안 업데이트를 계속 받을 수 있도록 pin도 정기적으로 검토해야 합니다.
추가 및 제거는 일반 코드 변경처럼 commit하고 보호 브랜치로 병합합니다. PR에는 meta.yaml 변경으로 표시됩니다.
Task Runner
Task Runner가 활성화된 저장소에서 Libraries로 패키지를 추가하면 Code Assist workspace를 새로 만드는 대신 현재 실행 환경 위에 설치하며 로그를 보여 줍니다. 성공하면 lockfile을 갱신하고 실패하면 아래 패널에서 오류를 확인합니다.
| 저장소 유형 | 기본 Task Runner의 최소 템플릿 |
|---|---|
parentTemplateId=transforms | 부모 8.220.0 이상, Python 자식 1.484.0 이상 |
parentTemplateId=python-library | 부모 1.497.0 이상 |
숨김 파일의 templateConfig.json에서 확인하고 필요하면 저장소를 업그레이드합니다. Task Runner의 설치는 run 환경만 갱신하며 test-only 의존성 설치는 지원하지 않습니다.
명령
| 명령 | 동작과 조건 |
|---|---|
install --packageSpecs=pandas | 실행 환경에 지정한 Conda 패키지를 설치합니다. |
uninstall --packageSpecs=numpy | 패키지와 그것에 의존하는 패키지도 제거합니다. Transforms Python 자식 또는 Python library 부모 템플릿이 1.525.0 이상이어야 합니다. |
whoneeds --packageSpec=transforms | 현재 run 환경에서 해당 패키지를 요구하는 의존 트리를 보여 줍니다. 해당 Python 템플릿이 1.522.0 이상이어야 합니다. |
formatCode | Black으로 코드를 정리하며 pyproject.toml의 설정을 사용합니다. |
tasks | 실행 가능한 task 목록을 표시합니다. |
제거는 지정한 패키지 하나만의 작업이 아닐 수 있으므로 whoneeds 등으로 의존 관계를 확인합니다.
Meta.yaml과 잠금 파일
패키지는 conda_recipe/meta.yaml에 포함되어야 합니다. UI로 추가하면 필요한 설정을 자동 갱신합니다. 직접 수정한 경우 파일 위쪽 Refresh dependencies로 Code Assist의 의존성을 갱신해야 합니다.
Checks는 Conda 환경을 해석하여 숨겨진 .lock 파일에 저장합니다. 예를 들어 run 환경에는 conda-versions.run.linux-64.lock이 있습니다. 다음 경우에는 새로 해석하여 lockfile을 씁니다.
- Meta.yaml의 패키지 목록이 바뀝니다.
- 저장소 템플릿을 업그레이드합니다.
- 잠금 파일에 회수된 패키지가 있습니다.
- 잠금 파일을 삭제하거나 수정합니다.
재해석 때 원래 commit은 SUPERSEDED와 뒤따르는 commit으로 표시될 수 있으며 새 lockfile을 기록하는 checks는 더 오래 걸릴 수 있습니다.
Lockfile 병합 충돌은 최종 meta.yaml에 맞는 환경으로 다시 해석하여 해결해야 합니다. 충돌 양쪽을 남기는 과정에서도 서로 다른 버전 줄을 그대로 최종 환경으로 확정하지 말고 checks가 생성한 해석 결과를 확인합니다.
이전 shared channel
Shared channel은 deprecated 기능이며 환경에 따라 제공되지 않을 수 있습니다. 기존 libs 채널에 발행한 라이브러리를 쓰는 지원 환경에서는 Python 하위 프로젝트의 build.gradle에서 플러그인 적용 뒤 다음을 추가합니다.
transformsPython {
sharedChannels "libs"
}
루트 build.gradle과 구분하며 채널 접근 권한이 있어야 합니다. 새 공유 패키지는 일반 발행 및 아티팩트 참조 경로를 우선 사용합니다.
플랫폼 밖에서 발행 패키지 받기
사용자 설정의 Tokens에서 용도와 설명을 정해 토큰을 발급할 수 있습니다. 토큰은 사용자를 대신하는 자격 증명이므로 다른 사람이나 애플리케이션에 공유하지 않습니다.
라이브러리 저장소 URL에서 ri.stemma.main.repository... 형식의 전체 repository RID를 확인합니다. 필요한 플랫폼은 예를 들어 noarch 또는 linux-64입니다.
조회 경로는 다음과 같습니다.
| 요청 | 경로 |
|---|---|
| 패키지 색인 | /artifacts/api/repositories/{repositoryRid}/contents/release/conda/{platform}/repodata.json |
| 패키지 파일 | /artifacts/api/repositories/{repositoryRid}/contents/release/conda/{platform}/{packageFilename} |
색인에서 실제 이름, 버전과 build string을 포함한 파일명을 고릅니다. 일반적인 발행물은 name-version-build-string.tar.bz2 형식입니다.
다음 로컬 Python 스크립트는 토큰을 명령행 인자나 URL에 넣지 않고 입력받아 Authorization header로 전달합니다. 조직의 Foundry HTTPS 주소를 직접 확인해 입력해야 합니다. 리디렉션은 따르지 않으며 HTTP 오류는 실패로 전달합니다.
import getpass
import json
from pathlib import Path
import shutil
import urllib.parse
import urllib.request
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
def main():
base = input("Foundry HTTPS base URL: ").strip().rstrip("/")
parsed = urllib.parse.urlsplit(base)
if parsed.scheme != "https" or not parsed.netloc or parsed.username or parsed.password:
raise ValueError("인증 정보를 넣지 않은 Foundry HTTPS 주소가 필요합니다.")
repository = input("Library repository RID: ").strip()
platform = input("Package platform (noarch or linux-64): ").strip()
token = getpass.getpass("Foundry user token: ")
opener = urllib.request.build_opener(NoRedirect())
prefix = (
base + "/artifacts/api/repositories/"
+ urllib.parse.quote(repository, safe="")
+ "/contents/release/conda/"
+ urllib.parse.quote(platform, safe="")
+ "/"
)
def request(url):
return urllib.request.Request(
url, headers={"Authorization": "Bearer " + token}
)
with opener.open(request(prefix + "repodata.json")) as response:
index = json.load(response)
packages = index.get("packages", {})
for filename in sorted(packages):
print(filename)
package = input("다운로드할 위 목록의 파일명: ").strip()
if package not in packages:
raise ValueError("색인에 없는 패키지 파일명입니다.")
destination = Path(input("저장할 로컬 파일 경로: ").strip())
package_url = prefix + urllib.parse.quote(package, safe="")
with opener.open(request(package_url)) as response:
with destination.open("xb") as target:
shutil.copyfileobj(response, target)
if __name__ == "__main__":
main()
기존 로컬 파일은 덮어쓰지 않는 xb 모드입니다. 이 작업은 패키지를 내려받을 뿐 설치하지 않습니다. 제공되지 않은 플랫폼이나 접근할 수 없는 저장소는 색인 또는 파일 요청에서 오류가 발생합니다.