zbq 0.0.1__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,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:old.reddit.com)"
5
+ ],
6
+ "deny": []
7
+ }
8
+ }
zbq-0.0.1/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .venv
@@ -0,0 +1 @@
1
+ 3.11
zbq-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,528 @@
1
+ Metadata-Version: 2.4
2
+ Name: zbq
3
+ Version: 0.0.1
4
+ Summary: A lightweight, wrapper around Google Cloud BigQuery with Polars integration. Simplifies querying and data ingestion with a unified interface, supporting read, write, insert, and delete operations on BigQuery tables.
5
+ Author-email: FrocketGaming <frocketgames@gmail.com>
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: google-cloud-bigquery-storage>=2.32.0
8
+ Requires-Dist: google-cloud-bigquery>=3.34.0
9
+ Requires-Dist: google-cloud-storage>=3.1.1
10
+ Requires-Dist: loguru>=0.7.0
11
+ Requires-Dist: polars>=1.31.0
12
+ Requires-Dist: pyarrow>=21.0.0
13
+ Requires-Dist: tqdm>=4.67.1
14
+ Description-Content-Type: text/markdown
15
+
16
+ # zbq
17
+
18
+ A lightweight, enhanced wrapper around Google Cloud BigQuery and Storage with Polars integration. Simplifies querying and data operations with a unified interface, supporting read, write, insert, delete operations on BigQuery tables, and advanced file upload/download with pattern matching, parallel processing, and comprehensive error handling.
19
+
20
+ ## Features
21
+
22
+ ### BigQuery Operations
23
+ * **Parameter substitution** - Secure `@param_name` syntax with automatic type handling and SQL injection protection
24
+ * **Conditional queries** - Optional `@condition` parameters for dynamic WHERE clauses
25
+ * **List parameters** - Support for arrays/lists in IN clauses and BigQuery arrays
26
+ * **Dry run mode** - Preview final queries with parameter substitution before execution
27
+ * Transparent BigQuery client initialization with automatic project and credentials detection
28
+ * Use Polars DataFrames seamlessly for input/output
29
+ * Unified methods for CRUD operations with SQL and DataFrame inputs
30
+ * Supports table creation, overwrite warnings, and write mode control
31
+ * Context manager support for client lifecycle management
32
+ * Enhanced error handling with custom exceptions and retry logic
33
+
34
+ ### Storage Operations
35
+ * **Advanced pattern matching** - Multiple include/exclude patterns, regex support, case-insensitive matching
36
+ * **Parallel uploads/downloads** - Configurable thread pool for better performance
37
+ * **Built-in progress bars** - Automatic visual progress tracking with tqdm
38
+ * **Progress tracking** - Built-in callbacks and detailed operation statistics
39
+ * **Dry-run mode** - Preview operations without executing
40
+ * **Retry logic** - Automatic retry with exponential backoff for failed operations
41
+ * **Comprehensive logging** - Structured logging with configurable levels
42
+ * **Operation results** - Detailed statistics including file counts, bytes transferred, duration, and errors
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pip install zbq
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ ### BigQuery Operations
53
+
54
+ ```python
55
+ from zbq import zclient
56
+
57
+ # Simple query
58
+ df = zclient.read("SELECT * FROM `project.dataset.table` LIMIT 1000")
59
+ print(df)
60
+
61
+ # Parameterized query (recommended)
62
+ df = zclient.read(
63
+ "SELECT * FROM @table_name WHERE region = @region LIMIT @limit",
64
+ parameters={
65
+ "table_name": "project.dataset.table",
66
+ "region": "US",
67
+ "limit": 1000
68
+ }
69
+ )
70
+
71
+ # Write DataFrame to BigQuery
72
+ result = zclient.write(
73
+ df=df,
74
+ full_table_path="project.dataset.new_table",
75
+ write_type="truncate", # or "append"
76
+ warning=True
77
+ )
78
+
79
+ # CRUD operations with parameters
80
+ zclient.insert(
81
+ "INSERT INTO @table_name (col1, col2) VALUES (@val1, @val2)",
82
+ parameters={"table_name": "project.dataset.table", "val1": "data", "val2": 123}
83
+ )
84
+ zclient.update(
85
+ "UPDATE @table_name SET col = @new_value WHERE id = @target_id",
86
+ parameters={"table_name": "project.dataset.table", "new_value": "updated", "target_id": 1}
87
+ )
88
+ zclient.delete(
89
+ "DELETE FROM @table_name WHERE id IN (@ids)",
90
+ parameters={"table_name": "project.dataset.table", "ids": [1, 2, 3]}
91
+ )
92
+
93
+ # Context manager support - automatic cleanup
94
+ with zclient as client:
95
+ # Test query with dry_run first
96
+ client.read(
97
+ "SELECT * FROM @table WHERE date >= @start_date",
98
+ parameters={"table": "project.dataset.table1", "start_date": "2024-01-01"},
99
+ dry_run=True
100
+ )
101
+
102
+ # Execute actual queries
103
+ df1 = client.read(
104
+ "SELECT * FROM @table WHERE date >= @start_date",
105
+ parameters={"table": "project.dataset.table1", "start_date": "2024-01-01"}
106
+ )
107
+ df2 = client.read("SELECT * FROM table2")
108
+ result = client.write(df1, "project.dataset.output_table")
109
+ # Client automatically cleaned up after context
110
+ ```
111
+
112
+ ### Parameter Substitution & Advanced Features
113
+
114
+ #### Parameter Substitution
115
+ zbq supports parameterized queries using `@param_name` syntax for secure and flexible SQL execution:
116
+
117
+ ```python
118
+ from zbq import zclient
119
+
120
+ # Basic parameter substitution
121
+ df = zclient.read(
122
+ """SELECT * FROM `project.dataset.table`
123
+ WHERE region = @region AND status = @status""",
124
+ parameters={
125
+ "region": "US", # Strings are automatically quoted
126
+ "status": "active"
127
+ }
128
+ )
129
+
130
+ # Multiple data types
131
+ df = zclient.read(
132
+ """SELECT * FROM hotels
133
+ WHERE hotel_id = @id
134
+ AND price > @min_price
135
+ AND is_available = @available
136
+ AND last_updated > @date""",
137
+ parameters={
138
+ "id": 123, # Numbers: no quotes
139
+ "min_price": 99.99, # Floats: no quotes
140
+ "available": True, # Booleans: TRUE/FALSE
141
+ "date": None # None: NULL
142
+ }
143
+ )
144
+
145
+ # Table identifiers (automatic backticks)
146
+ df = zclient.read(
147
+ "SELECT * FROM @table_name WHERE region = @region",
148
+ parameters={
149
+ "table_name": "project.dataset.hotels", # Becomes `project.dataset.hotels`
150
+ "region": "US" # Becomes 'US'
151
+ }
152
+ )
153
+ ```
154
+
155
+ #### List Parameters for IN Clauses
156
+ Use lists and tuples for IN clauses and array operations:
157
+
158
+ ```python
159
+ # IN clause with list
160
+ df = zclient.read(
161
+ "SELECT * FROM hotels WHERE hotel_code IN (@codes)",
162
+ parameters={
163
+ "codes": ['ABC', 'DEF', 'GHI'] # Becomes ('ABC', 'DEF', 'GHI')
164
+ }
165
+ )
166
+
167
+ # Numeric lists
168
+ df = zclient.read(
169
+ "SELECT * FROM bookings WHERE booking_id IN (@ids)",
170
+ parameters={
171
+ "ids": [123, 456, 789] # Becomes (123, 456, 789)
172
+ }
173
+ )
174
+
175
+ # Array syntax (BigQuery)
176
+ df = zclient.read(
177
+ "SELECT * FROM table WHERE col = ANY([@values])",
178
+ parameters={
179
+ "values": ['a', 'b', 'c'] # Becomes ['a', 'b', 'c']
180
+ }
181
+ )
182
+
183
+ # Mixed data types in lists
184
+ df = zclient.read(
185
+ "SELECT * FROM table WHERE col IN (@mixed)",
186
+ parameters={
187
+ "mixed": ['text', 123, None, True] # Becomes ('text', 123, NULL, TRUE)
188
+ }
189
+ )
190
+ ```
191
+
192
+ #### Conditional Parameters
193
+ Use `@condition` parameters for optional WHERE clauses:
194
+
195
+ ```python
196
+ # With condition
197
+ df = zclient.read(
198
+ """SELECT * FROM hotels
199
+ WHERE region = @region
200
+ @condition""",
201
+ parameters={
202
+ "region": "US",
203
+ "condition": "AND hotel_rating >= 4" # Raw SQL inserted
204
+ }
205
+ )
206
+ # Result: WHERE region = 'US' AND hotel_rating >= 4
207
+
208
+ # Without condition (automatically removed)
209
+ df = zclient.read(
210
+ """SELECT * FROM hotels
211
+ WHERE region = @region
212
+ @condition""",
213
+ parameters={
214
+ "region": "US"
215
+ # No condition parameter - @condition is removed entirely
216
+ }
217
+ )
218
+ # Result: WHERE region = 'US'
219
+ ```
220
+
221
+ #### Dry Run Mode
222
+ Preview your final query with parameter substitution without executing:
223
+
224
+ ```python
225
+ # Preview query with dry_run
226
+ result = zclient.read(
227
+ """SELECT * FROM `project.dataset.hotels`
228
+ WHERE region = @region
229
+ AND hotel_codes IN (@codes)
230
+ @condition""",
231
+ parameters={
232
+ "region": "US",
233
+ "codes": ['ABC', 'DEF'],
234
+ "condition": "AND rating >= 4"
235
+ },
236
+ dry_run=True # Prints query, returns None
237
+ )
238
+
239
+ # Output:
240
+ # DRY RUN - Query that would be executed:
241
+ # --------------------------------------------------
242
+ # SELECT * FROM `project.dataset.hotels`
243
+ # WHERE region = 'US'
244
+ # AND hotel_codes IN ('ABC', 'DEF')
245
+ # AND rating >= 4
246
+ # --------------------------------------------------
247
+ ```
248
+
249
+ ### Storage Operations
250
+
251
+ #### Basic Upload/Download
252
+ ```python
253
+ from zbq import zstorage
254
+
255
+ # Simple upload with pattern - files go to bucket root
256
+ result = zstorage.upload(
257
+ local_dir="./data",
258
+ bucket_path="my-bucket",
259
+ include_patterns="*.xlsx" # Upload only Excel files
260
+ )
261
+
262
+ # Upload to specific folder in bucket
263
+ result = zstorage.upload(
264
+ local_dir="./data",
265
+ bucket_path="my-bucket/reports/2024", # Upload to reports/2024/ folder
266
+ include_patterns="*.xlsx"
267
+ )
268
+
269
+ print(f"Uploaded {result.uploaded_files}/{result.total_files} files")
270
+ print(f"Total size: {result.total_bytes:,} bytes in {result.duration:.2f}s")
271
+
272
+ # Context manager support for batch operations
273
+ with zstorage as storage:
274
+ # Upload multiple directories in sequence
275
+ result1 = storage.upload("./data1", "my-bucket/folder1", include_patterns="*.csv")
276
+ result2 = storage.upload("./data2", "my-bucket/folder2", include_patterns="*.json")
277
+ result3 = storage.download("my-bucket/archive", "./downloads", include_patterns="*.parquet")
278
+ # Storage client automatically cleaned up
279
+
280
+ # Simple download from bucket root
281
+ result = zstorage.download(
282
+ bucket_path="my-bucket",
283
+ local_dir="./downloads",
284
+ include_patterns="*.csv" # Download only CSV files
285
+ )
286
+
287
+ # Download from specific folder in bucket
288
+ result = zstorage.download(
289
+ bucket_path="my-bucket/data/exports", # Download from data/exports/ folder
290
+ local_dir="./downloads",
291
+ include_patterns="*.csv"
292
+ )
293
+ ```
294
+
295
+ #### Advanced Pattern Matching
296
+ ```python
297
+ # Multiple include patterns
298
+ result = zstorage.upload(
299
+ local_dir="./reports",
300
+ bucket_path="my-bucket/reports",
301
+ include_patterns=["*.xlsx", "*.csv", "*.json"], # Multiple file types
302
+ exclude_patterns=["temp_*", "*_backup.*"], # Exclude temporary/backup files
303
+ case_sensitive=False # Case-insensitive matching
304
+ )
305
+
306
+ # Regex patterns for complex matching
307
+ result = zstorage.upload(
308
+ local_dir="./logs",
309
+ bucket_path="my-bucket/logs",
310
+ include_patterns=r"log_\d{4}-\d{2}-\d{2}\.txt", # Match log_YYYY-MM-DD.txt
311
+ use_regex=True
312
+ )
313
+ ```
314
+
315
+ #### Parallel Processing & Progress Tracking
316
+ ```python
317
+ # Automatic progress bar (shows for multiple files)
318
+ result = zstorage.upload(
319
+ local_dir="./large-dataset",
320
+ bucket_path="my-bucket",
321
+ include_patterns="*.xlsx",
322
+ parallel=True, # Enable parallel uploads
323
+ max_retries=5 # Retry failed uploads
324
+ )
325
+ # Shows: "Uploading: 75%|███████▌ | 15/20 [00:30<00:10, 0.5files/s]"
326
+
327
+ # Custom progress callback (optional)
328
+ def progress_callback(completed, total):
329
+ percentage = (completed / total) * 100
330
+ print(f"Custom progress: {completed}/{total} files ({percentage:.1f}%)")
331
+
332
+ result = zstorage.upload(
333
+ local_dir="./large-dataset",
334
+ bucket_path="my-bucket",
335
+ progress_callback=progress_callback,
336
+ show_progress=False # Disable built-in progress bar
337
+ )
338
+
339
+ # Handle results
340
+ if result.failed_files > 0:
341
+ print(f"WARNING: {result.failed_files} files failed to upload:")
342
+ for error in result.errors:
343
+ print(f" - {error}")
344
+
345
+ print(f"Successfully uploaded {result.uploaded_files} files")
346
+ print(f"Total: {result.total_bytes:,} bytes in {result.duration:.2f}s")
347
+ ```
348
+
349
+ #### Dry Run & Preview
350
+ ```python
351
+ # Preview what would be uploaded without actually uploading
352
+ result = zstorage.upload(
353
+ local_dir="./data",
354
+ bucket_path="my-bucket",
355
+ include_patterns="*.parquet",
356
+ dry_run=True # Preview only
357
+ )
358
+
359
+ print(f"Would upload {result.total_files} files ({result.total_bytes:,} bytes)")
360
+
361
+ # Progress bar control
362
+ result = zstorage.upload(
363
+ local_dir="./data",
364
+ bucket_path="my-bucket",
365
+ include_patterns="*.xlsx",
366
+ show_progress=True # Force show progress bar even for single files
367
+ )
368
+
369
+ result = zstorage.upload(
370
+ local_dir="./data",
371
+ bucket_path="my-bucket",
372
+ include_patterns="*.xlsx",
373
+ show_progress=False # Never show progress bar
374
+ )
375
+ ```
376
+
377
+ #### Advanced Download with Filtering
378
+ ```python
379
+ # Download with path filtering and patterns
380
+ result = zstorage.download(
381
+ bucket_path="my-data-bucket/reports/2024", # Only files under this path
382
+ local_dir="./downloaded-reports",
383
+ include_patterns=["*.xlsx", "*.pdf"],
384
+ exclude_patterns="*_draft.*", # Skip draft files
385
+ parallel=True,
386
+ max_results=500 # Limit number of files to list
387
+ )
388
+ ```
389
+
390
+ ## Advanced Configuration
391
+
392
+ ### Custom Logging
393
+ ```python
394
+ from zbq import setup_logging, StorageHandler, BigQueryHandler
395
+
396
+ # Configure logging
397
+ logger = setup_logging("DEBUG") # DEBUG, INFO, WARNING, ERROR
398
+
399
+ # Create handlers with custom settings
400
+ storage = StorageHandler(
401
+ project_id="my-project",
402
+ log_level="INFO",
403
+ max_workers=8 # More parallel workers
404
+ )
405
+
406
+ bq = BigQueryHandler(
407
+ project_id="my-project",
408
+ default_timeout=600, # 10 minute timeout
409
+ log_level="DEBUG"
410
+ )
411
+ ```
412
+
413
+ ### Error Handling
414
+ ```python
415
+ from zbq import ZbqAuthenticationError, ZbqOperationError, ZbqConfigurationError
416
+
417
+ try:
418
+ result = zstorage.upload("./data", "my-bucket", include_patterns="*.csv")
419
+ except ZbqAuthenticationError:
420
+ print("Authentication failed. Run: gcloud auth application-default login")
421
+ except ZbqConfigurationError:
422
+ print("Configuration error. Check your project settings.")
423
+ except ZbqOperationError as e:
424
+ print(f"Operation failed: {e}")
425
+ ```
426
+
427
+ ### Working with Results
428
+ ```python
429
+ from zbq import UploadResult, DownloadResult
430
+
431
+ # Upload with detailed result handling
432
+ result: UploadResult = zstorage.upload(
433
+ local_dir="./data",
434
+ bucket_name="my-bucket",
435
+ include_patterns=["*.json", "*.csv"]
436
+ )
437
+
438
+ # Detailed statistics
439
+ print(f"""
440
+ Upload Summary:
441
+ Total files: {result.total_files}
442
+ Uploaded: {result.uploaded_files}
443
+ Skipped: {result.skipped_files}
444
+ Failed: {result.failed_files}
445
+ Total size: {result.total_bytes:,} bytes
446
+ Duration: {result.duration:.2f} seconds
447
+ """)
448
+
449
+ # Handle errors
450
+ if result.errors:
451
+ print("Errors encountered:")
452
+ for error in result.errors[:5]: # Show first 5 errors
453
+ print(f" - {error}")
454
+ if len(result.errors) > 5:
455
+ print(f" ... and {len(result.errors) - 5} more errors")
456
+ ```
457
+
458
+ ## Pattern Matching Guide
459
+
460
+ ### Glob Patterns (Default)
461
+ - `*.xlsx` - All Excel files
462
+ - `data_*.csv` - CSV files starting with "data_"
463
+ - `report_????_??.pdf` - Reports with specific naming pattern
464
+ - `**/*.json` - All JSON files in subdirectories (recursive)
465
+ - `[!.]*.txt` - Text files not starting with dot
466
+
467
+ ### Regex Patterns
468
+ ```python
469
+ # Enable regex with use_regex=True
470
+ zstorage.upload(
471
+ local_dir="./logs",
472
+ bucket_name="my-bucket",
473
+ include_patterns=[
474
+ r"access_log_\d{4}-\d{2}-\d{2}\.log", # access_log_2024-01-01.log
475
+ r"error_log_\d{8}\.log" # error_log_20240101.log
476
+ ],
477
+ use_regex=True
478
+ )
479
+ ```
480
+
481
+ ### Complex Filtering
482
+ ```python
483
+ # Include multiple types, exclude temp files
484
+ zstorage.upload(
485
+ local_dir="./workspace",
486
+ bucket_name="my-bucket",
487
+ include_patterns=["*.py", "*.json", "*.md", "*.yml"],
488
+ exclude_patterns=["__pycache__/*", "*.pyc", "temp_*", ".git/*"],
489
+ case_sensitive=False
490
+ )
491
+ ```
492
+
493
+ ## Authentication & Setup
494
+
495
+ 1. **Install Google Cloud SDK**:
496
+ ```bash
497
+ gcloud auth application-default login
498
+ gcloud config set project YOUR_PROJECT_ID
499
+ ```
500
+
501
+ 2. **Or set environment variables**:
502
+ ```bash
503
+ export GOOGLE_APPLICATION_CREDENTIALS="path/to/service-account.json"
504
+ export GOOGLE_CLOUD_PROJECT="your-project-id"
505
+ ```
506
+
507
+ ## Requirements
508
+
509
+ - Python ≥ 3.11
510
+ - Google Cloud project with BigQuery and/or Storage APIs enabled
511
+ - Appropriate IAM permissions for your operations
512
+
513
+
514
+ ## Performance Tips
515
+
516
+ 1. **Use parallel processing** for multiple files: `parallel=True`
517
+ 2. **Adjust thread count** based on your system: `max_workers=8`
518
+ 3. **Use dry-run** to preview large operations first
519
+ 4. **Filter early** with specific patterns to avoid processing unwanted files
520
+ 5. **Monitor progress** with callback functions for long operations
521
+
522
+ ## Contributing
523
+
524
+ Issues and pull requests welcome at the project repository.
525
+
526
+ ## License
527
+
528
+ See LICENSE file for details.