From c07dabdb45631d8187c250dea0d86daf68b8f60f Mon Sep 17 00:00:00 2001 From: SkafteNicki Date: Thu, 26 Oct 2023 16:42:49 +0200 Subject: [PATCH] update conda module to be more general --- figures/conda_activate.PNG | Bin 0 -> 3498 bytes figures/standards.png | Bin 0 -> 24305 bytes mkdocs.yml | 1 + requirements.txt | 2 +- s1_development_environment/package_manager.md | 236 ++++++++++++++---- 5 files changed, 190 insertions(+), 49 deletions(-) create mode 100644 figures/conda_activate.PNG create mode 100644 figures/standards.png diff --git a/figures/conda_activate.PNG b/figures/conda_activate.PNG new file mode 100644 index 0000000000000000000000000000000000000000..1a82682a5b16e71335c82b979255110b02de5fbd GIT binary patch literal 3498 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 Z{~reG9>dZBmF55d002ovPDHLkV1j~{Q(gc7 literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml index 739601fc6..a03328657 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -49,6 +49,7 @@ markdown_extensions: - def_list - pymdownx.tasklist: custom_checkbox: true + - pymdownx.superfences plugins: - search diff --git a/requirements.txt b/requirements.txt index d8614d5ca..29b9f9788 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # Documentation -mkdocs-material>=9.1.6 +mkdocs-material>=9.4.6 mkdocs-glightbox>=0.3.2 mkdocs-material-extensions pymdown-extensions>=1.1.1 diff --git a/s1_development_environment/package_manager.md b/s1_development_environment/package_manager.md index 77f6c698d..12e7b0041 100644 --- a/s1_development_environment/package_manager.md +++ b/s1_development_environment/package_manager.md @@ -1,40 +1,159 @@ ![Logo](../figures/icons/conda.png){ align=right width="130"} -# Conda and virtual environments +# Package managers and virtual environments --- !!! info "Core Module" -You probably already have [conda](https://conda.io/projects/conda/en/latest/user-guide/getting-started.html) installed -on your laptop, which is great. Conda is an environment manager that helps you make sure that the dependencies of -different projects do not cross-contaminate each other. However, one thing is having conda installed, another is to -actually use it. +Python is a great programming language and this is mostly due to its vast ecosystem of packages. No matter what you want +to do, there is probably a package that can get you started. Just try to remember when the last time you wrote a program +only using the [python standard library](https://docs.python.org/3/library/index.html)? Probably never. For this reason, +we need a way to install third-party packages and this is where +[package managers](https://en.wikipedia.org/wiki/Package_manager) come into play. + +You have probably already used `pip` for the longest time, which is the default package manager for python. `pip` is +great for beginners but it is missing one essential feature that you will need as a developer or data scientist: +*virtual environments*. Virtual environments are an essential way to make sure that the dependencies of different +projects does not cross-contaminate each other. As an naive example, consider project A that requires `torch==1.3.0` and +project B that requires `torch==2.0`, then doing + +```bash +cd project_A # move to project A +pip install torch==1.3.0 # install old torch version +cd ../project_B # move to project B +pip install torch==2.0 # install new torch version +cd ../project_A # move back to project A +python main.py # try executing main script from project A +``` + +will mean that even though we are executing the main script from project A's folder, it will use `torch==2.0` instead of +`torch==1.3.0` because that is the last version we installed, because in both cases `pip` will install the package into +the same environment, in this case the global environment. Instead if we did something like: + +=== "Unix/macOS" -Before we get on with the exercises, it is probably important to mention the differences between `pip` and `conda`. -Here is a great [summary](https://www.anaconda.com/blog/understanding-conda-and-pip) but it essentially boils down -to this: - -* `pip` always install python packages (in the form of python wheels and distributions) whereas `conda` can - also install packages written in other languages because it installs from a binary file. -* `pip` installs dependencies in a serialized-recursive way, meaning that it can lead to dependencies issues, - because all other dependencies are ignored when we are installing the first and so on. On the other hand, `conda` - go over all dependencies in the beginning checking for compatibility before installing anything. -* `pip` is bound to a specific python version, whereas `conda` can manage multiple python versions at the same time + ```bash + cd project_A # move to project A + python -m venv env # create a virtual environment in project A + source env/bin/activate # activate that virtual environment + pip install torch==1.3.0 # install old torch version into the virtual environment belonging to project A + cd ../project_B # move to project B + python -m venv env # create a virtual environment in project B + source env/bin/activate # activate that virtual environment + pip install torch==2.0 # install new torch version into the virtual environment belonging to project B + cd ../project_A # move back to project A + python main.py # succeed in executing main script from project A + ``` -It is therefore highly recommended to use conda environments compared to python virtual environments. However, does that -mean that you cannot mix `pip install` and `conda install`? If you are using `conda>=4.6` then you should be fairly -safe, because it has build in compatible layer. In general, what works for me +=== "Windows" -* Use `conda` to create environments -* Use `pip` to install packages in that environment (with a few exceptions like `pytorch`) + ```bash + cd project_A # move to project A + python -m venv env # create a virtual environment in project A + .\env\Scripts\activate # activate that virtual environment + pip install torch==1.3.0 # install old torch version into the virtual environment belonging to project A + cd ../project_B # move to project B + python -m venv env # create a virtual environment in project B + .\env\Scripts\activate # activate that virtual environment + pip install torch==2.0 # install new torch version into the virtual environment belonging to project B + cd ../project_A # move back to project A + python main.py # succeed in executing main script from project A + ``` -Finally, it is worth mentioning that `pip` and `conda` are not the only two environment managers that exist for Python. -[Pipenv](https://pypi.org/project/pipenv/) is another alternative that is often used that has its own pros and cons -compared to other environment managers. +then we would be sure that `torch==1.3.0` is used when executing `main.py` in project A because we are using two +different virtual environments. In the above case we used the [venv module](https://docs.python.org/3/library/venv.html) +which is the build in python module for creating virtual environments. `venv+pip` is arguably a good combination +but when working on multiple projects it can quickly become a hassle to manage all the different +virtual environments yourself, remembering which python version to use, which packages to install and so on. + +For this reason, a number of package managers have been created that can help you manage your virtual environments and +dependencies, with some of the most popular being: + +* [conda](https://docs.conda.io/en/latest/) +* [pipenv](https://pipenv.pypa.io/en/latest/) +* [poetry](https://python-poetry.org/) +* [pipx](https://pypa.github.io/pipx/) +* [hatch](https://hatch.pypa.io/latest/) +* [pdm](https://pdm.fming.dev/latest/) + +with more being created every year ([rye](https://github.com/mitsuhiko/rye) is looking like a interesting project). This +is considered a problem in the python community, because it means that there is no standard way of managing +dependencies like in other languages like `npm` for `node.js` or `cargo` for `rust`. + +
+![Image](../figures/standards.png){ width="700" } +
Image credit
+
+ +In the course we do not care about which package manager you use, but we do care that you use one. If you are already +familiar with one package manager, then skip this exercise and continue to use that. The best recommendation that I can +give regarding package managers in general is to find one you like and then stick with it. A lot of time can be wasted +on trying to find the perfect package manager, but in the end they all do the same with some minor differences. + +If you are not familiar with any package managers, then we recommend that you use `conda` and `pip` for this course. You +probably already have [conda](https://conda.io/projects/conda/en/latest/user-guide/getting-started.html) installed +on your laptop, which is great. What conda especially does well, is that it allows you to create virtual environments +with different python versions, which can really be useful if you encounter dependencies that have not been updated in +a long time. In the course specifically we are going to recommend the following workflow + +* Use `conda` to create virtual environments with specific python versions +* Use `pip` to install packages in that environment + +Installing packages with `pip` inside `conda` environments have been considered a bad practice for a long time, but +since `conda>=4.6` it is considered safe to do so. The reason for this is that `conda` now has a build in compatibility +layer that makes sure that `pip` installed packages are compatible with the other packages installed in the environment. + +## Python dependencies + +Before we get started with the exercises, lets first talk a bit about python dependencies. One of the most common ways +to specify dependencies in the python community is through a `requirements.txt` file, which is a simple text file that +contains a list of all the packages that you want to install. The format allows you to specify the package name and +version number you want, with 7 different operators: + +```txt +package1 # any version +package2 == x.y.z # exact version +package3 >= x.y.z # at least version x.y.z +package4 > x.y.z # newer than version x.y.z +package4 <= x.y.z # at most version x.y.z +package5 < x.y.z # older than version x.y.z +package6 ~= x.y.z # install version newer than x.y.z and older than x.y+1 +``` + +In general all packages (should) follow the [semantic versioning](https://semver.org/) standard, which means that the +version number is split into three parts: `x.y.z` where `x` is the major version, `y` is the minor version and `z` is +the patch version. + +The reason that we need to specify the version number is that we want to make sure that we can reproduce our code at a +later point. If we do not specify the version number, then we are at the mercy of the package maintainer to not change +the API of the package. This is especially important when working with machine learning models, as we want to make sure +that we can reproduce the exact same model at a later point. + +Finally, we also need to discuss *dependency resolution*, which is the process of figuring out which packages are +compatible. This is a non-trivial problem, and there exists a lot of different algorithms for doing this. If you ever +have though that `pip` and `conda` is taking a long time to install something, then it is probably because it is trying +to figure out which packages are compatible with each other. For example, if you try to install + +```bash +pip install "matplotlib >= 3.8.0" "numpy <= 1.19" --dry-run +``` + +then it would simply fail, because there are no combination of `matplotlib` and `numpy` under the given version +constraints that are compatible with each other. In this case we would need to relax the constraints to something like + +```bash +pip install "matplotlib >= 3.8.0" "numpy <= 1.21" --dry-run +``` + +to make it work. ## ❔ Exercises +For hints regarding how to use `conda` you can checkout the +[cheat sheet](https://github.com/SkafteNicki/dtu_mlops/blob/main/s1_development_environment/exercise_files/conda_cheatsheet.pdf) +in the exercise folder. + 1. Download and install `conda`. You are free to either install full `conda` or the much simpler version `miniconda`. The core difference between the two packages is that `conda` already comes with a lot of packages that you would normally have to install with `miniconda`. The downside is that `conda` is an much larger package which can be a @@ -43,43 +162,42 @@ compared to other environment managers. system variable to [point to the conda installation](https://stackoverflow.com/questions/44597662/conda-command-is-not-recognized-on-windows-10) -2. Create a new conda environment for the remaining of the exercises using +2. If you have successfully install conda, then you should be able to execute the `conda` command in a terminal. - ```bash - conda create -n "my_environment" - ``` +
+ ![Image](../figures/conda_activate.PNG){ width="700" } +
+ + Conda will always tell you what environment you are currently in, indicated by the `(env_name)` in the prompt. By + default it will always start in the `(base)` environment. - We really recommend that you use multiple conda environment during the course to make sure you do not - mix dependencies between your exercises. +3. Try creating a new virtual environment. Make sure that it is called `my_enviroment` and that it install version + 3.11 of python. What command should you execute to do this? -3. When you create an environment with `conda`, how do you specify which python version it should be using? + ??? warning "Use python 3.8 or higher" - ??? warning "Use python 3.7 or higher" + We highly recommend that you use python 3.8 or higher for this course. In general, we recommend that you use + the second latest version of python that is available (currently python 3.11 as of writing this). This is + because the latest version of python is often not supported by all dependencies. You can always check the status + of different python version support [here](https://devguide.python.org/versions/). - We highly recommend that you use python 3.7 or higher for this course. In general, we recommend that you use - the second latest version of python that is available (currently python 3.10 as of writing this). This is - because many the latest version of python is often not supported by all packages. You can always check the - python version support [here](https://devguide.python.org/versions/). +4. Which `conda` commando gives you a list of all the environments that you have created? -4. Which `conda` commando gives you a list of the packages installed in the - current environment (HINT: check the `conda_cheatsheet.pdf` file in the `exercise_files` folder). +5. Which `conda` commando gives you a list of the packages installed in the current environment? 1. How do you easily export this list to a text file? Do this, and make sure you export it to - a file called `enviroment.txt`. + a file called `enviroment.yaml`, as conda uses another format by default than `pip`. - 2. Finally, inspect the file to see what is in it. + 2. Inspect the file to see what is in it. - 3. The `enviroment.txt` file you have created is one way to secure *reproducibility* between users, because - anyone should be able to get an exact copy of you environment if they have your `enviroment.txt` file. - Try creating a new environment directly from you `enviroment.txt` file and check that the packages being + 3. The `enviroment.yaml` file you have created is one way to secure *reproducibility* between users, because + anyone should be able to get an exact copy of you environment if they have your `enviroment.yaml` file. + Try creating a new environment directly from you `enviroment.yaml` file and check that the packages being installed exactly matches what you originally had. -5. Which `conda` commando gives you a list of all the environments that you have created? - -6. As the introduction states, it is fairly safe to use `pip` inside `conda` today. - What is the corresponding `pip` command that gives you a list of all `pip` installed packages? - and how to you export this to a file called `requirements.txt`? - (We will revisit requirement files at a later point) +6. As the introduction states, it is fairly safe to use `pip` inside `conda` today. What is the corresponding `pip` + command that gives you a list of all `pip` installed packages? and how to you export this to `requirements.txt` + file? 7. If you look through the requirements that both `pip` and `conda` produces then you will see that it is often filled with a lot more packages than what you are actually using in your project. What you are @@ -98,6 +216,28 @@ compared to other environment managers. What does the `requirements.txt` file `pipreqs` produce look like compared to the files produces by either `pip` or `conda`. +## 🧠 Knowledge check + +??? question "Knowledge question 1" + + Try executing the command + + ```bash + pip install "pytest < 4.6" pytest-cov==2.12.1 + ``` + + based on the error message you get, what would be a compatible way to install these? + + ??? success "Solution" + + As `pytess-cov==2.12.1` requires a version of `pytest` newer than `4.6`, we can simply change the command to be: + + ```bash + pip install "pytest >= 4.6" pytest-cov==2.12.1 + ``` + + but there of cause exists other solutions as well. + This ends the module on setting up virtual environments. While the methods mentioned in the exercises are great ways to construct requirements files automatic, sometimes it is just easier to manually sit down and create the files as you in that way secure that only the most necessary requirements are actually installed when creating a new environment.