AI Computer Institute
Expert-curated CS & AI curriculum aligned to CBSE standards. A bharath.ai initiative. About Us

Pip and Package Management: Standing on Giants

📚 Technology⏱️ 19 min read🎓 Grade 8
✍️ AI Computer Institute Editorial Team Updated: August 2026 CBSE-aligned · Peer-reviewed · 19 min read
Content curated by subject matter experts with IIT/NIT backgrounds. All chapters are fact-checked against official CBSE/NCERT syllabi.

The Problem: Building a QR Code From Scratch

Suppose you want to write a Python program that generates a QR code for a UPI payment — the kind of black-and-white square you scan at a tea stall or paste into a school fee-receipt. Sounds like a fun weekend project. Now think about what the program actually has to do. It must take your text (something like upi://pay?pa=school@upi&am=2500), convert every character into binary using the correct encoding mode, break that binary into data blocks, run a mathematical error-correction algorithm called Reed–Solomon coding so the code still scans even if a corner is torn or smudged, arrange the result into a square grid following a precise placement pattern for the finder squares and timing strips, and finally draw that grid as an image file. Each of those steps is a serious piece of applied mathematics and image processing. A strong programmer, working alone, could easily spend two or three weeks getting this right — and even then, a single subtle bug in the error-correction step could produce codes that fail to scan on some phones.

Here is the twist: you do not have to write a single line of that. Someone has already solved this exact problem, tested it against thousands of real scanners, and published the solution for free. You can use it with two words typed into a terminal:

pip install qrcode[pil]

That single line downloads a working, tested QR-code generator onto your computer. This chapter is about understanding exactly what happens when you type that line — what a "package" is, what pip does, where the code comes from, and how thousands of programs like this stay organized without breaking each other. This is what "standing on giants" means in programming: you build your program on top of code that other, often far more specialized, programmers have already perfected.

What Exactly Is a "Package"?

Start with the smallest unit and work up, because these words are used loosely even by working programmers, and mixing them up causes real confusion.

  • A module is a single Python file. If you save some functions in a file called helpers.py, that file is a module. Anywhere else you can write import helpers to use its functions.
  • A package is a folder containing multiple related modules, plus a special file that tells Python "treat this folder as one unit." A package groups related functionality together — for example, a package named qrcode might internally contain separate modules for encoding, error correction, and image drawing, but you interact with it as one thing.
  • A distribution (informally, still often just called a "package") is what actually gets uploaded, downloaded, and installed — a compressed archive containing one or more Python packages plus metadata like the version number, the author, and a list of other distributions it depends on.

In everyday conversation — and in this chapter — "package" usually means the third meaning: an installable unit of code with a name and a version, like qrcode version 7.4.2. That distinction matters later when we talk about dependencies, because a distribution can depend on other distributions, not just contain code.

Where Packages Live: PyPI

Third-party Python packages are hosted on a central website called the Python Package Index, or PyPI, at pypi.org. Anyone in the world can create an account and upload a package to PyPI — there is no committee that reviews code for quality before it goes live. This is a deliberate design choice: it keeps the barrier to publishing low, which is a big part of why the Python ecosystem grew so large. It also has a consequence we will return to later.

PyPI launched in 2003. Its original internal codename was "the Cheeseshop" — a nod to a Monty Python's Flying Circus sketch, since the Python programming language itself is named after that comedy troupe, not the snake. As of today PyPI hosts several hundred thousand distinct packages, covering everything from QR-code generation to weather-data parsing to machine learning.

PyPI is just a catalogue and file server, though — it does not install anything on your computer by itself. That job belongs to a separate tool.

Meet pip

pip is the command-line program that talks to PyPI on your behalf: it searches for the package you named, checks which version to fetch, downloads the distribution file, and unpacks it into the right folder on your computer so that Python's import statement can find it. The name pip is a recursive backronym — it officially stands for "Pip Installs Packages," where the first word of the phrase is the tool's own name. It was created by developer Ian Bicking and first released in 2008 as a replacement for an earlier, more limited installer called easy_install. Since Python version 3.4 (released in 2014), pip has shipped bundled with every standard Python installation, so on most computers you do not need to install pip separately — it is already there the moment Python is.

You run pip from a terminal (also called a command prompt or shell) — a text-based window where you type commands instead of clicking icons. On Windows this is typically Command Prompt or PowerShell; on macOS or Linux it is called Terminal. You do not write pip commands inside a .py file — they are typed directly at the terminal prompt.

A Worked Trace: Installing a Real Package

Let's install the QR-code package and watch, line by line, what pip actually does. Typing this at the terminal:

pip install qrcode[pil]

produces output that looks roughly like this (the exact version numbers you see will differ depending on when you run it, since packages are updated constantly):

Collecting qrcode[pil]
  Downloading qrcode-7.4.2-py3-none-any.whl (46 kB)
Collecting pillow
  Downloading pillow-10.3.0-cp311-cp311-win_amd64.whl (2.5 MB)
Installing collected packages: pillow, qrcode
Successfully installed pillow-10.3.0 qrcode-7.4.2

Trace what each line means:

  1. Collecting qrcode[pil] — pip looked up the name qrcode on PyPI and found a match. The [pil] is called an "extra" — it tells pip "also install whatever qrcode needs to work with images, specifically the Pillow library."
  2. Downloading qrcode-7.4.2... — pip fetched the actual file, a compressed archive called a wheel (file extension .whl), about 46 kilobytes.
  3. Collecting pillow / Downloading pillow-10.3.0... — pip noticed that qrcode's own metadata listed Pillow as something it needs, so pip fetched that too, automatically, without you asking for it by name.
  4. Installing collected packages: pillow, qrcode — notice the order. Pillow is installed before qrcode, because qrcode depends on Pillow already being present.
  5. Successfully installed... — confirmation, with the exact version of each package now sitting on your disk.

Now the code that took two or three weeks to write from scratch takes four lines:

import qrcode

upi_link = "upi://pay?pa=school@upi&pn=SchoolFees&am=2500&cu=INR"
img = qrcode.make(upi_link)
img.save("fees_qr.png")

Run this file and a real, scannable QR code appears as fees_qr.png in the same folder. The import qrcode line is doing something different from the pip install line, and mixing these two up is the single most common beginner confusion — worth pausing on before going further.

Common Misconception: "import" Does Not Install Anything

Misconception: Many students believe import qrcode is what fetches the package, and that if it fails, you fix it by editing the import line. This is backwards. pip install is a one-time (per computer, or per project) action that copies code onto your hard disk. import is something your program does every single time it runs — it loads code that is already sitting on disk into your program's memory. If you skip the install step and go straight to import qrcode, Python searches its known folders, finds nothing named qrcode, and raises ModuleNotFoundError: No module named 'qrcode'. The fix is never to change the import statement — it is to run pip install qrcode first. Think of it like a library book: pip install is borrowing the book and placing it on your shelf at home; import is opening it to read, which you can do many times, but only after it is actually on your shelf.

Standing on Giants: The Dependency Tree

Go back to the install trace above. You asked pip for one thing — qrcode — but pip installed two things, because qrcode itself is built on top of Pillow (a general-purpose image-manipulation library) rather than reimplementing image drawing from scratch. This is dependency resolution: pip reads the metadata of the package you asked for, finds every other package it lists as required, downloads those too, and repeats the process for each of those in turn until nothing new is needed. The result is a tree, with your program at the root and increasingly fundamental building blocks toward the leaves.

PyPI — pypi.org central index of published packages pip install qrcode[pil] pip resolves & fetches qrcode 7.4.2 the package you asked for (needs Pillow to draw images) Pillow 10.3.0 a dependency qrcode needs installed automatically, unasked site-packages/ both packages now sit on your disk import qrcode

Real-world dependency trees can go much deeper than this two-level example. The data-analysis package pandas, for instance, depends on numpy (for fast numeric arrays), which in turn may rely on low-level compiled math libraries. When you run pip install pandas, pip is silently resolving and fetching all of that on your behalf — potentially a dozen packages from one command. This is enormously convenient, but it also means one command can pull in a surprising amount of code, which is worth remembering when you think about what you are trusting.

Semantic Versioning: Reading 7.4.2

Every package on PyPI has a version number, almost always written as three numbers separated by dots: MAJOR.MINOR.PATCH. Take qrcode's version 7.4.2 apart:

  • MAJOR = 7 — the seventh major redesign of the package. A change here means the way you call the code may have changed in a way that breaks old programs using it.
  • MINOR = 4 — new features have been added four times since major version 7 started, but in a way that does not break programs written against version 7.0, 7.1, 7.2, or 7.3.
  • PATCH = 2 — two bug fixes have been released since 7.4.0, with no new features and no behaviour changes beyond fixing mistakes.

This convention is called semantic versioning, and it lets pip make safe automatic choices. When your project's requirements file says a package should be compatible with 7.4, pip can safely upgrade to 7.4.9 (bug fixes only, low risk) but should not silently jump to 8.0.0 (a major version, which might break your code).

You can tell pip exactly how strict to be using version specifiers, usually saved in a plain text file named requirements.txt so an entire project's dependencies can be installed with one command:

qrcode==7.4.2
pillow>=10.0
requests~=2.31.0

Reading these left to right: ==7.4.2 means exactly that version, no substitutes — useful when you need perfectly reproducible behaviour. >=10.0 means that version or any newer one — useful when you just want recent bug fixes. ~=2.31.0 is the "compatible release" operator: it means "at least 2.31.0, but strictly less than 2.32.0" — patch-level updates only, keeping the minor version locked. Installing everything a project needs, matching these exact rules, is then just:

pip install -r requirements.txt

Why the Same Project Should Not Share Packages With Every Other Project

Imagine two school projects on the same laptop. Project A was written two years ago and only works correctly with numpy==1.19.5, because it uses a function that newer numpy versions removed. Project B, started this month, needs a feature only available in numpy>=1.26. If both projects installed packages into one shared, computer-wide location, installing numpy for Project B would silently break Project A — there is only one numpy on the machine, and it can only be one version at a time.

The standard fix is a virtual environment: a self-contained, isolated folder with its own private site-packages, separate from every other project's. You create one per project:

python -m venv myenv
myenv\Scripts\activate
pip install numpy==1.19.5

(On macOS or Linux, the activation line is source myenv/bin/activate instead.) Once activated, any pip install you run installs only inside that folder, invisible to every other project. Project A and Project B can each have their own myenv, each pinned to the numpy version they actually need, and neither one ever sees or disturbs the other's copy.

Common Misconception: Anyone Can Publish to PyPI

Because pip fetches from a central, official-sounding website, students often assume every package on it has been reviewed for correctness or safety by some authority — the way an app on a school-approved list might be checked before distribution. That assumption is false. PyPI performs largely automated checks (mainly that the package name and file format are valid); there is no manual human review of the code inside every upload. This has a genuine consequence called typosquatting: someone can publish a package named almost identically to a popular one — say reqeusts instead of requests — hoping a programmer will mistype the real name and install the fake one instead. The lesson is not "avoid pip" but "type package names carefully, prefer packages with a long history and many existing users, and read what a package claims to do before trusting it with sensitive data."

A Cheat Sheet of pip Commands

  • pip install <name> — download and install the newest compatible version of a package.
  • pip install <name>==<version> — install one specific, exact version.
  • pip list — show every package currently installed in the active environment, with its version.
  • pip show <name> — display details about one installed package: its version, where it is stored, and what it depends on.
  • pip uninstall <name> — remove a package.
  • pip freeze > requirements.txt — write every currently installed package and its exact version into a text file, so someone else (or future-you, on a different computer) can recreate the identical environment with pip install -r requirements.txt.

Active Recall

  1. You run import matplotlib in a fresh Python installation and get ModuleNotFoundError. What is the actual mistake, and what single command fixes it?
  2. A package's version number changes from 3.8.1 to 4.0.0. Which part of the version number changed, and what should that change warn you about before you upgrade?
  3. Explain, in your own words, why installing pandas can end up installing several other packages you never named yourself.
  4. Two of your projects need different, incompatible versions of the same package. Name the tool that solves this, and describe in one sentence how it solves it.
  5. Why is it risky to assume every package on PyPI is safe simply because PyPI is the "official" index?
  6. Write the requirements.txt line that would install any patch update of pillow starting from 10.2.0, but refuse to jump to 10.3.0 or later. (Hint: which specifier locks the minor version?)

Summary

A Python package is installable code with a name and a version, hosted on PyPI, an open catalogue anyone can publish to. pip is the tool that reads a package's metadata, resolves everything it depends on, downloads all of it as a dependency tree, and places it in a site-packages folder your program can then import from — two separate actions, install once and import every run, that beginners routinely conflate. Version numbers follow MAJOR.MINOR.PATCH semantic versioning, letting you and pip judge how risky an upgrade is, and version specifiers in a requirements.txt file let a whole project's dependencies be reproduced with one command. Virtual environments keep each project's installed packages separate so that one project's needs never silently break another's. None of this replaces understanding how code works — but it means a Grade 8 student with four lines of Python and one terminal command can generate a working, scannable UPI QR code, standing on the tested, published work of programmers who solved that specific hard problem long ago.

Practice Exercises

Now it is time to practice! Complete these challenges to solidify your understanding:

  • Exercise 1: Write a short program that demonstrates the core concept from this chapter. Test it with at least 3 different inputs.
  • Exercise 2: Find a real-world example where pip and package management: standing on giants is used in an Indian company (like TCS, Infosys, Flipkart, or ISRO). Write a paragraph explaining the connection.
  • Exercise 3: Create a mind-map connecting pip and package management: standing on giants to at least 3 other topics you have studied.
← Virtual EnvSQLite with Python: Your Portable Database →

Found this useful? Share it!

📱 WhatsApp 🐦 Twitter 💼 LinkedIn