Metadata-Version: 2.4
Name: agileconfig-python
Version: 0.1.4
Summary: Python client for AgileConfig with WebSocket updates and environment fallback
Author: Wei Zheng
License: MIT License
        
        Copyright (c) 2026 AgileConfig contributors
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
Project-URL: Homepage, https://github.com/IshootLaser/agileconfig-python
Project-URL: Repository, https://github.com/IshootLaser/agileconfig-python
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31
Requires-Dist: websockets<16,>=15
Dynamic: license-file

# agileconfig-python

[![CI](https://github.com/IshootLaser/agileconfig-python/actions/workflows/ci.yml/badge.svg)](https://github.com/IshootLaser/agileconfig-python/actions/workflows/ci.yml)
[![Publish](https://github.com/IshootLaser/agileconfig-python/actions/workflows/publish.yml/badge.svg)](https://github.com/IshootLaser/agileconfig-python/actions/workflows/publish.yml)
[![PyPI version](https://img.shields.io/pypi/v/agileconfig-python.svg)](https://pypi.org/project/agileconfig-python/)
[![Python versions](https://img.shields.io/pypi/pyversions/agileconfig-python.svg)](https://pypi.org/project/agileconfig-python/)

Python client for [AgileConfig](https://github.com/dotnetcore/AgileConfig). The client loads configuration over HTTP, listens for reload notifications over WebSocket, and falls back to `os.environ` when the server is unavailable.

## Installation

```bash
python -m pip install agileconfig-python
```

## Usage

```python
from agileconfig_python import AgileConfigLoader

loader = AgileConfigLoader(
	url="wss://config.example.com/ws",
	app_id="my-app",
	secret="your-secret",
	env="DEV",
)

database_host = str(loader.get_var("host", prefix="database"))
loader.stop()
```

The initial HTTP request populates the cache. When AgileConfig publishes a reload event, the cache is refreshed. If the server cannot be reached within the configured timeout, `get_var_value` reads the variable name from the process environment and ignores its prefix.

## Singleton behavior and safety notes

`AgileConfigLoader` is a process-wide singleton. Creating it multiple times in the same Python process returns the same instance and does not reinitialize it.

Important implications:

- Do not assume a second `AgileConfigLoader(...)` call with different credentials or URL will switch connections. The first initialization wins for the process lifetime.
- Avoid creating loaders in many modules with different parameters. Prefer one startup location and reuse that instance.
- Calling `loader.stop()` stops the shared background listener for the singleton. Other parts of your app using the same loader will stop receiving updates.
- In tests, call `stop()` during teardown to avoid background-thread leakage across test cases.
- If your process forks workers, create the loader inside each worker process after fork, not in the parent before fork.

## API

- `AgileConfigLoader(url, app_id, secret, env)` creates the singleton loader and starts its listener.
- `loader.get_var_value(var_name, prefix="")` returns the current value, or an environment fallback.
- `loader.get_var(var_name, prefix="")` returns a lazy string-like proxy that reads the latest value when converted with `str()`.
- `loader.stop()` closes the WebSocket and stops the listener thread.

## Development

```bash
python -m pip install --upgrade pip build
python -m pip install -e .
python -m unittest discover -s unit_tests -v
python -m build
```

The live integration tests embedded in the source require an AgileConfig server and are not run by the offline unit-test command.

## Release checklist

1. Update `version` in `pyproject.toml`.
2. Run the unit tests and `python -m build`.
3. Inspect both artifacts with `python -m twine check dist/*`.
4. Upload to TestPyPI and install the uploaded version in a clean environment.
5. Create a Git tag matching the version, for example `v0.1.0`.
6. Publish to PyPI with a trusted publisher or `twine upload dist/*`.
