Hi everyone, I have a bit of a specific question s...
# general
Hi everyone, I have a bit of a specific question so apologies for the long message. Any help would be very appreciated! I am posting to ask what the best way is to fix the versions of sub-dependencies in a project that uses the pants build tool. Here is an outline of the issue we faced and how we want to prevent it in the future. Here is our repo structure: our_awesome_repository | pants.toml | pants |--src | |--all_modules | |--api | server.py | BUILD | |--cool_stuff | methods.py | BUILD | |--awesome_stuff | methods.py | BUILD |--3rdparty | |--python | requirements.txt | BUILD Our server.py file uses Flask, so in our 3rdparty/python/requirements.txt file we have: Flask==1.1.2 And in our src/all_modules/api/BUILD file, we have a python_sources looking like this: python_sources( name="lib", dependencies = [ "3rdparty/python:Flask", "src/all_modules/cool_stuff:dist ] ) Recently, we tried deploying the api, but encountered the following issue. Our version of Flask depends on jinja2, and a new version of jinja2 was released which broke our version of Flask. Since we hadn't specified a version for jinja2, our build process was automatically installing the latest version of jinja2, which broke our api. Here is a stackoverflow post where others encountered the same issue: https://stackoverflow.com/questions/71718167/importerror-cannot-import-name-escape-from-jinja2 In order to resolve this, we specified the version jinja2 in our 3rdparty/python/requirements.txt to a previous non-breaking version. So currently, our 3rdparty/python/requirements.txt contains: Flask==1.1.2 jinja2==3.0.3 However, when we tried redeploying, we still saw that the most recent version of jinja2 was being installed. It wasn't until we updated the src/all_modules/api/BUILD file that we were able to fix the version of jinja2 being installed. Currently, our src/all_modules/api/BUILD file contains: python_sources( name="lib", dependencies = [ "3rdparty/python:Flask", "3rdparty/python:jinja2", "src/all_modules/cool_stuff:dist ] ) We would like to specify the versions of all of the subdependencies of our 3rd party imports in order to prevent this issue in the future. To do this, so far we have just updated the 3rdparty/python/requirements.txt file fixing the versions of all of the subdependencies (using virtualenv and pip freeze). However, based on the fix with jinja2, it would seem that we would need to manually update all of the BUILD files with the subdependencies. This seems like a very ugly and difficult solution to maintain, so we are hoping that there is a more elegant option. Essentially I am posting to ask, why was it the case that we needed to specify jinja2 in our BUILD file in order for pants to install the correct version from the requirements.txt? Is there a way to get around manually adding the subdependencies in all of the BUILD files? If there is any additional information needed in order to understand the question or the structure of our repo, please let me know and I will be happy to provide what I can. I really appreciate any help with this! Thanks
Hi there, Michel! Welcome! This is exactly what a lockfile is for: it allows you to pin all transitive dependencies to an exact version, and makes sure things don't break overnight like what happened to you. Have you had a chance to read https://www.pantsbuild.org/docs/python-third-party-dependencies#lockfiles?
As Eric points out, this is exactly what lockfiles are for! Here is why just pinning jinja2 in your requirements.txt didn't work: When using Pants, a requirement.txt describes a universe of allowed direct requirements, but only the ones you actually depend on get used. Since your code doesn't import jinja2 directly, Pants doesn't infer that dependency, and so doesn't bring it in to the set of initial direct requirements to be resolved. Instead it gets pulled in as a transitive dependency of Flask, and so you end up with a bad version. When you added that explicit dependency on your pinned version of jinja2, Pants brings it into the resolve as a direct requirement at that pinned version, and the resolver respects it.
So, yes, lockfiles are your friends here. You can edit them manually if you must to tweak specific versions.
You can edit them manually if you must to tweak specific versions.
Or, change the inputs to the lockfile generation. When you generate a lockfile, Pants looks at all requirements you declare are in that "resolve" or lockfile, even if your code never directly imports it. So, if you change to use lockfiles and you keep Jinja in your requirements.txt, then Pants will do the right thing