-
Notifications
You must be signed in to change notification settings - Fork 147
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
Executable File Format, Yes or No? #10
Comments
Since the idea for this and the syntax came from me, here is my thoughts: The first thing to remember is that this is not a replacement for There are a few major problems with
Given all of the above, I set out to design a new format that would solve those issues. At the time I came up with three distinct options: # Proposal #1
# This attempts to closely match the Gemfiles from Bundler, this might mean that
# people would find it easier to pick up. However it could also mean that people
# will think down of it as a "copy" and the differences might be noticed more since
# The syntax is matched better.
source "https://simple.crate.io"
source "https://pypi.python.org/simple/"
dist "requests"
dist "Django", "~>1.4"
dist "pinax", git = "git://github.com/pinax/pinax.git", branch = "1.4"
dist "crate", path = "~/blech"
dist "test", group = "development"
dist "test2", group = ["development", "testing"]
group "development":
dist "blah" # Proposal #2
# This proposal takes the ideas from a Gemfile, and makes them more "Pythonic".
# Instead of creating a DSL, simple functions are used. This shares a lot of the
# same benefits as #1 in that people might find it easier to pick up as some will
# already know Gemfiles. An obvious negative is that it tends to be a bit more verbose
# than #1.
source("https://simple.crate.io")
source("https://pypi.python.org/simple/")
dist("requests")
dist("Django", "~>1.4")
dist("pinax", git="git://github.com/pinax/pinax.git", branch="1.4")
dist("crate", path="~/blech")
dist("test", group="development")
dist("test2", group=["development", "testing"])
group("development",
dist("blah"),
) # Proposal #3
# This proposal takes the power introduced via 1 and 2, but uses yaml to create a
# (hopefully) easy to parse set of requirements. The given example makes heavy
# use of inline style of declaration so that one line == one dependency. One area of
# concern is how to handle the "default" group. It will need to exist somehow. As a simpler
# way here the default group can be defined outside of a groups: tag, but the question remains
# what should that tag be called?
sources:
- https://simple.crate.io
- https://pypi.python.org/simple/
require:
- requests
- [Django, ~>1.4]
- [Pinax, {git: "git://github.com/pinax/pinax.git", branch: 1.4}]
- [crate, {path: ~/blech}]
- [test, {group: development}]
- [test2, {group: [development, testing]}]
groups:
development:
- blah My thoughts at the time were:
Since then I had picked up on the second option and refined it to look like: # This is designed to be edited by hand by python developers
# --index-url and friends look like command options, non inutive. No extra metadata available
source("https://simple.crate.io/")
source("https://pypi.python.org/simple/", verify_ssl=False)
# Design:
# - Use real datastructures, make things clearer
# - Seperate package name from version, using real strings.
# Django==1.4 is a valid PyPI package, uninstallable from current files
# - using kwargs creates a great way to provide optional options on a line by line basis
# that python programmers are already familar with
# - People should only have to worry about the things they depend on, the installer
# should do the right thing
# - People have different dependency based on environment
# - Allow advanced usage for "wierd use cases" or "patterns not anticipated"
# Concerns:
# - Using Python file might cause the same problems as setup.py
# - This File not designed to be directly executed
# - setup.py is typically sent to other people, requirements are typically for internal use
# - Final result is still deterministic
# - Somewhat more verbose
# - Uses a syntax familar with all python programmers
dist("requests") # Install the latest version of requests, and all dependency's
dist("Django", "==1.4") # Install a version of Django matching the "==1.4" version spec and all dependencies
dist("pinax", git="git://github.com/pinax/pinax.git", branch="1.4") # Install pinax, using git and the 1.4 branch
dist("crate", path="~/blech", editable=True) # Install crate from the supplied path, and install it as an editable
dist("test", group="development") # install test, but only if the development group is passed
dist("test2", group=["development", "testing"]) # install test2, but only if the development or testing group is passed
with group("development"): # start a context that is equivilant to passing everything a certain group
dist("another thing") # install ``another thing`` if the development group is passed
with source("https://inhouse.example.com/"):
# Things in here MUST be installed from the above source, the idea being that if you have forked, e.g. django-model-utils
# you can uplaod to an inhouse server and force django-model-utils to install from this source, even in the case
# there is another version higher then yours, or even the same version.
# Additionally if packages installed inside of a with source(): have dependencies their deps are searched for
# on source, and installed from there if possible. However if they are not available dependencies will fall back
# to the global source()es.
dist("django-model-utils") # Not shown in the json lockfile.
# Details
# - This file does NOT cause anything to directly get installed, the program uses it to build up an internal list of requirements
# - All dist()'s are considered for the requirements, even if they are not going to get installed (e.g. they are in a not currently active group)
# - This will allow things to work smoothly where the requirement in a group might modify the final installed version (e.g. a group might pin to Django<1.4)
# - This file will be "compiled" down to json.
# - When the compiled json exists, there is no need to do any sort of version resolving. We know exactly what it is we want to install. (e.g. --no-deps etc)
# - We still need to find out where to download the packages from? (Unless we encode that in the json. Might be risky)
# - If there's a corner case not thought of, file is still Python and allows people to easily extend
# - You don't need to pin to exact versions, you just need to pin to what you want to support. e.g. if a pacakge follows semver, you could do package>1.2.1,<1.3 (you first started using the 1.2.1 version, and you expect 1.2.X to always be good for you.
# - Exact versions are pinned automatically in the json file Which is basically what you see as the idea of My reasoning for going with Python instead of one of the other options are:
So where do we go from here? I see a few possible answers:
Personally I think the correct way forward right now is to just roll with the Python syntax to nail down the API and execute the file in order to get the information out of it. However we should do the things mentioned by @ncoghlan in #6 to discourage people away from doing things that make this file not declarative. Before we make this a final thing that people actually are expected to use we can refactor out the |
Thanks for the clarification (your post should be a must-read for everyone participating in this discussion) but you did not address any of the issues in #8 and #9. I also think that a system as powerful and flexible as the current idea of Pipfiles will be used to replace Your YAML proposal actually looks way better to me that the executable approach, limited or not (#6). |
I agree with @defnull, the YAML approach does look better to me as well. Having to learn a whole new syntax just for a |
@defnull It can't replace |
It actually sounds like you are going with the python-like syntax because it's easier for now. Please remember that removing features is way more difficult than adding features. Starting powerful and restrict it later is a very risky approach. |
@defnull Going with the Python-like syntax because I think it presents the best API out of the current options, going with a simple |
@dstufft So you suggest that Pidfiles should look like python, be parsed by the python AST parser, but not executed at all? Local variables, loops and other constructs won't work? Imports won't work? Then you have a save DSL (which is nice) that is hard to parse (by anyone but python), non-standard, and has no advantage over existing formats like YAML or TOML. |
It's important to remember that nothing is final until this lands in Pip proper — this is just a library to prototype the idea out and move forward with an implementation as opposed to debating forever :) |
Static analysis should only be encouraged on the generated lock file. The Pipfile is simply a tool to generate that in a friendly manner (which doesn't exist today). |
In support of @defnull: the advantages of using Python syntax seem to be:
A safe AST-based parser would throw away both of those advantages. 1 is not much of an advantage in any case if the alternative is a common format like TOML/YAML/JSON, because there are already parsers for those. |
Reading back through @dstufft's long post in more detail:
Fair enough. I agree that TOML and YAML are both less familiar, though many developers are used to using them in some other context. JSON is probably familiar to a lot of people, but its lack of comments make it a bad choice for human-written files.
These points seem to amount to a preference for Python syntax for declaring data. That's valid, and I agree that most declarative formats don't allow the mixture of positional and named values that function signatures do. But if this leads to having to scrape data out of a Python AST, I don't think the syntactic nicety is worth using a nonstandard format and special tooling.
This counters an argument against using Python, but it doesn't really provide a reason to pick Python.
If we're considering parsing data out of the AST instead of executing it, this advantage would presumably go away. |
Another point is that writing to a file with Python like syntax will be a lot harder than using something battle tested like YAML, TOML or JSON where parsers are widely available. Since the |
I agree that if we move to an AST based parser then we're essentially just looking at a DSL that happens to be a restricted subset of Python. I care a whole lot less about how hard it is to write the So we can restrict this to simply trying to figure out what the best format is for a DSL or file format for this and shelve execution for right now. I'm going to exclude JSON right off the bat because a lack of comments makes this unsuitable for a human writable file IMO. When I look at YAML I can't help but feel like it's simply not powerful enough of a format. For instance, looking at my original sketch of: sources:
- https://simple.crate.io
- https://pypi.python.org/simple/
require:
- requests
- [Django, ~>1.4]
- [Pinax, {git: "git://github.com/pinax/pinax.git", branch: 1.4}]
- [crate, {path: ~/blech}]
- [test, {group: development}]
- [test2, {group: [development, testing]}]
groups:
development:
- blah The problem here becomes that composing becomes difficult. For instance, how do I require something that comes from a specific source in my development group? Currently the only ways I can see are things like: require:
- [mything, {group: development, source: "https://internal/"}
groups:
development:
- [mything, {source: "https://internal/"}
from_source: # This can't be "sources" because that's already used
"https://internal":
- [mything, {group: development} Whereas in the Python inspired DSL, you can do any of: package("mything", group="development", "source="https://internal/")
with group("development"):
package("mything", source="https://internal/")
with source("https://internal/"):
package("mything", group="development")
with group("development"):
with source("https://internal/"):
package("mything") This flexibility lets you make things a lot cleaner and easier to read in a lot of situations whereas the YAML/JSON (I haven't messed with trying to spec a TOML version out) feel like a slightly improved version of what we already have where it becomes really ugly to do anything beyond the simple case. However, the DSL provides a much nicer ability to add even more utility beyond what's in my simple example. For example, what if there are a set of libraries that I want to install on Windows only? package("library", environment="sys_platform == 'win32'")
with group("development", environment="sys_platform=='win32'"):
package("library")
with source("https://internal/", environment="sys_platform == 'win32'"):
package("library")
with environment("sys_platform == 'win32'"):
package("library")
with group("developlment"):
package("other-library") You can even imagine some baked in things like: with environment(env.Windows):
pass Where I can imagine this being extended to other things as well, wanting to select binary versus source, turning on As one of the people who have to actually implement and maintain the thing that interprets this file, I am sympathetic to the goal of wanting to make it as easy as possible to implement it*. However, I think that making it easy and pleasant to actually write and read these files for a human matters far more. I will trade implementation complexity for a better end user experience every day of the week. Now I could be totally wrong and end users will hate using a Python inspired DSL, but certainly for my own use cases the YAML/JSON options feel like something I would not be very enthused about using. * One thing I'd mention in addition is that I'm not entirely sure that the off-the-shelf libraries are even going to be particularly useful here. For instance, if you're writing a tool that will submit a PR that updates |
I tend to agree with you here @dstufft. Bundling a YAML parser with pip is also going to be a nightmare on Windows based platforms. |
YAML surely has its own drawbacks (that is why Cargo uses TOML instead), but you can simplify the structure a lot if you allow for some repetition, or just rearrange some things. The structure can also be dynamic (e.g. allow the same value to be a literal, a list of literals or a map, depending on the requirements) to make it more convenient for the easy case, but also support complex cases.
It's a stupid example, but I want to show that YAML can be very flexible. A source definition may be a single string, a list of strings, a map or even a list of mixed types, if it fits the use-case. Duck-typing is considered pythonic, after all. Edit: Parsing YAML is not easy, but at least it's platform and language independent and there are a lot of battle-tested libraries out there. There is a reason why YAML is the defacto standard for complex configuration nowadays. (I'd still prefer TOML, by the way) |
I think I bring some experience of the pains of YAML at scale and pleasures of bundler to the table. Also, I've done some whacky customized bootstrap files. Also I've used PIP style URL's in vcspull, check out how I handle many VCS' in my .vcspull.yaml file. YAML is a pain to scale. It's easy to mess up indentation of yaml and get unexpected results too. Saltstate's are a pretty tricked out form of YAML (add's jinja2). Handling requirements in the declarations can be very error prone and hard to debug. See, in vcspull, I took the habit of being very flexible (like Salt was) in how you can use shorthand declarations in the yaml to make it look more brief. This leads to a lot of inferred behavior and complication in the parsing the yaml that creates confusion.
Having done some ruby / rails (after 2 + years of straight python) Gemfile's felt like a dream. Will pipfile also handle virtualenv type stuff? Will Prefer Example #2, @dstufft . I think Pipfile should resemble something close to Python. Even if it's being parsed custom in some way. edit: after consideration the post below, I'm also open to JSON / YAML. Nothing wrong with json either. It should make it easier to export freeze/locks and snapshot stuff. |
There is a plus side to using a declarative format like YAML and JSON though, in https://github.com/tony/tmuxp and https://github.com/tony/vcspull, we're able to use JSON or YAML, since it pretty much is a python dictionary in the end. See https://github.com/emre/kaptan. So while I'm sure you don't want the overhead of Pipfile having json and yaml to deal with, perhaps it's true packages will end up being represented internally as (a list of) dictionaries, which end up getting exported to json/yaml anyway Also, json is part of the standard library. No external dependencies like yaml has with pyyaml. Note: I'm not making suggestions or trying to sway anything, just relaying info. Would love to help this effort however possible (testings, docs, etc). |
@dstufft you say we shouldn't worry about how hard it is to parse this format. But that only holds if the one parser you write is the only one that anyone ever uses. If e.g. PyCharm wanted to implement support for automatically adding dependencies to a Pipfile, they would either have to code their own machinery in Java, or shell out to a Python tool to deal with it. And would your tool aim to support programmatic modification, or only parsing? Standard formats mean that parsing is a solved problem for most mainstream languages, and there are some tools already out there to do non-destructive modification. I think that's worth using a slightly less convenient syntax for. |
I felt obligated to ask about a methodology/thought for identifying system level packages. There are more than a few packages with actual system dependencies, not just python dependencies. If you want massive adoption, adding this feature will be a huge net positive. Also, I understand the level of difficulty this ask is. +1 to extending python and building out a better setup.py and not another *file, because quite frankly, declarative system installs are pretty terrible as solutions once you have a moving install target. I know everyone wants to make it simple, but it's just not. -1 on developing yet another DSL with it's own set of rules when you have Python available. +1 on YAML. The same arguments about indentation can be said about python. YAML doesn't really change this aspect. Presumably, the vast majority of full-time python developers have indentation down. I'd argue some of the other problems related to YAML have more to do with poor or non-existent error handling within the yaml tooling and less to do with the format itself. Decomposing or composing YAML has been done in both salt and ansible. They're not shining examples, but given the other options, I really think YAML is second best after improving setup.py directly. Just like code, though, YAML can be written well or not. |
How far could we realistically push JSON or YAML? What would be the shortcomings? Admittedly, a “Gemfile” type of Pipfile is much prettier IMO. But a pure JSON/YAML is rugged, utilitarian, ubiquitous. Has none of the downsites of You can just export a vanilla dictionary to pretty JSON/YAML. Also, would it be worth it to create a Google Doc to weigh pros / cons |
The proposed replacement for |
@takluyver pip will want to support programatic modification for things like My thing isn't that "nobody is ever going to have to implement this because I'm going to implement the one true parser and then everyone can use that!". My thing is that the number of people who are ever going to have to implement or work with the internals of an implementation are fairly low, possibly (probably?) fewer than 100 total people in the world, but even if it's 1000 people that's still VASTLY fewer people than will be writing, reading, and editing these files so I care a lot more about what presents the best interface for them over what makes it easier on the handful of people who will do the work here. So folks doing things in Python? Most (hopefully all?) of them can just use the exact same library that pip itself uses. Folks doing things in other languages? Well someone in that language will need to write that parser, but that's a limited number of people. Presumably PyCharm already has some mechanism for parsing Python AST given that it needs to do that to do things like syntax highlighting, auto completion, etc, but at the very worst, yes they can also shell out to Python if they really need to. |
@tony In the end this gets turned into some dictionaries (and in fact, our lockfile is currently proposed to be JSON, so it gets "compiled" into JSON either way, although our lockfile's structure is not designed with humans in mind but with computers). This means that it would be possible to have a DSL, a YAML, and a JSON representation of the same data and let end users pick. I don't think that is super valuable here though other to me so I don't have to make a decision ;). I think that supporting N formats ends up making it even harder on people (now they need to track down or write N different parsing/emitting libraries that can round trip successfully) and most folks are going to settle on a single format anyways. |
Yep, agreed. Saltstack technically allows more than just salt's version of yaml, but in practice, the community almost always uses it, even with the opportunity to plug in JSON and other stuff, Even with vcspull and tmuxp examples I used above, despite me offering the choices of JSON too, people tend to just want YAML. So agreed, the community tends to agree on a single format in the end. 😄
got it. Saying that, the python-inspired DSL (such as that in proposal 2) is just beautiful. very human friendly. |
@nchammas I disagreed with that Pep when I saw it in May and I still do here in November. The arguments made there seemed rather biased from a very personal viewpoint and not from a well thought-out set of technical ones. That makes me think there really isn't much discussion or push-back at all when there really should be. This discussion feels the same way. |
Let's go for TOML:
For me, TOML is by far the best option, and it being little known is easily offset by maturity, and simplicity in use and scope |
This is not an appropriate venue to (re)legislate PEP 518 or the fundamental difference between |
TOML example (for reference): https://gist.github.com/kennethreitz/9319936c301be5c01f6da04e518d2cf3 |
FWIW, on the Ruby side of life it has been very useful to me over the years to have Gemfiles be Ruby code. It allowed implementing a version of Gemfile inheritance (via |
I strongly suggest not to make I am convinced that Pypa should look into Rust's |
What makes specifying Python environments different then that of other languages? Are there differences? If so, do those differences warrant yet another format? How is this going to handle environment requirements that are not Python packages? With Nix we have a DSL that is used for specifying packages and environments. While the Nix package manager and OS isn't a solution for everybody, I think using the expression language could be beneficial. @HolgerPeters it seems Cargo.toml and Cargo.lock are also inspired by Gemfile and Gemfile.lock. Anyway, please try to reuse another format when that's possible. |
Regarding
I don't agree that requirements.txt is simpler, when trying to manually edit requirements.txt I often find myself having to lookup the syntax to get it right. This is why I think a Python executable file is a better idea. In a Python executable file the syntax doesn't need to be learnt aside from the API. The benefits of having a language level API is that it can be loaded into document browser such as Dash and configured IDEs will autocomplete and let the user browse the docs directly in their editor. A well designed Python API is simpler and more discoverable than a YAML/TOML/JSON based syntax. |
@FRidh It is, but IIRC it also incorporates learnings from stuff that wasn't optimal in Gemfile and Gemfile.lock i.e. it improves upon them. |
I think, Pipfile should use configparser format and instead of using separate A [requirements]
sources =
https://simple.crate.io
https://pypi.python.org/simple/
require =
requests
Django ~= 1.4
Pinax = git://github.com/pinax/pinax.git?branch=1.4
crate = ~/blech
test = development
test2 = development, testing
groups.development =
blah
groups.testing =
pytest See also: https://pypi.org/project/d2to1/ |
Here's an example with an expression written in a functional language, inspired by Nix (actually, it is valid Nix).
So what do we have here? Our file is actually a function returning a set of environments. It takes one argument, In the We then use these items in another set ( which is the return value of this function) to define two environments. A function A functional language (like Nix) allows more than a serialization language (like JSON) while still being entirely declarative and requiring only a single file. |
To summarise the suggestions so far:
So far, no suggestions for RDF or Lisp S-expressions. ;-) |
there was also a suggestion on reddit: strict YAML |
Please, let's not repeat our mistakes again based on our bad habit of having too much power when specifying dependencies. Take a look at Haskell's Stack example to see it's fully declarative, easily parsable and simple: flags: {}
packages:
- '.'
- location:
git: https://github.com/serokell/universum
commit: 8e33495fd58c5443e0c2b0f1b6646516a47bd8d6
extra-dep: true
- location:
git: https://github.com/serokell/time-warp.git
commit: 1758ce25ab96f01e8979379e66dea3c7dae6c8c4
extra-dep: true
- location:
git: https://github.com/serokell/log-warper.git
commit: 5de577c3ab25e6f9a4350a9646050a88b2b8996e
extra-dep: true
- location:
git: https://github.com/serokell/acid-state.git
commit: 95fce1dbada62020a0b2d6aa2dd7e88eadd7214b
extra-dep: true
- location:
git: https://github.com/input-output-hk/pvss-haskell.git
commit: 1b898a222341116d210f2d3a5566028e14a335ae
extra-dep: true
- location:
git: https://github.com/serokell/kademlia.git
commit: 062053ed11b92c8e25d4d61ea943506fd0482fa6
extra-dep: true
extra-deps:
- pqueue-1.3.2
- data-msgpack-0.0.8
- time-units-1.0.0
- aeson-extra-0.4.0.0
- recursion-schemes-5
- QuickCheck-2.9.2
- cryptonite-openssl-0.2
- UtilityTM-0.0.4
- serokell-util-0.1.1.1
resolver: lts-7.9 The key part is I've failed to find a good reason (besides conditionals that can be part of the syntax) in this discussion for a full blown language and we'll regret this decision in 10 years. On paper the Python syntax looks clean, but as soon as people start being creative you'll get the real world. A mess always starts simple and clean. my 2c. |
As developer you want to specify which packages you need, but you might want to keep open the exact version because that changes over time. Even so, you want to share your current environment with others. Therefore, you want to take your initial spec ( @domenkozar, am I correct that in your example |
@FRidh I'm talking about the lock file indeed. That's the part which matters for any use including development. If you have a static file lock, your tool can update the file if you install a new package. The Let's say you want to add |
How would you deal with other interpreter versions and operating systems than the ones you used to |
There are only two real options on the table for the abstract dependency declarations: the Python-based DSL and TOML. Other config languages are out at the ecosystem level due either to simple inconsistency with the decision in PEP 518, or else for the specific reasons that PEP 518 chose TOML over the other alternatives that were considered. As pretty as I think the Python-based DSL is, I'm struggling to see it ending well given the other factors at play (most notably, the fact that we're dealing with a 25+ year old language ecosystem, and an 18+ year old packaging ecosystem):
User inertia is an incredibly powerful force, and we've seen plenty of ecosystem improvement efforts run into problems by failing to grant it adequate respect (most notably The other problem I haven't seen being considered so far with the Python-based DSL is "Which version of Python?". The examples given so far have all been Python 2/3 compatible, but what if someone wanted to write:
People doing things like that would mean that not only does a consumer of the file need to be using Python 3.6 to generate By contrast, if the baseline format for abstract dependency declarations is TOML 0.4.0, then folks that really want an imperative format can still do that (e.g. by defining a (Note that to facilitate the latter, we could potentially borrow a concept from |
From the Haskell perspective: Cabal files:
This means that in Stack files:
By the way XML had all of these properties twenty years ago, but sadly the syntax was too verbose for developers to like it. Now that there are formats like YAML maybe we can get it right this time. It should also be possible to do schema verification with XML tools by translating the YAML files to an XML tree and specifying a RelaxNG for that. Free candy for everyone. Edit: All points about YAML apply to TOML as well of course. They are basically the same from a high-level view. |
@ncoghlan as you mentioned, for those 0.1% of packages needing such complexity they can either: a) Have hooks to override the staticness and call a function to change flow of the logic b) Generate the config file in any possible way they want to I think the important bit here is, do we want to design something easy for 99.9% of packages and have knobs for the rest, or do we say let's support everything by turing completeness. Developing an DSL sounds like a very bad idea to me. Maybe an EDSL if that was possible in Python. It's hard to get traction and it's not trivial to implement and maintain. Also, you'll have to fight feature freak requests for years to come. |
@ncoghlan regarding the python version used don't all python project already have an explicit/implicit requirement/contract for what version(s) of python they support? Thus if you have a 3.6 project than 3.6 syntax would be okay in the |
For your consideration, a discussion we just had on #nixos.
|
|
The procedural Python-style format implies a state which is being updated, which allows for syntactically valid but semantically conflicting declarations:
By making groupings a higher-level property which collects package declarations the state becomes explicitly declared and conflicts are more obvious:
It makes sense to use this arrangement while taking advantage of the built-in set characteristics of the StrictYAML or TOML parsers. The verbosity introduced by nesting objects in TOML leaves a little to be desired. Perhaps an example in StrictYAML would be more readable. |
My 2c from maintaining some pre-commit hooks that deal with requirements.txt We maintain several tools which make dealing with application requirements easier:
For the most part, these tools would be easy to adapt to the new lockfile proposed as it is an easy to parse format (json) in a regular structure (and in an ideal world, probably obsoletes the sorting hook since pip would only generate sorted json in a regular structure). In the current world, these tools are very simple, mostly owing that to the dead simple nature of requirements.txt files (easy to parse, easy to manipulate, syntax is dead simple). As soon as we move to a python syntax (or python-like syntax) a number of issues arise:
I think the |
I think it's important to consider IDE and human readability implications of the Pipfile. In terms of IDE, if it's a Python-like syntax, the IDE (or text editor like Sublime) would have to embed an interpreter if it doesn't already have one, and would have to hope that the syntax stays valid for the foreseeable future (if, for example, there was a bug due to different Python versions, an end user would be very confused if their IDE gave them errors). This is probably not much of a concern, but something to be aware of. I think one big benefit to making the Pipfile easily parseable is that an IDE could infer requirements and alert the user that they're attempting to require something that isn't in their Pipfile. In terms of human readability, I know this may be contentious but I believe that a config file like Pipfile should be easily human readable and I don't think having them use a Python like syntax would accomplish this. It would mean another syntax to something that's already been solved over and over (YAML, TOML, INI...) and would just be another overhead to users who want to develop in Python on a bigger project. The Pipfile.lock can be in whatever format necessary because end users aren't going to really be touching it, but the Pipfile should be easily edited. Whichever config language is chosen, I think choosing a standard one like TOML or YAML would really benefit not only programmers but also project maintainers who will have to deal with Pipfile issues over the course of the project lifetime. Also, slightly off topic: please make importing from a git repo dead simple. I love that in |
I like the idea of using TOML. I've been playing around with Rust a lot lately and I find that it's package management is really quite pleasant. I think that the pipfile should be 100% declarative and be in a pre-existing format. I think allowing it to be executable will lead to a slippery slope of dirty hacks in order to get unsupported use cases to work. I think creating a new syntax, whether it be Python inspired or not, reduces readability and increases cognitive load on the developer. In my personal experience, I find that the standard Something along the lines of: # ~/requirements.TOML
[meta]
name = "my-application"
authors = ["John Doe <jdoe@email.com", "Jane Doe <janed@email.com>"]
license = "BSD2"
version = "1.1.0"
[default]
py-version = ">=3.4"
[production]
django = ">=1.9"
[dev]
requests = "latest"
django-debug-toolbar = "latest"
[legacy]
py-version = "<=2.7"
[production]
django = ">=1.8"
[dev]
requests = "<=2.0.0"
django-debug-toolbar = "<=1.5.0"
# Possible system dependency declarations? Though, I don't know how we would be
# able to reliably resolve these.
[system-deps]
ffmpeg = "any" |
Regarding redistributor consumption of these kinds of files:
While we can (and do) handle language specific dependency declaration formats by analysing them with tools written in those languages, it's much nicer (and typically more reliable) when we don't have to and can instead just use standard file format parsers (with TOML and JSON being preferred to XML and YAML due to the security problems arising with the latter pair in their default parsing configurations). That said, I don't think making our lives easier should be the primary consideration here - as open source consumer facing service providers we pull in the largest share of open source funding, and are hence better equipped than most to adapt to whatever upstream communities decide is in their best interests. Instead, I think the key point is the one raised by @asottile and others: that regardless of the format chosen Python developers are likely to want to write custom tools to help them manage their With TOML, the knowledge of how to perform safe automated file manipulations would be applicable to both |
This issue got decided. It is going to be a non-executable file. So, maybe
Oh, and I'm a +1 to getting a TOML format
(Damn, I pressed comment too early, this should have been 2 separate comments.) |
And let's not forget:
|
I took the liberty of opening an issue (#46) for discussion on the format of Pipfile. Hopefully, no one minds that. |
closing this for #46 |
There have been a number of issues/comments that tend to trace back to a single question.. Should
Pipfile
be executable or not?The relevant comments are:
There's also some reaction on https://www.reddit.com/r/Python/comments/5e2vci/pipfile_a_new_and_much_better_way_to_declare/
The text was updated successfully, but these errors were encountered: