project-toolkit 1.0.2__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.
Files changed (39) hide show
  1. project_toolkit-1.0.2/LICENSE +20 -0
  2. project_toolkit-1.0.2/PKG-INFO +419 -0
  3. project_toolkit-1.0.2/README.md +376 -0
  4. project_toolkit-1.0.2/project_toolkit/__init__.py +76 -0
  5. project_toolkit-1.0.2/project_toolkit/cloudflare/__init__.py +0 -0
  6. project_toolkit-1.0.2/project_toolkit/cloudflare/boto3_client.py +115 -0
  7. project_toolkit-1.0.2/project_toolkit/cloudflare/requests_client.py +91 -0
  8. project_toolkit-1.0.2/project_toolkit/google/__init__.py +0 -0
  9. project_toolkit-1.0.2/project_toolkit/google/bigquery.py +216 -0
  10. project_toolkit-1.0.2/project_toolkit/google/drive.py +307 -0
  11. project_toolkit-1.0.2/project_toolkit/google/sheet.py +269 -0
  12. project_toolkit-1.0.2/project_toolkit/project_path.py +203 -0
  13. project_toolkit-1.0.2/project_toolkit/settings/__init__.py +27 -0
  14. project_toolkit-1.0.2/project_toolkit/settings/base.py +204 -0
  15. project_toolkit-1.0.2/project_toolkit/settings/cloudflare.py +30 -0
  16. project_toolkit-1.0.2/project_toolkit/settings/google.py +26 -0
  17. project_toolkit-1.0.2/project_toolkit/settings/path.py +40 -0
  18. project_toolkit-1.0.2/project_toolkit/settings/vault.py +161 -0
  19. project_toolkit-1.0.2/project_toolkit/settings/wan_ip.py +43 -0
  20. project_toolkit-1.0.2/project_toolkit/wan_ip/__init__.py +5 -0
  21. project_toolkit-1.0.2/project_toolkit/wan_ip/tracker.py +174 -0
  22. project_toolkit-1.0.2/project_toolkit.egg-info/PKG-INFO +419 -0
  23. project_toolkit-1.0.2/project_toolkit.egg-info/SOURCES.txt +37 -0
  24. project_toolkit-1.0.2/project_toolkit.egg-info/dependency_links.txt +1 -0
  25. project_toolkit-1.0.2/project_toolkit.egg-info/requires.txt +30 -0
  26. project_toolkit-1.0.2/project_toolkit.egg-info/top_level.txt +1 -0
  27. project_toolkit-1.0.2/pyproject.toml +85 -0
  28. project_toolkit-1.0.2/setup.cfg +4 -0
  29. project_toolkit-1.0.2/tests/test_auto_settings.py +81 -0
  30. project_toolkit-1.0.2/tests/test_bigquery.py +160 -0
  31. project_toolkit-1.0.2/tests/test_cloudflare.py +123 -0
  32. project_toolkit-1.0.2/tests/test_google.py +213 -0
  33. project_toolkit-1.0.2/tests/test_path.py +139 -0
  34. project_toolkit-1.0.2/tests/test_settings.py +232 -0
  35. project_toolkit-1.0.2/tests/test_settings_cloudflare.py +53 -0
  36. project_toolkit-1.0.2/tests/test_settings_google.py +74 -0
  37. project_toolkit-1.0.2/tests/test_settings_path.py +133 -0
  38. project_toolkit-1.0.2/tests/test_vault.py +107 -0
  39. project_toolkit-1.0.2/tests/test_wan_ip.py +198 -0
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 m3air
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 included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,419 @@
1
+ Metadata-Version: 2.4
2
+ Name: project-toolkit
3
+ Version: 1.0.2
4
+ Summary: A modular toolkit for managing project paths, cloud services, and configuration in Python projects
5
+ Author: guocity
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/guocity/project-toolkit
8
+ Project-URL: Repository, https://github.com/guocity/project-toolkit
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: pydantic>=2.0
19
+ Requires-Dist: pydantic-settings>=2.0
20
+ Provides-Extra: google
21
+ Requires-Dist: gspread; extra == "google"
22
+ Requires-Dist: gspread-dataframe; extra == "google"
23
+ Requires-Dist: ratelimit; extra == "google"
24
+ Requires-Dist: google-api-python-client; extra == "google"
25
+ Requires-Dist: google-auth; extra == "google"
26
+ Requires-Dist: google-cloud-bigquery; extra == "google"
27
+ Requires-Dist: pandas; extra == "google"
28
+ Requires-Dist: pandas_gbq; extra == "google"
29
+ Provides-Extra: cloudflare
30
+ Requires-Dist: boto3; extra == "cloudflare"
31
+ Requires-Dist: requests; extra == "cloudflare"
32
+ Provides-Extra: vault
33
+ Requires-Dist: hvac; extra == "vault"
34
+ Provides-Extra: wan-ip
35
+ Requires-Dist: requests; extra == "wan-ip"
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=7.0; extra == "dev"
38
+ Requires-Dist: pytest-cov; extra == "dev"
39
+ Requires-Dist: requests; extra == "dev"
40
+ Provides-Extra: all
41
+ Requires-Dist: project-toolkit[cloudflare,dev,google,vault,wan-ip]; extra == "all"
42
+ Dynamic: license-file
43
+
44
+ # project-toolkit
45
+
46
+ A modular Python toolkit for managing project paths, cloud services (Google Cloud, Cloudflare R2), HashiCorp Vault, and configuration — powered by **pydantic-settings**.
47
+
48
+ ## Features
49
+
50
+ - ⚙️ **Auto-Initialized Settings** — Just import and use, no setup needed
51
+ - 🔑 **Dynamic Env Access** — Read any env var or `.env` value with `settings.env("KEY")`
52
+ - 🔐 **Vault Integration** — Read secrets like `vault kv get` / `vault kv get -field=`
53
+ - 📁 **Project Path Management** — Create and navigate project directory structures
54
+ - ☁️ **Cloudflare R2** — Upload/download files with `boto3` and `requests` clients
55
+ - 📊 **Google Services** — BigQuery, Drive, and Sheets integrations
56
+ - 🌐 **WAN IP Tracker** — Detect and log public IP address changes
57
+
58
+ ## Installation
59
+
60
+ ```bash
61
+ # Core only
62
+ pip install project-toolkit
63
+
64
+ # With specific extras
65
+ pip install "project-toolkit[google]"
66
+ pip install "project-toolkit[cloudflare]"
67
+ pip install "project-toolkit[vault]"
68
+ pip install "project-toolkit[wan-ip]"
69
+
70
+ # Everything
71
+ pip install "project-toolkit[all]"
72
+ ```
73
+
74
+ ---
75
+
76
+ ## API Usage Guide
77
+
78
+ ### 1. Auto-Initialized Settings (No Setup Needed)
79
+
80
+ Settings auto-initialize on first use — no boilerplate required:
81
+
82
+ ```python
83
+ from project_toolkit import settings
84
+
85
+ # Access any module setting directly
86
+ print(settings.cloudflare.r2_access_token)
87
+ print(settings.google.google_service_account_json)
88
+ print(settings.vault.is_configured)
89
+ ```
90
+
91
+ **Environment detection** is automatic:
92
+ - If `AIRFLOW_HOME` is set → loads `config/.env.airflow`
93
+ - Otherwise → loads `config/.env.dev`
94
+
95
+ ---
96
+
97
+ ### 2. Dynamic Environment Variable Access
98
+
99
+ Read **any** env var or `.env` value — no need to pre-define it in code:
100
+
101
+ ```python
102
+ from project_toolkit import settings
103
+
104
+ # Read API keys (from env vars or .env file)
105
+ openai_key = settings.env("OPENAI_API_KEY")
106
+ gemini_key = settings.env("GEMINI_API_KEY")
107
+ claude_key = settings.env("CLAUDE_API_KEY")
108
+
109
+ # With a default fallback
110
+ db_host = settings.env("DATABASE_HOST", "localhost")
111
+ debug = settings.env("DEBUG_MODE", "false")
112
+ ```
113
+
114
+ **Priority**: Environment variable > `.env` file value > default
115
+
116
+ Your `config/.env.dev` can contain any key:
117
+
118
+ ```env
119
+ OPENAI_API_KEY=sk-xxxxx
120
+ GEMINI_API_KEY=AIza-xxxxx
121
+ CLAUDE_API_KEY=sk-ant-xxxxx
122
+ DATABASE_HOST=db.example.com
123
+ ```
124
+
125
+ ---
126
+
127
+ ### 3. HashiCorp Vault
128
+
129
+ Read secrets from Vault — same env vars as the `vault` CLI.
130
+
131
+ **Setup** — Add to `config/.env.dev`:
132
+
133
+ ```env
134
+ VAULT_ADDR=http://192.168.12.2:8200
135
+ VAULT_TOKEN=hvs.your-vault-token
136
+ ```
137
+
138
+ **Read all fields** (like `vault kv get secret/my-credentials`):
139
+
140
+ ```python
141
+ from project_toolkit import settings
142
+
143
+ secret = settings.vault.read_secret("secret/my-credentials")
144
+ print(secret)
145
+ # {'username': 'admin', 'password': 'secret-password-123'}
146
+ ```
147
+
148
+ **Read a single field** (like `vault kv get -field=OPENAI_API_KEY secret/api`):
149
+
150
+ ```python
151
+ from project_toolkit import settings
152
+
153
+ # Single field reads
154
+ api_key = settings.vault.read_field("secret/api", "OPENAI_API_KEY")
155
+ password = settings.vault.read_field("secret/my-credentials", "password")
156
+
157
+ # With fallback default
158
+ db_pass = settings.vault.read_field("secret/db", "password", "default-pw")
159
+ ```
160
+
161
+ **Use the hvac client directly** for advanced operations:
162
+
163
+ ```python
164
+ from project_toolkit import settings
165
+
166
+ client = settings.vault.get_vault_client()
167
+ if client:
168
+ # Write a secret
169
+ client.secrets.kv.v2.create_or_update_secret(
170
+ path="my-new-secret",
171
+ secret={"api_key": "abc123"},
172
+ )
173
+
174
+ # List secrets
175
+ secrets = client.secrets.kv.v2.list_secrets(path="", mount_point="secret")
176
+ print(secrets["data"]["keys"])
177
+ ```
178
+
179
+ ---
180
+
181
+ ### 4. Manual Settings Override
182
+
183
+ For testing or custom configurations, construct `AppSettings` directly:
184
+
185
+ ```python
186
+ from project_toolkit.settings.base import AppSettings
187
+ from project_toolkit.settings.cloudflare import CloudflareSettings
188
+ from project_toolkit.settings.google import GoogleSettings
189
+ from project_toolkit.settings.path import PathSettings
190
+ from project_toolkit.settings.vault import VaultSettings
191
+ from project_toolkit.settings.wan_ip import WanIpSettings
192
+
193
+ custom_settings = AppSettings(
194
+ path_config=PathSettings(
195
+ data_dir="/custom/data",
196
+ project_name="my-project",
197
+ ),
198
+ cloudflare=CloudflareSettings(
199
+ r2_access_token="custom-token",
200
+ r2_account_id="custom-id",
201
+ ),
202
+ google=GoogleSettings(),
203
+ vault=VaultSettings(),
204
+ wan_ip=WanIpSettings(),
205
+ )
206
+
207
+ # Use the custom settings
208
+ print(custom_settings.cloudflare.r2_access_token) # "custom-token"
209
+ print(custom_settings.env("R2_ACCESS_TOKEN")) # reads from env/dotenv
210
+ ```
211
+
212
+ To reset the cached auto-initialized singleton:
213
+
214
+ ```python
215
+ from project_toolkit import settings, get_settings
216
+
217
+ # Reset cached settings (e.g., after changing env vars)
218
+ get_settings.cache_clear()
219
+ settings._reset()
220
+
221
+ # Next access will re-initialize
222
+ print(settings.cloudflare.r2_access_token)
223
+ ```
224
+
225
+ ---
226
+
227
+ ### 5. Project Path Management
228
+
229
+ Manage project directory structures with automatic creation:
230
+
231
+ ```python
232
+ from project_toolkit import DataPathConfig
233
+
234
+ # Direct usage
235
+ dpc = DataPathConfig(
236
+ project_name="my-project",
237
+ subproject="etl",
238
+ data_dir="/data",
239
+ )
240
+ print(dpc.data_dir()) # /data
241
+ print(dpc.project_dir()) # /data/my-project
242
+ print(dpc.sub_project_dir()) # /data/my-project/etl
243
+
244
+ # Get a timestamped file path
245
+ filepath = dpc.get_filepath("output", "csv")
246
+ # /data/my-project/etl/output_20260209.csv
247
+ ```
248
+
249
+ **From auto-initialized settings** (recommended):
250
+
251
+ ```python
252
+ from project_toolkit import settings, DataPathConfig
253
+
254
+ dpc = DataPathConfig.from_settings(
255
+ settings.path_config,
256
+ project_name="my-project",
257
+ )
258
+ ```
259
+
260
+ ---
261
+
262
+ ### 6. Cloudflare R2
263
+
264
+ ```python
265
+ from project_toolkit import settings
266
+ from project_toolkit.cloudflare.boto3_client import CloudflareBoto3Client
267
+
268
+ # From auto-initialized settings
269
+ client = CloudflareBoto3Client.from_settings(settings.cloudflare)
270
+
271
+ # Upload a file
272
+ client.upload_file("my-bucket", "file.csv", open("/path/to/file.csv", "rb").read())
273
+
274
+ # List buckets
275
+ buckets = client.list_buckets()
276
+ ```
277
+
278
+ ---
279
+
280
+ ### 7. Google Sheets
281
+
282
+ ```python
283
+ from project_toolkit import settings
284
+ from project_toolkit.google.sheet import GoogleSheetsManager
285
+
286
+ sheets = GoogleSheetsManager.from_settings(settings.google)
287
+
288
+ # Read a sheet as a DataFrame
289
+ df = sheets.sheet_df(sheet_id="your-sheet-id")
290
+ print(df.head())
291
+ ```
292
+
293
+ ---
294
+
295
+ ### 8. Google BigQuery
296
+
297
+ ```python
298
+ from project_toolkit import settings
299
+ from project_toolkit.google.bigquery import BigQueryManager
300
+
301
+ bq = BigQueryManager.from_settings(settings.google)
302
+
303
+ # Run a query
304
+ df = bq.query_table("SELECT * FROM my_dataset.my_table LIMIT 10")
305
+ ```
306
+
307
+ ---
308
+
309
+ ### 9. Google Drive
310
+
311
+ ```python
312
+ from project_toolkit import settings
313
+ from project_toolkit.google.drive import GoogleDriveManager
314
+
315
+ drive = GoogleDriveManager.from_settings(settings.google)
316
+
317
+ # List files in a folder
318
+ files = drive.list_files(folder_id="your-folder-id")
319
+ ```
320
+
321
+ ---
322
+
323
+ ### 10. WAN IP Tracker
324
+
325
+ ```python
326
+ from project_toolkit.wan_ip import WanIpTracker
327
+
328
+ tracker = WanIpTracker(log_dir="/tmp/ip-logs")
329
+
330
+ # Get current IPs (includes datetime)
331
+ print(tracker)
332
+ # Date: 2026-02-09 22:31:45
333
+ # IPv4: 1.2.3.4
334
+ # IPv6: 2001:db8::1
335
+
336
+ # Log only if changed
337
+ entry = tracker.log_if_changed()
338
+ ```
339
+
340
+ ---
341
+
342
+ ## Configuration
343
+
344
+ ### Environment Variables
345
+
346
+ | Variable | Module | Description |
347
+ |---|---|---|
348
+ | `R2_ACCESS_TOKEN` | Cloudflare | Cloudflare R2 API token |
349
+ | `R2_ACCOUNT_ID` | Cloudflare | Cloudflare account ID |
350
+ | `R2_DOMAIN` | Cloudflare | R2 custom domain URL |
351
+ | `GOOGLE_SERVICE_ACCOUNT_JSON` | Google | Path to service account JSON |
352
+ | `TEST_GOOGLE_SHEET_ID` | Google | Test sheet ID (dev only) |
353
+ | `VAULT_ADDR` | Vault | Vault server address (e.g., `http://192.168.12.2:8200`) |
354
+ | `VAULT_TOKEN` | Vault | Vault authentication token |
355
+ | `WAN_IP_LOG_DIR` | WAN IP | Directory for IP change logs |
356
+ | `AIRFLOW_HOME` | Core | Auto-detected for env switching |
357
+
358
+ > **Any key** in your `.env` file or environment can be read dynamically
359
+ > via `settings.env("KEY")` — no need to define it in Python code.
360
+
361
+ ### `.env` Files
362
+
363
+ Place config files in the `config/` directory:
364
+
365
+ ```
366
+ config/
367
+ ├── .env.dev # Local development (loaded when no AIRFLOW_HOME)
368
+ └── .env.airflow # Airflow production (loaded when AIRFLOW_HOME is set)
369
+ ```
370
+
371
+ ### Priority Order
372
+
373
+ Environment variables > `.env` files > Vault secrets > defaults
374
+
375
+ ---
376
+
377
+ ## Development
378
+
379
+ ```bash
380
+ # Install with all dependencies
381
+ pip install -e ".[all]"
382
+
383
+ # Run tests
384
+ pytest
385
+
386
+ # Run specific tests
387
+ pytest tests/test_settings.py -s -vv
388
+ pytest tests/test_vault.py -s -vv
389
+ pytest tests/test_auto_settings.py -s -vv
390
+ ```
391
+
392
+ ## Project Structure
393
+
394
+ ```
395
+ project_toolkit/
396
+ ├── __init__.py # Package exports + auto-initialized settings singleton
397
+ ├── project_path.py # Project path management (DataPathConfig)
398
+ ├── settings/
399
+ │ ├── __init__.py # Settings exports
400
+ │ ├── base.py # AppSettings + get_settings() + env()
401
+ │ ├── path.py # PathSettings
402
+ │ ├── cloudflare.py # CloudflareSettings
403
+ │ ├── google.py # GoogleSettings
404
+ │ ├── vault.py # VaultSettings + read_secret() + read_field()
405
+ │ └── wan_ip.py # WanIpSettings
406
+ ├── cloudflare/
407
+ │ ├── boto3_client.py # R2 via boto3
408
+ │ └── requests_client.py # R2 via requests
409
+ ├── google/
410
+ │ ├── bigquery.py # BigQuery operations
411
+ │ ├── drive.py # Google Drive operations
412
+ │ └── sheet.py # Google Sheets operations
413
+ └── wan_ip/
414
+ └── tracker.py # WAN IP detection + logging
415
+ ```
416
+
417
+ ## License
418
+
419
+ MIT