pyplaykit 1.0.0__tar.gz → 1.4.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.
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/PKG-INFO +94 -8
- pyplaykit-1.0.0/pyplaykit.egg-info/PKG-INFO → pyplaykit-1.4.0/README.md +84 -34
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pyplaykit/__init__.py +31 -2
- pyplaykit-1.0.0/README.md → pyplaykit-1.4.0/pyplaykit.egg-info/PKG-INFO +120 -1
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pyplaykit.egg-info/SOURCES.txt +4 -0
- pyplaykit-1.4.0/pyplaykit.egg-info/requires.txt +14 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pyproject.toml +10 -7
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/api_client.py +9 -1
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/data_loader.py +42 -1
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/data_source.py +54 -6
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_client.py +78 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_drivers/db2.py +7 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_drivers/mysql.py +7 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_drivers/oracle.py +7 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_drivers/snowflake.py +7 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_drivers/sqlite.py +8 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_drivers/sqlserver.py +7 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_validation_helper.py +229 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/environment_validator.py +16 -3
- pyplaykit-1.4.0/utils/graphql_client.py +257 -0
- pyplaykit-1.4.0/utils/soap_client.py +260 -0
- pyplaykit-1.4.0/utils/transformation_validator.py +639 -0
- pyplaykit-1.4.0/utils/ui_db_validator.py +442 -0
- pyplaykit-1.0.0/pyplaykit.egg-info/requires.txt +0 -11
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/MANIFEST.in +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/components/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/components/base_component.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/components/data_table.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/components/form_field.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/components/modal.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/config/config.yaml +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/config/environments.yaml +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/config/projects/customs_modernization.yaml +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/config/projects/dispatcho.yaml +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/config/projects/timelyquote.yaml +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/core/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/core/base_page.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/core/base_test.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/core/browser_factory.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/core/playwright_manager.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/dist_pkg/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/dist_pkg/distribution.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/integrations/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/integrations/adapters.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/orchestration/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/orchestration/enforcer.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/orchestration/planner.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pages/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pages/dashboard_page.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pages/login_page.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pages/orangehrm_login_page.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pages/saucedemo_inventory_page.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pages/saucedemo_login_page.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/plugins/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/plugins/api_plugin.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/plugins/base.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/plugins/data_plugin.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/plugins/registry.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/plugins/security_plugin.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pyplaykit/cli.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pyplaykit/plugin.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pyplaykit.egg-info/dependency_links.txt +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pyplaykit.egg-info/entry_points.txt +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/pyplaykit.egg-info/top_level.txt +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/resilience/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/resilience/locator_recovery.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/setup.cfg +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/test_data/api/ecom/auth_payloads.json +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/test_data/api/ecom/product_payloads.json +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/test_data/api_test_data.json +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/test_data/data_validation_test_data.json +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/test_data/login_test_data.json +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/test_data/orangehrm_login_data.json +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/test_data/ui_test_data.json +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/api_test_helper.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/assertion_helper.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/auth_manager.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/data_validator.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_drivers/__init__.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_factory.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_fixtures.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/db_pool.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/file_reader.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/logger.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/observability.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/response_validator.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/screenshot_helper.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/tdm.py +0 -0
- {pyplaykit-1.0.0 → pyplaykit-1.4.0}/utils/yaml_config_reader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyplaykit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Enterprise Python + Playwright automation framework
|
|
5
5
|
Author-email: Shan Konduru <shankonduru@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -20,16 +20,19 @@ Classifier: Operating System :: OS Independent
|
|
|
20
20
|
Requires-Python: >=3.11
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
Requires-Dist: playwright==1.54.0
|
|
23
|
-
Requires-Dist: pytest==
|
|
24
|
-
Requires-Dist: pytest-cov==
|
|
25
|
-
Requires-Dist: pytest-xdist==3.
|
|
26
|
-
Requires-Dist: pytest-rerunfailures==
|
|
27
|
-
Requires-Dist: pytest-html==4.
|
|
28
|
-
Requires-Dist: allure-pytest==2.
|
|
23
|
+
Requires-Dist: pytest==9.1.1
|
|
24
|
+
Requires-Dist: pytest-cov==7.1.0
|
|
25
|
+
Requires-Dist: pytest-xdist==3.8.0
|
|
26
|
+
Requires-Dist: pytest-rerunfailures==16.4
|
|
27
|
+
Requires-Dist: pytest-html==4.2.0
|
|
28
|
+
Requires-Dist: allure-pytest==2.16.0
|
|
29
29
|
Requires-Dist: PyYAML==6.0.2
|
|
30
30
|
Requires-Dist: python-dotenv>=1.0.0
|
|
31
31
|
Requires-Dist: httpx>=0.27.0
|
|
32
32
|
Requires-Dist: jmespath>=1.0.0
|
|
33
|
+
Requires-Dist: xlrd>=2.0.1
|
|
34
|
+
Requires-Dist: zeep>=4.2.1
|
|
35
|
+
Requires-Dist: gql[httpx]>=3.5.0
|
|
33
36
|
|
|
34
37
|
# PyPlayKit - Enterprise Test Automation Framework
|
|
35
38
|
|
|
@@ -45,10 +48,11 @@ PyPlayKit is a **multi-project enterprise test automation framework** built with
|
|
|
45
48
|
- 🏢 **Multi-Project Support**: Separate test organization for multiple applications
|
|
46
49
|
- 👥 **Scalable**: Designed for 50-100 QE engineers working simultaneously
|
|
47
50
|
- 🎯 **Zero Merge Conflicts**: Project-first organization eliminates conflicts
|
|
51
|
+
- 📊 **Data Comparison**: File-to-File, File-to-DB, DB-to-File validation (Excel, CSV, JSON)
|
|
48
52
|
- 📚 **Comprehensive Documentation**: 8,700+ lines across 15+ guides
|
|
49
53
|
- 🔧 **Extensible**: Plugin architecture with orchestration support
|
|
50
54
|
- 🔍 **Self-Healing**: Locator recovery with fallback chains
|
|
51
|
-
-
|
|
55
|
+
- 📈 **Observable**: Comprehensive metrics and reporting
|
|
52
56
|
|
|
53
57
|
---
|
|
54
58
|
|
|
@@ -437,6 +441,82 @@ pytest tests/projects/timelyquote/functional/ui/quotes/test_create_quote.py -v
|
|
|
437
441
|
|
|
438
442
|
---
|
|
439
443
|
|
|
444
|
+
## Data Comparison Patterns
|
|
445
|
+
|
|
446
|
+
PyPlayKit provides comprehensive data comparison capabilities using the built-in `DataValidator` utility.
|
|
447
|
+
|
|
448
|
+
### Prerequisites for Data Comparison
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
# Install data comparison dependencies
|
|
452
|
+
pip install pandas openpyxl
|
|
453
|
+
|
|
454
|
+
# Or use the examples requirements file
|
|
455
|
+
pip install -r examples/requirements.txt
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Supported Comparison Types
|
|
459
|
+
|
|
460
|
+
| Comparison Type | Use Case | Example |
|
|
461
|
+
|----------------|----------|---------|
|
|
462
|
+
| **File-to-File** | Compare Excel, CSV, JSON files | Validate data export/import |
|
|
463
|
+
| **File-to-Database** | Verify data loads into database | ETL validation |
|
|
464
|
+
| **Database-to-File** | Validate database exports | Report generation testing |
|
|
465
|
+
|
|
466
|
+
### Quick Example: Excel-to-Excel Comparison
|
|
467
|
+
|
|
468
|
+
```python
|
|
469
|
+
import pytest
|
|
470
|
+
import pandas as pd
|
|
471
|
+
from pathlib import Path
|
|
472
|
+
from pyplaykit import DataValidator
|
|
473
|
+
|
|
474
|
+
@pytest.mark.data
|
|
475
|
+
def test_compare_excel_files(logger):
|
|
476
|
+
# Read both files
|
|
477
|
+
df1 = pd.read_excel("report_baseline.xlsx")
|
|
478
|
+
df2 = pd.read_excel("report_current.xlsx")
|
|
479
|
+
|
|
480
|
+
records1 = df1.to_dict('records')
|
|
481
|
+
records2 = df2.to_dict('records')
|
|
482
|
+
|
|
483
|
+
# Compare row by row
|
|
484
|
+
for idx, (r1, r2) in enumerate(zip(records1, records2)):
|
|
485
|
+
DataValidator.assert_records_equal(r1, r2)
|
|
486
|
+
|
|
487
|
+
logger.info("✓ Files match!")
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### DataValidator Methods
|
|
491
|
+
|
|
492
|
+
| Method | Purpose |
|
|
493
|
+
|--------|---------|
|
|
494
|
+
| `assert_records_equal()` | Compare two dictionaries exactly |
|
|
495
|
+
| `assert_floats_equal()` | Compare numeric values with tolerance |
|
|
496
|
+
| `assert_strings_equal_normalized()` | Compare strings with normalization |
|
|
497
|
+
| `assert_collection_contains_record()` | Check if record exists in collection |
|
|
498
|
+
| `assert_datetimes_equal()` | Compare datetime values |
|
|
499
|
+
|
|
500
|
+
### Runnable Examples
|
|
501
|
+
|
|
502
|
+
See **[examples/data_comparison_examples.py](examples/data_comparison_examples.py)** for 8 complete examples:
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
# Run all data comparison examples
|
|
506
|
+
pytest examples/data_comparison_examples.py -v -s
|
|
507
|
+
|
|
508
|
+
# Run specific example
|
|
509
|
+
pytest examples/data_comparison_examples.py::test_excel_to_excel_basic -v -s
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Full Documentation
|
|
513
|
+
|
|
514
|
+
- **[docs/PIP_INSTALL_GUIDE.md](docs/PIP_INSTALL_GUIDE.md#step-8--data-comparison-patterns)** - Complete patterns guide
|
|
515
|
+
- **[examples/README.md](examples/README.md)** - Examples documentation
|
|
516
|
+
- **[docs/DATA_VALIDATION_GUIDE.md](docs/DATA_VALIDATION_GUIDE.md)** - Advanced validation
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
440
520
|
## Unit Testing and Coverage
|
|
441
521
|
|
|
442
522
|
```bash
|
|
@@ -506,6 +586,7 @@ Each project config includes:
|
|
|
506
586
|
- **[QUICK_START_QE.md](QUICK_START_QE.md)** - Get started in 10 minutes
|
|
507
587
|
- **[TEST_LOCATION_CHEATSHEET.md](TEST_LOCATION_CHEATSHEET.md)** - One-page reference
|
|
508
588
|
- **[COMMANDS_FOR_YOUR_PROJECTS.md](COMMANDS_FOR_YOUR_PROJECTS.md)** - Command reference
|
|
589
|
+
- **[docs/PIP_INSTALL_GUIDE.md](docs/PIP_INSTALL_GUIDE.md)** - Pip install guide with data comparison patterns
|
|
509
590
|
|
|
510
591
|
### Implementation
|
|
511
592
|
- **[YOUR_PROJECTS_IMPLEMENTATION_GUIDE.md](YOUR_PROJECTS_IMPLEMENTATION_GUIDE.md)** - Step-by-step guide
|
|
@@ -517,6 +598,11 @@ Each project config includes:
|
|
|
517
598
|
- **[docs/TEST_ORGANIZATION.md](docs/TEST_ORGANIZATION.md)** - Organization strategy
|
|
518
599
|
- **[docs/QE_VISUAL_GUIDE.md](docs/QE_VISUAL_GUIDE.md)** - Visual guide with diagrams
|
|
519
600
|
|
|
601
|
+
### Data Comparison & Validation
|
|
602
|
+
- **[examples/data_comparison_examples.py](examples/data_comparison_examples.py)** - Runnable examples for Excel, CSV, DB comparison
|
|
603
|
+
- **[examples/README.md](examples/README.md)** - Examples documentation
|
|
604
|
+
- **[docs/DATA_VALIDATION_GUIDE.md](docs/DATA_VALIDATION_GUIDE.md)** - Advanced validation patterns
|
|
605
|
+
|
|
520
606
|
### Project Guides
|
|
521
607
|
- **[tests/projects/timelyquote/README.md](tests/projects/timelyquote/README.md)** - TimelyQuote guide
|
|
522
608
|
- **[tests/projects/dispatcho/README.md](tests/projects/dispatcho/README.md)** - Dispatcho guide
|
|
@@ -1,36 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: pyplaykit
|
|
3
|
-
Version: 1.0.0
|
|
4
|
-
Summary: Enterprise Python + Playwright automation framework
|
|
5
|
-
Author-email: Shan Konduru <shankonduru@gmail.com>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://github.com/ShanKonduru/pyplaykit
|
|
8
|
-
Project-URL: Repository, https://github.com/ShanKonduru/pyplaykit
|
|
9
|
-
Project-URL: Bug Tracker, https://github.com/ShanKonduru/pyplaykit/issues
|
|
10
|
-
Keywords: playwright,pytest,automation,framework,enterprise,testing,page-object-model
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Framework :: Pytest
|
|
16
|
-
Classifier: Topic :: Software Development :: Testing
|
|
17
|
-
Classifier: Intended Audience :: Developers
|
|
18
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
-
Classifier: Operating System :: OS Independent
|
|
20
|
-
Requires-Python: >=3.11
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
Requires-Dist: playwright==1.54.0
|
|
23
|
-
Requires-Dist: pytest==8.4.1
|
|
24
|
-
Requires-Dist: pytest-cov==6.2.1
|
|
25
|
-
Requires-Dist: pytest-xdist==3.6.1
|
|
26
|
-
Requires-Dist: pytest-rerunfailures==15.1
|
|
27
|
-
Requires-Dist: pytest-html==4.1.1
|
|
28
|
-
Requires-Dist: allure-pytest==2.13.5
|
|
29
|
-
Requires-Dist: PyYAML==6.0.2
|
|
30
|
-
Requires-Dist: python-dotenv>=1.0.0
|
|
31
|
-
Requires-Dist: httpx>=0.27.0
|
|
32
|
-
Requires-Dist: jmespath>=1.0.0
|
|
33
|
-
|
|
34
1
|
# PyPlayKit - Enterprise Test Automation Framework
|
|
35
2
|
|
|
36
3
|
**Tagline**: A modular, scalable, and CI/CD-ready Python + Playwright framework for enterprise automation supporting multiple projects and 50+ QE engineers.
|
|
@@ -45,10 +12,11 @@ PyPlayKit is a **multi-project enterprise test automation framework** built with
|
|
|
45
12
|
- 🏢 **Multi-Project Support**: Separate test organization for multiple applications
|
|
46
13
|
- 👥 **Scalable**: Designed for 50-100 QE engineers working simultaneously
|
|
47
14
|
- 🎯 **Zero Merge Conflicts**: Project-first organization eliminates conflicts
|
|
15
|
+
- 📊 **Data Comparison**: File-to-File, File-to-DB, DB-to-File validation (Excel, CSV, JSON)
|
|
48
16
|
- 📚 **Comprehensive Documentation**: 8,700+ lines across 15+ guides
|
|
49
17
|
- 🔧 **Extensible**: Plugin architecture with orchestration support
|
|
50
18
|
- 🔍 **Self-Healing**: Locator recovery with fallback chains
|
|
51
|
-
-
|
|
19
|
+
- 📈 **Observable**: Comprehensive metrics and reporting
|
|
52
20
|
|
|
53
21
|
---
|
|
54
22
|
|
|
@@ -437,6 +405,82 @@ pytest tests/projects/timelyquote/functional/ui/quotes/test_create_quote.py -v
|
|
|
437
405
|
|
|
438
406
|
---
|
|
439
407
|
|
|
408
|
+
## Data Comparison Patterns
|
|
409
|
+
|
|
410
|
+
PyPlayKit provides comprehensive data comparison capabilities using the built-in `DataValidator` utility.
|
|
411
|
+
|
|
412
|
+
### Prerequisites for Data Comparison
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
# Install data comparison dependencies
|
|
416
|
+
pip install pandas openpyxl
|
|
417
|
+
|
|
418
|
+
# Or use the examples requirements file
|
|
419
|
+
pip install -r examples/requirements.txt
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Supported Comparison Types
|
|
423
|
+
|
|
424
|
+
| Comparison Type | Use Case | Example |
|
|
425
|
+
|----------------|----------|---------|
|
|
426
|
+
| **File-to-File** | Compare Excel, CSV, JSON files | Validate data export/import |
|
|
427
|
+
| **File-to-Database** | Verify data loads into database | ETL validation |
|
|
428
|
+
| **Database-to-File** | Validate database exports | Report generation testing |
|
|
429
|
+
|
|
430
|
+
### Quick Example: Excel-to-Excel Comparison
|
|
431
|
+
|
|
432
|
+
```python
|
|
433
|
+
import pytest
|
|
434
|
+
import pandas as pd
|
|
435
|
+
from pathlib import Path
|
|
436
|
+
from pyplaykit import DataValidator
|
|
437
|
+
|
|
438
|
+
@pytest.mark.data
|
|
439
|
+
def test_compare_excel_files(logger):
|
|
440
|
+
# Read both files
|
|
441
|
+
df1 = pd.read_excel("report_baseline.xlsx")
|
|
442
|
+
df2 = pd.read_excel("report_current.xlsx")
|
|
443
|
+
|
|
444
|
+
records1 = df1.to_dict('records')
|
|
445
|
+
records2 = df2.to_dict('records')
|
|
446
|
+
|
|
447
|
+
# Compare row by row
|
|
448
|
+
for idx, (r1, r2) in enumerate(zip(records1, records2)):
|
|
449
|
+
DataValidator.assert_records_equal(r1, r2)
|
|
450
|
+
|
|
451
|
+
logger.info("✓ Files match!")
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### DataValidator Methods
|
|
455
|
+
|
|
456
|
+
| Method | Purpose |
|
|
457
|
+
|--------|---------|
|
|
458
|
+
| `assert_records_equal()` | Compare two dictionaries exactly |
|
|
459
|
+
| `assert_floats_equal()` | Compare numeric values with tolerance |
|
|
460
|
+
| `assert_strings_equal_normalized()` | Compare strings with normalization |
|
|
461
|
+
| `assert_collection_contains_record()` | Check if record exists in collection |
|
|
462
|
+
| `assert_datetimes_equal()` | Compare datetime values |
|
|
463
|
+
|
|
464
|
+
### Runnable Examples
|
|
465
|
+
|
|
466
|
+
See **[examples/data_comparison_examples.py](examples/data_comparison_examples.py)** for 8 complete examples:
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
# Run all data comparison examples
|
|
470
|
+
pytest examples/data_comparison_examples.py -v -s
|
|
471
|
+
|
|
472
|
+
# Run specific example
|
|
473
|
+
pytest examples/data_comparison_examples.py::test_excel_to_excel_basic -v -s
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Full Documentation
|
|
477
|
+
|
|
478
|
+
- **[docs/PIP_INSTALL_GUIDE.md](docs/PIP_INSTALL_GUIDE.md#step-8--data-comparison-patterns)** - Complete patterns guide
|
|
479
|
+
- **[examples/README.md](examples/README.md)** - Examples documentation
|
|
480
|
+
- **[docs/DATA_VALIDATION_GUIDE.md](docs/DATA_VALIDATION_GUIDE.md)** - Advanced validation
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
440
484
|
## Unit Testing and Coverage
|
|
441
485
|
|
|
442
486
|
```bash
|
|
@@ -506,6 +550,7 @@ Each project config includes:
|
|
|
506
550
|
- **[QUICK_START_QE.md](QUICK_START_QE.md)** - Get started in 10 minutes
|
|
507
551
|
- **[TEST_LOCATION_CHEATSHEET.md](TEST_LOCATION_CHEATSHEET.md)** - One-page reference
|
|
508
552
|
- **[COMMANDS_FOR_YOUR_PROJECTS.md](COMMANDS_FOR_YOUR_PROJECTS.md)** - Command reference
|
|
553
|
+
- **[docs/PIP_INSTALL_GUIDE.md](docs/PIP_INSTALL_GUIDE.md)** - Pip install guide with data comparison patterns
|
|
509
554
|
|
|
510
555
|
### Implementation
|
|
511
556
|
- **[YOUR_PROJECTS_IMPLEMENTATION_GUIDE.md](YOUR_PROJECTS_IMPLEMENTATION_GUIDE.md)** - Step-by-step guide
|
|
@@ -517,6 +562,11 @@ Each project config includes:
|
|
|
517
562
|
- **[docs/TEST_ORGANIZATION.md](docs/TEST_ORGANIZATION.md)** - Organization strategy
|
|
518
563
|
- **[docs/QE_VISUAL_GUIDE.md](docs/QE_VISUAL_GUIDE.md)** - Visual guide with diagrams
|
|
519
564
|
|
|
565
|
+
### Data Comparison & Validation
|
|
566
|
+
- **[examples/data_comparison_examples.py](examples/data_comparison_examples.py)** - Runnable examples for Excel, CSV, DB comparison
|
|
567
|
+
- **[examples/README.md](examples/README.md)** - Examples documentation
|
|
568
|
+
- **[docs/DATA_VALIDATION_GUIDE.md](docs/DATA_VALIDATION_GUIDE.md)** - Advanced validation patterns
|
|
569
|
+
|
|
520
570
|
### Project Guides
|
|
521
571
|
- **[tests/projects/timelyquote/README.md](tests/projects/timelyquote/README.md)** - TimelyQuote guide
|
|
522
572
|
- **[tests/projects/dispatcho/README.md](tests/projects/dispatcho/README.md)** - Dispatcho guide
|
|
@@ -13,27 +13,56 @@ from core.base_page import BasePage
|
|
|
13
13
|
from utils.api_client import ApiClient, ApiResponse
|
|
14
14
|
from utils.auth_manager import AuthTokenManager
|
|
15
15
|
from utils.data_validator import DataValidator
|
|
16
|
+
from utils.ui_db_validator import UIDbValidator
|
|
17
|
+
from utils.graphql_client import GraphQLClient, GraphQLResponse, GraphQLValidator
|
|
18
|
+
from utils.soap_client import SoapClient, SoapResponse, SoapValidator
|
|
16
19
|
from utils.db_client import BaseDbClient, DbClient
|
|
20
|
+
from utils.transformation_validator import (
|
|
21
|
+
ColumnSpec,
|
|
22
|
+
PairRule,
|
|
23
|
+
RowRule,
|
|
24
|
+
RuleValidator,
|
|
25
|
+
TransformationValidator,
|
|
26
|
+
Violation,
|
|
27
|
+
)
|
|
17
28
|
from utils.db_fixtures import DbCleaner, DbSeeder, DbTransaction
|
|
29
|
+
from utils.data_source import ChunkedDbDataSource
|
|
18
30
|
from utils.logger import get_logger
|
|
19
31
|
from utils.response_validator import ResponseValidator
|
|
20
32
|
|
|
21
|
-
__version__ = "1.
|
|
33
|
+
__version__ = "1.3.0"
|
|
22
34
|
|
|
23
35
|
__all__ = [
|
|
24
|
-
# HTTP / API
|
|
36
|
+
# HTTP / API — REST
|
|
25
37
|
"ApiClient",
|
|
26
38
|
"ApiResponse",
|
|
27
39
|
"AuthTokenManager",
|
|
28
40
|
"ResponseValidator",
|
|
41
|
+
# HTTP / API — GraphQL
|
|
42
|
+
"GraphQLClient",
|
|
43
|
+
"GraphQLResponse",
|
|
44
|
+
"GraphQLValidator",
|
|
45
|
+
# HTTP / API — SOAP
|
|
46
|
+
"SoapClient",
|
|
47
|
+
"SoapResponse",
|
|
48
|
+
"SoapValidator",
|
|
29
49
|
# Data validation
|
|
30
50
|
"DataValidator",
|
|
51
|
+
"UIDbValidator",
|
|
52
|
+
# Transformation + rule-based validation
|
|
53
|
+
"ColumnSpec",
|
|
54
|
+
"TransformationValidator",
|
|
55
|
+
"RowRule",
|
|
56
|
+
"PairRule",
|
|
57
|
+
"RuleValidator",
|
|
58
|
+
"Violation",
|
|
31
59
|
# Database
|
|
32
60
|
"BaseDbClient",
|
|
33
61
|
"DbClient",
|
|
34
62
|
"DbTransaction",
|
|
35
63
|
"DbSeeder",
|
|
36
64
|
"DbCleaner",
|
|
65
|
+
"ChunkedDbDataSource",
|
|
37
66
|
# UI
|
|
38
67
|
"BasePage",
|
|
39
68
|
"BaseComponent",
|
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyplaykit
|
|
3
|
+
Version: 1.4.0
|
|
4
|
+
Summary: Enterprise Python + Playwright automation framework
|
|
5
|
+
Author-email: Shan Konduru <shankonduru@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ShanKonduru/pyplaykit
|
|
8
|
+
Project-URL: Repository, https://github.com/ShanKonduru/pyplaykit
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/ShanKonduru/pyplaykit/issues
|
|
10
|
+
Keywords: playwright,pytest,automation,framework,enterprise,testing,page-object-model
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Framework :: Pytest
|
|
16
|
+
Classifier: Topic :: Software Development :: Testing
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Requires-Dist: playwright==1.54.0
|
|
23
|
+
Requires-Dist: pytest==9.1.1
|
|
24
|
+
Requires-Dist: pytest-cov==7.1.0
|
|
25
|
+
Requires-Dist: pytest-xdist==3.8.0
|
|
26
|
+
Requires-Dist: pytest-rerunfailures==16.4
|
|
27
|
+
Requires-Dist: pytest-html==4.2.0
|
|
28
|
+
Requires-Dist: allure-pytest==2.16.0
|
|
29
|
+
Requires-Dist: PyYAML==6.0.2
|
|
30
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
31
|
+
Requires-Dist: httpx>=0.27.0
|
|
32
|
+
Requires-Dist: jmespath>=1.0.0
|
|
33
|
+
Requires-Dist: xlrd>=2.0.1
|
|
34
|
+
Requires-Dist: zeep>=4.2.1
|
|
35
|
+
Requires-Dist: gql[httpx]>=3.5.0
|
|
36
|
+
|
|
1
37
|
# PyPlayKit - Enterprise Test Automation Framework
|
|
2
38
|
|
|
3
39
|
**Tagline**: A modular, scalable, and CI/CD-ready Python + Playwright framework for enterprise automation supporting multiple projects and 50+ QE engineers.
|
|
@@ -12,10 +48,11 @@ PyPlayKit is a **multi-project enterprise test automation framework** built with
|
|
|
12
48
|
- 🏢 **Multi-Project Support**: Separate test organization for multiple applications
|
|
13
49
|
- 👥 **Scalable**: Designed for 50-100 QE engineers working simultaneously
|
|
14
50
|
- 🎯 **Zero Merge Conflicts**: Project-first organization eliminates conflicts
|
|
51
|
+
- 📊 **Data Comparison**: File-to-File, File-to-DB, DB-to-File validation (Excel, CSV, JSON)
|
|
15
52
|
- 📚 **Comprehensive Documentation**: 8,700+ lines across 15+ guides
|
|
16
53
|
- 🔧 **Extensible**: Plugin architecture with orchestration support
|
|
17
54
|
- 🔍 **Self-Healing**: Locator recovery with fallback chains
|
|
18
|
-
-
|
|
55
|
+
- 📈 **Observable**: Comprehensive metrics and reporting
|
|
19
56
|
|
|
20
57
|
---
|
|
21
58
|
|
|
@@ -404,6 +441,82 @@ pytest tests/projects/timelyquote/functional/ui/quotes/test_create_quote.py -v
|
|
|
404
441
|
|
|
405
442
|
---
|
|
406
443
|
|
|
444
|
+
## Data Comparison Patterns
|
|
445
|
+
|
|
446
|
+
PyPlayKit provides comprehensive data comparison capabilities using the built-in `DataValidator` utility.
|
|
447
|
+
|
|
448
|
+
### Prerequisites for Data Comparison
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
# Install data comparison dependencies
|
|
452
|
+
pip install pandas openpyxl
|
|
453
|
+
|
|
454
|
+
# Or use the examples requirements file
|
|
455
|
+
pip install -r examples/requirements.txt
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Supported Comparison Types
|
|
459
|
+
|
|
460
|
+
| Comparison Type | Use Case | Example |
|
|
461
|
+
|----------------|----------|---------|
|
|
462
|
+
| **File-to-File** | Compare Excel, CSV, JSON files | Validate data export/import |
|
|
463
|
+
| **File-to-Database** | Verify data loads into database | ETL validation |
|
|
464
|
+
| **Database-to-File** | Validate database exports | Report generation testing |
|
|
465
|
+
|
|
466
|
+
### Quick Example: Excel-to-Excel Comparison
|
|
467
|
+
|
|
468
|
+
```python
|
|
469
|
+
import pytest
|
|
470
|
+
import pandas as pd
|
|
471
|
+
from pathlib import Path
|
|
472
|
+
from pyplaykit import DataValidator
|
|
473
|
+
|
|
474
|
+
@pytest.mark.data
|
|
475
|
+
def test_compare_excel_files(logger):
|
|
476
|
+
# Read both files
|
|
477
|
+
df1 = pd.read_excel("report_baseline.xlsx")
|
|
478
|
+
df2 = pd.read_excel("report_current.xlsx")
|
|
479
|
+
|
|
480
|
+
records1 = df1.to_dict('records')
|
|
481
|
+
records2 = df2.to_dict('records')
|
|
482
|
+
|
|
483
|
+
# Compare row by row
|
|
484
|
+
for idx, (r1, r2) in enumerate(zip(records1, records2)):
|
|
485
|
+
DataValidator.assert_records_equal(r1, r2)
|
|
486
|
+
|
|
487
|
+
logger.info("✓ Files match!")
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
### DataValidator Methods
|
|
491
|
+
|
|
492
|
+
| Method | Purpose |
|
|
493
|
+
|--------|---------|
|
|
494
|
+
| `assert_records_equal()` | Compare two dictionaries exactly |
|
|
495
|
+
| `assert_floats_equal()` | Compare numeric values with tolerance |
|
|
496
|
+
| `assert_strings_equal_normalized()` | Compare strings with normalization |
|
|
497
|
+
| `assert_collection_contains_record()` | Check if record exists in collection |
|
|
498
|
+
| `assert_datetimes_equal()` | Compare datetime values |
|
|
499
|
+
|
|
500
|
+
### Runnable Examples
|
|
501
|
+
|
|
502
|
+
See **[examples/data_comparison_examples.py](examples/data_comparison_examples.py)** for 8 complete examples:
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
# Run all data comparison examples
|
|
506
|
+
pytest examples/data_comparison_examples.py -v -s
|
|
507
|
+
|
|
508
|
+
# Run specific example
|
|
509
|
+
pytest examples/data_comparison_examples.py::test_excel_to_excel_basic -v -s
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### Full Documentation
|
|
513
|
+
|
|
514
|
+
- **[docs/PIP_INSTALL_GUIDE.md](docs/PIP_INSTALL_GUIDE.md#step-8--data-comparison-patterns)** - Complete patterns guide
|
|
515
|
+
- **[examples/README.md](examples/README.md)** - Examples documentation
|
|
516
|
+
- **[docs/DATA_VALIDATION_GUIDE.md](docs/DATA_VALIDATION_GUIDE.md)** - Advanced validation
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
407
520
|
## Unit Testing and Coverage
|
|
408
521
|
|
|
409
522
|
```bash
|
|
@@ -473,6 +586,7 @@ Each project config includes:
|
|
|
473
586
|
- **[QUICK_START_QE.md](QUICK_START_QE.md)** - Get started in 10 minutes
|
|
474
587
|
- **[TEST_LOCATION_CHEATSHEET.md](TEST_LOCATION_CHEATSHEET.md)** - One-page reference
|
|
475
588
|
- **[COMMANDS_FOR_YOUR_PROJECTS.md](COMMANDS_FOR_YOUR_PROJECTS.md)** - Command reference
|
|
589
|
+
- **[docs/PIP_INSTALL_GUIDE.md](docs/PIP_INSTALL_GUIDE.md)** - Pip install guide with data comparison patterns
|
|
476
590
|
|
|
477
591
|
### Implementation
|
|
478
592
|
- **[YOUR_PROJECTS_IMPLEMENTATION_GUIDE.md](YOUR_PROJECTS_IMPLEMENTATION_GUIDE.md)** - Step-by-step guide
|
|
@@ -484,6 +598,11 @@ Each project config includes:
|
|
|
484
598
|
- **[docs/TEST_ORGANIZATION.md](docs/TEST_ORGANIZATION.md)** - Organization strategy
|
|
485
599
|
- **[docs/QE_VISUAL_GUIDE.md](docs/QE_VISUAL_GUIDE.md)** - Visual guide with diagrams
|
|
486
600
|
|
|
601
|
+
### Data Comparison & Validation
|
|
602
|
+
- **[examples/data_comparison_examples.py](examples/data_comparison_examples.py)** - Runnable examples for Excel, CSV, DB comparison
|
|
603
|
+
- **[examples/README.md](examples/README.md)** - Examples documentation
|
|
604
|
+
- **[docs/DATA_VALIDATION_GUIDE.md](docs/DATA_VALIDATION_GUIDE.md)** - Advanced validation patterns
|
|
605
|
+
|
|
487
606
|
### Project Guides
|
|
488
607
|
- **[tests/projects/timelyquote/README.md](tests/projects/timelyquote/README.md)** - TimelyQuote guide
|
|
489
608
|
- **[tests/projects/dispatcho/README.md](tests/projects/dispatcho/README.md)** - Dispatcho guide
|
|
@@ -68,11 +68,15 @@ utils/db_pool.py
|
|
|
68
68
|
utils/db_validation_helper.py
|
|
69
69
|
utils/environment_validator.py
|
|
70
70
|
utils/file_reader.py
|
|
71
|
+
utils/graphql_client.py
|
|
71
72
|
utils/logger.py
|
|
72
73
|
utils/observability.py
|
|
73
74
|
utils/response_validator.py
|
|
74
75
|
utils/screenshot_helper.py
|
|
76
|
+
utils/soap_client.py
|
|
75
77
|
utils/tdm.py
|
|
78
|
+
utils/transformation_validator.py
|
|
79
|
+
utils/ui_db_validator.py
|
|
76
80
|
utils/yaml_config_reader.py
|
|
77
81
|
utils/db_drivers/__init__.py
|
|
78
82
|
utils/db_drivers/db2.py
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
playwright==1.54.0
|
|
2
|
+
pytest==9.1.1
|
|
3
|
+
pytest-cov==7.1.0
|
|
4
|
+
pytest-xdist==3.8.0
|
|
5
|
+
pytest-rerunfailures==16.4
|
|
6
|
+
pytest-html==4.2.0
|
|
7
|
+
allure-pytest==2.16.0
|
|
8
|
+
PyYAML==6.0.2
|
|
9
|
+
python-dotenv>=1.0.0
|
|
10
|
+
httpx>=0.27.0
|
|
11
|
+
jmespath>=1.0.0
|
|
12
|
+
xlrd>=2.0.1
|
|
13
|
+
zeep>=4.2.1
|
|
14
|
+
gql[httpx]>=3.5.0
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyplaykit"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.4.0"
|
|
8
8
|
description = "Enterprise Python + Playwright automation framework"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -27,16 +27,19 @@ classifiers = [
|
|
|
27
27
|
|
|
28
28
|
dependencies = [
|
|
29
29
|
"playwright==1.54.0",
|
|
30
|
-
"pytest==
|
|
31
|
-
"pytest-cov==
|
|
32
|
-
"pytest-xdist==3.
|
|
33
|
-
"pytest-rerunfailures==
|
|
34
|
-
"pytest-html==4.
|
|
35
|
-
"allure-pytest==2.
|
|
30
|
+
"pytest==9.1.1",
|
|
31
|
+
"pytest-cov==7.1.0",
|
|
32
|
+
"pytest-xdist==3.8.0",
|
|
33
|
+
"pytest-rerunfailures==16.4",
|
|
34
|
+
"pytest-html==4.2.0",
|
|
35
|
+
"allure-pytest==2.16.0",
|
|
36
36
|
"PyYAML==6.0.2",
|
|
37
37
|
"python-dotenv>=1.0.0",
|
|
38
38
|
"httpx>=0.27.0",
|
|
39
39
|
"jmespath>=1.0.0",
|
|
40
|
+
"xlrd>=2.0.1",
|
|
41
|
+
"zeep>=4.2.1",
|
|
42
|
+
"gql[httpx]>=3.5.0",
|
|
40
43
|
]
|
|
41
44
|
|
|
42
45
|
[project.scripts]
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
"""Reusable API client built on httpx for framework-level API testing.
|
|
1
|
+
"""Reusable REST API client built on httpx for framework-level API testing.
|
|
2
|
+
|
|
3
|
+
For GraphQL APIs use ``utils.graphql_client.GraphQLClient``.
|
|
4
|
+
For SOAP / WSDL APIs use ``utils.soap_client.SoapClient``.
|
|
5
|
+
|
|
6
|
+
Not supported:
|
|
7
|
+
- WebSocket connections (use Playwright's page.on("websocket") for browser WS testing)
|
|
8
|
+
- gRPC (requires protobuf compilation — out of scope for this framework)
|
|
9
|
+
"""
|
|
2
10
|
|
|
3
11
|
from __future__ import annotations
|
|
4
12
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Data loading utilities for JSON, CSV, and Excel (XLSX) with environment placeholder support."""
|
|
1
|
+
"""Data loading utilities for JSON, CSV, and Excel (XLSX / XLS) with environment placeholder support."""
|
|
2
2
|
|
|
3
3
|
import csv
|
|
4
4
|
import json
|
|
@@ -75,3 +75,44 @@ def load_xlsx(
|
|
|
75
75
|
rows.append(row)
|
|
76
76
|
|
|
77
77
|
return _resolve_env_placeholders(rows)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def load_xls(
|
|
81
|
+
file_path: str,
|
|
82
|
+
sheet_name: str | None = None,
|
|
83
|
+
) -> list[dict[str, Any]]:
|
|
84
|
+
"""Load a legacy Excel (.xls) worksheet and return rows as a list of dicts.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
file_path: Path to the .xls file.
|
|
88
|
+
sheet_name: Worksheet name to load. Defaults to the first sheet.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
list[dict] where each dict maps column header → cell value.
|
|
92
|
+
Fully blank rows are skipped. Cell values are returned as native Python
|
|
93
|
+
types via xlrd — numeric, text, bool, or None.
|
|
94
|
+
|
|
95
|
+
Environment placeholder resolution is applied to string cell values so that
|
|
96
|
+
${ENV_VAR} tokens work the same way as in load_json(), load_csv(), and load_xlsx().
|
|
97
|
+
"""
|
|
98
|
+
import xlrd # noqa: PLC0415 — lazy import; xlrd is an optional install
|
|
99
|
+
|
|
100
|
+
wb = xlrd.open_workbook(file_path)
|
|
101
|
+
ws = wb.sheet_by_name(sheet_name) if sheet_name else wb.sheet_by_index(0)
|
|
102
|
+
|
|
103
|
+
if ws.nrows == 0:
|
|
104
|
+
return []
|
|
105
|
+
|
|
106
|
+
headers = [
|
|
107
|
+
str(ws.cell_value(0, col)) if ws.cell_value(0, col) != "" else f"col_{col}"
|
|
108
|
+
for col in range(ws.ncols)
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
rows: list[dict[str, Any]] = []
|
|
112
|
+
for row_idx in range(1, ws.nrows):
|
|
113
|
+
raw = [ws.cell_value(row_idx, col) for col in range(ws.ncols)]
|
|
114
|
+
if all(v == "" or v is None for v in raw):
|
|
115
|
+
continue
|
|
116
|
+
rows.append(dict(zip(headers, raw)))
|
|
117
|
+
|
|
118
|
+
return _resolve_env_placeholders(rows)
|