> ## Documentation Index
> Fetch the complete documentation index at: https://genai.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 08-Python Project Essentials

> Learn the essential tools and practices for building modern Python projects, including dependency management, configuration, JSON handling, REST APIs, and dependency injection.

# Python Project Essentials

Developing a Python application involves much more than writing code. Modern Python projects require proper project organization, dependency management, configuration handling, and communication with external services.

In this module, you'll learn the essential tools and practices used in professional Python development.

## Topics Covered

In this module, you'll learn:

1. [Project Structure](#project-structure)
2. [Virtual Environments](#virtual-environments)
3. [Dependency Management with `uv`](#dependency-management-with-uv)
4. [Environment Variables and Configuration](#environment-variables)
5. [Working with JSON Data](#working-with-json-data)
6. [Consuming REST APIs](#consuming-rest-apis)
7. [Dependency Injection Concepts](#dependency-injection-concepts)
8. [Best Practices](#best-practices)

> **Try Yourself:** [💻 VS Code](vscode://file/Users/sivaprasad/Downloads/DSA%20With%20Python/public/notebooks/workshop-notebooks/08-project-essentials/08-project-essentials-exercises.ipynb) | [🚀 Colab](https://colab.research.google.com/github/prasad230776/genai-course/blob/main/public/notebooks/workshop-notebooks/08-project-essentials/08-project-essentials-exercises-colab.ipynb) | <a href="/notebooks/workshop-notebooks/08-project-essentials/08-project-essentials-exercises.ipynb" download>📥 Download</a><br />
> **Verify Solutions:** [💻 VS Code](vscode://file/Users/sivaprasad/Downloads/DSA%20With%20Python/public/notebooks/workshop-notebooks/08-project-essentials/08-project-essentials.ipynb) | [🚀 Colab](https://colab.research.google.com/github/prasad230776/genai-course/blob/main/public/notebooks/workshop-notebooks/08-project-essentials/08-project-essentials-colab.ipynb) | <a href="/notebooks/workshop-notebooks/08-project-essentials/08-project-essentials.ipynb" download>📥 Download</a>

By the end of this module, you'll be able to organize Python projects, manage dependencies efficiently, configure applications securely, exchange JSON data, consume REST APIs, and understand the fundamentals of dependency injection.

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Project Structure

A well-organized project is easier to understand, maintain, and extend.

A typical Python project might look like this.

```text theme={null}
student-management/

├── .venv/
├── .env
├── pyproject.toml
├── main.py
├── README.md
├── app/
│   ├── __init__.py
│   ├── models.py
│   ├── services.py
│   └── utils.py
└── data/
```

### Common Files

| File             | Purpose                                |
| ---------------- | -------------------------------------- |
| `pyproject.toml` | Project configuration and dependencies |
| `.venv`          | Virtual environment                    |
| `.env`           | Environment variables                  |
| `README.md`      | Project documentation                  |
| `main.py`        | Application entry point                |

As your project grows, organizing code into separate modules and packages improves readability and maintainability.

### Exercise 1

Create the following project structure.

**Expected Structure**

```text theme={null}
library-system/

├── app/
├── data/
├── .venv/
├── .env
├── pyproject.toml
└── main.py
```

<Accordion title="Solution">
  Create the folders and files using your preferred editor or the terminal.
</Accordion>

### Exercise 2

Identify the purpose of each file.

| File             | Purpose |
| ---------------- | ------- |
| `.env`           | ?       |
| `.venv`          | ?       |
| `pyproject.toml` | ?       |
| `README.md`      | ?       |

<Accordion title="Solution">
  | File             | Purpose                                 |
  | ---------------- | --------------------------------------- |
  | `.env`           | Stores configuration values and secrets |
  | `.venv`          | Isolated Python environment             |
  | `pyproject.toml` | Project metadata and dependencies       |
  | `README.md`      | Project documentation                   |
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Virtual Environments

A **virtual environment** is an isolated Python environment for a project.

It allows each project to have its own:

* Python packages
* Package versions
* Dependencies

Without virtual environments, installing a package for one project affects every project on your system.

### Creating a Virtual Environment

Using the built-in `venv` module.

```bash theme={null}
python -m venv .venv
```

Activate it.

**Windows**

```bash theme={null}
.venv\Scripts\activate
```

**macOS / Linux**

```bash theme={null}
source .venv/bin/activate
```

Deactivate the environment.

```bash theme={null}
deactivate
```

### Why Use Virtual Environments?

* Prevent dependency conflicts.
* Keep projects isolated.
* Make projects reproducible.
* Simplify dependency management.

### Exercise 1

Create and activate a virtual environment for a new project.

<Accordion title="Solution">
  ```bash theme={null}
  python -m venv .venv

  source .venv/bin/activate
  ```

  (macOS/Linux)

  or

  ```bash theme={null}
  .venv\Scripts\activate
  ```

  (Windows)
</Accordion>

### Exercise 2

Deactivate the virtual environment.

**Expected Command**

```bash theme={null}
deactivate
```

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Dependency Management with `uv`

`uv` is a modern Python package and project manager.

It is significantly faster than traditional tools such as `pip` because it is implemented in Rust.

### Installing `uv`

```bash theme={null}
pip install uv
```

Verify the installation.

```bash theme={null}
uv --version
```

### Creating a Project

```bash theme={null}
uv init student-management
```

This creates a new project with a standard structure.

### Adding a Package

```bash theme={null}
uv add requests
```

The dependency is automatically added to the project configuration.

### Installing Dependencies

```bash theme={null}
uv sync
```

This installs all dependencies listed in `pyproject.toml`.

### Removing a Package

```bash theme={null}
uv remove requests
```

### Running Python

```bash theme={null}
uv run main.py
```

### Why Use `uv`?

* Fast dependency resolution.
* Modern project management.
* Automatic virtual environment creation.
* Reproducible builds.
* Simple dependency synchronization.

### Exercise 1

Create a new project named **student-management** using `uv`.

**Expected Command**

```bash theme={null}
uv init student-management
```

### Exercise 2

Add the `requests` package and synchronize the project.

**Expected Commands**

```bash theme={null}
uv add requests

uv sync
```

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Environment Variables

Environment variables allow you to store configuration values outside your source code.

They are commonly used to store:

* API Keys
* Database URLs
* Secret Keys
* Application Settings

Instead of writing sensitive information directly in your code,

```python theme={null}
API_KEY = "my-secret-key"
```

store it in a `.env` file.

```text theme={null}
API_KEY=my-secret-key
DATABASE_URL=sqlite:///students.db
DEBUG=True
```

This keeps sensitive information separate from your application.

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Reading Environment Variables

Install the package.

```bash theme={null}
uv add python-dotenv
```

Create a `.env` file.

```text theme={null}
API_KEY=my-secret-key
```

Read the value.

```python theme={null}
from dotenv import load_dotenv
import os

load_dotenv()

api_key = os.getenv("API_KEY")

print(api_key)
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  my-secret-key
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Providing Default Values

Use a default value when an environment variable is missing.

```python theme={null}
import os

host = os.getenv("HOST", "localhost")

print(host)
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  localhost
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Why Use Environment Variables?

* Keep secrets out of source code.
* Use different configurations for development and production.
* Improve application security.
* Simplify deployment.

### Exercise 1

Create a `.env` file containing:

* `APP_NAME`
* `PORT`

Read and display both values.

**Expected Output**

```text theme={null}
Student Management System
8000
```

<Accordion title="Solution">
  ```text theme={null}
  APP_NAME=Student Management System
  PORT=8000
  ```

  ```python theme={null}
  from dotenv import load_dotenv
  import os

  load_dotenv()

  print(os.getenv("APP_NAME"))
  print(os.getenv("PORT"))
  ```
</Accordion>

### Exercise 2

Read an environment variable named `HOST`.

If it doesn't exist, display `"localhost"`.

**Expected Output**

```text theme={null}
localhost
```

<Accordion title="Solution">
  ```python theme={null}
  import os

  host = os.getenv("HOST", "localhost")

  print(host)
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Configuration Management

As projects grow, configuration values increase.

Instead of scattering configuration throughout the application, place them in one location.

Example project.

```text theme={null}
project/

├── .env
├── config.py
└── main.py
```

**config.py**

```python theme={null}
from dotenv import load_dotenv
import os

load_dotenv()

APP_NAME = os.getenv("APP_NAME")
DATABASE_URL = os.getenv("DATABASE_URL")
DEBUG = os.getenv("DEBUG")
```

**main.py**

```python theme={null}
import config

print(config.APP_NAME)
print(config.DATABASE_URL)
```

This approach centralizes application configuration and makes maintenance easier.

### Best Practices

* Never hardcode secrets.
* Use `.env` for local development.
* Keep configuration in one module.
* Add `.env` to `.gitignore`.

### Exercise 1

Create a `config.py` file that loads:

* `APP_NAME`
* `DEBUG`

<Accordion title="Solution">
  ```python theme={null}
  from dotenv import load_dotenv
  import os

  load_dotenv()

  APP_NAME = os.getenv("APP_NAME")
  DEBUG = os.getenv("DEBUG")
  ```
</Accordion>

### Exercise 2

Use `config.py` in another Python file to display the application name.

<Accordion title="Solution">
  ```python theme={null}
  import config

  print(config.APP_NAME)
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Working with JSON Data

JSON (JavaScript Object Notation) is the most widely used format for exchanging data between applications.

A JSON document consists of key-value pairs.

Example JSON.

```json theme={null}
{
    "name": "Alice",
    "age": 20,
    "course": "Python"
}
```

Python provides the built-in `json` module for working with JSON.

```python theme={null}
import json
```

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Converting Python Objects to JSON

Use `json.dumps()`.

```python theme={null}
import json

student = {
    "name": "Alice",
    "age": 20,
    "course": "Python"
}

json_data = json.dumps(student, indent=4)

print(json_data)
```

Output ?

<Accordion title="Show Output">
  ```json theme={null}
  {
      "name": "Alice",
      "age": 20,
      "course": "Python"
  }
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Converting JSON to Python Objects

Use `json.loads()`.

```python theme={null}
import json

data = '''
{
    "name": "Alice",
    "age": 20
}
'''

student = json.loads(data)

print(student["name"])
print(student["age"])
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  Alice
  20
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Reading JSON from a File

Suppose `student.json` contains

```json theme={null}
{
    "name": "Alice",
    "course": "Python"
}
```

Read the file.

```python theme={null}
import json

with open("student.json") as file:
    student = json.load(file)

print(student)
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  {'name': 'Alice', 'course': 'Python'}
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Writing JSON to a File

```python theme={null}
import json

student = {
    "name": "Alice",
    "course": "Python"
}

with open("student.json", "w") as file:
    json.dump(student, file, indent=4)
```

This creates a formatted JSON file.

### Exercise 1

Convert the following dictionary into JSON.

```python theme={null}
employee = {
    "name": "Rahul",
    "department": "IT"
}
```

<Accordion title="Solution">
  ```python theme={null}
  import json

  employee = {
      "name": "Rahul",
      "department": "IT"
  }

  print(json.dumps(employee, indent=4))
  ```
</Accordion>

### Exercise 2

Read the following JSON string and display the employee name.

```json theme={null}
{
    "name": "Rahul",
    "salary": 50000
}
```

**Expected Output**

```text theme={null}
Rahul
```

<Accordion title="Solution">
  ```python theme={null}
  import json

  data = '''
  {
      "name": "Rahul",
      "salary": 50000
  }
  '''

  employee = json.loads(data)

  print(employee["name"])
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Consuming REST APIs

Modern applications frequently communicate with external services using **REST APIs**.

Some common examples include:

* Weather applications
* Payment gateways
* AI services
* Maps and location services
* Social media platforms

Python provides several libraries for consuming REST APIs. One of the most popular is **requests**.

Install it using `uv`.

```bash theme={null}
uv add requests
```

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Making a GET Request

Let's fetch sample user information from the JSONPlaceholder API.

```python theme={null}
import requests

url = "https://jsonplaceholder.typicode.com/users/1"

response = requests.get(url)

print(response.status_code)
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  200
  ```
</Accordion>

A status code of **200** indicates that the request was successful.

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Reading JSON Response

Most REST APIs return JSON.

```python theme={null}
import requests

url = "https://jsonplaceholder.typicode.com/users/1"

response = requests.get(url)

user = response.json()

print(user["name"])
print(user["email"])
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  Leanne Graham
  Sincere@april.biz
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Sending Query Parameters

Query parameters provide additional information to the server.

```python theme={null}
import requests

url = "https://jsonplaceholder.typicode.com/comments"

params = {
    "postId": 1
}

response = requests.get(
    url,
    params=params
)

print(response.status_code)
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  200
  ```
</Accordion>

The generated URL becomes

```text theme={null}
https://jsonplaceholder.typicode.com/comments?postId=1
```

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Sending Headers

Headers provide additional metadata such as API keys.

```python theme={null}
headers = {
    "Authorization": "Bearer YOUR_API_KEY"
}

response = requests.get(
    url,
    headers=headers
)
```

Many real-world APIs require authentication through headers.

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Making a POST Request

```python theme={null}
import requests

url = "https://jsonplaceholder.typicode.com/posts"

data = {
    "title": "Python",
    "body": "Learning REST APIs",
    "userId": 1
}

response = requests.post(
    url,
    json=data
)

print(response.status_code)
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  201
  ```
</Accordion>

Status code **201** indicates that a new resource was created.

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Common HTTP Methods

| Method | Purpose                   |
| ------ | ------------------------- |
| GET    | Retrieve data             |
| POST   | Create data               |
| PUT    | Update an entire resource |
| PATCH  | Update part of a resource |
| DELETE | Remove a resource         |

### Exercise 1

Fetch user **5** from JSONPlaceholder and display the user's name.

**Expected Output**

```text theme={null}
Chelsey Dietrich
```

<Accordion title="Solution">
  ```python theme={null}
  import requests

  response = requests.get(
      "https://jsonplaceholder.typicode.com/users/5"
  )

  user = response.json()

  print(user["name"])
  ```
</Accordion>

### Exercise 2

Create a new post using the JSONPlaceholder API.

**Expected Output**

```text theme={null}
201
```

<Accordion title="Solution">
  ```python theme={null}
  import requests

  response = requests.post(
      "https://jsonplaceholder.typicode.com/posts",
      json={
          "title": "Python",
          "body": "Learning APIs",
          "userId": 1
      }
  )

  print(response.status_code)
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Dependency Injection Concepts

As applications grow, classes often depend on other classes.

For example, an application may have:

* Database Service
* Email Service
* Notification Service
* Authentication Service

If one class creates its own dependencies, the code becomes tightly coupled.

### Without Dependency Injection

```python theme={null}
class EmailService:

    def send(self):
        print("Email Sent")


class NotificationService:

    def __init__(self):
        self.email = EmailService()

    def notify(self):
        self.email.send()


notification = NotificationService()

notification.notify()
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  Email Sent
  ```
</Accordion>

Here, `NotificationService` directly creates an `EmailService`.

Replacing the email service later becomes difficult.

<div align="right">[Back to Top ↑](#topics-covered)</div>

## With Dependency Injection

Instead of creating the dependency inside the class, pass it from outside.

```python theme={null}
class EmailService:

    def send(self):
        print("Email Sent")


class NotificationService:

    def __init__(self, service):
        self.service = service

    def notify(self):
        self.service.send()


email = EmailService()

notification = NotificationService(email)

notification.notify()
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  Email Sent
  ```
</Accordion>

The dependency is now injected from outside.

This makes the code:

* Easier to test
* Easier to maintain
* More reusable
* Less tightly coupled

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Another Example

```python theme={null}
class SMSService:

    def send(self):
        print("SMS Sent")


sms = SMSService()

notification = NotificationService(sms)

notification.notify()
```

Output ?

<Accordion title="Show Output">
  ```text theme={null}
  SMS Sent
  ```
</Accordion>

Notice that **NotificationService** did not change.

Only the dependency changed.

This is the primary advantage of Dependency Injection.

### Exercise 1

Create a `PaymentService` class and inject it into an `OrderService`.

**Expected Output**

```text theme={null}
Payment Successful
```

<Accordion title="Solution">
  ```python theme={null}
  class PaymentService:

      def pay(self):
          print("Payment Successful")


  class OrderService:

      def __init__(self, payment):
          self.payment = payment

      def checkout(self):
          self.payment.pay()


  payment = PaymentService()

  order = OrderService(payment)

  order.checkout()
  ```
</Accordion>

### Exercise 2

Create both `EmailService` and `SMSService` and inject each into `NotificationService`.

**Expected Output**

```text theme={null}
Email Sent
SMS Sent
```

<Accordion title="Solution">
  ```python theme={null}
  class EmailService:

      def send(self):
          print("Email Sent")


  class SMSService:

      def send(self):
          print("SMS Sent")


  class NotificationService:

      def __init__(self, service):
          self.service = service

      def notify(self):
          self.service.send()


  NotificationService(
      EmailService()
  ).notify()

  NotificationService(
      SMSService()
  ).notify()
  ```
</Accordion>

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Best Practices

* Organize projects into logical modules and packages.
* Use virtual environments for every project.
* Manage dependencies with `uv`.
* Store secrets in environment variables.
* Keep configuration separate from source code.
* Use JSON for data exchange.
* Handle API responses and errors gracefully.
* Prefer dependency injection over creating dependencies inside classes.

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Practice

To reinforce what you've learned in this section, practice with the interactive follow-along notebook:

<CardGroup cols={1}>
  <Card title="Follow-Along Practice" icon="laptop-code">
    Practice dependency management using uv, reading environment variables from .env, parsing/generating JSON payloads, calling REST APIs using requests, and implementing dependency injection.

    [💻 VS Code](vscode://file/Users/sivaprasad/Downloads/DSA%20With%20Python/public/notebooks/workshop-notebooks/08-project-essentials/08-project-essentials-exercises.ipynb) | [🚀 Colab](https://colab.research.google.com/github/prasad230776/genai-course/blob/main/public/notebooks/workshop-notebooks/08-project-essentials/08-project-essentials-exercises-colab.ipynb) | <a href="/notebooks/workshop-notebooks/08-project-essentials/08-project-essentials-exercises.ipynb" download>📥 Download</a>
  </Card>
</CardGroup>

***

<div align="right">[Back to Top ↑](#topics-covered)</div>

## Summary

In this module, you learned the essential tools and practices used in modern Python projects.

### Key Concepts Covered

* Project Structure
* Virtual Environments
* Dependency Management with `uv`
* Environment Variables
* Configuration Management
* Working with JSON
* Consuming REST APIs
* Dependency Injection Concepts
* Best Practices

These concepts form the foundation of professional Python development and are widely used in frameworks such as FastAPI, Django, and Flask.
