<p>рдкрд╛рдЗрдк рд╕реАрдзреЗ URL рд╕рдВрджрд░реНрднреЛрдВ рдореЗрдВ рд╕рд╛рдкреЗрдХреНрд╖ рдкрдереЛрдВ рдХрд╛ рд╕рдорд░реНрдерди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ</p>

рдХреЛ рдирд┐рд░реНрдорд┐рдд 28 рдЬреВрди 2019  ┬╖  7рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: pypa/pip

рд╡рд╛рддрд╛рд╡рд░рдг

  • рдкрд╛рдЗрдк рд╕рдВрд╕реНрдХрд░рдг: 19.1.1
  • рдкрд╛рдпрдерди рд╕рдВрд╕реНрдХрд░рдг: 3.7
  • OS: MacOS Mojave
рдпрд╣рд╛рдВ рдЕрдкрдиреЗ рдкрд░реНрдпрд╛рд╡рд░рдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рдЬрд╛рдирдХрд╛рд░реА рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рд╕реНрд╡рддрдВрддреНрд░ рдорд╣рд╕реВрд╕ рдХрд░реЗрдВ

рд╡рд┐рд╡рд░рдг
рдореБрдЭреЗ рдпрдХреАрди рдирд╣реАрдВ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ six рдмрдЧ рдпрд╛ рдкрд┐рдк рдмрдЧ рд╣реИред рдХреНрд╖рдорд╛ рдХрд░реЗрдВ рдпрджрд┐ рдпрд╣ six ред

рдкрд┐рдк рдореЗрдВ рд╕реНрдерд╛рдиреАрдп рдкрде рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реБрдЖ рдкреНрд░рддреАрдд install_requires рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ name @ ./some/path , рд▓реЗрдХрд┐рди рдпреВрдЖрд░рдПрд▓ рдкрд╛рд░реНрд╕ рдмрд╣реБрдд рдЯреВрдЯ рдЧрдпрд╛ рд╣реИред

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

рдЗрд╕ рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ, рдпрд╣ рдЖрдиреЗ рд╡рд╛рд▓реЗ URL рдХреЗ рд╡реНрдпрдХреНрддрд┐рдЧрдд рдШрдЯрдХреЛрдВ рдХреЛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП urlsplit рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред

рдпрд╣рд╛рдБ рд╡рд╣ рд╣реИ рдЬреЛ рдЗрдирдкреБрдЯ рдХреЗ рдХреБрдЫ рдЯреБрдХрдбрд╝реЛрдВ рдХреЗ рд╕рд╛рде рджрд┐рдЦрддрд╛ рд╣реИ:

./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 рдлреЙрд░реНрдо рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдирд┐рд░реНрджреЗрд╢ рджреЗрдХрд░ рдкрд┐рдк рдореЗрдВ рд╢рдорди рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред

рддрд░реНрдХ рдХреЗ рдЗрди рджреЛ рд╡рд┐рд░реЛрдзрд╛рднрд╛рд╕реА рдЯреБрдХрдбрд╝реЛрдВ рдХреЛ рджреЗрдЦрддреЗ рд╣реБрдП, install_requires рдХреБрдВрдЬреА рдореЗрдВ distutils рдпрд╛ setuptools рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдореЗрдВ рд╕реНрдерд╛рдиреАрдп рдкрдереЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЕрд╕рдВрднрд╡ рд╣реИред

рдЕрдкреЗрдХреНрд╖рд┐рддреН рд╡реНрдпрд╡рд╣рд╛рд░
рдореБрдЭреЗ рдЕрдкрдиреЗ рдХреЛрдбрдмреЗрд╕ рдореЗрдВ рд╕реНрдерд╛рдиреАрдп рд╢реНрд░реЗрдгреА рдХреЗ рдкреИрдХреЗрдЬ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП 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 рдпрд╣ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рддрд╛ рд╣реИ рдХрд┐ рд╕рд╛рдкреЗрдХреНрд╖ рдкрде URI рдХреЛ file: рдпреЛрдЬрдирд╛ рдХреЗ рд╕рд╛рде рдЕрдиреБрдорддрд┐ рдирд╣реАрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП рддрдХрдиреАрдХреА рд░реВрдк рд╕реЗ six file:./foo/bar рдкрд░ рдЧрд▓рдд рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред

рд╣рд╛рд▓рд╛рдБрдХрд┐, рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐, рддрдХрдиреАрдХреА рд░реВрдк рд╕реЗ, рдореБрдЭреЗ рдЕрдкрдиреЗ setup.py рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП:

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

рд╣рд╛рд▓рд╛рдВрдХрд┐, рдкрд╛рдЗрдк /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='')

рд╡рд╣рд╛рдВ рд╕рдм рдХреБрдЫ рдЕрдЪреНрдЫрд╛ рд╣реИред "рдЕрдирдкреЗрдХреНрд╖рд┐рдд" рдПрдХ рд╣реА рдкрд░рд┐рдгрд╛рдо рджреЗрддрд╛ рд╣реИ, рдФрд░ netloc рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдХреЛ рдкрд╣рд▓реЗ рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд╕рд╢рд░реНрдд рдХреЗ рд▓рд┐рдП рдкреВрд░рд╛ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рд╣рд╛рд▓рд╛рдБрдХрд┐, рд╣рдо рдЕрднреА рднреА Invalid URL рддреНрд░реБрдЯрд┐ рдХреЗ рд╕рд╛рде рдорд┐рд▓реЗ рд╣реИрдВ, рднрд▓реЗ рд╣реА рджреВрд╕рд░реЗ рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рддрд░реНрдХ рд╕рдВрддреБрд╖реНрдЯ рд╣реЛред

рдЪреВрдВрдХрд┐ pip (рдпрд╛ рдбрд┐рд╕реНрдЯрд▓реНрдпреВрдЯ рдпрд╛ рд╕реЗрдЯрдкреВрд▓ рдпрд╛ рдЬреЛ рднреА) рдЖрдЙрдЯрдкреБрдЯ рдирд┐рдЧрд▓рддрд╛ рд╣реИ, рдореИрдВ рдЖрдЧреЗ рдмрдврд╝рд╛ рдФрд░ рдЕрдкрдиреЗ 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 рдФрд░ рдбрд┐рдкреЗрдВрдбреЗрдВрд╕реА_рд▓рд┐рдВрдХ рдХрд╛ рдбрд┐рдкреНрд░реЗрд╢рди рд╣реИред

рд╣рдо рдПрдХ рдирд┐рдЬреА рд░реЗрдкреЛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд░рд╣реЗ рд╣реИрдВ рдФрд░ рд╣рдорд╛рд░рд╛ рдЕрдкрдирд╛ рдЖрдВрддрд░рд┐рдХ рд╕рд░реНрд╡рд░ рдирд╣реАрдВ рд╣реИред рд╣рдорд╛рд░рд╛ рд╕рдорд╛рдзрд╛рди s3 рдХреЗ рд▓рд┐рдП рдкреИрдХреЗрдЬ рдкреНрд░рдХрд╛рд╢рд┐рдд рдХрд░рдирд╛ рд╣реИ рдФрд░ рдлрд┐рд░ рдЙрдиреНрд╣реЗрдВ рдЙрдкрднреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдо рдЙрдиреНрд╣реЗрдВ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рддреЗ рд╣реИрдВ, рдЙрдиреНрд╣реЗрдВ рдПрдХ рд╕реНрдерд╛рдиреАрдп рдлрд╝реЛрд▓реНрдбрд░ рдореЗрдВ рдбрд╛рд▓рддреЗ рд╣реИрдВ, рдлрд┐рд░ рдЙрдиреНрд╣реЗрдВ install_requires рдЬреЛрдбрд╝рддреЗ рд╣реИрдВред

рдореБрдЭреЗ рдпрдХреАрди рд╣реИ рдХрд┐ рдХрдИ рдЕрдиреНрдп рдЙрдкрдпреЛрдЧ рдХреЗ рдорд╛рдорд▓реЗ рд╣реИрдВ рдЬреЛ рд╕реНрдерд╛рдиреАрдп рдкреИрдХреЗрдЬреЛрдВ рдХреЛ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд╣рдЬ рддрд░реАрдХреЗ рд╕реЗ рд▓рд╛рднрд╛рдиреНрд╡рд┐рдд рд╣реЛрдВрдЧреЗред

@ryanaklein рдореИрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рджреВрдВрдЧрд╛ рдФрд░ рдЙрдиреНрд╣реЗрдВ

S3 рдХреЗ рдиреЗрдЯрд╡рд░реНрдХ / $ $ рд▓рд╛рдЧрдд рдореЗрдВ рдХрдЯреМрддреА рд╣реЛ рд╕рдХрддреА рд╣реИ :)

рдЕрдкреЗрдХреНрд╖рд┐рддреН рд╡реНрдпрд╡рд╣рд╛рд░
рдореБрдЭреЗ рдЕрдкрдиреЗ рдХреЛрдбрдмреЗрд╕ рдкрд░ рд╕реНрдерд╛рдиреАрдп рд╢реНрд░реЗрдгреА рдХреЗ рдкреИрдХреЗрдЬ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП name @ ./some/path (рдпрд╛, рдИрдорд╛рдирджрд╛рд░реА рд╕реЗ, рдХреЗрд╡рд▓ ./some/path ) рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред

рдкреНрд░рддреНрдпрдХреНрд╖ URL рд╕рдВрджрд░реНрдн рдХреЗ рд▓рд┐рдП ( name @ ./some/path ) рджреЛ рд╕реНрдерд╛рди рд╣реИрдВ рдЬрд╣рд╛рдВ рдХрд╛рдо рд╣реЛ рд░рд╣рд╛ рд╣реИ:

  1. pypa / рдкреИрдХреЗрдЬрд┐рдВрдЧ # 120 рдЬреЛ рд╡реИрдз PEP 508 рдкреНрд░рддреНрдпрдХреНрд╖ url рд╕рдВрджрд░реНрднреЛрдВ рдХреЛ рдЕрд╕реНрд╡реАрдХрд╛рд░ рдХрд░рддрд╛ рд╣реИ
  2. рдкрд┐рдк рдУрд░ рд╣рдо рдХреБрдЫ рдХреЗ рд╕рд╛рдкреЗрдХреНрд╖ рд╕рдВрдмрдВрдзрд┐рдд рдкрде рдХреА рд╡реНрдпрд╛рдЦреНрдпрд╛ рдХрд░рдиреЗ рдХреА рдЬрд░реВрд░рдд рд╣реИ рдкрд░, рдореИрдВ рдЪрд░реНрдЪрд╛ рд╢реБрд░реВ рдпрд╣рд╛рдБ рдХреНрдпрд╛ рд╕рдмрд╕реЗ рд╕рдордЭ рдореЗрдВ рдЖрддрд╛ рд╣реИ рдкрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдПред рд╣рдо рдпрд╣рд╛рдВ рдпрд╛ рдПрдХ рдирдП рд╕рдорд░реНрдкрд┐рдд рдореБрджреНрджреЗ рдореЗрдВ рдЕрдВрддрд┐рдо рдХрд╛рд░реНрд░рд╡рд╛рдИ рдХреЛ рдЯреНрд░реИрдХ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

рдмрд╛рдж рд╡рд╛рд▓реЗ рдкреАрдИрдкреА 508 рдХреЗ рдЕрдиреБрд╕рд╛рд░ рд╕реНрд╡реАрдХрд╛рд░реНрдп рдирд╣реАрдВ рд╣реЛрдВрдЧреЗ, рдЗрд╕рд▓рд┐рдП рд╕рднреА рдЙрдкрдХрд░рдгреЛрдВ рдкрд░ рдХрд╛рдо рдХрд░рдирд╛ рдХрдо рд╕рдорд░реНрдерди рдХрд╛ рдФрдЪрд┐рддреНрдп рд╕рд╛рдмрд┐рдд рдХрд░рдирд╛ рдХрдард┐рди рд╣реЛрдЧрд╛ред

рдпрд╣ рдЕрд╕реНрдкрд╖реНрдЯ рдФрд░ рд╕рдордп рдмрд░реНрдмрд╛рдж рдХрд░рдиреЗ рд╡рд╛рд▓реА рддреНрд░реБрдЯрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдмрд╣реБрдд рдмреБрд░рд╛ UX рдорд┐рд╢реНрд░рд┐рдд рд╣реИред
рд╣рдо рдЗрд╕ рд╕реНрдерд┐рддрд┐ рдХреЛ рдХреИрд╕реЗ рд╕реБрдзрд╛рд░ рд╕рдХрддреЗ рд╣реИрдВ?

5204 рдХреЛ рдкрд╛рдЗрдк рд▓рд╛рдЗрди рдХреЗ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕реЗ рдЗрд╕ рд╕рд╛рдорд╛рдиреНрдп рдкреНрд░рд╢реНрди рдореЗрдВ рдорджрдж рдХрд░рдиреА рдЪрд╛рд╣рд┐рдПред

рдКрдкреНрд╕! рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐, рдЬрдм рд╣рдо рдкрд╣рд▓реЗ рдкрд╛рд░реНрд╕ рдХреЙрд▓ рд╕реЗ "рдЕрдирдкреЗрдХреНрд╖рд┐рдд" рдкрд░рд┐рдгрд╛рдо рджреЗрддреЗ рд╣реИрдВ, рддреЛ рд╣рдорд╛рд░рд╛ рдкрде рдкреВрд░реНрдг рдлрд╝рд╛рдЗрд▓ рдмрди рдЬрд╛рддрд╛ рд╣реИ: /// ...

рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ 22852 рдЕрдВрдХ рдореЗрдВ рдЙрдард╛рдП рдЧрдП - "urllib.parse рдЧрд▓рдд рддрд░реАрдХреЗ рд╕реЗ рдЦрд╛рд▓реА #fragment рд╕реНрдЯреНрд░рд┐рдкреНрд╕? рдХреНрд╡реЗрд░реА; // netloc"

рдпрд╣ рдмрдЧ # 3783 рднреА рдЬрд╛рд░реА рдХрд░рддрд╛ рд╣реИ - рдЗрд╕ рдЯрд┐рдкреНрдкрдгреА рдХреЛ рджреЗрдЦреЗрдВред

рдЗрд╕ рдореБрджреНрджреЗ рдХреА рд╕реНрдерд┐рддрд┐ рдХреНрдпрд╛ рд╣реИ? рд╕реНрдерд╛рдиреАрдп рдирд┐рд░реНрднрд░рддрд╛ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдорд╛рдзрд╛рди рдЬреЛ PyPI рдкрд░ рдирд╣реАрдВ рд╣реИ, рдХреА рддрддреНрдХрд╛рд▓ рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рдЕрдЦрдВрдб рд╕рдВрд╡рд╣рдиреА рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВред

рдзреНрдпрд╛рди рджреЗрдВ рдХрд┐ npm рдиреЗ рдПрдХ рд╕рдорд╛рди рддрд░реАрдХреЗ рд╕реЗ рдЗрд╕ рд╕реБрд╡рд┐рдзрд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рд╣реИ рдФрд░ dependencies рд╕реНрдерд╛рдиреАрдп рдкрде рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ package.json рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ред

рдХреГрдкрдпрд╛ рдпрд╣ рдЬрд╛рдирдиреЗ рдХреЗ рд▓рд┐рдП

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

wrouesnel picture wrouesnel  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

kkom picture kkom  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

pradyunsg picture pradyunsg  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

lululukas picture lululukas  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

dstufft picture dstufft  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ