Pipfile for Python
github.com
github.com
There are arguments for and against, but in general I think a configuration file should be human readable and editable, as well as easily understood by an IDE without having to run an interpreter. So something like YAML or TOML, or even a simple INI would be better than the function calls in the Pipfile. However, the lock file isn't meant to be edited by hand or really ever looked at, so it being in JSON or something less human readable is fine.
Also, why doesn't pip just make it, by default, look for the requirements.txt or the Pipfile? It's silly having to type pip install -r requirements.txt and I will also find it silly having to type pip -p. It's how NPM, Bundler, and many other package managers work; why do I need to call a flag to install requirements?
Because pip is used extensively as a manual command to install something - you don't expect something like DIR or ls to automatically get its list of files to list from a DIR.txt or a ls.txt file, do you?
What if one intended to type "pip install pillow" but got as far as "pip install " and unintentionally hit <Return> ?
Oh but there's a requirements.txt file sitting right there and "awwww heck now pip is reading it in and installing everything in there.. all I wanted was this new package not listed in that requirements.txt file..."
This is a similar argument to what happened when Babel in JS land decided to not have anything happen by default and explicitly require you to decide your configuration. It's silly to force users to do something that could be solved by a sensible default, and this is a clear case where a sensible default is appropriate.
Especially for newcomers, an implicit "use Pipfile if exists, else requirements.txt" is just unnecessary magic.
I also don't think it's ever a good idea to have multiple options for something like that.
Personally, I'm in the "whatever" camp. But you can be certain that such a change will break something.
Right now, the whole system encourages you to build bespoke environments that are really difficult to replicate. Making the default use requirements.txt encourages you to write down everything you're doing in a reproducible way.
Making `pip install` context-sensitive (dependent on the current working directory) would reduce reproducibility. Imagine the instructions to a beginner: (1) download, (2) cd, (3) pip install. Many times the beginner will skip step 2 or accidentally wind up in the wrong directory.
I do think you're misrepresenting the instructions to the beginner. The instructions to the beginner are (1) download (2) cd (3) run tox.
Beginners shouldn't even need to know what pip is, tox (or another build tool) should handle everything for you.
When I say "beginner" I mean someone who is learning their first programming language and may have never opened Terminal or `cmd.exe` before.
> run tox
I thought the argument above was that the command should be `pip install`. This is confusing.
> Beginners shouldn't even need to know what pip is
Ok, in that case, why do you care whether `pip install` has an implicit default mode?
Dear god we love to bikeshed.
Requiring a user to say what they want from a command line tool (that takes barely two seconds to type) is hardly an exhausting task.
Besides. Tab completion. And if it's really that important, make an `alias pipi="pip install -r requirements.txt"`.
Having pip 'just work' if there's a config file in the current directory would make it behave the same as 'npm install'. I have absolutely no love for npm and the whole node_modules debacle, but neither have I heard of anyone complain of the problem you describe, inadvertently 'npm install'ing instead of 'npm install foo'ing. It's rare enough and easy enough to revert that it's not that much of a problem, IMO.
Of course, if you're going to design a new package manager, you should look at the way OSes do it, not the way language ecosystems do it. OSes are much more battle-tested with their managers, and can't afford to handwave away problems and edge-cases.
[1] HN interprets stars as italic markup, so here are hashes...
I think you should look carefully at what npm and cargo do, and try your best to avoid what os package managers do.
Unfortunately, half the problem with python is that system level package managers want to rule the world, and not allow you to have 'user level' packages (ie. libraries) installed.
pygtk, for example, can't be pip installed. In fact, you can't actually (afaik) even use it from a virtualenv without modifying sys.path.
The most annoying thing about pip for me is that it isn't a 'complete story' in many cases. You have to actually install system level python-foo packages, which are not pinned in any meaningful way, and then hope that your application works.
Repeatable builds should be the goal of a python package manager; and that means being able to install specific versions of python (and c) libraries, in a way that is idempotent to the underlying OS; whatever it is.
OS level package managers solve a different problem; providing a single consistent packaged version of libraries and applications for users; having repeatable builds to generate those packages is a different problem, and, as evidenced by the huge amount of work the debian folk are putting into repeatable builds, not a trivial one; certainly not one that dpkg has already solved.
The reproducible builds project in Debian is about reproducing the build and getting every single bit the same. I think that's the primary reason why it's hard - there are various innocent sources of non-determinism, e.g. dictionary ordering in Python (pre 3.6), time stamps, build environment details, etc. If you can settle for just getting a specific repeatable set of versions, I think Debian solved that ages ago, it's just that they don't keep old versions lingering around forever.
By the way, if you take a step back and squint, I wouldn't be so sure OS level package managers really are that different from what what you call language package managers.
Sadly the same for build programs.
“The command-line tools of Unix are crude and backward,” he scoffed. “Modern, properly designed operating systems do everything through a graphical user interface.”
Master Foo said nothing, but pointed at the moon. A nearby dog began to bark at the master's hand.
“I don't understand you!” said the programmer.
Master Foo remained silent, and pointed at an image of the Buddha. Then he pointed at a window.
“What are you trying to tell me?” asked the programmer.
Master Foo pointed at the programmer's head. Then he pointed at a rock.
“Why can't you make yourself clear?” demanded the programmer.
Master Foo frowned thoughtfully, tapped the programmer twice on the nose, and dropped him in a nearby trashcan.
As the programmer was attempting to extricate himself from the garbage, the dog wandered over and piddled on him.
At that moment, the programmer achieved enlightenment.
--Master Foo Discourses on the Graphical User Interface
Thanks, I laughed.
Flexibility?
Adding custom programming logic into the requirements' file isn't really an issue, when setup.py could already grab a C compiler and start binding in system libraries into its package.
And unless you're using a homoiconic format, code is not (easily) parsable as data.
1. The executable format is used to create and output a .lock file, which is JSON and is the thing actually used to reproduce the environment (i.e., the .lock file is what you'd use when deploying the full environment).
2. TOML and other formats are being considered; right now something that parses to a Python AST is used for convenience to work out the API.
Of course, nobody commenting in this thread actually read any of that, so it's just people reiterating "I can't believe they didn't consider this thing they considered!"
So it would follow that tools do their thing based on the current directory, as specified by package.json/Makefile/Gulpfile/Dockerfile/Vagrantfile et all.
Many packages have abused the executable nature of setup.py files by importing obscure packages or adding otherwise fragile logic. I would hate to see Pipfiles go the same way.
Strong +1 to making it a declarative format like YAML or TOML instead of something executable.
Why do you prefer using ``pip`` despite ``pip install`` using the package's ``setup.py`` (and therefore is an eval)?
Further, if you're using a package manager, I expect you won't be combing through the source to check for malicious code anyway.
This is also why wheels (the new python package format) use a static file instead of setup.py. The Python ecosystem has been trying to get off of "just eval setup.py" for years.
You have just ignored the point that your parent point was making. It's not the install case that they were complaining about.
if soandso:
package("pack1")
else:
...
?Py2/3 compatibility? I've never had to do this, interested what the use-case is.
Requirements.txt is very pythonic in the sense that it's as simple as possible, but no simpler.
Accepted standards.
The main problem with requirements.txt, as I see it, is that you don't get exact versions unless you specify it in your requirements.txt. So you'd have to have a loose requirements.txt and then generate a second requirements file after having done `pip install -r requirements.txt` to get the exact versions that were installed.
Further, if you happen to "accidentally" `pip install some-package` in your virtual environment, your app might now be using different packages locally without you noticing. With Pipfile the need for virtual environments is pretty much gone, assuming that at runtime it will automatically load the version of a package specified in the lockfile, which is not clear to me yet from the README.
pip freeze > requirements.txt
it will have all the files' versions specifiedgenerate a second requirements file after having done `pip install -r requirements.txt` to get the exact versions that were installed
So you'd need to have a `requirements.txt` with loose versions suitable for upgrading your apps deps, run `pip install -r requirements.txt` and then `pip freeze > requirements.locked.txt`. Then everyone should be using `pip install -r requirements.locked.txt` as well as during your build. But that's cumbersome and error prone and doesn't free you from having the wrong version of a dep in case you `pip install some-package` later on.
What fphilipe is talking about is something like
$ cat requirements.txt
requests>=2.12.1,<3.0.0
$ pip install -r requirements.txt
$ pip freeze > requirements.locked.txt
$ cat requirements.locked.txt
requests==2.12.1
This way you can run pip install -r requirements.txt when you want to update your dependencies and then lock the resolved dependencies in requirements.locked.txt so that you get deterministic builds when the code runs in production environments where reproducibility and reliability are important. It also gives you a clearer idea of what are top level dependencies and what are transitive dependencies because the transitive dependencies will only be listed in requirements.locked.txt. However this system has limitations and isn't standardized. If you want to have different groups, say development, production, testing. You end up with + requirements.development.txt
+ requirements.development.locked.txt
+ requirements.production.txt
+ requirements.production.locked.txt
+ requirements.test.txt
+ requirements.test.locked.txt
And even if you can tell which are your transitive dependencies by comparing .locked.txt to .txt it does not tell you why a given transitive dependency is in your locked dependencies e.g you don't know which of your top level dependencies is pulling it in.Pipfile looks like a definite improvement over the pip install, pip freeze workflow.
This sounds exactly the opposite of what I'd want. I don't want some one to slip in a Guy Fiery into my dependence chain without me noticing.
So you start with a Gemfile that is your minimum requirements with no versions specified, the first time you `bundle install` it generates a Gemfile.lock which is then sticky. Over time your requirements are completely frozen until you decide to update, which you can do piecemeal via `bundle update gem1 gem2 etc...`. If you have a reason to avoid a newer library, then put a soft restriction in the Gemfile, preferably with a comment as to why that restriction is there and you have a very powerful long-term system for managing versions over time.
Just freezing and forgetting is a recipe for disaster when you have to update months or years later, and the transitive dependency updates are overwhelming and conflicted. Similarly exact versions specified make it fiddly to upgrade and hard to tell if there were reasons behind specific versions.
It's nearly impossible to further develop the result of 'pip freeze'.
$ pip install pip-chill
$ pip-chill > requirements.txt
Note: I built it because I got tired of reading through long auto-generated requirements files.But that's not always an option. Bower exists for a reason - all that duplication / bloat is unacceptable for browsers to download. It can also mean hell for static initialization / mutable state, because there's no longer a single owner of the global resource.
It's identical because bundler mostly got it right and dependency management in Ruby, while still not great/perfet, is better than just about everywhere else.
Kudos to python for moving forward.
I'm sitting here thinking, "what the hell is wrong?". I've honestly only experienced trouble with Ruby while being completely satisified with how python virtualenv works.
I guess if anything this proves that it's about habit. Habitual use of something makes it the easiest product for the habitual user. Ruby is something I force myself through when I want to try a product while Python is something I develop my own products in.
I think this is the product of people who got to know both Python and Ruby very well and found Python lacking here. There are lots of things Ruby developers were gifted from people who also know Python and found Ruby lacking. Python is generally something I force myself through so I'm not one of those people but so glad they exist.
BTW, Ruby has tools similar to virtualenv: chruby, rbenv, and rvm all do basically the same thing.
Take zulip for example, they do use requirements but they go their own way in most other things.
Managing an application is much easier if it uses standard build system. Setup.py, requirements.txt and so forth.
Gitlab is an example of a very complex packaging for a ruby application, but it works! It's complex but solid.
People are tugging in all kinds of different directions. My bad experiences with Ruby and node usually include seeing a loooong list of dependencies being installed and then at dependency #187 it suddenly stops for some reason like one rogue commit breaking compatibility with other packages.
This is hell to someone who doesn't develop in the language regularly, it's a bad packaging system for users.
To be clear, I'm not saying Python is better. I'm just identifying the issues I've had. The only reason Python is easier for me is because I've decided to use it more than the other languages.
On the one hand you can build a complex but solid system like Gitlab has, on the other you can use more standardized systems to distribute your app that require more steps and are less automated. But they're well documented and established methods used for that language.
I tried to pitch the Gemfile and lockfile approach when we were developing our own internal packaging system but nobody seemed to "get it" or see the value. I also tried to pitch database versioning (which alembic seems to do), but again, no takers.
I feel like it was a failing on my part to communicate or show the value in these things...they came randomly out of meetings and I probably botched the concept when pitching it.
I've stumbled with requirements.txt and setting up a new package. I'm also picky and don't like installing packages to my system (and I'm not always using virtualenv) so I have to look up how to install to my homedir. So I've stumbled with Python packaging (although, I think everyone can admit it has a bit of a hodgepodge) while I liked how Ruby did it.
I've been using pip-tools with tox for a couple of years now. I maintain a requirements.in and requirements.testing.in, and then I can run
$ tox -e pip-compile
to generate my fully qualified requirements. The pip-compile command is handled by a tox.ini section. [testenv:pip-compile]
commands =
pip-compile {posargs}
pip-compile -o requirements.testing.txt {posargs} requirements.testing.in
deps =
pip
pip-tools
The remaining nasty part is automated extraction requirements for setup.py's install_requires and dependency_links. I wrote a function to handle VCS links and other complicated syntax that I'm copying around to all of my projects. Otherwise, pip-tools has been a great solution.It seems to me that that's where we should be heading towards.
All that said, I have no clue what Pipfile is adding. The rationale appears to be that people sometimes don't use requirements.txt properly? Can one of the very enthusiastic commentators explain their enthusiasm?
That hasn't been settled yet. It's being actively discussed [0].
> Or is the idea to eventually get rid of setup.py?
Yes, a replacement for setup.py has already been agreed on in PEP 518 [1]. It's called pyproject.toml.
https://pypi.python.org/pypi/flit
Looks quite nice, a flat .ini file for specifying deps, though it only builds wheels.
There is another discussion on the Pipfile repo about this that may also clarify things for you [0]. The example I posted there [1], which I'll post again here, is:
---
A project can only have 1 set of abstract dependencies (setup.py/pyproject.toml), but different users working with that project can have different sets of concrete dependencies (requirements.txt/Pipfile) which allow them to fulfill those abstract dependencies from PyPI mirrors, private package indexes, personal forks on GitHub, or somewhere else.
So if project Car depends on Engine (an abstract dependency), I can choose to install Car but grab Engine specifically from a fork I made on GitHub (a concrete dependency) that has some performance improvements. Meanwhile, someone else working at a big company that doesn't want to depend on external services to build and deploy their internal Python projects can choose to install both Car and Engine from their private package index as opposed to PyPI (another concrete dependency).
You can't merge these two types of dependencies together into one file without hampering people's ability to choose where to get their dependencies from.
---
According to another commenter on that issue [2], both Rust and Ruby have a similar split in how they specify dependencies.
[0] https://github.com/pypa/pipfile/issues/27
[1] https://github.com/pypa/pipfile/issues/27#issuecomment-26228...
[2] https://github.com/pypa/pipfile/issues/27#issuecomment-26226...
...however, I strongly disagree on the benefit of making the `Pipfile` executable python. Just read this gist: https://gist.github.com/kennethreitz/4745d35e57108f5b766b8f6...
- This file will be "compiled" down to json.
Then why does it exist?We know it'll be abused; and we should have learnt our lesson from scons and setup.py that it wasn't a great idea before, and still isn't a great idea using python code itself as a declarative DSL. Just use a standard hierarchical file format (json, toml, xml, whatever)
Features of introspecting and editing `Pipfile.lock` should be rolled into pip and exported as a core python module; an api for editing pipfile.lock is a good idea, but executing a `Pipefile`, is not.
Also check out https://github.com/npm/npm/issues/10999 This is just insane!
Check out yarn as well, some of it's practices are really awesome - getting inspiration from bundler and cargo.
This is as someone who doesn't use either of these languages that much. Even composer is better than pip IMO.
Additionally, since each package gets its own `node_modules` directory, there's no need to worry about conflicting dependencies. Multiple versions of the same module can run in the same process with no interference.
Probably like 90% of the python projects I've looked at have requirement-conflicts because pip doesn't even warn you about this.
[1] this is quite a large number of cases, but they are significant drawbacks.
I do sort of like node's ability to recursively nest dependencies to avoid dependency hell. Though that has it's downsides too.. you will never know what is in your modules dir.
And if you don't want that (for example if you distribute a library), you can still distribute the project without the lockfile, and then have the users distribute the lockfile back to you to get a repeatable error.
There are a lot of mentions of deterministic builds, but that is already very achievable with pip-compile (part of pip-tools) or just pip freeze.
The grouping functionality allows you to have just one requirements file instead of one per environment (i.e. production, test, development, etc) which is mostly just a personal preference IMO. This isn't particularly compelling to me, but that could be because I'm already used to the traditional "pythonic" way of having one file per environment. Using the -r command within a requirement file allows one to recursively include other requirements files to avoid duplication of common dependencies across environments.
The difference in syntax between traditional requirements files and Pipfiles is indeed pretty large. The Pipfile syntax is quite a bit more verbose which I'm personally not a fan of, but this will come down to personal preference and what one is familiar with.
It's unclear if Pipfiles as proposed here is meant to include the dependency resolution functionality of the pip-compile command provided by pip-tools. That is a very critical step as vanilla pip makes no guarantees about respecting version pins of nested dependencies; only that some version of a nested dependency will be present but not necessarily the one intended.
Another big unknown that others have asked about as well is how Pipfiles can be used to manage requirements for a library in a way that allows other libraries/apps that do not use Pipfiles themselves to still list said library in their requirements.txt.
Apologies if my comments comes across as overly negative or dismissive; I applaud any effort to improve the tooling around Python dependencies. But as someone already familiar with the Python/pip ecosystem, it's not clear how this would improve or simplify the solutions that are already out there.
In my python workflow everything lives within a project virtualenv. Dependencies are defined in setup.py with install_requires, extras_require and tests_require. I build against latest, version constraints are added mostly when the latest version of a package has a problem.
Now when i commit to dev, stage or prod branch our ci generates a version pinned requirements.txt which is used to install the virtualenv on stage/prod.
I don't really see the improvements in Pipfile, feels like https://xkcd.com/927/.
>In my python workflow everything lives within a project virtualenv
Were you using rbenv or rvm with Ruby?
For Python, there's `pipsi` for automatically creating unique virtualenvs for binaries - that's the right approach, and it's worth adopting ASAP for future sanity. It even lets you mix python 2 and 3 binaries without any issues. I don't know what the equivalent would be for Ruby.
#!/bin/bash -x WHEELHOUSE="/usr/local/wheelhouse" [ -d "$WHEELHOUSE" ] || ( sudo mkdir -p /usr/local/wheelhouse/ ; sudo chmod -R 0777 /usr/local/wheelhouse/ ) deactivate set -e cd .requirements for reqfile in requirements*txt ; do TEMPDIR="$(mktemp -d)" virtualenv -ppython3 "$TEMPDIR" . "$TEMPDIR"/bin/activate pip install -U pip pip install -U wheel pip wheel --find-links="$WHEELHOUSE" --wheel-dir="$WHEELHOUSE" -r $reqfile pip install --find-links="$WHEELHOUSE" -r $reqfile pip freeze | grep -v "pkg-resources" | sort > "../$reqfile" rm -rf "$TEMPDIR" done wait
I welcome any improvement to python packaging, but I'd start with improving pip itself & making it into a library that tools can wrap around.
If you don't control all the projects involved, this needs some quite convoluted inheritance chains to work around.
I can't tell if this change will fix that.
But I'm sure I'm missing something. Please feel free to convince me I'm wrong :)
Grouping of sub-dependency groups (e.g. a testing group).
That seems like an improvement over having multiple requirement files. Then again, I'm perfectly happy with separate dev/testing files.It'd be nice to be able to mark a dependency as being conditional based on some expression, e.g. `$PYTHON_VERSION_MAJOR >= 3`.
I know that it's just a few simple operations for me to do it myself, but npm has spoiled me.
This is a missing feature, there are various hacks but nothing concrete.
If you're working on a large project you have to keep track of all your dependencies and preferably only add the main dependency, not its dependencies, into requirements.
I tend to avoid doing pip freeze > requirements.txt because it lists packages I know shouldn't be in requirements.
Like civilian, my main ask for an improved pip would be a way to save a single explicit package into my requirements.txt by adding a --save flag when installing it.
I don't want to include sub-dependencies in my requirements.txt.
The way to maintain requirements.txt with single dependencies (base dependencies maybe, I'm self-taught on the lingo), is to manually add them one by one.
Pip freeze is useless because it adds a lot of things you don't need into requirements, making it cluttered and hard to follow.
Another benefit is named groups, which allow you to more succinctly specify dependencies for various environments (dev/test/production/etc.) Along with this you get the benefits of the lock file so you can be assured the subset of libraries you use in production will be the exact tested libraries you use in your larger dev/test environment.
Installing the project would cause it to be built from the lock file (assuming the Pipefile hasn't changed). This will mean your users won't get "some version" of a library you depend on between verion 1.0 and 2.0 or whatever range you specified. They'll get the exact package you last successfully used and checked in yourself, right down to the git commit if applicable.
Once you modify the Pipfile then pip will resolve your dependencies and try to make the specified changes by adding/removing/upgrading packages. If Pypa continues following bundler conventions this will be done by making the fewest changes possible from your existing versions in your lock file. You'll also have an upgrade mode where pip will rebuild you project from the Pipfile looking for the most recent versions of all libraries or a specified library within your specified version ranges. When done and your app/tests are working, check in the new version and you can ensure all your users/environments will be able to upgrade cleanly.
https://caremad.io/posts/2013/07/setup-vs-requirement/ covers it pretty well. Or https://medium.com/@sdboyer/so-you-want-to-write-a-package-m... for a fantastic read and a LOT more context.
in fact, there's already a library for this called pip-tools (https://github.com/nvie/pip-tools) that generates a requirements.txt (the lock file) from a requirements.in (your direct dependencies)
To fix that, you'd need something like Nix.
From the original prototype's comments: https://gist.github.com/dstufft/2904d2e663461f010bbf
"- If there's a corner case not thought of, file is still Python and allows people to easily extend"
... and ...
"- Using Python file might cause the same problems as setup.py"
Ugh.
Also, how is the "lock file" actually distributed? Unless you can pip install a wheel and have it include an embedded lock file, then you've still got to have some out-of-band mechanism for copying the lock file around like you would a requirements.txt or even a fully-fledged virtual environment.
Don't be so scared. Ruby developers have been doing this for over 7 years now. Trust us, this is much better. Your Python friends at Pypa are copying Ruby because dependencies are less painful there than just about anywhere. They are still painful, but this will help.
Also, I'm not sure how a lock file in source control will help me when someone who is not checking out source wants to pip install my project using said lock file.
There seem to be a lot of strange fears from python developers about this approach and my comment on age was an attempt to assuage those fears. Since ruby developers are quite happy with bundler (at least in comparison to other communities) and have been for some time it's a reasonable point and not the only one I made.
I'm not sure how pip plans to use the lock file for projects distributed via pip. They may very well have a solution planned for that.
However, for projects that are distributed by source (which are many) the lock file ensures deterministic builds, and requirements.txt generally doesn't without being pedantic with your versions.
There you go again.
This isn't fear. It's asking the question of: What. Is. The Damn. Point. Of. This?
I have yet to see a simple, cogent explanation of why this is better than a list of requirements specifying;
pyside==x.y.z
pillow==x.y.z
As opposed to this proposed Thing.
You know what this reminds of?
It reminds me of replacing super simple .INI text files with configuration files stored as XML - change for the sake of it, because Change!
Now, I'd love to see a very simple explanation and justification on why we should all move to this new Thing you're making.
Something like "This new Thing is better because..." , followed by practical /examples/ , because right now, all I see is More Complicated Stuff For The Sake Of It.
> subject to all the abuse you can introduce
> might cause [...] problems
I've said a few things about the potential benefits, though I am not involved in the project. The actual authors offer the best list of benefits: https://github.com/pypa/pipfile#the-concept
They also point out this will eventually be built into pip, so it will have the benefits of all the existing workarounds like pip-tools without any setup. It does seem like a lot more than change for the sake of change, and along with it since there is so much change why not take the opportunity to adopt a flexible DSL built for future extension so there is less real change in the future.
SomeProject==5.4; python_version < '2.7'
SomeProject; sys.platform == 'win32'
In my view, that's already out of hand, and isn't even really parsable without some bespoke library.I'm a big fan of this idea of having a requirements.lock. I can call pip to do the parsing, then I can parse the lock which is just json.
The fact that you need to parse the requirements.txt feels like a design issue with pip to me. (Of course, I'm coming from Rubyland.) As for setup.py, setup.py is broken because it conflates build scripting with package metadata and dependencies. Now that tox exists things are better (but the fact that tox is not a general-purpose programming language is IMO unfortunate coming from Rake/Ruby/Bundler/Gemfiles.)
Was that really necessary? You just moved the tone of your reply to a less pleasant manner.
The poster isn't scared. They, like me, are failing to see the necessity of this compared to the more simple, straightforward, already-working, KISS-principle-following requirements.txt, which was my immediate reaction to reading the description of this project on the github link.
Maybe my tone came across condescending. That wasn't the intent. It was meant in jest in hopes the reader would second guess the fear.
KISS is great, but this project is here because it's not already working for everyone. There is benefit to a flexible dependency system that keeps the concepts of "acceptable versions" separate from "actual versions last used" while also adopting an extendible DSL ready for edge cases that haven't been thought of.
And you don't need both. You can pip freeze a frozen_requirements.txt. You can ship around a whole virtual environment. And I can conceive of more requirements.txt 2.0 solutions that don't require over-wrought DSLs.
I also find it a little odd since a destructive project author can still find ways to mess things up without any code executing during setup, right?
Since this is for project level dependencies it doesn't seem like the potential for abuse is too high since you'll likely have a single Pipfile per project and the project will control it.
I don't know that anything will prevent destructive developers from being destructive. I would hate to throw out all DSLs because of a few bad actors.
For what it's worth in Ruby even library dependencies are written using a ruby DSL that runs unprotected during install, as well as some post installation hook facilities. There have been minor abuses like annoyingly long post-install messages and other crap that has occurred, but through simple community pressure the offending libraries are eventually pushed back in line and it's not an issue Ruby developers regularly encounter. Communities are different but Python seems like a community that appreciates best practices and is maybe better at enforcing them than Ruby is, so I'm not sure preventing one minor way a terrible developer can bite you is worth hamstringing your build system.
Yes, an over-wrought DSL is not a prerequisite. You can do deterministic builds and even grouped dependencies without it. But complex platform specific dependencies, git based dependencies, and dependency edge cases we haven't yet envisioned are more easily captured in an extendible native DSL than a more static data format. The DSL also has the advantage of being "just Python" so it will be very easy to remember for Python developers.
Maybe the benefits aren't worth the trade-offs but don't be so quick to count potential or perceived trade-offs as actual ones until you try out the new build system and see how it impacts your workflow. I know you got burned but this isn't setup.py and potential for abuse isn't the same as abuse... yet.
The whole point is that if you want things to be reproducible you don't even need to look in the Pipfile. requirements.txt already supports conditionals, so the Pipfile.lock is strictly simpler to reason about in that sense.
Here's a setup.py that cannot even be imported unless another package (numpy) is already installed: https://github.com/scipy/scipy/blob/master/setup.py
I've seen setup.pys that read random files from the filesystem. I've seen setup.pys that shell out to random system commands that may or may not be installed. It's just a horrible format for what it's trying to accomplish.. because it's not a format! It's a programming language.
Having a more complex requirements system that what we have with requirements.txt is good, but at what cost? Is Python doing this just because other languages to it this way? I think it is actually
Dependency groups don't really mean an advantage to me. E.g. with pip-tools, on the code I work on, we've got 3 requirements*.txt files. One (requirements.txt) for prod, and ones for test/dev/any additional scopes you may want. Then you `-r requirements.txt` in requirements-dev.in (and -test), and you're guaranteed to maintain the same versions as production when resolving requirements-dev.txt, or have conflicts if something dev adds prevents that from working. In CI / build / etc you just install the single file that's relevant to you.
Bundling groups into the file would be nice for not being able to make mistakes (our approach above requires you to compile things in order, for example), but that would have to weigh pretty heavily against breaking backwards compatibility and a very-simple DSL that already exists.
I think the condescending tone is quite unnecessary ("Trust us, this is much better. Your Python friends at Pypa")..
In my experience Ruby is a a far worse culprit than Python for breaking things. First of all `rvm` and `rbenv` are both bloated and rather unreasonable invasions of the shell (hi-jacking `cd` seriously?) Secondly, the fact that Ruby modules are globally shared -- can cause endless breakage from a few misbehaving packages, especially with ones that like to monkey-patch std-lib classes (refinements should improve the the later but it will take a while for the ecosystem to catch-up).
If this Pipfile.lock approach allows me to always have the correct package versions without virtualenv, it's a huge step forward.
Just one request: please, please add a --save option to pip like npm's.
What does that even mean?
A .txt file is meant for human readable sentences, yet this file is meant to be consumed by pip. You can only "customize" it working within the confines of pip freeze. If you were new to programming, would you suspect that requirements.txt was somehow linked to Python? Probably not.
Why can't ruby or JavaScript or any language make use of requirements.txt? They could, and make the same claim you just made, which would break pip install -r.
For a comparision, NPM has a packages.json which is hand-editable, and lots of people do customize it by hand to get their NPM scripts running, but:
a) If you're new to programming, you might be confused by which language it belongs to just like a requirements.txt
b) if you peek into it, you may not think it's generated, consumed, or editted by machines because JSON format is so readable
My point is that the .txt extension doesn't give any hints to its use or that it's breakable, and that the name "requirements" doesn't describe the intended domain of the file (Python only). Even naming it "python_requirements.txt" would be an improvement.
I've used it in countless projects and it just works. It's also simple to understand.
This facilitates that.