Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal not enough values to unpack error for pex3 lock create 'pip @ https://github.com/pypa/pip/archive/22.0.2.zip' ... #2057

Closed
huonw opened this issue Feb 15, 2023 · 7 comments · Fixed by #2060
Assignees

Comments

@huonw
Copy link
Collaborator

huonw commented Feb 15, 2023

I'm trying to create a lock file with a requirement like pip @ https://github.com/pypa/pip/archive/22.0.2.zip. Pex fails with a very opaque error from an uncaught ValueError: not enough values to unpack.

Potentially I'm doing something that's not supported. If so, I'd be hoping for a more explanatory error message than the raw exception.

Workaround

For a Github archive URL like that, it seems like git requirement like pip @ git+https://github.com/pypa/pip@22.0.2 works instead.

Reproducer

pex3 --version # 2.1.122
PEX_VERBOSE=1 pex3 -v lock create 'pip @ https://github.com/pypa/pip/archive/22.0.2.zip' --output=lock.json

(That requirement spec is taken from https://pip.pypa.io/en/stable/reference/requirement-specifiers/#examples, and indeed pip install 'pip @ https://github.com/pypa/pip/archive/22.0.2.zip' seems to work.)

Output (running under 3.9, on macOS):

pex: Resolving for:
pex: Hashing pex: 82.0ms                                              
pex: Isolating pex: 0.0ms
Traceback (most recent call last):
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/result.py", line 105, in catch
    return func(*args, **kwargs)
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/cli/command.py", line 84, in run
    return subcommand_func(self)
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/cli/commands/lock.py", line 448, in _create
    create(
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/resolve/lockfile/create.py", line 378, in create
    downloaded = resolver.download(
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/resolver.py", line 1232, in download
    build_requests, download_results = _download_internal(
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/resolver.py", line 1117, in _download_internal
    download_results = download_request.download_distributions(
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/resolver.py", line 111, in download_distributions
    return list(
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/jobs.py", line 586, in execute_parallel
    yield spawn_result.spawned_job.await_result()
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/jobs.py", line 222, in await_result
    job.wait()
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/jobs.py", line 81, in wait
    self._check_returncode(stderr)
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/pip/log_analyzer.py", line 104, in _check_returncode
    result = analyzer.analyze(line)
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/resolve/locker.py", line 412, in analyze
    project_name_and_version, partial_artifact = self._extract_resolve_data(url)
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/resolve/locker.py", line 264, in _extract_resolve_data
    ProjectNameAndVersion.from_filename(unquote(urlparse.urlparse(url).path))
  File "/Users/huon/.pyenv/versions/3.9.10/lib/python3.9/site-packages/pex/dist_metadata.py", line 294, in from_filename
    project_name, version = fname.rsplit("-", 1)
ValueError: not enough values to unpack (expected 2, got 1)
not enough values to unpack (expected 2, got 1)
  • The default output (without PEX_VERBOSE) is just the exception message not enough values to unpack (expected 2, got 1).
  • Running under other Python versions (e.g. 3.10) and other systems (e.g. a Linux docker image) gives similar output.
This was referenced Feb 15, 2023
@jsirois jsirois self-assigned this Feb 15, 2023
@jsirois
Copy link
Member

jsirois commented Feb 15, 2023

A tighter / smaller case for the IT:

pex works fine:

$ pex "cowsay @ https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip" -c cowsay -- 'Moo!'
  ____
| Moo! |
  ====
    \
     \
       ^__^
       (oo)\_______
       (__)\       )\/\
           ||----w |
           ||     ||

pex3 lock create does not:

$ pex3 lock create "cowsay @ https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip"
not enough values to unpack (expected 2, got 1)

@jsirois
Copy link
Member

jsirois commented Feb 15, 2023

Ok, the lock code assumes any URL with a path not ending in ".whl" must be an sdist. That is not correct and this case demonstrates that. A 3rd possibility is the path portion could point to any old archive of the source and not necessarily a proper sdist.

The Pip download log reveals:

2023-02-15T09:46:48,574 Collecting cowsay@ https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip
2023-02-15T09:46:48,574   Created temporary directory: /home/jsirois/.pex/pip_cache/.tmp/pip-unpack-4mgnc0jy
2023-02-15T09:46:48,575   Found credentials in netrc for github.com
2023-02-15T09:46:48,576   Looking up "https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip" in the cache
2023-02-15T09:46:48,576   No cache entry available
2023-02-15T09:46:48,576   Starting new HTTPS connection (1): github.com:443
2023-02-15T09:46:49,037   https://github.com:443 "GET /VaasuDevanS/cowsay-python/archive/v5.0.zip HTTP/1.1" 302 None
2023-02-15T09:46:49,039   Status code 302 not in (200, 203, 300, 301)
2023-02-15T09:46:49,041   Looking up "https://codeload.github.com/VaasuDevanS/cowsay-python/zip/refs/tags/v5.0" in the cache
2023-02-15T09:46:49,042   Current age based on date: 192
2023-02-15T09:46:49,042   Starting new HTTPS connection (1): codeload.github.com:443
2023-02-15T09:46:49,459   https://codeload.github.com:443 "GET /VaasuDevanS/cowsay-python/zip/refs/tags/v5.0 HTTP/1.1" 304 0
2023-02-15T09:46:49,468   Using cached https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip
2023-02-15T09:46:49,473   Added cowsay@ https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip from https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip to build tracker '/home/jsirois/.pex/pip_cache/.tmp/pip-req-tracker-g_xajh45'
2023-02-15T09:46:49,473     Running setup.py (path:/home/jsirois/.pex/pip_cache/.tmp/pip-download-2o83ui_r/cowsay/setup.py) egg_info for package cowsay
2023-02-15T09:46:49,473     Created temporary directory: /home/jsirois/.pex/pip_cache/.tmp/pip-pip-egg-info-gwnqxql1
2023-02-15T09:46:49,473     Running command python setup.py egg_info
2023-02-15T09:46:49,531     running egg_info
2023-02-15T09:46:49,531     creating /home/jsirois/.pex/pip_cache/.tmp/pip-pip-egg-info-gwnqxql1/cowsay.egg-info
2023-02-15T09:46:49,531     writing /home/jsirois/.pex/pip_cache/.tmp/pip-pip-egg-info-gwnqxql1/cowsay.egg-info/PKG-INFO
2023-02-15T09:46:49,531     writing dependency_links to /home/jsirois/.pex/pip_cache/.tmp/pip-pip-egg-info-gwnqxql1/cowsay.egg-info/dependency_links.txt
2023-02-15T09:46:49,531     writing entry points to /home/jsirois/.pex/pip_cache/.tmp/pip-pip-egg-info-gwnqxql1/cowsay.egg-info/entry_points.txt
2023-02-15T09:46:49,531     writing top-level names to /home/jsirois/.pex/pip_cache/.tmp/pip-pip-egg-info-gwnqxql1/cowsay.egg-info/top_level.txt
2023-02-15T09:46:49,531     writing manifest file '/home/jsirois/.pex/pip_cache/.tmp/pip-pip-egg-info-gwnqxql1/cowsay.egg-info/SOURCES.txt'
2023-02-15T09:46:49,550     reading manifest file '/home/jsirois/.pex/pip_cache/.tmp/pip-pip-egg-info-gwnqxql1/cowsay.egg-info/SOURCES.txt'
2023-02-15T09:46:49,550     reading manifest template 'MANIFEST.in'
2023-02-15T09:46:49,550     writing manifest file '/home/jsirois/.pex/pip_cache/.tmp/pip-pip-egg-info-gwnqxql1/cowsay.egg-info/SOURCES.txt'
2023-02-15T09:46:49,558   Source in /home/jsirois/.pex/pip_cache/.tmp/pip-download-2o83ui_r/cowsay has version 5.0, which satisfies requirement cowsay@ https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip from https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip
2023-02-15T09:46:49,558   Removed cowsay@ https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip from https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip from build tracker '/home/jsirois/.pex/pip_cache/.tmp/pip-req-tracker-g_xajh45'
2023-02-15T09:46:49,559 Saved /tmp/tmp3oj5_pna/home.jsirois.bin.pex.venv.bin.python/v5.0.zip

So there is enough information to robustly scrape the version info here to avoid a re-build of the archive to find out its version without adding special-case GitHub logic, namely:

2023-02-15T09:46:49,558   Source in /home/jsirois/.pex/pip_cache/.tmp/pip-download-2o83ui_r/cowsay has version 5.0, which satisfies requirement cowsay@ https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip from https://github.com/VaasuDevanS/cowsay-python/archive/v5.0.zip

@jsirois
Copy link
Member

jsirois commented Feb 16, 2023

And a 4th possibility is the URL is a local file:// URL pointing to a project source directory:

Creating a PEX works fine:

$ git clone https://github.com/jonathaneunice/colors/ /tmp/colors
Cloning into '/tmp/colors'...
remote: Enumerating objects: 165, done.
remote: Total 165 (delta 0), reused 0 (delta 0), pack-reused 165
Receiving objects: 100% (165/165), 33.77 KiB | 426.00 KiB/s, done.
Resolving deltas: 100% (80/80), done.
$ pex "ansicolors @ file:///tmp/colors" -- -c 'import colors; print(colors.green("OK"))'
OK

Creating a lock does not:

$ pex3 lock create "ansicolors @ file:///tmp/colors"
The distribution at path '/tmp/colors' does not have a file name matching known sdist or wheel file name formats.
$ pex3 lock create -v "ansicolors @ file:///tmp/colors"
pex: Resolving for:
pex: Hashing pex: 16.2ms
pex: Isolating pex: 0.0ms
Traceback (most recent call last):
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/result.py", line 105, in catch
    return func(*args, **kwargs)
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/cli/command.py", line 84, in run
    return subcommand_func(self)
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/cli/commands/lock.py", line 448, in _create
    create(
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/resolve/lockfile/create.py", line 378, in create
    downloaded = resolver.download(
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/resolver.py", line 1232, in download
    build_requests, download_results = _download_internal(
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/resolver.py", line 1117, in _download_internal
    download_results = download_request.download_distributions(
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/resolver.py", line 111, in download_distributions
    return list(
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/jobs.py", line 586, in execute_parallel
    yield spawn_result.spawned_job.await_result()
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/jobs.py", line 222, in await_result
    job.wait()
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/jobs.py", line 81, in wait
    self._check_returncode(stderr)
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/pip/log_analyzer.py", line 104, in _check_returncode
    result = analyzer.analyze(line)
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/resolve/locker.py", line 412, in analyze
    project_name_and_version, partial_artifact = self._extract_resolve_data(url)
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/resolve/locker.py", line 264, in _extract_resolve_data
    ProjectNameAndVersion.from_filename(unquote(urlparse.urlparse(url).path))
  File "/home/jsirois/bin/pex.venv/lib/python3.10/site-packages/pex/dist_metadata.py", line 297, in from_filename
    raise UnrecognizedDistributionFormat(
pex.dist_metadata.UnrecognizedDistributionFormat: The distribution at path '/tmp/colors' does not have a file name matching known sdist or wheel file name formats.
The distribution at path '/tmp/colors' does not have a file name matching known sdist or wheel file name formats.

@jsirois
Copy link
Member

jsirois commented Feb 16, 2023

Noting on the above, the specific issue is the direct reference URL format. This works fine:

$ pex3 lock create /tmp/colors --indent 2 -o lock.json 2>/dev/null

Which nets:

{
  "allow_builds": true,
  "allow_prereleases": false,
  "allow_wheels": true,
  "build_isolation": true,
  "constraints": [],
  "locked_resolves": [
    {
      "locked_requirements": [
        {
          "artifacts": [
            {
              "algorithm": "sha256",
              "hash": "fca533d24ea5fc1b0fc8bc10ee146535bddda1c40c85510544c5766cef4d10b6",
              "url": "file:///tmp/colors"
            }
          ],
          "project_name": "ansicolors",
          "requires_dists": [],
          "requires_python": null,
          "version": "1.1.8"
        }
      ],
      "platform_tag": [
        "cp310",
        "cp310",
        "manylinux_2_35_x86_64"
      ]
    }
  ],
  "path_mappings": {},
  "pex_version": "2.1.122",
  "pip_version": "20.3.4-patched",
  "prefer_older_binary": false,
  "requirements": [
    "ansicolors==1.1.8"
  ],
  "requires_python": [],
  "resolver_version": "pip-legacy-resolver",
  "style": "strict",
  "target_systems": [],
  "transitive": true,
  "use_pep517": null
}

And that can be used to build and run a PEX:

$ pex --lock lock.json -- -c 'import colors; print(colors.__file__)' 2>/dev/null
/home/jsirois/.pex/installed_wheels/93af7ea3da5ba7bc052ab3838e8e22c476fe06da42498e5be1f8e75dfffeac9a/ansicolors-1.1.8-py3-none-any.whl/colors/__init__.py

jsirois added a commit to jsirois/pex that referenced this issue Feb 17, 2023
Previously, it was assumed that direct reference URLs were either VCS
URLs or else wheel or sdist URLs. This neglected two remaining cases,
both of which failed to lock with better or worse error messages. The
two missed cases were:
1. URLs of source archives not conforming to the sdist quasi-standard
   naming convention of `<project name>-<version>.{.tar.gz,.zip}`.
2. Local file:// URLs pointing at project directories.

A notable case of the 1st are project archives provided by GitHub.
A notable need for the 2nd case comes from Pants where Pip proprietary
requirement strings are not handled (e.g.: `/path/to/project`) and a
direct reference URL must be used instead (e.g.: `projectname @
/path/to/project`).

Fixes pex-tool#2057
@jsirois
Copy link
Member

jsirois commented Feb 17, 2023

Alright, both of these cases are now covered in #2060.

jsirois added a commit to jsirois/pex that referenced this issue Feb 23, 2023
Previously, it was assumed that direct reference URLs were either VCS
URLs or else wheel or sdist URLs. This neglected two remaining cases,
both of which failed to lock with better or worse error messages. The
two missed cases were:
1. URLs of source archives not conforming to the sdist quasi-standard
   naming convention of `<project name>-<version>.{.tar.gz,.zip}`.
2. Local file:// URLs pointing at project directories.

A notable case of the 1st are project archives provided by GitHub.
A notable need for the 2nd case comes from Pants where Pip proprietary
requirement strings are not handled (e.g.: `/path/to/project`) and a
direct reference URL must be used instead (e.g.: `projectname @
/path/to/project`).

Fixes pex-tool#2057
jsirois added a commit that referenced this issue Feb 23, 2023
Previously, it was assumed that direct reference URLs were either VCS
URLs or else wheel or sdist URLs. This neglected two remaining cases,
both of which failed to lock with better or worse error messages. The
two missed cases were:
1. URLs of source archives not conforming to the sdist quasi-standard
   naming convention of `<project name>-<version>.{.tar.gz,.zip}`.
2. Local `file://` URLs pointing at project directories.

A notable case of the 1st are project archives provided by GitHub.
A notable need for the 2nd case comes from Pants where Pip proprietary
requirement strings are not handled (e.g.: `/path/to/project`) and a
direct reference URL must be used instead (e.g.: `projectname @
file:///path/to/project`).

Fixes #2057
@huonw
Copy link
Collaborator Author

huonw commented Feb 23, 2023

Thanks for the fix! I've tested 2.1.123, and indeed pex3 lock create now works with the specific URLs we were using internally that were causing issues (they're just some 'normal' GitHub archives, so that's unsurprising).

@jsirois
Copy link
Member

jsirois commented Feb 23, 2023

And thanks for the review. It took a good bit of work to be robust to ~random archive names and still stay performant for --style universal locks, but the fix is much better for you pointing out the hole.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants