Skip to content

DreamDevourer/Python-Fundamentals-Study

Repository files navigation

Python Fundamentals Study πŸ“š

This repository is dedicated for my fundamental knowledge related to Python 3 and newer. I intend to work on this repository weekly.

License

Sample password generator script study.

In this repository I study the practical application of:

  • Python built-in functions
  • Types of numeric data (such as int and float)
  • Strings Formatting
  • Variables
  • Mathematical operations with Python
  • Libraries
  • Decision making (if, elif, else)
  • Repetitions (while, for)
  • User-defined functions (def())
  • My knowledge of English and general Python use.
  • Advanced use cases

πŸ—Ί Directory Map

  1. Advanced Applications
  2. Conversion Scripts
  3. Math Scripts
  4. Problem Solving Scripts
  5. String Based Scripts
  6. 100 Days Coding Challenge

1. Advanced Applications

Advanced scripts related to daily and possible market problems solving, high use of all practical applications on each script.

2. Conversion Scripts

Scripts with focus on conversion operations, using part of practical application topics

3. Math Scripts

Mathematical operations scripts to calculate and/or give mathematical results.

4. Problem Solving Scripts

These scripts are daily general problem solving scripts, like an investment calculator, a salary bonus calculation, price per quantity, etc.

5. String Based Scripts

Simple and direct strings scripts with minimal interaction with the user.

6. 100 Days Coding Challenge

An initiative by Dr. Angela Yu to learn Python by building 100 projects in 100 days. Learning to build websites, games, apps, plus scraping and data science.

🦾 100 Days Coding Challenge Pieces

Day 1 - Band Name Generator

Day 2 - Tip Calculator

Day 3 - Interactive Story

Day 4 - Rock, Paper, Scissors game

Day 5 - Password Generator

Day 6 - Reeborg Maze Solution

Day 7 - Hangman Game

Day 8 - Caesar Cipher

Day 9 - Silent/Secret Auction

Day 10 - Calculator App

Day 11 - Blackjack Capstone Project

Day 12 - Guessing Game

Day 13 - Debugger

Day 14 - Higher or Lower game

Day 15 - Coffee machine project

Day 16 - Object Oriented Programming with Coffee Machine

Day 17 - Object Oriented Programming with Quiz App

Day 18 - Turtle and GUI

Day 19 - Etch a Sketch with Turtle GUI

Day 20 - Snake Game

Day 21 - Advanced Snake Game

Day 22 - Pong Game

Day 23 - Turtle crossing road Game

Day 24 - Improved Snake Game

Day 25 - Work with CSV files with Pandas

Day 26 - List and Dictionary comprehensions with NATO phonetic alphabet

Day 27 - Tkinter GUI

Day 28 - Advanced Tkinter GUI - Pomodoro App

Day 29 - Tkinter GUI Password Manager

Day 30 - Password Manager JSON implementation

Day 31 - Flash card program - Frequency language dictionary

Day 32 - Send Emails and date/time management

Day 33 - API endpoints - ISS tracker

Day 34 - Trivia API to work with Quiz app

Day 35 - Authentication, SMS sender

Day 36 - Stock Trading News

Day 37 - Habit tracking app

Day 38 - Exercise tracking app

Day 39 - Cheap flight finder

Day 40 - Flight Club app

Day 41 - Basic Web Development with Python

Day 42 - Intermediate Web Development with Python

Day 43 & Day 44 - Website Development

Day 45 - Web scraping with BeautifulSoup

Day 46 - Musical Time Machine

Day 47 - Automated Amazon price tracker

Day 48 - Game Playing Bot

Day 49 - LinkedIn Job Application Automation

Day 50 - Auto Tinder Swiping Bot

Day 51 - Twitter compliant bot

Day 52 - Instagram follower bot

Day 53 - Web scraping - Data entry job automation

Day 54 - Web Development with Flask

Day 55 - Parsing web elements with Flask

Day 56 - Website Templates

Day 57 - Jinja with Flask

Day 58 - Bootstrap

Day 59 - Blog Capstone

Day 60 - Web forms and POST with Flask

Day 61 - Advanced forms with Flask WT-Forms

Day 62 - Wifi Project

Day 63 - Database with SQLite

Day 64 - Top 10 Movies

Day 65 - Web Design and Development project study

Day 66 - RESTful API

Day 67 - Advanced RESTful API

Day 68 - Authentication with Flask

Day 69 - Adding users in a database

Day 70 - Deploy web app with Heroku

Day 71 - Data exploration with Pandas

Day 72 - Data visualization with Matplotlib

Day 73 - Analysis of LEGO database

Day 74 - Google Trends Data

Day 75 - Plotly Charts

Day 76 - Computation with NumPy

Day 77 - Linear Regression

Day 78 - Nobel Prize Analysis

Day 79 - Handwashing

Day 80 - Predict Houses Prices

Day 81 - Convert Strings into Morse Code

Day 82 - Portfolio Project Development

Day 83 - Professional Portfolio Project

Day 84 - Watermark adder (GUI)

Day 85 - Typing speed tester (GUI)

Day 86 - Game development with Godot

Day 87 - Cafe and Wifi Website

Day 88 - Web Todo List

Day 89 - Text Writing App

Day 90 - PDF to Audiobook Converter

Day 91 - Image Colour Palette Generator

Day 92 - Custom Web Scraper

Day 93 - Google Dinosaur Game bot

Day 94 - Space Invaders with Godot

Day 95 - Landing Page

Day 96 - Ecommerce website page

Day 97 - Custom Automation

Day 98 - Generate random profile pictures

Day 99 - Youtube channel tracker

Day 100 - Predict Earnings using Multivariable Regressio

πŸ’‘ Useful Snippets and study pieces

Here are some useful snippets to use daily for boosting code efficiency. Every single snippet is coming from a study script that I made from this repository.

Dynamic File Path

Useful for loading external assets in specific directories and subdirectories, this snippet will work on every major OS like Windows, MacOS, Linux and BSD.

import pathlib
from pathlib import Path

# Dynamic File Path Solution
OUTPUT_PATH = pathlib.Path(__file__).parent.absolute()
ASSETS_PATH = OUTPUT_PATH / Path("./assets")


def relative_to_assets(path: str) -> Path:
    return ASSETS_PATH / Path(path)

OR

import pathlib
import shutil
from pathlib import Path

# Dynamic File Path Solution
THIS_PATH = pathlib.Path(__file__).parent.absolute()
ASSETS_PATH = THIS_PATH / Path("aarch64")


def relative_to_assets(path: str) -> Path:
    return ASSETS_PATH / Path(path)


def relative_to_target(path: str) -> Path:
    return THIS_PATH / Path(path)


def copy_and_overwrite(from_path, to_path):
    if os.path.exists(to_path):
        shutil.rmtree(to_path)
    shutil.copytree(from_path, to_path)

While

While can be used to start a script on the core part of it or to use as a logic operator.

while True:
    answer = input("Do you want to try again? (Y/n) ")
    if answer == 'y' or answer == 'Y' or sys.stdin.isatty():
        num = int(input("Enter a number: "))
        print(num2roman(num))
    elif answer == 'n':
        break
    else:
        print("Invalid input.")

For loops

For loops are essential these days, there is no doubt about that. It's possible to make a huge range of logical solutions with lists, arrays and other variables.

for link in linksFinder:
    print(link.get("href"))

    saveFile = input(
        "Do you want to save this list inside a text file? (y/n) ")
    if saveFile == "y":
        with open("links.txt", "a") as file:
            file.write(link.get("href") + "\n")
    else:
        pass

Random List

Randomization is a must need thing in our code, so this is a quick snippet to random lists.

import random

sample = ['a', 'b', 'c', 'd', 'e']
print(random.choice(sample))

# For cryptographically secure random choices (e.g., for generating a passphrase from a wordlist), use secrets.choice():

import secrets

sample = ['battery', 'correct', 'horse', 'staple']
print(secrets.choice(sample))

Random Dictionary

Randomization is a must need thing in our code, so this is a quick snippet to randomize dictionaries.

import random

visualSet = {"Rock": rock, "Paper": paper, "Scissors": scissors}
aiChoice = random.choice(list(visualSet.values()))

"""All possible ways to randomize:

'd' is the dictionary variable.

A random key:
random.choice(list(d.keys()))

A random value:
random.choice(list(d.values()))

A random key and value:
random.choice(list(d.items()))
"""

Unix Administrative Request

If the script needs root user privileges then we need to use this snippet to call sudo.

import subprocess

rootNeeded = subprocess.call(["/usr/bin/sudo", "/usr/bin/id"])

Bytes Encode and Decode

Bytes converts an object to an immutable byte-represented object of given size and data, which is useful for writing or reading HEX values inside a file.

    # Bytes Encode and Decode Study

    def writeString():
        # Write a string at the end of a JPG file.
        with open(relative_to_assets('photo.jpg'), 'ab') as f:  # ab append bytes mode
            f.write(b' Hidden message: test :)') # b is for bytes

    def readString():
        # Read HEX of the JPG file.
        with open(relative_to_assets('photo.jpg'), 'rb') as f:  # Read bytes mode
            jpgContent = f.read()
            # when FF D9 occurs.
            offset = jpgContent.index(bytes.fromhex('FFD9'))
            f.seek(offset + 2)
            print(f.read())

    def deleteString():
        # delete everything after the last FF D9 from a JPG file
        with open(relative_to_assets('photo.jpg'), 'r+') as f:  # Read bytes mode
            jpgContent = f.read()
            offset = jpgContent.index(bytes.fromhex('FFD9'))
            f.seek(offset + 2)
            f.truncate()

Endswith

This function returns True if a string ends with the specified suffix (case-sensitive), otherwise returns False. A tuple of string elements can also be passed to check for multiple options. For startswith we have a similar approach.

from pathlib import Path
import pathlib
files = os.listdir(ASSETS_PATH)

# Interesting solution to pick specific files inside a list.
for file in files:
    if file.endswith(".png") or file.endswith(".jpg") or file.endswith(".jpeg"):
      print(f"Optimizing {file}")
      imgOptimize = Image.open(relative_to_assets(str(file)))
      imgWidth, imgHeight = imgOptimize.size
    else:
      print(f"{file} is not a PNG or JPG, skipping")

Regular Expression

Regular expression is a special sequence of characters that helps you match or find other strings or sets of strings, using a specialized syntax held in a pattern. Regular expressions are widely used in UNIX world.

import re
    
# Regular Expression from module re;
# https://docs.python.org/3/library/re.html
# validate def will make sure that the remTwo var can have a "." as float


def validate(string):
    result = re.match(r"(\+|\-)?\d+(\.\d+)?$", string)
    return result is not None

Using Shell Paths

Usually when we need to call a shell script within a python script, paths tends to break and not be cross compatible between python and shell lines. So this snippet helps to solve that.

import os
import sys

# Python path. This will pick the current directory where the script is located.
current_dir = os.path.dirname(sys.argv[0])
# OR
current_shell_dir = os.path.dirname(os.path.abspath(__file__))

# Shell path. This will pick "current_dir" and replace any possible empty spaces with "\" and other fixes to be compatible with any unix-like Shell.

fixDirPath = current_dir.replace(" ", "\\ ").replace("?", "\\?").replace("&", "\\&").replace(
    "(", "\\(").replace(")", "\\)").replace("*", "\\*").replace("<", "\\<").replace(">", "\\>")


targetDirectory = "Sample"

# Sample script usage to delete a directory using shell commands
os.system(f"rm -rf {str(fixDirPath)}/{str(targetDirectory)}")

Input

Input is a built-in function in Python, allows coders to receive information through the keyboard, which they can process in a Python program. This is basic and essential.

# Simple like that :)
userString = input("Enter a text: ")
print(userString[::-1]) # Reverse the string

Time Conversion

Making mathematical operations inside python are easy things to do, you basically need to know the formula and the logic to implement conversion and precision operations.

minutes = "24.785089"
print(minutes + " minutes is equal to " +
      str(float(minutes)/60/24/365) + " years.")

Ordinal Numbers to Roman Numbers

With a simple array, it's possible to compare ordinal numbers with strings that represents roman numbers.

num_map = [(1000, 'M'), (900, 'CM'), (500, 'D'), (400, 'CD'), (100, 'C'), (90, 'XC'),
           (50, 'L'), (40, 'XL'), (10, 'X'), (9, 'IX'), (5, 'V'), (4, 'IV'), (1, 'I')]

# Function to convert the numbers to roman values. Based on NullDev algorithm.


def num2roman(num):

    roman = ''

    while num > 0:
        for i, r in num_map:
            while num >= i:
                roman += r
                num -= i

    return roman

Temperature Conversion

Converting temperature units is a simple task, all that is needed is the formula of each unit.

# Simple formulas to convert temperature from Celsius to Fahrenheit and vice versa.
fahrenheit = celsius * 9 / 5 + 32
celsius = (fahrenheit - 32) * 5 / 9

Copy to Clipboard

When copying content to the user's clipboard, it's needed to import the OS module to use features like that.

import os
nameGen = "Hello there :)"
# Copy to the variable "nameGen" to the clipboard
os.system(f"echo {nameGen} | pbcopy")

Open Website or Application

Opening external websites or applications also requires the OS module to be imported, this snippet allows the script to open almost every type of file, like Shell scripts, default applications and websites.

import os
targetSite = "https://google.com/"
os.system(f"open {targetSite}")

Watchdog Usage

Watchdog is used to monitor events on the OS, that means that it's possible to monitor files and folders. To be more specific: Python API library and shell utilities to monitor file system events.

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

currentUser = "default"
mainDirectory = f"/Users/{currentUser}/Downloads"
jobDestinationPath = f"/Users/{currentUser}/Documents/Remotish"

class MyHandler(FileSystemEventHandler): # We need to create a class with the FileSystemEventHandler

    # After that we need to make a function with self and event. So when anything happens to the folder it will execute the function.
    def on_modified(self, event):
        for individualFile in os.listdir(mainDirectory):
            os.rename(f"{mainDirectory}/{individualFile}", f"{jobDestinationPath}/{individualFile}")

# Needed to monitor Watchdog
eventHandler = MyHandler()
observer = Observer()
observer.schedule(eventHandler, mainDirectory, recursive=True)
observer.start()

# Needed to monitor Watchdog
try:
    while True:
        time.sleep(10)
except KeyboardInterrupt:
    observer.stop()
observer.join()

Simple JSON read method

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. These days, JSON is mandatory. This little snippet can read a json file and output its content.

{
    "_comment": "SAMPLE JSON FILE",
    "username": "testABC",
    "password": "testDEF"
}
import json
import sys
import os

userContent = {}
current_dir = os.path.dirname(sys.argv[0])

with open(f"{current_dir}/sample-json.json", "r") as json_file:
    userContent = json.load(json_file)
    print(userContent["username"])
    print(userContent["password"])

Simple JSON write method

This little snippet can write a json file and output its content.

{
    "_comment": "SAMPLE JSON FILE",
    "username": "testABC",
    "password": "testDEF"
}
import json
import sys
import os

userContent = {}
current_dir = os.path.dirname(sys.argv[0])

userInp = input("Enter your username: ")
userInp2 = input("Enter your password: ")

userContent["username"] = f"{str(userInp)}"
userContent["password"] = f"{str(userInp2)}"


with open(f"{current_dir}/sample-json.json", "w") as json_file:
    json.dump(userContent, json_file)
    print(f"The new username is: {str(userContent['username'])}")
    print(f"The new password is: {str(userContent['password'])}")

Simple API Read Methods

Here are some ways to read the API path content with a simple GET method and some extra calls.

import requests
import json

responseAPI = requests.get('https://randomfox.ca/floof')

# This will return a simple status code.
print(responseAPI.status_code)
# This will return a string value with the contents inside the API URL.
print(responseAPI.text)
# Now printing json format and using as a dictionary.
print(responseAPI.json())

Simple API GET Method

Reading values from an API can be done very easily by using the requests module and also to converting the API values into Python dictionaries using json module and function.

import requests
import json

# Getting the API url/path
responseAPI = requests.get('https://randomfox.ca/floof')
# Output from GET: {'image': 'https://randomfox.ca/images/13.jpg', 'link': 'https://randomfox.ca/?i=13'}
generatedFoxImg = responseAPI.json()

print(f"Your random fox: {generatedFoxImg['image']} \n")

FastAPI GET Method

FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints. Making GET requests are easy thing to do, just need to import the module and associate the function to a variable and start coding paths and parameters with the FastAPI Functions.

from fastapi import FastAPI

appStudy = FastAPI()


@appStudy.get("/")
async def root():
    return {"messageField": "Message content here."}

FastAPI POST Method

The POST method is used to request that the origin server accept the entity attached in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line.

from fastapi import FastAPI, Path
from pydantic import BaseModel

appStudy = FastAPI()

class Item(BaseModel):
    name: str
    price: float
    quantity: Optional[int] = None

# This is actually the API values
inventoryDict = {
    "1": {"name": "Bread", "price": 1.25, "quantity": "10"},
    "2": {"name": "Milk", "price": 2.45, "quantity": "5"},
    "3": {"name": "Eggs", "price": 3.99, "quantity": "20"},
    "4": {"name": "Cheese", "price": 4.99, "quantity": "15"},
    "5": {"name": "Butter", "price": 5.00, "quantity": "5"}
}

# Using POST method
@appStudy.post("/post-item/{item_id}")
def createItem(item_id: int, item: Item):
    # Let's create a new item id.
    if item_id in inventoryDict:
        return {"DataErr": "Item already exists"}
    else:
        inventoryDict[str(item_id)] = item
        return inventoryDict[str(item_id)]

FastAPI PUT Method

PUT method requests for the attached entity (in the request body) to be stored into the server which hosts the supplied Request-URI. If the Request-URI refers to an already existing resource – an update operation will happen

from fastapi import FastAPI, Path
from pydantic import BaseModel
from fastapi.encoders import jsonable_encoder
from typing import Optional

appStudy = FastAPI()

# This class is for the PUT request
class updateItem(BaseModel):
    name: Optional[str] = None
    price: Optional[float] = None
    quantity: Optional[int] = None

# This is actually the API values
inventoryDict = {
    "1": {"name": "Bread", "price": 1.25, "quantity": "10"},
    "2": {"name": "Milk", "price": 2.45, "quantity": "5"},
    "3": {"name": "Eggs", "price": 3.99, "quantity": "20"},
    "4": {"name": "Cheese", "price": 4.99, "quantity": "15"},
    "5": {"name": "Butter", "price": 5.00, "quantity": "5"}
}

# PUT method
@appStudy.put("/put-item/{item_id}")
def createItem(item_id: int, item: Item):
    # Let's create a new item id.
    if item_id in inventoryDict:
        return {"DataErr": "Item already exists"}
    else:
        inventoryDict[str(item_id)] = item
        return inventoryDict[str(item_id)]

Key Obfuscation

Obfuscation is the deliberate act of creating source or machine code that is difficult for humans to understand, this helps to improve security - it is far from being the ultimate security solution but is a thing to use in a non-production environment.

For creating a file with the key and obfuscating it:

import base64
import pathlib
import os
import re
from pathlib import Path

# Dynamic File Path Solution
API_PATH = pathlib.Path(__file__).parent.absolute()


def relative_to_assets(path: str) -> Path:
    return API_PATH / Path(path)

userChange = input("Enter key: ").strip()

# Pick userChange and encode it to base64
userChange = base64.b64encode(userChange.encode('utf-8'))
# Save userChange to "API" file
with open(relative_to_assets('Data/security/API'), 'wb') as f:
    # Delete everything inside the file.
    f.truncate()
    f.write(userChange)

    print("DONE! You are ready to use the API!")

XOR Cipher

XOR Encryption is an encryption method used to encrypt data and is hard to crack by brute-force method, i.e generating random encryption keys to match with the correct one.

Encrypting:

#! /usr/bin/env python3
import base64
import os
import pathlib
import re
from pathlib import Path

# Dynamic File Path Solution
KEY_PATH = pathlib.Path(__file__).parent.absolute()


def relative_to_assets(path: str) -> Path:
    return KEY_PATH / Path(path)


def encryptSecurity():
    # Use external script to make base64 or https://www.base64encode.org/
    key = "MTMy"  # up 255
    key = base64.b64decode(key)
    cleanKey = re.sub(
        r"[^A-Za-z0-9-]", "", key.decode("utf-8"))
    finalKey = int(cleanKey)

    loadEnc00 = open(relative_to_assets("Data/security/.KEY"), "rb")
    byteReaderData = loadEnc00.read()
    loadEnc00.close()

    byteReaderData = bytearray(byteReaderData)
    for index, value in enumerate(byteReaderData):
        byteReaderData[index] = value ^ finalKey

    Enc = open(relative_to_assets("Data/security/.KEY.nclmE"), "wb")
    Enc.write(byteReaderData)
    Enc.close()

    # Delete Data/security/KEY
    os.remove(relative_to_assets("Data/security/.KEY"))


encryptSecurity()

Decrypting:

#! /usr/bin/env python3
import base64
import os
import pathlib
import re
import string
from pathlib import Path
import signal

# Dynamic File Path Solution
KEY_PATH = pathlib.Path(__file__).parent.absolute()


def relative_to_assets(path: str) -> Path:
    return KEY_PATH / Path(path)


def signal_handler(sig, frame):
    # If the program exits then remove important files.
    os.remove(relative_to_assets("Data/security/.tmp/.KEY"))
    exit()


def decryptSecurity():
    # Use external script to make base64 or https://www.base64encode.org/
    key = "MTMy"  # up 255
    key = base64.b64decode(key)
    cleanKey = re.sub(
        r"[^A-Za-z0-9-]", "", key.decode("utf-8"))
    finalKey = int(cleanKey)

    loadEnc00 = open(relative_to_assets(
        "Data/security/.KEY.nclmE"), "rb").read()

    byteReader = bytearray(loadEnc00)
    for index, value in enumerate(byteReader):
        byteReader[index] = value ^ finalKey

    decEnc = open(relative_to_assets("Data/security/.tmp/.KEY"), "wb")
    decEnc.write(byteReader)


try:
    # signal handler for "CTRL + C"
    signal.signal(signal.SIGINT, signal_handler)
    decryptSecurity()
    signal.pause()
except:
    # In exeption remove important files.
    os.remove(relative_to_assets("Data/security/.tmp/.KEY"))

Quick logger

Logs provide developers with an extra set of eyes that are constantly looking at the flow that an application is going through.

import os
import time
import subprocess
import pathlib
from pathlib import Path

currentVersion = "v1.0.5 - Release"
pid = os.getpid()

OUTPUT_PATH = pathlib.Path(__file__).parent.absolute()
LOGS_PATH = OUTPUT_PATH / Path("./logs")

def relative_to_logs(path: str) -> Path:
    """Return a path relative to the logs folder."""
    return LOGS_PATH / Path(path)

def get_timestamp():
    """Return a unix timestamp."""
    return time.time()

def logRoutine(log: str, timeNeeded: bool = True):
    """Write strings to the log file and if debug is enabled, print it to console."""

    if timeNeeded is None:
        timeNeeded = True

    debugMode = False
    currentTime = time.strftime("%m-%d-%Y -> %H:%M:%S")
    logHeader = f"""{currentVersion}
===================================================
          LOG FILE MADE FOR DEBUG PURPOSES
      made by Nicolas Mendes - September 2021
===================================================\n
"""

    # Check if "ioc.log" exists, if not create this file.
    if not os.path.exists(relative_to_logs("ioc.log")):
        open(f"{relative_to_logs('ioc.log')}", "w+")
        # append logHeader to the file.
        with open(f"{relative_to_logs('ioc.log')}", "a") as logFile:
            logFile.write(logHeader)

    # if the first line of ioc.log is different from currentVersion
    with open(f"{relative_to_logs('ioc.log')}") as checkVer:
        firstlineVer = checkVer.readline().rstrip()
        if firstlineVer != currentVersion:
            # Delete everything inside the file and append logHeader.
            with open(f"{relative_to_logs('ioc.log')}", "w+") as logFile:
                logFile.write(logHeader)

    # if the file exceeds 1000 lines, delete everything and append logHeader to the file.
    with open(f"{relative_to_logs('ioc.log')}", "r") as logFile:
        if len(logFile.readlines()) > 1000:
            with open(f"{relative_to_logs('ioc.log')}", "w") as logFile:
                logFile.write(logHeader)

    # Append the log to the file.

    if timeNeeded == True:
        with open(f"{relative_to_logs('ioc.log')}", "a") as logFile:
            logFile.write(f"{currentTime} - {log}\n")
    else:
        with open(f"{relative_to_logs('ioc.log')}", "a") as logFile:
            logFile.write(f"{log}\n")

    if debugMode == True:
        return print(f"DEBUG LOG: {log}")

logRoutine(
    f"\n\n[OK] ===> Python loaded. Starting new instance at PID: {pid} | UTS: {get_timestamp()}\n",
    False,
)

πŸ“„ License

Permissions of this strong copyleft license are conditioned on making available complete source code of licensed works and modifications, which include larger works using a licensed work, under the same license. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.

Permissions Restrictions Conditions
βœ“ Commercial Use Γ— Liability πŸ›ˆ License and Copyright Notice
βœ“ Modification Γ— Warranty πŸ›ˆ State changes
βœ“ Distribution πŸ›ˆ Disclose source
βœ“ Patent Use πŸ›ˆ Same license
βœ“ Private Use