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

Remove --upgrade from pip install #1136

Closed

Conversation

carlcsaposs-canonical
Copy link
Contributor

Fixes #1135

@sed-i
Copy link
Contributor

sed-i commented Jul 4, 2023

Not sure we should change priority between PYDEPS and reqs.txt.
See comment here: #1135

@carlcsaposs-canonical
Copy link
Contributor Author

Not sure we should change priority between PYDEPS and reqs.txt. See comment here: #1135

This doesn't change priority, it just fixes that unpinned PYDEPS will not override pinned requirements.txt deps

@lengau
Copy link
Collaborator

lengau commented Jul 5, 2023

I think this is reasonable, but I'll have to check for edge cases this might break. (@cmatsuoka can you think of any?)

If we make this change though, I'd like a behaviour test for this with a link to #1135 so we don't break this later.

@carlcsaposs-canonical
Copy link
Contributor Author

I'd like a behaviour test for this with a link to #1135 so we don't break this later.

I'm not sure how to implement this—let me know if you'd like me to try to add a behavior test

Here's a simple example:
requirements.txt ops==2.0.0
PYDEPS for charm lib ["ops"]
ensure that ops==2.0.0 is installed

@sed-i
Copy link
Contributor

sed-i commented Jul 5, 2023

Any idea how this should interact with version pinning specified in charm-binary-python-packages?

@lengau
Copy link
Collaborator

lengau commented Jul 5, 2023

@carlcsaposs-canonical A spread test something like this should do it: (Caveat: I haven't run this test)

summary: pack a charm with a specific (old) dependency version

include:
  - tests/

prepare: |
  tests.pkgs install unzip
  charmcraft init --project-dir=charm
  cd charm
  echo "ops==2.0.0" > requirements.txt

  cat <<- EOF >> charmcraft.yaml
  parts:
    charm:
      charm-requirements: [requirements.txt]
  EOF

  cat <<- EOF > lib/charms/charm/v0/my_lib.py
  PYDEPS = ["ops"]
  LIBID = "my_lib"
  LIBAPI = 0
  LIBPATCH = 1
  EOF


restore: |
  pushd charm
  charmcraft clean
  popd

  rm -rf charm

execute: |
  cd charm
  charmcraft pack --verbose
  test -f charm*.charm
  unzip -p charm_*.charm venv/ops/version.py | MATCH "version = '2.0.0'"

@lengau
Copy link
Collaborator

lengau commented Jul 5, 2023

@sed-i (I could be wrong but) that it'll install necessary updates, but not update anything not necessary for the PYDEPS.

One potential edge case here is if the first thing that requires a particular dependency is a charmlib. Take for example a requirements.txt file that says:

websocket-client==0.9.0

and a lib that requires ops. This would mean what ends up being run is:

pip install --no-binary :all: --requirement=requirements.txt
pip install --no-binary :all: ops

This would install the latest ops, bringing an updated websocket-client with it, which is probably not what's intended. If we had charmcraft run pip install --no-binary :all: --requirement=requirements.txt ops instead, that would get us an older ops that doesn't require the update to websocket-client. (Specifically, it would get us 2.0.0, which is the latest version that doesn't require that package.)

If the lib requires ops>=2.1.0 for example, the charm would then fail to build due to a dependency resolution error. This is possibly a better outcome than either the current state (versions from requirements.txt are not respected if charmlibs have dependencies) or the proposed state (versions from requirements.txt are not respected if charmlibs require updates).

Feel free to weigh in about preferred behaviours and backwards compatibility — I'll reach out to a few others to consider options too.

@carlcsaposs-canonical
Copy link
Contributor Author

carlcsaposs-canonical commented Jul 5, 2023

This would install the latest ops, bringing an updated websocket-client with it, which is probably not what's intended.

Good point! It looks like installing all the dependencies in one pip install would be best, so that any dependency conflicts fail the build

Except it seems like binary dependencies cannot be installed in the same command

@carlcsaposs-canonical
Copy link
Contributor Author

Except it seems like binary dependencies cannot be installed in the same command

Charmcraft could run pip install --dry-run with all the dependencies (binary or not) to check for conflicts, then install binary dependencies separately

However, even if there aren't conflicts, later commands could still override binary dependencies

Instead of using --no-binary :all: it might be possible to use --dry-run to get the dependencies and generate lists for --no-binary and --binary-only, but that might get tricky with subdependencies

@carlcsaposs-canonical
Copy link
Contributor Author

carlcsaposs-canonical commented Jul 5, 2023

Also just want to mention that because of #1077, pip doesn't have access to the full dependency resolution

i.e. during charmcraft's build, pip install runs in a virtual environment but, during runtime, system Python packages are also available that could have dependency conflicts

@carlcsaposs-canonical
Copy link
Contributor Author

Nice to have: it'd be great if whatever solution is implemented is compatible with #1115 (feature request)

@@ -239,26 +239,26 @@ def _install_dependencies(self, staging_venv_dir):
with instrum.Timer("Installing all dependencies"):
if self.binary_python_packages:
# install python packages, allowing binary packages
cmd = [pip_cmd, "install", "--upgrade"] # base command
cmd = [pip_cmd, "install"] # base command
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is breaking centos 7. I'm not sure what the best way to handle it is, so I'm going to tag in @syu-w for thoughts. My best proposal right now is to add a pip install --upgrade pip setuptools run before starting any of the conditional installs.

cmd.extend(self.binary_python_packages) # the python packages to install
_process_run(cmd)

if self.python_packages:
# install python packages from source
cmd = [pip_cmd, "install", "--upgrade", "--no-binary", ":all:"] # base command
cmd = [pip_cmd, "install", "--no-binary", ":all:"] # base command
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the Jupyter notebooks charm is failing because of this (see spread test.

Between this and the above, I'm wondering whether the right change for now might be to keep the --upgrade here and above and merge the two commands below (given the edge case we discussed in the main conversation).

I believe that'll resolve #1135 with a fairly minimal change that we can put into a 2.4 release fairly quickly, with a bigger rethink of package handling for a future release. How's that sound?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we can change the order of install requirement.txt and PYDEPS.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if a pinned version is installed, pip install without --upgrade will not try to get a new version. And if the package is not in requirement.txt, it will install latest stable version.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my original thought, but it could leave us with invalid PYDEPS that we won't discover until runtime.

Example:

PYDEPS includes pkg_a>=2.0, and requirement.txt includes pkg_a==1.0

Ideal behaviour would probably be to error out during the charm build due to a dependency conflict. However:

  • Current behaviour: we get pkg_a>=2.0 and a maybe-working charm.
  • Alternative behaviour: we get pkg_a==1.0 and an almost-guaranteed-broken charm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we need to check all deps in charmcraft without rely on pip

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe we can add PYDEPS to a new requirement.txt

@carlcsaposs-canonical
Copy link
Contributor Author

Superseded by #1157

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

Successfully merging this pull request may close these issues.

PYDEPS overrides pinned dependencies in requirements.txt
4 participants