configpig 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ CONFIGPIG_API_KEY=sk-api01-your-key-here
2
+ CONFIGPIG_URL=https://configpig.com
@@ -0,0 +1,35 @@
1
+ name: Publish package to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build-and-publish:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Checkout code
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: '3.11'
22
+
23
+ - name: Install build tools
24
+ run: |
25
+ python -m pip install --upgrade pip
26
+ pip install build
27
+
28
+ - name: Build package
29
+ run: python -m build
30
+
31
+ - name: Publish to PyPI
32
+ uses: pypa/gh-action-pypi-publish@release/v1
33
+ with:
34
+ password: ${{ secrets.PYPI_API_TOKEN }}
35
+ skip-existing: true
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .venv/
10
+ venv/
11
+ env/
12
+ .env
13
+ *.so
14
+ .mypy_cache/
15
+ .pytest_cache/
16
+ .ruff_cache/
17
+ .coverage
18
+ htmlcov/
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-02-24)
4
+
5
+ - Initial release
6
+ - `Client` class with 19 API methods (configs, versions, fetch, labels, exports, health)
7
+ - `get_config()` drop-in function with TTL caching
8
+ - `configure()` for setting global defaults
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bright Wing Solutions LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: configpig
3
+ Version: 0.1.0
4
+ Summary: Python SDK for ConfigPig — managed config file registry for teams
5
+ Project-URL: Homepage, https://configpig.com
6
+ Project-URL: Repository, https://github.com/Brightwing-Systems-LLC/configpig-python
7
+ Project-URL: Documentation, https://configpig.com/docs/sdks/python
8
+ Project-URL: Bug Tracker, https://github.com/Brightwing-Systems-LLC/configpig-python/issues
9
+ Project-URL: Changelog, https://github.com/Brightwing-Systems-LLC/configpig-python/blob/main/CHANGELOG.md
10
+ Author-email: Bright Wing Solutions LLC <support@brightwingsolutions.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: config,configuration,json,sdk,toml,yaml
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Libraries
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: httpx>=0.24
25
+ Description-Content-Type: text/markdown
26
+
27
+ # ConfigPig — Python SDK
28
+
29
+ Python SDK for [ConfigPig](https://configpig.com), a managed config file registry for teams.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install configpig
35
+ ```
36
+
37
+ Or with uv:
38
+
39
+ ```bash
40
+ uv add configpig
41
+ ```
42
+
43
+ ## Configuration
44
+
45
+ Set environment variables or pass directly to the client:
46
+
47
+ ```bash
48
+ export CONFIGPIG_API_KEY="sk-api01-your-key-here"
49
+ export CONFIGPIG_URL="https://configpig.com" # optional, this is the default
50
+ ```
51
+
52
+ ## Quick Start — `get_config()`
53
+
54
+ The simplest way to use ConfigPig. One function, no boilerplate:
55
+
56
+ ```python
57
+ from configpig import get_config
58
+
59
+ # Fetch a config
60
+ content = get_config("my-config")
61
+
62
+ # With a specific label
63
+ content = get_config("my-config", label="production")
64
+
65
+ # With format conversion
66
+ content = get_config("my-config", output_format="yaml")
67
+
68
+ # With a fallback if the service is unreachable
69
+ content = get_config("my-config", fallback='{"default": true}')
70
+
71
+ # With TTL caching (seconds)
72
+ content = get_config("my-config", ttl=300)
73
+ ```
74
+
75
+ Set global defaults so you don't repeat yourself:
76
+
77
+ ```python
78
+ import configpig
79
+
80
+ configpig.configure(
81
+ api_key="sk-api01-...",
82
+ base_url="https://configpig.com",
83
+ default_label="latest",
84
+ default_ttl=300,
85
+ )
86
+ ```
87
+
88
+ ## Using the Full Client
89
+
90
+ For advanced use cases (creating configs, managing versions, labels, etc.):
91
+
92
+ ```python
93
+ from configpig import Client
94
+
95
+ client = Client()
96
+
97
+ # List all configs
98
+ result = client.list_configs()
99
+ if result.ok:
100
+ for cfg in result.data:
101
+ print(f"{cfg.slug}: {cfg.name}")
102
+
103
+ # Get a specific config
104
+ result = client.get_config("my-config")
105
+ if result.ok:
106
+ print(result.data.name)
107
+
108
+ # Fetch config content with optional format conversion
109
+ result = client.fetch("my-config", label="latest", output_format="yaml")
110
+ if result.ok:
111
+ print(result.data.content)
112
+
113
+ # Create a new config
114
+ result = client.create_config(
115
+ name="App Settings",
116
+ slug="app-settings",
117
+ content='{"debug": false, "log_level": "info"}',
118
+ format="json",
119
+ tags=["app"],
120
+ )
121
+
122
+ # Create a new version
123
+ result = client.create_version(
124
+ "app-settings",
125
+ content='{"debug": false, "log_level": "warn"}',
126
+ change_note="Changed log level to warn",
127
+ )
128
+
129
+ # Promote a version to a label
130
+ result = client.promote("app-settings", version=2, label="production")
131
+ ```
132
+
133
+ ## API Reference
134
+
135
+ All methods return `APIResponse[T]` with fields:
136
+ - `ok: bool` — whether the request succeeded
137
+ - `status_code: int` — HTTP status code
138
+ - `data: T | None` — response data (when `ok` is True)
139
+ - `error: str | None` — error message (when `ok` is False)
140
+
141
+ ### Configs
142
+
143
+ | Method | Description |
144
+ |---|---|
145
+ | `list_configs(tag?, search?)` | List all configs |
146
+ | `get_config(slug)` | Get config details |
147
+ | `create_config(...)` | Create a new config |
148
+ | `update_config(slug, ...)` | Update config metadata |
149
+ | `delete_config(slug)` | Delete a config |
150
+
151
+ ### Versions
152
+
153
+ | Method | Description |
154
+ |---|---|
155
+ | `list_versions(slug)` | List all versions |
156
+ | `get_version(slug, version_number)` | Get specific version |
157
+ | `create_version(slug, ...)` | Create a new version |
158
+
159
+ ### Fetch
160
+
161
+ | Method | Description |
162
+ |---|---|
163
+ | `fetch(slug, label?, output_format?)` | Fetch config content with optional format conversion |
164
+
165
+ ### Labels
166
+
167
+ | Method | Description |
168
+ |---|---|
169
+ | `list_labels()` | List team labels |
170
+ | `create_label(name, ...)` | Create a team label |
171
+ | `delete_label(name)` | Delete a team label |
172
+ | `list_config_labels(slug)` | List labels for a config |
173
+ | `assign_label(slug, label, version)` | Assign label to version |
174
+
175
+ ### Promote / Rollback
176
+
177
+ | Method | Description |
178
+ |---|---|
179
+ | `promote(slug, version, label)` | Assign label to a version |
180
+ | `rollback(slug, label)` | Rollback label to previous version |
181
+
182
+ ### Exports
183
+
184
+ | Method | Description |
185
+ |---|---|
186
+ | `list_exports()` | List exports |
187
+ | `create_export(name, ...)` | Create an export |
188
+ | `get_export(export_id)` | Get export details |
189
+ | `delete_export(export_id)` | Delete an export |
190
+
191
+ ### System
192
+
193
+ | Method | Description |
194
+ |---|---|
195
+ | `health_check()` | Check API health |
196
+
197
+ ## License
198
+
199
+ MIT — Bright Wing Solutions LLC
@@ -0,0 +1,173 @@
1
+ # ConfigPig — Python SDK
2
+
3
+ Python SDK for [ConfigPig](https://configpig.com), a managed config file registry for teams.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install configpig
9
+ ```
10
+
11
+ Or with uv:
12
+
13
+ ```bash
14
+ uv add configpig
15
+ ```
16
+
17
+ ## Configuration
18
+
19
+ Set environment variables or pass directly to the client:
20
+
21
+ ```bash
22
+ export CONFIGPIG_API_KEY="sk-api01-your-key-here"
23
+ export CONFIGPIG_URL="https://configpig.com" # optional, this is the default
24
+ ```
25
+
26
+ ## Quick Start — `get_config()`
27
+
28
+ The simplest way to use ConfigPig. One function, no boilerplate:
29
+
30
+ ```python
31
+ from configpig import get_config
32
+
33
+ # Fetch a config
34
+ content = get_config("my-config")
35
+
36
+ # With a specific label
37
+ content = get_config("my-config", label="production")
38
+
39
+ # With format conversion
40
+ content = get_config("my-config", output_format="yaml")
41
+
42
+ # With a fallback if the service is unreachable
43
+ content = get_config("my-config", fallback='{"default": true}')
44
+
45
+ # With TTL caching (seconds)
46
+ content = get_config("my-config", ttl=300)
47
+ ```
48
+
49
+ Set global defaults so you don't repeat yourself:
50
+
51
+ ```python
52
+ import configpig
53
+
54
+ configpig.configure(
55
+ api_key="sk-api01-...",
56
+ base_url="https://configpig.com",
57
+ default_label="latest",
58
+ default_ttl=300,
59
+ )
60
+ ```
61
+
62
+ ## Using the Full Client
63
+
64
+ For advanced use cases (creating configs, managing versions, labels, etc.):
65
+
66
+ ```python
67
+ from configpig import Client
68
+
69
+ client = Client()
70
+
71
+ # List all configs
72
+ result = client.list_configs()
73
+ if result.ok:
74
+ for cfg in result.data:
75
+ print(f"{cfg.slug}: {cfg.name}")
76
+
77
+ # Get a specific config
78
+ result = client.get_config("my-config")
79
+ if result.ok:
80
+ print(result.data.name)
81
+
82
+ # Fetch config content with optional format conversion
83
+ result = client.fetch("my-config", label="latest", output_format="yaml")
84
+ if result.ok:
85
+ print(result.data.content)
86
+
87
+ # Create a new config
88
+ result = client.create_config(
89
+ name="App Settings",
90
+ slug="app-settings",
91
+ content='{"debug": false, "log_level": "info"}',
92
+ format="json",
93
+ tags=["app"],
94
+ )
95
+
96
+ # Create a new version
97
+ result = client.create_version(
98
+ "app-settings",
99
+ content='{"debug": false, "log_level": "warn"}',
100
+ change_note="Changed log level to warn",
101
+ )
102
+
103
+ # Promote a version to a label
104
+ result = client.promote("app-settings", version=2, label="production")
105
+ ```
106
+
107
+ ## API Reference
108
+
109
+ All methods return `APIResponse[T]` with fields:
110
+ - `ok: bool` — whether the request succeeded
111
+ - `status_code: int` — HTTP status code
112
+ - `data: T | None` — response data (when `ok` is True)
113
+ - `error: str | None` — error message (when `ok` is False)
114
+
115
+ ### Configs
116
+
117
+ | Method | Description |
118
+ |---|---|
119
+ | `list_configs(tag?, search?)` | List all configs |
120
+ | `get_config(slug)` | Get config details |
121
+ | `create_config(...)` | Create a new config |
122
+ | `update_config(slug, ...)` | Update config metadata |
123
+ | `delete_config(slug)` | Delete a config |
124
+
125
+ ### Versions
126
+
127
+ | Method | Description |
128
+ |---|---|
129
+ | `list_versions(slug)` | List all versions |
130
+ | `get_version(slug, version_number)` | Get specific version |
131
+ | `create_version(slug, ...)` | Create a new version |
132
+
133
+ ### Fetch
134
+
135
+ | Method | Description |
136
+ |---|---|
137
+ | `fetch(slug, label?, output_format?)` | Fetch config content with optional format conversion |
138
+
139
+ ### Labels
140
+
141
+ | Method | Description |
142
+ |---|---|
143
+ | `list_labels()` | List team labels |
144
+ | `create_label(name, ...)` | Create a team label |
145
+ | `delete_label(name)` | Delete a team label |
146
+ | `list_config_labels(slug)` | List labels for a config |
147
+ | `assign_label(slug, label, version)` | Assign label to version |
148
+
149
+ ### Promote / Rollback
150
+
151
+ | Method | Description |
152
+ |---|---|
153
+ | `promote(slug, version, label)` | Assign label to a version |
154
+ | `rollback(slug, label)` | Rollback label to previous version |
155
+
156
+ ### Exports
157
+
158
+ | Method | Description |
159
+ |---|---|
160
+ | `list_exports()` | List exports |
161
+ | `create_export(name, ...)` | Create an export |
162
+ | `get_export(export_id)` | Get export details |
163
+ | `delete_export(export_id)` | Delete an export |
164
+
165
+ ### System
166
+
167
+ | Method | Description |
168
+ |---|---|
169
+ | `health_check()` | Check API health |
170
+
171
+ ## License
172
+
173
+ MIT — Bright Wing Solutions LLC
@@ -0,0 +1,56 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "configpig"
7
+ version = "0.1.0"
8
+ description = "Python SDK for ConfigPig — managed config file registry for teams"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "Bright Wing Solutions LLC", email = "support@brightwingsolutions.com" },
14
+ ]
15
+ keywords = ["config", "configuration", "json", "yaml", "toml", "sdk"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Software Development :: Libraries",
26
+ ]
27
+ dependencies = [
28
+ "httpx>=0.24",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://configpig.com"
33
+ Repository = "https://github.com/Brightwing-Systems-LLC/configpig-python"
34
+ Documentation = "https://configpig.com/docs/sdks/python"
35
+ "Bug Tracker" = "https://github.com/Brightwing-Systems-LLC/configpig-python/issues"
36
+ Changelog = "https://github.com/Brightwing-Systems-LLC/configpig-python/blob/main/CHANGELOG.md"
37
+
38
+ [tool.hatch.build.targets.wheel]
39
+ packages = ["src/configpig"]
40
+
41
+ [tool.bumpversion]
42
+ current_version = "0.1.0"
43
+ commit = true
44
+ tag = true
45
+ allow_dirty = true
46
+
47
+ [[tool.bumpversion.files]]
48
+ filename = "pyproject.toml"
49
+
50
+ [[tool.bumpversion.files]]
51
+ filename = "src/configpig/__init__.py"
52
+
53
+ [dependency-groups]
54
+ dev = [
55
+ "bump-my-version>=1.2.7",
56
+ ]
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+ # Usage: ./scripts/bump_version.sh [patch|minor|major]
3
+
4
+ set -e
5
+
6
+ VERSION_TYPE=${1:-patch}
7
+
8
+ if [[ ! "$VERSION_TYPE" =~ ^(patch|minor|major)$ ]]; then
9
+ echo "Error: Version type must be 'patch', 'minor', or 'major'"
10
+ echo "Usage: $0 [patch|minor|major]"
11
+ exit 1
12
+ fi
13
+
14
+ echo "Bumping $VERSION_TYPE version..."
15
+
16
+ source .venv/bin/activate
17
+ bump-my-version bump "$VERSION_TYPE"
18
+
19
+ echo "Version bump completed successfully!"
20
+ echo "Don't forget to:"
21
+ echo "1. Update CHANGELOG.md"
22
+ echo "2. git add CHANGELOG.md && git commit -m 'Update changelog for version'"
23
+ echo "3. git push origin main && git push origin --tags"
@@ -0,0 +1,34 @@
1
+ """ConfigPig — Python SDK for managing configs via API."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ from .client import Client
6
+ from .drop_in import configure, get_config
7
+ from .types import (
8
+ APIResponse,
9
+ Config,
10
+ ConfigLabel,
11
+ ConfigSummary,
12
+ ConfigVersion,
13
+ Export,
14
+ FetchResult,
15
+ HealthCheck,
16
+ LabelInfo,
17
+ TeamLabel,
18
+ )
19
+
20
+ __all__ = [
21
+ "Client",
22
+ "configure",
23
+ "get_config",
24
+ "APIResponse",
25
+ "Config",
26
+ "ConfigLabel",
27
+ "ConfigSummary",
28
+ "ConfigVersion",
29
+ "Export",
30
+ "FetchResult",
31
+ "HealthCheck",
32
+ "LabelInfo",
33
+ "TeamLabel",
34
+ ]
@@ -0,0 +1,103 @@
1
+ """HTTP transport layer with retry, logging, and connection pooling."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ import time
8
+
9
+ logger = logging.getLogger("configpig")
10
+
11
+ _MAX_RETRIES = 2
12
+ _INITIAL_BACKOFF = 0.5 # seconds; doubles each retry
13
+ _USER_AGENT = "ConfigPig-Python/0.1.0"
14
+ _LOG_PREFIX = "CONFIGPIG_UNSENT_REQUEST"
15
+
16
+ _client = None
17
+
18
+
19
+ def _get_client():
20
+ import httpx
21
+
22
+ global _client
23
+ if _client is None or _client.is_closed:
24
+ _client = httpx.Client(
25
+ timeout=httpx.Timeout(connect=5.0, read=15.0, write=10.0, pool=5.0),
26
+ headers={"User-Agent": _USER_AGENT},
27
+ )
28
+ return _client
29
+
30
+
31
+ def _is_retryable(status_code: int) -> bool:
32
+ return status_code in (429, 500, 502, 503, 504)
33
+
34
+
35
+ def _log_unsent(method: str, url: str, reason: str, body: dict | None = None) -> None:
36
+ logger.warning(
37
+ "%s method=%s url=%s reason=%s payload=%s",
38
+ _LOG_PREFIX,
39
+ method,
40
+ url,
41
+ reason,
42
+ json.dumps(body, separators=(",", ":")) if body else "null",
43
+ )
44
+
45
+
46
+ def request(
47
+ method: str,
48
+ url: str,
49
+ *,
50
+ headers: dict | None = None,
51
+ json_body: dict | None = None,
52
+ params: dict | None = None,
53
+ ) -> tuple[int, dict | None]:
54
+ """Make an HTTP request with retry on transient failures.
55
+
56
+ Returns (status_code, parsed_json_or_None).
57
+ """
58
+ import httpx
59
+
60
+ client = _get_client()
61
+ last_err = None
62
+
63
+ for attempt in range(_MAX_RETRIES + 1):
64
+ try:
65
+ resp = client.request(
66
+ method,
67
+ url,
68
+ headers=headers,
69
+ json=json_body,
70
+ params=params,
71
+ )
72
+
73
+ if _is_retryable(resp.status_code) and attempt < _MAX_RETRIES:
74
+ logger.info(
75
+ "Server returned %d, retrying (%d/%d)",
76
+ resp.status_code,
77
+ attempt + 1,
78
+ _MAX_RETRIES,
79
+ )
80
+ time.sleep(_INITIAL_BACKOFF * (2**attempt))
81
+ continue
82
+
83
+ try:
84
+ data = resp.json()
85
+ except Exception:
86
+ data = None
87
+
88
+ return resp.status_code, data
89
+
90
+ except (httpx.ConnectError, httpx.TimeoutException, httpx.ReadTimeout) as e:
91
+ last_err = e
92
+ if attempt < _MAX_RETRIES:
93
+ logger.info(
94
+ "Request failed (%s), retrying (%d/%d)",
95
+ e,
96
+ attempt + 1,
97
+ _MAX_RETRIES,
98
+ )
99
+ time.sleep(_INITIAL_BACKOFF * (2**attempt))
100
+ continue
101
+
102
+ _log_unsent(method, url, f"unreachable:{last_err}", json_body)
103
+ return 0, None