My Favorite Package Managers: A Comparison Between Pip and Pkg | by Emmett Boudreau | Towards Data Science

There are a lot of tools that alter the usage of a programming language and can set it apart from many of its competitors. One great example of something that is going to be used a lot by programmers and has a huge effect on their experience in your language is the package manager. There are a lot of very cool and interesting package-management solutions that have come out of the programming world, and this is especially true of scripting languages like Python and Julia. Both of these languages have pretty cool package managers, PIP and Pkg respectively, that I think have some pretty interesting solutions to a lot of the problems that can often be faced with dependency management and package installation. Today I wanted to take a step back and point out some features I like about the two because I think they are both pretty interesting in the way that they solve certain problems of computing.
The first and most obvious thing to look at when it comes to package managers like this is the Command Line Interface, or CLI, of both packages. The CLI is how most users are going to be interacting with the package manager, and it is certainly an important aspect to the overall user experience of both the programming language in question as well as how its users interact with its packages. Right out of the gate, we see an enormous difference between PIP and Pkg.
The PIP package manager is typically installed separately from the Python programming language itself. Furthermore, it is typically called from Bash on Unix-like systems. Typically in order to install a Python package globally, we would run something like:
sudo pip install numpy
On the other hand, with Pkg we have two options. In Julia, the Pkg package manager is typically used from within the language itself, rather than as a separately packaged application. For this, Julia includes both a Pkg REPL for managing packages and includes the package Pkg. These are both included with a base installation of Julia, creating a clear distinction between its integration into the language and PIP’s integration into Python. This means that we usually are going to access it using Julia, rather than Bash to call on an application. We can do this by either entering the Julia REPL and pressing ] to enter the Pkg REPL, or alternatively by calling the package with Julia.
juliajulia> ]pkg> add DataFramesjulia> using Pkgjulia> Pkg.add("DataFrames")
I think that both of these approaches are pretty cool. I enjoy adding packages from the Pkg REPL. Another cool factor in this regard is the ability to call using on the Pkg package and create an entire environment from within Julia. It makes it rather convenient to create environments from within packages, which is something I do like. I think that the PIP solution of calling it as a separate application is also convenient because it does make it really easy to add a package very quickly from your terminal. Of course, I cannot speak on how this works on operating systems that are not Unix-like, but on my Linux machine it is certainly a great implementation and a great way to work with packages in Python.
An other important aspect of package managers to look at is how packages are managed for individual projects. For this, we use something called a virtual environment, which makes it relatively easy to set up and load dependencies for any package regardless of the versions required for the package you are currently working with for your package, regardless of what they are. For PIP, we have Pipenv. Using Pipenv, which is packaged with PIP, you can effectively load your project dependencies using
pip install -r requirements.txt
I think this is a pretty great solution, however, it does take somewhat of a back-seat to Pkg’s approach in my opinion. This is because Pkg automates the entire process of creating virtual environments using the Project.toml file. As opposed to Python’s approach, the Julian approach to package management is incredibly organized and universally consistent. Using Pkg.activate() and Pkg.generate(), we can create new environments a lot more easily with the dependencies loaded into that environment already ending up into that package.
Regardless of what high-level programming language you are working in, package managers are incredibly important to getting the job done. Of course, not all package managers are created equal, and there are definitely some things that are better about one package manager or the other. I think that regardless as to the package manager you use, it is pretty cool and interesting to compare the package managers with one another and work with each one. I think there are some great ideas that come from both of these implementations, and both could certainly learn a few things by examining each other.