Skip to main content

Centralized, reusable utilities for Python applications (Python counterpart of go-utils).

Project description

faze-utils

Centralized, reusable utilities for Python applications — the Python counterpart of the internal go-utils module.

Supports Python 3.9+. Core install has zero runtime dependencies; every backend integration (Redis, MongoDB, PostgreSQL, Aerospike, Pub/Sub, GCS, S3) imports its driver lazily and is enabled via an extra.

Installation

pip install faze-utils                       # core: config, logs, errors, validation, ...
pip install 'faze-utils[redis]'              # + cache
pip install 'faze-utils[mongo,postgres]'     # + db backends
pip install 'faze-utils[pubsub]'             # + pubsub and queue
pip install 'faze-utils[gcs,s3]'             # + upload packages
pip install 'faze-utils[all]'                # everything (except aerospike)

Package structure

Package Ported from What it does
faze_utils.config config/ Viper-equivalent config: ENV_FILE/CONFIG JSON loading, case-insensitive dot-notation typed getters, is_set, internal-service URL registry, proxy URLs
faze_utils.logs logs/ stdlib-logging setup: JSON/INFO in prod, readable/DEBUG otherwise, IST timestamps, OTel trace-id correlation via with_context()
faze_utils.cache cache/ Full Redis wrapper (Cache): strings/JSON, counters, TTLs, hashes, lists, sets, NX locks, pattern scan/delete, pipelines
faze_utils.db db/ Connection factories: init_mongo_db, init_postgres_db/close_postgres_db, init_aerospike_db/close_aerospike_db
faze_utils.pubsub pubsub/ GCP Pub/Sub publisher/subscribers, queueName envelope compatibility, OTel propagation, idempotent topology bootstrap with DLQ support
faze_utils.queue queue/ Simple Pub/Sub string-message client with auto-ack-on-success consumer
faze_utils.request request/ camelCase wire error codes, RequestError, {"status": ...} envelopes
faze_utils.response response/ ServiceError + ErrorCode, HTTP status mapping, {"success": ...} envelopes
faze_utils.slack utils/slack.go send_slack_alert via chat.postMessage (stdlib HTTP)
faze_utils.upload_files utils/upload.files.go Upload validation, safe unique object naming, GCS upload
faze_utils.upload_s3_compat utils/upload_s3_compat.go GCSUploader for any S3-compatible endpoint: unique/conflict-check/overwrite uploads, raw-bytes upload
faze_utils.validation validation/ Declarative field rules (required, lengths, bounds, email, pattern, custom, nested) raising VALIDATION_FAILED ServiceErrors

Configuration example

from faze_utils.config import Config

# From the CONFIG env var or ./env.<ENV_FILE>.json:
cfg = Config.load()

# Or construct directly (tests, scripts):
cfg = Config({
    "environment": "prod",
    "redis": {"address": "localhost:6379", "db": 0},
    "postgres": {"user": "svc", "password": "...", "host": "db", "port": 5432,
                  "dbname": "app", "sslmode": "require"},
    "pubSub": {"serviceAccount": {...}, "topicSubscriptions": {
        "orders": [{"name": "orders-sub", "maxDeliveryAttempts": 5}],
    }},
})

cfg.get_string("redis.address")   # "localhost:6379"
cfg.get_int("postgres.port")      # 5432
cfg.is_set("mongodb.minPoolSize") # distinguishes unset from explicit 0
cfg.get_service_url("walletService")

Usage examples

# logs
from faze_utils import logs
logger = logs.new_logger("prod")          # JSON lines, INFO+
logs.with_context().info("handling job")  # + trace_id/span_id when OTel active

# cache (pip install 'faze-utils[redis]')
from faze_utils.cache import Cache, KeyNotFoundError
cache = Cache.from_config(cfg)
cache.set_json("user:1", {"name": "a"}, expiration=300)
try:
    value = cache.get("user:1")
except KeyNotFoundError:
    ...

# db (pip install 'faze-utils[mongo]' / '[postgres]' / '[aerospike]')
from faze_utils.db import init_mongo_db, init_postgres_db, close_postgres_db
mongo = init_mongo_db(cfg)
pool = init_postgres_db(cfg)
with pool.connection() as conn:
    conn.execute("SELECT 1")
close_postgres_db(pool)

# pubsub (pip install 'faze-utils[pubsub]')
from faze_utils.pubsub import PubSub
ps = PubSub.from_config(cfg)
ps.ensure_topology_from_config(cfg)   # idempotent create-only bootstrap
ps.publish("orders", {"orderId": 42}, attrs={"region": "us"})
ps.start_subscribers({"orders": lambda msg: (process(msg), msg.ack())},
                     topics=["orders"])

# queue
from faze_utils.queue import PubSubClient
q = PubSubClient.from_config(cfg)
q.send_message_to_queue("jobs", "payload")
q.receive_message("jobs", process_func=handle)   # acks on success

# request / response envelopes
from faze_utils import request as req
from faze_utils.response import not_found, success_response
err = req.not_found_error("player not found")
body, status = req.error_envelope(err), err.http_status

# validation
from faze_utils import validation as v
v.validate_or_raise(payload, {
    "name": [v.required, v.min_length(2)],
    "email": [v.required, v.email],
    "team.name": [v.required],
})  # raises ServiceError(VALIDATION_FAILED) listing every failed field

# slack
from faze_utils.slack import send_slack_alert
send_slack_alert("#alerts", "deploy finished", "deploy-bot", icon_emoji=":rocket:")

# uploads (pip install 'faze-utils[gcs]' / '[s3]')
from faze_utils.upload_files import upload_file_to_gcp
from faze_utils.upload_s3_compat import GCSUploader
resp = upload_file_to_gcp(fileobj, "reports/q3", "media-bucket",
                          original_filename="q3.pdf")
uploader = GCSUploader.from_env("media-bucket")
resp = uploader.upload_file_with_conflict_check(fileobj, "docs/report.txt")

Development

python3 -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'        # install with dev dependencies

pytest                         # run tests
pytest --cov                   # tests with coverage
ruff format src tests          # format
ruff check src tests           # lint
mypy                           # type-check

python -m build                # build sdist + wheel into dist/
twine check dist/*             # validate the distributions

Tests use fakeredis and mocks — no real Redis, databases, brokers, Slack, or cloud accounts are contacted.

Publishing

Confirm the package name, version (pyproject.toml), and author details first. You need API tokens for TestPyPI and PyPI.

# 1. Clean build + validate
rm -rf dist/ && python -m build && twine check dist/*

# 2. Publish to TestPyPI first
twine upload --repository testpypi dist/*
# username: __token__, password: your TestPyPI API token

# 3. Verify the TestPyPI install in a scratch environment
pip install --index-url https://test.pypi.org/simple/ \
    --extra-index-url https://pypi.org/simple/ faze-utils

# 4. Publish to PyPI
twine upload dist/*

Security guidance

  • Never commit credentials. All secrets flow in via the CONFIG env var, env files excluded from VCS, or dedicated env vars (SLACK_TOKEN, GCS_ACCESS_KEY_ID, GCS_SECRET_ACCESS_KEY).
  • Config/DB error messages and logs never echo secret values (the postgres factory deliberately does not log the DSN, unlike the Go original).
  • Store PyPI tokens in ~/.pypirc (chmod 600) or TWINE_USERNAME=__token__ / TWINE_PASSWORD env vars.
  • upload_files rejects path-traversal object names (.., absolute paths, backslashes).

Behavioral differences from the Go implementation

  • request: too_many_requests_error returns HTTP 429; the Go version returned 404 (a bug).
  • cache: no context.Context parameters (sync redis-py); expirations accept seconds or timedelta; misses raise KeyNotFoundError instead of returning an error string.
  • db/postgres: connection URL (with password) is not logged.
  • db/aerospike: Go's ConnectionQueueSize has no direct equivalent in the Python client and is not applied; TLS "skip verify" is likewise driver-managed.
  • logs: with_context() takes no arguments — the active OTel span is ambient in Python.
  • pubsub/queue: subscriber topics are passed explicitly (Go read pubSub.subscribers config inside the function); OTel tracing degrades to a no-op when opentelemetry isn't installed; PublishV2 is folded into publish(..., ordering_key=...).
  • upload_files: takes file objects + field values instead of *http.Request; adds path-traversal protection.
  • upload_s3_compat: NewGCSUploaderFromViper became GCSUploader.from_config (reads the same gcs.options keys).
  • response: gRPC status converters were not ported (would force a grpcio dependency); HTTP mapping is complete.
  • config: formatEnvKeys (dotenv-style key rewriting) was not ported — nothing in the migrated packages uses it.

License

MIT

Project details


Download files

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

Source Distribution

faze_utils-0.1.1.tar.gz (46.3 kB view details)

Uploaded Source

Built Distribution

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

faze_utils-0.1.1-py3-none-any.whl (40.2 kB view details)

Uploaded Python 3

File details

Details for the file faze_utils-0.1.1.tar.gz.

File metadata

  • Download URL: faze_utils-0.1.1.tar.gz
  • Upload date:
  • Size: 46.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for faze_utils-0.1.1.tar.gz
Algorithm Hash digest
SHA256 513f089fe00c4b67b7bffa8631300b060447fa15353a657c65af2df0b6b8cb35
MD5 16c8531cfec50da2b15d5e644be400a7
BLAKE2b-256 00758274b7c7b5db2d6815ea59e6dafb7f0902756445068acab700aafb3fb916

See more details on using hashes here.

Provenance

The following attestation bundles were made for faze_utils-0.1.1.tar.gz:

Publisher: auto-release.yml on Faze-Technologies/python-utils

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file faze_utils-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: faze_utils-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 40.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for faze_utils-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 14b58bcb6326a6288995472d8916622fd3521fdc0bdf04e9a664f733a41f55de
MD5 5e39784ac0780e6796cf0a786daff2ca
BLAKE2b-256 5fa2210eca53e9891147f7b708d3a1e82a17fd0951f381a85a85c2ea93c26f8a

See more details on using hashes here.

Provenance

The following attestation bundles were made for faze_utils-0.1.1-py3-none-any.whl:

Publisher: auto-release.yml on Faze-Technologies/python-utils

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page