<p>pip๋Š” ์ง์ ‘ URL ์ฐธ์กฐ์—์„œ ์ƒ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.</p>

์— ๋งŒ๋“  2019๋…„ 06์›” 28์ผ  ยท  7์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: pypa/pip

ํ™˜๊ฒฝ

  • pip ๋ฒ„์ „ : 19.1.1
  • Python ๋ฒ„์ „ : 3.7
  • ์šด์˜์ฒด์ œ : MacOS Mojave
์—ฌ๊ธฐ์— ํ™˜๊ฒฝ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ์ •๋ณด๋ฅผ ์ž์œ ๋กญ๊ฒŒ ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค.

๊ธฐ์ˆ 
์ด๊ฒƒ์ด six ๋ฒ„๊ทธ์ธ์ง€ Pip ๋ฒ„๊ทธ์ธ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. six ์†ํ•˜๋Š” ๊ฒฝ์šฐ ์‹ค๋ก€ํ•ฉ๋‹ˆ๋‹ค.

ํ•์€ ๋กœ์ปฌ ๊ฒฝ๋กœ๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ install_requires ๋ฅผ ํ†ตํ•ด name @ ./some/path ํ•˜์ง€๋งŒ, URL ํŒŒ์‹ฑ์€ ์ •๋ง ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

https://github.com/pypa/pip/blob/a38a0eacd4650a7d9af4e6831a2c0aed0b6a0329/src/pip/_internal/download.py#L670 -L690

์ด ํ•จ์ˆ˜์—์„œ๋Š” urlsplit ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋“ค์–ด์˜ค๋Š” URL์˜ ๊ฐœ๋ณ„ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๋ช‡ ๊ฐ€์ง€ ์ž…๋ ฅ์œผ๋กœ ํ‘œ์‹œ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

./foo/bar -> SplitResult(scheme='', netloc='', path='./foo/bar', query='', fragment='')
file:./foo/bar -> SplitResult(scheme='file', netloc='', path='./foo/bar', query='', fragment='')
file://./foo/bar -> SplitResult(scheme='file', netloc='.', path='/foo/bar', query='', fragment='')

๋งˆ์ง€๋ง‰ ๊ฒฐ๊ณผ๋Š” netloc ๊ฐ€ ๋น„์–ด์žˆ๋Š” ๋Œ€์‹  . ์ด๊ณ  path ๋Š” ๋กœ์ปฌ์ด ์•„๋‹Œ ์ ˆ๋Œ€ ๊ฐ’์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋กœ์ปฌ์ด ์•„๋‹Œ ๊ฒฝ๋กœ์™€ ๊ด€๋ จ๋œ ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ ํ˜•์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์กฐ๊ฑด๋ถ€ ๋…ผ๋ฆฌ๋ฅผ ์ถฉ์กฑ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (์ฒซ ๋ฒˆ์งธ ํ˜•์‹๋„ ์ง€์›ํ•ด์•ผํ•˜์ง€๋งŒ).

๊ทธ๋Ÿฌ๋‚˜ ๋‹ค๋ฅธ ๊ณณ์— ์ƒ์ถฉ๋˜๋Š” ๋…ผ๋ฆฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

https://github.com/pypa/pip/blob/169eebdb6e36a31e545804228cad94902a1ec8e9/src/pip/_vendor/packaging/requirements.py#L103 -L106

์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์ด์ „ ๋…ผ๋ฆฌ๋ฅผ ๋งŒ์กฑํ•˜๋”๋ผ๋„ ์‹คํŒจํ•˜๋Š” ๋…ผ๋ฆฌ์ž…๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๋ฌธ์ œ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ํ…Œ์ŠคํŠธ ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

from six.moves.urllib import parse as urllib_parse

def tryparse(url):
    print(url)
    parsed = urllib_parse.urlparse(url)
    unparsed = urllib_parse.urlunparse(parsed)
    parsed_again = urllib_parse.urlparse(unparsed)
    print(parsed)
    print(unparsed)
    print(parsed_again)

./foo/bar ๋Œ€ํ•œ ์ถœ๋ ฅ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

>>> tryparse('./foo/bar')
./foo/bar
ParseResult(scheme='', netloc='', path='./foo/bar', params='', query='', fragment='')
./foo/bar
ParseResult(scheme='', netloc='', path='./foo/bar', params='', query='', fragment='')

file: ์Šคํ‚ค๋งˆ๋ฅผ ์š”๊ตฌํ•˜๋Š” ์ฒซ ๋ฒˆ์งธ ํ•จ์ˆ˜์˜ ๋…ผ๋ฆฌ๋ฅผ ๋งŒ์กฑ ์‹œํ‚ค์ง€๋Š” ์•Š์ง€๋งŒ ๋ชจ๋‘ ์ข‹์Šต๋‹ˆ๋‹ค.

file:./foo/bar ๋Œ€ํ•œ ์ถœ๋ ฅ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

>>> tryparse('file:./foo/bar')
file:./foo/bar
ParseResult(scheme='file', netloc='', path='./foo/bar', params='', query='', fragment='')
file:///./foo/bar
ParseResult(scheme='file', netloc='', path='/./foo/bar', params='', query='', fragment='')

์ด๋Ÿฐ! ์ฒซ ๋ฒˆ์งธ ๊ตฌ๋ฌธ ๋ถ„์„ ํ˜ธ์ถœ์˜ ๊ฒฐ๊ณผ๋ฅผ "unparse"ํ•  ๋•Œ path ๊ฐ€ ์ ˆ๋Œ€ file:///... ๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๋‘ ๋ฒˆ์งธ๋กœ ์–ธ๊ธ‰ ๋œ ๊ฒ€์‚ฌ๊ฐ€ ์‹คํŒจํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ๊ฒฝ๋กœ๊ฐ€ ๋กœ์ปฌ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด six ์˜ ๋ฒ„๊ทธ๋ผ๊ณ  ์ƒ๊ฐ ํ•˜์ง€๋งŒ scheme in ['file', ''] ํ•˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ./foo/bar URI ์–‘์‹์„ ์‚ฌ์šฉํ•˜๋„๋ก ์ง€์‹œํ•จ์œผ๋กœ์จ Pip์—์„œ ์™„ํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋‘ ๊ฐ€์ง€ ๋ชจ์ˆœ ๋œ ๋…ผ๋ฆฌ๋ฅผ ๊ฐ์•ˆํ•  ๋•Œ distutils ๋˜๋Š” setuptools ๊ตฌ์„ฑ์—์„œ install_requires ํ‚ค์˜ ๋กœ์ปฌ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™
name @ ./some/path (๋˜๋Š” ์†”์งํžˆ ./some/path )๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋‚ด ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๋กœ์ปฌ๋กœ ๊ณต๊ธ‰๋˜๋Š” ํŒจํ‚ค์ง€๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์žฌํ˜„ ๋ฐฉ๋ฒ•

#!/usr/bin/env bash
mkdir /tmp/pip-uri-repro && cd /tmp/pip-uri-repro

mkdir -p foo/bar

cat > requirements.txt <<EOF
./foo
EOF

cat > foo/setup.py <<EOF
#!/usr/bin/env python
from setuptools import setup
setup(
    name="foo",
    version="0.1",
    install_requires=[
        "bar @ file:./bar"
    ]
)
EOF

cat > foo/bar/setup.py <<EOF
#!/usr/bin/env python
from setuptools import setup
setup(
    name="bar",
    version="0.1"
)
EOF

# (OUTPUT 1)
pip install -r requirements.txt

cat > foo/setup.py <<EOF
#!/usr/bin/env python
from setuptools import setup
setup(
    name="foo",
    version="0.1",
    install_requires=[
        # we're forced to use an absolute path
        # to make the "Invalid URL" error go
        # away, which isn't right anyway (the
        # error that is raised as a result
        # is justified)
        "bar @ file://./bar"
    ]
)
EOF

# (OUTPUT 2)
pip install -r requirements.txt

์‚ฐ์ถœ

์ฒ˜์Œ pip install :

Processing ./foo
    ERROR: Complete output from command python setup.py egg_info:
    ERROR: error in foo setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers; Invalid URL given

๋‘ ๋ฒˆ์งธ pip install :

Processing ./foo
ERROR: Exception:
Traceback (most recent call last):
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/cli/base_command.py", line 178, in main
    status = self.run(options, args)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/commands/install.py", line 352, in run
    resolver.resolve(requirement_set)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/resolve.py", line 131, in resolve
    self._resolve_one(requirement_set, req)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/resolve.py", line 294, in _resolve_one
    abstract_dist = self._get_abstract_dist_for(req_to_install)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/resolve.py", line 242, in _get_abstract_dist_for
    self.require_hashes
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/operations/prepare.py", line 256, in prepare_linked_requirement
    path = url_to_path(req.link.url)
  File "/private/tmp/repro-pip-egg/env3/lib/python3.7/site-packages/pip/_internal/download.py", line 521, in url_to_path
    % url
ValueError: non-local file URIs are not supported on this platform: 'file://./bar'

ํŽธ์ง‘ํ•˜๋‹ค:

RFC 3986 ์ด file: ์Šคํ‚ค๋งˆ์—์„œ ์ƒ๋Œ€ ๊ฒฝ๋กœ URI๊ฐ€ ํ—ˆ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ์ง€์ •ํ•˜๋ฏ€๋กœ ๊ธฐ์ˆ ์ ์œผ๋กœ six ๋Š” file:./foo/bar ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๊ธฐ์ˆ ์ ์œผ๋กœ๋Š” setup.py์—์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

PKG_DIR = os.path.dirname(os.path.abspath(__file__))
install_requires = [
    f"name @ file://{PKG_DIR}/foo/bar"
]

๊ทธ๋Ÿฌ๋‚˜ pip๋Š” /tmp ์—์„œ ํŒจํ‚ค์ง€์˜ "๊นจ๋—ํ•œ"๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฏ€๋กœ file:///tmp/pip-req-build-9u3z545j/foo/bar ์™€ ๊ฐ™์€ ๊ฒƒ์„ ์–ป์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์‹คํ–‰ํ•˜๋ฉด ๋‘ ๋ฒˆ์งธ ํ•จ์ˆ˜์˜ ์กฐ๊ฑด์„ ์ถฉ์กฑํ•ฉ๋‹ˆ๋‹ค.

>>> tryparse('file:///tmp/pip-req-build-9u3z545j/foo/bar')
file:///tmp/pip-req-build-9u3z545j/foo/bar
ParseResult(scheme='file', netloc='', path='/tmp/pip-req-build-9u3z545j/foo/bar', params='', query='', fragment='')
file:///tmp/pip-req-build-9u3z545j/foo/bar
ParseResult(scheme='file', netloc='', path='/tmp/pip-req-build-9u3z545j/foo/bar', params='', query='', fragment='')

๊ฑฐ๊ธฐ์— ๋ชจ๋“  ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. "unparse"๋Š” ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์‚ฐ์ถœํ•˜๊ณ  netloc ์š”๊ตฌ ์‚ฌํ•ญ์€ ์ฒซ ๋ฒˆ์งธ ํ•จ์ˆ˜์˜ ์กฐ๊ฑด์— ๋Œ€ํ•ด ์ถฉ์กฑ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋‘ ๋ฒˆ์งธ ํ•จ์ˆ˜์˜ ๋…ผ๋ฆฌ๊ฐ€ ๋งŒ์กฑ ๋˜๋”๋ผ๋„ ์—ฌ์ „ํžˆ Invalid URL ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

pip (๋˜๋Š” distutils ๋˜๋Š” setuptools ๋“ฑ)์ด ์ถœ๋ ฅ์„ ์‚ผํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ์ €๋Š” setup.py์—์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

import os
PKG_DIR = os.path.dirname(os.path.abspath(__file__))
assert False, os.system(f"find {PKG_DIR}")

์˜ˆ์ƒ๋Œ€๋กœ ๋ชจ๋“  ํŒŒ์ผ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋ฏ€๋กœ ํŒŒ์ผ์ด ๋ˆ„๋ฝ๋˜๊ฑฐ๋‚˜ ๋ฌด์–ธ๊ฐ€๊ฐ€ ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. "Invalid URL given" ๊ฐ€์žˆ๋Š” ์œ„์˜ ์ค„์€ ์ฝ”๋“œ๋ฒ ์ด์Šค์—์„œ ๋ฌธ์ž์—ด์ด ํ‘œ์‹œ๋˜๋Š” ์œ ์ผํ•œ ์œ„์น˜์ž…๋‹ˆ๋‹ค.

์ด ์‹œ์ ์—์„œ ๋‚˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

vendored dependency needs discussion bug

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ข‹์•„, ๋ฌธ์ œ๊ฐ€ ๋ณด์ธ๋‹ค. setuptools , pkg-resources ๋ฐ pip ๋ชจ๋‘ ์•ฝ๊ฐ„ ๋‹ค๋ฅธ ๋ฒ„์ „์˜ packaging ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

pip ์—์„œ๋Š” ์œ„์—์„œ ๋ณด์—ฌ ๋“œ๋ฆฐ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋‹ค๋ฅธ ๋ชจ๋“  ๊ฒƒ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค ( "์ƒˆ๋กœ์šด"ํ•ญ๋ชฉ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ๋‹ค์Œ ๋…ผ๋ฆฌ๋Š” ๋งค์šฐ ์ œํ•œ์ ์ด๋ฉฐ RFC 3986์— ๋”ฐ๋ผ ์™„์ „ํžˆ ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. file:/// ๊ฐ€ ํ—ˆ์šฉ๋˜์–ด์•ผํ•˜๋ฏ€๋กœ ๋น„์–ด์žˆ๋Š” netloc ) :

        if req.url:
            parsed_url = urlparse.urlparse(req.url)
            if not (parsed_url.scheme and parsed_url.netloc) or (
                    not parsed_url.scheme and not parsed_url.netloc):
                raise InvalidRequirement("Invalid URL given")

๐Ÿ™„

์ฆ‰, ๋‚ด ํŒŒ์ผ ๊ฒฝ๋กœ์— file:///foo/bar ์žˆ๊ณ  file://localhost/foo/bar ์•„๋‹ˆ๋ฏ€๋กœ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

์™„์ „ํ•œ ์†”๋ฃจ์…˜ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import os
from setuptools import setup

PKG_DIR = os.path.dirname(os.path.abspath(__file__))

setup(
    install_requires=[
        f'foo @ file://localhost{PKG_DIR}/foo/bar'
    ]
)

์ด๊ฒƒ์€ ๋ชจํ˜ธํ•˜๊ณ  ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•˜๋Š” ์˜ค๋ฅ˜์™€ ํ˜ผํ•ฉ ๋œ ๋งค์šฐ ๋‚˜์œ UX์ž…๋‹ˆ๋‹ค.

์ด ์ƒํ™ฉ์„ ์–ด๋–ป๊ฒŒ ๊ฐœ์„  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

๋ชจ๋“  7 ๋Œ“๊ธ€

์ข‹์•„, ๋ฌธ์ œ๊ฐ€ ๋ณด์ธ๋‹ค. setuptools , pkg-resources ๋ฐ pip ๋ชจ๋‘ ์•ฝ๊ฐ„ ๋‹ค๋ฅธ ๋ฒ„์ „์˜ packaging ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

pip ์—์„œ๋Š” ์œ„์—์„œ ๋ณด์—ฌ ๋“œ๋ฆฐ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋‹ค๋ฅธ ๋ชจ๋“  ๊ฒƒ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค ( "์ƒˆ๋กœ์šด"ํ•ญ๋ชฉ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ๋‹ค์Œ ๋…ผ๋ฆฌ๋Š” ๋งค์šฐ ์ œํ•œ์ ์ด๋ฉฐ RFC 3986์— ๋”ฐ๋ผ ์™„์ „ํžˆ ํ˜ธํ™˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. file:/// ๊ฐ€ ํ—ˆ์šฉ๋˜์–ด์•ผํ•˜๋ฏ€๋กœ ๋น„์–ด์žˆ๋Š” netloc ) :

        if req.url:
            parsed_url = urlparse.urlparse(req.url)
            if not (parsed_url.scheme and parsed_url.netloc) or (
                    not parsed_url.scheme and not parsed_url.netloc):
                raise InvalidRequirement("Invalid URL given")

๐Ÿ™„

์ฆ‰, ๋‚ด ํŒŒ์ผ ๊ฒฝ๋กœ์— file:///foo/bar ์žˆ๊ณ  file://localhost/foo/bar ์•„๋‹ˆ๋ฏ€๋กœ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

์™„์ „ํ•œ ์†”๋ฃจ์…˜ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import os
from setuptools import setup

PKG_DIR = os.path.dirname(os.path.abspath(__file__))

setup(
    install_requires=[
        f'foo @ file://localhost{PKG_DIR}/foo/bar'
    ]
)

์ด๊ฒƒ์€ ๋ชจํ˜ธํ•˜๊ณ  ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•˜๋Š” ์˜ค๋ฅ˜์™€ ํ˜ผํ•ฉ ๋œ ๋งค์šฐ ๋‚˜์œ UX์ž…๋‹ˆ๋‹ค.

์ด ์ƒํ™ฉ์„ ์–ด๋–ป๊ฒŒ ๊ฐœ์„  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@ Qix- ๋‹น์‹ ์ด ์ด๊ฒƒ์„ ๋ฐœ๊ฒฌํ•ด์„œ ๋‹คํ–‰์ž…๋‹ˆ๋‹ค! ๋‚˜๋Š” ๋ชจ๋“  ๋™์ผํ•œ ํ˜•์‹์„ ์‹œ๋„ํ•˜๋ฉด์„œ ๋ฒฝ์— ๋จธ๋ฆฌ๋ฅผ ์น˜๊ณ  ์žˆ์—ˆ๋‹ค. ์ด๊ฒƒ์€ https://github.com/pypa/pip/issues/6162์˜ ๋Œ€์ฒด ์˜ต์…˜์ด๋ฉฐ dependency_links์˜ ์‚ฌ์šฉ ์ค‘๋‹จ์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ๊ฐœ์ธ ์ €์žฅ์†Œ๋ฅผ ์„ค์ •ํ•˜๋ ค๊ณ ํ•˜๋Š”๋ฐ ์ž์ฒด ๋‚ด๋ถ€ ์„œ๋ฒ„๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ํ•ด๊ฒฐ์ฑ…์€ ํŒจํ‚ค์ง€๋ฅผ s3์— ๊ฒŒ์‹œ ํ•œ ๋‹ค์Œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ๋กœ์ปฌ ํด๋”์— ๋„ฃ์€ ๋‹ค์Œ install_requires ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋กœ์ปฌ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๋Š” ์ง๊ด€์  ์ธ ๋ฐฉ๋ฒ•์˜ ์ด์ ์„ ์–ป์„ ์ˆ˜์žˆ๋Š” ๋‹ค๋ฅธ ๋งŽ์€ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์žˆ๋‹ค๊ณ  ํ™•์‹ ํ•ฉ๋‹ˆ๋‹ค.

@ryanaklein ์‹ค์ œ๋กœ git ํ•˜์œ„ ๋ชจ๋“ˆ์— ๋Œ€ํ•œ ์—ฐ๊ตฌ๋˜์ง€ ์•Š์€ ๋ชจ๋“  ๋ถ€์ •์„ฑ์„ ๋ฌด์‹œํ•˜๊ณ  ์‹œ๋„ํ•ด ๋ณผ ๊ฒƒ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค (Git๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •). ๊ทธ๊ฒƒ๋“ค์„ ๋ธŒ๋žœ์น˜๋กœ ์ƒ๊ฐํ•˜์ง€ ์•Š๊ณ  ํƒœ๊ทธ (๋˜๋Š” ๋ฆด๋ฆฌ์Šค)๋กœ ์ƒ๊ฐํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ์ •๋ง ์ž˜ ์ž‘๋™ํ•˜๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ๋“ค์€ C / C ++ ์„ธ๊ณ„์—์„œ ๋งค์šฐ ์ž์ฃผ ์‚ฌ์šฉ๋˜๋ฉฐ, ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ๋“ค์„ ๊ฝค ์„ฑ๊ณต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ด์ฌ ํŒจํ‚ค์ง€๋ฅผ ๊ณต๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค (๋ฌผ๋ก  ์œ„์˜ ๋ฒ„๊ทธ๋ฅผ ์ œ์™ธํ•˜๊ณ !).

S3์˜ ๋„คํŠธ์›Œํฌ / $$ ๋น„์šฉ์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. :)

์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™
name @ ./some/path (๋˜๋Š” ์†”์งํžˆ ./some/path )๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ์ฝ”๋“œ๋ฒ ์ด์Šค์— ๋กœ์ปฌ๋กœ ๊ณต๊ธ‰๋˜๋Š” ํŒจํ‚ค์ง€๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์ง์ ‘ URL ์ฐธ์กฐ ( name @ ./some/path )์˜ ๊ฒฝ์šฐ ์ž‘์—…์ด ์ง„ํ–‰๋˜๋Š” ๋‘ ๊ณณ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์œ ํšจํ•œ PEP 508 ์ง์ ‘ URL ์ฐธ์กฐ ๊ฑฐ๋ถ€๋ฅผ ์ถ”์ ํ•˜๋Š” pypa / packaging # 120
  2. pip ์ธก๋ฉด์—์„œ ์šฐ๋ฆฌ๋Š” ๋ฌด์–ธ๊ฐ€์— ์ƒ๋Œ€์ ์ธ ๊ฒฝ๋กœ๋ฅผ ํ•ด์„ ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ์—ฌ๊ธฐ์„œ ๊ฐ€์žฅ ์˜๋ฏธ์žˆ๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์–ป๊ธฐ ์œ„ํ•ด ํ† ๋ก ์„ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ ๋˜๋Š” ์ƒˆ๋กœ์šด ์ „์šฉ ๋ฌธ์ œ์—์„œ ์ตœ์ข… ์กฐ์น˜๋ฅผ ์ถ”์  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ›„์ž๋Š” PEP 508์— ๋”ฐ๋ผ ํ—ˆ์šฉ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ชจ๋“  ๋„๊ตฌ์—์„œ ์ž‘๋™ํ•˜๋„๋ก ์ง€์›ํ•˜๋Š” ๊ฒƒ์„ ์ •๋‹นํ™”ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋ชจํ˜ธํ•˜๊ณ  ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•˜๋Š” ์˜ค๋ฅ˜์™€ ํ˜ผํ•ฉ ๋œ ๋งค์šฐ ๋‚˜์œ UX์ž…๋‹ˆ๋‹ค.
์ด ์ƒํ™ฉ์„ ์–ด๋–ป๊ฒŒ ๊ฐœ์„  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

5204๋Š” ํ• ๊ด€์ ์—์„œ์ด ์ผ๋ฐ˜์ ์ธ ์งˆ๋ฌธ์— ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฐ! ์ฒซ ๋ฒˆ์งธ ๊ตฌ๋ฌธ ๋ถ„์„ ํ˜ธ์ถœ์˜ ๊ฒฐ๊ณผ๋ฅผ "unparse"ํ•  ๋•Œ ๊ฒฝ๋กœ๊ฐ€ ์ ˆ๋Œ€ file : /// ...์ด๋ฉ๋‹ˆ๋‹ค.

๋ฌธ์ œ 22852 ์—์„œ ๋ฐœ์ƒํ•œ CPython ๋ฒ„๊ทธ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฒ„๊ทธ๋Š” ๋ฌธ์ œ # 3783์„ ์œ ๋ฐœํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.์ด ์ฃผ์„์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ด ๋ฌธ์ œ์˜ ์ƒํƒœ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ์˜ˆ๋ฅผ ๋“ค์–ด ๋ชจ ๋†€๋ฆฌ ์‹ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ ๋งฅ๋ฝ์—์„œ PyPI์—์—†๋Š” ๋กœ์ปฌ ์ข…์†์„ฑ์„ ํ•ด๊ฒฐํ•˜๊ธฐ์œ„ํ•œ ์†”๋ฃจ์…˜์ด ๊ธด๊ธ‰ํžˆ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

npm์€์ด ๊ธฐ๋Šฅ์„ ์œ ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ์œผ๋ฉฐ dependencies ๋Š” ๋กœ์ปฌ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ package.json ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์•”์‹œ๋˜๊ธฐ ์ „์— ์–ด๋–ค ์ผ์ด ์ผ์–ด๋‚˜์•ผํ•˜๋Š”์ง€ ์•Œ์•„ ๋ณด๋ ค๋ฉด

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰