Spyke

Posts

programming·Programmingbyharsh3466

Looking for feedback on a python class definition

Hello Programming.dev gang,

I'm still wrapping my head around classes, oop, and scoping, and was hoping to get some feedback. on a class definition.

Here's the background:

I'm implementing pydantic for handing my .env secrets and constants (Haven't gotten to a secrets manager yet), with pydantic_settings's BaseSettings and SettingsConfigDict, as well as pydantic's SecretStr.

I set up a parent class in settings.py to configure the .env file and settings, which you can see below.

::: spoiler settings.py Parent Class (click to expand)

from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import SecretStr


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file="/opt/pyprojects/media-scripts/.env.dev", extra="ignore"
    )

:::

I've got a self hosted Baserow database that I'm using for my project, and I defined a class in baserow.py that inherits from the Settings class for the .env config, and has all of the fucntions for interacting with the database as methods of the class, which you can see below.

(I've got a number of other classes similar to Baserow planned that will inherit from the Settings class, provided I'm on the right track with this layout.)

I thought it was a good idea to put the methods in the class with the .env var attributes to keep scoping focused and local. I'm wondering if this is a good idea or not, and am also open to any other feedback on the code/classes anyone might have. Thank you to anyone taking the time to read and/or respond.

::: spoiler baserow.py with Baserow child class (click to expand)

import json

import requests
from pydantic import SecretStr

from mods.settings import Settings


class Baserow(Settings):
    br_url: str
    br_api: SecretStr

    def query_db(self, urlFilters):
        url = f"{self.br_url}?user_field_names=true&{urlFilters}"
        headers = {"Authorization": f"Token {self.br_api}"}

        brQueryResults = requests.get(url, headers=headers)

        return brQueryResults.json()

    def get_all_shows(self):
        url = f"{self.br_url}?user_field_names=true&size=200"
        headers = {"Authorization": f"Token {self.br_api.get_secret_value()}"}

        brQueryResults = requests.get(url, headers=headers)

        return brQueryResults.json()

    def post_db(self, showDictionary):
        brPostResults = requests.post(
            f"{self.br_url}?user_field_names=true",
            headers={
                "Authorization": f"Token {self.br_api.get_secret_value()}",
                "Content-Type": "application/json",
            },
            json=showDictionary,
        )

        print(json.dumps(brPostResults.json(), indent=2))

    def patch_db(self, showDictionary, row):
        brPatchResults = requests.patch(
            f"{self.br_url}?user_field_names=true",
            headers={
                "Authorization": f"Token {self.br_api.get_secret_value()}",
                "Content-Type": "application/json",
            },
            json=showDictionary,
        )

        print(brPatchResults)

    def delete_db_row(self, row):
        brDeleteResults = requests.delete(
            f"{self.br_url}{row}/",
            headers={
                "Authorization": f"Token {self.br_api.get_secret_value()}",
            },
        )
        print(brDeleteResults)

:::

View original on lemmy.ml
programming·Programmingbyharsh3466

Kinda proud of this python script I wrote

So I'm learning python (by doing, reading, and doing some more), and I've been wanting to automate updating the listening port in my qbittorrent docker container when the gluetun vpn container changes the forwarded port.

This is what I came up with, which I combined with an hourly cronjob to keep it the listen port updated.

(Code under the spoiler tag to make this more readable).

::: spoiler Tap for spoiler

import re
import os
import logging
from datetime import datetime
from dotenv import load_dotenv

import docker
from qbittorrent import Client

# FUNCTION DECLARATION #


def log(code, message):
    logFilePath = "/opt/pyprojects/linux-iso-torrents/pf.log"
    logDateTime = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
    message = f"[{logDateTime}] {message}"
    logger = logging.getLogger(__name__)
    logging.basicConfig(
        filename=f"{logFilePath}", encoding="utf-8", level=logging.DEBUG
    )
    match code:
        case "debug":
            logger.debug(message)
        case "info":
            logger.info(message)
        case "warning":
            logger.warning(message)
        case "error":
            logger.error(message)


def get_current_forwarded_port():
    client = docker.from_env()
    container = client.containers.get("a40124291102d")
    logs = container.logs().decode("utf-8")
    allPortForwards = re.findall(r".+port forwarded is [0-9]{5}", logs)
    currentForwardedPort = allPortForwards[-1].split(" ")[-1]

    return currentForwardedPort


def get_current_listening_port():
    qbConfigUrl = "/home/server/docker/qbit/config/qBittorrent/qBittorrent.conf"
    with open(qbConfigUrl) as f:
        config = f.read()
        qbitListenPort = re.search(r"Session\\Port=[0-9]{5}", config)
        currentListeningPort = qbitListenPort.group(0).split("=")[-1]

    return currentListeningPort


def update_qbittorrent_listen_port(port):
    QB_URL = os.getenv("QB_URL")
    QB_USER = os.getenv("QB_USER")
    QB_PASSWORD = os.getenv("QB_PASSWORD")
    portJSON = {}
    portJSON["listen_port"] = port
    qb = Client(QB_URL)
    qb.login(f"{QB_USER}", f"{QB_PASSWORD}")

    qb.set_preferences(**portJSON)


# BEGIN SCRIPT #

load_dotenv()

currentForwardedPort = get_current_forwarded_port()
currentListeningPort = get_current_listening_port()

if currentForwardedPort != currentListeningPort:
    update_qbittorrent_listen_port(currentPort)
    log("info", f"qbittorrent listen port set to {currentForwardedPort}")
else:
    log("info", "forwarded port and listen port are a match")

:::

There's more I want to do, the next thing being to check the status of both containers and if one or both are down, to log that and gracefully exit, but for now, I'm pretty happy with this (open to any feedback, always willing to learn).

View original on lemmy.ml

Question About Bash Command Grouping Behavior in Script vs CLI

Question for you all.

I was working on a bash script and working out some logic for command chaining and grouping for executing different functions in my script based on the return status of other functions.

I know you can group commands with (), which spawns a subshell, and within the grouping you can chain with ;, &&, and ||.

I also know that you can group commands with {}, but, within the curly braces you can only separate commands with ;.

EDIT: the above statement of the curly braces only allowing ; is incorrect. I misunderstood what I had read. @[email protected] pointed out my mistake.

The requirement is that the list of commands in the curly braces needs to be terminated with a semicolon or a newline character, and in the script, I unknowingly was meeting the rules by using the newlines to make the code easier to read for myself.

END EDIT:

In the script, for readability I did group the commands across lines kind of like a function.

The script is pretty simple. I created a few functions with echo commands to test the logic. the script asks for input of foo or bar. The first function has an if, and if the input is foo, it executes. If it's bar it returns 1.

The return of 1 triggers the or (||) and executes the second command group.

The idea was, if the user inputs foo, the first group triggers printing foo baz to stdout. If the user inputs bar, the foo function returns 1, the baz function does not execute, the or is triggered, and bar but is printed to stdout

Here's the script (which executes as described):

Can anyone explain why I'm able to group with curly braces and still use && inside them?

(Also, the reason I want to use the curly braces is I don't want to spawn a subshell. I want variable persistence in the current shell)

#! /usr/bin/bash

# BEGIN FUNCTIONS #

foo () {

    if [[ "${input}" = foo ]]; then
        echo "foo"
        return 0
    else
        return 1
    fi

}

bar () {

    echo "bar"

}

baz () {

    echo "baz"

}

but () {

    echo "but"

}

# END FUNCTIONS #

read -p "foo or bar? " input

{
    foo && 
    baz
} ||

    {
        bar &&
        but
    }
View original on lemmy.ml