Skip to main content

Setech Utilities

Python utility library for API clients, logging, and various development tasks.

Installation

Install the package using pip:

pip install setech

Or with virtualenv (recommended):

python -m venv .venv 
source .venv/bin/activate # On Linux/macOS

Extra Dependencies

The following optional dependencies are available:

Extra Description
django Django support (>=5.2, django-crum, django-model-utils)
drf Django REST Framework with full Django stack
dns DNS utilities (dnspython with dnssec, doh, idna support)
dns-redis DNS cache using Redis backend

Install extras:

pip install "setech[django]"      # Django support
pip install "setech[drf]"         # Django REST Framework
pip install "setech[dns]"         # DNS utilities
pip install "setech[dns-redis]"   # DNS cache with Redis

Core Dependencies

The package requires the following core dependencies:

  • httpx2[http2]
  • num2words
  • phonenumberslite
  • pydantic
  • unidecode

Development Setup

The repository uses uv as the package manager and dependency resolver.
Follow these steps to get a fully‑featured development environment with one command.

# 1. Create and activate a virtual environment (recommended)
python -m venv .venv 
source .venv/bin/activate # On Windows use `.venv\Scripts\activate`
# 2. Install uv (if you don’t already have it)
curl -LsSf [https://astral.sh/uv/install.sh](https://astral.sh/uv/install.sh) | sh
# 3. Sync all base, dev and optional extras in a single command
uv sync --dev
# 4. Verify everything is installed
uv run python -m pip list | grep setech

What the --dev flag does

  • Installs the core dependencies listed under [project].
  • Adds the optional extras django, drf and dns‑redis (via setech[... ]).
  • Installs the development tooling (ipython, ruff, mypy, test runners, etc.) from the [dependency-groups] dev section.
  • Resolves transitive dependencies only once, so no duplication occurs.

Tip: If you ever need to refresh the lockfile after a dependency bump, run:

uv sync --dev --upgrade

Example code

API Clients

# integration/client.py
from setech import SyncClient


class LocalClient(SyncClient):
    name = "local"
    _base_url = "https://obligari.serveo.net/ping/local"

    def __init__(self, nonce=None):
        super().__init__(nonce)
        self._session.headers.update(
            {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; rv:123.0) Gecko/20100101 Firefox/123.0"}
        )

    def send_post_ping(self, var1: str, var2: int) -> bool:
        res = self.post("/some-post", json={"variable_one": var1, "second_variable": var2})
        return res.json().get("status")

    def send_put_ping(self, var1: str, var2: int) -> bool:
        res = self.put("/some-put", data={"variable_one": var1, "second_variable": var2})
        return res.json().get("status")

    def send_get_ping(self, var1: str, var2: int) -> bool:
        res = self.get("/some-get", params={"variable_one": var1, "second_variable": var2})
        return res.json().get("status")

    def send_patch_ping(self, var1: str, var2: int) -> bool:
        res = self.put("/some-patch", data=(("variable_one", var1), ("variable_one", var2)))
        return res.json().get("status")

    def send_trace_ping(self, var1: str, var2: int) -> bool:
        res = self.trace("/some-trace", params=(("variable_one", var1), ("variable_one", var2)))
        return res.json().get("status")
# integration/main.py
from integration.client import LocalClient


client = LocalClient()
client.send_post_ping("asd", 123)
client.send_put_ping("asd", 123)
client.send_get_ping("asd", 123)
client.send_patch_ping("asd", 123)
client.send_trace_ping("asd", 123)

Log output

Simple

[14d709e02c0c] Preparing POST request to "https://obligari.serveo.net/ping/local/some-post"
[14d709e02c0c] Sending request with payload=b'{"variable_one": "asd", "second_variable": 123}'
[14d709e02c0c] Response response.status_code=200 str_repr_content='{"status":true,"request_id":62}'
[14d709e02c0c] Preparing GET request to "https://obligari.serveo.net/ping/local/some-get"
[14d709e02c0c] Sending request with payload=None
[14d709e02c0c] Response response.status_code=200 str_repr_content='{"status":true,"request_id":63}'

Structured

{"app": "dev", "level": "DEBUG", "name": "APIClient", "date_time": "2024-03-09 22:59:24", "location": "api_client/client.py:_request:71", "message": "[cfbdadc56f53] Preparing POST request to \"https://obligari.serveo.net/ping/local/some-post\"", "extra_data": {"hooks": {"response": []}, "method": "POST", "url": "https://obligari.serveo.net/ping/local/some-post", "headers": {}, "files": [], "data": [], "json": {"variable_one": "asd", "second_variable": 123}, "params": {}, "auth": null, "cookies": null}}
{"app": "dev", "level": "INFO", "name": "APIClient", "date_time": "2024-03-09 22:59:24", "location": "api_client/client.py:_request:74", "message": "[cfbdadc56f53] Sending request with payload=b'{\"variable_one\": \"asd\", \"second_variable\": 123}'", "extra_data": {"payload": "{\"variable_one\": \"asd\", \"second_variable\": 123}"}}
{"app": "dev", "level": "INFO", "name": "APIClient", "date_time": "2024-03-09 22:59:25", "location": "api_client/client.py:_request:81", "message": "[cfbdadc56f53] Response response.status_code=200 str_repr_content='{\"status\":true,\"request_id\":72}'", "extra_data": {"status_code": 200, "content": "{\"status\":true,\"request_id\":72}"}}
{"app": "dev", "level": "DEBUG", "name": "APIClient", "date_time": "2024-03-09 22:59:25", "location": "api_client/client.py:_request:71", "message": "[cfbdadc56f53] Preparing GET request to \"https://obligari.serveo.net/ping/local/some-get\"", "extra_data": {"hooks": {"response": []}, "method": "GET", "url": "https://obligari.serveo.net/ping/local/some-get", "headers": {}, "files": [], "data": [], "json": null, "params": {"variable_one": "asd", "second_variable": 123}, "auth": null, "cookies": null}}
{"app": "dev", "level": "INFO", "name": "APIClient", "date_time": "2024-03-09 22:59:25", "location": "api_client/client.py:_request:74", "message": "[cfbdadc56f53] Sending request with payload=None", "extra_data": {"payload": "{}"}}
{"app": "dev", "level": "INFO", "name": "APIClient", "date_time": "2024-03-09 22:59:25", "location": "api_client/client.py:_request:81", "message": "[cfbdadc56f53] Response response.status_code=200 str_repr_content='{\"status\":true,\"request_id\":74}'", "extra_data": {"status_code": 200, "content": "{\"status\":true,\"request_id\":73}"}}

django features

Using the setech.django in a Django project

Models

The library ships two abstract base models that can be inherited to get timestamping, UUID and audit fields.

# myapp/models.py
from django.db import models 
from setech.django.models import TimeStampedUUIDModel, UserTimeStampedUUIDModel

class Article(TimeStampedUUIDModel): 
    title = models.CharField()
    content = models.TextField()

class Comment(UserTimeStampedUUIDModel): 
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    text = models.TextField()

What each base class provides

Base model Fields added Notes
TimeStampedUUIDModel uid, created_at, updated_at UUID (v4 or v7 if available) and timestamps
UserTimeStampedUUIDModel created_by, modified_by Populated automatically from the current request when using Django‑CRUM

Validators

setech.django.validators contains a handful of reusable validator functions that can be dropped straight into Django model fields or form/serializer validations.
They wrap common checks such as phone numbers, personal IDs and MX‑record validation for e‑mail addresses.

# myapp/models.py
from django.db import models
from setech.django.models import TimeStampedUUIDModel
from setech.django.validators import django_phone_number_validator, django_personal_code_validator, django_validate_email_mx_domain, validate_image_and_svg_file_extension

class UserProfile(TimeStampedUUIDModel):
    # Phone number in international format, e.g. "+44 20 7946 0958" 
    phone = models.CharField(validators=[django_phone_number_validator])
    
    # Personal ID (SSN/NRIC/etc.) – the validator automatically uses your
    # `settings.COUNTRY_CODE` (e.g. "LV" for Latvia)
    personal_id = models.CharField(max_length=20, validators=[django_personal_code_validator])
    
    # Email address – ensures the domain can receive mail
    email = models.EmailField(validators=[django_validate_email_mx_domain])
    
    # Image file – ensures the file is either image or svg
    picture = models.FileField(validators=[validate_image_and_svg_file_extension])

Customizing the country code

The validators rely on settings.COUNTRY_CODE to decide which format to expect.
Add the following to your Django settings:

# settings.py
from setech.django.constants import Country

...
COUNTRY_CODE = Country.LV # Latvia, change to your locale

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

setech-1.8.0.tar.gz (23.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

setech-1.8.0-py3-none-any.whl (34.7 kB view details)

Uploaded Python 3

File details

Details for the file setech-1.8.0.tar.gz.

File metadata

  • Download URL: setech-1.8.0.tar.gz
  • Upload date:
  • Size: 23.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for setech-1.8.0.tar.gz
Algorithm Hash digest
SHA256 34ef602cc810c7c9308b87f1494d5b2cc882312a95802c6ddff4139e432e7238
MD5 f5f0f5faa1b6c627c4758e9cfa9b2dbe
BLAKE2b-256 1087653e7df80058d52e972a5f1ddff9210fb8c019b3c6061a565bcaf6aeadc0

See more details on using hashes here.

File details

Details for the file setech-1.8.0-py3-none-any.whl.

File metadata

  • Download URL: setech-1.8.0-py3-none-any.whl
  • Upload date:
  • Size: 34.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for setech-1.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 356b3bde403f0d2d1989fb660b901659c8b8036667238afb0117d1b4306d884f
MD5 752d1b1b5fbd8635a66abf45bc40322f
BLAKE2b-256 38115f73d47a6179f8f6bf24bc4727826f504a7e608d87b99847a3ff0a8b4cd0

See more details on using hashes here.

Release history Release notifications | RSS feed

1.8.3

2 files

1.8.2

2 files

1.8.1

2 files

1.8.0.post1

2 files

This release

1.8.0 This release

2 files

1.7.1

2 files

1.7.0

2 files

1.6.1

2 files

1.6.0

2 files

1.5.4

2 files

1.5.3

2 files

1.5.2

2 files

1.5.1

2 files

1.5.0

2 files

1.4.2

2 files

1.4.1

2 files

1.4.0

2 files

1.3.4

2 files

1.3.3

2 files

1.3.2

2 files

1.3.1

2 files

1.3.0

2 files

1.2.1

2 files

1.2.0

2 files

1.1.1

2 files

1.1.0

2 files

1.0.1

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page