Skip to main content

Common Application Framework Code and Utilities

Project description

scitrera-app-framework

Common code and utilities for Scitrera applications and container images.

This package provides a lightweight application framework for Python services, CLIs, and desktop apps. It centers on a small environment/variables abstraction, structured logging, a simple plugin system, optional stateful working directories, and helper utilities for background execution, multi‑tenant configuration, and integration with external tools like Pyroscope.

Note: This repository is being gradually opened and updated. Expect incremental improvements and additional docs as code is moved from private projects.

Stack and Packaging

  • Language: Python (>= 3.9)
  • Dependencies (runtime):
    • botwinick-utils (>= 0.0.20)
    • vpd
    • python-json-logger (>= 4.0.0)
  • Optional/dev dependencies:
    • python-dotenv (for loading .env files via add_env_file_source)

Overview

Key capabilities:

  • Variables and configuration abstraction with layered sources and ergonomic access.
  • Structured logging integration with simple JSON formatting option.
  • Plugin and extension registry supporting both single and multi-extension modes.
  • Optional stateful working directory management (e.g., for containerized or desktop apps).
  • Background execution plugin backed by thread pool.
  • Optional profiling integration via Pyroscope plugin.
  • Multi-tenant configuration helper with pluggable provider.
  • Convenience utilities for desktop apps and test harnesses.

Requirements

  • Python 3.9+
  • OS: Windows, macOS, Linux (framework is OS-independent; some optional pieces may differ by platform)

Installation

Choose one of the following:

  • From source (editable):

    • pip install -e .
    • or install runtime deps only: pip install -r requirements.txt
  • From PyPI:

    • pip install scitrera-app-framework

Quick Start

Initialize the framework at the start of your program and get a logger:

from scitrera_app_framework import init_framework, get_logger

if __name__ == '__main__':
    v = init_framework('my-app', log_level='INFO')
    logger = get_logger(v)
    logger.info('Hello from SAF!')

    logger2 = get_logger(v, name='child')
    logger2.info('Hello from SAF child logger!')

Enable base plugins (e.g., background execution) and submit a job:

from scitrera_app_framework import init_framework, get_extension
from scitrera_app_framework.base_plugins import EXT_BACKGROUND_EXEC

init_framework('my-app', base_plugins=True)
get_extension(EXT_BACKGROUND_EXEC).submit_job(print, 'background task')

Multi-tenant example:

from scitrera_app_framework import init_framework, get_logger
from scitrera_app_framework.ext_plugins.multi_tenant import get_tenant_variables

v = init_framework('my-app', log_level='DEBUG', multitenant=True)

# Obtain per-tenant Variables and a logger
tenant_v = get_tenant_variables('tenant1', v=v)
tenant_logger = get_logger(tenant_v)
tenant_logger.info('tenant-specific log')

List plugins for a multi-extension point (from tests):

from scitrera_app_framework import init_framework, register_plugin, get_extensions
from scitrera_app_framework.api import Plugin, Variables

EXT = 'example-ext'


class ImplA(Plugin):
    def extension_point_name(self, v: Variables):
        return EXT

    def is_multi_extension(self, v: Variables):
        return True

    def initialize(self, v, logger):
        return 'A'


class ImplB(ImplA):
    def initialize(self, v, logger):
        return 'B'


init_framework('my-app')
register_plugin(ImplA)
register_plugin(ImplB)
print(get_extensions(EXT))  # ['A', 'B']

Entry Points and Scripts

  • Primary usage is as a Python library you import into your own application.
  • Utilities under scitrera_app_framework/slaunch/ provide helpers for launching and managing environments (e.g., conda), but are not wired as direct CLIs. You can import and call functions like slaunch.main.launch_app(...) from your code.

Key Modules and APIs

  • scitrera_app_framework.init_framework(...): main initialization; sets up logging, stateful paths, and shutdown hooks based on env/kwargs.
  • init_framework_desktop(...): desktop-friendly defaults (e.g., use ~/.config/<APP_NAME> and atexit hooks).
  • init_framework_test_harness(...): testing-friendly defaults (DEBUG logging, no shutdown hooks/stateful).
  • init_framework_embedded(...): use when embedding within larger apps; avoids overriding external logging.
  • get_logger(v=None, name=None): get the main or child logger.
  • get_working_path(v=None, default='.', env_key='DATA_WORKING_PATH'): resolve a working path from env/stateful.
  • Plugin system:
    • register_plugin(PluginType, v=None, init=False)
    • get_extension(ext_name_or_type, v=None) for single extension
    • get_extensions(ext_name_or_type, v=None) for multi-extension
    • Built-ins:
      • Background executor: EXT_BACKGROUND_EXEC and get_background_exec()
      • Pyroscope profiling: ext_plugins.pyroscope_plugin.PyroscopePlugin
      • Multi-tenant: ext_plugins.muti_tenant.MultiTenantPlugin
  • Env file support: scitrera_app_framework.core.util.add_env_file_source(".env") (requires python-dotenv).

Environment Variables

Common environment variables recognized by the framework and plugins:

Core and logging:

  • APP_NAME: Override computed application name.
  • BUILD_IMAGE_NAME: Image name for logging context (default: base app name).
  • BUILD_CONTAINER_VERSION: Version string (default: DEV).
  • LOGGING_LEVEL: Log level (default from init_framework(log_level=...)).
  • LOGGING_FORMAT: 'json' for JSON logs or a Python %-format string. Otherwise uses a sensible default.
  • LOGGING_DATE_FORMAT: Date/time format string.
  • SAF_ENABLE_PYTHON_FAULT_HANDLER: Enable Python faulthandler (default True unless overridden by init_framework).
  • SAF_INSTALL_SHUTDOWN_HOOKS: Install shutdown hooks (default True unless overridden).
  • SAF_SHUTDOWN_HOOK_VIA_ATEXIT: Prefer atexit-based hooks (default depends on context; init_framework_desktop uses True).

Stateful and paths:

  • SAF_SETUP_STATEFUL: Enable stateful working directory setup (default True unless overridden).
  • STATEFUL_ROOT: Root directory for stateful data (default: ./scratch unless overridden by init_framework).
  • RUN_ID: Used in stateful path composition.
  • RUN_SERIAL: Used in stateful path composition; may be auto-set when SAF_STATEFUL_SERIAL_STRATEGY=ms.
  • SAF_STATEFUL_CHDIR: Change working directory into the stateful path (default True for base; False in desktop helper).
  • SAF_STATEFUL_SERIAL_STRATEGY: e.g., ms to use current time in milliseconds.
  • DATA_WORKING_PATH: When set, overrides get_working_path() resolution.

Background executor plugin:

  • SAF_JOB_THREADS: Max worker threads (default from botwinick-utils).
  • SAF_JOB_COLLISIONS_INFO: Log job collisions as info (default False).

Pyroscope plugin:

  • PYROSCOPE_ENABLED: Enable plugin (also controllable via init_framework(pyroscope=True) defaulting).
  • PYROSCOPE_SERVER: Server address (default http://pyroscope.pyroscope.svc:4040).
  • PYROSCOPE_USER: Basic auth user (default empty).
  • PYROSCOPE_TOKEN: Basic auth password/token (default empty).
  • PYROSCOPE_TENANT: Tenant ID (default empty).
  • PYROSCOPE_SAMPLE_RATE: Sample rate (int, default 100).
  • PYROSCOPE_DETECT_SUBPROCESSES: Detect subprocesses (bool, default True).
  • PYROSCOPE_ON_CPU: CPU profiling (bool, default True).
  • PYROSCOPE_GIL_ONLY: GIL-only profiling (bool, default True).
  • PYROSCOPE_ENABLE_LOGGING: Enable Pyroscope client logging (bool, default False).
  • PYROSCOPE_TAG_*: Any variables with this prefix are turned into Pyroscope tags.

Multi-tenant plugin:

  • SAF_MULTITENANT_ENABLED: Enable multi-tenant plugin.
  • SAF_MULTITENANT_PROVIDER: Python import path for provider type (default is the built-in BaseMultiTenantProvider).
  • SAF_MULTITENANT_INCLUDE_ENV: If True, include process env in tenant Variables placement (EnvPlacement.BOTTOM).

Slaunch utilities (conda/python helpers)

Located under scitrera_app_framework/slaunch/ with functions for:

  • Creating and updating conda environments (apply conda/pip requirements).
  • Launching apps with manifests of libs/apps and self-update support.
  • Windows/macOS/Linux support via platform detection.

These are utility functions intended to be imported and used by your own launcher scripts. There is no direct CLI entry point defined in this package.

Kubernetes utilities (k8s.util)

The module scitrera_app_framework.k8s.util contains small, practical helpers for working with Kubernetes objects. These are useful when building simple operators or orchestrating activities from Python in a K8s environment.

  • Completely optional and separate from the core framework; nothing depends on them by default.
  • Requires the Kubernetes Python client (and uses utilities from vpd). Install with: pip install kubernetes.
  • Functions work with either plain dicts (YAML loaded) or Kubernetes client objects where noted.

Highlights:

  • apply_yaml_object(obj, verb='apply'|'get'|'replace'): Apply/get/replace a K8s object (TODO: describe server-side vs client-side apply).
  • parse_yaml(path_or_text): Parse YAML into Python objects (dicts). Handy for loading manifests.
  • start_pod(pod_def, wait=True, replace=False, wait_delay=0.25, wait_until_terminated=False): Create or replace a Pod and optionally wait until Running (or Terminated).
  • is_pod_running(pod_def, strict=False): Check if a Pod is Running (or Pending when strict=False).
  • is_pod_in_terminated_state(pod_def): Check if a Pod finished (Succeeded or Failed).
  • pod_exists(pod_def): Determine if a Pod currently exists.
  • get_pod_env(pod_def, container_name=None, container_index=0): Get a reference to a container's env list for in-place edits.
  • merge_env_vars(env, *complex_items, key_upper=True, **fixed_pairs): Merge environment variable definitions (supports complex valueFrom entries and simple key=value pairs). Modifies list in-place.
  • fixed_env_vars(**pairs): Build fixed env var entries from kwargs.
  • get_metadata_name(obj), get_metadata_namespace(obj): Extract metadata fields from dicts or client objects.
  • get_headless_service_dns_name_for_pod(pod_def, svc_def): Compose the DNS name <pod>.<service>.<namespace>.svc for a headless Service.

Example: launch a Pod from YAML and wait until it runs

from scitrera_app_framework.k8s.util import parse_yaml, start_pod, is_pod_running

pod = parse_yaml('pod.yaml')
start_pod(pod, wait=True)
print('running?', is_pod_running(pod, strict=True))

Example: add env vars to the first container of a Pod manifest before applying

from scitrera_app_framework.k8s.util import parse_yaml, get_pod_env, merge_env_vars, apply_yaml_object

pod = parse_yaml('pod.yaml')
env = get_pod_env(pod)
merge_env_vars(env, {'name': 'CONFIG_PATH', 'valueFrom': {'configMapKeyRef': {'name': 'my-cm', 'key': 'cfg'}}},
               IMAGE_TAG='v1.2.3', DEBUG=True)
apply_yaml_object(pod)  # apply modified manifest

Note: These helpers assume your local environment is configured to talk to a cluster (e.g., KUBECONFIG or in-cluster config).

Project Structure

  • scitrera_app_framework/ – Library code
    • api/ – Variables and Plugin base types
    • core/ – Initialization, logging, stateful paths, plugin registry (not intended to be used directly by users)
    • base_plugins/ – Built-in optional plugins (e.g., background executor)
    • ext_plugins/ – Optional extensions (e.g., Pyroscope, Multi-tenant)
    • k8s/ - Optional Kubernetes Utilities
    • slaunch/ – "slaunch" is a white-label ready set of utilities to maintain structured conda environments and applications with auto-updating on start, etc.
    • util/ – Miscellaneous helpers (async, parsing, imports)
  • setup.py – Build configuration
  • requirements.txt – Development/runtime dependencies list
  • LICENSE.txt – BSD 3‑Clause license text

License

BSD 3‑Clause License. See LICENSE.txt for details.

Contributing

  • Issues and PRs are welcome. Please keep changes small and focused.

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

scitrera_app_framework-0.0.62.tar.gz (64.3 kB view details)

Uploaded Source

Built Distribution

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

scitrera_app_framework-0.0.62-py3-none-any.whl (52.8 kB view details)

Uploaded Python 3

File details

Details for the file scitrera_app_framework-0.0.62.tar.gz.

File metadata

  • Download URL: scitrera_app_framework-0.0.62.tar.gz
  • Upload date:
  • Size: 64.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for scitrera_app_framework-0.0.62.tar.gz
Algorithm Hash digest
SHA256 bff7fa10b9980059cf6175a23edeb0ede071d696bc4f456d58db57c5805c6e70
MD5 10f7d913f42f81bfc20fb29c06ea6b70
BLAKE2b-256 89e55841dbaa4888c2458ba919580dc399e12c73af4bf7d30ac0d879b3da7301

See more details on using hashes here.

File details

Details for the file scitrera_app_framework-0.0.62-py3-none-any.whl.

File metadata

File hashes

Hashes for scitrera_app_framework-0.0.62-py3-none-any.whl
Algorithm Hash digest
SHA256 eae004c8cf7bcea361de0c2480e7ca06e154158d3b7929eb58174bb66ea59cde
MD5 c5a9725dd8edcccda46325ec2e96db11
BLAKE2b-256 91926b956e32d5d591b2431982f6de228a88e416572d6fbf27db17e4adee3732

See more details on using hashes here.

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