pyplaykit 1.7.0__tar.gz → 1.7.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.
Files changed (92) hide show
  1. {pyplaykit-1.7.0/pyplaykit.egg-info → pyplaykit-1.7.1}/PKG-INFO +1 -1
  2. {pyplaykit-1.7.0 → pyplaykit-1.7.1/pyplaykit.egg-info}/PKG-INFO +1 -1
  3. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pyplaykit.egg-info/SOURCES.txt +1 -0
  4. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pyproject.toml +1 -1
  5. pyplaykit-1.7.1/utils/database_clients.py +310 -0
  6. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/MANIFEST.in +0 -0
  7. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/README.md +0 -0
  8. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/components/__init__.py +0 -0
  9. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/components/base_component.py +0 -0
  10. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/components/data_table.py +0 -0
  11. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/components/form_field.py +0 -0
  12. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/components/modal.py +0 -0
  13. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/config/config.yaml +0 -0
  14. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/config/environments.yaml +0 -0
  15. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/config/projects/customs_modernization.yaml +0 -0
  16. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/config/projects/dispatcho.yaml +0 -0
  17. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/config/projects/timelyquote.yaml +0 -0
  18. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/core/__init__.py +0 -0
  19. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/core/base_page.py +0 -0
  20. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/core/base_test.py +0 -0
  21. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/core/browser_factory.py +0 -0
  22. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/core/playwright_manager.py +0 -0
  23. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/dist_pkg/__init__.py +0 -0
  24. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/dist_pkg/distribution.py +0 -0
  25. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/integrations/__init__.py +0 -0
  26. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/integrations/adapters.py +0 -0
  27. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/orchestration/__init__.py +0 -0
  28. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/orchestration/enforcer.py +0 -0
  29. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/orchestration/planner.py +0 -0
  30. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pages/__init__.py +0 -0
  31. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pages/dashboard_page.py +0 -0
  32. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pages/login_page.py +0 -0
  33. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pages/orangehrm_login_page.py +0 -0
  34. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pages/saucedemo_inventory_page.py +0 -0
  35. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pages/saucedemo_login_page.py +0 -0
  36. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/plugins/__init__.py +0 -0
  37. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/plugins/api_plugin.py +0 -0
  38. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/plugins/base.py +0 -0
  39. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/plugins/data_plugin.py +0 -0
  40. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/plugins/registry.py +0 -0
  41. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/plugins/security_plugin.py +0 -0
  42. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pyplaykit/__init__.py +0 -0
  43. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pyplaykit/cli.py +0 -0
  44. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pyplaykit/plugin.py +0 -0
  45. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pyplaykit.egg-info/dependency_links.txt +0 -0
  46. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pyplaykit.egg-info/entry_points.txt +0 -0
  47. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pyplaykit.egg-info/requires.txt +0 -0
  48. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/pyplaykit.egg-info/top_level.txt +0 -0
  49. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/resilience/__init__.py +0 -0
  50. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/resilience/locator_recovery.py +0 -0
  51. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/setup.cfg +0 -0
  52. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/test_data/api/ecom/auth_payloads.json +0 -0
  53. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/test_data/api/ecom/product_payloads.json +0 -0
  54. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/test_data/api_test_data.json +0 -0
  55. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/test_data/data_validation_test_data.json +0 -0
  56. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/test_data/login_test_data.json +0 -0
  57. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/test_data/orangehrm_login_data.json +0 -0
  58. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/test_data/ui_test_data.json +0 -0
  59. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/__init__.py +0 -0
  60. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/api_client.py +0 -0
  61. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/api_test_helper.py +0 -0
  62. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/assertion_helper.py +0 -0
  63. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/auth_manager.py +0 -0
  64. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/data_comparison_report.py +0 -0
  65. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/data_comparison_utils.py +0 -0
  66. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/data_loader.py +0 -0
  67. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/data_source.py +0 -0
  68. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/data_validator.py +0 -0
  69. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_client.py +0 -0
  70. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_drivers/__init__.py +0 -0
  71. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_drivers/db2.py +0 -0
  72. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_drivers/mysql.py +0 -0
  73. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_drivers/oracle.py +0 -0
  74. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_drivers/snowflake.py +0 -0
  75. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_drivers/sqlite.py +0 -0
  76. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_drivers/sqlserver.py +0 -0
  77. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_factory.py +0 -0
  78. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_fixtures.py +0 -0
  79. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_pool.py +0 -0
  80. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/db_validation_helper.py +0 -0
  81. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/environment_validator.py +0 -0
  82. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/file_reader.py +0 -0
  83. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/graphql_client.py +0 -0
  84. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/logger.py +0 -0
  85. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/observability.py +0 -0
  86. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/response_validator.py +0 -0
  87. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/screenshot_helper.py +0 -0
  88. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/soap_client.py +0 -0
  89. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/tdm.py +0 -0
  90. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/transformation_validator.py +0 -0
  91. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/ui_db_validator.py +0 -0
  92. {pyplaykit-1.7.0 → pyplaykit-1.7.1}/utils/yaml_config_reader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyplaykit
3
- Version: 1.7.0
3
+ Version: 1.7.1
4
4
  Summary: Enterprise Python + Playwright automation framework
5
5
  Author-email: Shan Konduru <shankonduru@gmail.com>
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyplaykit
3
- Version: 1.7.0
3
+ Version: 1.7.1
4
4
  Summary: Enterprise Python + Playwright automation framework
5
5
  Author-email: Shan Konduru <shankonduru@gmail.com>
6
6
  License: MIT
@@ -63,6 +63,7 @@ utils/data_comparison_utils.py
63
63
  utils/data_loader.py
64
64
  utils/data_source.py
65
65
  utils/data_validator.py
66
+ utils/database_clients.py
66
67
  utils/db_client.py
67
68
  utils/db_factory.py
68
69
  utils/db_fixtures.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyplaykit"
7
- version = "1.7.0"
7
+ version = "1.7.1"
8
8
  description = "Enterprise Python + Playwright automation framework"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -0,0 +1,310 @@
1
+ """
2
+ Custom database client wrappers for cross-database comparisons.
3
+
4
+ PyPlayKit's built-in db_client fixture supports PostgreSQL only.
5
+ Use these wrappers to compare other database types:
6
+ - Oracle
7
+ - SQL Server
8
+ - MySQL
9
+
10
+ Installation:
11
+ pip install cx_Oracle # Oracle
12
+ pip install pyodbc # SQL Server
13
+ pip install pymysql # MySQL
14
+
15
+ Usage:
16
+ from utils.database_clients import OracleClient, SQLServerClient, MySQLClient
17
+
18
+ oracle = OracleClient(user="...", password="...", dsn="...")
19
+ results = oracle.fetch_all("SELECT * FROM customers")
20
+ oracle.close()
21
+ """
22
+
23
+ from typing import Any
24
+
25
+
26
+ class DatabaseClient:
27
+ """Base class for all database clients."""
28
+
29
+ def fetch_all(self, query: str, params: tuple = ()) -> list[dict[str, Any]]:
30
+ """
31
+ Execute query and return results as list of dictionaries.
32
+
33
+ Args:
34
+ query: SQL query to execute
35
+ params: Query parameters (default: ())
36
+
37
+ Returns:
38
+ List of dictionaries where each dict represents one row
39
+
40
+ Example:
41
+ results = client.fetch_all("SELECT id, name FROM users WHERE id = %s", (123,))
42
+ # Returns: [{"id": 123, "name": "Alice"}]
43
+ """
44
+ raise NotImplementedError("Subclasses must implement fetch_all()")
45
+
46
+ def close(self):
47
+ """Close database connection."""
48
+ raise NotImplementedError("Subclasses must implement close()")
49
+
50
+
51
+ class OracleClient(DatabaseClient):
52
+ """
53
+ Oracle database client wrapper.
54
+
55
+ Installation:
56
+ pip install cx_Oracle
57
+ # Also install Oracle Instant Client
58
+
59
+ Usage:
60
+ oracle = OracleClient(
61
+ user="oracle_user",
62
+ password="oracle_password",
63
+ dsn="hostname:1521/service_name"
64
+ )
65
+
66
+ results = oracle.fetch_all("SELECT * FROM CUSTOMERS ORDER BY ID")
67
+ oracle.close()
68
+
69
+ Column Names:
70
+ Automatically converts Oracle's uppercase column names to lowercase
71
+ for consistency with PostgreSQL/MySQL.
72
+ """
73
+
74
+ def __init__(self, user: str, password: str, dsn: str):
75
+ """
76
+ Initialize Oracle connection.
77
+
78
+ Args:
79
+ user: Oracle username
80
+ password: Oracle password
81
+ dsn: Data Source Name in format "host:port/service_name"
82
+ Example: "oracle.example.com:1521/PROD"
83
+ """
84
+ try:
85
+ import cx_Oracle
86
+ except ImportError:
87
+ raise ImportError(
88
+ "cx_Oracle is required for Oracle connections. "
89
+ "Install with: pip install cx_Oracle"
90
+ )
91
+
92
+ self.connection = cx_Oracle.connect(user=user, password=password, dsn=dsn)
93
+
94
+ def fetch_all(self, query: str, params: tuple = ()) -> list[dict[str, Any]]:
95
+ """Execute query and return list of dictionaries."""
96
+ cursor = self.connection.cursor()
97
+ cursor.execute(query, params)
98
+
99
+ # Get column names (convert to lowercase for consistency)
100
+ columns = [desc[0].lower() for desc in cursor.description]
101
+
102
+ # Fetch all rows
103
+ rows = cursor.fetchall()
104
+
105
+ # Convert to list of dictionaries
106
+ result = [dict(zip(columns, row)) for row in rows]
107
+
108
+ cursor.close()
109
+ return result
110
+
111
+ def close(self):
112
+ """Close Oracle connection."""
113
+ if self.connection:
114
+ self.connection.close()
115
+
116
+
117
+ class SQLServerClient(DatabaseClient):
118
+ """
119
+ SQL Server database client wrapper.
120
+
121
+ Installation:
122
+ pip install pyodbc
123
+ # Also install: ODBC Driver 17 for SQL Server
124
+
125
+ Windows:
126
+ Download ODBC Driver from Microsoft
127
+
128
+ Linux:
129
+ sudo apt-get install msodbcsql17
130
+
131
+ macOS:
132
+ brew tap microsoft/mssql-release
133
+ brew install msodbcsql17
134
+
135
+ Usage:
136
+ sqlserver = SQLServerClient(
137
+ server="sqlserver.example.com",
138
+ database="production",
139
+ username="sa",
140
+ password="password"
141
+ )
142
+
143
+ results = sqlserver.fetch_all("SELECT * FROM dbo.Customers")
144
+ sqlserver.close()
145
+
146
+ Column Names:
147
+ Automatically converts column names to lowercase for consistency.
148
+ """
149
+
150
+ def __init__(
151
+ self,
152
+ server: str,
153
+ database: str,
154
+ username: str,
155
+ password: str,
156
+ driver: str = "ODBC Driver 17 for SQL Server",
157
+ port: int = 1433
158
+ ):
159
+ """
160
+ Initialize SQL Server connection.
161
+
162
+ Args:
163
+ server: SQL Server hostname
164
+ database: Database name
165
+ username: SQL Server username
166
+ password: SQL Server password
167
+ driver: ODBC driver name (default: ODBC Driver 17 for SQL Server)
168
+ port: SQL Server port (default: 1433)
169
+ """
170
+ try:
171
+ import pyodbc
172
+ except ImportError:
173
+ raise ImportError(
174
+ "pyodbc is required for SQL Server connections. "
175
+ "Install with: pip install pyodbc"
176
+ )
177
+
178
+ conn_string = (
179
+ f"DRIVER={{{driver}}};"
180
+ f"SERVER={server},{port};"
181
+ f"DATABASE={database};"
182
+ f"UID={username};"
183
+ f"PWD={password}"
184
+ )
185
+ self.connection = pyodbc.connect(conn_string)
186
+
187
+ def fetch_all(self, query: str, params: tuple = ()) -> list[dict[str, Any]]:
188
+ """Execute query and return list of dictionaries."""
189
+ cursor = self.connection.cursor()
190
+ cursor.execute(query, params)
191
+
192
+ # Get column names (convert to lowercase)
193
+ columns = [column[0].lower() for column in cursor.description]
194
+
195
+ # Fetch all rows
196
+ rows = cursor.fetchall()
197
+
198
+ # Convert to list of dictionaries
199
+ result = [dict(zip(columns, row)) for row in rows]
200
+
201
+ cursor.close()
202
+ return result
203
+
204
+ def close(self):
205
+ """Close SQL Server connection."""
206
+ if self.connection:
207
+ self.connection.close()
208
+
209
+
210
+ class MySQLClient(DatabaseClient):
211
+ """
212
+ MySQL database client wrapper.
213
+
214
+ Installation:
215
+ pip install pymysql
216
+
217
+ Usage:
218
+ mysql = MySQLClient(
219
+ host="mysql.example.com",
220
+ user="root",
221
+ password="password",
222
+ database="myapp"
223
+ )
224
+
225
+ results = mysql.fetch_all("SELECT * FROM products")
226
+ mysql.close()
227
+
228
+ Column Names:
229
+ Returns lowercase column names by default (MySQL convention).
230
+ """
231
+
232
+ def __init__(
233
+ self,
234
+ host: str,
235
+ user: str,
236
+ password: str,
237
+ database: str,
238
+ port: int = 3306,
239
+ charset: str = "utf8mb4"
240
+ ):
241
+ """
242
+ Initialize MySQL connection.
243
+
244
+ Args:
245
+ host: MySQL hostname
246
+ user: MySQL username
247
+ password: MySQL password
248
+ database: Database name
249
+ port: MySQL port (default: 3306)
250
+ charset: Character set (default: utf8mb4)
251
+ """
252
+ try:
253
+ import pymysql
254
+ except ImportError:
255
+ raise ImportError(
256
+ "pymysql is required for MySQL connections. "
257
+ "Install with: pip install pymysql"
258
+ )
259
+
260
+ self.connection = pymysql.connect(
261
+ host=host,
262
+ port=port,
263
+ user=user,
264
+ password=password,
265
+ database=database,
266
+ charset=charset,
267
+ cursorclass=pymysql.cursors.DictCursor # Returns dict automatically
268
+ )
269
+
270
+ def fetch_all(self, query: str, params: tuple = ()) -> list[dict[str, Any]]:
271
+ """Execute query and return list of dictionaries."""
272
+ cursor = self.connection.cursor()
273
+ cursor.execute(query, params)
274
+
275
+ # pymysql DictCursor already returns list of dicts
276
+ result = cursor.fetchall()
277
+
278
+ cursor.close()
279
+ return result
280
+
281
+ def close(self):
282
+ """Close MySQL connection."""
283
+ if self.connection:
284
+ self.connection.close()
285
+
286
+
287
+ # Context manager support for all clients
288
+ class DatabaseClientContextManager:
289
+ """Context manager wrapper for database clients."""
290
+
291
+ def __init__(self, client: DatabaseClient):
292
+ self.client = client
293
+
294
+ def __enter__(self):
295
+ return self.client
296
+
297
+ def __exit__(self, exc_type, exc_val, exc_tb):
298
+ self.client.close()
299
+ return False
300
+
301
+
302
+ # Make all clients usable as context managers
303
+ OracleClient.__enter__ = lambda self: self
304
+ OracleClient.__exit__ = lambda self, *args: self.close() or False
305
+
306
+ SQLServerClient.__enter__ = lambda self: self
307
+ SQLServerClient.__exit__ = lambda self, *args: self.close() or False
308
+
309
+ MySQLClient.__enter__ = lambda self: self
310
+ MySQLClient.__exit__ = lambda self, *args: self.close() or False
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes