Edit: I don't mean to disparage projects like this and pipfile. Both are great efforts to bring the packaging interface in line with what's available in other languages, and might be the only way up and out of the current state of affairs.
Edit: I don't mean to disparage projects like this and pipfile. Both are great efforts to bring the packaging interface in line with what's available in other languages, and might be the only way up and out of the current state of affairs.
From binary wheels (including on different linux architectures), to things like local caching of packages (taking LOTS of load off the main servers). To the organisation github of pypa [0], to `python -m venv` working.
Also lots of work around standardising things in peps, and writing documentation for people.
I would like to applaud all the hard work people have done over the years on python packaging. It really is quite nice these days, and I look forward to all the improvements coming up (like pipenv!).
I'd suggest people checkout fades [1] (for running scripts and automatically downloading dependencies in a venv), as well as conda [2] the alternative package manager.
[1] https://fades.readthedocs.io/en/release-5/readme.html#what-d...
Now it's hard to compete with JS on some stuff : it's the only language in the most popular dev plateform (the web) and it has one implicit standardized async model by default.
It's hard to compete with rust on some stuff : it's compiled and is fast, can provide stand alone binaries easily and has a checker that can avoid many bugs.
But this. The package manager. We can compete. And yet we are late.
It's partially my fault since it's a project I had in mind for years and never took the time to work on. It's partially everybody's fault I guess :)
On top of all that, npm itself just does so many things right. It's quite happy to install from npm repos, from dvcs repos, from regular directories, or from anything that looks like a directory. It just needs to find a single file called "package.json". It requires no build step to prepare a module for upload to an npm repo, but it easily allows for one if that's necessary. package.json itself is basically declarative, but provides scripting hooks for imperative actions if necessary. At every opportunity, npm allows devs to do what they need to do, the easy way.
In a sense, node and npm are victims of their own quality. The types of "issues" (e.g. too many deps, too many layers of deps, too many versions of a particular dep, deps that are too trivial, etc.) about which anal code puritans complain with respect to node simply couldn't arise on other platforms, because dependency hell would cause the tower of module dependencies to collapse first. node happily chugs along, blithely ignoring the "problems".
Personally, I used to be able to build python packages for distribution, but since I've been spoiled by node and npm for several years I've found I simply can't do that for python anymore. It is so much harder.
> it finds modules in a bottom-up hierarchical fashion. That allows a single app or module to use modules that in turn use different versions of other modules, and not to worry about what other modules are doing
To my understanding, if your app transitively depends on package foo-1.2 in thirty different places [0], there will be thirty copies of foo-1.2 on disk under node_modules/ . Each package reads its very own copy of foo-1.2 when it require()s foo.
On a large app, that adds up to a lot of inodes ("why does it say my filesystem is full? there's only 10G of stuff on my 80G partition!" because it's used up all its inodes, not its bytes.) and a _lot_ of unnecessary I/O. The second through thirtieth copies of foo-1.2 don't come from the kernel's block cache "for free", they come from spinning rust (or if you're lucky, the dwindling number of IOps your SSD can choke out. Do you pay money for provisioned IOps?).
[0] and thirty is a lowball number for some projects, especially given the community's preference to require "leftpad" or whatever instead of writing a couple lines in their own projects
Haha npm@3 was out June 2015. b^)
I agree that it would have been better, on balance, for previous versions to have created hard links to already-installed modules. Actually that wouldn't be a bad option to have even now, since debugging is often easier when one has a deep directory structure to explore rather than hundreds of random names in the top-level node_modules directory. That is, if I know the problem is in module foo, I can pushd to node_modules/foo, find the problematic submodule again, and repeat until I get all the way to the bottom. [EDIT: it occurs to me that having all these hard links would make e.g. dependency version updates easier, since un-updated dependencies wouldn't have to be recopied, unix-stow-style.]
To me, the more amusing file descriptor problem is caused by the module "chokidar", which when used in naive fashion tries to set up watches on all 360 files and directories created by itself and its own 55 dependencies. At that point it's real easy to run out of file watches altogether. Some of the utilities that call chokidar do so while ignoring node_modules, but many do not.
Subsequent versions of NPM continue to improve upon this flattening effort.
Verses node which makes horribly assumptions that allow dependencies of libraries to be resolved and dumped into the same library namespace as your application. It also allows users to use the shrinkwrap feature, which imposes the dependency's settings on me (say, for example, I want to use my own npmjs proxy, this is bypassed...)
Recently, I built a small CLI tool in Python, and learned all of the bits needed to build, test and package my application "the right way". I knew Python syntax before, but it was a lot of effort to set this up. The difference in the experience between Python and Rust or .NET Core is actually shocking, and most it isn't down to anything that Python couldn't do, just the current state of the tooling.
Rust: type "cargo new", README and doc comments in Markdown, type "cargo test" and "cargo build".
I'm being deliberately snarky, but you get the point: there has been a slow accretion of complexity over a very long time, and most of it is not the language itself.
The Python best practice: you do not need "correct directory structure" to start programming in Python--there is no boilerplate structure to create (and that would require a tool): REPL, Jupyter notebook in a browser, a single file (module) is enough in a simple case.
There is no single "correct directory structure" for more complex cases either e.g., a Django web app and Ubuntu's Appindicator would probably have different structures and it is likely that there are tools that create corresponding project skeletons such as "django-admin startproject mysite".
Usually, the packaging/deploying are not nice & neat and differ depending on your project, chosen platform--either you follow an example or you have to study a never ending list of incomplete/contradicting standards/semi-documented rules & conventions--it is unrelated to Python (even if tools such as Ansible, Salt are implemented in Python). Naturally, there are things that can be improved.
btw, reStructuredText is a great text format for documenting Python projects (it scales from a simple README.rst to a whole book). You can always convert README.md written in your favorite Markdown flavour using pandoc or a similar tool. Markdown is great for a short human-generated plaintext content common on Github, Stack Overflow.
Rhetorical question: is Python a scripting language or an applications language?
If it's a scripting language, then yes, a single file is all that you need. Otherwise, you need a directory structure with various support files to be able to create and build your CLI tool or library, and you need to follow conventions if you are going to publish your work as Open Source. Which in turn requires you to know which tools to use, and what they require.
Go raised the bar here, I think. Rust and .NET Core both follow the Go idea of having one top level tool that gives you what you need to test and package your library or CLI application. That means that new users can get success with at least two basic use cases very rapidly and with little effort. Which in turn means that the previous status quo is no longer so tolerable.
> btw, reStructuredText is a great text format for documenting Python projects (it scales from a simple README.rst to a whole book).
Sure, the big problem with RST is that it lost the fight. Every developer who uses GitHub has to know a little Markdown, not RST or something else. For larger documents, AsciiDoc is probably the thing. I used to be a Textile loyalist, myself, but stopped fighting against the tide and switched to Markdown some years ago.
You've ignored "in a simple case" and ignored the next paragraph that discussed "more complex cases." If I have a task: publish Ubuntu PPA with a package that does X, or deploy a web app on a PaaS, or add a component that massages some data for a numerical simulation on a cluster or something else. I do not see what Go, Rust, .NET do here that Python can't.
Obviously, there could be specific use-cases where some language/framework has advantages over other languages/frameworks and (as I said) there may be use-cases that can be improved that why they should be named explicitly -- that is why I've asked in the first place -- so far I don't see "shoking difference" in the general case (I don't doubt that you had experienced the shoking difference for your specific (unnamed) task).
Please bear in mind here that I did this because I wanted to learn and follow current best practices, with a project that I specifically picked because it would not require 3rd-party libraries or big frameworks to muddle things.
I don't think that it was dissatisfying because Python is a general-purpose programming language, but because the process currently happens to involve an assortment of different tools, piecing together scattered information, and being willing to suffer a few paper cuts like Python versions and multiple old file formats. That was my experience building a simple Python 3 application, and it didn't leave me eager to do it again.
We can discuss how Go-style tooling and packaging can also help with the cases that you mentioned, but I didn't really want to stray off into an extended analysis. My original points were just that the overall developer experience that new Python users currently get compares poorly to some other languages, and that is totally fixable. I hope that things do improve, and feel that Pipenv is a very welcome move in that direction.
What is so bad about "a multi-file Python project that could act as a library as well as a CLI, and be packaged as a wheel for PyPI."
Put __init__.py in a directory and you've created a Python package where you could add as many modules including other packages as you like.
Copy-paste setup.py ("pip init" like "npm init" would help here), update it with your info and to upload a wheel to PyPI, run:
$ pip install twine
$ python setup.py sdist bdist_wheel
$ twine upload dist/*
(you might need "twine register". "pip publish" would be nice).I didn't read it but as far as I understand all of this is on one page https://packaging.python.org/distributing/
I gave you a breakdown in my earlier comment, but I'm thinking that writing more isn't going to make this better:
* Your experience: Python does everything that you need, and quickly, because you are already very proficient in the language and the tools. * My experience: Shopping for a programming language, knows Python syntax but no attachment beyond that, has tried out a number of languages and has working knowledge of their tooling.
I'm not suggesting that Python is a bad language, just that I think that the tooling and developer experience for new users is not competitive with some other languages. If you are proficient in Python, and haven't tried other languages recently, you won't have experienced the jump: Python is slightly more hard work than Ruby or Node.js, but Rust, Go and .NET Core have a level of slickness and integration that older platforms just don't have.
> What is so bad about "a multi-file Python project that could act as a library as well as a CLI, and be packaged as a wheel for PyPI."
Again, see my previous comment for a list, including some bits that you are omitting. I found that document and read it, but it assumes that you have already created something with docs and tests (laid out in an appropriate directory structure). Since I didn't actually submit the package to PyPI, I didn't have to think about twine, but that's another paper cut. Which is my main point: if someone is not already sold on Python, every small quirk and special case becomes another obstacle, and the Python tooling has collected more than a few.
Could you provide an explicit list of what do you mean by "a breakdown in my earlier comment"? (obviously, I wouldn't ask for more information in my comments if your previous comments were specific enough) e.g., in language X I can do Y using command Z but Python requires A,B,C instead (implying that Z is "shockingly" better than doing A,B,C).
As I understand, the issue is that you want the structure of a project in a language X to be generated for you by a single command (whatever the project) or that this command must be available by default.
I've googled "python create command-line project template":
$ pip install -U cookiecutter
$ cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git
I don't remember using this tool but If you like a machine-generated configuration; it might be the start.Please try installing Rust with the rustup tool, creating a "Hello world" skeleton CLI tool, and compiling a deployable executable for it. It will take you (not joking) perhaps 10 minutes or less if you have never done anything with Rust before. Then look at how you would add docs and tests. The Rust language is not simple, but the tools are really good. Try the same exercise with Go if you prefer.
If you are willing to give this a try and invest the 10 minutes, you'll understand why I didn't use the phrase "shocking" casually: listing off features doesn't really convey how huge the usability gap is.
> Could you provide an explicit list of what do you mean by "a breakdown in my earlier comment"?
It was this:
> "Python best practice: figure out the correct directory structure by reading docs and looking at GitHub repositories, learn how to write setup.py & setup.cfg & requirements.txt & MANIFEST.in files, setup py.test and tox (because Python 2 still lives), write your README in RST format (as used by nothing else ever), and for bonus points: write your own Makefile. Get depressed when you realize that target platforms either don't have Python or have the wrong version."
I'm curious what I've been missing about pip that makes it problematic - I've never used the other tools you mentioned (setuptools/distutils/ez_install) - so I can't comment on them, but, on the flip side, I've never had to use them, so maybe my requirements are somewhat more simple than yours.
Another thing is providing a stand alone build. Something you can just ship without asking the client to run commands in the terminal to make it work. I use nuikta (http://nuitka.net/) for this. It's a fantastic project, but man it's a lot of work for something that works out of the box in Go or Rust.
One last thing is to generate packages for OS (msi/deb/rpm/dmg/snap). Your sysadmin will like you. Pex (https://pypi.python.org/pypi/pex) is the closest, but not very standard.
Other pet peeves of mine:
- you can't easily move virtualenvs;
- creating a setup.py is very hard for a beginner and has numerous traps;
- setup.py are executable files. Meh.
- what's with this setup.cfg containing 2 lines ? And the MANIFEST.IN being a separate files. Why do I have to put conf also in tox.ini ? And one for each of my linters ? I want ONE setup.cfg file with all config for all tools for my project inside and be done with it. TOML can handle rich sections, just stop creating new files.
- accessing file with pkg_resources() is way harder than it should be. I made a wrapper for this (http://sametmax.com/embarquer-un-fichier-non-python-propreme...).
- one place to have __version__, please. I want it readable in my code AND in my package metadata, without having to use regex or have side effects on imports.
- remove the "can't build wheel message" when it's useless. It scares newcomers.
- README is the long_description. Don't make me read it manually.
- how do I provide vendors in a clean way ?
- install_requires, extras_requires, setup_requires, tests_requires... Make it one require with hooks and tags and be done with it.
- creating a setup.py test config is way harder than it should be and breaks in CI on strange edge cases.
- can we get a PEP on the standard project structure and built it in our tools to be done with it? We all have src/package + setup.py on root anyway.
- pip installs packages in the site-packages dir of the python executable it's installed for. It makes sense, and I think Python deals pretty well with the fact you can have various versions installed on the same machine. But God people are confused by this. Now you can recommend to do "python -m pip", but it's very verbose and it assumes people know what version of Python is behind the "python" executable. On windows it can be any, and they must chose with... yet another command ("py")! pipenv just bypass that by assuming you want a virtualenv, and be able to access it. It's a very good call.
- pip install --user will create commands you can't use unless you edit your PATH. This makes newcomers go mad.
Pipenv shows its pedigree and looks like a great tool...that also overlaps significantly with conda. What are the use cases that Pipenv addresses better than/in lieu of conda?
That said pipenv still looks awesome. Any improvement to the python packaging world is welcome gift.
- one for graph deps;
- one for packaging;
- one for managing your project.
Then you can let people like Kenneth build a big friendly wrapper on top of it.
The only problem with those is that it must be pure Python. Otherwise you will have problems. Wheel are not bullet proof, and while you can get away with tinkering for dependencies, you can't with your package manager. It should work out of the box.
But creating "cargo for Python" is a very, very hard job. And nobody will remember you for it.
There's pip-tools.
That doesn't guaranty your setup will work.
Dependency management suppose to create a graph of all requirements, lower and upper versions bound for the runtime and the libs, and find the most up to date combination of those.
If a combination can't be found, it should let you know that either you can't upgrade, or suggest alternative upgrade paths.
pip-tools will just happily upgrade your package and let you with something broken, because it's based on pip which does that. They don't check mutually exclusive dependencies versions, deprecation, runtime compatibility and such. And they don't build a graph of their relations.
Something that would say: "You can safely upgrade to Django 1.9.12. Upgrading to latest Django 1.10.5 breaks 20 tests."
Nothing guarantees your setup will work.
Note, it doesn't feel terrible in Python land, to me at least. But it was almost a joy working with Perl's packaging system.
That's been the default behaviour of Perl package managers for a long, long time now.
The whole setup.py/setup.cfg situation really is ridiculous. Having to import the __version__, read() the README, no markdown support on pypi, MANIFEST / MANIFEST.in files, tox.ini, what a mess.
> - you can't easily move virtualenvs;
`virtualenv --relocatable`, though it's weird that it's not the default, yes.
you can't easily move virtualenvs
I'm not sure whether it qualifies as easy, but you can use virtualenv-mvWhy exe? How do you package libraries using this new tool you are envisioning?
But a good package manager should ALSO allow you to produce a:
- a stand alone executable for most OS.
- a standard package for major OS (msi, deb, snap, rpm, dmg, etc).
Doing that right now with Python requires you to setup stuff like nuikta and the like. It works but it's much harder than it should be.
Windows and OS X are always a pain in the ass, but that's more an issue of the platforms lacking in package management than anything else.
Now pipenv centralize stuff we were doing anyway.
We should have a tool to centralize those as well.
Do you have a setup.py? No, then it's not going to be on PyPi anyway so ignore the rest of this since you'll need to figure out how to package this special snowflake.
0. Run `yum install -y rpmdevtools mock`
1. Run `rpmdev-newspec python-mypackage`.
2. Run `python setup.py sdist`
3. Modify the .spec as appropriate to add dependencies the package requires as well as set the version correctly.
4. Run `mock -r fedora-25-x86_64 --buildsprm python-mypackage.spec`
5. Run `mock -r fedora-25-x86_64 --rebuild python-mypackage.f25.src.rpm`
Done, there's your RPM, debian packaging is roughly as simple (I'm less a fan because of how they try to fudge a Makefile into an adequate tool for package building, but hey, most everything can be built automatically by dh so the average makefile is ~3 lines).
The biggest problem with learning this is simply finding the right documentation, Fedora has some pretty great guides for new packagers on the wiki.
Also notable, IMO, is the lack of a tool like rbenv or rustup for python. I can't tell you how many times I have had to try to figure out which python version a given pip executable worked with.
pip does cache the wheels so instead of moving the virtualenvs around, just recreate them. This also ensures the virtualenv is up to date. Using tox this is fairly easy to do.
Sure virtualenv is a bit of a hack but it's not that bad.
Does pyenv not meet your needs there?
It also has plugins to automatically work with venv, if you don't mind some 'magic' in your workflow.
Overall it's a solid setup.
Node would be the last place I'd look for a good solution in. Not sure if there was some progress recently, but it was hell some time back. Modules were huge, taking thousands of other modules with them, majority of those being duplicates. There was no deduplication, no version wildcards I believe either. It wouldn't even work with some tools because the path would end up being hundreds of characters long.
There have always been version wildcards as far as I know. Long paths caused by the deeply nested tree were a problem in Windows only, addressed (I believe, I can't find open issues on it) by the flattening in npm 3.
I've tried relocating node_modules. It's a recipe for pain and tears.
I don't see why it's a big problem that virtualenv is a package rather than built in.
I also haven't had much of a problem with virtualenv not being relocatable. If you want it somewhere else, just build it there.
>Also notable, IMO, is the lack of a tool like rbenv
Ummmmm the creator of rbenv also created pyenv.
venv is in the stdlib since 3.3. (Though I agree with the annoyance at the need.)
I'd ask for your reasoning but it seems sametmax has done a good job of that for you:
A more controversial statement with the same content probably wouldn't have been voted up, but thread parent is objectively true even though it doesn't contain its own proof.
Ha, that's what I came here to say!
Or better - a new packaging paradigm.
Maybe it's extremely uncool to say ... but I think Java still has the best packaging paradigm of all languages. Jars rule. Of course 'gradle' is kind of a confusing mess so they don't have dependencies worked out very well ...
Nevertheless I do feel that Python's packaging and dependency/versioning woes create a much bigger systematic problem than many realize.
Kudos to the author though ...