Compare commits
19 Commits
38943392c2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f081c9d3a | ||
|
|
5b2723718f | ||
|
|
97814b3b57 | ||
|
|
2222dc63a1 | ||
|
|
7c08e19545 | ||
|
|
4cc57be794 | ||
|
|
b251ae7e2e | ||
|
|
e2711d6591 | ||
|
|
1b5d863dfa | ||
|
|
0e0eb64ce0 | ||
| 581d4999ba | |||
| 2b7ffd4dde | |||
|
|
90f5451ff8 | ||
|
|
2e229ceadd | ||
|
|
32615bf615 | ||
|
|
7de5efab3f | ||
|
|
6fbd717868 | ||
|
|
6be588aa58 | ||
|
|
01a3efd424 |
281
Docs/Basic/05-oop.md
Normal file
281
Docs/Basic/05-oop.md
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
# 05 – Object-Oriented Programming (OOP) in Python
|
||||||
|
|
||||||
|
This document explains the basics of **Object-Oriented Programming (OOP)** in Python using simple examples.
|
||||||
|
We cover:
|
||||||
|
|
||||||
|
* Classes and objects
|
||||||
|
* Attributes and methods
|
||||||
|
* Class attributes vs instance attributes
|
||||||
|
* Inheritance
|
||||||
|
* Special (magic) methods
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Basic Class, Attribute, and Method
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
class test_class():
|
||||||
|
def __init__(self, input):
|
||||||
|
self.parm = input
|
||||||
|
print("Class Created")
|
||||||
|
|
||||||
|
def result(self):
|
||||||
|
print(f"param is : {self.parm}")
|
||||||
|
|
||||||
|
var = test_class('abbas')
|
||||||
|
var.result()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
#### Class
|
||||||
|
|
||||||
|
* `test_class` is a **class**, which acts as a blueprint for creating objects.
|
||||||
|
|
||||||
|
#### `__init__` method (Constructor)
|
||||||
|
|
||||||
|
* `__init__` is a **special method** that runs automatically when a new object is created.
|
||||||
|
* `input` is a **parameter** passed when creating the object.
|
||||||
|
* `self.parm = input` creates an **instance attribute** called `parm`.
|
||||||
|
|
||||||
|
#### Attribute
|
||||||
|
|
||||||
|
* `parm` is an **attribute** (a variable that belongs to the object).
|
||||||
|
* It stores data specific to each object.
|
||||||
|
|
||||||
|
#### Method
|
||||||
|
|
||||||
|
* `result()` is a **method** (a function that belongs to the class).
|
||||||
|
* It uses `self.parm` to access the object’s data.
|
||||||
|
|
||||||
|
#### Object Creation
|
||||||
|
|
||||||
|
```python
|
||||||
|
var = test_class('abbas')
|
||||||
|
```
|
||||||
|
|
||||||
|
* Creates an object named `var`.
|
||||||
|
* Calls `__init__` automatically.
|
||||||
|
|
||||||
|
#### Method Call
|
||||||
|
|
||||||
|
```python
|
||||||
|
var.result()
|
||||||
|
```
|
||||||
|
|
||||||
|
* Calls the `result` method on the object.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Class Attributes vs Instance Attributes
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
class test_class():
|
||||||
|
test_value = 'abbas'
|
||||||
|
|
||||||
|
def __init__(self, input):
|
||||||
|
self.parm = input
|
||||||
|
print("Class Created")
|
||||||
|
|
||||||
|
def result(self):
|
||||||
|
print(f"param is : {self.parm}")
|
||||||
|
|
||||||
|
var = test_class('abbas')
|
||||||
|
var2 = test_class('mmd')
|
||||||
|
|
||||||
|
var.result()
|
||||||
|
var.test_value
|
||||||
|
|
||||||
|
var2.test_value = 'mmd'
|
||||||
|
var2.test_value
|
||||||
|
var.test_value
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
#### Class Attribute
|
||||||
|
|
||||||
|
```python
|
||||||
|
test_value = 'abbas'
|
||||||
|
```
|
||||||
|
|
||||||
|
* This is a **class attribute**.
|
||||||
|
* It belongs to the class itself.
|
||||||
|
* Shared by all objects unless overridden.
|
||||||
|
|
||||||
|
#### Instance Attribute
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.parm = input
|
||||||
|
```
|
||||||
|
|
||||||
|
* This is an **instance attribute**.
|
||||||
|
* Each object has its own value.
|
||||||
|
|
||||||
|
#### Behavior Analysis
|
||||||
|
|
||||||
|
```python
|
||||||
|
var.test_value
|
||||||
|
```
|
||||||
|
|
||||||
|
* Accesses the class attribute → `'abbas'`
|
||||||
|
|
||||||
|
```python
|
||||||
|
var2.test_value = 'mmd'
|
||||||
|
```
|
||||||
|
|
||||||
|
* Creates a **new instance attribute** for `var2`.
|
||||||
|
* Does not change the class attribute.
|
||||||
|
|
||||||
|
```python
|
||||||
|
var2.test_value
|
||||||
|
```
|
||||||
|
|
||||||
|
* Returns `'mmd'` (instance attribute)
|
||||||
|
|
||||||
|
```python
|
||||||
|
var.test_value
|
||||||
|
```
|
||||||
|
|
||||||
|
* Still returns `'abbas'` (class attribute)
|
||||||
|
|
||||||
|
#### Key Rule
|
||||||
|
|
||||||
|
* Instance attributes override class attributes **only for that object**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Inheritance
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
class class_1():
|
||||||
|
def __init__(self):
|
||||||
|
print("Class 1 Created")
|
||||||
|
|
||||||
|
def hi(self):
|
||||||
|
print("Hi")
|
||||||
|
|
||||||
|
class class_2(class_1):
|
||||||
|
def __init__(self):
|
||||||
|
print("Class 2 Created")
|
||||||
|
self.hi()
|
||||||
|
|
||||||
|
b = class_2()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
#### Parent Class
|
||||||
|
|
||||||
|
```python
|
||||||
|
class class_1():
|
||||||
|
```
|
||||||
|
|
||||||
|
* `class_1` is the **parent (base) class**.
|
||||||
|
|
||||||
|
#### Child Class
|
||||||
|
|
||||||
|
```python
|
||||||
|
class class_2(class_1):
|
||||||
|
```
|
||||||
|
|
||||||
|
* `class_2` **inherits** from `class_1`.
|
||||||
|
* It automatically has access to all public methods of `class_1`.
|
||||||
|
|
||||||
|
#### Method Usage
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.hi()
|
||||||
|
```
|
||||||
|
|
||||||
|
* `hi()` is defined in `class_1`.
|
||||||
|
* Because of inheritance, `class_2` can call it.
|
||||||
|
|
||||||
|
#### Output Order
|
||||||
|
|
||||||
|
```text
|
||||||
|
Class 2 Created
|
||||||
|
Hi
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Important Note
|
||||||
|
|
||||||
|
* `class_1.__init__()` is **not called automatically** here.
|
||||||
|
* To call it, you would need:
|
||||||
|
|
||||||
|
```python
|
||||||
|
super().__init__()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Special (Magic) Methods
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
class class_1():
|
||||||
|
def __init__(self):
|
||||||
|
print("Class 1 Created")
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'print command on class'
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
return 'on del value'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
Special methods start and end with **double underscores (`__`)** and control built-in behavior.
|
||||||
|
|
||||||
|
#### `__init__`
|
||||||
|
|
||||||
|
* Runs when an object is created.
|
||||||
|
|
||||||
|
#### `__len__`
|
||||||
|
|
||||||
|
```python
|
||||||
|
len(object)
|
||||||
|
```
|
||||||
|
|
||||||
|
* Defines the behavior of `len()` on the object.
|
||||||
|
* Returns `1` in this example.
|
||||||
|
|
||||||
|
#### `__str__`
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(object)
|
||||||
|
```
|
||||||
|
|
||||||
|
* Defines the string representation of the object.
|
||||||
|
* Used by `print()` and `str()`.
|
||||||
|
|
||||||
|
#### `__del__`
|
||||||
|
|
||||||
|
* Runs when the object is deleted or garbage-collected.
|
||||||
|
* Used rarely in modern Python.
|
||||||
|
* Return value is ignored.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
* **Class**: Blueprint for objects
|
||||||
|
* **Object**: Instance of a class
|
||||||
|
* **Attribute**: Data stored in an object
|
||||||
|
* **Method**: Function inside a class
|
||||||
|
* **Class Attribute**: Shared across all objects
|
||||||
|
* **Instance Attribute**: Unique per object
|
||||||
|
* **Inheritance**: Child class reuses parent class logic
|
||||||
|
* **Magic Methods**: Customize built-in Python behavior
|
||||||
|
|
||||||
218
Docs/Basic/06-pkg-modules.md
Normal file
218
Docs/Basic/06-pkg-modules.md
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
# 06 – Packages and Modules in Python
|
||||||
|
|
||||||
|
This document explains how **modules**, **packages**, and the `__name__` concept work in Python.
|
||||||
|
These features help organize code, reuse functionality, and build scalable projects.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Importing External Modules
|
||||||
|
|
||||||
|
Python allows you to import **external libraries** installed in your environment.
|
||||||
|
|
||||||
|
### Example: Using the `emoji` Module
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
import emoji
|
||||||
|
print(emoji.emojize("abbas is :red_heart:"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `import emoji` imports the entire `emoji` module.
|
||||||
|
* `emoji.emojize()` converts emoji aliases into actual emoji characters.
|
||||||
|
* You must use the module name (`emoji`) to access its functions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Importing a Specific Function
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
from emoji import emojize
|
||||||
|
print(emojize("abbas is :red_heart:"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `from emoji import emojize` imports only the `emojize` function.
|
||||||
|
* You can call the function directly without prefixing the module name.
|
||||||
|
* This approach is cleaner when you only need a specific function.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Creating a Module
|
||||||
|
|
||||||
|
A **module** is a single Python file containing functions, classes, or variables.
|
||||||
|
|
||||||
|
### File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
hi.py
|
||||||
|
main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### `hi.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def hi():
|
||||||
|
print("Hi :)")
|
||||||
|
```
|
||||||
|
|
||||||
|
### `main.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
import hi
|
||||||
|
hi.hi()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `hi.py` is a module.
|
||||||
|
* `hi()` is a function defined inside the module.
|
||||||
|
* `import hi` loads the module.
|
||||||
|
* `hi.hi()` calls the function from the module.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Creating a Package
|
||||||
|
|
||||||
|
A **package** is a directory that contains multiple modules.
|
||||||
|
|
||||||
|
### Package Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
honor/
|
||||||
|
│── __init__.py
|
||||||
|
│── hi.py
|
||||||
|
main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### `honor/hi.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def hello():
|
||||||
|
print("Hi :)")
|
||||||
|
```
|
||||||
|
|
||||||
|
### `honor/__init__.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* The `honor` directory is a package.
|
||||||
|
* `__init__.py` tells Python that this directory is a package.
|
||||||
|
* The file can be empty, but it **must exist** (especially for older Python versions and clarity).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Importing from a Package (Method 1)
|
||||||
|
|
||||||
|
### `main.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
from honor import hi
|
||||||
|
hi.hello()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
* Imports the `hi` module from the `honor` package.
|
||||||
|
* Accesses the function using `hi.hello()`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Importing from a Package (Method 2)
|
||||||
|
|
||||||
|
### `main.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
from honor.hi import hello
|
||||||
|
hello()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
* Imports the `hello` function directly.
|
||||||
|
* Allows calling the function without the module name.
|
||||||
|
* Cleaner when only one function is needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. The `__name__` Concept
|
||||||
|
|
||||||
|
Every Python file has a built-in variable called `__name__`.
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(__name__)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Behavior
|
||||||
|
|
||||||
|
#### When a File Is Run Directly
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 abbas.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
__main__
|
||||||
|
```
|
||||||
|
|
||||||
|
* This means the file is the **entry point** of the program.
|
||||||
|
|
||||||
|
#### When a File Is Imported
|
||||||
|
|
||||||
|
```python
|
||||||
|
import abbas
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
abbas
|
||||||
|
```
|
||||||
|
|
||||||
|
* `__name__` is set to the **module name**, not `__main__`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Why `__name__ == "__main__"` Is Important
|
||||||
|
|
||||||
|
This pattern allows code to run **only when the file is executed directly**, not when imported.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```python
|
||||||
|
def main():
|
||||||
|
print("Running directly")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* The code inside the `if` block runs only when the file is executed directly.
|
||||||
|
* Prevents unwanted execution when the file is imported as a module.
|
||||||
|
* This is a standard Python best practice.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
* **Module**: A single `.py` file
|
||||||
|
* **Package**: A directory containing modules
|
||||||
|
* `__init__.py`: Marks a directory as a package
|
||||||
|
* `import module`: Imports the whole module
|
||||||
|
* `from module import item`: Imports specific items
|
||||||
|
* `__name__`: Identifies how a file is executed
|
||||||
|
|
||||||
219
Docs/Basic/07-error-handeling.md
Normal file
219
Docs/Basic/07-error-handeling.md
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
# 06 – Error Handling, Linting, Formatting, and Testing in Python
|
||||||
|
|
||||||
|
This document explains how Python handles runtime errors, how to write safer code using `try / except`, and how to improve code quality using **linting**, **formatting**, and **unit testing** tools.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Error Handling with `try / except`
|
||||||
|
|
||||||
|
Python uses `try / except` blocks to handle runtime errors gracefully without crashing the program.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```python
|
||||||
|
def abbas(a, b):
|
||||||
|
try:
|
||||||
|
res = a / b
|
||||||
|
print(res)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
print("Zero Number Detected")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error Detected {e}")
|
||||||
|
|
||||||
|
abbas(1, 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* The code inside `try` is executed first.
|
||||||
|
* If no error occurs, the result is printed.
|
||||||
|
* If `b` is `0`, a `ZeroDivisionError` is raised.
|
||||||
|
* The specific `ZeroDivisionError` block runs first.
|
||||||
|
* Any other error is caught by the generic `Exception` block.
|
||||||
|
|
||||||
|
### Key Rule
|
||||||
|
|
||||||
|
* Always catch **specific exceptions first**.
|
||||||
|
* Use `Exception` only as a fallback.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Full `try / except` Structure
|
||||||
|
|
||||||
|
Python supports additional blocks for more control.
|
||||||
|
|
||||||
|
### Syntax
|
||||||
|
|
||||||
|
```python
|
||||||
|
try:
|
||||||
|
# code that may raise an error
|
||||||
|
except:
|
||||||
|
# runs if an error occurs
|
||||||
|
else:
|
||||||
|
# runs if no error occurs
|
||||||
|
finally:
|
||||||
|
# always runs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `try`: code that may fail
|
||||||
|
* `except`: handles errors
|
||||||
|
* `else`: runs only if no exception occurred
|
||||||
|
* `finally`: runs no matter what (used for cleanup)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Linting with `pylint`
|
||||||
|
|
||||||
|
Linting analyzes code for:
|
||||||
|
|
||||||
|
* Syntax errors
|
||||||
|
* Style problems
|
||||||
|
* Bad practices
|
||||||
|
|
||||||
|
### Basic Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pylint main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detailed Report
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pylint --report y main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `pylint` gives a score and suggestions.
|
||||||
|
* Helps maintain readable and maintainable code.
|
||||||
|
* Commonly used in professional Python projects.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Code Formatting with `black`
|
||||||
|
|
||||||
|
`black` is an automatic code formatter that enforces a consistent style.
|
||||||
|
|
||||||
|
### Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
black main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* Reformats code automatically.
|
||||||
|
* Removes style debates.
|
||||||
|
* Safe and widely used.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Unit Testing with `unittest`
|
||||||
|
|
||||||
|
Unit tests verify that individual parts of code behave as expected.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Application Code
|
||||||
|
|
||||||
|
#### `abbas.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def bemola(a, b):
|
||||||
|
try:
|
||||||
|
res = a / b
|
||||||
|
print(res)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
print("Zero Number Detected")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error Detected {e}")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Test Code
|
||||||
|
|
||||||
|
#### `abbas_test.py`
|
||||||
|
|
||||||
|
```python
|
||||||
|
import unittest
|
||||||
|
from abbas import bemola
|
||||||
|
|
||||||
|
class TestAbbas(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_bemola(self):
|
||||||
|
a = 10
|
||||||
|
b = 2
|
||||||
|
self.assertEqual(bemola(a, b), 5)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
#### `unittest.TestCase`
|
||||||
|
|
||||||
|
* Base class for writing test cases.
|
||||||
|
|
||||||
|
#### Test Method
|
||||||
|
|
||||||
|
```python
|
||||||
|
def test_bemola(self):
|
||||||
|
```
|
||||||
|
|
||||||
|
* Test methods must start with `test_`.
|
||||||
|
|
||||||
|
#### Assertion
|
||||||
|
|
||||||
|
```python
|
||||||
|
self.assertEqual(bemola(a, b), 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
* Checks if the function returns the expected result.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Important Note (Design Issue)
|
||||||
|
|
||||||
|
The function `bemola` **prints** the result but does not return it.
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(res)
|
||||||
|
```
|
||||||
|
|
||||||
|
This causes the test to fail because the function returns `None`.
|
||||||
|
|
||||||
|
#### Correct Implementation
|
||||||
|
|
||||||
|
```python
|
||||||
|
def bemola(a, b):
|
||||||
|
try:
|
||||||
|
return a / b
|
||||||
|
except ZeroDivisionError:
|
||||||
|
return "Zero Number Detected"
|
||||||
|
except Exception as e:
|
||||||
|
return f"Error Detected {e}"
|
||||||
|
```
|
||||||
|
|
||||||
|
This version:
|
||||||
|
|
||||||
|
* Returns values instead of printing
|
||||||
|
* Is testable
|
||||||
|
* Follows best practices
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
* `try / except` prevents program crashes
|
||||||
|
* `else` runs only when no error occurs
|
||||||
|
* `finally` always runs
|
||||||
|
* `pylint` improves code quality
|
||||||
|
* `black` enforces formatting
|
||||||
|
* `unittest` verifies correctness
|
||||||
|
* Functions should **return values**, not print them, when tested
|
||||||
182
Docs/Basic/08-decorators.md
Normal file
182
Docs/Basic/08-decorators.md
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
# 08 – Decorators in Python
|
||||||
|
|
||||||
|
This document explains **decorators**, how they work, and how they are used to extend function behavior without modifying the original function code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. What Is a Decorator?
|
||||||
|
|
||||||
|
A **decorator** is a function that:
|
||||||
|
|
||||||
|
* Takes another function as input
|
||||||
|
* Adds extra behavior
|
||||||
|
* Returns a new function
|
||||||
|
|
||||||
|
Decorators are commonly used for:
|
||||||
|
|
||||||
|
* Input validation
|
||||||
|
* Logging
|
||||||
|
* Authentication
|
||||||
|
* Performance measurement
|
||||||
|
* Access control
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Basic Decorator Structure
|
||||||
|
|
||||||
|
A decorator has three layers:
|
||||||
|
|
||||||
|
1. The decorator function
|
||||||
|
2. The wrapper function
|
||||||
|
3. The original function
|
||||||
|
|
||||||
|
### General Pattern
|
||||||
|
|
||||||
|
```python
|
||||||
|
def decorator(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
# extra behavior
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Example: Input Validation Decorator
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
def check_number(func):
|
||||||
|
def wrapper(a, b):
|
||||||
|
if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
|
||||||
|
print("Input must be numbers")
|
||||||
|
return
|
||||||
|
return func(a, b)
|
||||||
|
return wrapper
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `check_number` is the decorator.
|
||||||
|
* `func` is the function being decorated.
|
||||||
|
* `wrapper` replaces the original function.
|
||||||
|
* `a` and `b` are the arguments passed to the original function.
|
||||||
|
* `isinstance(a, (int, float))` ensures inputs are numeric.
|
||||||
|
* If validation fails, execution stops.
|
||||||
|
* If validation passes, the original function is called.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Using the Decorator with `@` Syntax
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
@check_number
|
||||||
|
def bemola(a, b):
|
||||||
|
try:
|
||||||
|
res = a / b
|
||||||
|
print(res)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
print("Zero Number Detected")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error Detected {e}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### What Happens Internally
|
||||||
|
|
||||||
|
This line:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@check_number
|
||||||
|
```
|
||||||
|
|
||||||
|
Is equivalent to:
|
||||||
|
|
||||||
|
```python
|
||||||
|
bemola = check_number(bemola)
|
||||||
|
```
|
||||||
|
|
||||||
|
The function `bemola` is replaced by `wrapper`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Execution Flow
|
||||||
|
|
||||||
|
When calling:
|
||||||
|
|
||||||
|
```python
|
||||||
|
bemola(10, 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
The flow is:
|
||||||
|
|
||||||
|
1. `wrapper(10, 2)` is called
|
||||||
|
2. Inputs are validated
|
||||||
|
3. `func(10, 2)` is executed
|
||||||
|
4. Result is printed
|
||||||
|
|
||||||
|
If calling:
|
||||||
|
|
||||||
|
```python
|
||||||
|
bemola(10, "a")
|
||||||
|
```
|
||||||
|
|
||||||
|
The output will be:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Input must be numbers
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Why Use Decorators?
|
||||||
|
|
||||||
|
Without decorators, input validation would need to be repeated in every function.
|
||||||
|
|
||||||
|
Decorators allow:
|
||||||
|
|
||||||
|
* Reusable logic
|
||||||
|
* Cleaner code
|
||||||
|
* Separation of concerns
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Limitations in This Example
|
||||||
|
|
||||||
|
* The decorator only works with exactly two arguments.
|
||||||
|
* It does not preserve the original function’s metadata (`__name__`, `__doc__`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Improved Version (Best Practice)
|
||||||
|
|
||||||
|
```python
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
def check_number(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
if not all(isinstance(x, (int, float)) for x in args):
|
||||||
|
print("Input must be numbers")
|
||||||
|
return
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
```
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* Supports any number of arguments
|
||||||
|
* Preserves function name and documentation
|
||||||
|
* More reusable and professional
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
* Decorators modify function behavior without changing its code
|
||||||
|
* They wrap functions inside another function
|
||||||
|
* `@decorator` is syntactic sugar
|
||||||
|
* Commonly used for validation, logging, and access control
|
||||||
|
* Best practice is to use `*args`, `**kwargs`, and `functools.wraps`
|
||||||
266
Docs/Basic/09-standard-libs.md
Normal file
266
Docs/Basic/09-standard-libs.md
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
# 09 – Python Standard Library
|
||||||
|
|
||||||
|
This document introduces some of the most commonly used **Python standard library** modules:
|
||||||
|
|
||||||
|
* `datetime`
|
||||||
|
* `math`
|
||||||
|
* `random`
|
||||||
|
* `decimal`
|
||||||
|
|
||||||
|
These modules come bundled with Python and require no external installation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Date and Time with `datetime`
|
||||||
|
|
||||||
|
The `datetime` module provides classes for working with dates and times.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Working with Dates
|
||||||
|
|
||||||
|
#### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
date_1 = datetime.date(2026, 1, 1)
|
||||||
|
|
||||||
|
print(date_1.year)
|
||||||
|
print(date_1.month)
|
||||||
|
print(date_1.day)
|
||||||
|
|
||||||
|
print(date_1.weekday)
|
||||||
|
print(date_1.ctime)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
* `datetime.date(year, month, day)` creates a date object.
|
||||||
|
* `.year`, `.month`, `.day` access individual components.
|
||||||
|
|
||||||
|
#### Important Note
|
||||||
|
|
||||||
|
```python
|
||||||
|
date_1.weekday()
|
||||||
|
```
|
||||||
|
|
||||||
|
* Returns the day of the week as an integer:
|
||||||
|
|
||||||
|
* Monday = 0
|
||||||
|
* Sunday = 6
|
||||||
|
|
||||||
|
```python
|
||||||
|
date_1.ctime()
|
||||||
|
```
|
||||||
|
|
||||||
|
* Returns a human-readable string representation of the date.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Working with Time
|
||||||
|
|
||||||
|
#### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
time_1 = datetime.time(12, 12)
|
||||||
|
|
||||||
|
print(time_1.hour)
|
||||||
|
print(time_1.min)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
* `datetime.time(hour, minute)` creates a time object.
|
||||||
|
* `.hour` returns the hour.
|
||||||
|
* `.minute` returns the minute.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Working with Date and Time Together
|
||||||
|
|
||||||
|
#### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
abbas_birth = datetime.datetime(2026, 1, 1, 12, 12)
|
||||||
|
today = datetime.date.today()
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
|
||||||
|
diff_time = now - abbas_birth
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
* `datetime.datetime` includes both date and time.
|
||||||
|
* `date.today()` returns today’s date.
|
||||||
|
* `datetime.now()` returns the current date and time.
|
||||||
|
* Subtracting two `datetime` objects returns a `timedelta`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Mathematical Operations with `math`
|
||||||
|
|
||||||
|
The `math` module provides advanced mathematical functions and constants.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Mathematical Constants
|
||||||
|
|
||||||
|
```python
|
||||||
|
import math
|
||||||
|
|
||||||
|
print(math.pi)
|
||||||
|
print(math.e)
|
||||||
|
print(math.inf)
|
||||||
|
```
|
||||||
|
|
||||||
|
* `math.pi`: π constant
|
||||||
|
* `math.e`: Euler’s number
|
||||||
|
* `math.inf`: infinity
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Power and Rounding
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(math.pow(2, 3))
|
||||||
|
|
||||||
|
print(round(4.2))
|
||||||
|
print(round(4.8))
|
||||||
|
```
|
||||||
|
|
||||||
|
* `math.pow(a, b)` returns `a` raised to the power of `b`.
|
||||||
|
* `round()` rounds to the nearest integer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Floor and Ceil
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(math.floor(4.2))
|
||||||
|
print(math.floor(4.9))
|
||||||
|
|
||||||
|
print(math.ceil(4.2))
|
||||||
|
print(math.ceil(4.9))
|
||||||
|
```
|
||||||
|
|
||||||
|
* `floor`: rounds down
|
||||||
|
* `ceil`: rounds up
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Logarithms
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(math.log(100, 10))
|
||||||
|
```
|
||||||
|
|
||||||
|
* Returns the logarithm of 100 with base 10.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Random Values with `random`
|
||||||
|
|
||||||
|
The `random` module is used to generate pseudo-random values.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Random Numbers
|
||||||
|
|
||||||
|
```python
|
||||||
|
import random
|
||||||
|
|
||||||
|
print(random.randint(1, 6))
|
||||||
|
print(random.random())
|
||||||
|
```
|
||||||
|
|
||||||
|
* `randint(a, b)`: random integer between `a` and `b` (inclusive)
|
||||||
|
* `random()`: random float between `0` and `1`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Random Selection
|
||||||
|
|
||||||
|
```python
|
||||||
|
number_list = list(range(15))
|
||||||
|
print(random.choice(number_list))
|
||||||
|
|
||||||
|
char_list = ['a', 'm', 's']
|
||||||
|
print(random.choice(char_list))
|
||||||
|
```
|
||||||
|
|
||||||
|
* `choice()` selects a random element from a sequence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Shuffling
|
||||||
|
|
||||||
|
```python
|
||||||
|
random.shuffle(number_list)
|
||||||
|
print(number_list)
|
||||||
|
```
|
||||||
|
|
||||||
|
* `shuffle()` randomly rearranges the list in place.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Decimal Precision with `decimal`
|
||||||
|
|
||||||
|
The `decimal` module provides precise decimal arithmetic, avoiding floating-point errors.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Decimal Context
|
||||||
|
|
||||||
|
```python
|
||||||
|
import decimal
|
||||||
|
|
||||||
|
print(decimal.getcontext())
|
||||||
|
```
|
||||||
|
|
||||||
|
* Shows current precision and rounding settings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Float vs Decimal
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(decimal.Decimal(0.1))
|
||||||
|
print(decimal.Decimal('0.1'))
|
||||||
|
```
|
||||||
|
|
||||||
|
* Passing a float carries floating-point error.
|
||||||
|
* Passing a string preserves exact value.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Precision Comparison
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(0.1 + 0.2 == 0.3)
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns `False` due to floating-point precision issues.
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(decimal.Decimal(0.1) + decimal.Decimal(0.2) == decimal.Decimal(0.3))
|
||||||
|
```
|
||||||
|
|
||||||
|
Still `False` because the floats are imprecise.
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(decimal.Decimal('0.1') + decimal.Decimal('0.2') == decimal.Decimal('0.3'))
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns `True` because strings preserve precision.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
* `datetime` handles dates and times
|
||||||
|
* `math` provides mathematical constants and functions
|
||||||
|
* `random` generates pseudo-random values
|
||||||
|
* `decimal` solves floating-point precision problems
|
||||||
|
* Always use strings when creating `Decimal` values
|
||||||
|
|
||||||
274
Docs/Libs/Docker/01-Setup.md
Normal file
274
Docs/Libs/Docker/01-Setup.md
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
# Docker SDK for Python – Setup and First Steps
|
||||||
|
|
||||||
|
This document introduces the **Docker SDK for Python (`docker` library)** and explains how to connect to the Docker Engine, inspect its status, and authenticate with a registry. The goal is not just to show code, but to clearly explain **what each part does, why it exists, and when you would use it**.
|
||||||
|
|
||||||
|
This guide assumes you already understand basic Docker concepts (images, containers, daemon) and are approaching this from a DevOps perspective.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Installing the Docker SDK for Python
|
||||||
|
|
||||||
|
Before Python can communicate with Docker, we need to install the official SDK.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install docker
|
||||||
|
```
|
||||||
|
|
||||||
|
### What this does
|
||||||
|
|
||||||
|
* Installs the **docker** Python package (often called *docker-py*).
|
||||||
|
* This package acts as a **client wrapper** around Docker’s REST API.
|
||||||
|
* All interactions ultimately talk to the Docker daemon (`dockerd`) over a socket or TCP connection.
|
||||||
|
|
||||||
|
Important note:
|
||||||
|
|
||||||
|
* Installing the library alone is not enough.
|
||||||
|
* Docker **must already be installed and running** on the system.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Connecting to Docker Using Environment Configuration
|
||||||
|
|
||||||
|
The simplest and most common way to create a Docker client is by using environment-based configuration.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import docker
|
||||||
|
|
||||||
|
client = docker.from_env()
|
||||||
|
version = client.version()
|
||||||
|
ping_docker = client.ping()
|
||||||
|
|
||||||
|
print(version, ping_docker)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step-by-step explanation
|
||||||
|
|
||||||
|
#### `import docker`
|
||||||
|
|
||||||
|
* Imports the Docker SDK.
|
||||||
|
* This module exposes high-level objects for interacting with Docker resources.
|
||||||
|
|
||||||
|
#### `docker.from_env()`
|
||||||
|
|
||||||
|
* Automatically creates a `DockerClient` instance.
|
||||||
|
* Reads Docker connection details from environment variables such as:
|
||||||
|
|
||||||
|
* `DOCKER_HOST`
|
||||||
|
* `DOCKER_TLS_VERIFY`
|
||||||
|
* `DOCKER_CERT_PATH`
|
||||||
|
* On Linux, this usually resolves to:
|
||||||
|
|
||||||
|
* `unix:///var/run/docker.sock`
|
||||||
|
|
||||||
|
This is why `from_env()` is preferred:
|
||||||
|
|
||||||
|
* Works across Linux, macOS, Windows, and CI systems
|
||||||
|
* Requires no hardcoded connection strings
|
||||||
|
|
||||||
|
#### `client.version()`
|
||||||
|
|
||||||
|
* Calls the Docker Engine `/version` API endpoint.
|
||||||
|
* Returns detailed metadata such as:
|
||||||
|
|
||||||
|
* Docker Engine version
|
||||||
|
* API version
|
||||||
|
* Go version
|
||||||
|
* OS and architecture
|
||||||
|
|
||||||
|
This is commonly used for:
|
||||||
|
|
||||||
|
* Debugging
|
||||||
|
* Compatibility checks
|
||||||
|
* Logging runtime environment details
|
||||||
|
|
||||||
|
#### `client.ping()`
|
||||||
|
|
||||||
|
* Sends a lightweight request to the Docker daemon.
|
||||||
|
* Returns `True` if Docker is reachable and responsive.
|
||||||
|
|
||||||
|
This is a **health check**, often used in:
|
||||||
|
|
||||||
|
* Startup validation
|
||||||
|
* Monitoring scripts
|
||||||
|
* CI/CD pipelines
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. What Does “from_env” Actually Mean?
|
||||||
|
|
||||||
|
Using `from_env()` tells the SDK:
|
||||||
|
|
||||||
|
> “Figure out how to connect to Docker using the same configuration the Docker CLI uses.”
|
||||||
|
|
||||||
|
Behind the scenes, it:
|
||||||
|
|
||||||
|
* Detects whether Docker is local or remote
|
||||||
|
* Determines socket vs TCP
|
||||||
|
* Applies TLS settings if required
|
||||||
|
|
||||||
|
### When should you use it?
|
||||||
|
|
||||||
|
* Local development
|
||||||
|
* Kubernetes nodes
|
||||||
|
* CI runners
|
||||||
|
* Most production automation
|
||||||
|
|
||||||
|
### When not to use it?
|
||||||
|
|
||||||
|
* When you need explicit control over connection parameters
|
||||||
|
* When connecting to a **remote Docker daemon** with custom networking
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Creating a Docker Client Explicitly
|
||||||
|
|
||||||
|
Instead of relying on environment detection, you can create a client manually.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import docker
|
||||||
|
|
||||||
|
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||||
|
version = client.version()
|
||||||
|
ping_docker = client.ping()
|
||||||
|
|
||||||
|
print(version, ping_docker)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
#### `docker.DockerClient(...)`
|
||||||
|
|
||||||
|
* Directly instantiates a Docker client object.
|
||||||
|
* Requires you to specify how to reach the Docker daemon.
|
||||||
|
|
||||||
|
#### `base_url='unix://var/run/docker.sock'`
|
||||||
|
|
||||||
|
* Points to the Unix socket used by Docker on Linux systems.
|
||||||
|
* This socket is owned by root and the `docker` group.
|
||||||
|
|
||||||
|
Important implications:
|
||||||
|
|
||||||
|
* Your Python process must have permission to access the socket
|
||||||
|
* Usually means running as root or a user in the `docker` group
|
||||||
|
|
||||||
|
### When this approach is useful
|
||||||
|
|
||||||
|
* Controlled environments
|
||||||
|
* Educational examples
|
||||||
|
* Explicit infrastructure scripts
|
||||||
|
|
||||||
|
### Why it is less common
|
||||||
|
|
||||||
|
* Not portable across OSes
|
||||||
|
* Hardcodes infrastructure assumptions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Inspecting Docker and Authenticating
|
||||||
|
|
||||||
|
Once connected, the client can retrieve detailed system information and authenticate with registries.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import docker
|
||||||
|
|
||||||
|
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||||
|
version = client.version()
|
||||||
|
ping_docker = client.ping()
|
||||||
|
information = client.info()
|
||||||
|
|
||||||
|
client.login(
|
||||||
|
username='user',
|
||||||
|
password='password',
|
||||||
|
registry='registry_link'
|
||||||
|
)
|
||||||
|
|
||||||
|
print(information, ping_docker)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `client.info()`
|
||||||
|
|
||||||
|
* Calls the Docker `/info` endpoint.
|
||||||
|
* Returns a comprehensive snapshot of the Docker host, including:
|
||||||
|
|
||||||
|
* Number of containers (running/stopped)
|
||||||
|
* Number of images
|
||||||
|
* Storage driver
|
||||||
|
* Cgroup and kernel features
|
||||||
|
* Security options (AppArmor, seccomp)
|
||||||
|
|
||||||
|
This is extremely valuable for:
|
||||||
|
|
||||||
|
* Capacity planning
|
||||||
|
* Debugging runtime issues
|
||||||
|
* Auditing host configuration
|
||||||
|
|
||||||
|
### `client.login(...)`
|
||||||
|
|
||||||
|
* Authenticates Docker with a container registry.
|
||||||
|
* Stores credentials in Docker’s credential store.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
* `username`: registry account username
|
||||||
|
* `password`: registry password or access token
|
||||||
|
* `registry`: registry URL (e.g. Docker Hub or private registry)
|
||||||
|
|
||||||
|
Common use cases:
|
||||||
|
|
||||||
|
* Pulling private images
|
||||||
|
* Pushing images in CI/CD pipelines
|
||||||
|
* Automating registry interactions
|
||||||
|
|
||||||
|
Security note:
|
||||||
|
|
||||||
|
* Avoid hardcoding credentials in source code
|
||||||
|
* Prefer environment variables or secret managers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. High-Level vs Low-Level API (Important Concept)
|
||||||
|
|
||||||
|
What you are using here is the **high-level Docker client**.
|
||||||
|
|
||||||
|
Characteristics:
|
||||||
|
|
||||||
|
* Pythonic object model
|
||||||
|
* Resource-oriented (containers, images, networks)
|
||||||
|
* Easier to read and maintain
|
||||||
|
|
||||||
|
The SDK also exposes a **low-level API**:
|
||||||
|
|
||||||
|
* Direct access to Docker REST endpoints
|
||||||
|
* More control, less abstraction
|
||||||
|
|
||||||
|
As a DevOps engineer, you will typically:
|
||||||
|
|
||||||
|
* Use high-level API for automation
|
||||||
|
* Drop to low-level API for advanced edge cases
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Summary
|
||||||
|
|
||||||
|
In this setup phase, you learned how to:
|
||||||
|
|
||||||
|
* Install the Docker SDK for Python
|
||||||
|
* Connect to Docker using environment-based configuration
|
||||||
|
* Explicitly define Docker connection settings
|
||||||
|
* Verify Docker availability
|
||||||
|
* Retrieve system-level Docker information
|
||||||
|
* Authenticate with container registries
|
||||||
|
|
||||||
|
This foundation is critical before moving on to:
|
||||||
|
|
||||||
|
* Managing containers
|
||||||
|
* Building images
|
||||||
|
* Working with volumes and networks
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
Official Docker SDK for Python documentation:
|
||||||
|
|
||||||
|
* [https://docker-py.readthedocs.io/](https://docker-py.readthedocs.io/)
|
||||||
360
Docs/Libs/Docker/02-Images.md
Normal file
360
Docs/Libs/Docker/02-Images.md
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
# Docker SDK for Python – Working with Images
|
||||||
|
|
||||||
|
This document explains how to **manage Docker images** using the Docker SDK for Python. Instead of treating the SDK as a set of function calls, we’ll approach images the same way Docker itself does: as immutable artifacts that are pulled, built, tagged, pushed, inspected, and eventually cleaned up.
|
||||||
|
|
||||||
|
All examples assume:
|
||||||
|
|
||||||
|
* Docker is installed and running
|
||||||
|
* The Python process has access to the Docker socket
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Creating the Docker Client
|
||||||
|
|
||||||
|
```python
|
||||||
|
import docker
|
||||||
|
|
||||||
|
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||||
|
ping_docker = client.ping()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `DockerClient` establishes a connection to the Docker daemon.
|
||||||
|
* `ping()` verifies that Docker is reachable before doing any real work.
|
||||||
|
|
||||||
|
This is a common pattern in automation:
|
||||||
|
|
||||||
|
* Fail fast if Docker is unavailable
|
||||||
|
* Avoid partial execution later in the script
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Pulling Images from a Registry
|
||||||
|
|
||||||
|
```python
|
||||||
|
def pull_image(name_image, tag_image):
|
||||||
|
image = client.images.pull(name_image, tag=tag_image)
|
||||||
|
print(image)
|
||||||
|
```
|
||||||
|
|
||||||
|
### What this does
|
||||||
|
|
||||||
|
* Downloads an image from a registry (Docker Hub or private registry).
|
||||||
|
* If the image already exists locally, Docker may reuse layers.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
* `name_image`: repository name (e.g. `alpine`, `nginx`, `myrepo/app`)
|
||||||
|
* `tag_image`: specific version or variant (e.g. `3.20`, `latest`)
|
||||||
|
|
||||||
|
Returned value:
|
||||||
|
|
||||||
|
* An `Image` object representing the pulled image
|
||||||
|
|
||||||
|
Why this matters:
|
||||||
|
|
||||||
|
* Pulling explicitly avoids relying on implicit image downloads
|
||||||
|
* Makes automation predictable and repeatable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Building Images from a Dockerfile
|
||||||
|
|
||||||
|
### Basic build
|
||||||
|
|
||||||
|
```python
|
||||||
|
def build_image():
|
||||||
|
image, logs = client.images.build(
|
||||||
|
path=".",
|
||||||
|
tag="myapp:1.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
for log in logs:
|
||||||
|
if "stream" in log:
|
||||||
|
print(log["stream"].strip())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `path="."` tells Docker to use the current directory as the build context.
|
||||||
|
* Docker automatically looks for a file named `Dockerfile`.
|
||||||
|
* `tag="myapp:1.0"` assigns a name and version to the resulting image.
|
||||||
|
|
||||||
|
The build process returns:
|
||||||
|
|
||||||
|
* `image`: the final built image object
|
||||||
|
* `logs`: a stream of build output messages
|
||||||
|
|
||||||
|
Printing build logs is important because:
|
||||||
|
|
||||||
|
* Docker build failures are only visible in logs
|
||||||
|
* CI pipelines rely on this output for debugging
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Advanced Build with Custom Dockerfile and Build Arguments
|
||||||
|
|
||||||
|
```python
|
||||||
|
def build_image_2():
|
||||||
|
image, logs = client.images.build(
|
||||||
|
path=".",
|
||||||
|
dockerfile="Dockerfile.prod",
|
||||||
|
tag="myapp:prod",
|
||||||
|
buildargs={
|
||||||
|
"APP_ENV": "production",
|
||||||
|
"VERSION": "1.0.0"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
This version adds more control:
|
||||||
|
|
||||||
|
* `dockerfile="Dockerfile.prod"`
|
||||||
|
|
||||||
|
* Allows multiple Dockerfiles per project
|
||||||
|
* Common for dev vs prod builds
|
||||||
|
|
||||||
|
* `buildargs`
|
||||||
|
|
||||||
|
* Passed to `ARG` instructions inside the Dockerfile
|
||||||
|
* Enables parameterized builds without editing the Dockerfile
|
||||||
|
|
||||||
|
Typical DevOps use cases:
|
||||||
|
|
||||||
|
* Environment-specific builds
|
||||||
|
* Injecting version numbers
|
||||||
|
* Feature flags during build time
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Tagging Images
|
||||||
|
|
||||||
|
```python
|
||||||
|
def tag_image():
|
||||||
|
image = client.images.get("myapp:1.0")
|
||||||
|
image.tag("myrepo/myapp", tag="latest")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* Docker images are immutable, but tags are not.
|
||||||
|
* This creates an additional reference to the same image ID.
|
||||||
|
|
||||||
|
Why tagging is important:
|
||||||
|
|
||||||
|
* One image can have multiple tags
|
||||||
|
* Tags represent lifecycle stages (`1.0`, `prod`, `latest`)
|
||||||
|
|
||||||
|
This is how promotion pipelines work:
|
||||||
|
|
||||||
|
* Build once
|
||||||
|
* Tag many times
|
||||||
|
* Push selectively
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Removing Images
|
||||||
|
|
||||||
|
```python
|
||||||
|
def remove_image():
|
||||||
|
client.images.remove("myapp:1.0", force=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* Removes the image reference from the local Docker host.
|
||||||
|
* `force=True` removes the image even if it is in use by stopped containers.
|
||||||
|
|
||||||
|
Use with care:
|
||||||
|
|
||||||
|
* Running containers still prevent deletion
|
||||||
|
* Forced removal is destructive
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Pushing Images to a Registry
|
||||||
|
|
||||||
|
```python
|
||||||
|
def push_image():
|
||||||
|
client.images.push(
|
||||||
|
repository="myrepo/myapp",
|
||||||
|
tag="latest"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* Uploads the image layers to a registry.
|
||||||
|
* Requires prior authentication (`client.login`).
|
||||||
|
|
||||||
|
Important notes:
|
||||||
|
|
||||||
|
* Only new or changed layers are pushed
|
||||||
|
* Tags determine what remote users pull
|
||||||
|
|
||||||
|
This step is usually automated in:
|
||||||
|
|
||||||
|
* CI/CD pipelines
|
||||||
|
* Release workflows
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Cleaning Up Unused Images
|
||||||
|
|
||||||
|
```python
|
||||||
|
def prune_images():
|
||||||
|
result = client.images.prune()
|
||||||
|
print(result)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* Removes dangling images (untagged and unused).
|
||||||
|
* Helps reclaim disk space on build servers.
|
||||||
|
|
||||||
|
The result includes:
|
||||||
|
|
||||||
|
* Number of images removed
|
||||||
|
* Amount of disk space freed
|
||||||
|
|
||||||
|
This is essential for:
|
||||||
|
|
||||||
|
* CI runners
|
||||||
|
* Long-lived build machines
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Inspecting Image Metadata
|
||||||
|
|
||||||
|
```python
|
||||||
|
def inspect_image():
|
||||||
|
alpine_image = client.images.get("alpine:3.20")
|
||||||
|
|
||||||
|
print(alpine_image.attrs)
|
||||||
|
print(alpine_image.attrs["Id"])
|
||||||
|
print(alpine_image.attrs["Size"])
|
||||||
|
print(alpine_image.attrs["Config"]["Env"])
|
||||||
|
print(alpine_image.attrs["Config"]["Cmd"])
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `attrs` exposes the raw Docker image inspection data.
|
||||||
|
* This is equivalent to `docker image inspect`.
|
||||||
|
|
||||||
|
Useful fields:
|
||||||
|
|
||||||
|
* `Id`: content-addressable image hash
|
||||||
|
* `Size`: image size in bytes
|
||||||
|
* `Config.Env`: environment variables baked into the image
|
||||||
|
* `Config.Cmd`: default command
|
||||||
|
|
||||||
|
This information is often used for:
|
||||||
|
|
||||||
|
* Debugging unexpected behavior
|
||||||
|
* Auditing images
|
||||||
|
* Validating build output
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Listing Local Images
|
||||||
|
|
||||||
|
```python
|
||||||
|
def image_list():
|
||||||
|
images = client.images.list()
|
||||||
|
for item in images:
|
||||||
|
print(item.id, item.tags)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* Lists all images stored locally.
|
||||||
|
* Each image may have multiple tags or none.
|
||||||
|
|
||||||
|
This mirrors:
|
||||||
|
|
||||||
|
* `docker images`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Ensuring an Image Exists
|
||||||
|
|
||||||
|
```python
|
||||||
|
def ensure_image(name):
|
||||||
|
try:
|
||||||
|
client.images.get(name)
|
||||||
|
print(f"{name} exists")
|
||||||
|
except docker.errors.ImageNotFound:
|
||||||
|
print(f"Pulling {name}")
|
||||||
|
client.images.pull(name)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
This is a very common DevOps pattern:
|
||||||
|
|
||||||
|
* Check if the image exists locally
|
||||||
|
* Pull it only if necessary
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
|
||||||
|
* Avoids unnecessary network calls
|
||||||
|
* Makes scripts idempotent
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Important Exceptions to Know
|
||||||
|
|
||||||
|
When working with images, you must handle failures explicitly.
|
||||||
|
|
||||||
|
### `docker.errors.ImageNotFound`
|
||||||
|
|
||||||
|
* Raised when an image does not exist locally
|
||||||
|
* Common when calling `get()`
|
||||||
|
|
||||||
|
### `docker.errors.BuildError`
|
||||||
|
|
||||||
|
* Raised when an image build fails
|
||||||
|
* Usually due to Dockerfile errors or missing files
|
||||||
|
|
||||||
|
### `docker.errors.APIError`
|
||||||
|
|
||||||
|
* Raised for general Docker API failures
|
||||||
|
* Includes permission issues, daemon errors, and network problems
|
||||||
|
|
||||||
|
Catching these exceptions is critical for:
|
||||||
|
|
||||||
|
* Reliable automation
|
||||||
|
* Meaningful error reporting
|
||||||
|
* CI/CD stability
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Summary
|
||||||
|
|
||||||
|
In this section, you learned how to:
|
||||||
|
|
||||||
|
* Pull images from registries
|
||||||
|
* Build images using Dockerfiles
|
||||||
|
* Use build arguments and multiple Dockerfiles
|
||||||
|
* Tag and push images
|
||||||
|
* Inspect and list images
|
||||||
|
* Clean up unused images
|
||||||
|
* Handle common Docker image errors
|
||||||
|
|
||||||
|
This image workflow is the backbone of:
|
||||||
|
|
||||||
|
* CI pipelines
|
||||||
|
* Release automation
|
||||||
|
* Platform engineering systems
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
Official Docker SDK for Python documentation:
|
||||||
|
|
||||||
|
* [https://docker-py.readthedocs.io/](https://docker-py.readthedocs.io/)
|
||||||
284
Docs/Libs/Docker/03-Container-Lifecycle.md
Normal file
284
Docs/Libs/Docker/03-Container-Lifecycle.md
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
# Docker SDK for Python – Working with Containers
|
||||||
|
|
||||||
|
This document explains how to **manage Docker containers** using the Docker SDK for Python. Containers are the runtime unit of Docker, so this section focuses on lifecycle management: listing, creating, running, starting, stopping, restarting, pausing, and inspecting logs.
|
||||||
|
|
||||||
|
The goal is to understand **how container state changes**, how the Python SDK maps to Docker CLI behavior, and how these operations are typically used in real DevOps automation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Creating the Docker Client
|
||||||
|
|
||||||
|
```python
|
||||||
|
import docker
|
||||||
|
import time
|
||||||
|
|
||||||
|
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||||||
|
ping_docker = client.ping()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* Establishes a connection to the Docker daemon via the Unix socket.
|
||||||
|
* `ping()` is used as a sanity check before executing container operations.
|
||||||
|
|
||||||
|
In production-grade scripts, this check prevents:
|
||||||
|
|
||||||
|
* Silent failures
|
||||||
|
* Partial execution when Docker is down
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Listing Running Containers
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_containers_list():
|
||||||
|
containers = client.containers.list()
|
||||||
|
|
||||||
|
for c in containers:
|
||||||
|
print(c.id, c.name, c.status)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `containers.list()` returns **only running containers** by default.
|
||||||
|
* Each returned object represents a live container instance.
|
||||||
|
|
||||||
|
Common attributes:
|
||||||
|
|
||||||
|
* `id`: unique container identifier
|
||||||
|
* `name`: human-readable container name
|
||||||
|
* `status`: runtime state (running)
|
||||||
|
|
||||||
|
This is equivalent to:
|
||||||
|
|
||||||
|
* `docker ps`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Listing All Containers (Including Stopped)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_all_containers_list():
|
||||||
|
containers = client.containers.list(all=True)
|
||||||
|
|
||||||
|
for c in containers:
|
||||||
|
print(c.id, c.name, c.status)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `all=True` includes stopped, exited, and created containers.
|
||||||
|
* Useful for cleanup, auditing, and lifecycle reconciliation.
|
||||||
|
|
||||||
|
Equivalent CLI command:
|
||||||
|
|
||||||
|
* `docker ps -a`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Creating a Container (Without Starting It)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def create_container():
|
||||||
|
container = client.containers.create(
|
||||||
|
image="nginx:latest",
|
||||||
|
name="my_nginx",
|
||||||
|
ports={"80/tcp": 8080},
|
||||||
|
detach=True
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
This creates a container in the **created** state.
|
||||||
|
|
||||||
|
Key parameters:
|
||||||
|
|
||||||
|
* `image`: base image for the container
|
||||||
|
* `name`: explicit container name
|
||||||
|
* `ports`: port mapping (container → host)
|
||||||
|
* `detach=True`: container runs in background when started
|
||||||
|
|
||||||
|
Important distinction:
|
||||||
|
|
||||||
|
* `create()` does **not** start the container
|
||||||
|
* This mirrors `docker create`
|
||||||
|
|
||||||
|
Use cases:
|
||||||
|
|
||||||
|
* Deferred startup
|
||||||
|
* Multi-step initialization
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Running a Container (Create + Start)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def run_container():
|
||||||
|
container = client.containers.run(
|
||||||
|
"nginx:latest",
|
||||||
|
name="nginx_run",
|
||||||
|
ports={"80/tcp": 8081},
|
||||||
|
detach=True
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* `run()` is a convenience method.
|
||||||
|
* Internally performs `create()` followed by `start()`.
|
||||||
|
|
||||||
|
Equivalent CLI command:
|
||||||
|
|
||||||
|
* `docker run -d -p 8081:80 nginx:latest`
|
||||||
|
|
||||||
|
This is the most common approach for:
|
||||||
|
|
||||||
|
* Short-lived workloads
|
||||||
|
* Simple services
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Managing Container Lifecycle
|
||||||
|
|
||||||
|
```python
|
||||||
|
def start_container():
|
||||||
|
container = client.containers.get("my_nginx")
|
||||||
|
|
||||||
|
container.start()
|
||||||
|
time.sleep(1000)
|
||||||
|
|
||||||
|
container.restart()
|
||||||
|
time.sleep(1000)
|
||||||
|
|
||||||
|
container.pause()
|
||||||
|
container.unpause()
|
||||||
|
time.sleep(1000)
|
||||||
|
|
||||||
|
container.stop(timeout=10)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
This function demonstrates multiple lifecycle transitions.
|
||||||
|
|
||||||
|
#### `containers.get(name)`
|
||||||
|
|
||||||
|
* Retrieves an existing container by name or ID.
|
||||||
|
* Raises an exception if the container does not exist.
|
||||||
|
|
||||||
|
#### `start()`
|
||||||
|
|
||||||
|
* Transitions container from `created` or `stopped` → `running`.
|
||||||
|
|
||||||
|
#### `restart()`
|
||||||
|
|
||||||
|
* Stops and immediately starts the container.
|
||||||
|
* Often used after configuration changes.
|
||||||
|
|
||||||
|
#### `pause()` / `unpause()`
|
||||||
|
|
||||||
|
* Freezes container processes using cgroups.
|
||||||
|
* Network and filesystem state remain intact.
|
||||||
|
|
||||||
|
#### `stop(timeout=10)`
|
||||||
|
|
||||||
|
* Sends SIGTERM, then SIGKILL after timeout.
|
||||||
|
* Allows graceful shutdown.
|
||||||
|
|
||||||
|
The `sleep()` calls simulate:
|
||||||
|
|
||||||
|
* Long-running services
|
||||||
|
* Observing container behavior between states
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Reading Container Logs
|
||||||
|
|
||||||
|
```python
|
||||||
|
def log_container():
|
||||||
|
container = client.containers.get("my_nginx")
|
||||||
|
logs = container.logs(tail=20)
|
||||||
|
print(logs.decode())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* Retrieves logs from the container’s stdout/stderr.
|
||||||
|
* `tail=20` limits output to the last 20 lines.
|
||||||
|
|
||||||
|
Important details:
|
||||||
|
|
||||||
|
* Logs are returned as bytes
|
||||||
|
* Decoding is required for readable output
|
||||||
|
|
||||||
|
Equivalent CLI command:
|
||||||
|
|
||||||
|
* `docker logs --tail 20 my_nginx`
|
||||||
|
|
||||||
|
This is critical for:
|
||||||
|
|
||||||
|
* Debugging runtime issues
|
||||||
|
* Health checks
|
||||||
|
* Observability tooling
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Conditional Execution Based on Docker Availability
|
||||||
|
|
||||||
|
```python
|
||||||
|
if ping_docker:
|
||||||
|
get_containers_list()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
* Ensures container operations only run if Docker is reachable.
|
||||||
|
* Prevents unhandled API errors.
|
||||||
|
|
||||||
|
This pattern is common in:
|
||||||
|
|
||||||
|
* Entry-point scripts
|
||||||
|
* Health probes
|
||||||
|
* Automation jobs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Container States (Conceptual Model)
|
||||||
|
|
||||||
|
Understanding container states is essential:
|
||||||
|
|
||||||
|
* `created`: container exists but is not running
|
||||||
|
* `running`: container processes are active
|
||||||
|
* `paused`: execution is suspended
|
||||||
|
* `exited`: container stopped normally
|
||||||
|
* `dead`: container failed unexpectedly
|
||||||
|
|
||||||
|
Every method in this document transitions the container between these states.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Summary
|
||||||
|
|
||||||
|
In this section, you learned how to:
|
||||||
|
|
||||||
|
* List running and stopped containers
|
||||||
|
* Create containers separately from starting them
|
||||||
|
* Run containers in a single step
|
||||||
|
* Control container lifecycle states
|
||||||
|
* Read container logs
|
||||||
|
* Safely execute operations based on Docker availability
|
||||||
|
|
||||||
|
This container-level control is the foundation for:
|
||||||
|
|
||||||
|
* Service orchestration
|
||||||
|
* CI/CD job execution
|
||||||
|
* Debugging and recovery automation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
Official Docker SDK for Python documentation:
|
||||||
|
|
||||||
|
* [https://docker-py.readthedocs.io/](https://docker-py.readthedocs.io/)
|
||||||
0
Docs/Libs/Docker/04-Container-Configuration.md
Normal file
0
Docs/Libs/Docker/04-Container-Configuration.md
Normal file
0
Docs/Libs/Docker/05-Networks.md
Normal file
0
Docs/Libs/Docker/05-Networks.md
Normal file
0
Docs/Libs/Docker/06-Volumes.md
Normal file
0
Docs/Libs/Docker/06-Volumes.md
Normal file
0
Docs/Libs/Docker/07-Monitoring.md
Normal file
0
Docs/Libs/Docker/07-Monitoring.md
Normal file
0
Docs/Libs/Docker/08-Error-Handeling.md
Normal file
0
Docs/Libs/Docker/08-Error-Handeling.md
Normal file
0
Docs/Libs/Docker/09-Compose.md
Normal file
0
Docs/Libs/Docker/09-Compose.md
Normal file
271
Docs/Libs/FastAPI/02-Simple-Route.md
Normal file
271
Docs/Libs/FastAPI/02-Simple-Route.md
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
# FastAPI – Simple Route Example
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document explains how to create a basic FastAPI application with a single HTTP route and how to run it using `uvicorn`.
|
||||||
|
|
||||||
|
FastAPI is an ASGI web framework, and `uvicorn` is commonly used as the ASGI server to run FastAPI applications.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Create a Simple FastAPI Application
|
||||||
|
|
||||||
|
Create a Python file named:
|
||||||
|
|
||||||
|
```text
|
||||||
|
main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the following code:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def home_dir():
|
||||||
|
return {"message": "Home Page"}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Code Explanation
|
||||||
|
|
||||||
|
## Import FastAPI
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
```
|
||||||
|
|
||||||
|
This imports the `FastAPI` class from the FastAPI package.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Create the Application Instance
|
||||||
|
|
||||||
|
```python
|
||||||
|
app = FastAPI()
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates the main FastAPI application instance.
|
||||||
|
|
||||||
|
The variable name `app` is important because it is later used by `uvicorn` when starting the server.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Define a GET Route
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.get("/")
|
||||||
|
def home_dir():
|
||||||
|
return {"message": "Home Page"}
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates an HTTP `GET` endpoint at the root path:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /
|
||||||
|
```
|
||||||
|
|
||||||
|
When a user sends a request to `/`, the `home_dir` function is executed.
|
||||||
|
|
||||||
|
The function returns a Python dictionary:
|
||||||
|
|
||||||
|
```python
|
||||||
|
{"message": "Home Page"}
|
||||||
|
```
|
||||||
|
|
||||||
|
FastAPI automatically converts this dictionary into a JSON response.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Running the Application
|
||||||
|
|
||||||
|
FastAPI applications are usually run using `uvicorn`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Option 1: Run Using FastAPI CLI
|
||||||
|
|
||||||
|
You can run the application in development mode using:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
fastapi dev main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This command starts the FastAPI application in development mode.
|
||||||
|
|
||||||
|
It is useful during local development because it supports automatic reload when the source code changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Option 2: Run Directly with Uvicorn
|
||||||
|
|
||||||
|
You can also run the application directly using `uvicorn`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the more explicit and commonly used method.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Uvicorn Command Breakdown
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
||||||
|
```
|
||||||
|
|
||||||
|
## `uvicorn`
|
||||||
|
|
||||||
|
Starts the ASGI server.
|
||||||
|
|
||||||
|
## `main`
|
||||||
|
|
||||||
|
Refers to the Python file name:
|
||||||
|
|
||||||
|
```text
|
||||||
|
main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
You write `main`, not `main.py`.
|
||||||
|
|
||||||
|
## `app`
|
||||||
|
|
||||||
|
Refers to the FastAPI application instance:
|
||||||
|
|
||||||
|
```python
|
||||||
|
app = FastAPI()
|
||||||
|
```
|
||||||
|
|
||||||
|
## `--reload`
|
||||||
|
|
||||||
|
Automatically restarts the server when code changes.
|
||||||
|
|
||||||
|
This should only be used in development.
|
||||||
|
|
||||||
|
## `--host 0.0.0.0`
|
||||||
|
|
||||||
|
Makes the application listen on all available network interfaces.
|
||||||
|
|
||||||
|
This is useful when running inside Docker, virtual machines, or remote servers.
|
||||||
|
|
||||||
|
## `--port 1234`
|
||||||
|
|
||||||
|
Runs the application on port `1234`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Accessing the Application
|
||||||
|
|
||||||
|
After starting the server, the application will be available at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:1234/
|
||||||
|
```
|
||||||
|
|
||||||
|
The response will be:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "Home Page"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Interactive API Documentation
|
||||||
|
|
||||||
|
FastAPI automatically generates API documentation.
|
||||||
|
|
||||||
|
## Swagger UI
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:1234/docs
|
||||||
|
```
|
||||||
|
|
||||||
|
Swagger UI allows you to test API endpoints directly from the browser.
|
||||||
|
|
||||||
|
## ReDoc
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:1234/redoc
|
||||||
|
```
|
||||||
|
|
||||||
|
ReDoc provides a clean documentation view for the API.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Complete Example
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def home_dir():
|
||||||
|
return {"message": "Home Page"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Run it with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Best Practices
|
||||||
|
|
||||||
|
Use `--reload` only during development.
|
||||||
|
|
||||||
|
Use a consistent application entry point such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
main:app
|
||||||
|
```
|
||||||
|
|
||||||
|
Explicitly define the host and port in Docker, cloud, or server environments.
|
||||||
|
|
||||||
|
Use `0.0.0.0` when the application needs to be reachable from outside the local machine.
|
||||||
|
|
||||||
|
Use `127.0.0.1` or `localhost` when the application should only be accessible locally.
|
||||||
|
|
||||||
|
Do not expose development servers directly to the internet.
|
||||||
|
|
||||||
|
In production, run FastAPI behind a proper process manager, reverse proxy, or container orchestration platform.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. DevOps Production Note
|
||||||
|
|
||||||
|
The following command is suitable for local development:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
||||||
|
```
|
||||||
|
|
||||||
|
For production, avoid using `--reload`.
|
||||||
|
|
||||||
|
A typical production setup may include:
|
||||||
|
|
||||||
|
```text
|
||||||
|
FastAPI
|
||||||
|
Uvicorn or Gunicorn with Uvicorn workers
|
||||||
|
Nginx or Traefik
|
||||||
|
Docker or Kubernetes
|
||||||
|
Logging and monitoring
|
||||||
|
Health checks
|
||||||
|
Environment-based configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
Example production-style command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --host 0.0.0.0 --port 1234
|
||||||
|
```
|
||||||
566
Docs/Libs/FastAPI/03-Get-Method.md
Normal file
566
Docs/Libs/FastAPI/03-Get-Method.md
Normal file
@@ -0,0 +1,566 @@
|
|||||||
|
# FastAPI – GET Endpoints and JSON Responses
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document explains how to create multiple `GET` endpoints in FastAPI, return JSON responses, and use path parameters to retrieve specific data from an in-memory dataset.
|
||||||
|
|
||||||
|
The example uses a simple list of users to simulate a database.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Example Application
|
||||||
|
|
||||||
|
Create or update `main.py` with the following content:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
users = [
|
||||||
|
{"name": "abbas", "age": 20},
|
||||||
|
{"name": "mmd", "age": 37},
|
||||||
|
{"name": "asghar", "age": 19},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def root_dir():
|
||||||
|
return {"message": "API is working"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/users")
|
||||||
|
def get_users():
|
||||||
|
return users
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/user/{name_input}")
|
||||||
|
def get_user_by_name(name_input: str):
|
||||||
|
for item in users:
|
||||||
|
if item["name"] == name_input:
|
||||||
|
return {"information": item}
|
||||||
|
|
||||||
|
return {"message": "User not found"}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Application Initialization
|
||||||
|
|
||||||
|
```python
|
||||||
|
app = FastAPI()
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates the main FastAPI application instance.
|
||||||
|
|
||||||
|
The variable `app` is used by `uvicorn` when starting the service.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
In `main:app`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
main = Python file name
|
||||||
|
app = FastAPI application instance
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. In-Memory Data Store
|
||||||
|
|
||||||
|
```python
|
||||||
|
users = [
|
||||||
|
{"name": "abbas", "age": 20},
|
||||||
|
{"name": "mmd", "age": 37},
|
||||||
|
{"name": "asghar", "age": 19},
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
This list is used as temporary storage.
|
||||||
|
|
||||||
|
Each user is represented as a Python dictionary.
|
||||||
|
|
||||||
|
FastAPI can automatically convert these dictionaries into JSON responses.
|
||||||
|
|
||||||
|
Example Python object:
|
||||||
|
|
||||||
|
```python
|
||||||
|
{"name": "abbas", "age": 20}
|
||||||
|
```
|
||||||
|
|
||||||
|
Example JSON response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "abbas",
|
||||||
|
"age": 20
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Note
|
||||||
|
|
||||||
|
This in-memory list is only suitable for learning, development, and testing.
|
||||||
|
|
||||||
|
If the application restarts, the data will be reset.
|
||||||
|
|
||||||
|
For production, use a persistent database such as PostgreSQL, MySQL, MongoDB, or another database system.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Defined Endpoints
|
||||||
|
|
||||||
|
The application defines three `GET` endpoints:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /
|
||||||
|
GET /users
|
||||||
|
GET /user/{name_input}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Root Endpoint
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.get("/")
|
||||||
|
def root_dir():
|
||||||
|
return {"message": "API is working"}
|
||||||
|
```
|
||||||
|
|
||||||
|
This endpoint is available at:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /
|
||||||
|
```
|
||||||
|
|
||||||
|
It returns a simple JSON response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "API is working"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This type of endpoint is commonly used as a simple health check.
|
||||||
|
|
||||||
|
For example, load balancers, monitoring systems, or Kubernetes probes can use it to check whether the API is reachable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Get All Users Endpoint
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.get("/users")
|
||||||
|
def get_users():
|
||||||
|
return users
|
||||||
|
```
|
||||||
|
|
||||||
|
This endpoint is available at:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /users
|
||||||
|
```
|
||||||
|
|
||||||
|
It returns the full list of users.
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "abbas",
|
||||||
|
"age": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mmd",
|
||||||
|
"age": 37
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "asghar",
|
||||||
|
"age": 19
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
FastAPI automatically serializes the Python list into a JSON array.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Get User by Name Endpoint
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.get("/user/{name_input}")
|
||||||
|
def get_user_by_name(name_input: str):
|
||||||
|
for item in users:
|
||||||
|
if item["name"] == name_input:
|
||||||
|
return {"information": item}
|
||||||
|
|
||||||
|
return {"message": "User not found"}
|
||||||
|
```
|
||||||
|
|
||||||
|
This endpoint is available at:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /user/{name_input}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `{name_input}` part is a path parameter.
|
||||||
|
|
||||||
|
Example request:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /user/abbas
|
||||||
|
```
|
||||||
|
|
||||||
|
In this request, FastAPI assigns:
|
||||||
|
|
||||||
|
```python
|
||||||
|
name_input = "abbas"
|
||||||
|
```
|
||||||
|
|
||||||
|
The function then searches the `users` list for a user whose name matches the input.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Successful Response Example
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /user/abbas
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"information": {
|
||||||
|
"name": "abbas",
|
||||||
|
"age": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. Failure Response Example
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /user/ali
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "User not found"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the current simple version, the API returns a normal JSON message even when the user does not exist.
|
||||||
|
|
||||||
|
However, in a real API, it is better to return a proper `404 Not Found` response.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. Path Parameters
|
||||||
|
|
||||||
|
A path parameter is a dynamic part of the URL.
|
||||||
|
|
||||||
|
In this route:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.get("/user/{name_input}")
|
||||||
|
```
|
||||||
|
|
||||||
|
`name_input` is a path parameter.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /user/mmd
|
||||||
|
```
|
||||||
|
|
||||||
|
FastAPI extracts the value from the URL and passes it to the function:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_user_by_name(name_input: str):
|
||||||
|
```
|
||||||
|
|
||||||
|
Because `name_input` is defined as a string:
|
||||||
|
|
||||||
|
```python
|
||||||
|
name_input: str
|
||||||
|
```
|
||||||
|
|
||||||
|
FastAPI validates it as a string.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 11. Better Version with HTTPException
|
||||||
|
|
||||||
|
The previous version works, but it does not return the correct HTTP status code when a user is not found.
|
||||||
|
|
||||||
|
A better version uses `HTTPException`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, HTTPException, status
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
users = [
|
||||||
|
{"name": "abbas", "age": 20},
|
||||||
|
{"name": "mmd", "age": 37},
|
||||||
|
{"name": "asghar", "age": 19},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def root_dir():
|
||||||
|
return {"message": "API is working"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/users")
|
||||||
|
def get_users():
|
||||||
|
return users
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/user/{name_input}")
|
||||||
|
def get_user_by_name(name_input: str):
|
||||||
|
for item in users:
|
||||||
|
if item["name"] == name_input:
|
||||||
|
return {"information": item}
|
||||||
|
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="User not found"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 12. Improved Failure Response
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /user/ali
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"detail": "User not found"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP status code:
|
||||||
|
|
||||||
|
```http
|
||||||
|
404 Not Found
|
||||||
|
```
|
||||||
|
|
||||||
|
This is better because the API response now correctly tells the client that the requested resource does not exist.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 13. Running the Application
|
||||||
|
|
||||||
|
Start the FastAPI application using `uvicorn`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
The application will be available at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 14. Accessing the Endpoints
|
||||||
|
|
||||||
|
Root endpoint:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/
|
||||||
|
```
|
||||||
|
|
||||||
|
Get all users:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/users
|
||||||
|
```
|
||||||
|
|
||||||
|
Get user by name:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/user/abbas
|
||||||
|
```
|
||||||
|
|
||||||
|
Interactive API documentation:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/docs
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternative API documentation:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/redoc
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 15. Testing with curl
|
||||||
|
|
||||||
|
## Test Root Endpoint
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "API is working"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Get All Users
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/users
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "abbas",
|
||||||
|
"age": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mmd",
|
||||||
|
"age": 37
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "asghar",
|
||||||
|
"age": 19
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Get User by Name
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/user/abbas
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"information": {
|
||||||
|
"name": "abbas",
|
||||||
|
"age": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test User Not Found
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8000/user/ali
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"detail": "User not found"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 16. Complete Recommended Version
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, HTTPException, status
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
users = [
|
||||||
|
{"name": "abbas", "age": 20},
|
||||||
|
{"name": "mmd", "age": 37},
|
||||||
|
{"name": "asghar", "age": 19},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def root_dir():
|
||||||
|
return {"message": "API is working"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/users")
|
||||||
|
def get_users():
|
||||||
|
return users
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/user/{name_input}")
|
||||||
|
def get_user_by_name(name_input: str):
|
||||||
|
for item in users:
|
||||||
|
if item["name"] == name_input:
|
||||||
|
return {"information": item}
|
||||||
|
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="User not found"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 17. Best Practices
|
||||||
|
|
||||||
|
Use `GET` endpoints only for retrieving data.
|
||||||
|
|
||||||
|
Do not use `GET` requests to create, update, or delete resources.
|
||||||
|
|
||||||
|
Return structured JSON responses.
|
||||||
|
|
||||||
|
Use proper HTTP status codes.
|
||||||
|
|
||||||
|
Use `404 Not Found` when a requested resource does not exist.
|
||||||
|
|
||||||
|
Use `200 OK` when the request is successful.
|
||||||
|
|
||||||
|
Replace in-memory lists with a real database in production.
|
||||||
|
|
||||||
|
Keep route names clear and consistent.
|
||||||
|
|
||||||
|
Use plural naming for collection endpoints, such as:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /users
|
||||||
|
```
|
||||||
|
|
||||||
|
Use specific resource endpoints for single items, such as:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /users/{username}
|
||||||
|
```
|
||||||
|
|
||||||
|
As the project grows, separate code into different files for routes, models, services, and database logic.
|
||||||
369
Docs/Libs/FastAPI/04-Post-Method.md
Normal file
369
Docs/Libs/FastAPI/04-Post-Method.md
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
# FastAPI – POST Endpoint and JSON Input
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document explains how to create a `POST` endpoint in FastAPI for adding new users. It covers two approaches:
|
||||||
|
|
||||||
|
1. Sending input as query parameters
|
||||||
|
2. Sending input as a JSON request body using a Pydantic model
|
||||||
|
|
||||||
|
For real API development, the Pydantic model approach is recommended because it provides a cleaner structure, better validation, and more realistic request handling.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Example Application Using Query Parameters
|
||||||
|
|
||||||
|
Create or update `main.py` with the following code:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
users = [
|
||||||
|
{"name": "abbas", "age": 20},
|
||||||
|
{"name": "mmd", "age": 37},
|
||||||
|
{"name": "asghar", "age": 19},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def home_page():
|
||||||
|
return {"msg": "API is working"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/new_user")
|
||||||
|
def create_user(name: str, age: int):
|
||||||
|
new_user = {"name": name, "age": age}
|
||||||
|
users.append(new_user)
|
||||||
|
return {"msg": "User created successfully"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.post("/new_user")
|
||||||
|
def create_user(name: str, age: int):
|
||||||
|
```
|
||||||
|
|
||||||
|
This registers a `POST` endpoint at:
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /new_user
|
||||||
|
```
|
||||||
|
|
||||||
|
The endpoint receives two parameters:
|
||||||
|
|
||||||
|
```python
|
||||||
|
name: str
|
||||||
|
age: int
|
||||||
|
```
|
||||||
|
|
||||||
|
FastAPI automatically validates the input types. If `age` is not an integer, FastAPI returns a validation error.
|
||||||
|
|
||||||
|
## Example Request
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/new_user?name=ali&age=25"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Response
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "User created successfully"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Note
|
||||||
|
|
||||||
|
In this version, the data is sent through query parameters, not as a JSON body.
|
||||||
|
|
||||||
|
This is acceptable for simple testing, but it is not the best practice for real APIs that create resources.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Recommended Application Using JSON Body
|
||||||
|
|
||||||
|
A better approach is to define a Pydantic model and receive the user data as JSON.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class User(BaseModel):
|
||||||
|
name: str
|
||||||
|
age: int
|
||||||
|
|
||||||
|
|
||||||
|
users = [
|
||||||
|
{"name": "abbas", "age": 20},
|
||||||
|
{"name": "mmd", "age": 37},
|
||||||
|
{"name": "asghar", "age": 19},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def home_page():
|
||||||
|
return {"msg": "API is working"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/new_user")
|
||||||
|
def create_user(user: User):
|
||||||
|
new_user = {"name": user.name, "age": user.age}
|
||||||
|
users.append(new_user)
|
||||||
|
return {"msg": "User created successfully"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
```python
|
||||||
|
class User(BaseModel):
|
||||||
|
name: str
|
||||||
|
age: int
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a request model named `User`.
|
||||||
|
|
||||||
|
The model defines the expected structure of the JSON input:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "ali",
|
||||||
|
"age": 25
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
FastAPI uses this model to:
|
||||||
|
|
||||||
|
Validate incoming data
|
||||||
|
Convert JSON into a Python object
|
||||||
|
Generate automatic API documentation
|
||||||
|
Return useful validation errors when input is invalid
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. POST Endpoint Definition
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.post("/new_user")
|
||||||
|
def create_user(user: User):
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a `POST` endpoint at:
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /new_user
|
||||||
|
```
|
||||||
|
|
||||||
|
The endpoint expects a JSON request body matching the `User` model.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Creating a New Resource
|
||||||
|
|
||||||
|
```python
|
||||||
|
new_user = {"name": user.name, "age": user.age}
|
||||||
|
users.append(new_user)
|
||||||
|
```
|
||||||
|
|
||||||
|
This code creates a new dictionary using the received request data and appends it to the `users` list.
|
||||||
|
|
||||||
|
The `users` list acts as temporary in-memory storage.
|
||||||
|
|
||||||
|
This simulates inserting a new record into a database.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Returning a JSON Response
|
||||||
|
|
||||||
|
```python
|
||||||
|
return {"msg": "User created successfully"}
|
||||||
|
```
|
||||||
|
|
||||||
|
FastAPI automatically converts the returned dictionary into a JSON response.
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "User created successfully"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Example Request Using curl
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/new_user" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"name": "ali", "age": 25}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "User created successfully"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Verifying the Result
|
||||||
|
|
||||||
|
To verify that the user was added, you should also define a `GET /users` endpoint.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.get("/users")
|
||||||
|
def get_users():
|
||||||
|
return users
|
||||||
|
```
|
||||||
|
|
||||||
|
Then send this request:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /users
|
||||||
|
```
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "abbas",
|
||||||
|
"age": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mmd",
|
||||||
|
"age": 37
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "asghar",
|
||||||
|
"age": 19
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ali",
|
||||||
|
"age": 25
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Complete Recommended Version
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, status
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class User(BaseModel):
|
||||||
|
name: str
|
||||||
|
age: int
|
||||||
|
|
||||||
|
|
||||||
|
users = [
|
||||||
|
{"name": "abbas", "age": 20},
|
||||||
|
{"name": "mmd", "age": 37},
|
||||||
|
{"name": "asghar", "age": 19},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def home_page():
|
||||||
|
return {"msg": "API is working"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/users")
|
||||||
|
def get_users():
|
||||||
|
return users
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/new_user", status_code=status.HTTP_201_CREATED)
|
||||||
|
def create_user(user: User):
|
||||||
|
new_user = {"name": user.name, "age": user.age}
|
||||||
|
users.append(new_user)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"msg": "User created successfully",
|
||||||
|
"user": new_user
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. Running the Application
|
||||||
|
|
||||||
|
Start the FastAPI application using `uvicorn`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
The API will run at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Interactive API documentation will be available at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/docs
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. Best Practices
|
||||||
|
|
||||||
|
Use `POST` requests when creating new resources.
|
||||||
|
|
||||||
|
Use Pydantic models for request bodies instead of query parameters.
|
||||||
|
|
||||||
|
Return proper HTTP status codes, such as:
|
||||||
|
|
||||||
|
```http
|
||||||
|
201 Created
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not use in-memory lists for production data storage.
|
||||||
|
|
||||||
|
Use a real database such as PostgreSQL, MySQL, or MongoDB in production.
|
||||||
|
|
||||||
|
Validate and sanitize all client-provided input.
|
||||||
|
|
||||||
|
Keep model names clear and professional. For example, use `User` instead of `user_class`.
|
||||||
|
|
||||||
|
Return meaningful responses that include the created resource when useful.
|
||||||
|
|
||||||
|
Use `/docs` to test and verify API behavior during development.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 11. Production Note for DevOps
|
||||||
|
|
||||||
|
The in-memory `users` list is reset every time the application restarts. In production environments, application data must be stored in a persistent database.
|
||||||
|
|
||||||
|
For production deployment, the FastAPI application should usually run behind a process manager and reverse proxy, such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Gunicorn / Uvicorn workers
|
||||||
|
Nginx or Traefik
|
||||||
|
Docker or Kubernetes
|
||||||
|
PostgreSQL or another persistent database
|
||||||
|
```
|
||||||
|
|
||||||
|
The development command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
should not be used in production because `--reload` is intended only for local development.
|
||||||
514
Docs/Libs/FastAPI/09-Responces.md
Normal file
514
Docs/Libs/FastAPI/09-Responces.md
Normal file
@@ -0,0 +1,514 @@
|
|||||||
|
# FastAPI – Response Model and JSONResponse
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document explains two important FastAPI response concepts:
|
||||||
|
|
||||||
|
1. Using `response_model` to control what data is returned to the client
|
||||||
|
2. Using `JSONResponse` when you need explicit control over the response body, status code, or headers
|
||||||
|
|
||||||
|
Response models are especially useful when you want to hide sensitive fields such as passwords from API responses.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Response Model
|
||||||
|
|
||||||
|
## Incorrect Example
|
||||||
|
|
||||||
|
The following code has several issues:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, status , HTTPExption
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class usersin(BaseModel):
|
||||||
|
username: str
|
||||||
|
pass: str
|
||||||
|
|
||||||
|
class usersout(BaseModel):
|
||||||
|
username: str
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/user")
|
||||||
|
def home(user: usersin, responce_model=usersout):
|
||||||
|
return user
|
||||||
|
```
|
||||||
|
|
||||||
|
## Problems in the Code
|
||||||
|
|
||||||
|
### 1. `HTTPExption` is misspelled
|
||||||
|
|
||||||
|
Correct import:
|
||||||
|
|
||||||
|
```python
|
||||||
|
HTTPException
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. `pass` cannot be used as a field name
|
||||||
|
|
||||||
|
`pass` is a reserved keyword in Python.
|
||||||
|
|
||||||
|
Instead of:
|
||||||
|
|
||||||
|
```python
|
||||||
|
pass: str
|
||||||
|
```
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
```python
|
||||||
|
password: str
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. `response_model` is written in the wrong place
|
||||||
|
|
||||||
|
This is incorrect:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def home(user: usersin, responce_model=usersout):
|
||||||
|
```
|
||||||
|
|
||||||
|
`response_model` must be passed inside the route decorator.
|
||||||
|
|
||||||
|
Correct:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.post("/user", response_model=UserOut)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. `responce_model` is misspelled
|
||||||
|
|
||||||
|
Correct spelling:
|
||||||
|
|
||||||
|
```python
|
||||||
|
response_model
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Correct Response Model Example
|
||||||
|
|
||||||
|
Create or update `main.py` with the following content:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class UserIn(BaseModel):
|
||||||
|
username: str
|
||||||
|
password: str
|
||||||
|
|
||||||
|
|
||||||
|
class UserOut(BaseModel):
|
||||||
|
username: str
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/user", response_model=UserOut)
|
||||||
|
def create_user(user: UserIn):
|
||||||
|
return user
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. How Response Model Works
|
||||||
|
|
||||||
|
## Input Model
|
||||||
|
|
||||||
|
```python
|
||||||
|
class UserIn(BaseModel):
|
||||||
|
username: str
|
||||||
|
password: str
|
||||||
|
```
|
||||||
|
|
||||||
|
This model defines the data that the API receives from the client.
|
||||||
|
|
||||||
|
Example request body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "abbas",
|
||||||
|
"password": "123456"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The API accepts both fields:
|
||||||
|
|
||||||
|
```text
|
||||||
|
username
|
||||||
|
password
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Output Model
|
||||||
|
|
||||||
|
```python
|
||||||
|
class UserOut(BaseModel):
|
||||||
|
username: str
|
||||||
|
```
|
||||||
|
|
||||||
|
This model defines the data that the API returns to the client.
|
||||||
|
|
||||||
|
Even though the endpoint receives the password, the response only returns the username.
|
||||||
|
|
||||||
|
Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "abbas"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The password is removed from the response automatically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Endpoint Definition
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.post("/user", response_model=UserOut)
|
||||||
|
def create_user(user: UserIn):
|
||||||
|
return user
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a `POST` endpoint at:
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /user
|
||||||
|
```
|
||||||
|
|
||||||
|
The endpoint receives data based on `UserIn` and returns data based on `UserOut`.
|
||||||
|
|
||||||
|
FastAPI uses `response_model` to filter the response before sending it to the client.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Example Request
|
||||||
|
|
||||||
|
## Using curl
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/user" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"username": "abbas", "password": "123456"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "abbas"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The password is not included in the response.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Why Use Response Models
|
||||||
|
|
||||||
|
Response models are useful because they help you:
|
||||||
|
|
||||||
|
Protect sensitive data
|
||||||
|
Keep API responses consistent
|
||||||
|
Control exactly what the client receives
|
||||||
|
Improve automatic documentation
|
||||||
|
Validate response data before sending it
|
||||||
|
Separate input schemas from output schemas
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Better Version with Status Code
|
||||||
|
|
||||||
|
For user creation endpoints, it is better to return `201 Created`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, status
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class UserIn(BaseModel):
|
||||||
|
username: str
|
||||||
|
password: str
|
||||||
|
|
||||||
|
|
||||||
|
class UserOut(BaseModel):
|
||||||
|
username: str
|
||||||
|
|
||||||
|
|
||||||
|
@app.post(
|
||||||
|
"/user",
|
||||||
|
response_model=UserOut,
|
||||||
|
status_code=status.HTTP_201_CREATED
|
||||||
|
)
|
||||||
|
def create_user(user: UserIn):
|
||||||
|
return user
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. JSONResponse
|
||||||
|
|
||||||
|
FastAPI automatically converts Python dictionaries into JSON responses.
|
||||||
|
|
||||||
|
For most endpoints, this is enough:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.get("/")
|
||||||
|
def home():
|
||||||
|
return {"msg": "API is working"}
|
||||||
|
```
|
||||||
|
|
||||||
|
However, FastAPI also allows you to use `JSONResponse` when you need more control.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. Example Application Using JSONResponse
|
||||||
|
|
||||||
|
Create or update `main.py` with the following content:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, status
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def home():
|
||||||
|
return JSONResponse(
|
||||||
|
content={"msg": "API is working"},
|
||||||
|
status_code=status.HTTP_200_OK
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. Response Behavior
|
||||||
|
|
||||||
|
## Endpoint
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response Body
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"msg": "API is working"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## HTTP Status Code
|
||||||
|
|
||||||
|
```http
|
||||||
|
200 OK
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 11. Default Response vs JSONResponse
|
||||||
|
|
||||||
|
## Default FastAPI Response
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.get("/")
|
||||||
|
def home():
|
||||||
|
return {"msg": "API is working"}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the recommended style for most simple APIs.
|
||||||
|
|
||||||
|
FastAPI automatically serializes the dictionary into JSON.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Explicit JSONResponse
|
||||||
|
|
||||||
|
```python
|
||||||
|
return JSONResponse(
|
||||||
|
content={"msg": "API is working"},
|
||||||
|
status_code=status.HTTP_200_OK
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
This gives more direct control over the response.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 12. When to Use JSONResponse
|
||||||
|
|
||||||
|
Use `JSONResponse` when you need to:
|
||||||
|
|
||||||
|
Return dynamic status codes
|
||||||
|
Add custom headers
|
||||||
|
Customize the response structure manually
|
||||||
|
Return responses from exception handlers
|
||||||
|
Return responses from middleware
|
||||||
|
Override FastAPI’s default response behavior
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 13. Example: JSONResponse with Custom Status Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, status
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/login")
|
||||||
|
def login():
|
||||||
|
return JSONResponse(
|
||||||
|
content={"msg": "Login successful"},
|
||||||
|
status_code=status.HTTP_200_OK
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 14. Example: JSONResponse with Headers
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/custom")
|
||||||
|
def custom_response():
|
||||||
|
return JSONResponse(
|
||||||
|
content={"msg": "Custom response"},
|
||||||
|
headers={"X-App-Version": "1.0.0"}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 15. Complete Example with Response Model and JSONResponse
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, status
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
class UserIn(BaseModel):
|
||||||
|
username: str
|
||||||
|
password: str
|
||||||
|
|
||||||
|
|
||||||
|
class UserOut(BaseModel):
|
||||||
|
username: str
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def home():
|
||||||
|
return JSONResponse(
|
||||||
|
content={"msg": "API is working"},
|
||||||
|
status_code=status.HTTP_200_OK
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post(
|
||||||
|
"/user",
|
||||||
|
response_model=UserOut,
|
||||||
|
status_code=status.HTTP_201_CREATED
|
||||||
|
)
|
||||||
|
def create_user(user: UserIn):
|
||||||
|
return user
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 16. Running the Application
|
||||||
|
|
||||||
|
Start the FastAPI application using `uvicorn`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
The API will run at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Interactive API documentation will be available at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/docs
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 17. Best Practices
|
||||||
|
|
||||||
|
Use `response_model` to control API output.
|
||||||
|
|
||||||
|
Never return sensitive data such as passwords, tokens, or secrets.
|
||||||
|
|
||||||
|
Use separate models for input and output.
|
||||||
|
|
||||||
|
Use clear class names such as `UserIn` and `UserOut`.
|
||||||
|
|
||||||
|
Use `password` instead of `pass` because `pass` is a reserved Python keyword.
|
||||||
|
|
||||||
|
Place `response_model` inside the route decorator, not inside the function parameters.
|
||||||
|
|
||||||
|
Prefer returning normal dictionaries for simple responses.
|
||||||
|
|
||||||
|
Use `JSONResponse` only when extra control is required.
|
||||||
|
|
||||||
|
Use proper HTTP status codes, such as:
|
||||||
|
|
||||||
|
```http
|
||||||
|
200 OK
|
||||||
|
201 Created
|
||||||
|
400 Bad Request
|
||||||
|
401 Unauthorized
|
||||||
|
404 Not Found
|
||||||
|
500 Internal Server Error
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not use `uvicorn --reload` in production.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 18. DevOps Production Note
|
||||||
|
|
||||||
|
In production, the FastAPI application should usually run behind a production-grade ASGI server setup and a reverse proxy.
|
||||||
|
|
||||||
|
A common production stack is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
FastAPI
|
||||||
|
Gunicorn with Uvicorn workers
|
||||||
|
Nginx or Traefik
|
||||||
|
Docker or Kubernetes
|
||||||
|
PostgreSQL or another persistent database
|
||||||
|
```
|
||||||
|
|
||||||
|
The development command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
is only for local development.
|
||||||
|
|
||||||
|
For production, use a more stable process configuration, such as Gunicorn with Uvicorn workers, container health checks, logging, monitoring, and proper secret management.
|
||||||
512
Docs/Libs/FastAPI/14-File-Post.md
Normal file
512
Docs/Libs/FastAPI/14-File-Post.md
Normal file
@@ -0,0 +1,512 @@
|
|||||||
|
# FastAPI – POST Requests with File Uploads
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document explains how to handle file uploads in FastAPI using `POST` requests.
|
||||||
|
|
||||||
|
File upload endpoints are commonly used when an API needs to receive:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Images
|
||||||
|
Documents
|
||||||
|
PDF files
|
||||||
|
Text files
|
||||||
|
Binary files
|
||||||
|
Multiple files in one request
|
||||||
|
```
|
||||||
|
|
||||||
|
FastAPI supports file uploads using:
|
||||||
|
|
||||||
|
```python
|
||||||
|
File
|
||||||
|
UploadFile
|
||||||
|
```
|
||||||
|
|
||||||
|
For real applications, `UploadFile` is usually preferred because it provides file metadata and handles large files more efficiently.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Required Package
|
||||||
|
|
||||||
|
FastAPI file uploads require `python-multipart`.
|
||||||
|
|
||||||
|
Install it with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install python-multipart
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using the standard FastAPI installation, it may already be included:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install "fastapi[standard]"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Example Application
|
||||||
|
|
||||||
|
Create or update `main.py` with the following content:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, File, UploadFile
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/file")
|
||||||
|
def upload_file_bytes(file: bytes = File(...)):
|
||||||
|
"""
|
||||||
|
Receive a file as raw bytes.
|
||||||
|
Returns the size of the uploaded file.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"file_size": len(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/uploadfile")
|
||||||
|
async def upload_file_uploadfile(file: UploadFile):
|
||||||
|
"""
|
||||||
|
Receive a file as an UploadFile object.
|
||||||
|
Returns filename, content type, and file size.
|
||||||
|
"""
|
||||||
|
content = await file.read()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"filename": file.filename,
|
||||||
|
"content_type": file.content_type,
|
||||||
|
"file_size": len(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/uploadmultifile")
|
||||||
|
async def upload_multiple_files(files: List[UploadFile]):
|
||||||
|
"""
|
||||||
|
Receive multiple files as UploadFile objects.
|
||||||
|
Returns filenames and content types.
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
result.append({
|
||||||
|
"filename": file.filename,
|
||||||
|
"content_type": file.content_type
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. File Upload as Bytes
|
||||||
|
|
||||||
|
## Endpoint
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.post("/file")
|
||||||
|
def upload_file_bytes(file: bytes = File(...)):
|
||||||
|
return {
|
||||||
|
"file_size": len(file)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This endpoint receives the uploaded file as raw bytes.
|
||||||
|
|
||||||
|
## Endpoint URL
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Request
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/file" \
|
||||||
|
-F "file=@example.txt"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Response
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"file_size": 128
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
```python
|
||||||
|
file: bytes = File(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
This tells FastAPI to expect a file field named `file`.
|
||||||
|
|
||||||
|
The uploaded file is loaded directly into memory as bytes.
|
||||||
|
|
||||||
|
## When to Use This Method
|
||||||
|
|
||||||
|
This method is suitable for:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Small files
|
||||||
|
Simple testing
|
||||||
|
Direct in-memory processing
|
||||||
|
Quick file size checks
|
||||||
|
```
|
||||||
|
|
||||||
|
## Limitation
|
||||||
|
|
||||||
|
This method does not provide file metadata such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Original filename
|
||||||
|
Content type
|
||||||
|
File headers
|
||||||
|
```
|
||||||
|
|
||||||
|
It also loads the whole file into memory, so it is not ideal for large files.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. File Upload with `UploadFile`
|
||||||
|
|
||||||
|
## Endpoint
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.post("/uploadfile")
|
||||||
|
async def upload_file_uploadfile(file: UploadFile):
|
||||||
|
content = await file.read()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"filename": file.filename,
|
||||||
|
"content_type": file.content_type,
|
||||||
|
"file_size": len(content)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Endpoint URL
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /uploadfile
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Request
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/uploadfile" \
|
||||||
|
-F "file=@example.txt"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Response
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"filename": "example.txt",
|
||||||
|
"content_type": "text/plain",
|
||||||
|
"file_size": 128
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Why Use `UploadFile`
|
||||||
|
|
||||||
|
`UploadFile` is better than raw bytes for most real APIs.
|
||||||
|
|
||||||
|
It provides useful metadata:
|
||||||
|
|
||||||
|
```python
|
||||||
|
file.filename
|
||||||
|
file.content_type
|
||||||
|
file.file
|
||||||
|
```
|
||||||
|
|
||||||
|
It also supports file operations such as:
|
||||||
|
|
||||||
|
```python
|
||||||
|
await file.read()
|
||||||
|
await file.write()
|
||||||
|
await file.seek()
|
||||||
|
await file.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Note
|
||||||
|
|
||||||
|
When using `UploadFile.read()`, the endpoint should usually be asynchronous:
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def upload_file_uploadfile(file: UploadFile):
|
||||||
|
content = await file.read()
|
||||||
|
```
|
||||||
|
|
||||||
|
This is better than writing:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def upload_file_uploadfile(file: UploadFile):
|
||||||
|
content = file.read()
|
||||||
|
```
|
||||||
|
|
||||||
|
because `file.read()` is asynchronous and should be awaited.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Multiple File Uploads
|
||||||
|
|
||||||
|
## Endpoint
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.post("/uploadmultifile")
|
||||||
|
async def upload_multiple_files(files: List[UploadFile]):
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
result.append({
|
||||||
|
"filename": file.filename,
|
||||||
|
"content_type": file.content_type
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
```
|
||||||
|
|
||||||
|
## Endpoint URL
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /uploadmultifile
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Request
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/uploadmultifile" \
|
||||||
|
-F "files=@file1.txt" \
|
||||||
|
-F "files=@file2.txt"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Response
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"filename": "file1.txt",
|
||||||
|
"content_type": "text/plain"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "file2.txt",
|
||||||
|
"content_type": "text/plain"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
```python
|
||||||
|
files: List[UploadFile]
|
||||||
|
```
|
||||||
|
|
||||||
|
This tells FastAPI to receive multiple uploaded files using the same form field name:
|
||||||
|
|
||||||
|
```text
|
||||||
|
files
|
||||||
|
```
|
||||||
|
|
||||||
|
Each uploaded file is handled as an `UploadFile` object.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Content-Type for File Uploads
|
||||||
|
|
||||||
|
File uploads use:
|
||||||
|
|
||||||
|
```http
|
||||||
|
Content-Type: multipart/form-data
|
||||||
|
```
|
||||||
|
|
||||||
|
When using `curl` with `-F`, this header is generated automatically.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/uploadfile" \
|
||||||
|
-F "file=@example.txt"
|
||||||
|
```
|
||||||
|
|
||||||
|
The request sends the file as a multipart form field.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Complete Recommended Version
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, File, UploadFile, HTTPException, status
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
MAX_FILE_SIZE = 5 * 1024 * 1024
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def root():
|
||||||
|
return {
|
||||||
|
"message": "API is working"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/file")
|
||||||
|
def upload_file_bytes(file: bytes = File(...)):
|
||||||
|
if len(file) > MAX_FILE_SIZE:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
|
||||||
|
detail="File is too large"
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"file_size": len(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/uploadfile")
|
||||||
|
async def upload_file_uploadfile(file: UploadFile):
|
||||||
|
content = await file.read()
|
||||||
|
|
||||||
|
if len(content) > MAX_FILE_SIZE:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
|
||||||
|
detail="File is too large"
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"filename": file.filename,
|
||||||
|
"content_type": file.content_type,
|
||||||
|
"file_size": len(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/uploadmultifile")
|
||||||
|
async def upload_multiple_files(files: List[UploadFile]):
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
content = await file.read()
|
||||||
|
|
||||||
|
if len(content) > MAX_FILE_SIZE:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
|
||||||
|
detail=f"File is too large: {file.filename}"
|
||||||
|
)
|
||||||
|
|
||||||
|
result.append({
|
||||||
|
"filename": file.filename,
|
||||||
|
"content_type": file.content_type,
|
||||||
|
"file_size": len(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. Running the Application
|
||||||
|
|
||||||
|
Start the FastAPI service using `uvicorn`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
The application will be available at:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Interactive API documentation:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/docs
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternative API documentation:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/redoc
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. Testing with curl
|
||||||
|
|
||||||
|
## Upload File as Bytes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/file" \
|
||||||
|
-F "file=@example.txt"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upload Single File with `UploadFile`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/uploadfile" \
|
||||||
|
-F "file=@example.txt"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upload Multiple Files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8000/uploadmultifile" \
|
||||||
|
-F "files=@file1.txt" \
|
||||||
|
-F "files=@file2.txt"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 11. `bytes` vs `UploadFile`
|
||||||
|
|
||||||
|
| Feature | `bytes` | `UploadFile` |
|
||||||
|
| -------------------------- | --------------------------- | ------------------------- |
|
||||||
|
| File content | Loaded directly into memory | Uses file-like object |
|
||||||
|
| Filename | Not available | Available |
|
||||||
|
| Content type | Not available | Available |
|
||||||
|
| Best for | Small files | Large files and real APIs |
|
||||||
|
| Metadata | No | Yes |
|
||||||
|
| Recommended for production | Usually no | Yes |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 12. Best Practices
|
||||||
|
|
||||||
|
Use `UploadFile` for real-world APIs.
|
||||||
|
|
||||||
|
Use `bytes` only for small files or simple testing.
|
||||||
|
|
||||||
|
Validate file size on the server.
|
||||||
|
|
||||||
|
Validate file type before processing or storing the file.
|
||||||
|
|
||||||
|
Do not trust the uploaded filename.
|
||||||
|
|
||||||
|
Do not store uploaded files directly using user-provided names.
|
||||||
|
|
||||||
|
Avoid loading very large files fully into memory.
|
||||||
|
|
||||||
|
Use HTTPS for secure file transfer.
|
||||||
|
|
||||||
|
Store files in dedicated storage such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Local disk
|
||||||
|
Object storage such as S3 or MinIO
|
||||||
|
Database storage when appropriate
|
||||||
|
Network file storage
|
||||||
|
```
|
||||||
|
|
||||||
|
Return clear metadata to the client, such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Filename
|
||||||
|
Content type
|
||||||
|
File size
|
||||||
|
Upload status
|
||||||
|
```
|
||||||
382
Docs/Libs/FastAPI/15-Life-Span.md
Normal file
382
Docs/Libs/FastAPI/15-Life-Span.md
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
# FastAPI – Application Lifespan, Startup, and Shutdown Events
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
FastAPI allows you to execute code when the application starts and when it shuts down. This is useful for initializing and cleaning up shared resources such as database connections, cache clients, machine learning models, message queues, or background services.
|
||||||
|
|
||||||
|
The modern recommended approach is to use the `lifespan` parameter with an async context manager. The older `@app.on_event()` method is still available, but FastAPI marks it as deprecated in favor of lifespan handlers. ([fastapi.tiangolo.com][1])
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Deprecated Method: `@app.on_event`
|
||||||
|
|
||||||
|
Older FastAPI applications often use `@app.on_event("startup")` and `@app.on_event("shutdown")`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
def on_startup():
|
||||||
|
print("App is loading")
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_event("shutdown")
|
||||||
|
def on_shutdown():
|
||||||
|
print("App is shutting down")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.on_event("startup")
|
||||||
|
def on_startup():
|
||||||
|
print("App is loading")
|
||||||
|
```
|
||||||
|
|
||||||
|
This function runs once when the application starts.
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.on_event("shutdown")
|
||||||
|
def on_shutdown():
|
||||||
|
print("App is shutting down")
|
||||||
|
```
|
||||||
|
|
||||||
|
This function runs once when the application is shutting down.
|
||||||
|
|
||||||
|
## Important Note
|
||||||
|
|
||||||
|
FastAPI documentation recommends using the `lifespan` parameter instead of `@app.on_event()`. Also, when a `lifespan` handler is provided, FastAPI does not call the old `startup` and `shutdown` event handlers. You should use one approach consistently, not both. ([fastapi.tiangolo.com][1])
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Recommended Method: `lifespan`
|
||||||
|
|
||||||
|
The modern approach is to define one lifecycle function using `asynccontextmanager`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
print("App is loading")
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
print("App is shutting down")
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. How Lifespan Works
|
||||||
|
|
||||||
|
The `lifespan` function has two main sections:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
# Startup logic
|
||||||
|
print("App is loading")
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Shutdown logic
|
||||||
|
print("App is shutting down")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Before `yield`
|
||||||
|
|
||||||
|
This runs when the application starts.
|
||||||
|
|
||||||
|
Use this section for:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Database connection setup
|
||||||
|
Cache connection setup
|
||||||
|
Loading configuration
|
||||||
|
Initializing shared services
|
||||||
|
Starting background clients
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code After `yield`
|
||||||
|
|
||||||
|
This runs when the application shuts down.
|
||||||
|
|
||||||
|
Use this section for:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Closing database connections
|
||||||
|
Closing cache clients
|
||||||
|
Flushing logs
|
||||||
|
Releasing resources
|
||||||
|
Stopping background services
|
||||||
|
```
|
||||||
|
|
||||||
|
FastAPI passes the lifespan context manager into the application and executes the code before `yield` on startup and after `yield` on shutdown. ([fastapi.tiangolo.com][1])
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Example Application
|
||||||
|
|
||||||
|
Create or update `main.py` with the following content:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
print("App is loading")
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
print("App is shutting down")
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def root():
|
||||||
|
return {"message": "API is working"}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Running the Application
|
||||||
|
|
||||||
|
Start the service using `uvicorn`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
When the application starts, the terminal prints:
|
||||||
|
|
||||||
|
```text
|
||||||
|
App is loading
|
||||||
|
```
|
||||||
|
|
||||||
|
When you stop the application, for example with `Ctrl + C`, the terminal prints:
|
||||||
|
|
||||||
|
```text
|
||||||
|
App is shutting down
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. Example: Database Initialization Pattern
|
||||||
|
|
||||||
|
In real applications, lifespan is commonly used to initialize and close database connections.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|
||||||
|
async def connect_to_db():
|
||||||
|
# Replace this with a real database connection
|
||||||
|
return "database-connection"
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
app.state.db = await connect_to_db()
|
||||||
|
print("Database connected")
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Replace this with real cleanup logic
|
||||||
|
print("Database disconnected")
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def root():
|
||||||
|
return {"message": "API is working"}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. Using `app.state`
|
||||||
|
|
||||||
|
`app.state` is useful for storing shared application-level resources.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
app.state.db = await connect_to_db()
|
||||||
|
```
|
||||||
|
|
||||||
|
Later, this resource can be accessed from the application.
|
||||||
|
|
||||||
|
Common resources stored in `app.state` include:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Database clients
|
||||||
|
Redis clients
|
||||||
|
HTTP clients
|
||||||
|
Configuration objects
|
||||||
|
Service clients
|
||||||
|
Loaded models
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. Better Database Cleanup Example
|
||||||
|
|
||||||
|
If the database client has a `close()` method, close it after `yield`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
app.state.db = await connect_to_db()
|
||||||
|
print("Database connected")
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
await app.state.db.close()
|
||||||
|
print("Database disconnected")
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures that the application does not leave open connections after shutdown.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. Why Lifespan Is Better
|
||||||
|
|
||||||
|
The lifespan approach is better for modern FastAPI applications because it keeps startup and shutdown logic in one place.
|
||||||
|
|
||||||
|
It helps with:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Centralized lifecycle management
|
||||||
|
Cleaner async resource handling
|
||||||
|
Better application structure
|
||||||
|
Easier testing
|
||||||
|
More predictable production behavior
|
||||||
|
Cleaner resource cleanup
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. Complete Recommended Version
|
||||||
|
|
||||||
|
```python
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
print("Application startup started")
|
||||||
|
|
||||||
|
# Initialize shared resources here
|
||||||
|
app.state.service_status = "ready"
|
||||||
|
|
||||||
|
print("Application startup completed")
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
print("Application shutdown started")
|
||||||
|
|
||||||
|
# Clean up shared resources here
|
||||||
|
app.state.service_status = "stopped"
|
||||||
|
|
||||||
|
print("Application shutdown completed")
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def root():
|
||||||
|
return {"message": "API is working"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/health")
|
||||||
|
def health_check():
|
||||||
|
return {
|
||||||
|
"status": "healthy",
|
||||||
|
"service": app.state.service_status
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 11. Testing the Application
|
||||||
|
|
||||||
|
Run the app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
Open:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "API is working"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Open:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:8000/health
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "healthy",
|
||||||
|
"service": "ready"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 12. Best Practices
|
||||||
|
|
||||||
|
Use `lifespan` for new FastAPI applications.
|
||||||
|
|
||||||
|
Avoid using `@app.on_event()` in new code because it is deprecated.
|
||||||
|
|
||||||
|
Do not mix `lifespan` with `startup` and `shutdown` event decorators.
|
||||||
|
|
||||||
|
Use `app.state` for shared application resources.
|
||||||
|
|
||||||
|
Close database connections, cache clients, HTTP clients, and background services during shutdown.
|
||||||
|
|
||||||
|
Keep startup logic lightweight.
|
||||||
|
|
||||||
|
Avoid using `print()` in production.
|
||||||
|
|
||||||
|
Use structured logging instead of printing to the console.
|
||||||
|
|
||||||
|
Do not use `--reload` in production.
|
||||||
|
|
||||||
312
Docs/Libs/Requests/requests.md
Normal file
312
Docs/Libs/Requests/requests.md
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
# HTTP Requests in Python with `requests`
|
||||||
|
|
||||||
|
This document explains how to use the **`requests`** library to send HTTP requests, handle responses, work with APIs, upload/download files, manage headers, authentication, errors, and more.
|
||||||
|
|
||||||
|
> `requests` is not part of the standard library and must be installed:
|
||||||
|
```bash
|
||||||
|
pip install requests
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Sending a Basic GET Request
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
r = requests.get("http://myip.abbascloud.ir")
|
||||||
|
|
||||||
|
print(r.url)
|
||||||
|
print(r.status_code, r.ok)
|
||||||
|
print(r.text)
|
||||||
|
print(r.content)
|
||||||
|
print(r.json())
|
||||||
|
print(r.headers)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- `requests.get(url)` sends an HTTP GET request.
|
||||||
|
- `r.url`: final URL after redirects.
|
||||||
|
- `r.status_code`: HTTP status code (e.g. 200, 404).
|
||||||
|
- `r.ok`: `True` if status code is < 400.
|
||||||
|
- `r.text`: response body as string.
|
||||||
|
- `r.content`: response body as raw bytes.
|
||||||
|
- `r.json()`: parses JSON response into Python objects.
|
||||||
|
- `r.headers`: response headers as a dictionary.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Passing Query Parameters
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
params = {
|
||||||
|
"q": "python",
|
||||||
|
"page": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get("https://api.example.com/search", params=params)
|
||||||
|
print(response.url)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- `params` are appended to the URL as query strings.
|
||||||
|
- Automatically encoded by `requests`.
|
||||||
|
- Resulting URL:
|
||||||
|
```
|
||||||
|
https://api.example.com/search?q=python&page=1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Sending POST Requests (Form Data)
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
data = {
|
||||||
|
"username": "alex",
|
||||||
|
"password": "secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
requests.post(url, data=data)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- Sends data as `application/x-www-form-urlencoded`.
|
||||||
|
- Common for HTML form submissions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Sending POST Requests (JSON)
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
json_data = {
|
||||||
|
"name": "Alex",
|
||||||
|
"age": 25
|
||||||
|
}
|
||||||
|
|
||||||
|
requests.post(url, json=json_data)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- Automatically serializes data to JSON.
|
||||||
|
- Sets `Content-Type: application/json`.
|
||||||
|
- Preferred for REST APIs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Custom Request Headers
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
headers = {
|
||||||
|
"Authorization": "Bearer YOUR_TOKEN",
|
||||||
|
"User-Agent": "MyApp/1.0",
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get(url, headers=headers)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- Used for authentication, API versioning, and content negotiation.
|
||||||
|
- Common headers:
|
||||||
|
- `Authorization`
|
||||||
|
- `User-Agent`
|
||||||
|
- `Accept`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Authentication (Basic Auth)
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
from requests.auth import HTTPBasicAuth
|
||||||
|
|
||||||
|
requests.get(
|
||||||
|
"https://api.example.com",
|
||||||
|
auth=HTTPBasicAuth("username", "password")
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- Sends credentials using HTTP Basic Authentication.
|
||||||
|
- Automatically encodes credentials in headers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Working with Cookies
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
response = requests.get(url)
|
||||||
|
print(response.cookies)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- Cookies are stored in a `RequestsCookieJar`.
|
||||||
|
- Useful for sessions and login persistence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Uploading Files
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
files = {
|
||||||
|
"file": open("example.txt", "rb")
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(url, files=files)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- Sends files as `multipart/form-data`.
|
||||||
|
- Common for file uploads to APIs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Downloading Files
|
||||||
|
|
||||||
|
### Simple Download
|
||||||
|
|
||||||
|
```python
|
||||||
|
response = requests.get("https://example.com/image.png")
|
||||||
|
|
||||||
|
with open("image.png", "wb") as f:
|
||||||
|
f.write(response.content)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Downloads entire file into memory.
|
||||||
|
- Suitable for small files.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Streaming Large Files
|
||||||
|
|
||||||
|
```python
|
||||||
|
response = requests.get(url, stream=True)
|
||||||
|
|
||||||
|
with open("big.zip", "wb") as f:
|
||||||
|
for chunk in response.iter_content(chunk_size=8192):
|
||||||
|
f.write(chunk)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Downloads file in chunks.
|
||||||
|
- Prevents high memory usage.
|
||||||
|
- Recommended for large files.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Timeout and Error Handling
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
try:
|
||||||
|
response = requests.get(url, timeout=5)
|
||||||
|
response.raise_for_status()
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
print("Request timed out")
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
print("HTTP error:", e)
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print("Request failed:", e)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- `timeout`: maximum wait time (seconds).
|
||||||
|
- `raise_for_status()`: raises exception for 4xx / 5xx errors.
|
||||||
|
- `RequestException`: base class for all request errors.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Using Proxies
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
proxies = {
|
||||||
|
"http": "http://127.0.0.1:8080",
|
||||||
|
"https": "http://127.0.0.1:8080"
|
||||||
|
}
|
||||||
|
|
||||||
|
requests.get(url, proxies=proxies)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- Routes requests through a proxy server.
|
||||||
|
- Useful for debugging, privacy, or corporate networks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. SSL Verification
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
requests.get(url, verify=True) # default
|
||||||
|
requests.get(url, verify=False) # NOT recommended
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- `verify=True` checks SSL certificates.
|
||||||
|
- Disabling SSL verification is insecure and should only be used for testing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Real API Example (GitHub)
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
|
||||||
|
API_URL = "https://api.github.com/users/octocat"
|
||||||
|
|
||||||
|
response = requests.get(API_URL)
|
||||||
|
|
||||||
|
if response.ok:
|
||||||
|
user = response.json()
|
||||||
|
print(user["login"], user["public_repos"])
|
||||||
|
else:
|
||||||
|
print("Error:", response.status_code)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Explanation
|
||||||
|
|
||||||
|
- Sends a GET request to GitHub’s public API.
|
||||||
|
- Parses JSON response.
|
||||||
|
- Accesses specific fields safely.
|
||||||
|
- Checks request success using `response.ok`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- `requests` simplifies HTTP communication
|
||||||
|
- Supports GET, POST, headers, params, JSON, files
|
||||||
|
- Handles authentication, cookies, proxies, and SSL
|
||||||
|
- Built-in error handling improves reliability
|
||||||
|
- Widely used for REST APIs and web services
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
# FastAPI – Simple Route Example
|
|
||||||
|
|
||||||
This document demonstrates how to create a basic FastAPI application with a single HTTP route and how to run it using `uvicorn`, which is the default and recommended ASGI server for FastAPI.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create a Simple FastAPI Application
|
|
||||||
|
|
||||||
Create a Python file named `main.py` and add the following content:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def home_dir():
|
|
||||||
return {"message": "Home Page"}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explanation
|
|
||||||
|
|
||||||
* `FastAPI()` initializes the application instance.
|
|
||||||
* `@app.get("/")` registers an HTTP GET endpoint at the root path (`/`).
|
|
||||||
* The `home_dir` function is the request handler and returns a JSON response.
|
|
||||||
* FastAPI automatically handles JSON serialization and response headers.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Running the Application
|
|
||||||
|
|
||||||
FastAPI applications are typically run using **uvicorn**, an ASGI server designed for high performance.
|
|
||||||
|
|
||||||
### Option 1: Run Using FastAPI CLI (Development Mode)
|
|
||||||
|
|
||||||
FastAPI provides a development-friendly CLI wrapper that uses `uvicorn` internally:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
fastapi dev main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
This command:
|
|
||||||
|
|
||||||
* Starts the application in development mode
|
|
||||||
* Enables auto-reload on code changes
|
|
||||||
* Automatically binds to a local development interface
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Option 2: Run Directly with Uvicorn (Recommended)
|
|
||||||
|
|
||||||
For explicit control over runtime configuration, run the application directly with `uvicorn`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload --host 0.0.0.0 --port 1234
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Command Breakdown
|
|
||||||
|
|
||||||
* `main` → Python file name (without `.py`)
|
|
||||||
* `app` → FastAPI application instance
|
|
||||||
* `--reload` → Automatically reloads the server on code changes (development only)
|
|
||||||
* `--host 0.0.0.0` → Exposes the service on all network interfaces
|
|
||||||
* `--port 1234` → Custom application port
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Accessing the Application
|
|
||||||
|
|
||||||
Once running, the application will be available at:
|
|
||||||
|
|
||||||
* API Endpoint:
|
|
||||||
`http://localhost:1234/`
|
|
||||||
|
|
||||||
* Interactive API Docs (Swagger UI):
|
|
||||||
`http://localhost:1234/docs`
|
|
||||||
|
|
||||||
* Alternative API Docs (ReDoc):
|
|
||||||
`http://localhost:1234/redoc`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
* Use `--reload` only in development environments
|
|
||||||
* In production, run `uvicorn` behind a process manager (e.g., systemd, Docker, Kubernetes)
|
|
||||||
* Explicitly define host and port for containerized and cloud deployments
|
|
||||||
* Keep the application entry point (`main:app`) consistent across environments
|
|
||||||
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
# FastAPI – GET Endpoints and JSON Responses
|
|
||||||
|
|
||||||
This document demonstrates how to define multiple **GET endpoints** in FastAPI, return JSON responses, and use **path parameters** to retrieve specific data from an in-memory dataset.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Example Application
|
|
||||||
|
|
||||||
Create or update `main.py` with the following content:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
users = [
|
|
||||||
{"name": "abbas", "age": 20},
|
|
||||||
{"name": "mmd", "age": 37},
|
|
||||||
{"name": "asghar", "age": 19},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def root_dir():
|
|
||||||
return {"message": "API is working"}
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/users")
|
|
||||||
def get_users():
|
|
||||||
return users
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/user/{name_input}")
|
|
||||||
def get_user_by_name(name_input: str):
|
|
||||||
for item in users:
|
|
||||||
if item["name"] == name_input:
|
|
||||||
return {"information": item}
|
|
||||||
return {"message": "User not found"}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Overview
|
|
||||||
|
|
||||||
### Application Initialization
|
|
||||||
|
|
||||||
```python
|
|
||||||
app = FastAPI()
|
|
||||||
```
|
|
||||||
|
|
||||||
Initializes the FastAPI application instance.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### In-Memory Data Store
|
|
||||||
|
|
||||||
```python
|
|
||||||
users = [
|
|
||||||
{"name": "abbas", "age": 20},
|
|
||||||
{"name": "mmd", "age": 37},
|
|
||||||
{"name": "asghar", "age": 19},
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
* Simulates a database using a Python list
|
|
||||||
* Each user is represented as a JSON-compatible dictionary
|
|
||||||
* Suitable for development and testing purposes
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Defined Endpoints
|
|
||||||
|
|
||||||
### Root Endpoint
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"message": "API is working"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Used as a health check or readiness probe.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Get All Users
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /users
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{"name": "abbas", "age": 20},
|
|
||||||
{"name": "mmd", "age": 37},
|
|
||||||
{"name": "asghar", "age": 19}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns the full list of users as JSON.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Get User by Name (Path Parameter)
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /user/{name_input}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example Request:**
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /user/abbas
|
|
||||||
```
|
|
||||||
|
|
||||||
**Successful Response:**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"information": {
|
|
||||||
"name": "abbas",
|
|
||||||
"age": 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Failure Response:**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"message": "User not found"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Path Parameters
|
|
||||||
|
|
||||||
* `name_input` is a dynamic path parameter
|
|
||||||
* Automatically validated and converted to `str` by FastAPI
|
|
||||||
* Used to filter data at runtime
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Running the Application
|
|
||||||
|
|
||||||
Use `uvicorn` to start the service:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
* Use structured JSON responses (`key: value`) instead of tuples
|
|
||||||
* Validate user input when moving beyond in-memory data
|
|
||||||
* Replace in-memory storage with a database for production
|
|
||||||
* Use proper HTTP status codes (`404`, `200`) in real-world APIs
|
|
||||||
* Separate routing, models, and business logic as the project grows
|
|
||||||
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
# FastAPI – POST Endpoint and JSON Input
|
|
||||||
|
|
||||||
This section demonstrates how to handle **POST requests** in FastAPI to create new resources using request data and return JSON responses.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Example Application (POST Request)
|
|
||||||
|
|
||||||
Extend `main.py` with the following code:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
users = [
|
|
||||||
{"name": "abbas", "age": 20},
|
|
||||||
{"name": "mmd", "age": 37},
|
|
||||||
{"name": "asghar", "age": 19},
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def home_page():
|
|
||||||
return {"msg": "API is working"}
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/new_user")
|
|
||||||
def create_user(name: str, age: int):
|
|
||||||
new_user = {"name": name, "age": age}
|
|
||||||
users.append(new_user)
|
|
||||||
return {"msg": "User created successfully"}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Overview
|
|
||||||
|
|
||||||
### POST Endpoint Definition
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.post("/new_user")
|
|
||||||
def create_user(name: str, age: int):
|
|
||||||
```
|
|
||||||
|
|
||||||
* Registers an HTTP **POST** endpoint at `/new_user`
|
|
||||||
* Accepts input parameters:
|
|
||||||
|
|
||||||
* `name` → string
|
|
||||||
* `age` → integer
|
|
||||||
* FastAPI automatically validates input types
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Creating a New Resource
|
|
||||||
|
|
||||||
```python
|
|
||||||
new_user = {"name": name, "age": age}
|
|
||||||
users.append(new_user)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Constructs a new user object
|
|
||||||
* Appends it to the in-memory `users` list
|
|
||||||
* Simulates creating a record in a database
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### JSON Response
|
|
||||||
|
|
||||||
```python
|
|
||||||
return {"msg": "User created successfully"}
|
|
||||||
```
|
|
||||||
|
|
||||||
* Returns a structured JSON response
|
|
||||||
* Automatically serialized by FastAPI
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Example Request
|
|
||||||
|
|
||||||
### Using `curl`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST "http://localhost:8000/new_user?name=ali&age=25"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Response
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"msg": "User created successfully"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Verifying the Result
|
|
||||||
|
|
||||||
After creating a user, retrieve the updated list:
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /users
|
|
||||||
```
|
|
||||||
|
|
||||||
Response will now include the newly added user.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Running the Application
|
|
||||||
|
|
||||||
Start the FastAPI service using `uvicorn`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
* POST requests should be used to create resources
|
|
||||||
* Avoid modifying in-memory data in production environments
|
|
||||||
* Use request bodies with Pydantic models instead of query parameters for real APIs
|
|
||||||
* Return appropriate HTTP status codes (`201 Created`)
|
|
||||||
* Validate and sanitize all client-provided input
|
|
||||||
* Replace in-memory storage with persistent databases
|
|
||||||
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
# FastAPI – JSONResponse (Explicit JSON Responses)
|
|
||||||
|
|
||||||
This document demonstrates how to return **explicit JSON responses** in FastAPI using `JSONResponse`.
|
|
||||||
While FastAPI automatically serializes dictionaries to JSON, `JSONResponse` is useful when you need **full control** over the response.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Example Application
|
|
||||||
|
|
||||||
Create or update `main.py` with the following content:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from fastapi import FastAPI, status
|
|
||||||
from fastapi.responses import JSONResponse
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
|
||||||
def home():
|
|
||||||
return JSONResponse(
|
|
||||||
content={"msg": "API is working"},
|
|
||||||
status_code=status.HTTP_200_OK
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why Use `JSONResponse`
|
|
||||||
|
|
||||||
FastAPI automatically converts Python dictionaries into JSON responses.
|
|
||||||
However, `JSONResponse` is useful when you need to:
|
|
||||||
|
|
||||||
* Explicitly control the response type
|
|
||||||
* Set custom status codes dynamically
|
|
||||||
* Customize headers
|
|
||||||
* Return non-standard JSON structures
|
|
||||||
* Override default response behavior
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Response Behavior
|
|
||||||
|
|
||||||
### Endpoint
|
|
||||||
|
|
||||||
```http
|
|
||||||
GET /
|
|
||||||
```
|
|
||||||
|
|
||||||
### Response Body
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"msg": "API is working"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### HTTP Status Code
|
|
||||||
|
|
||||||
```
|
|
||||||
200 OK
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Comparison: Default Response vs JSONResponse
|
|
||||||
|
|
||||||
### Default FastAPI Response
|
|
||||||
|
|
||||||
```python
|
|
||||||
@app.get("/")
|
|
||||||
def home():
|
|
||||||
return {"msg": "API is working"}
|
|
||||||
```
|
|
||||||
|
|
||||||
* Automatically serialized to JSON
|
|
||||||
* Simpler and recommended for most cases
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Explicit JSONResponse
|
|
||||||
|
|
||||||
```python
|
|
||||||
return JSONResponse(
|
|
||||||
content={"msg": "API is working"},
|
|
||||||
status_code=status.HTTP_200_OK
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
* Explicit control over response
|
|
||||||
* Useful for advanced use cases
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## When to Use `JSONResponse`
|
|
||||||
|
|
||||||
* Returning conditional status codes
|
|
||||||
* Adding custom headers
|
|
||||||
* Returning responses outside standard request flow
|
|
||||||
* Building middleware or exception handlers
|
|
||||||
* Integrating with legacy systems
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Running the Application
|
|
||||||
|
|
||||||
Start the application using `uvicorn`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uvicorn main:app --reload
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
* Prefer returning dictionaries for simple APIs
|
|
||||||
* Use `JSONResponse` only when additional control is required
|
|
||||||
* Keep response formats consistent across endpoints
|
|
||||||
* Avoid mixing response styles unnecessarily
|
|
||||||
* Use response models for structured APIs
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user