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.
- zbq-0.0.1/.claude/settings.local.json +8 -0
- zbq-0.0.1/.gitignore +1 -0
- zbq-0.0.1/.python-version +1 -0
- zbq-0.0.1/PKG-INFO +528 -0
- zbq-0.0.1/README.md +513 -0
- zbq-0.0.1/pyproject.toml +23 -0
- zbq-0.0.1/src/zbq/__init__.py +24 -0
- zbq-0.0.1/src/zbq/base.py +139 -0
- zbq-0.0.1/src/zbq/main.py +394 -0
- zbq-0.0.1/src/zbq/storage.py +645 -0
- zbq-0.0.1/uv.lock +491 -0
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.
|